goldfish-opengl: threadID --> resourceId am: f4ee8074ec

Original change: https://android-review.googlesource.com/c/device/generic/goldfish-opengl/+/2180735

Change-Id: Ic688c68094cb5c663296df5c6f564fab22999ca2
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.mk b/Android.mk
index 9a98fb0..8e3be61 100644
--- a/Android.mk
+++ b/Android.mk
@@ -167,6 +167,7 @@
 ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 28 -o $(IS_AT_LEAST_QPR1) = true && echo isApi29OrHigher),isApi29OrHigher)
     # HWC2 enabled after P
     include $(GOLDFISH_OPENGL_PATH)/system/hwc2/Android.mk
+    include $(GOLDFISH_OPENGL_PATH)/system/hwc3/Android.mk
     # hardware codecs enabled after P
     include $(GOLDFISH_OPENGL_PATH)/system/codecs/omx/common/Android.mk
     include $(GOLDFISH_OPENGL_PATH)/system/codecs/omx/plugin/Android.mk
diff --git a/system/codecs/c2/decoders/avcdec/Android.bp b/system/codecs/c2/decoders/avcdec/Android.bp
index 78ffd82..5d840b0 100644
--- a/system/codecs/c2/decoders/avcdec/Android.bp
+++ b/system/codecs/c2/decoders/avcdec/Android.bp
@@ -15,6 +15,7 @@
     ],
 
     srcs: ["C2GoldfishAvcDec.cpp",
+        "GoldfishH264Helper.cpp",
         "MediaH264Decoder.cpp",
     ],
 
@@ -27,4 +28,7 @@
    header_libs: [
     "libgralloc_cb.ranchu",
     ],
+
+   static_libs: ["libavcdec",
+   ],
 }
diff --git a/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.cpp b/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.cpp
index 6c57922..90b5653 100644
--- a/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.cpp
+++ b/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.cpp
@@ -55,7 +55,7 @@
 namespace android {
 
 namespace {
-constexpr size_t kMinInputBufferSize = 2 * 1024 * 1024;
+constexpr size_t kMinInputBufferSize = 6 * 1024 * 1024;
 constexpr char COMPONENT_NAME[] = "c2.goldfish.h264.decoder";
 constexpr uint32_t kDefaultOutputDelay = 8;
 /* avc specification allows for a maximum delay of 16 frames.
@@ -643,7 +643,7 @@
         uint32_t format = HAL_PIXEL_FORMAT_YCBCR_420_888;
         C2MemoryUsage usage = {C2MemoryUsage::CPU_READ,
                                C2MemoryUsage::CPU_WRITE};
-        usage.expected = (uint64_t)(BufferUsage::GPU_DATA_BUFFER);
+        usage.expected = (uint64_t)(BufferUsage::VIDEO_DECODER);
         // C2MemoryUsage usage = {(unsigned
         // int)(BufferUsage::GPU_DATA_BUFFER)};// { C2MemoryUsage::CPU_READ,
         // C2MemoryUsage::CPU_WRITE };
@@ -670,31 +670,14 @@
 void C2GoldfishAvcDec::checkMode(const std::shared_ptr<C2BlockPool> &pool) {
     mWidth = mIntf->width();
     mHeight = mIntf->height();
-    {
-        // now get the block
-        constexpr uint32_t format = HAL_PIXEL_FORMAT_YCBCR_420_888;
-        std::shared_ptr<C2GraphicBlock> block;
-        C2MemoryUsage usage = {C2MemoryUsage::CPU_READ,
-                               C2MemoryUsage::CPU_WRITE};
-        usage.expected = (uint64_t)(BufferUsage::GPU_DATA_BUFFER);
-
-        c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 16), mHeight,
-                                                  format, usage, &block);
-        if (err != C2_OK) {
-            ALOGE("fetchGraphicBlock for Output failed with status %d", err);
-            return;
-        }
-        auto c2Handle = block->handle();
-        native_handle_t *grallocHandle =
-            UnwrapNativeCodec2GrallocHandle(c2Handle);
-        int hostColorBufferId = getColorBufferHandle(grallocHandle);
-        if (hostColorBufferId > 0) {
-            DDD("decoding to host color buffer");
-            mEnableAndroidNativeBuffers = true;
-        } else {
-            DDD("decoding to guest byte buffer");
-            mEnableAndroidNativeBuffers = false;
-        }
+    const bool isGraphic = (pool->getAllocatorId() & C2Allocator::GRAPHIC);
+    DDD("buffer id %d", (int)(pool->getAllocatorId()));
+    if (isGraphic) {
+        DDD("decoding to host color buffer");
+        mEnableAndroidNativeBuffers = true;
+    } else {
+        DDD("decoding to guest byte buffer");
+        mEnableAndroidNativeBuffers = false;
     }
 }
 
@@ -894,11 +877,6 @@
                 return;
             }
 
-            if (false == mHeaderDecoded) {
-                /* Decode header and get dimensions */
-                setParams(mStride);
-            }
-
             DDD("flag is %x", work->input.flags);
             if (work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) {
                 hasPicture = false;
@@ -913,9 +891,50 @@
                 removePts(mPts);
             }
 
+            bool whChanged = false;
+            if (GoldfishH264Helper::isSpsFrame(mInPBuffer, mInPBufferSize)) {
+                mH264Helper.reset(new GoldfishH264Helper(mWidth, mHeight));
+                whChanged = mH264Helper->decodeHeader(mInPBuffer, mInPBufferSize);
+                if (whChanged) {
+                        DDD("w changed from old %d to new %d\n", mWidth, mH264Helper->getWidth());
+                        DDD("h changed from old %d to new %d\n", mHeight, mH264Helper->getHeight());
+                        if (1) {
+                            drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work);
+                            resetDecoder();
+                            resetPlugin();
+                            work->workletsProcessed = 0u;
+                        }
+                        {
+                            mWidth = mH264Helper->getWidth();
+                            mHeight = mH264Helper->getHeight();
+                            C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight);
+                            std::vector<std::unique_ptr<C2SettingResult>> failures;
+                            c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures);
+                            if (err == OK) {
+                                work->worklets.front()->output.configUpdate.push_back(
+                                        C2Param::Copy(size));
+                                ensureDecoderState(pool);
+                            } else {
+                                ALOGE("Cannot set width and height");
+                                mSignalledError = true;
+                                work->workletsProcessed = 1u;
+                                work->result = C2_CORRUPTED;
+                                return;
+                            }
+                        }
+                        if (!mContext) {
+                            DDD("creating decoder context to host in process work");
+                            checkMode(pool);
+                            createDecoder();
+                        }
+                        continue;
+                } // end of whChanged
+            } // end of isSpsFrame
+
             uint32_t delay;
             GETTIME(&mTimeStart, nullptr);
             TIME_DIFF(mTimeEnd, mTimeStart, delay);
+            (void)delay;
             //(void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
             DDD("decoding");
             h264_result_t h264Res =
@@ -932,44 +951,10 @@
             uint32_t decodeTime;
             GETTIME(&mTimeEnd, nullptr);
             TIME_DIFF(mTimeStart, mTimeEnd, decodeTime);
+            (void)decodeTime;
         }
-        // TODO: handle res change
-        if (0) {
-            DDD("resolution changed");
-            drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work);
-            resetDecoder();
-            resetPlugin();
-            work->workletsProcessed = 0u;
 
-            /* Decode header and get new dimensions */
-            setParams(mStride);
-            //            (void) ivdec_api_function(mDecHandle, &s_decode_ip,
-            //            &s_decode_op);
-        }
         if (mImg.data != nullptr) {
-            // check for new width and height
-            auto decodedW = mImg.width;
-            auto decodedH = mImg.height;
-            if (decodedW != mWidth || decodedH != mHeight) {
-                mWidth = decodedW;
-                mHeight = decodedH;
-
-                C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight);
-                std::vector<std::unique_ptr<C2SettingResult>> failures;
-                c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures);
-                if (err == OK) {
-                    work->worklets.front()->output.configUpdate.push_back(
-                        C2Param::Copy(size));
-                    ensureDecoderState(pool);
-                } else {
-                    ALOGE("Cannot set width and height");
-                    mSignalledError = true;
-                    work->workletsProcessed = 1u;
-                    work->result = C2_CORRUPTED;
-                    return;
-                }
-            }
-
             DDD("got data %" PRIu64 " with pts %" PRIu64,  getWorkIndex(mImg.pts), mImg.pts);
             mHeaderDecoded = true;
             copyImageData(mImg);
diff --git a/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.h b/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.h
index 914a10e..afa27f5 100644
--- a/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.h
+++ b/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.h
@@ -22,6 +22,7 @@
 #include <media/stagefright/foundation/ColorUtils.h>
 
 #include "MediaH264Decoder.h"
+#include "GoldfishH264Helper.h"
 #include <SimpleC2Component.h>
 #include <atomic>
 #include <map>
@@ -152,6 +153,8 @@
     std::vector<uint8_t> mCsd1;
     void decodeHeaderAfterFlush();
 
+    std::unique_ptr<GoldfishH264Helper> mH264Helper;
+
     C2_DO_NOT_COPY(C2GoldfishAvcDec);
 };
 
diff --git a/system/codecs/c2/decoders/avcdec/GoldfishH264Helper.cpp b/system/codecs/c2/decoders/avcdec/GoldfishH264Helper.cpp
new file mode 100644
index 0000000..077ef15
--- /dev/null
+++ b/system/codecs/c2/decoders/avcdec/GoldfishH264Helper.cpp
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#include "GoldfishH264Helper.h"
+
+#define LOG_TAG "GoldfishH264Helper"
+#include <log/log.h>
+
+
+#define DEBUG 0
+#if DEBUG
+#define DDD(...) ALOGD(__VA_ARGS__)
+#else
+#define DDD(...) ((void)0)
+#endif
+
+
+#include <Codec2Mapper.h>
+
+#define ivdec_api_function              ih264d_api_function
+#define ivdext_create_ip_t              ih264d_create_ip_t
+#define ivdext_create_op_t              ih264d_create_op_t
+#define ivdext_delete_ip_t              ih264d_delete_ip_t
+#define ivdext_delete_op_t              ih264d_delete_op_t
+#define ivdext_ctl_set_num_cores_ip_t   ih264d_ctl_set_num_cores_ip_t
+#define ivdext_ctl_set_num_cores_op_t   ih264d_ctl_set_num_cores_op_t
+#define ivdext_ctl_get_vui_params_ip_t  ih264d_ctl_get_vui_params_ip_t
+#define ivdext_ctl_get_vui_params_op_t  ih264d_ctl_get_vui_params_op_t
+#define ALIGN128(x)                     ((((x) + 127) >> 7) << 7)
+#define MAX_NUM_CORES                   4
+#define IVDEXT_CMD_CTL_SET_NUM_CORES    \
+        (IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_SET_NUM_CORES
+#define MIN(a, b)                       (((a) < (b)) ? (a) : (b))
+
+namespace android {
+
+static void *ivd_aligned_malloc(void *ctxt, WORD32 alignment, WORD32 size) {
+    (void) ctxt;
+    return memalign(alignment, size);
+}
+
+static void ivd_aligned_free(void *ctxt, void *mem) {
+    (void) ctxt;
+    free(mem);
+}
+
+
+GoldfishH264Helper::GoldfishH264Helper(int w, int h):mWidth(w),mHeight(h) { createDecoder(); }
+
+GoldfishH264Helper::~GoldfishH264Helper() {
+    destroyDecoder();
+}
+
+void GoldfishH264Helper::createDecoder() {
+    ivdext_create_ip_t s_create_ip = {};
+    ivdext_create_op_t s_create_op = {};
+
+    s_create_ip.s_ivd_create_ip_t.u4_size = sizeof(ivdext_create_ip_t);
+    s_create_ip.s_ivd_create_ip_t.e_cmd = IVD_CMD_CREATE;
+    s_create_ip.s_ivd_create_ip_t.u4_share_disp_buf = 0;
+    s_create_ip.s_ivd_create_ip_t.e_output_format = mIvColorformat;
+    s_create_ip.s_ivd_create_ip_t.pf_aligned_alloc = ivd_aligned_malloc;
+    s_create_ip.s_ivd_create_ip_t.pf_aligned_free = ivd_aligned_free;
+    s_create_ip.s_ivd_create_ip_t.pv_mem_ctxt = nullptr;
+    s_create_op.s_ivd_create_op_t.u4_size = sizeof(ivdext_create_op_t);
+    IV_API_CALL_STATUS_T status =
+        ivdec_api_function(mDecHandle, &s_create_ip, &s_create_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("error in %s: 0x%x", __func__,
+              s_create_op.s_ivd_create_op_t.u4_error_code);
+        return;
+    }
+    mDecHandle = (iv_obj_t *)s_create_op.s_ivd_create_op_t.pv_handle;
+    mDecHandle->pv_fxns = (void *)ivdec_api_function;
+    mDecHandle->u4_size = sizeof(iv_obj_t);
+
+    mStride = ALIGN128(mWidth);
+
+    setNumCores();
+}
+
+void GoldfishH264Helper::destroyDecoder() {
+    if (mDecHandle) {
+        ivdext_delete_ip_t s_delete_ip = {};
+        ivdext_delete_op_t s_delete_op = {};
+
+        s_delete_ip.s_ivd_delete_ip_t.u4_size = sizeof(ivdext_delete_ip_t);
+        s_delete_ip.s_ivd_delete_ip_t.e_cmd = IVD_CMD_DELETE;
+        s_delete_op.s_ivd_delete_op_t.u4_size = sizeof(ivdext_delete_op_t);
+        IV_API_CALL_STATUS_T status =
+            ivdec_api_function(mDecHandle, &s_delete_ip, &s_delete_op);
+        if (status != IV_SUCCESS) {
+            ALOGE("error in %s: 0x%x", __func__,
+                  s_delete_op.s_ivd_delete_op_t.u4_error_code);
+        }
+        mDecHandle = nullptr;
+    }
+}
+
+void GoldfishH264Helper::setNumCores() {
+    ivdext_ctl_set_num_cores_ip_t s_set_num_cores_ip = {};
+    ivdext_ctl_set_num_cores_op_t s_set_num_cores_op = {};
+
+    s_set_num_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t);
+    s_set_num_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_set_num_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES;
+    s_set_num_cores_ip.u4_num_cores = mNumCores;
+    s_set_num_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(
+        mDecHandle, &s_set_num_cores_ip, &s_set_num_cores_op);
+    if (IV_SUCCESS != status) {
+        DDD("error in %s: 0x%x", __func__, s_set_num_cores_op.u4_error_code);
+    }
+}
+
+void GoldfishH264Helper::resetDecoder() {
+    ivd_ctl_reset_ip_t s_reset_ip = {};
+    ivd_ctl_reset_op_t s_reset_op = {};
+
+    s_reset_ip.u4_size = sizeof(ivd_ctl_reset_ip_t);
+    s_reset_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_reset_ip.e_sub_cmd = IVD_CMD_CTL_RESET;
+    s_reset_op.u4_size = sizeof(ivd_ctl_reset_op_t);
+    IV_API_CALL_STATUS_T status =
+        ivdec_api_function(mDecHandle, &s_reset_ip, &s_reset_op);
+    if (IV_SUCCESS != status) {
+        ALOGE("error in %s: 0x%x", __func__, s_reset_op.u4_error_code);
+    }
+    setNumCores();
+}
+
+void GoldfishH264Helper::setParams(size_t stride,
+                                   IVD_VIDEO_DECODE_MODE_T dec_mode) {
+    ih264d_ctl_set_config_ip_t s_h264d_set_dyn_params_ip = {};
+    ih264d_ctl_set_config_op_t s_h264d_set_dyn_params_op = {};
+    ivd_ctl_set_config_ip_t *ps_set_dyn_params_ip =
+        &s_h264d_set_dyn_params_ip.s_ivd_ctl_set_config_ip_t;
+    ivd_ctl_set_config_op_t *ps_set_dyn_params_op =
+        &s_h264d_set_dyn_params_op.s_ivd_ctl_set_config_op_t;
+
+    ps_set_dyn_params_ip->u4_size = sizeof(ih264d_ctl_set_config_ip_t);
+    ps_set_dyn_params_ip->e_cmd = IVD_CMD_VIDEO_CTL;
+    ps_set_dyn_params_ip->e_sub_cmd = IVD_CMD_CTL_SETPARAMS;
+    ps_set_dyn_params_ip->u4_disp_wd = (UWORD32) stride;
+    ps_set_dyn_params_ip->e_frm_skip_mode = IVD_SKIP_NONE;
+    ps_set_dyn_params_ip->e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
+    ps_set_dyn_params_ip->e_vid_dec_mode = dec_mode;
+    ps_set_dyn_params_op->u4_size = sizeof(ih264d_ctl_set_config_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_h264d_set_dyn_params_ip,
+                                                     &s_h264d_set_dyn_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("error in %s: 0x%x", __func__,
+              ps_set_dyn_params_op->u4_error_code);
+    }
+}
+
+bool GoldfishH264Helper::isSpsFrame(const uint8_t* frame, int inSize) {
+    if (inSize < 5) return false;
+    if (frame[0] == 0 && frame[1] == 0 && frame[2] == 0 && frame[3] == 1) {
+        const bool forbiddenBitIsInvalid = 0x80 & frame[4];
+        if (forbiddenBitIsInvalid) {
+            return false;
+        }
+        // nalu type is the lower 5 bits
+        uint8_t naluType = 0x1f & frame[4];
+        if (naluType == 7
+            || naluType == 8
+                ) return true;
+        else return false;
+    } else {
+        return false;
+    }
+}
+
+bool GoldfishH264Helper::decodeHeader(const uint8_t *frame, int inSize) {
+    // should we check the header for vps/sps/pps frame ? otherwise
+    // there is no point calling decoder
+    if (!isSpsFrame(frame, inSize)) {
+        DDD("could not find valid vps frame");
+        return false;
+    } else {
+        DDD("found valid vps frame");
+    }
+
+    ih264d_video_decode_ip_t s_h264d_decode_ip = {};
+    ih264d_video_decode_op_t s_h264d_decode_op = {};
+    ivd_video_decode_ip_t *ps_decode_ip = &s_h264d_decode_ip.s_ivd_video_decode_ip_t;
+    ivd_video_decode_op_t *ps_decode_op = &s_h264d_decode_op.s_ivd_video_decode_op_t;
+
+    // setup input/output arguments to decoder
+    setDecodeArgs(ps_decode_ip, ps_decode_op, frame, mStride,
+            0, // offset
+            inSize, // size
+            0 // time-stamp, does not matter
+            );
+
+    setParams(mStride, IVD_DECODE_HEADER);
+
+    // now kick off the decoding
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle, ps_decode_ip, ps_decode_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("failed to call decoder function for header\n");
+        ALOGE("error in %s: 0x%x", __func__,
+              ps_decode_op->u4_error_code);
+    }
+
+    if (IVD_RES_CHANGED == (ps_decode_op->u4_error_code & IVD_ERROR_MASK)) {
+        DDD("resolution changed, reset decoder");
+        resetDecoder();
+        setParams(mStride, IVD_DECODE_HEADER);
+        ivdec_api_function(mDecHandle, ps_decode_ip, ps_decode_op);
+    }
+
+    // get the w/h and update
+    if (0 < ps_decode_op->u4_pic_wd && 0 < ps_decode_op->u4_pic_ht) {
+        DDD("success decode w/h %d %d", ps_decode_op->u4_pic_wd , ps_decode_op->u4_pic_ht);
+        DDD("existing w/h %d %d", mWidth, mHeight);
+        if (ps_decode_op->u4_pic_wd != mWidth ||  ps_decode_op->u4_pic_ht != mHeight) {
+            mWidth = ps_decode_op->u4_pic_wd;
+            mHeight = ps_decode_op->u4_pic_ht;
+            return true;
+        } else {
+            DDD("success decode w/h, but they are the same %d %d", ps_decode_op->u4_pic_wd , ps_decode_op->u4_pic_ht);
+        }
+    } else {
+        ALOGE("could not decode w/h");
+    }
+
+    // get output delay
+    if (ps_decode_op->i4_reorder_depth >= 0) {
+        if (mOutputDelay != ps_decode_op->i4_reorder_depth) {
+            mOutputDelay = ps_decode_op->i4_reorder_depth;
+            DDD("New Output delay %d ", mOutputDelay);
+        } else {
+            DDD("same Output delay %d ", mOutputDelay);
+        }
+    }
+
+    return false;
+}
+
+bool GoldfishH264Helper::setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip,
+                                       ivd_video_decode_op_t *ps_decode_op,
+                                       const uint8_t *inBuffer,
+                                       uint32_t displayStride, size_t inOffset,
+                                       size_t inSize, uint32_t tsMarker) {
+    uint32_t displayHeight = mHeight;
+    size_t lumaSize = displayStride * displayHeight;
+    size_t chromaSize = lumaSize >> 2;
+
+    if (mStride != displayStride) {
+        mStride = displayStride;
+    }
+
+    // force decoder to always decode header and get dimensions,
+    // hope this will be quick and cheap
+    setParams(mStride, IVD_DECODE_HEADER);
+
+    ps_decode_ip->u4_size = sizeof(ih264d_video_decode_ip_t);
+    ps_decode_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
+    if (inBuffer) {
+        ps_decode_ip->u4_ts = tsMarker;
+        ps_decode_ip->pv_stream_buffer = const_cast<uint8_t *>(inBuffer) + inOffset;
+        ps_decode_ip->u4_num_Bytes = inSize;
+    } else {
+        ps_decode_ip->u4_ts = 0;
+        ps_decode_ip->pv_stream_buffer = nullptr;
+        ps_decode_ip->u4_num_Bytes = 0;
+    }
+    DDD("setting pv_stream_buffer 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
+            ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[0],
+            ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[1],
+            ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[2],
+            ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[3],
+            ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[4],
+            ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[5],
+            ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[6],
+            ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[7]
+            );
+    DDD("input bytes %d", ps_decode_ip->u4_num_Bytes);
+
+    ps_decode_ip->s_out_buffer.u4_min_out_buf_size[0] = lumaSize;
+    ps_decode_ip->s_out_buffer.u4_min_out_buf_size[1] = chromaSize;
+    ps_decode_ip->s_out_buffer.u4_min_out_buf_size[2] = chromaSize;
+    {
+        ps_decode_ip->s_out_buffer.pu1_bufs[0] = nullptr;
+        ps_decode_ip->s_out_buffer.pu1_bufs[1] = nullptr;
+        ps_decode_ip->s_out_buffer.pu1_bufs[2] = nullptr;
+    }
+    ps_decode_ip->s_out_buffer.u4_num_bufs = 3;
+    ps_decode_op->u4_size = sizeof(ih264d_video_decode_op_t);
+    ps_decode_op->u4_output_present = 0;
+
+    return true;
+}
+
+} // namespace android
diff --git a/system/codecs/c2/decoders/avcdec/GoldfishH264Helper.h b/system/codecs/c2/decoders/avcdec/GoldfishH264Helper.h
new file mode 100644
index 0000000..ec3b384
--- /dev/null
+++ b/system/codecs/c2/decoders/avcdec/GoldfishH264Helper.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef GOLDFISH_H264_HELPER_H_
+#define GOLDFISH_H264_HELPER_H_
+
+#include <inttypes.h>
+#include "ih264_typedefs.h"
+#include "ih264d.h"
+
+
+namespace android {
+
+// this class is just to provide some functions to decode header
+// so that we know w/h of each sps
+class GoldfishH264Helper {
+  public:
+    GoldfishH264Helper(int w, int h);
+    ~GoldfishH264Helper();
+
+    // check whether the frame is sps; typical h264 will have
+    // a frame that is sps/pps together
+    static bool isSpsFrame(const uint8_t* frame, int inSize);
+  public:
+    // return true if decoding finds out w/h changed;
+    // otherwise false
+    bool decodeHeader(const uint8_t *frame, int inSize);
+    int getWidth() const { return mWidth; }
+    int getHeight() const { return mHeight; }
+
+  private:
+    void createDecoder();
+    void destroyDecoder();
+    void resetDecoder();
+    void setNumCores();
+    void setParams(size_t stride, IVD_VIDEO_DECODE_MODE_T dec_mode);
+    bool setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip,
+                       ivd_video_decode_op_t *ps_decode_op,
+                       const uint8_t *inBuffer, uint32_t displayStride,
+                       size_t inOffset, size_t inSize, uint32_t tsMarker);
+
+  private:
+    iv_obj_t *mDecHandle = nullptr;
+    int mWidth = 320;
+    int mHeight = 240;
+    int mNumCores = 1;
+    int mStride = 16;
+    int mOutputDelay = 8; // default
+    IV_COLOR_FORMAT_T mIvColorformat = IV_YUV_420P;
+};
+
+} // namespace android
+#endif
diff --git a/system/codecs/c2/decoders/base/SimpleC2Interface.cpp b/system/codecs/c2/decoders/base/SimpleC2Interface.cpp
index 9f76520..5e18da9 100644
--- a/system/codecs/c2/decoders/base/SimpleC2Interface.cpp
+++ b/system/codecs/c2/decoders/base/SimpleC2Interface.cpp
@@ -29,6 +29,14 @@
 
 /* SimpleInterface */
 
+static C2R SubscribedParamIndicesSetter(
+        bool mayBlock, C2InterfaceHelper::C2P<C2SubscribedParamIndicesTuning> &me) {
+    (void)mayBlock;
+    (void)me;
+
+    return C2R::Ok();
+}
+
 SimpleInterface<void>::BaseParams::BaseParams(
     const std::shared_ptr<C2ReflectorHelper> &reflector, C2String name,
     C2Component::kind_t kind, C2Component::domain_t domain, C2String mediaType,
@@ -196,9 +204,7 @@
             .withDefault(C2SubscribedParamIndicesTuning::AllocShared(0u))
             .withFields({C2F(mSubscribedParamIndices, m.values[0]).any(),
                          C2F(mSubscribedParamIndices, m.values).any()})
-            .withSetter(
-                Setter<
-                    C2SubscribedParamIndicesTuning>::NonStrictValuesWithNoDeps)
+            .withSetter(SubscribedParamIndicesSetter)
             .build());
 
     /* TODO
diff --git a/system/codecs/c2/decoders/base/include/goldfish_media_utils.h b/system/codecs/c2/decoders/base/include/goldfish_media_utils.h
index b3e26ad..efa8859 100644
--- a/system/codecs/c2/decoders/base/include/goldfish_media_utils.h
+++ b/system/codecs/c2/decoders/base/include/goldfish_media_utils.h
@@ -22,7 +22,8 @@
     VP8Codec = 0,
     VP9Codec = 1,
     H264Codec = 2,
-    Max = 3,
+    HevcCodec = 3,
+    Max = 4,
 };
 
 enum class MediaOperation : __u8 {
diff --git a/system/codecs/c2/decoders/hevcdec/Android.bp b/system/codecs/c2/decoders/hevcdec/Android.bp
new file mode 100644
index 0000000..f1b9c82
--- /dev/null
+++ b/system/codecs/c2/decoders/hevcdec/Android.bp
@@ -0,0 +1,36 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "device_generic_goldfish-opengl_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["device_generic_goldfish-opengl_license"],
+}
+
+cc_library_shared {
+    name: "libcodec2_goldfish_hevcdec",
+    vendor: true,
+    defaults: [
+        "libcodec2_goldfish-defaults",
+    ],
+
+    srcs: ["C2GoldfishHevcDec.cpp",
+        "GoldfishHevcHelper.cpp",
+        "MediaHevcDecoder.cpp",
+    ],
+
+    shared_libs: [
+	    "android.hardware.graphics.allocator@3.0",
+		"android.hardware.graphics.mapper@3.0",
+        "libgoldfish_codec2_store",
+    ],
+
+   header_libs: [
+    "libgralloc_cb.ranchu",
+    ],
+
+   static_libs: ["libhevcdec",
+   ],
+
+}
+
diff --git a/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.cpp b/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.cpp
new file mode 100644
index 0000000..7008bd5
--- /dev/null
+++ b/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.cpp
@@ -0,0 +1,1080 @@
+/*
+ * Copyright 2022 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_NDEBUG 0
+#define LOG_TAG "C2GoldfishHevcDec"
+#include <inttypes.h>
+#include <log/log.h>
+#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2AllocatorGralloc.h>
+#include <C2PlatformSupport.h>
+//#include <android/hardware/graphics/common/1.0/types.h>
+
+#include <android/hardware/graphics/allocator/3.0/IAllocator.h>
+#include <android/hardware/graphics/mapper/3.0/IMapper.h>
+#include <hidl/LegacySupport.h>
+
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+#include <Codec2Mapper.h>
+#include <SimpleC2Interface.h>
+#include <goldfish_codec2/store/GoldfishComponentStore.h>
+#include <gralloc_cb_bp.h>
+
+#include <color_buffer_utils.h>
+
+#include "C2GoldfishHevcDec.h"
+
+#define DEBUG 0
+#if DEBUG
+#define DDD(...) ALOGD(__VA_ARGS__)
+#else
+#define DDD(...) ((void)0)
+#endif
+
+using ::android::hardware::graphics::common::V1_0::BufferUsage;
+using ::android::hardware::graphics::common::V1_2::PixelFormat;
+
+namespace android {
+
+namespace {
+constexpr size_t kMinInputBufferSize = 6 * 1024 * 1024;
+constexpr char COMPONENT_NAME[] = "c2.goldfish.hevc.decoder";
+constexpr uint32_t kDefaultOutputDelay = 8;
+constexpr uint32_t kMaxOutputDelay = 16;
+} // namespace
+
+class C2GoldfishHevcDec::IntfImpl : public SimpleInterface<void>::BaseParams {
+  public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+              helper, COMPONENT_NAME, C2Component::KIND_DECODER,
+              C2Component::DOMAIN_VIDEO, MEDIA_MIMETYPE_VIDEO_HEVC) {
+        noPrivateBuffers(); // TODO: account for our buffers here
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+
+        // TODO: Proper support for reorder depth.
+        addParameter(
+            DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY)
+                .withDefault(
+                    new C2PortActualDelayTuning::output(kDefaultOutputDelay))
+                .withFields({C2F(mActualOutputDelay, value)
+                                 .inRange(0, kMaxOutputDelay)})
+                .withSetter(
+                    Setter<
+                        decltype(*mActualOutputDelay)>::StrictValueWithNoDeps)
+                .build());
+
+        // TODO: output latency and reordering
+
+        addParameter(DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                         .withConstValue(new C2ComponentAttributesSetting(
+                             C2Component::ATTRIB_IS_TEMPORAL))
+                         .build());
+
+        // coded and output picture size is the same for this codec
+        addParameter(
+            DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
+                .withDefault(new C2StreamPictureSizeInfo::output(0u, 320, 240))
+                .withFields({
+                    C2F(mSize, width).inRange(2, 4096, 2),
+                    C2F(mSize, height).inRange(2, 4096, 2),
+                })
+                .withSetter(SizeSetter)
+                .build());
+
+        addParameter(DefineParam(mMaxSize, C2_PARAMKEY_MAX_PICTURE_SIZE)
+                         .withDefault(new C2StreamMaxPictureSizeTuning::output(
+                             0u, 320, 240))
+                         .withFields({
+                             C2F(mSize, width).inRange(2, 4096, 2),
+                             C2F(mSize, height).inRange(2, 4096, 2),
+                         })
+                         .withSetter(MaxPictureSizeSetter, mSize)
+                         .build());
+
+        addParameter(
+            DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+                .withDefault(new C2StreamProfileLevelInfo::input(
+                    0u, C2Config::PROFILE_HEVC_MAIN, C2Config::LEVEL_HEVC_MAIN_5_1))
+                .withFields({
+                    C2F(mProfileLevel, profile).oneOf({
+                            C2Config::PROFILE_HEVC_MAIN,
+                            C2Config::PROFILE_HEVC_MAIN_STILL}),
+                    C2F(mProfileLevel, level).oneOf({
+                            C2Config::LEVEL_HEVC_MAIN_1,
+                            C2Config::LEVEL_HEVC_MAIN_2, C2Config::LEVEL_HEVC_MAIN_2_1,
+                            C2Config::LEVEL_HEVC_MAIN_3, C2Config::LEVEL_HEVC_MAIN_3_1,
+                            C2Config::LEVEL_HEVC_MAIN_4, C2Config::LEVEL_HEVC_MAIN_4_1,
+                            C2Config::LEVEL_HEVC_MAIN_5, C2Config::LEVEL_HEVC_MAIN_5_1,
+                            C2Config::LEVEL_HEVC_MAIN_5_2, C2Config::LEVEL_HEVC_HIGH_4,
+                            C2Config::LEVEL_HEVC_HIGH_4_1, C2Config::LEVEL_HEVC_HIGH_5,
+                            C2Config::LEVEL_HEVC_HIGH_5_1, C2Config::LEVEL_HEVC_HIGH_5_2
+                    })
+                })
+                .withSetter(ProfileLevelSetter, mSize)
+                .build());
+
+        addParameter(
+            DefineParam(mMaxInputSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+                .withDefault(new C2StreamMaxBufferSizeInfo::input(
+                    0u, kMinInputBufferSize))
+                .withFields({
+                    C2F(mMaxInputSize, value).any(),
+                })
+                .calculatedAs(MaxInputSizeSetter, mMaxSize)
+                .build());
+
+        C2ChromaOffsetStruct locations[1] = {
+            C2ChromaOffsetStruct::ITU_YUV_420_0()};
+        std::shared_ptr<C2StreamColorInfo::output> defaultColorInfo =
+            C2StreamColorInfo::output::AllocShared(1u, 0u, 8u /* bitDepth */,
+                                                   C2Color::YUV_420);
+        memcpy(defaultColorInfo->m.locations, locations, sizeof(locations));
+
+        defaultColorInfo = C2StreamColorInfo::output::AllocShared(
+            {C2ChromaOffsetStruct::ITU_YUV_420_0()}, 0u, 8u /* bitDepth */,
+            C2Color::YUV_420);
+        helper->addStructDescriptors<C2ChromaOffsetStruct>();
+
+        addParameter(DefineParam(mColorInfo, C2_PARAMKEY_CODED_COLOR_INFO)
+                         .withConstValue(defaultColorInfo)
+                         .build());
+
+        addParameter(
+            DefineParam(mDefaultColorAspects, C2_PARAMKEY_DEFAULT_COLOR_ASPECTS)
+                .withDefault(new C2StreamColorAspectsTuning::output(
+                    0u, C2Color::RANGE_UNSPECIFIED,
+                    C2Color::PRIMARIES_UNSPECIFIED,
+                    C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+                .withFields({C2F(mDefaultColorAspects, range)
+                                 .inRange(C2Color::RANGE_UNSPECIFIED,
+                                          C2Color::RANGE_OTHER),
+                             C2F(mDefaultColorAspects, primaries)
+                                 .inRange(C2Color::PRIMARIES_UNSPECIFIED,
+                                          C2Color::PRIMARIES_OTHER),
+                             C2F(mDefaultColorAspects, transfer)
+                                 .inRange(C2Color::TRANSFER_UNSPECIFIED,
+                                          C2Color::TRANSFER_OTHER),
+                             C2F(mDefaultColorAspects, matrix)
+                                 .inRange(C2Color::MATRIX_UNSPECIFIED,
+                                          C2Color::MATRIX_OTHER)})
+                .withSetter(DefaultColorAspectsSetter)
+                .build());
+
+        addParameter(
+            DefineParam(mCodedColorAspects, C2_PARAMKEY_VUI_COLOR_ASPECTS)
+                .withDefault(new C2StreamColorAspectsInfo::input(
+                    0u, C2Color::RANGE_LIMITED, C2Color::PRIMARIES_UNSPECIFIED,
+                    C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+                .withFields({C2F(mCodedColorAspects, range)
+                                 .inRange(C2Color::RANGE_UNSPECIFIED,
+                                          C2Color::RANGE_OTHER),
+                             C2F(mCodedColorAspects, primaries)
+                                 .inRange(C2Color::PRIMARIES_UNSPECIFIED,
+                                          C2Color::PRIMARIES_OTHER),
+                             C2F(mCodedColorAspects, transfer)
+                                 .inRange(C2Color::TRANSFER_UNSPECIFIED,
+                                          C2Color::TRANSFER_OTHER),
+                             C2F(mCodedColorAspects, matrix)
+                                 .inRange(C2Color::MATRIX_UNSPECIFIED,
+                                          C2Color::MATRIX_OTHER)})
+                .withSetter(CodedColorAspectsSetter)
+                .build());
+
+        addParameter(
+            DefineParam(mColorAspects, C2_PARAMKEY_COLOR_ASPECTS)
+                .withDefault(new C2StreamColorAspectsInfo::output(
+                    0u, C2Color::RANGE_UNSPECIFIED,
+                    C2Color::PRIMARIES_UNSPECIFIED,
+                    C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+                .withFields({C2F(mColorAspects, range)
+                                 .inRange(C2Color::RANGE_UNSPECIFIED,
+                                          C2Color::RANGE_OTHER),
+                             C2F(mColorAspects, primaries)
+                                 .inRange(C2Color::PRIMARIES_UNSPECIFIED,
+                                          C2Color::PRIMARIES_OTHER),
+                             C2F(mColorAspects, transfer)
+                                 .inRange(C2Color::TRANSFER_UNSPECIFIED,
+                                          C2Color::TRANSFER_OTHER),
+                             C2F(mColorAspects, matrix)
+                                 .inRange(C2Color::MATRIX_UNSPECIFIED,
+                                          C2Color::MATRIX_OTHER)})
+                .withSetter(ColorAspectsSetter, mDefaultColorAspects,
+                            mCodedColorAspects)
+                .build());
+
+        // TODO: support more formats?
+        addParameter(DefineParam(mPixelFormat, C2_PARAMKEY_PIXEL_FORMAT)
+                         .withConstValue(new C2StreamPixelFormatInfo::output(
+                             0u, HAL_PIXEL_FORMAT_YCBCR_420_888))
+                         .build());
+    }
+    static C2R SizeSetter(bool mayBlock,
+                          const C2P<C2StreamPictureSizeInfo::output> &oldMe,
+                          C2P<C2StreamPictureSizeInfo::output> &me) {
+        (void)mayBlock;
+        DDD("calling sizesetter now %d", oldMe.v.height);
+        DDD("new calling sizesetter now %d", me.v.height);
+
+        C2R res = C2R::Ok();
+        if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
+            me.set().width = oldMe.v.width;
+        }
+        if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
+            me.set().height = oldMe.v.height;
+        }
+        return res;
+    }
+
+    static C2R
+    MaxPictureSizeSetter(bool mayBlock,
+                         C2P<C2StreamMaxPictureSizeTuning::output> &me,
+                         const C2P<C2StreamPictureSizeInfo::output> &size) {
+        (void)mayBlock;
+        // TODO: get max width/height from the size's field helpers vs.
+        // hardcoding
+        me.set().width = c2_min(c2_max(me.v.width, size.v.width), 4096u);
+        me.set().height = c2_min(c2_max(me.v.height, size.v.height), 4096u);
+        return C2R::Ok();
+    }
+
+    static C2R MaxInputSizeSetter(
+        bool mayBlock, C2P<C2StreamMaxBufferSizeInfo::input> &me,
+        const C2P<C2StreamMaxPictureSizeTuning::output> &maxSize) {
+        (void)mayBlock;
+        // assume compression ratio of 2
+        me.set().value = c2_max((((maxSize.v.width + 63) / 64) *
+                                 ((maxSize.v.height + 64) / 64) * 3072),
+                                kMinInputBufferSize);
+        return C2R::Ok();
+    }
+
+    static C2R
+    ProfileLevelSetter(bool mayBlock, C2P<C2StreamProfileLevelInfo::input> &me,
+                       const C2P<C2StreamPictureSizeInfo::output> &size) {
+        (void)mayBlock;
+        (void)size;
+        (void)me; // TODO: validate
+        return C2R::Ok();
+    }
+
+    static C2R
+    DefaultColorAspectsSetter(bool mayBlock,
+                              C2P<C2StreamColorAspectsTuning::output> &me) {
+        (void)mayBlock;
+        if (me.v.range > C2Color::RANGE_OTHER) {
+            me.set().range = C2Color::RANGE_OTHER;
+        }
+        if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
+            me.set().primaries = C2Color::PRIMARIES_OTHER;
+        }
+        if (me.v.transfer > C2Color::TRANSFER_OTHER) {
+            me.set().transfer = C2Color::TRANSFER_OTHER;
+        }
+        if (me.v.matrix > C2Color::MATRIX_OTHER) {
+            me.set().matrix = C2Color::MATRIX_OTHER;
+        }
+        return C2R::Ok();
+    }
+
+    static C2R
+    CodedColorAspectsSetter(bool mayBlock,
+                            C2P<C2StreamColorAspectsInfo::input> &me) {
+        (void)mayBlock;
+        if (me.v.range > C2Color::RANGE_OTHER) {
+            me.set().range = C2Color::RANGE_OTHER;
+        }
+        if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
+            me.set().primaries = C2Color::PRIMARIES_OTHER;
+        }
+        if (me.v.transfer > C2Color::TRANSFER_OTHER) {
+            me.set().transfer = C2Color::TRANSFER_OTHER;
+        }
+        if (me.v.matrix > C2Color::MATRIX_OTHER) {
+            me.set().matrix = C2Color::MATRIX_OTHER;
+        }
+        return C2R::Ok();
+    }
+
+    static C2R
+    ColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::output> &me,
+                       const C2P<C2StreamColorAspectsTuning::output> &def,
+                       const C2P<C2StreamColorAspectsInfo::input> &coded) {
+        (void)mayBlock;
+        // take default values for all unspecified fields, and coded values for
+        // specified ones
+        me.set().range =
+            coded.v.range == RANGE_UNSPECIFIED ? def.v.range : coded.v.range;
+        me.set().primaries = coded.v.primaries == PRIMARIES_UNSPECIFIED
+                                 ? def.v.primaries
+                                 : coded.v.primaries;
+        me.set().transfer = coded.v.transfer == TRANSFER_UNSPECIFIED
+                                ? def.v.transfer
+                                : coded.v.transfer;
+        me.set().matrix = coded.v.matrix == MATRIX_UNSPECIFIED ? def.v.matrix
+                                                               : coded.v.matrix;
+        return C2R::Ok();
+    }
+
+    std::shared_ptr<C2StreamColorAspectsInfo::output> getColorAspects_l() {
+        return mColorAspects;
+    }
+
+    int width() const { return mSize->width; }
+
+    int height() const { return mSize->height; }
+
+  private:
+    std::shared_ptr<C2StreamProfileLevelInfo::input> mProfileLevel;
+    std::shared_ptr<C2StreamPictureSizeInfo::output> mSize;
+    std::shared_ptr<C2StreamMaxPictureSizeTuning::output> mMaxSize;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mMaxInputSize;
+    std::shared_ptr<C2StreamColorInfo::output> mColorInfo;
+    std::shared_ptr<C2StreamColorAspectsInfo::input> mCodedColorAspects;
+    std::shared_ptr<C2StreamColorAspectsTuning::output> mDefaultColorAspects;
+    std::shared_ptr<C2StreamColorAspectsInfo::output> mColorAspects;
+    std::shared_ptr<C2StreamPixelFormatInfo::output> mPixelFormat;
+};
+
+static void *ivd_aligned_malloc(void *ctxt, uint32_t alignment, uint32_t size) {
+    (void)ctxt;
+    return memalign(alignment, size);
+}
+
+static void ivd_aligned_free(void *ctxt, void *mem) {
+    (void)ctxt;
+    free(mem);
+}
+
+C2GoldfishHevcDec::C2GoldfishHevcDec(const char *name, c2_node_id_t id,
+                                   const std::shared_ptr<IntfImpl> &intfImpl)
+    : SimpleC2Component(
+          std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl), mOutBufferFlush(nullptr), mWidth(1920), mHeight(1080),
+      mHeaderDecoded(false), mOutIndex(0u) {
+    mWidth = mIntf->width();
+    mHeight = mIntf->height();
+    DDD("creating hevc decoder now w %d h %d", mWidth, mHeight);
+}
+
+C2GoldfishHevcDec::~C2GoldfishHevcDec() { onRelease(); }
+
+c2_status_t C2GoldfishHevcDec::onInit() {
+    status_t err = initDecoder();
+    return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+c2_status_t C2GoldfishHevcDec::onStop() {
+    if (OK != resetDecoder())
+        return C2_CORRUPTED;
+    resetPlugin();
+    return C2_OK;
+}
+
+void C2GoldfishHevcDec::onReset() { (void)onStop(); }
+
+void C2GoldfishHevcDec::onRelease() {
+    deleteContext();
+    if (mOutBlock) {
+        mOutBlock.reset();
+    }
+}
+
+void C2GoldfishHevcDec::decodeHeaderAfterFlush() {
+    if (mContext && !mCsd0.empty() && !mCsd1.empty()) {
+        mContext->decodeFrame(&(mCsd0[0]), mCsd0.size(), 0);
+        mContext->decodeFrame(&(mCsd1[0]), mCsd1.size(), 0);
+        DDD("resending csd0 and csd1");
+    }
+}
+
+c2_status_t C2GoldfishHevcDec::onFlush_sm() {
+    if (OK != setFlushMode())
+        return C2_CORRUPTED;
+
+    if (!mContext) {
+        // just ignore if context is not even created
+        return C2_OK;
+    }
+
+    uint32_t bufferSize = mStride * mHeight * 3 / 2;
+    mOutBufferFlush = (uint8_t *)ivd_aligned_malloc(nullptr, 128, bufferSize);
+    if (!mOutBufferFlush) {
+        ALOGE("could not allocate tmp output buffer (for flush) of size %u ",
+              bufferSize);
+        return C2_NO_MEMORY;
+    }
+
+    while (true) {
+        mPts = 0;
+        setDecodeArgs(nullptr, nullptr, 0, 0, 0);
+        mImg = mContext->getImage();
+        if (mImg.data == nullptr) {
+            resetPlugin();
+            break;
+        }
+    }
+
+    if (mOutBufferFlush) {
+        ivd_aligned_free(nullptr, mOutBufferFlush);
+        mOutBufferFlush = nullptr;
+    }
+
+    deleteContext();
+    return C2_OK;
+}
+
+status_t C2GoldfishHevcDec::createDecoder() {
+
+    DDD("creating hevc context now w %d h %d", mWidth, mHeight);
+    if (mEnableAndroidNativeBuffers) {
+        mContext.reset(new MediaHevcDecoder(RenderMode::RENDER_BY_HOST_GPU));
+    } else {
+        mContext.reset(new MediaHevcDecoder(RenderMode::RENDER_BY_GUEST_CPU));
+    }
+    mContext->initHevcContext(mWidth, mHeight, mWidth, mHeight,
+                              MediaHevcDecoder::PixelFormat::YUV420P);
+
+    return OK;
+}
+
+status_t C2GoldfishHevcDec::setParams(size_t stride) {
+    (void)stride;
+    return OK;
+}
+
+status_t C2GoldfishHevcDec::initDecoder() {
+    //    if (OK != createDecoder()) return UNKNOWN_ERROR;
+    mStride = ALIGN2(mWidth);
+    mSignalledError = false;
+    resetPlugin();
+
+    return OK;
+}
+
+bool C2GoldfishHevcDec::setDecodeArgs(C2ReadView *inBuffer,
+                                     C2GraphicView *outBuffer, size_t inOffset,
+                                     size_t inSize, uint32_t tsMarker) {
+    uint32_t displayStride = mStride;
+    (void)inBuffer;
+    (void)inOffset;
+    (void)inSize;
+    (void)tsMarker;
+    if (outBuffer) {
+        C2PlanarLayout layout;
+        layout = outBuffer->layout();
+        displayStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+    }
+
+    if (inBuffer) {
+        //= tsMarker;
+        mInPBuffer = const_cast<uint8_t *>(inBuffer->data() + inOffset);
+        mInPBufferSize = inSize;
+        mInTsMarker = tsMarker;
+        insertPts(tsMarker, mPts);
+    }
+
+    // uint32_t displayHeight = mHeight;
+    // size_t lumaSize = displayStride * displayHeight;
+    // size_t chromaSize = lumaSize >> 2;
+
+    if (mStride != displayStride) {
+        mStride = displayStride;
+        if (OK != setParams(mStride))
+            return false;
+    }
+
+    return true;
+}
+
+status_t C2GoldfishHevcDec::setFlushMode() {
+    if (mContext) {
+        mContext->flush();
+    }
+    mHeaderDecoded = false;
+    return OK;
+}
+
+status_t C2GoldfishHevcDec::resetDecoder() {
+    mStride = 0;
+    mSignalledError = false;
+    mHeaderDecoded = false;
+    deleteContext();
+
+    return OK;
+}
+
+void C2GoldfishHevcDec::resetPlugin() {
+    mSignalledOutputEos = false;
+    gettimeofday(&mTimeStart, nullptr);
+    gettimeofday(&mTimeEnd, nullptr);
+}
+
+void C2GoldfishHevcDec::deleteContext() {
+    if (mContext) {
+        mContext->destroyHevcContext();
+        mContext.reset(nullptr);
+        mPts2Index.clear();
+        mOldPts2Index.clear();
+        mIndex2Pts.clear();
+    }
+}
+
+static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+    uint32_t flags = 0;
+    if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+        flags |= C2FrameData::FLAG_END_OF_STREAM;
+        DDD("signalling eos");
+    }
+    DDD("fill empty work");
+    work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->workletsProcessed = 1u;
+}
+
+void C2GoldfishHevcDec::finishWork(uint64_t index,
+                                  const std::unique_ptr<C2Work> &work) {
+    std::shared_ptr<C2Buffer> buffer =
+        createGraphicBuffer(std::move(mOutBlock), C2Rect(mWidth, mHeight));
+    mOutBlock = nullptr;
+    {
+        IntfImpl::Lock lock = mIntf->lock();
+        buffer->setInfo(mIntf->getColorAspects_l());
+    }
+
+    class FillWork {
+      public:
+        FillWork(uint32_t flags, C2WorkOrdinalStruct ordinal,
+                 const std::shared_ptr<C2Buffer> &buffer)
+            : mFlags(flags), mOrdinal(ordinal), mBuffer(buffer) {}
+        ~FillWork() = default;
+
+        void operator()(const std::unique_ptr<C2Work> &work) {
+            work->worklets.front()->output.flags = (C2FrameData::flags_t)mFlags;
+            work->worklets.front()->output.buffers.clear();
+            work->worklets.front()->output.ordinal = mOrdinal;
+            work->workletsProcessed = 1u;
+            work->result = C2_OK;
+            if (mBuffer) {
+                work->worklets.front()->output.buffers.push_back(mBuffer);
+            }
+            DDD("timestamp = %lld, index = %lld, w/%s buffer",
+                mOrdinal.timestamp.peekll(), mOrdinal.frameIndex.peekll(),
+                mBuffer ? "" : "o");
+        }
+
+      private:
+        const uint32_t mFlags;
+        const C2WorkOrdinalStruct mOrdinal;
+        const std::shared_ptr<C2Buffer> mBuffer;
+    };
+
+    auto fillWork = [buffer](const std::unique_ptr<C2Work> &work) {
+        work->worklets.front()->output.flags = (C2FrameData::flags_t)0;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.buffers.push_back(buffer);
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        work->workletsProcessed = 1u;
+    };
+    if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
+        bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+        // TODO: Check if cloneAndSend can be avoided by tracking number of
+        // frames remaining
+        if (eos) {
+            if (buffer) {
+                mOutIndex = index;
+                C2WorkOrdinalStruct outOrdinal = work->input.ordinal;
+                DDD("%s %d: cloneAndSend ", __func__, __LINE__);
+                cloneAndSend(
+                    mOutIndex, work,
+                    FillWork(C2FrameData::FLAG_INCOMPLETE, outOrdinal, buffer));
+                buffer.reset();
+            }
+        } else {
+            DDD("%s %d: fill", __func__, __LINE__);
+            fillWork(work);
+        }
+    } else {
+        DDD("%s %d: finish", __func__, __LINE__);
+        finish(index, fillWork);
+    }
+}
+
+c2_status_t
+C2GoldfishHevcDec::ensureDecoderState(const std::shared_ptr<C2BlockPool> &pool) {
+    if (mOutBlock && (mOutBlock->width() != ALIGN2(mWidth) ||
+                      mOutBlock->height() != mHeight)) {
+        mOutBlock.reset();
+    }
+    if (!mOutBlock) {
+        uint32_t format = HAL_PIXEL_FORMAT_YCBCR_420_888;
+        C2MemoryUsage usage = {C2MemoryUsage::CPU_READ,
+                               C2MemoryUsage::CPU_WRITE};
+        usage.expected = (uint64_t)(BufferUsage::VIDEO_DECODER);
+        // C2MemoryUsage usage = {(unsigned
+        // int)(BufferUsage::GPU_DATA_BUFFER)};// { C2MemoryUsage::CPU_READ,
+        // C2MemoryUsage::CPU_WRITE };
+        c2_status_t err = pool->fetchGraphicBlock(ALIGN2(mWidth), mHeight,
+                                                  format, usage, &mOutBlock);
+        if (err != C2_OK) {
+            ALOGE("fetchGraphicBlock for Output failed with status %d", err);
+            return err;
+        }
+        if (mEnableAndroidNativeBuffers) {
+            auto c2Handle = mOutBlock->handle();
+            native_handle_t *grallocHandle =
+                UnwrapNativeCodec2GrallocHandle(c2Handle);
+            mHostColorBufferId = getColorBufferHandle(grallocHandle);
+            DDD("found handle %d", mHostColorBufferId);
+        }
+        DDD("provided (%dx%d) required (%dx%d)", mOutBlock->width(),
+            mOutBlock->height(), ALIGN2(mWidth), mHeight);
+    }
+
+    return C2_OK;
+}
+
+void C2GoldfishHevcDec::checkMode(const std::shared_ptr<C2BlockPool> &pool) {
+    mWidth = mIntf->width();
+    mHeight = mIntf->height();
+    const bool isGraphic = (pool->getAllocatorId() & C2Allocator::GRAPHIC);
+    DDD("buffer id %d", (int)(pool->getAllocatorId()));
+    if (isGraphic) {
+        DDD("decoding to host color buffer");
+        mEnableAndroidNativeBuffers = true;
+    } else {
+        DDD("decoding to guest byte buffer");
+        mEnableAndroidNativeBuffers = false;
+    }
+}
+
+void C2GoldfishHevcDec::getVuiParams(hevc_image_t &img) {
+
+    VuiColorAspects vuiColorAspects;
+    vuiColorAspects.primaries = img.color_primaries;
+    vuiColorAspects.transfer = img.color_trc;
+    vuiColorAspects.coeffs = img.colorspace;
+    vuiColorAspects.fullRange = img.color_range == 2 ? true : false;
+
+    // convert vui aspects to C2 values if changed
+    if (!(vuiColorAspects == mBitstreamColorAspects)) {
+        mBitstreamColorAspects = vuiColorAspects;
+        ColorAspects sfAspects;
+        C2StreamColorAspectsInfo::input codedAspects = {0u};
+        ColorUtils::convertIsoColorAspectsToCodecAspects(
+            vuiColorAspects.primaries, vuiColorAspects.transfer,
+            vuiColorAspects.coeffs, vuiColorAspects.fullRange, sfAspects);
+        if (!C2Mapper::map(sfAspects.mPrimaries, &codedAspects.primaries)) {
+            codedAspects.primaries = C2Color::PRIMARIES_UNSPECIFIED;
+        }
+        if (!C2Mapper::map(sfAspects.mRange, &codedAspects.range)) {
+            codedAspects.range = C2Color::RANGE_UNSPECIFIED;
+        }
+        if (!C2Mapper::map(sfAspects.mMatrixCoeffs, &codedAspects.matrix)) {
+            codedAspects.matrix = C2Color::MATRIX_UNSPECIFIED;
+        }
+        if (!C2Mapper::map(sfAspects.mTransfer, &codedAspects.transfer)) {
+            codedAspects.transfer = C2Color::TRANSFER_UNSPECIFIED;
+        }
+        std::vector<std::unique_ptr<C2SettingResult>> failures;
+        (void)mIntf->config({&codedAspects}, C2_MAY_BLOCK, &failures);
+    }
+}
+
+void C2GoldfishHevcDec::copyImageData(hevc_image_t &img) {
+    getVuiParams(img);
+    if (mEnableAndroidNativeBuffers)
+        return;
+
+    auto writeView = mOutBlock->map().get();
+    if (writeView.error()) {
+        ALOGE("graphic view map failed %d", writeView.error());
+        return;
+    }
+    size_t dstYStride = writeView.layout().planes[C2PlanarLayout::PLANE_Y].rowInc;
+    size_t dstUVStride = writeView.layout().planes[C2PlanarLayout::PLANE_U].rowInc;
+
+    uint8_t *pYBuffer = const_cast<uint8_t *>(writeView.data()[C2PlanarLayout::PLANE_Y]);
+    uint8_t *pUBuffer = const_cast<uint8_t *>(writeView.data()[C2PlanarLayout::PLANE_U]);
+    uint8_t *pVBuffer = const_cast<uint8_t *>(writeView.data()[C2PlanarLayout::PLANE_V]);
+
+    for (int i = 0; i < mHeight; ++i) {
+        memcpy(pYBuffer + i * dstYStride, img.data + i * mWidth, mWidth);
+    }
+    for (int i = 0; i < mHeight / 2; ++i) {
+        memcpy(pUBuffer + i * dstUVStride,
+               img.data + mWidth * mHeight + i * mWidth / 2, mWidth / 2);
+    }
+    for (int i = 0; i < mHeight / 2; ++i) {
+        memcpy(pVBuffer + i * dstUVStride,
+               img.data + mWidth * mHeight * 5 / 4 + i * mWidth / 2,
+               mWidth / 2);
+    }
+}
+
+uint64_t C2GoldfishHevcDec::getWorkIndex(uint64_t pts) {
+    if (!mOldPts2Index.empty()) {
+        auto iter = mOldPts2Index.find(pts);
+        if (iter != mOldPts2Index.end()) {
+            auto index = iter->second;
+            DDD("found index %d for pts %" PRIu64, (int)index, pts);
+            return index;
+        }
+    }
+    auto iter = mPts2Index.find(pts);
+    if (iter != mPts2Index.end()) {
+        auto index = iter->second;
+        DDD("found index %d for pts %" PRIu64, (int)index, pts);
+        return index;
+    }
+    DDD("not found index for pts %" PRIu64, pts);
+    return 0;
+}
+
+void C2GoldfishHevcDec::insertPts(uint32_t work_index, uint64_t pts) {
+    auto iter = mPts2Index.find(pts);
+    if (iter != mPts2Index.end()) {
+        // we have a collision here:
+        // apparently, older session is not done yet,
+        // lets save them
+        DDD("inserted to old pts %" PRIu64 " with index %d", pts, (int)iter->second);
+        mOldPts2Index[iter->first] = iter->second;
+    }
+    DDD("inserted pts %" PRIu64 " with index %d", pts, (int)work_index);
+    mIndex2Pts[work_index] = pts;
+    mPts2Index[pts] = work_index;
+}
+
+void C2GoldfishHevcDec::removePts(uint64_t pts) {
+    bool found = false;
+    uint64_t index = 0;
+    // note: check old pts first to see
+    // if we have some left over, check them
+    if (!mOldPts2Index.empty()) {
+        auto iter = mOldPts2Index.find(pts);
+        if (iter != mOldPts2Index.end()) {
+            mOldPts2Index.erase(iter);
+            index = iter->second;
+            found = true;
+        }
+    } else {
+        auto iter = mPts2Index.find(pts);
+        if (iter != mPts2Index.end()) {
+            mPts2Index.erase(iter);
+            index = iter->second;
+            found = true;
+        }
+    }
+
+    if (!found) return;
+
+    auto iter2 = mIndex2Pts.find(index);
+    if (iter2 == mIndex2Pts.end()) return;
+    mIndex2Pts.erase(iter2);
+}
+
+// TODO: can overall error checking be improved?
+// TODO: allow configuration of color format and usage for graphic buffers
+// instead
+//       of hard coding them to HAL_PIXEL_FORMAT_YV12
+// TODO: pass coloraspects information to surface
+// TODO: test support for dynamic change in resolution
+// TODO: verify if the decoder sent back all frames
+void C2GoldfishHevcDec::process(const std::unique_ptr<C2Work> &work,
+                               const std::shared_ptr<C2BlockPool> &pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 0u;
+    work->worklets.front()->output.flags = work->input.flags;
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    DDD("process work");
+    if (!mContext) {
+        DDD("creating decoder context to host in process work");
+        checkMode(pool);
+        createDecoder();
+        decodeHeaderAfterFlush();
+    }
+
+    size_t inOffset = 0u;
+    size_t inSize = 0u;
+    uint32_t workIndex = work->input.ordinal.frameIndex.peeku() & 0xFFFFFFFF;
+    mPts = work->input.ordinal.timestamp.peeku();
+    C2ReadView rView = mDummyReadView;
+    if (!work->input.buffers.empty()) {
+        rView =
+            work->input.buffers[0]->data().linearBlocks().front().map().get();
+        inSize = rView.capacity();
+        if (inSize && rView.error()) {
+            ALOGE("read view map failed %d", rView.error());
+            work->result = rView.error();
+            return;
+        }
+    }
+    bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+    bool hasPicture = (inSize > 0);
+
+    DDD("in buffer attr. size %zu timestamp %d frameindex %d, flags %x", inSize,
+        (int)work->input.ordinal.timestamp.peeku(),
+        (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
+    size_t inPos = 0;
+    while (inPos < inSize) {
+        if (C2_OK != ensureDecoderState(pool)) {
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+
+        {
+            // C2GraphicView wView;// = mOutBlock->map().get();
+            // if (wView.error()) {
+            //    ALOGE("graphic view map failed %d", wView.error());
+            //    work->result = wView.error();
+            //    return;
+            //}
+            if (!setDecodeArgs(&rView, nullptr, inOffset + inPos,
+                               inSize - inPos, workIndex)) {
+                mSignalledError = true;
+                work->workletsProcessed = 1u;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+
+            DDD("flag is %x", work->input.flags);
+            if (work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) {
+                hasPicture = false;
+                if (mCsd0.empty()) {
+                    mCsd0.assign(mInPBuffer, mInPBuffer + mInPBufferSize);
+                    DDD("assign to csd0 with %d bytpes", mInPBufferSize);
+                } else if (mCsd1.empty()) {
+                    mCsd1.assign(mInPBuffer, mInPBuffer + mInPBufferSize);
+                    DDD("assign to csd1 with %d bytpes", mInPBufferSize);
+                }
+                // this is not really a valid pts from config
+                removePts(mPts);
+            }
+
+            bool whChanged = false;
+            if (GoldfishHevcHelper::isVpsFrame(mInPBuffer, mInPBufferSize)) {
+                mHevcHelper.reset(new GoldfishHevcHelper(mWidth, mHeight));
+                whChanged = mHevcHelper->decodeHeader(mInPBuffer, mInPBufferSize);
+                if (whChanged) {
+                        DDD("w changed from old %d to new %d\n", mWidth, mHevcHelper->getWidth());
+                        DDD("h changed from old %d to new %d\n", mHeight, mHevcHelper->getHeight());
+                        if (1) {
+                            drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work);
+                            resetDecoder();
+                            resetPlugin();
+                            work->workletsProcessed = 0u;
+                        }
+                        {
+                            mWidth = mHevcHelper->getWidth();
+                            mHeight = mHevcHelper->getHeight();
+                            C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight);
+                            std::vector<std::unique_ptr<C2SettingResult>> failures;
+                            c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures);
+                            if (err == OK) {
+                                work->worklets.front()->output.configUpdate.push_back(
+                                        C2Param::Copy(size));
+                                ensureDecoderState(pool);
+                            } else {
+                                ALOGE("Cannot set width and height");
+                                mSignalledError = true;
+                                work->workletsProcessed = 1u;
+                                work->result = C2_CORRUPTED;
+                                return;
+                            }
+                        }
+                        if (!mContext) {
+                            DDD("creating decoder context to host in process work");
+                            checkMode(pool);
+                            createDecoder();
+                        }
+                        continue;//return;
+                } // end of whChanged
+            } // end of isVpsFrame
+
+            uint32_t delay;
+            GETTIME(&mTimeStart, nullptr);
+            TIME_DIFF(mTimeEnd, mTimeStart, delay);
+            (void)delay;
+            //(void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
+            DDD("decoding");
+            hevc_result_t hevcRes =
+                mContext->decodeFrame(mInPBuffer, mInPBufferSize, mIndex2Pts[mInTsMarker]);
+            mConsumedBytes = hevcRes.bytesProcessed;
+            DDD("decoding consumed %d", (int)mConsumedBytes);
+
+            if (mHostColorBufferId > 0) {
+                mImg = mContext->renderOnHostAndReturnImageMetadata(
+                    mHostColorBufferId);
+            } else {
+                mImg = mContext->getImage();
+            }
+            uint32_t decodeTime;
+            GETTIME(&mTimeEnd, nullptr);
+            TIME_DIFF(mTimeStart, mTimeEnd, decodeTime);
+            (void)decodeTime;
+        }
+        if (mImg.data != nullptr) {
+            DDD("got data %" PRIu64 " with pts %" PRIu64,  getWorkIndex(mImg.pts), mImg.pts);
+            mHeaderDecoded = true;
+            copyImageData(mImg);
+            finishWork(getWorkIndex(mImg.pts), work);
+            removePts(mImg.pts);
+        } else {
+            work->workletsProcessed = 0u;
+        }
+
+        inPos += mConsumedBytes;
+    }
+    if (eos) {
+        DDD("drain because of eos");
+        drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
+        mSignalledOutputEos = true;
+    } else if (!hasPicture) {
+        DDD("no picture, fill empty work");
+        fillEmptyWork(work);
+    }
+
+    work->input.buffers.clear();
+}
+
+c2_status_t
+C2GoldfishHevcDec::drainInternal(uint32_t drainMode,
+                                const std::shared_ptr<C2BlockPool> &pool,
+                                const std::unique_ptr<C2Work> &work) {
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    if (OK != setFlushMode())
+        return C2_CORRUPTED;
+    while (true) {
+        if (C2_OK != ensureDecoderState(pool)) {
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            work->result = C2_CORRUPTED;
+            return C2_CORRUPTED;
+        }
+        /*
+        C2GraphicView wView = mOutBlock->map().get();
+        if (wView.error()) {
+            ALOGE("graphic view map failed %d", wView.error());
+            return C2_CORRUPTED;
+        }
+        if (!setDecodeArgs(nullptr, &wView, 0, 0, 0)) {
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            return C2_CORRUPTED;
+        }
+        */
+
+        if (mHostColorBufferId > 0) {
+            mImg = mContext->renderOnHostAndReturnImageMetadata(
+                mHostColorBufferId);
+        } else {
+            mImg = mContext->getImage();
+        }
+
+        // TODO: maybe keep rendering to screen
+        //        mImg = mContext->getImage();
+        if (mImg.data != nullptr) {
+            DDD("got data in drain mode %" PRIu64 " with pts %" PRIu64,  getWorkIndex(mImg.pts), mImg.pts);
+            copyImageData(mImg);
+            finishWork(getWorkIndex(mImg.pts), work);
+            removePts(mImg.pts);
+        } else {
+            fillEmptyWork(work);
+            break;
+        }
+    }
+
+    return C2_OK;
+}
+
+c2_status_t C2GoldfishHevcDec::drain(uint32_t drainMode,
+                                    const std::shared_ptr<C2BlockPool> &pool) {
+    DDD("drainInternal because of drain");
+    return drainInternal(drainMode, pool, nullptr);
+}
+
+class C2GoldfishHevcDecFactory : public C2ComponentFactory {
+  public:
+    C2GoldfishHevcDecFactory()
+        : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+              GoldfishComponentStore::Create()->getParamReflector())) {}
+
+    virtual c2_status_t
+    createComponent(c2_node_id_t id,
+                    std::shared_ptr<C2Component> *const component,
+                    std::function<void(C2Component *)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+            new C2GoldfishHevcDec(
+                COMPONENT_NAME, id,
+                std::make_shared<C2GoldfishHevcDec::IntfImpl>(mHelper)),
+            deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+        c2_node_id_t id, std::shared_ptr<C2ComponentInterface> *const interface,
+        std::function<void(C2ComponentInterface *)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+            new SimpleInterface<C2GoldfishHevcDec::IntfImpl>(
+                COMPONENT_NAME, id,
+                std::make_shared<C2GoldfishHevcDec::IntfImpl>(mHelper)),
+            deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2GoldfishHevcDecFactory() override = default;
+
+  private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+} // namespace android
+
+extern "C" ::C2ComponentFactory *CreateCodec2Factory() {
+    DDD("in %s", __func__);
+    return new ::android::C2GoldfishHevcDecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory *factory) {
+    DDD("in %s", __func__);
+    delete factory;
+}
diff --git a/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.h b/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.h
new file mode 100644
index 0000000..fe080cf
--- /dev/null
+++ b/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_C2_SOFT_HEVC_DEC_H_
+#define ANDROID_C2_SOFT_HEVC_DEC_H_
+
+#include <sys/time.h>
+
+#include <media/stagefright/foundation/ColorUtils.h>
+
+#include "MediaHevcDecoder.h"
+#include "GoldfishHevcHelper.h"
+#include <SimpleC2Component.h>
+#include <atomic>
+#include <map>
+
+namespace android {
+
+#define ALIGN2(x) ((((x) + 1) >> 1) << 1)
+#define ALIGN8(x) ((((x) + 7) >> 3) << 3)
+#define ALIGN16(x) ((((x) + 15) >> 4) << 4)
+#define ALIGN32(x) ((((x) + 31) >> 5) << 5)
+#define MAX_NUM_CORES 4
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define GETTIME(a, b) gettimeofday(a, b);
+#define TIME_DIFF(start, end, diff)                                            \
+    diff = (((end).tv_sec - (start).tv_sec) * 1000000) +                       \
+           ((end).tv_usec - (start).tv_usec);
+
+class C2GoldfishHevcDec : public SimpleC2Component {
+  public:
+    class IntfImpl;
+    C2GoldfishHevcDec(const char *name, c2_node_id_t id,
+                     const std::shared_ptr<IntfImpl> &intfImpl);
+    virtual ~C2GoldfishHevcDec();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(const std::unique_ptr<C2Work> &work,
+                 const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(uint32_t drainMode,
+                      const std::shared_ptr<C2BlockPool> &pool) override;
+
+  private:
+    std::unique_ptr<MediaHevcDecoder> mContext;
+    bool mEnableAndroidNativeBuffers{true};
+
+    void checkMode(const std::shared_ptr<C2BlockPool> &pool);
+    //    status_t createDecoder();
+    status_t createDecoder();
+    status_t setParams(size_t stride);
+    status_t initDecoder();
+    bool setDecodeArgs(C2ReadView *inBuffer, C2GraphicView *outBuffer,
+                       size_t inOffset, size_t inSize, uint32_t tsMarker);
+    c2_status_t ensureDecoderState(const std::shared_ptr<C2BlockPool> &pool);
+    void finishWork(uint64_t index, const std::unique_ptr<C2Work> &work);
+    status_t setFlushMode();
+    c2_status_t drainInternal(uint32_t drainMode,
+                              const std::shared_ptr<C2BlockPool> &pool,
+                              const std::unique_ptr<C2Work> &work);
+    status_t resetDecoder();
+    void resetPlugin();
+    void deleteContext();
+
+    std::shared_ptr<IntfImpl> mIntf;
+
+    void removePts(uint64_t pts);
+    void insertPts(uint32_t work_index, uint64_t pts);
+    uint64_t getWorkIndex(uint64_t pts);
+
+    // there are same pts matching to different work indices
+    // this happen during csd0/csd1 switching
+    std::map<uint64_t, uint64_t> mOldPts2Index;
+    std::map<uint64_t, uint64_t> mPts2Index;
+    std::map<uint64_t, uint64_t> mIndex2Pts;
+    uint64_t  mPts {0};
+
+    // TODO:This is not the right place for this enum. These should
+    // be part of c2-vndk so that they can be accessed by all video plugins
+    // until then, make them feel at home
+    enum {
+        kNotSupported,
+        kPreferBitstream,
+        kPreferContainer,
+    };
+
+    std::shared_ptr<C2GraphicBlock> mOutBlock;
+    uint8_t *mOutBufferFlush;
+
+    int mHostColorBufferId{-1};
+
+    void getVuiParams(hevc_image_t &img);
+    void copyImageData(hevc_image_t &img);
+
+    hevc_image_t mImg{};
+    uint32_t mConsumedBytes{0};
+    uint8_t *mInPBuffer{nullptr};
+    uint32_t mInPBufferSize;
+    uint32_t mInTsMarker;
+
+    // size_t mNumCores;
+    // uint32_t mOutputDelay;
+    uint32_t mWidth;
+    uint32_t mHeight;
+    uint32_t mStride;
+    bool mSignalledOutputEos;
+    bool mSignalledError;
+    bool mHeaderDecoded;
+    std::atomic_uint64_t mOutIndex;
+    // Color aspects. These are ISO values and are meant to detect changes in
+    // aspects to avoid converting them to C2 values for each frame
+    struct VuiColorAspects {
+        uint8_t primaries;
+        uint8_t transfer;
+        uint8_t coeffs;
+        uint8_t fullRange;
+
+        // default color aspects
+        VuiColorAspects()
+            : primaries(2), transfer(2), coeffs(2), fullRange(0) {}
+
+        bool operator==(const VuiColorAspects &o) {
+            return primaries == o.primaries && transfer == o.transfer &&
+                   coeffs == o.coeffs && fullRange == o.fullRange;
+        }
+    } mBitstreamColorAspects;
+
+    // profile
+    struct timeval mTimeStart;
+    struct timeval mTimeEnd;
+#ifdef FILE_DUMP_ENABLE
+    char mInFile[200];
+#endif /* FILE_DUMP_ENABLE */
+
+    std::vector<uint8_t> mCsd0;
+    std::vector<uint8_t> mCsd1;
+    void decodeHeaderAfterFlush();
+
+    std::unique_ptr<GoldfishHevcHelper> mHevcHelper;
+
+    C2_DO_NOT_COPY(C2GoldfishHevcDec);
+};
+
+} // namespace android
+
+#endif // ANDROID_C2_SOFT_HEVC_DEC_H_
diff --git a/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.cpp b/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.cpp
new file mode 100644
index 0000000..1b93a0d
--- /dev/null
+++ b/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.cpp
@@ -0,0 +1,315 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#include "GoldfishHevcHelper.h"
+
+#define LOG_TAG "GoldfishHevcHelper"
+#include <log/log.h>
+
+#include "ihevc_typedefs.h"
+#include "ihevcd_cxa.h"
+
+#define DEBUG 0
+#if DEBUG
+#define DDD(...) ALOGD(__VA_ARGS__)
+#else
+#define DDD(...) ((void)0)
+#endif
+
+
+#include <Codec2Mapper.h>
+
+#define ivdec_api_function ihevcd_cxa_api_function
+#define ivdext_create_ip_t ihevcd_cxa_create_ip_t
+#define ivdext_create_op_t ihevcd_cxa_create_op_t
+#define ivdext_delete_ip_t ihevcd_cxa_delete_ip_t
+#define ivdext_delete_op_t ihevcd_cxa_delete_op_t
+#define ivdext_ctl_set_num_cores_ip_t ihevcd_cxa_ctl_set_num_cores_ip_t
+#define ivdext_ctl_set_num_cores_op_t ihevcd_cxa_ctl_set_num_cores_op_t
+#define ivdext_ctl_get_vui_params_ip_t ihevcd_cxa_ctl_get_vui_params_ip_t
+#define ivdext_ctl_get_vui_params_op_t ihevcd_cxa_ctl_get_vui_params_op_t
+#define ALIGN128(x) ((((x) + 127) >> 7) << 7)
+#define MAX_NUM_CORES 4
+#define IVDEXT_CMD_CTL_SET_NUM_CORES                                           \
+    (IVD_CONTROL_API_COMMAND_TYPE_T) IHEVCD_CXA_CMD_CTL_SET_NUM_CORES
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+namespace android {
+
+static void *ivd_aligned_malloc(void *ctxt, WORD32 alignment, WORD32 size) {
+    (void) ctxt;
+    return memalign(alignment, size);
+}
+
+static void ivd_aligned_free(void *ctxt, void *mem) {
+    (void) ctxt;
+    free(mem);
+}
+
+
+GoldfishHevcHelper::GoldfishHevcHelper(int w, int h):mWidth(w),mHeight(h) { createDecoder(); }
+
+GoldfishHevcHelper::~GoldfishHevcHelper() {
+    destroyDecoder();
+}
+
+void GoldfishHevcHelper::createDecoder() {
+    ivdext_create_ip_t s_create_ip = {};
+    ivdext_create_op_t s_create_op = {};
+
+    s_create_ip.s_ivd_create_ip_t.u4_size = sizeof(ivdext_create_ip_t);
+    s_create_ip.s_ivd_create_ip_t.e_cmd = IVD_CMD_CREATE;
+    s_create_ip.s_ivd_create_ip_t.u4_share_disp_buf = 0;
+    s_create_ip.s_ivd_create_ip_t.e_output_format = mIvColorformat;
+    s_create_ip.s_ivd_create_ip_t.pf_aligned_alloc = ivd_aligned_malloc;
+    s_create_ip.s_ivd_create_ip_t.pf_aligned_free = ivd_aligned_free;
+    s_create_ip.s_ivd_create_ip_t.pv_mem_ctxt = nullptr;
+    s_create_op.s_ivd_create_op_t.u4_size = sizeof(ivdext_create_op_t);
+    IV_API_CALL_STATUS_T status =
+        ivdec_api_function(mDecHandle, &s_create_ip, &s_create_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("error in %s: 0x%x", __func__,
+              s_create_op.s_ivd_create_op_t.u4_error_code);
+        return;
+    }
+    mDecHandle = (iv_obj_t *)s_create_op.s_ivd_create_op_t.pv_handle;
+    mDecHandle->pv_fxns = (void *)ivdec_api_function;
+    mDecHandle->u4_size = sizeof(iv_obj_t);
+
+    mStride = ALIGN128(mWidth);
+
+    setNumCores();
+}
+
+void GoldfishHevcHelper::destroyDecoder() {
+    if (mDecHandle) {
+        ivdext_delete_ip_t s_delete_ip = {};
+        ivdext_delete_op_t s_delete_op = {};
+
+        s_delete_ip.s_ivd_delete_ip_t.u4_size = sizeof(ivdext_delete_ip_t);
+        s_delete_ip.s_ivd_delete_ip_t.e_cmd = IVD_CMD_DELETE;
+        s_delete_op.s_ivd_delete_op_t.u4_size = sizeof(ivdext_delete_op_t);
+        IV_API_CALL_STATUS_T status =
+            ivdec_api_function(mDecHandle, &s_delete_ip, &s_delete_op);
+        if (status != IV_SUCCESS) {
+            ALOGE("error in %s: 0x%x", __func__,
+                  s_delete_op.s_ivd_delete_op_t.u4_error_code);
+        }
+        mDecHandle = nullptr;
+    }
+}
+
+void GoldfishHevcHelper::setNumCores() {
+    ivdext_ctl_set_num_cores_ip_t s_set_num_cores_ip = {};
+    ivdext_ctl_set_num_cores_op_t s_set_num_cores_op = {};
+
+    s_set_num_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t);
+    s_set_num_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_set_num_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES;
+    s_set_num_cores_ip.u4_num_cores = mNumCores;
+    s_set_num_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(
+        mDecHandle, &s_set_num_cores_ip, &s_set_num_cores_op);
+    if (IV_SUCCESS != status) {
+        DDD("error in %s: 0x%x", __func__, s_set_num_cores_op.u4_error_code);
+    }
+}
+
+void GoldfishHevcHelper::resetDecoder() {
+    ivd_ctl_reset_ip_t s_reset_ip = {};
+    ivd_ctl_reset_op_t s_reset_op = {};
+
+    s_reset_ip.u4_size = sizeof(ivd_ctl_reset_ip_t);
+    s_reset_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_reset_ip.e_sub_cmd = IVD_CMD_CTL_RESET;
+    s_reset_op.u4_size = sizeof(ivd_ctl_reset_op_t);
+    IV_API_CALL_STATUS_T status =
+        ivdec_api_function(mDecHandle, &s_reset_ip, &s_reset_op);
+    if (IV_SUCCESS != status) {
+        ALOGE("error in %s: 0x%x", __func__, s_reset_op.u4_error_code);
+    }
+    setNumCores();
+}
+
+void GoldfishHevcHelper::setParams(size_t stride,
+                                   IVD_VIDEO_DECODE_MODE_T dec_mode) {
+    ihevcd_cxa_ctl_set_config_ip_t s_hevcd_set_dyn_params_ip = {};
+    ihevcd_cxa_ctl_set_config_op_t s_hevcd_set_dyn_params_op = {};
+    ivd_ctl_set_config_ip_t *ps_set_dyn_params_ip =
+        &s_hevcd_set_dyn_params_ip.s_ivd_ctl_set_config_ip_t;
+    ivd_ctl_set_config_op_t *ps_set_dyn_params_op =
+        &s_hevcd_set_dyn_params_op.s_ivd_ctl_set_config_op_t;
+
+    ps_set_dyn_params_ip->u4_size = sizeof(ihevcd_cxa_ctl_set_config_ip_t);
+    ps_set_dyn_params_ip->e_cmd = IVD_CMD_VIDEO_CTL;
+    ps_set_dyn_params_ip->e_sub_cmd = IVD_CMD_CTL_SETPARAMS;
+    ps_set_dyn_params_ip->u4_disp_wd = (UWORD32)stride;
+    ps_set_dyn_params_ip->e_frm_skip_mode = IVD_SKIP_NONE;
+    ps_set_dyn_params_ip->e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
+    ps_set_dyn_params_ip->e_vid_dec_mode = dec_mode;
+    ps_set_dyn_params_op->u4_size = sizeof(ihevcd_cxa_ctl_set_config_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(
+        mDecHandle, ps_set_dyn_params_ip, ps_set_dyn_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("error in %s: 0x%x", __func__,
+              ps_set_dyn_params_op->u4_error_code);
+    }
+}
+
+bool GoldfishHevcHelper::isVpsFrame(const uint8_t* frame, int inSize) {
+    if (inSize < 5) return false;
+    if (frame[0] == 0 && frame[1] == 0 && frame[2] == 0 && frame[3] == 1) {
+        const bool forbiddenBitIsInvalid = 0x80 & frame[4];
+        if (forbiddenBitIsInvalid) {
+            return false;
+        }
+        // nalu type is the lower 6 bits after shiftting to right 1 bit
+        uint8_t naluType = 0x3f & (frame[4] >> 1);
+        if (naluType == 32
+            || naluType == 33
+            || naluType == 34
+                ) return true;
+        else return false;
+    } else {
+        return false;
+    }
+}
+
+bool GoldfishHevcHelper::decodeHeader(const uint8_t *frame, int inSize) {
+    // should we check the header for vps/sps/pps frame ? otherwise
+    // there is no point calling decoder
+    if (!isVpsFrame(frame, inSize)) {
+        DDD("could not find valid vps frame");
+        return false;
+    } else {
+        DDD("found valid vps frame");
+    }
+
+    ihevcd_cxa_video_decode_ip_t s_hevcd_decode_ip = {};
+    ihevcd_cxa_video_decode_op_t s_hevcd_decode_op = {};
+    ivd_video_decode_ip_t *ps_decode_ip =
+        &s_hevcd_decode_ip.s_ivd_video_decode_ip_t;
+    ivd_video_decode_op_t *ps_decode_op =
+        &s_hevcd_decode_op.s_ivd_video_decode_op_t;
+
+    // setup input/output arguments to decoder
+    setDecodeArgs(ps_decode_ip, ps_decode_op, frame, mStride,
+            0, // offset
+            inSize, // size
+            0 // time-stamp, does not matter
+            );
+
+    setParams(mStride, IVD_DECODE_HEADER);
+
+    // now kick off the decoding
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle, ps_decode_ip, ps_decode_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("failed to call decoder function for header\n");
+        ALOGE("error in %s: 0x%x", __func__,
+              ps_decode_op->u4_error_code);
+    }
+
+    if (IVD_RES_CHANGED == (ps_decode_op->u4_error_code & IVD_ERROR_MASK)) {
+        DDD("resolution changed, reset decoder");
+        resetDecoder();
+        setParams(mStride, IVD_DECODE_HEADER);
+        ivdec_api_function(mDecHandle, ps_decode_ip, ps_decode_op);
+    }
+
+    // get the w/h and update
+    if (0 < ps_decode_op->u4_pic_wd && 0 < ps_decode_op->u4_pic_ht) {
+        DDD("success decode w/h %d %d", ps_decode_op->u4_pic_wd , ps_decode_op->u4_pic_ht);
+        DDD("existing w/h %d %d", mWidth, mHeight);
+        if (ps_decode_op->u4_pic_wd != mWidth ||  ps_decode_op->u4_pic_ht != mHeight) {
+            mWidth = ps_decode_op->u4_pic_wd;
+            mHeight = ps_decode_op->u4_pic_ht;
+            return true;
+        } else {
+            DDD("success decode w/h, but they are the same %d %d", ps_decode_op->u4_pic_wd , ps_decode_op->u4_pic_ht);
+        }
+    } else {
+        ALOGE("could not decode w/h");
+    }
+
+    // get output delay
+    if (ps_decode_op->i4_reorder_depth >= 0) {
+        if (mOutputDelay != ps_decode_op->i4_reorder_depth) {
+            mOutputDelay = ps_decode_op->i4_reorder_depth;
+            DDD("New Output delay %d ", mOutputDelay);
+        } else {
+            DDD("same Output delay %d ", mOutputDelay);
+        }
+    }
+
+    return false;
+}
+
+bool GoldfishHevcHelper::setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip,
+                                       ivd_video_decode_op_t *ps_decode_op,
+                                       const uint8_t *inBuffer,
+                                       uint32_t displayStride, size_t inOffset,
+                                       size_t inSize, uint32_t tsMarker) {
+    uint32_t displayHeight = mHeight;
+    size_t lumaSize = displayStride * displayHeight;
+    size_t chromaSize = lumaSize >> 2;
+
+    if (mStride != displayStride) {
+        mStride = displayStride;
+    }
+
+    // force decoder to always decode header and get dimensions,
+    // hope this will be quick and cheap
+    setParams(mStride, IVD_DECODE_HEADER);
+
+    ps_decode_ip->u4_size = sizeof(ihevcd_cxa_video_decode_ip_t);
+    ps_decode_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
+    if (inBuffer) {
+        ps_decode_ip->u4_ts = tsMarker;
+        ps_decode_ip->pv_stream_buffer = const_cast<uint8_t *>(inBuffer) + inOffset;
+        ps_decode_ip->u4_num_Bytes = inSize;
+    } else {
+        ps_decode_ip->u4_ts = 0;
+        ps_decode_ip->pv_stream_buffer = nullptr;
+        ps_decode_ip->u4_num_Bytes = 0;
+    }
+    DDD("setting pv_stream_buffer 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
+            ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[0],
+            ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[1],
+            ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[2],
+            ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[3],
+            ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[4],
+            ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[5],
+            ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[6],
+            ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[7]
+            );
+    DDD("input bytes %d", ps_decode_ip->u4_num_Bytes);
+
+    ps_decode_ip->s_out_buffer.u4_min_out_buf_size[0] = lumaSize;
+    ps_decode_ip->s_out_buffer.u4_min_out_buf_size[1] = chromaSize;
+    ps_decode_ip->s_out_buffer.u4_min_out_buf_size[2] = chromaSize;
+    {
+        ps_decode_ip->s_out_buffer.pu1_bufs[0] = nullptr;
+        ps_decode_ip->s_out_buffer.pu1_bufs[1] = nullptr;
+        ps_decode_ip->s_out_buffer.pu1_bufs[2] = nullptr;
+    }
+    ps_decode_ip->s_out_buffer.u4_num_bufs = 3;
+    ps_decode_op->u4_size = sizeof(ihevcd_cxa_video_decode_op_t);
+    ps_decode_op->u4_output_present = 0;
+
+    return true;
+}
+
+} // namespace android
diff --git a/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.h b/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.h
new file mode 100644
index 0000000..09de830
--- /dev/null
+++ b/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef GOLDFISH_HEVC_HELPER_H_
+#define GOLDFISH_HEVC_HELPER_H_
+
+#include <inttypes.h>
+#include "ihevc_typedefs.h"
+#include "ihevcd_cxa.h"
+
+
+namespace android {
+
+// this class is just to provide some functions to decode header
+// so that we know w/h of each sps
+class GoldfishHevcHelper {
+  public:
+    GoldfishHevcHelper(int w, int h);
+    ~GoldfishHevcHelper();
+
+    // check whether the frame is vps; typical hevc will have
+    // a frame that is vps/sps/pps together
+    static bool isVpsFrame(const uint8_t* frame, int inSize);
+  public:
+    // return true if decoding finds out w/h changed;
+    // otherwise false
+    bool decodeHeader(const uint8_t *frame, int inSize);
+    int getWidth() const { return mWidth; }
+    int getHeight() const { return mHeight; }
+
+  private:
+    void createDecoder();
+    void destroyDecoder();
+    void resetDecoder();
+    void setNumCores();
+    void setParams(size_t stride, IVD_VIDEO_DECODE_MODE_T dec_mode);
+    bool setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip,
+                       ivd_video_decode_op_t *ps_decode_op,
+                       const uint8_t *inBuffer, uint32_t displayStride,
+                       size_t inOffset, size_t inSize, uint32_t tsMarker);
+
+  private:
+    iv_obj_t *mDecHandle = nullptr;
+    int mWidth = 320;
+    int mHeight = 240;
+    int mNumCores = 1;
+    int mStride = 16;
+    int mOutputDelay = 8; // default
+    IV_COLOR_FORMAT_T mIvColorformat = IV_YUV_420P;
+};
+
+} // namespace android
+#endif
diff --git a/system/codecs/c2/decoders/hevcdec/MediaHevcDecoder.cpp b/system/codecs/c2/decoders/hevcdec/MediaHevcDecoder.cpp
new file mode 100644
index 0000000..bb2fbfa
--- /dev/null
+++ b/system/codecs/c2/decoders/hevcdec/MediaHevcDecoder.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#include <utils/Log.h>
+
+#define DEBUG 0
+#if DEBUG
+#define DDD(...) ALOGD(__VA_ARGS__)
+#else
+#define DDD(...) ((void)0)
+#endif
+
+#include "MediaHevcDecoder.h"
+#include "goldfish_media_utils.h"
+#include <string.h>
+
+MediaHevcDecoder::MediaHevcDecoder(RenderMode renderMode)
+    : mRenderMode(renderMode) {
+    if (renderMode == RenderMode::RENDER_BY_HOST_GPU) {
+        mVersion = 200;
+    } else if (renderMode == RenderMode::RENDER_BY_GUEST_CPU) {
+        mVersion = 100;
+    }
+}
+
+void MediaHevcDecoder::initHevcContext(unsigned int width, unsigned int height,
+                                       unsigned int outWidth,
+                                       unsigned int outHeight,
+                                       PixelFormat pixFmt) {
+    auto transport = GoldfishMediaTransport::getInstance();
+    if (!mHasAddressSpaceMemory) {
+        int slot = transport->getMemorySlot();
+        if (slot < 0) {
+            ALOGE("ERROR: Failed to initHevcContext: cannot get memory slot");
+            return;
+        }
+        mSlot = slot;
+        mAddressOffSet = static_cast<unsigned int>(mSlot) * (1 << 20);
+        DDD("got memory lot %d addrr %x", mSlot, mAddressOffSet);
+        mHasAddressSpaceMemory = true;
+    }
+    transport->writeParam(mVersion, 0, mAddressOffSet);
+    transport->writeParam(width, 1, mAddressOffSet);
+    transport->writeParam(height, 2, mAddressOffSet);
+    transport->writeParam(outWidth, 3, mAddressOffSet);
+    transport->writeParam(outHeight, 4, mAddressOffSet);
+    transport->writeParam(static_cast<uint64_t>(pixFmt), 5, mAddressOffSet);
+    transport->sendOperation(MediaCodecType::HevcCodec,
+                             MediaOperation::InitContext, mAddressOffSet);
+    auto *retptr = transport->getReturnAddr(mAddressOffSet);
+    mHostHandle = *(uint64_t *)(retptr);
+    DDD("initHevcContext: got handle to host %lld", mHostHandle);
+}
+
+void MediaHevcDecoder::resetHevcContext(unsigned int width, unsigned int height,
+                                        unsigned int outWidth,
+                                        unsigned int outHeight,
+                                        PixelFormat pixFmt) {
+    auto transport = GoldfishMediaTransport::getInstance();
+    if (!mHasAddressSpaceMemory) {
+        ALOGE("%s no address space memory", __func__);
+        return;
+    }
+    transport->writeParam((uint64_t)mHostHandle, 0, mAddressOffSet);
+    transport->writeParam(width, 1, mAddressOffSet);
+    transport->writeParam(height, 2, mAddressOffSet);
+    transport->writeParam(outWidth, 3, mAddressOffSet);
+    transport->writeParam(outHeight, 4, mAddressOffSet);
+    transport->writeParam(static_cast<uint64_t>(pixFmt), 5, mAddressOffSet);
+    transport->sendOperation(MediaCodecType::HevcCodec, MediaOperation::Reset,
+                             mAddressOffSet);
+    DDD("resetHevcContext: done");
+}
+
+void MediaHevcDecoder::destroyHevcContext() {
+
+    DDD("return memory lot %d addrr %x", (int)(mAddressOffSet >> 23),
+        mAddressOffSet);
+    auto transport = GoldfishMediaTransport::getInstance();
+    transport->writeParam((uint64_t)mHostHandle, 0, mAddressOffSet);
+    transport->sendOperation(MediaCodecType::HevcCodec,
+                             MediaOperation::DestroyContext, mAddressOffSet);
+    transport->returnMemorySlot(mSlot);
+    mHasAddressSpaceMemory = false;
+}
+
+hevc_result_t MediaHevcDecoder::decodeFrame(uint8_t *img, size_t szBytes,
+                                            uint64_t pts) {
+    DDD("decode frame: use handle to host %lld", mHostHandle);
+    hevc_result_t res = {0, 0};
+    if (!mHasAddressSpaceMemory) {
+        ALOGE("%s no address space memory", __func__);
+        return res;
+    }
+    auto transport = GoldfishMediaTransport::getInstance();
+    uint8_t *hostSrc = transport->getInputAddr(mAddressOffSet);
+    if (img != nullptr && szBytes > 0) {
+        memcpy(hostSrc, img, szBytes);
+    }
+    transport->writeParam((uint64_t)mHostHandle, 0, mAddressOffSet);
+    transport->writeParam(transport->offsetOf((uint64_t)(hostSrc)) -
+                              mAddressOffSet,
+                          1, mAddressOffSet);
+    transport->writeParam((uint64_t)szBytes, 2, mAddressOffSet);
+    transport->writeParam((uint64_t)pts, 3, mAddressOffSet);
+    transport->sendOperation(MediaCodecType::HevcCodec,
+                             MediaOperation::DecodeImage, mAddressOffSet);
+
+    auto *retptr = transport->getReturnAddr(mAddressOffSet);
+    res.bytesProcessed = *(uint64_t *)(retptr);
+    res.ret = *(int *)(retptr + 8);
+
+    return res;
+}
+
+void MediaHevcDecoder::flush() {
+    if (!mHasAddressSpaceMemory) {
+        ALOGE("%s no address space memory", __func__);
+        return;
+    }
+    DDD("flush: use handle to host %lld", mHostHandle);
+    auto transport = GoldfishMediaTransport::getInstance();
+    transport->writeParam((uint64_t)mHostHandle, 0, mAddressOffSet);
+    transport->sendOperation(MediaCodecType::HevcCodec, MediaOperation::Flush,
+                             mAddressOffSet);
+}
+
+hevc_image_t MediaHevcDecoder::getImage() {
+    DDD("getImage: use handle to host %lld", mHostHandle);
+    hevc_image_t res{};
+    if (!mHasAddressSpaceMemory) {
+        ALOGE("%s no address space memory", __func__);
+        return res;
+    }
+    auto transport = GoldfishMediaTransport::getInstance();
+    uint8_t *dst = transport->getInputAddr(
+        mAddressOffSet); // Note: reuse the same addr for input and output
+    transport->writeParam((uint64_t)mHostHandle, 0, mAddressOffSet);
+    transport->writeParam(transport->offsetOf((uint64_t)(dst)) - mAddressOffSet,
+                          1, mAddressOffSet);
+    transport->writeParam(-1, 2, mAddressOffSet);
+    transport->sendOperation(MediaCodecType::HevcCodec,
+                             MediaOperation::GetImage, mAddressOffSet);
+    auto *retptr = transport->getReturnAddr(mAddressOffSet);
+    res.ret = *(int *)(retptr);
+    if (res.ret >= 0) {
+        res.data = dst;
+        res.width = *(uint32_t *)(retptr + 8);
+        res.height = *(uint32_t *)(retptr + 16);
+        res.pts = *(uint64_t *)(retptr + 24);
+        res.color_primaries = *(uint32_t *)(retptr + 32);
+        res.color_range = *(uint32_t *)(retptr + 40);
+        res.color_trc = *(uint32_t *)(retptr + 48);
+        res.colorspace = *(uint32_t *)(retptr + 56);
+    } else if (res.ret == (int)(Err::DecoderRestarted)) {
+        res.width = *(uint32_t *)(retptr + 8);
+        res.height = *(uint32_t *)(retptr + 16);
+    }
+    return res;
+}
+
+hevc_image_t
+MediaHevcDecoder::renderOnHostAndReturnImageMetadata(int hostColorBufferId) {
+    DDD("%s: use handle to host %lld", __func__, mHostHandle);
+    hevc_image_t res{};
+    if (hostColorBufferId < 0) {
+        ALOGE("%s negative color buffer id %d", __func__, hostColorBufferId);
+        return res;
+    }
+    DDD("%s send color buffer id %d", __func__, hostColorBufferId);
+    if (!mHasAddressSpaceMemory) {
+        ALOGE("%s no address space memory", __func__);
+        return res;
+    }
+    auto transport = GoldfishMediaTransport::getInstance();
+    uint8_t *dst = transport->getInputAddr(
+        mAddressOffSet); // Note: reuse the same addr for input and output
+    transport->writeParam((uint64_t)mHostHandle, 0, mAddressOffSet);
+    transport->writeParam(transport->offsetOf((uint64_t)(dst)) - mAddressOffSet,
+                          1, mAddressOffSet);
+    transport->writeParam((uint64_t)hostColorBufferId, 2, mAddressOffSet);
+    transport->sendOperation(MediaCodecType::HevcCodec,
+                             MediaOperation::GetImage, mAddressOffSet);
+    auto *retptr = transport->getReturnAddr(mAddressOffSet);
+    res.ret = *(int *)(retptr);
+    if (res.ret >= 0) {
+        res.data = dst; // note: the data could be junk
+        res.width = *(uint32_t *)(retptr + 8);
+        res.height = *(uint32_t *)(retptr + 16);
+        res.pts = *(uint64_t *)(retptr + 24);
+        res.color_primaries = *(uint32_t *)(retptr + 32);
+        res.color_range = *(uint32_t *)(retptr + 40);
+        res.color_trc = *(uint32_t *)(retptr + 48);
+        res.colorspace = *(uint32_t *)(retptr + 56);
+    } else if (res.ret == (int)(Err::DecoderRestarted)) {
+        res.width = *(uint32_t *)(retptr + 8);
+        res.height = *(uint32_t *)(retptr + 16);
+    }
+    return res;
+}
diff --git a/system/codecs/c2/decoders/hevcdec/MediaHevcDecoder.h b/system/codecs/c2/decoders/hevcdec/MediaHevcDecoder.h
new file mode 100644
index 0000000..8dfb0cf
--- /dev/null
+++ b/system/codecs/c2/decoders/hevcdec/MediaHevcDecoder.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef GOLDFISH_MEDIA_Hevc_DEC_H_
+#define GOLDFISH_MEDIA_Hevc_DEC_H_
+
+struct hevc_init_result_t {
+    uint64_t host_handle;
+    int ret;
+};
+
+struct hevc_result_t {
+    int ret;
+    uint64_t bytesProcessed;
+};
+
+struct hevc_image_t {
+    const uint8_t *data;
+    uint32_t width;
+    uint32_t height;
+    uint64_t pts; // presentation time stamp
+    uint64_t color_primaries;
+    uint64_t color_range;
+    uint64_t color_trc;
+    uint64_t colorspace;
+    // on success, |ret| will indicate the size of |data|.
+    // If failed, |ret| will contain some negative error code.
+    int ret;
+};
+
+enum class RenderMode {
+    RENDER_BY_HOST_GPU = 1,
+    RENDER_BY_GUEST_CPU = 2,
+};
+
+class MediaHevcDecoder {
+    uint64_t mHostHandle = 0;
+    uint32_t mVersion = 100;
+    RenderMode mRenderMode = RenderMode::RENDER_BY_GUEST_CPU;
+
+    bool mHasAddressSpaceMemory = false;
+    uint64_t mAddressOffSet = 0;
+    int mSlot = -1;
+
+  public:
+    MediaHevcDecoder(RenderMode renderMode);
+    virtual ~MediaHevcDecoder() = default;
+
+    enum class PixelFormat : uint8_t {
+        YUV420P = 0,
+        UYVY422 = 1,
+        BGRA8888 = 2,
+    };
+
+    enum class Err : int {
+        NoErr = 0,
+        NoDecodedFrame = -1,
+        InitContextFailed = -2,
+        DecoderRestarted = -3,
+        NALUIgnored = -4,
+    };
+
+    bool getAddressSpaceMemory();
+    void initHevcContext(unsigned int width, unsigned int height,
+                         unsigned int outWidth, unsigned int outHeight,
+                         PixelFormat pixFmt);
+    void resetHevcContext(unsigned int width, unsigned int height,
+                          unsigned int outWidth, unsigned int outHeight,
+                          PixelFormat pixFmt);
+    void destroyHevcContext();
+    hevc_result_t decodeFrame(uint8_t *img, size_t szBytes, uint64_t pts);
+    void flush();
+    // ask host to copy image data back to guest, with image metadata
+    // to guest as well
+    hevc_image_t getImage();
+    // ask host to render to hostColorBufferId, return only image metadata back
+    // to guest
+    hevc_image_t renderOnHostAndReturnImageMetadata(int hostColorBufferId);
+};
+#endif
diff --git a/system/codecs/c2/decoders/vpxdec/C2GoldfishVpxDec.cpp b/system/codecs/c2/decoders/vpxdec/C2GoldfishVpxDec.cpp
index 7df99ff..99f0469 100644
--- a/system/codecs/c2/decoders/vpxdec/C2GoldfishVpxDec.cpp
+++ b/system/codecs/c2/decoders/vpxdec/C2GoldfishVpxDec.cpp
@@ -52,7 +52,7 @@
 using ::android::hardware::graphics::common::V1_2::PixelFormat;
 
 namespace android {
-constexpr size_t kMinInputBufferSize = 2 * 1024 * 1024;
+constexpr size_t kMinInputBufferSize = 6 * 1024 * 1024;
 #ifdef VP9
 constexpr char COMPONENT_NAME[] = "c2.goldfish.vp9.decoder";
 #else
@@ -465,32 +465,14 @@
     mCtx = new vpx_codec_ctx_t;
     mCtx->vpversion = mMode == MODE_VP8 ? 8 : 9;
 
-    // check for decoding mode:
-    {
-        // now get the block
-        constexpr uint32_t format = HAL_PIXEL_FORMAT_YCBCR_420_888;
-        std::shared_ptr<C2GraphicBlock> block;
-        C2MemoryUsage usage = {C2MemoryUsage::CPU_READ,
-                               C2MemoryUsage::CPU_WRITE};
-        usage.expected = (uint64_t)(BufferUsage::GPU_DATA_BUFFER);
-
-        c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 2), mHeight,
-                                                  format, usage, &block);
-        if (err != C2_OK) {
-            ALOGE("fetchGraphicBlock for Output failed with status %d", err);
-            return;
-        }
-        auto c2Handle = block->handle();
-        native_handle_t *grallocHandle =
-            UnwrapNativeCodec2GrallocHandle(c2Handle);
-        int hostColorBufferId = getColorBufferHandle(grallocHandle);
-        if (hostColorBufferId > 0) {
-            DDD("decoding to host color buffer");
-            mEnableAndroidNativeBuffers = true;
-        } else {
-            DDD("decoding to guest byte buffer");
-            mEnableAndroidNativeBuffers = false;
-        }
+    const bool isGraphic = (pool->getAllocatorId() & C2Allocator::GRAPHIC);
+    DDD("buffer id %d", (int)(pool->getAllocatorId()));
+    if (isGraphic) {
+        DDD("decoding to host color buffer");
+        mEnableAndroidNativeBuffers = true;
+    } else {
+        DDD("decoding to guest byte buffer");
+        mEnableAndroidNativeBuffers = false;
     }
 
     mCtx->version = mEnableAndroidNativeBuffers ? 200 : 100;
@@ -711,7 +693,7 @@
     std::shared_ptr<C2GraphicBlock> block;
     C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
     uint32_t format = HAL_PIXEL_FORMAT_YCBCR_420_888;
-    usage.expected = (uint64_t)(BufferUsage::GPU_DATA_BUFFER);
+    usage.expected = (uint64_t)(BufferUsage::VIDEO_DECODER);
 
     c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 2), mHeight, format,
                                               usage, &block);
@@ -721,24 +703,24 @@
         return UNKNOWN_ERROR;
     }
 
-    bool decodingToByteBuffer = false;
-    {
+    int hostColorBufferId = -1;
+    const bool decodingToHostColorBuffer = mEnableAndroidNativeBuffers;
+    if(decodingToHostColorBuffer){
         auto c2Handle = block->handle();
         native_handle_t *grallocHandle =
             UnwrapNativeCodec2GrallocHandle(c2Handle);
-        int hostColorBufferId = getColorBufferHandle(grallocHandle);
+        hostColorBufferId = getColorBufferHandle(grallocHandle);
         if (hostColorBufferId > 0) {
             DDD("found handle %d", hostColorBufferId);
         } else {
-            decodingToByteBuffer = true;
             DDD("decode to buffer, because handle %d is invalid",
                 hostColorBufferId);
             // change to -1 so host knows it is definitely invalid
             // 0 is a bit confusing
             hostColorBufferId = -1;
         }
-        setup_ctx_parameters(mCtx, hostColorBufferId);
     }
+    setup_ctx_parameters(mCtx, hostColorBufferId);
 
     vpx_image_t *img = vpx_codec_get_frame(mCtx);
 
@@ -798,7 +780,7 @@
         }
     }
 
-    if (decodingToByteBuffer) {
+    if (!decodingToHostColorBuffer) {
 
         C2GraphicView wView = block->map().get();
         if (wView.error()) {
diff --git a/system/codecs/c2/store/GoldfishComponentStore.cpp b/system/codecs/c2/store/GoldfishComponentStore.cpp
index 49c5a32..2d2ab47 100644
--- a/system/codecs/c2/store/GoldfishComponentStore.cpp
+++ b/system/codecs/c2/store/GoldfishComponentStore.cpp
@@ -272,6 +272,9 @@
     if (useAndroidGoldfishComponentInstance("avcdec")) {
         emplace("libcodec2_goldfish_avcdec.so");
     }
+    if (useAndroidGoldfishComponentInstance("hevcdec")) {
+        emplace("libcodec2_goldfish_hevcdec.so");
+    }
 }
 
 c2_status_t
diff --git a/system/hals/allocator3.cpp b/system/hals/allocator3.cpp
index d3cf5c1..3619d91 100644
--- a/system/hals/allocator3.cpp
+++ b/system/hals/allocator3.cpp
@@ -309,7 +309,7 @@
             }
             RETURN_ERROR(Error3::UNSUPPORTED);
         } else if (static_cast<int>(frameworkFormat) == kOMX_COLOR_FormatYUV420Planar &&
-               (usage & BufferUsage::GPU_DATA_BUFFER)) {
+               (usage & BufferUsage::VIDEO_DECODER)) {
             ALOGW("gralloc_alloc: Requested OMX_COLOR_FormatYUV420Planar, given "
               "YCbCr_420_888, taking experimental path. "
               "usage=%x", usage);
@@ -341,6 +341,7 @@
                             | BufferUsage::GPU_RENDER_TARGET
                             | BufferUsage::COMPOSER_OVERLAY
                             | BufferUsage::VIDEO_ENCODER
+                            | BufferUsage::VIDEO_DECODER
                             | BufferUsage::COMPOSER_CLIENT_TARGET
                             | BufferUsage::CPU_READ_MASK));
     }
diff --git a/system/hals/mapper3.cpp b/system/hals/mapper3.cpp
index 99a8d6a..6e3a664 100644
--- a/system/hals/mapper3.cpp
+++ b/system/hals/mapper3.cpp
@@ -634,7 +634,7 @@
 
         default:
             if (static_cast<int>(descriptor.format) == kOMX_COLOR_FormatYUV420Planar) {
-                return (usage & BufferUsage::GPU_DATA_BUFFER) != 0;
+                return (usage & BufferUsage::VIDEO_DECODER) != 0;
             }
 
             RETURN(false);
diff --git a/system/hwc3/Android.mk b/system/hwc3/Android.mk
new file mode 100644
index 0000000..3d30e89
--- /dev/null
+++ b/system/hwc3/Android.mk
@@ -0,0 +1,101 @@
+#
+# Copyright 2022 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := android.hardware.graphics.composer3-service.ranchu
+
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../LICENSE
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_VENDOR_MODULE := true
+
+LOCAL_SHARED_LIBRARIES := \
+    android.hardware.graphics.composer@2.1-resources \
+    android.hardware.graphics.composer@2.2-resources \
+    android.hardware.graphics.composer3-V1-ndk \
+    android.hardware.graphics.mapper@2.0 \
+    android.hardware.graphics.mapper@4.0 \
+    libbase \
+    libbinder \
+    libbinder_ndk \
+    libEGL \
+    libcutils \
+    libcuttlefish_device_config \
+    libcuttlefish_device_config_proto \
+    libcuttlefish_utils \
+    libcuttlefish_fs \
+    libdrm \
+    libgralloctypes \
+    libhardware \
+    libhidlbase \
+    libjsoncpp \
+    libjpeg \
+    liblog \
+    libsync \
+    libui \
+    libutils \
+    libutils \
+    libOpenglSystemCommon \
+    lib_renderControl_enc \
+    libui
+
+LOCAL_STATIC_LIBRARIES := \
+    libaidlcommonsupport \
+    libyuv_static
+
+LOCAL_C_INCLUDES := \
+    device/generic/goldfish-opengl/host/include/libOpenglRender \
+    device/generic/goldfish-opengl/android-emu \
+    device/generic/goldfish-opengl/shared/OpenglCodecCommon \
+    device/generic/goldfish-opengl/system/OpenglSystemCommon \
+    device/generic/goldfish-opengl/system/include \
+    device/generic/goldfish-opengl/system/renderControl_enc \
+    external/libdrm \
+    external/minigbm/cros_gralloc \
+    system/core/libsync \
+    system/core/libsync/include \
+
+LOCAL_SRC_FILES := \
+    ClientFrameComposer.cpp \
+    Common.cpp \
+    Composer.cpp \
+    ComposerClient.cpp \
+    ComposerResources.cpp \
+    Device.cpp \
+    Display.cpp \
+    DisplayConfig.cpp \
+    DisplayFinder.cpp \
+    Drm.cpp \
+    DrmPresenter.cpp \
+    Gralloc.cpp \
+    GuestFrameComposer.cpp \
+    HostFrameComposer.cpp \
+    HostUtils.cpp \
+    Layer.cpp \
+    Main.cpp \
+    NoOpFrameComposer.cpp \
+    VsyncThread.cpp \
+
+LOCAL_VINTF_FRAGMENTS := hwc3.xml
+LOCAL_INIT_RC := hwc3.rc
+
+include $(BUILD_EXECUTABLE)
+
diff --git a/system/hwc3/ClientFrameComposer.cpp b/system/hwc3/ClientFrameComposer.cpp
new file mode 100644
index 0000000..d7f8b55
--- /dev/null
+++ b/system/hwc3/ClientFrameComposer.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#include "ClientFrameComposer.h"
+
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android/hardware/graphics/common/1.0/types.h>
+#include <device_config_shared.h>
+#include <drm_fourcc.h>
+#include <libyuv.h>
+#include <sync/sync.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicBufferAllocator.h>
+#include <ui/GraphicBufferMapper.h>
+
+#include "Display.h"
+#include "Drm.h"
+#include "Layer.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+HWC3::Error ClientFrameComposer::init() {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  HWC3::Error error = mDrmPresenter.init();
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: failed to initialize DrmPresenter", __FUNCTION__);
+    return error;
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error ClientFrameComposer::registerOnHotplugCallback(
+    const HotplugCallback& cb) {
+  return mDrmPresenter.registerOnHotplugCallback(cb);
+  return HWC3::Error::None;
+}
+
+HWC3::Error ClientFrameComposer::unregisterOnHotplugCallback() {
+  return mDrmPresenter.unregisterOnHotplugCallback();
+}
+
+HWC3::Error ClientFrameComposer::onDisplayCreate(Display* display) {
+  const auto displayId = display->getId();
+  DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+  // Ensure created.
+  mDisplayInfos.emplace(displayId, DisplayInfo{});
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error ClientFrameComposer::onDisplayDestroy(Display* display) {
+  const auto displayId = display->getId();
+  DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+  auto it = mDisplayInfos.find(displayId);
+  if (it == mDisplayInfos.end()) {
+    ALOGE("%s: display:%" PRIu64 " missing display buffers?", __FUNCTION__,
+          displayId);
+    return HWC3::Error::BadDisplay;
+  }
+
+  mDisplayInfos.erase(it);
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error ClientFrameComposer::onDisplayClientTargetSet(Display* display) {
+  const auto displayId = display->getId();
+  DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+  auto it = mDisplayInfos.find(displayId);
+  if (it == mDisplayInfos.end()) {
+    ALOGE("%s: display:%" PRIu64 " missing display buffers?", __FUNCTION__,
+          displayId);
+    return HWC3::Error::BadDisplay;
+  }
+
+  DisplayInfo& displayInfo = it->second;
+
+  auto [drmBufferCreateError, drmBuffer] =
+    mDrmPresenter.create(display->getClientTarget().getBuffer());
+  if (drmBufferCreateError != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRIu64 " failed to create client target drm buffer",
+          __FUNCTION__, displayId);
+    return HWC3::Error::NoResources;
+  }
+  displayInfo.clientTargetDrmBuffer = std::move(drmBuffer);
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error ClientFrameComposer::onActiveConfigChange(Display* /*display*/) {
+  return HWC3::Error::None;
+};
+
+HWC3::Error ClientFrameComposer::validateDisplay(Display* display,
+                                                DisplayChanges* outChanges) {
+  const auto displayId = display->getId();
+  DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+  const std::vector<Layer*>& layers = display->getOrderedLayers();
+
+  for (Layer* layer : layers) {
+    const auto layerId = layer->getId();
+    const auto layerCompositionType = layer->getCompositionType();
+
+    if (layerCompositionType != Composition::CLIENT) {
+      outChanges->addLayerCompositionChange(displayId, layerId, Composition::CLIENT);
+      continue;
+    }
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error ClientFrameComposer::presentDisplay(
+    Display* display, ::android::base::unique_fd* outDisplayFence,
+    std::unordered_map<int64_t,
+                       ::android::base::unique_fd>* /*outLayerFences*/) {
+  const auto displayId = display->getId();
+  DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+  auto displayInfoIt = mDisplayInfos.find(displayId);
+  if (displayInfoIt == mDisplayInfos.end()) {
+    ALOGE("%s: failed to find display buffers for display:%" PRIu64,
+          __FUNCTION__, displayId);
+    return HWC3::Error::BadDisplay;
+  }
+
+  DisplayInfo& displayInfo = displayInfoIt->second;
+
+  ::android::base::unique_fd fence = display->getClientTarget().getFence();
+
+  auto [flushError, flushCompleteFence] = mDrmPresenter.flushToDisplay(
+        displayId, *displayInfo.clientTargetDrmBuffer, fence);
+  if (flushError != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRIu64 " failed to flush drm buffer" PRIu64,
+          __FUNCTION__, displayId);
+  }
+
+  *outDisplayFence = std::move(flushCompleteFence);
+  return flushError;
+}
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/ClientFrameComposer.h b/system/hwc3/ClientFrameComposer.h
new file mode 100644
index 0000000..3fb0ba8
--- /dev/null
+++ b/system/hwc3/ClientFrameComposer.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWC_CLIENTFRAMECOMPOSER_H
+#define ANDROID_HWC_CLIENTFRAMECOMPOSER_H
+
+#include "Common.h"
+#include "Display.h"
+#include "DrmPresenter.h"
+#include "FrameComposer.h"
+#include "Gralloc.h"
+#include "Layer.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+// A frame composer which always fallsback to client composition
+// (a.k.a make SurfaceFlinger do the composition).
+class ClientFrameComposer : public FrameComposer {
+ public:
+  ClientFrameComposer() = default;
+
+  ClientFrameComposer(const ClientFrameComposer&) = delete;
+  ClientFrameComposer& operator=(const ClientFrameComposer&) = delete;
+
+  ClientFrameComposer(ClientFrameComposer&&) = delete;
+  ClientFrameComposer& operator=(ClientFrameComposer&&) = delete;
+
+  HWC3::Error init() override;
+
+  HWC3::Error registerOnHotplugCallback(const HotplugCallback& cb) override;
+
+  HWC3::Error unregisterOnHotplugCallback() override;
+
+  HWC3::Error onDisplayCreate(Display* display) override;
+
+  HWC3::Error onDisplayDestroy(Display* display) override;
+
+  HWC3::Error onDisplayClientTargetSet(Display* display) override;
+
+  HWC3::Error onActiveConfigChange(Display* display) override;
+
+  // Determines if this composer can compose the given layers on the given
+  // display and requests changes for layers that can't not be composed.
+  HWC3::Error validateDisplay(Display* display,
+                              DisplayChanges* outChanges) override;
+
+  // Performs the actual composition of layers and presents the composed result
+  // to the display.
+  HWC3::Error presentDisplay(
+      Display* display, ::android::base::unique_fd* outDisplayFence,
+      std::unordered_map<int64_t, ::android::base::unique_fd>* outLayerFences)
+      override;
+
+ private:
+  struct DisplayInfo {
+    std::unique_ptr<DrmBuffer> clientTargetDrmBuffer;;
+  };
+
+  std::unordered_map<int64_t, DisplayInfo> mDisplayInfos;
+
+  DrmPresenter mDrmPresenter;
+};
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/Common.cpp b/system/hwc3/Common.cpp
new file mode 100644
index 0000000..497f764
--- /dev/null
+++ b/system/hwc3/Common.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#include "Common.h"
+
+#include <android-base/properties.h>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+bool IsCuttlefish() {
+  return ::android::base::GetProperty("ro.product.board", "") == "cutf";
+}
+
+bool IsCuttlefishFoldable() {
+  return IsCuttlefish() &&
+         ::android::base::GetProperty("ro.product.name", "").find("foldable") !=
+             std::string::npos;
+}
+
+bool IsNoOpMode() {
+  const std::string mode = ::android::base::GetProperty("ro.vendor.hwcomposer.mode", "");
+  DEBUG_LOG("%s: sysprop ro.vendor.hwcomposer.mode is %s", __FUNCTION__, mode.c_str());
+  return mode == "noop";
+}
+
+bool IsClientCompositionMode() {
+  const std::string mode = ::android::base::GetProperty("ro.vendor.hwcomposer.mode", "");
+  DEBUG_LOG("%s: sysprop ro.vendor.hwcomposer.mode is %s", __FUNCTION__, mode.c_str());
+  return mode == "client";
+}
+
+std::string toString(HWC3::Error error) {
+  switch (error) {
+    case HWC3::Error::None:
+      return "None";
+    case HWC3::Error::BadConfig:
+      return "BadConfig";
+    case HWC3::Error::BadDisplay:
+      return "BadDisplay";
+    case HWC3::Error::BadLayer:
+      return "BadLayer";
+    case HWC3::Error::BadParameter:
+      return "BadParameter";
+    case HWC3::Error::NoResources:
+      return "NoResources";
+    case HWC3::Error::NotValidated:
+      return "NotValidated";
+    case HWC3::Error::Unsupported:
+      return "Unsupported";
+    case HWC3::Error::SeamlessNotAllowed:
+      return "SeamlessNotAllowed";
+  }
+}
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/Common.h b/system/hwc3/Common.h
new file mode 100644
index 0000000..5b6587e
--- /dev/null
+++ b/system/hwc3/Common.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWC_COMMON_H
+#define ANDROID_HWC_COMMON_H
+
+#include <inttypes.h>
+
+#include <string>
+
+#define ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL)
+
+#undef LOG_TAG
+#define LOG_TAG "RanchuHwc"
+
+#include <aidl/android/hardware/graphics/composer3/IComposerClient.h>
+#include <android-base/logging.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+// Uncomment to enable additional debug logging.
+//#define DEBUG_RANCHU_HWC
+
+#if defined(DEBUG_RANCHU_HWC)
+#define DEBUG_LOG ALOGE
+#else
+#define DEBUG_LOG(...) ((void)0)
+#endif
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+bool IsCuttlefish();
+bool IsCuttlefishFoldable();
+bool IsNoOpMode();
+bool IsClientCompositionMode();
+
+namespace HWC3 {
+enum class Error : int32_t {
+  None = 0,
+  BadConfig = aidl::android::hardware::graphics::composer3::IComposerClient::
+      EX_BAD_CONFIG,
+  BadDisplay = aidl::android::hardware::graphics::composer3::IComposerClient::
+      EX_BAD_DISPLAY,
+  BadLayer = aidl::android::hardware::graphics::composer3::IComposerClient::
+      EX_BAD_LAYER,
+  BadParameter = aidl::android::hardware::graphics::composer3::IComposerClient::
+      EX_BAD_PARAMETER,
+  NoResources = aidl::android::hardware::graphics::composer3::IComposerClient::
+      EX_NO_RESOURCES,
+  NotValidated = aidl::android::hardware::graphics::composer3::IComposerClient::
+      EX_NOT_VALIDATED,
+  Unsupported = aidl::android::hardware::graphics::composer3::IComposerClient::
+      EX_UNSUPPORTED,
+  SeamlessNotAllowed = aidl::android::hardware::graphics::composer3::
+      IComposerClient::EX_SEAMLESS_NOT_ALLOWED,
+};
+}  // namespace HWC3
+
+std::string toString(HWC3::Error error);
+
+inline ndk::ScopedAStatus ToBinderStatus(HWC3::Error error) {
+  if (error != HWC3::Error::None) {
+    return ndk::ScopedAStatus::fromServiceSpecificError(
+        static_cast<int32_t>(error));
+  }
+  return ndk::ScopedAStatus::ok();
+}
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/Composer.cpp b/system/hwc3/Composer.cpp
new file mode 100644
index 0000000..b2a1b7e
--- /dev/null
+++ b/system/hwc3/Composer.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include "Composer.h"
+
+#include <android-base/logging.h>
+#include <android/binder_ibinder_platform.h>
+
+#include "Common.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+ndk::ScopedAStatus Composer::createClient(
+    std::shared_ptr<IComposerClient>* outClient) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mClientMutex);
+
+  const bool previousClientDestroyed = waitForClientDestroyedLocked(lock);
+  if (!previousClientDestroyed) {
+    ALOGE("%s: failed as composer client already exists", __FUNCTION__);
+    *outClient = nullptr;
+    return ToBinderStatus(HWC3::Error::NoResources);
+  }
+
+  auto client = ndk::SharedRefBase::make<ComposerClient>();
+  if (!client) {
+    ALOGE("%s: failed to init composer client", __FUNCTION__);
+    *outClient = nullptr;
+    return ToBinderStatus(HWC3::Error::NoResources);
+  }
+
+  auto error = client->init();
+  if (error != HWC3::Error::None) {
+    *outClient = nullptr;
+    return ToBinderStatus(error);
+  }
+
+  auto clientDestroyed = [this]() { onClientDestroyed(); };
+  client->setOnClientDestroyed(clientDestroyed);
+
+  mClient = client;
+  *outClient = client;
+
+  return ndk::ScopedAStatus::ok();
+}
+
+bool Composer::waitForClientDestroyedLocked(
+    std::unique_lock<std::mutex>& lock) {
+  if (!mClient.expired()) {
+    // In surface flinger we delete a composer client on one thread and
+    // then create a new client on another thread. Although surface
+    // flinger ensures the calls are made in that sequence (destroy and
+    // then create), sometimes the calls land in the composer service
+    // inverted (create and then destroy). Wait for a brief period to
+    // see if the existing client is destroyed.
+    constexpr const auto kTimeout = std::chrono::seconds(5);
+    mClientDestroyedCondition.wait_for(
+        lock, kTimeout, [this]() -> bool { return mClient.expired(); });
+    if (!mClient.expired()) {
+      ALOGW("%s: previous client was not destroyed", __FUNCTION__);
+    }
+  }
+
+  return mClient.expired();
+}
+
+void Composer::onClientDestroyed() {
+  std::lock_guard<std::mutex> lock(mClientMutex);
+
+  mClientDestroyedCondition.notify_all();
+}
+
+binder_status_t Composer::dump(int fd, const char** /*args*/,
+                               uint32_t /*numArgs*/) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::string output("TODO");
+
+  write(fd, output.c_str(), output.size());
+  return STATUS_OK;
+}
+
+ndk::ScopedAStatus Composer::getCapabilities(std::vector<Capability>* caps) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  caps->clear();
+  caps->emplace_back(Capability::PRESENT_FENCE_IS_NOT_RELIABLE);
+  caps->emplace_back(Capability::BOOT_DISPLAY_CONFIG);
+
+  return ndk::ScopedAStatus::ok();
+}
+
+::ndk::SpAIBinder Composer::createBinder() {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  auto binder = BnComposer::createBinder();
+  AIBinder_setInheritRt(binder.get(), true);
+  return binder;
+}
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
\ No newline at end of file
diff --git a/system/hwc3/Composer.h b/system/hwc3/Composer.h
new file mode 100644
index 0000000..f564169
--- /dev/null
+++ b/system/hwc3/Composer.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWC_COMPOSER_H
+#define ANDROID_HWC_COMPOSER_H
+
+#include <aidl/android/hardware/graphics/composer3/BnComposer.h>
+#include <utils/Mutex.h>
+
+#include <memory>
+
+#include "ComposerClient.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+// This class is basically just the interface to create a client.
+class Composer : public BnComposer {
+ public:
+  Composer() = default;
+
+  binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
+
+  // compser3 api
+  ndk::ScopedAStatus createClient(
+      std::shared_ptr<IComposerClient>* client) override;
+  ndk::ScopedAStatus getCapabilities(std::vector<Capability>* caps) override;
+
+ protected:
+  ndk::SpAIBinder createBinder() override;
+
+ private:
+  bool waitForClientDestroyedLocked(std::unique_lock<std::mutex>& lock);
+  void onClientDestroyed();
+
+  std::mutex mClientMutex;
+  std::weak_ptr<ComposerClient> mClient GUARDED_BY(mClientMutex);
+  std::condition_variable mClientDestroyedCondition;
+};
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
\ No newline at end of file
diff --git a/system/hwc3/ComposerClient.cpp b/system/hwc3/ComposerClient.cpp
new file mode 100644
index 0000000..db05383
--- /dev/null
+++ b/system/hwc3/ComposerClient.cpp
@@ -0,0 +1,1379 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include "ComposerClient.h"
+
+#include <aidlcommonsupport/NativeHandle.h>
+#include <android/binder_ibinder_platform.h>
+
+#include "Common.h"
+#include "Device.h"
+#include "GuestFrameComposer.h"
+#include "HostFrameComposer.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+namespace {
+
+#define GET_DISPLAY_OR_RETURN_ERROR()                                    \
+  Display* display = getDisplay(displayId);                              \
+  if (display == nullptr) {                                              \
+    ALOGE("%s failed to get display:%" PRIu64, __FUNCTION__, displayId); \
+    return ToBinderStatus(HWC3::Error::BadDisplay);                      \
+  }
+
+}  // namespace
+
+using ::aidl::android::hardware::graphics::common::PixelFormat;
+
+class ComposerClient::CommandResultWriter {
+ public:
+  CommandResultWriter(std::vector<CommandResultPayload>* results)
+      : mIndex(0), mResults(results) {}
+
+  void nextCommand() { ++mIndex; }
+
+  void addError(HWC3::Error error) {
+    CommandError commandErrorResult;
+    commandErrorResult.commandIndex = mIndex;
+    commandErrorResult.errorCode = static_cast<int32_t>(error);
+    mResults->emplace_back(std::move(commandErrorResult));
+  }
+
+  void addPresentFence(int64_t displayId, ::android::base::unique_fd fence) {
+    if (fence >= 0) {
+      PresentFence presentFenceResult;
+      presentFenceResult.display = displayId;
+      presentFenceResult.fence = ndk::ScopedFileDescriptor(fence.release());
+      mResults->emplace_back(std::move(presentFenceResult));
+    }
+  }
+
+  void addReleaseFences(
+      int64_t displayId,
+      std::unordered_map<int64_t, ::android::base::unique_fd> layerFences) {
+    ReleaseFences releaseFencesResult;
+    releaseFencesResult.display = displayId;
+    for (auto& [layer, layerFence] : layerFences) {
+      if (layerFence >= 0) {
+        ReleaseFences::Layer releaseFencesLayerResult;
+        releaseFencesLayerResult.layer = layer;
+        releaseFencesLayerResult.fence =
+            ndk::ScopedFileDescriptor(layerFence.release());
+        releaseFencesResult.layers.emplace_back(
+            std::move(releaseFencesLayerResult));
+      }
+    }
+    mResults->emplace_back(std::move(releaseFencesResult));
+  }
+
+  void addChanges(const DisplayChanges& changes) {
+    if (changes.compositionChanges) {
+      mResults->emplace_back(*changes.compositionChanges);
+    }
+    if (changes.displayRequestChanges) {
+      mResults->emplace_back(*changes.displayRequestChanges);
+    }
+  }
+
+  void addPresentOrValidateResult(int64_t displayId,
+                                  PresentOrValidate::Result pov) {
+    PresentOrValidate result;
+    result.display = displayId;
+    result.result = pov;
+    mResults->emplace_back(std::move(result));
+  }
+
+ private:
+  int32_t mIndex = 0;
+  std::vector<CommandResultPayload>* mResults = nullptr;
+};
+
+ComposerClient::ComposerClient() { DEBUG_LOG("%s", __FUNCTION__); }
+
+ComposerClient::~ComposerClient() {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  destroyDisplaysLocked();
+
+  if (mOnClientDestroyed) {
+    mOnClientDestroyed();
+  }
+}
+
+HWC3::Error ComposerClient::init() {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  HWC3::Error error = HWC3::Error::None;
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  mResources = std::make_unique<ComposerResources>();
+  if (!mResources) {
+    ALOGE("%s failed to allocate ComposerResources", __FUNCTION__);
+    return HWC3::Error::NoResources;
+  }
+
+  error = mResources->init();
+  if (error != HWC3::Error::None) {
+    ALOGE("%s failed to initialize ComposerResources", __FUNCTION__);
+    return error;
+  }
+
+  error = Device::getInstance().getComposer(&mComposer);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s failed to get FrameComposer", __FUNCTION__);
+    return error;
+  }
+
+  const auto HotplugCallback = [this](bool connected,   //
+                                      int32_t id,       //
+                                      uint32_t width,   //
+                                      uint32_t height,  //
+                                      uint32_t dpiX,    //
+                                      uint32_t dpiY,    //
+                                      uint32_t refreshRate) {
+    handleHotplug(connected, id, width, height, dpiX, dpiY, refreshRate);
+  };
+  error = mComposer->registerOnHotplugCallback(HotplugCallback);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s failed to register hotplug callback", __FUNCTION__);
+    return error;
+  }
+
+  error = createDisplaysLocked();
+  if (error != HWC3::Error::None) {
+    ALOGE("%s failed to create displays.", __FUNCTION__);
+    return error;
+  }
+
+  DEBUG_LOG("%s initialized!", __FUNCTION__);
+  return HWC3::Error::None;
+}
+
+ndk::ScopedAStatus ComposerClient::createLayer(int64_t displayId,
+                                               int32_t bufferSlotCount,
+                                               int64_t* layerId) {
+  DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  HWC3::Error error = display->createLayer(layerId);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRIu64 " failed to create layer", __FUNCTION__,
+          displayId);
+    return ToBinderStatus(error);
+  }
+
+  error = mResources->addLayer(displayId, *layerId, bufferSlotCount);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRIu64 " resources failed to create layer",
+          __FUNCTION__, displayId);
+    return ToBinderStatus(error);
+  }
+
+  return ToBinderStatus(HWC3::Error::None);
+}
+
+ndk::ScopedAStatus ComposerClient::createVirtualDisplay(
+    int32_t /*width*/, int32_t /*height*/, PixelFormat /*formatHint*/,
+    int32_t /*outputBufferSlotCount*/, VirtualDisplay* /*display*/) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  return ToBinderStatus(HWC3::Error::Unsupported);
+}
+
+ndk::ScopedAStatus ComposerClient::destroyLayer(int64_t displayId,
+                                                int64_t layerId) {
+  DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  HWC3::Error error = display->destroyLayer(layerId);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRIu64 " failed to destroy layer:%" PRIu64,
+          __FUNCTION__, displayId, layerId);
+    return ToBinderStatus(error);
+  }
+
+  error = mResources->removeLayer(displayId, layerId);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRIu64 " resources failed to destroy layer:%" PRIu64,
+          __FUNCTION__, displayId, layerId);
+    return ToBinderStatus(error);
+  }
+
+  return ToBinderStatus(HWC3::Error::None);
+}
+
+ndk::ScopedAStatus ComposerClient::destroyVirtualDisplay(
+    int64_t /*displayId*/) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  return ToBinderStatus(HWC3::Error::Unsupported);
+}
+
+ndk::ScopedAStatus ComposerClient::executeCommands(
+    const std::vector<DisplayCommand>& commands,
+    std::vector<CommandResultPayload>* commandResultPayloads) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  mCommandResults =
+      std::make_unique<CommandResultWriter>(commandResultPayloads);
+
+  for (const DisplayCommand& command : commands) {
+    executeDisplayCommand(command);
+    mCommandResults->nextCommand();
+  }
+
+  mCommandResults.reset();
+
+  return ToBinderStatus(HWC3::Error::None);
+}
+
+ndk::ScopedAStatus ComposerClient::getActiveConfig(int64_t displayId,
+                                                   int32_t* config) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->getActiveConfig(config));
+}
+
+ndk::ScopedAStatus ComposerClient::getColorModes(
+    int64_t displayId, std::vector<ColorMode>* colorModes) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->getColorModes(colorModes));
+}
+
+ndk::ScopedAStatus ComposerClient::getDataspaceSaturationMatrix(
+    common::Dataspace dataspace, std::vector<float>* matrix) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  if (dataspace != common::Dataspace::SRGB_LINEAR) {
+    return ToBinderStatus(HWC3::Error::BadParameter);
+  }
+
+  // clang-format off
+  constexpr std::array<float, 16> kUnit {
+    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,
+  };
+  // clang-format on
+  matrix->clear();
+  matrix->insert(matrix->begin(), kUnit.begin(), kUnit.end());
+
+  return ToBinderStatus(HWC3::Error::None);
+}
+
+ndk::ScopedAStatus ComposerClient::getDisplayAttribute(
+    int64_t displayId, int32_t config, DisplayAttribute attribute,
+    int32_t* value) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->getDisplayAttribute(config, attribute, value));
+}
+
+ndk::ScopedAStatus ComposerClient::getDisplayCapabilities(
+    int64_t displayId, std::vector<DisplayCapability>* outCaps) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->getDisplayCapabilities(outCaps));
+}
+
+ndk::ScopedAStatus ComposerClient::getDisplayConfigs(
+    int64_t displayId, std::vector<int32_t>* outConfigs) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->getDisplayConfigs(outConfigs));
+}
+
+ndk::ScopedAStatus ComposerClient::getDisplayConnectionType(
+    int64_t displayId, DisplayConnectionType* outType) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->getDisplayConnectionType(outType));
+}
+
+ndk::ScopedAStatus ComposerClient::getDisplayIdentificationData(
+    int64_t displayId, DisplayIdentification* outIdentification) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(
+      display->getDisplayIdentificationData(outIdentification));
+}
+
+ndk::ScopedAStatus ComposerClient::getDisplayName(int64_t displayId,
+                                                  std::string* outName) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->getDisplayName(outName));
+}
+
+ndk::ScopedAStatus ComposerClient::getDisplayVsyncPeriod(
+    int64_t displayId, int32_t* outVsyncPeriod) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->getDisplayVsyncPeriod(outVsyncPeriod));
+}
+
+ndk::ScopedAStatus ComposerClient::getDisplayedContentSample(
+    int64_t displayId, int64_t maxFrames, int64_t timestamp,
+    DisplayContentSample* outSamples) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(
+      display->getDisplayedContentSample(maxFrames, timestamp, outSamples));
+}
+
+ndk::ScopedAStatus ComposerClient::getDisplayedContentSamplingAttributes(
+    int64_t displayId, DisplayContentSamplingAttributes* outAttributes) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(
+      display->getDisplayedContentSamplingAttributes(outAttributes));
+}
+
+ndk::ScopedAStatus ComposerClient::getDisplayPhysicalOrientation(
+    int64_t displayId, common::Transform* outOrientation) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->getDisplayPhysicalOrientation(outOrientation));
+}
+
+ndk::ScopedAStatus ComposerClient::getHdrCapabilities(
+    int64_t displayId, HdrCapabilities* outCapabilities) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->getHdrCapabilities(outCapabilities));
+}
+
+ndk::ScopedAStatus ComposerClient::getMaxVirtualDisplayCount(
+    int32_t* outCount) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  // Not supported.
+  *outCount = 0;
+
+  return ToBinderStatus(HWC3::Error::None);
+}
+
+ndk::ScopedAStatus ComposerClient::getPerFrameMetadataKeys(
+    int64_t displayId, std::vector<PerFrameMetadataKey>* outKeys) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->getPerFrameMetadataKeys(outKeys));
+}
+
+ndk::ScopedAStatus ComposerClient::getReadbackBufferAttributes(
+    int64_t displayId, ReadbackBufferAttributes* outAttributes) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->getReadbackBufferAttributes(outAttributes));
+}
+
+ndk::ScopedAStatus ComposerClient::getReadbackBufferFence(
+    int64_t displayId, ndk::ScopedFileDescriptor* outAcquireFence) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->getReadbackBufferFence(outAcquireFence));
+}
+
+ndk::ScopedAStatus ComposerClient::getRenderIntents(
+    int64_t displayId, ColorMode mode, std::vector<RenderIntent>* outIntents) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->getRenderIntents(mode, outIntents));
+}
+
+ndk::ScopedAStatus ComposerClient::getSupportedContentTypes(
+    int64_t displayId, std::vector<ContentType>* outTypes) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->getSupportedContentTypes(outTypes));
+}
+
+ndk::ScopedAStatus ComposerClient::getDisplayDecorationSupport(
+    int64_t displayId,
+    std::optional<common::DisplayDecorationSupport>* outSupport) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->getDecorationSupport(outSupport));
+}
+
+ndk::ScopedAStatus ComposerClient::registerCallback(
+    const std::shared_ptr<IComposerCallback>& callback) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  const bool isFirstRegisterCallback = mCallbacks == nullptr;
+
+  mCallbacks = callback;
+
+  for (auto& [_, display] : mDisplays) {
+    display->registerCallback(callback);
+  }
+
+  if (isFirstRegisterCallback) {
+    lock.unlock();
+    for (auto& [displayId, _] : mDisplays) {
+      mCallbacks->onHotplug(displayId, /*connected=*/true);
+    }
+  }
+
+  return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus ComposerClient::setActiveConfig(int64_t displayId,
+                                                   int32_t configId) {
+  DEBUG_LOG("%s display:%" PRIu64 " config:%" PRIu32, __FUNCTION__, displayId,
+            configId);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->setActiveConfig(configId));
+}
+
+ndk::ScopedAStatus ComposerClient::setActiveConfigWithConstraints(
+    int64_t displayId, int32_t configId,
+    const VsyncPeriodChangeConstraints& constraints,
+    VsyncPeriodChangeTimeline* outTimeline) {
+  DEBUG_LOG("%s display:%" PRIu64 " config:%" PRIu32, __FUNCTION__, displayId,
+            configId);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->setActiveConfigWithConstraints(
+      configId, constraints, outTimeline));
+}
+
+ndk::ScopedAStatus ComposerClient::setBootDisplayConfig(int64_t displayId,
+                                                        int32_t configId) {
+  DEBUG_LOG("%s display:%" PRIu64 " config:%" PRIu32, __FUNCTION__, displayId,
+            configId);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->setBootConfig(configId));
+}
+
+ndk::ScopedAStatus ComposerClient::clearBootDisplayConfig(int64_t displayId) {
+  DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->clearBootConfig());
+}
+
+ndk::ScopedAStatus ComposerClient::getPreferredBootDisplayConfig(
+    int64_t displayId, int32_t* outConfigId) {
+  DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->getPreferredBootConfig(outConfigId));
+}
+
+ndk::ScopedAStatus ComposerClient::setAutoLowLatencyMode(int64_t displayId,
+                                                         bool on) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->setAutoLowLatencyMode(on));
+}
+
+ndk::ScopedAStatus ComposerClient::setClientTargetSlotCount(int64_t displayId,
+                                                            int32_t count) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(
+      mResources->setDisplayClientTargetCacheSize(displayId, count));
+}
+
+ndk::ScopedAStatus ComposerClient::setColorMode(int64_t displayId,
+                                                ColorMode mode,
+                                                RenderIntent intent) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->setColorMode(mode, intent));
+}
+
+ndk::ScopedAStatus ComposerClient::setContentType(int64_t displayId,
+                                                  ContentType type) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->setContentType(type));
+}
+
+ndk::ScopedAStatus ComposerClient::setDisplayedContentSamplingEnabled(
+    int64_t displayId, bool enable, FormatColorComponent componentMask,
+    int64_t maxFrames) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->setDisplayedContentSamplingEnabled(
+      enable, componentMask, maxFrames));
+}
+
+ndk::ScopedAStatus ComposerClient::setPowerMode(int64_t displayId,
+                                                PowerMode mode) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->setPowerMode(mode));
+}
+
+ndk::ScopedAStatus ComposerClient::setReadbackBuffer(
+    int64_t displayId,
+    const aidl::android::hardware::common::NativeHandle& buffer,
+    const ndk::ScopedFileDescriptor& releaseFence) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  // Owned by mResources.
+  buffer_handle_t importedBuffer = nullptr;
+
+  auto releaser = mResources->createReleaser(true /* isBuffer */);
+  auto error = mResources->getDisplayReadbackBuffer(
+      displayId, buffer, &importedBuffer, releaser.get());
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: failed to get readback buffer from resources.", __FUNCTION__);
+    return ToBinderStatus(error);
+  }
+
+  error = display->setReadbackBuffer(importedBuffer, releaseFence);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: failed to set readback buffer to display.", __FUNCTION__);
+    return ToBinderStatus(error);
+  }
+
+  return ToBinderStatus(HWC3::Error::None);
+}
+
+ndk::ScopedAStatus ComposerClient::setVsyncEnabled(int64_t displayId,
+                                                   bool enabled) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->setVsyncEnabled(enabled));
+}
+
+ndk::ScopedAStatus ComposerClient::setIdleTimerEnabled(int64_t displayId,
+                                                       int32_t timeoutMs) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  GET_DISPLAY_OR_RETURN_ERROR();
+
+  return ToBinderStatus(display->setIdleTimerEnabled(timeoutMs));
+}
+
+ndk::SpAIBinder ComposerClient::createBinder() {
+  auto binder = BnComposerClient::createBinder();
+  AIBinder_setInheritRt(binder.get(), true);
+  return binder;
+}
+
+namespace {
+
+#define DISPATCH_LAYER_COMMAND(layerCmd, display, layer, field, funcName)     \
+  do {                                                                        \
+    if (layerCmd.field) {                                                     \
+      ComposerClient::executeLayerCommandSetLayer##funcName(display, layer,   \
+                                                            *layerCmd.field); \
+    }                                                                         \
+  } while (0)
+
+#define DISPATCH_DISPLAY_COMMAND(displayCmd, display, field, funcName) \
+  do {                                                                 \
+    if (displayCmd.field) {                                            \
+      executeDisplayCommand##funcName(display, *displayCmd.field);     \
+    }                                                                  \
+  } while (0)
+
+#define DISPATCH_DISPLAY_BOOL_COMMAND(displayCmd, display, field, funcName) \
+  do {                                                                      \
+    if (displayCmd.field) {                                                 \
+      executeDisplayCommand##funcName(display);                             \
+    }                                                                       \
+  } while (0)
+
+#define DISPATCH_DISPLAY_BOOL_COMMAND_AND_DATA(displayCmd, display, field, \
+                                               data, funcName)             \
+  do {                                                                     \
+    if (displayCmd.field) {                                                \
+      executeDisplayCommand##funcName(display, displayCmd.data);           \
+    }                                                                      \
+  } while (0)
+
+#define LOG_DISPLAY_COMMAND_ERROR(display, error)                 \
+  do {                                                            \
+    const std::string errorString = toString(error);              \
+    ALOGE("%s: display:%" PRId64 " failed with:%s", __FUNCTION__, \
+          display->getId(), errorString.c_str());                 \
+  } while (0)
+
+#define LOG_LAYER_COMMAND_ERROR(display, layer, error)                \
+  do {                                                                \
+    const std::string errorString = toString(error);                  \
+    ALOGE("%s: display:%" PRId64 " layer:%" PRId64 " failed with:%s", \
+          __FUNCTION__, display->getId(), layer->getId(),             \
+          errorString.c_str());                                       \
+  } while (0)
+
+}  // namespace
+
+void ComposerClient::executeDisplayCommand(
+    const DisplayCommand& displayCommand) {
+  Display* display = getDisplay(displayCommand.display);
+  if (display == nullptr) {
+    mCommandResults->addError(HWC3::Error::BadDisplay);
+    return;
+  }
+
+  for (const LayerCommand& layerCmd : displayCommand.layers) {
+    executeLayerCommand(display, layerCmd);
+  }
+
+  DISPATCH_DISPLAY_COMMAND(displayCommand, display, colorTransformMatrix,
+                           SetColorTransform);
+  DISPATCH_DISPLAY_COMMAND(displayCommand, display, brightness, SetBrightness);
+  DISPATCH_DISPLAY_COMMAND(displayCommand, display, clientTarget,
+                           SetClientTarget);
+  DISPATCH_DISPLAY_COMMAND(displayCommand, display, virtualDisplayOutputBuffer,
+                           SetOutputBuffer);
+  DISPATCH_DISPLAY_BOOL_COMMAND_AND_DATA(displayCommand, display,
+                                         validateDisplay, expectedPresentTime,
+                                         ValidateDisplay);
+  DISPATCH_DISPLAY_BOOL_COMMAND(displayCommand, display, acceptDisplayChanges,
+                                AcceptDisplayChanges);
+  DISPATCH_DISPLAY_BOOL_COMMAND(displayCommand, display, presentDisplay,
+                                PresentDisplay);
+  DISPATCH_DISPLAY_BOOL_COMMAND_AND_DATA(
+      displayCommand, display, presentOrValidateDisplay, expectedPresentTime,
+      PresentOrValidateDisplay);
+}
+
+void ComposerClient::executeLayerCommand(Display* display,
+                                         const LayerCommand& layerCommand) {
+  Layer* layer = display->getLayer(layerCommand.layer);
+  if (layer == nullptr) {
+    mCommandResults->addError(HWC3::Error::BadLayer);
+    return;
+  }
+
+  DISPATCH_LAYER_COMMAND(layerCommand, display, layer, cursorPosition,
+                         CursorPosition);
+  DISPATCH_LAYER_COMMAND(layerCommand, display, layer, buffer, Buffer);
+  DISPATCH_LAYER_COMMAND(layerCommand, display, layer, damage, SurfaceDamage);
+  DISPATCH_LAYER_COMMAND(layerCommand, display, layer, blendMode, BlendMode);
+  DISPATCH_LAYER_COMMAND(layerCommand, display, layer, color, Color);
+  DISPATCH_LAYER_COMMAND(layerCommand, display, layer, composition,
+                         Composition);
+  DISPATCH_LAYER_COMMAND(layerCommand, display, layer, dataspace, Dataspace);
+  DISPATCH_LAYER_COMMAND(layerCommand, display, layer, displayFrame,
+                         DisplayFrame);
+  DISPATCH_LAYER_COMMAND(layerCommand, display, layer, planeAlpha, PlaneAlpha);
+  DISPATCH_LAYER_COMMAND(layerCommand, display, layer, sidebandStream,
+                         SidebandStream);
+  DISPATCH_LAYER_COMMAND(layerCommand, display, layer, sourceCrop, SourceCrop);
+  DISPATCH_LAYER_COMMAND(layerCommand, display, layer, transform, Transform);
+  DISPATCH_LAYER_COMMAND(layerCommand, display, layer, visibleRegion,
+                         VisibleRegion);
+  DISPATCH_LAYER_COMMAND(layerCommand, display, layer, z, ZOrder);
+  DISPATCH_LAYER_COMMAND(layerCommand, display, layer, colorTransform,
+                         ColorTransform);
+  DISPATCH_LAYER_COMMAND(layerCommand, display, layer, brightness, Brightness);
+  DISPATCH_LAYER_COMMAND(layerCommand, display, layer, perFrameMetadata,
+                         PerFrameMetadata);
+  DISPATCH_LAYER_COMMAND(layerCommand, display, layer, perFrameMetadataBlob,
+                         PerFrameMetadataBlobs);
+}
+
+void ComposerClient::executeDisplayCommandSetColorTransform(
+    Display* display, const std::vector<float>& matrix) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  auto error = display->setColorTransform(matrix);
+  if (error != HWC3::Error::None) {
+    LOG_DISPLAY_COMMAND_ERROR(display, error);
+    mCommandResults->addError(error);
+  }
+}
+
+void ComposerClient::executeDisplayCommandSetBrightness(
+    Display* display, const DisplayBrightness& brightness) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  auto error = display->setBrightness(brightness.brightness);
+  if (error != HWC3::Error::None) {
+    LOG_DISPLAY_COMMAND_ERROR(display, error);
+    mCommandResults->addError(error);
+  }
+}
+
+void ComposerClient::executeDisplayCommandSetClientTarget(
+    Display* display, const ClientTarget& clientTarget) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  // Owned by mResources.
+  buffer_handle_t importedBuffer = nullptr;
+
+  auto releaser = mResources->createReleaser(/*isBuffer=*/true);
+  auto error = mResources->getDisplayClientTarget(
+      display->getId(), clientTarget.buffer, &importedBuffer, releaser.get());
+  if (error != HWC3::Error::None) {
+    LOG_DISPLAY_COMMAND_ERROR(display, error);
+    mCommandResults->addError(error);
+    return;
+  }
+
+  error = display->setClientTarget(importedBuffer, clientTarget.buffer.fence,
+                                   clientTarget.dataspace, clientTarget.damage);
+  if (error != HWC3::Error::None) {
+    LOG_DISPLAY_COMMAND_ERROR(display, error);
+    mCommandResults->addError(error);
+    return;
+  }
+}
+
+void ComposerClient::executeDisplayCommandSetOutputBuffer(
+    Display* display, const Buffer& buffer) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  // Owned by mResources.
+  buffer_handle_t importedBuffer = nullptr;
+
+  auto releaser = mResources->createReleaser(/*isBuffer=*/true);
+  auto error = mResources->getDisplayOutputBuffer(
+      display->getId(), buffer, &importedBuffer, releaser.get());
+  if (error != HWC3::Error::None) {
+    LOG_DISPLAY_COMMAND_ERROR(display, error);
+    mCommandResults->addError(error);
+    return;
+  }
+
+  error = display->setOutputBuffer(importedBuffer, buffer.fence);
+  if (error != HWC3::Error::None) {
+    LOG_DISPLAY_COMMAND_ERROR(display, error);
+    mCommandResults->addError(error);
+    return;
+  }
+}
+
+void ComposerClient::executeDisplayCommandValidateDisplay(
+    Display* display,
+    const std::optional<ClockMonotonicTimestamp> expectedPresentTime) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  auto error = display->setExpectedPresentTime(expectedPresentTime);
+  if (error != HWC3::Error::None) {
+    LOG_DISPLAY_COMMAND_ERROR(display, error);
+    mCommandResults->addError(error);
+  }
+
+  DisplayChanges changes;
+
+  error = display->validate(&changes);
+  if (error != HWC3::Error::None) {
+    LOG_DISPLAY_COMMAND_ERROR(display, error);
+    mCommandResults->addError(error);
+  } else {
+    mCommandResults->addChanges(changes);
+  }
+
+  mResources->setDisplayMustValidateState(display->getId(), false);
+}
+
+void ComposerClient::executeDisplayCommandAcceptDisplayChanges(
+    Display* display) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  auto error = display->acceptChanges();
+  if (error != HWC3::Error::None) {
+    LOG_DISPLAY_COMMAND_ERROR(display, error);
+    mCommandResults->addError(error);
+  }
+}
+
+void ComposerClient::executeDisplayCommandPresentOrValidateDisplay(
+    Display* display,
+    const std::optional<ClockMonotonicTimestamp> expectedPresentTime) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  // TODO: Support SKIP_VALIDATE.
+
+  auto error = display->setExpectedPresentTime(expectedPresentTime);
+  if (error != HWC3::Error::None) {
+    LOG_DISPLAY_COMMAND_ERROR(display, error);
+    mCommandResults->addError(error);
+  }
+
+  DisplayChanges changes;
+
+  error = display->validate(&changes);
+  if (error != HWC3::Error::None) {
+    LOG_DISPLAY_COMMAND_ERROR(display, error);
+    mCommandResults->addError(error);
+  } else {
+    const int64_t displayId = display->getId();
+    mCommandResults->addChanges(changes);
+    mCommandResults->addPresentOrValidateResult(
+        displayId, PresentOrValidate::Result::Validated);
+  }
+
+  mResources->setDisplayMustValidateState(display->getId(), false);
+}
+
+void ComposerClient::executeDisplayCommandPresentDisplay(Display* display) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  if (mResources->mustValidateDisplay(display->getId())) {
+    ALOGE("%s: display:%" PRIu64 " not validated", __FUNCTION__,
+          display->getId());
+    mCommandResults->addError(HWC3::Error::NotValidated);
+    return;
+  }
+
+  ::android::base::unique_fd displayFence;
+  std::unordered_map<int64_t, ::android::base::unique_fd> layerFences;
+
+  auto error = display->present(&displayFence, &layerFences);
+  if (error != HWC3::Error::None) {
+    LOG_DISPLAY_COMMAND_ERROR(display, error);
+    mCommandResults->addError(error);
+  } else {
+    const int64_t displayId = display->getId();
+    mCommandResults->addPresentFence(displayId, std::move(displayFence));
+    mCommandResults->addReleaseFences(displayId, std::move(layerFences));
+  }
+}
+
+void ComposerClient::executeLayerCommandSetLayerCursorPosition(
+    Display* display, Layer* layer, const common::Point& cursorPosition) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  auto error = layer->setCursorPosition(cursorPosition);
+  if (error != HWC3::Error::None) {
+    LOG_LAYER_COMMAND_ERROR(display, layer, error);
+    mCommandResults->addError(error);
+  }
+}
+
+void ComposerClient::executeLayerCommandSetLayerBuffer(Display* display,
+                                                       Layer* layer,
+                                                       const Buffer& buffer) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  // Owned by mResources.
+  buffer_handle_t importedBuffer = nullptr;
+
+  auto releaser = mResources->createReleaser(/*isBuffer=*/true);
+  auto error =
+      mResources->getLayerBuffer(display->getId(), layer->getId(), buffer,
+                                 &importedBuffer, releaser.get());
+  if (error != HWC3::Error::None) {
+    LOG_LAYER_COMMAND_ERROR(display, layer, error);
+    mCommandResults->addError(error);
+    return;
+  }
+
+  error = layer->setBuffer(importedBuffer, buffer.fence);
+  if (error != HWC3::Error::None) {
+    LOG_LAYER_COMMAND_ERROR(display, layer, error);
+    mCommandResults->addError(error);
+  }
+}
+
+void ComposerClient::executeLayerCommandSetLayerSurfaceDamage(
+    Display* display, Layer* layer,
+    const std::vector<std::optional<common::Rect>>& damage) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  auto error = layer->setSurfaceDamage(damage);
+  if (error != HWC3::Error::None) {
+    LOG_LAYER_COMMAND_ERROR(display, layer, error);
+    mCommandResults->addError(error);
+  }
+}
+
+void ComposerClient::executeLayerCommandSetLayerBlendMode(
+    Display* display, Layer* layer, const ParcelableBlendMode& blendMode) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  auto error = layer->setBlendMode(blendMode.blendMode);
+  if (error != HWC3::Error::None) {
+    LOG_LAYER_COMMAND_ERROR(display, layer, error);
+    mCommandResults->addError(error);
+  }
+}
+
+void ComposerClient::executeLayerCommandSetLayerColor(Display* display,
+                                                      Layer* layer,
+                                                      const Color& color) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  auto error = layer->setColor(color);
+  if (error != HWC3::Error::None) {
+    LOG_LAYER_COMMAND_ERROR(display, layer, error);
+    mCommandResults->addError(error);
+  }
+}
+
+void ComposerClient::executeLayerCommandSetLayerComposition(
+    Display* display, Layer* layer, const ParcelableComposition& composition) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  auto error = layer->setCompositionType(composition.composition);
+  if (error != HWC3::Error::None) {
+    LOG_LAYER_COMMAND_ERROR(display, layer, error);
+    mCommandResults->addError(error);
+  }
+}
+
+void ComposerClient::executeLayerCommandSetLayerDataspace(
+    Display* display, Layer* layer, const ParcelableDataspace& dataspace) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  auto error = layer->setDataspace(dataspace.dataspace);
+  if (error != HWC3::Error::None) {
+    LOG_LAYER_COMMAND_ERROR(display, layer, error);
+    mCommandResults->addError(error);
+  }
+}
+
+void ComposerClient::executeLayerCommandSetLayerDisplayFrame(
+    Display* display, Layer* layer, const common::Rect& rect) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  auto error = layer->setDisplayFrame(rect);
+  if (error != HWC3::Error::None) {
+    LOG_LAYER_COMMAND_ERROR(display, layer, error);
+    mCommandResults->addError(error);
+  }
+}
+
+void ComposerClient::executeLayerCommandSetLayerPlaneAlpha(
+    Display* display, Layer* layer, const PlaneAlpha& planeAlpha) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  auto error = layer->setPlaneAlpha(planeAlpha.alpha);
+  if (error != HWC3::Error::None) {
+    LOG_LAYER_COMMAND_ERROR(display, layer, error);
+    mCommandResults->addError(error);
+  }
+}
+
+void ComposerClient::executeLayerCommandSetLayerSidebandStream(
+    Display* display, Layer* layer,
+    const aidl::android::hardware::common::NativeHandle& handle) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  // Owned by mResources.
+  buffer_handle_t importedStream = nullptr;
+
+  auto releaser = mResources->createReleaser(/*isBuffer=*/false);
+  auto error = mResources->getLayerSidebandStream(
+      display->getId(), layer->getId(), handle, &importedStream,
+      releaser.get());
+  if (error != HWC3::Error::None) {
+    LOG_LAYER_COMMAND_ERROR(display, layer, error);
+    mCommandResults->addError(error);
+    return;
+  }
+
+  error = layer->setSidebandStream(importedStream);
+  if (error != HWC3::Error::None) {
+    LOG_LAYER_COMMAND_ERROR(display, layer, error);
+    mCommandResults->addError(error);
+  }
+}
+
+void ComposerClient::executeLayerCommandSetLayerSourceCrop(
+    Display* display, Layer* layer, const common::FRect& sourceCrop) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  auto error = layer->setSourceCrop(sourceCrop);
+  if (error != HWC3::Error::None) {
+    LOG_LAYER_COMMAND_ERROR(display, layer, error);
+    mCommandResults->addError(error);
+  }
+}
+
+void ComposerClient::executeLayerCommandSetLayerTransform(
+    Display* display, Layer* layer, const ParcelableTransform& transform) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  auto error = layer->setTransform(transform.transform);
+  if (error != HWC3::Error::None) {
+    LOG_LAYER_COMMAND_ERROR(display, layer, error);
+    mCommandResults->addError(error);
+  }
+}
+
+void ComposerClient::executeLayerCommandSetLayerVisibleRegion(
+    Display* display, Layer* layer,
+    const std::vector<std::optional<common::Rect>>& visibleRegion) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  auto error = layer->setVisibleRegion(visibleRegion);
+  if (error != HWC3::Error::None) {
+    LOG_LAYER_COMMAND_ERROR(display, layer, error);
+    mCommandResults->addError(error);
+  }
+}
+
+void ComposerClient::executeLayerCommandSetLayerZOrder(Display* display,
+                                                       Layer* layer,
+                                                       const ZOrder& zOrder) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  auto error = layer->setZOrder(zOrder.z);
+  if (error != HWC3::Error::None) {
+    LOG_LAYER_COMMAND_ERROR(display, layer, error);
+    mCommandResults->addError(error);
+  }
+}
+
+void ComposerClient::executeLayerCommandSetLayerPerFrameMetadata(
+    Display* display, Layer* layer,
+    const std::vector<std::optional<PerFrameMetadata>>& perFrameMetadata) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  auto error = layer->setPerFrameMetadata(perFrameMetadata);
+  if (error != HWC3::Error::None) {
+    LOG_LAYER_COMMAND_ERROR(display, layer, error);
+    mCommandResults->addError(error);
+  }
+}
+
+void ComposerClient::executeLayerCommandSetLayerColorTransform(
+    Display* display, Layer* layer, const std::vector<float>& colorTransform) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  auto error = layer->setColorTransform(colorTransform);
+  if (error != HWC3::Error::None) {
+    LOG_LAYER_COMMAND_ERROR(display, layer, error);
+    mCommandResults->addError(error);
+  }
+}
+
+void ComposerClient::executeLayerCommandSetLayerBrightness(
+    Display* display, Layer* layer, const LayerBrightness& brightness) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  auto error = layer->setBrightness(brightness.brightness);
+  if (error != HWC3::Error::None) {
+    LOG_LAYER_COMMAND_ERROR(display, layer, error);
+    mCommandResults->addError(error);
+  }
+}
+
+void ComposerClient::executeLayerCommandSetLayerPerFrameMetadataBlobs(
+    Display* display, Layer* layer,
+    const std::vector<std::optional<PerFrameMetadataBlob>>&
+        perFrameMetadataBlob) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  auto error = layer->setPerFrameMetadataBlobs(perFrameMetadataBlob);
+  if (error != HWC3::Error::None) {
+    LOG_LAYER_COMMAND_ERROR(display, layer, error);
+    mCommandResults->addError(error);
+  }
+}
+
+Display* ComposerClient::getDisplay(int64_t displayId) {
+  auto it = mDisplays.find(displayId);
+  if (it == mDisplays.end()) {
+    ALOGE("%s: no display:%" PRIu64, __FUNCTION__, displayId);
+    return nullptr;
+  }
+  return it->second.get();
+}
+
+HWC3::Error ComposerClient::createDisplaysLocked() {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  if (!mComposer) {
+    ALOGE("%s composer not initialized!", __FUNCTION__);
+    return HWC3::Error::NoResources;
+  }
+
+  std::vector<DisplayMultiConfigs> displays;
+
+  HWC3::Error error = findDisplays(displays);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s failed to find display configs", __FUNCTION__);
+    return error;
+  }
+
+  for (const auto& iter : displays) {
+    error =
+        createDisplayLocked(iter.displayId, iter.activeConfigId, iter.configs);
+    if (error != HWC3::Error::None) {
+      ALOGE("%s failed to create display from config", __FUNCTION__);
+      return error;
+    }
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error ComposerClient::createDisplayLocked(
+    int64_t displayId, int32_t activeConfigId,
+    const std::vector<DisplayConfig>& configs) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  if (!mComposer) {
+    ALOGE("%s composer not initialized!", __FUNCTION__);
+    return HWC3::Error::NoResources;
+  }
+
+  auto display = std::make_unique<Display>(mComposer, displayId);
+  if (display == nullptr) {
+    ALOGE("%s failed to allocate display", __FUNCTION__);
+    return HWC3::Error::NoResources;
+  }
+
+  HWC3::Error error = display->init(configs, activeConfigId);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s failed to initialize display:%" PRIu64, __FUNCTION__, displayId);
+    return error;
+  }
+
+  error = mComposer->onDisplayCreate(display.get());
+  if (error != HWC3::Error::None) {
+    ALOGE("%s failed to register display:%" PRIu64 " with composer",
+          __FUNCTION__, displayId);
+    return error;
+  }
+
+  DEBUG_LOG("%s: adding display:%" PRIu64, __FUNCTION__, displayId);
+  mDisplays.emplace(displayId, std::move(display));
+
+  error = mResources->addPhysicalDisplay(displayId);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s failed to initialize display:%" PRIu64 " resources", __FUNCTION__,
+          displayId);
+    return error;
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error ComposerClient::destroyDisplaysLocked() {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  std::vector<int64_t> displayIds;
+  for (const auto& [displayId, _] : mDisplays) {
+    displayIds.push_back(displayId);
+  }
+  for (const int64_t displayId : displayIds) {
+    destroyDisplayLocked(displayId);
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error ComposerClient::destroyDisplayLocked(int64_t displayId) {
+  DEBUG_LOG("%s display:%" PRId64, __FUNCTION__, displayId);
+
+  auto it = mDisplays.find(displayId);
+  if (it == mDisplays.end()) {
+    ALOGE("%s: display:%" PRId64 " no such display?", __FUNCTION__, displayId);
+    return HWC3::Error::BadDisplay;
+  }
+
+  HWC3::Error error = mComposer->onDisplayDestroy(it->second.get());
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRId64 " failed to destroy with frame composer",
+          __FUNCTION__, displayId);
+  }
+
+  error = mResources->removeDisplay(displayId);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRId64 " failed to destroy with resources",
+          __FUNCTION__, displayId);
+  }
+
+  mDisplays.erase(it);
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error ComposerClient::handleHotplug(bool connected, uint32_t id,
+                                          uint32_t width, uint32_t height,
+                                          uint32_t dpiX, uint32_t dpiY,
+                                          uint32_t refreshRate) {
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  if (!mCallbacks) {
+    return HWC3::Error::None;
+  }
+
+  const int64_t displayId = static_cast<int64_t>(id);
+
+  Display* display = getDisplay(displayId);
+  if (display != nullptr) {
+    ALOGI("Disconnecting display:%" PRIu64, displayId);
+    mCallbacks->onHotplug(displayId, /*connected=*/false);
+
+    destroyDisplayLocked(displayId);
+  }
+
+  if (connected) {
+    const int32_t configId = static_cast<int32_t>(id);
+    const std::vector<DisplayConfig> configs = {
+        DisplayConfig(configId, static_cast<int>(width),
+                      static_cast<int>(height), static_cast<int>(dpiX),
+                      static_cast<int>(dpiY), static_cast<int>(refreshRate))};
+
+    createDisplayLocked(displayId, configId, configs);
+
+    ALOGI("Connecting display:%" PRIu32 " w:%" PRIu32 " h:%" PRIu32
+          " dpiX:%" PRIu32 " dpiY %" PRIu32 "fps %" PRIu32,
+          id, width, height, dpiX, dpiY, refreshRate);
+
+    mCallbacks->onHotplug(displayId, /*connected=*/true);
+  }
+
+  return HWC3::Error::None;
+}
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
\ No newline at end of file
diff --git a/system/hwc3/ComposerClient.h b/system/hwc3/ComposerClient.h
new file mode 100644
index 0000000..7e19d2c
--- /dev/null
+++ b/system/hwc3/ComposerClient.h
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWC_COMPOSERCLIENT_H
+#define ANDROID_HWC_COMPOSERCLIENT_H
+
+#include <aidl/android/hardware/graphics/composer3/BnComposerClient.h>
+#include <utils/Mutex.h>
+
+#include <memory>
+
+#include "ComposerResources.h"
+#include "Display.h"
+#include "FrameComposer.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class ComposerClient : public BnComposerClient {
+ public:
+  ComposerClient();
+  virtual ~ComposerClient();
+
+  HWC3::Error init();
+
+  void setOnClientDestroyed(std::function<void()> onClientDestroyed) {
+    mOnClientDestroyed = onClientDestroyed;
+  }
+
+  // HWC3 interface:
+  ndk::ScopedAStatus createLayer(int64_t displayId, int32_t bufferSlotCount,
+                                 int64_t* layer) override;
+  ndk::ScopedAStatus createVirtualDisplay(int32_t width, int32_t height,
+                                          common::PixelFormat formatHint,
+                                          int32_t outputBufferSlotCount,
+                                          VirtualDisplay* display) override;
+  ndk::ScopedAStatus destroyLayer(int64_t displayId, int64_t layer) override;
+  ndk::ScopedAStatus destroyVirtualDisplay(int64_t displayId) override;
+  ndk::ScopedAStatus executeCommands(
+      const std::vector<DisplayCommand>& commands,
+      std::vector<CommandResultPayload>* results) override;
+  ndk::ScopedAStatus getActiveConfig(int64_t displayId,
+                                     int32_t* config) override;
+  ndk::ScopedAStatus getColorModes(int64_t displayId,
+                                   std::vector<ColorMode>* colorModes) override;
+  ndk::ScopedAStatus getDataspaceSaturationMatrix(
+      common::Dataspace dataspace, std::vector<float>* matrix) override;
+  ndk::ScopedAStatus getDisplayAttribute(int64_t displayId, int32_t config,
+                                         DisplayAttribute attribute,
+                                         int32_t* value) override;
+  ndk::ScopedAStatus getDisplayCapabilities(
+      int64_t displayId, std::vector<DisplayCapability>* caps) override;
+  ndk::ScopedAStatus getDisplayConfigs(int64_t displayId,
+                                       std::vector<int32_t>* configs) override;
+  ndk::ScopedAStatus getDisplayConnectionType(
+      int64_t displayId, DisplayConnectionType* type) override;
+  ndk::ScopedAStatus getDisplayIdentificationData(
+      int64_t displayId, DisplayIdentification* id) override;
+  ndk::ScopedAStatus getDisplayName(int64_t displayId,
+                                    std::string* name) override;
+  ndk::ScopedAStatus getDisplayVsyncPeriod(int64_t displayId,
+                                           int32_t* vsyncPeriod) override;
+  ndk::ScopedAStatus getDisplayedContentSample(
+      int64_t displayId, int64_t maxFrames, int64_t timestamp,
+      DisplayContentSample* samples) override;
+  ndk::ScopedAStatus getDisplayedContentSamplingAttributes(
+      int64_t displayId, DisplayContentSamplingAttributes* attrs) override;
+  ndk::ScopedAStatus getDisplayPhysicalOrientation(
+      int64_t displayId, common::Transform* orientation) override;
+  ndk::ScopedAStatus getHdrCapabilities(int64_t displayId,
+                                        HdrCapabilities* caps) override;
+  ndk::ScopedAStatus getMaxVirtualDisplayCount(int32_t* count) override;
+  ndk::ScopedAStatus getPerFrameMetadataKeys(
+      int64_t displayId, std::vector<PerFrameMetadataKey>* keys) override;
+  ndk::ScopedAStatus getReadbackBufferAttributes(
+      int64_t displayId, ReadbackBufferAttributes* attrs) override;
+  ndk::ScopedAStatus getReadbackBufferFence(
+      int64_t displayId, ndk::ScopedFileDescriptor* acquireFence) override;
+  ndk::ScopedAStatus getRenderIntents(
+      int64_t displayId, ColorMode mode,
+      std::vector<RenderIntent>* intents) override;
+  ndk::ScopedAStatus getSupportedContentTypes(
+      int64_t displayId, std::vector<ContentType>* types) override;
+  ndk::ScopedAStatus getDisplayDecorationSupport(
+      int64_t displayId,
+      std::optional<common::DisplayDecorationSupport>* support) override;
+  ndk::ScopedAStatus registerCallback(
+      const std::shared_ptr<IComposerCallback>& callback) override;
+  ndk::ScopedAStatus setActiveConfig(int64_t displayId,
+                                     int32_t config) override;
+  ndk::ScopedAStatus setActiveConfigWithConstraints(
+      int64_t displayId, int32_t config,
+      const VsyncPeriodChangeConstraints& constraints,
+      VsyncPeriodChangeTimeline* timeline) override;
+  ndk::ScopedAStatus setBootDisplayConfig(int64_t displayId,
+                                          int32_t config) override;
+  ndk::ScopedAStatus clearBootDisplayConfig(int64_t displayId) override;
+  ndk::ScopedAStatus getPreferredBootDisplayConfig(int64_t displayId,
+                                                   int32_t* config) override;
+  ndk::ScopedAStatus setAutoLowLatencyMode(int64_t displayId, bool on) override;
+  ndk::ScopedAStatus setClientTargetSlotCount(int64_t displayId,
+                                              int32_t count) override;
+  ndk::ScopedAStatus setColorMode(int64_t displayId, ColorMode mode,
+                                  RenderIntent intent) override;
+  ndk::ScopedAStatus setContentType(int64_t displayId,
+                                    ContentType type) override;
+  ndk::ScopedAStatus setDisplayedContentSamplingEnabled(
+      int64_t displayId, bool enable, FormatColorComponent componentMask,
+      int64_t maxFrames) override;
+  ndk::ScopedAStatus setPowerMode(int64_t displayId, PowerMode mode) override;
+  ndk::ScopedAStatus setReadbackBuffer(
+      int64_t displayId,
+      const aidl::android::hardware::common::NativeHandle& buffer,
+      const ndk::ScopedFileDescriptor& releaseFence) override;
+  ndk::ScopedAStatus setVsyncEnabled(int64_t displayId, bool enabled) override;
+  ndk::ScopedAStatus setIdleTimerEnabled(int64_t displayId,
+                                         int32_t timeoutMs) override;
+
+ protected:
+  ndk::SpAIBinder createBinder() override;
+
+ private:
+  class CommandResultWriter;
+
+  void executeDisplayCommand(const DisplayCommand& displayCommand);
+  void executeLayerCommand(Display* display, const LayerCommand& layerCommand);
+
+  void executeDisplayCommandSetColorTransform(Display* display,
+                                              const std::vector<float>& matrix);
+  void executeDisplayCommandSetBrightness(Display* display,
+                                          const DisplayBrightness& brightness);
+  void executeDisplayCommandSetClientTarget(Display* display,
+                                            const ClientTarget& command);
+  void executeDisplayCommandSetOutputBuffer(Display* display,
+                                            const Buffer& buffer);
+  void executeDisplayCommandValidateDisplay(
+      Display* display,
+      const std::optional<ClockMonotonicTimestamp> expectedPresentTime);
+  void executeDisplayCommandAcceptDisplayChanges(Display* display);
+  void executeDisplayCommandPresentOrValidateDisplay(
+      Display* display,
+      const std::optional<ClockMonotonicTimestamp> expectedPresentTime);
+  void executeDisplayCommandPresentDisplay(Display* display);
+
+  void executeLayerCommandSetLayerCursorPosition(
+      Display* display, Layer* layer, const common::Point& cursorPosition);
+  void executeLayerCommandSetLayerBuffer(Display* display, Layer* layer,
+                                         const Buffer& buffer);
+  void executeLayerCommandSetLayerSurfaceDamage(
+      Display* display, Layer* layer,
+      const std::vector<std::optional<common::Rect>>& damage);
+  void executeLayerCommandSetLayerBlendMode(
+      Display* display, Layer* layer, const ParcelableBlendMode& blendMode);
+  void executeLayerCommandSetLayerColor(Display* display, Layer* layer,
+                                        const Color& color);
+  void executeLayerCommandSetLayerComposition(
+      Display* display, Layer* layer, const ParcelableComposition& composition);
+  void executeLayerCommandSetLayerDataspace(
+      Display* display, Layer* layer, const ParcelableDataspace& dataspace);
+  void executeLayerCommandSetLayerDisplayFrame(Display* display, Layer* layer,
+                                               const common::Rect& rect);
+  void executeLayerCommandSetLayerPlaneAlpha(Display* display, Layer* layer,
+                                             const PlaneAlpha& planeAlpha);
+  void executeLayerCommandSetLayerSidebandStream(
+      Display* display, Layer* layer,
+      const aidl::android::hardware::common::NativeHandle& sidebandStream);
+  void executeLayerCommandSetLayerSourceCrop(Display* display, Layer* layer,
+                                             const common::FRect& sourceCrop);
+  void executeLayerCommandSetLayerTransform(
+      Display* display, Layer* layer, const ParcelableTransform& transform);
+  void executeLayerCommandSetLayerVisibleRegion(
+      Display* display, Layer* layer,
+      const std::vector<std::optional<common::Rect>>& visibleRegion);
+  void executeLayerCommandSetLayerZOrder(Display* display, Layer* layer,
+                                         const ZOrder& zOrder);
+  void executeLayerCommandSetLayerPerFrameMetadata(
+      Display* display, Layer* layer,
+      const std::vector<std::optional<PerFrameMetadata>>& perFrameMetadata);
+  void executeLayerCommandSetLayerColorTransform(
+      Display* display, Layer* layer, const std::vector<float>& colorTransform);
+  void executeLayerCommandSetLayerBrightness(Display* display, Layer* layer,
+                                             const LayerBrightness& brightness);
+  void executeLayerCommandSetLayerPerFrameMetadataBlobs(
+      Display* display, Layer* layer,
+      const std::vector<std::optional<PerFrameMetadataBlob>>&
+          perFrameMetadataBlob);
+
+  // Returns the display with the given id or nullptr if not found.
+  Display* getDisplay(int64_t displayId);
+
+  // Finds the Cuttlefish/Goldfish specific configuration and initializes the
+  // displays.
+  HWC3::Error createDisplaysLocked();
+
+  // Creates a display with the given properties.
+  HWC3::Error createDisplayLocked(int64_t displayId, int32_t activeConfigId,
+                                  const std::vector<DisplayConfig>& configs);
+
+  HWC3::Error destroyDisplaysLocked();
+
+  HWC3::Error destroyDisplayLocked(int64_t displayId);
+
+  HWC3::Error handleHotplug(bool connected,   //
+                            uint32_t id,      //
+                            uint32_t width,   //
+                            uint32_t height,  //
+                            uint32_t dpiX,    //
+                            uint32_t dpiY,    //
+                            uint32_t refreshRate);
+
+  std::mutex mStateMutex;
+
+  std::map<int64_t, std::unique_ptr<Display>> mDisplays;
+
+  // The onHotplug(), onVsync(), etc callbacks registered by SurfaceFlinger.
+  std::shared_ptr<IComposerCallback> mCallbacks;
+
+  std::function<void()> mOnClientDestroyed;
+
+  // Underlying interface for composing layers in the guest using libyuv or in
+  // the host using opengl. Owned by Device.
+  FrameComposer* mComposer = nullptr;
+
+  // For the duration of a executeCommands(), the helper used to collect
+  // individual command results.
+  std::unique_ptr<CommandResultWriter> mCommandResults;
+
+  // Manages importing and caching gralloc buffers for displays and layers.
+  std::unique_ptr<ComposerResources> mResources;
+};
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
\ No newline at end of file
diff --git a/system/hwc3/ComposerResources.cpp b/system/hwc3/ComposerResources.cpp
new file mode 100644
index 0000000..2e17c2c
--- /dev/null
+++ b/system/hwc3/ComposerResources.cpp
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include "ComposerResources.h"
+
+#include <aidlcommonsupport/NativeHandle.h>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+namespace {
+
+HWC3::Error toHwc3Error(
+    ::android::hardware::graphics::composer::V2_1::Error error) {
+  switch (error) {
+    case ::android::hardware::graphics::composer::V2_1::Error::NONE:
+      return HWC3::Error::None;
+    case ::android::hardware::graphics::composer::V2_1::Error::BAD_CONFIG:
+      return HWC3::Error::BadConfig;
+    case ::android::hardware::graphics::composer::V2_1::Error::BAD_DISPLAY:
+      return HWC3::Error::BadDisplay;
+    case ::android::hardware::graphics::composer::V2_1::Error::BAD_LAYER:
+      return HWC3::Error::BadLayer;
+    case ::android::hardware::graphics::composer::V2_1::Error::BAD_PARAMETER:
+      return HWC3::Error::BadParameter;
+    case ::android::hardware::graphics::composer::V2_1::Error::NO_RESOURCES:
+      return HWC3::Error::NoResources;
+    case ::android::hardware::graphics::composer::V2_1::Error::NOT_VALIDATED:
+      return HWC3::Error::NotValidated;
+    case ::android::hardware::graphics::composer::V2_1::Error::UNSUPPORTED:
+      return HWC3::Error::Unsupported;
+  }
+}
+
+::android::hardware::graphics::composer::V2_1::Display toHwc2Display(
+    int64_t displayId) {
+  return static_cast<::android::hardware::graphics::composer::V2_1::Display>(
+      displayId);
+}
+
+::android::hardware::graphics::composer::V2_1::Layer toHwc2Layer(
+    int64_t layerId) {
+  return static_cast<::android::hardware::graphics::composer::V2_1::Layer>(
+      layerId);
+}
+
+}  // namespace
+
+std::unique_ptr<ComposerResourceReleaser> ComposerResources::createReleaser(
+    bool isBuffer) {
+  return std::make_unique<ComposerResourceReleaser>(isBuffer);
+}
+
+HWC3::Error ComposerResources::init() {
+  mImpl = ::android::hardware::graphics::composer::V2_2::hal::
+      ComposerResources::create();
+  if (!mImpl) {
+    ALOGE("%s: failed to create underlying ComposerResources.", __FUNCTION__);
+    return HWC3::Error::NoResources;
+  }
+  return HWC3::Error::None;
+}
+
+void ComposerResources::clear(
+    ::android::hardware::graphics::composer::V2_2::hal::ComposerResources::
+        RemoveDisplay removeDisplay) {
+  mImpl->clear(removeDisplay);
+}
+
+bool ComposerResources::hasDisplay(int64_t displayId) {
+  ::android::hardware::graphics::composer::V2_1::Display display =
+      toHwc2Display(displayId);
+  return mImpl->hasDisplay(display);
+}
+
+HWC3::Error ComposerResources::addPhysicalDisplay(int64_t displayId) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, displayId);
+  ::android::hardware::graphics::composer::V2_1::Display display =
+      toHwc2Display(displayId);
+  return toHwc3Error(mImpl->addPhysicalDisplay(display));
+}
+
+HWC3::Error ComposerResources::addVirtualDisplay(
+    int64_t displayId, uint32_t outputBufferCacheSize) {
+  ::android::hardware::graphics::composer::V2_1::Display display =
+      toHwc2Display(displayId);
+  return toHwc3Error(mImpl->addVirtualDisplay(display, outputBufferCacheSize));
+}
+
+HWC3::Error ComposerResources::removeDisplay(int64_t displayId) {
+  ::android::hardware::graphics::composer::V2_1::Display display =
+      toHwc2Display(displayId);
+  return toHwc3Error(mImpl->removeDisplay(display));
+}
+
+HWC3::Error ComposerResources::setDisplayClientTargetCacheSize(
+    int64_t displayId, uint32_t clientTargetCacheSize) {
+  ::android::hardware::graphics::composer::V2_1::Display display =
+      toHwc2Display(displayId);
+  return toHwc3Error(
+      mImpl->setDisplayClientTargetCacheSize(display, clientTargetCacheSize));
+}
+
+HWC3::Error ComposerResources::getDisplayClientTargetCacheSize(
+    int64_t displayId, size_t* outCacheSize) {
+  ::android::hardware::graphics::composer::V2_1::Display display =
+      toHwc2Display(displayId);
+  return toHwc3Error(
+      mImpl->getDisplayClientTargetCacheSize(display, outCacheSize));
+}
+
+HWC3::Error ComposerResources::getDisplayOutputBufferCacheSize(
+    int64_t displayId, size_t* outCacheSize) {
+  ::android::hardware::graphics::composer::V2_1::Display display =
+      toHwc2Display(displayId);
+  return toHwc3Error(
+      mImpl->getDisplayOutputBufferCacheSize(display, outCacheSize));
+}
+
+HWC3::Error ComposerResources::addLayer(int64_t displayId, int64_t layerId,
+                                        uint32_t bufferCacheSize) {
+  DEBUG_LOG("%s: display:%" PRId64 " layer:%" PRId64, __FUNCTION__, displayId,
+            layerId);
+
+  ::android::hardware::graphics::composer::V2_1::Display display =
+      toHwc2Display(displayId);
+  ::android::hardware::graphics::composer::V2_1::Layer layer =
+      toHwc2Layer(layerId);
+  return toHwc3Error(mImpl->addLayer(display, layer, bufferCacheSize));
+}
+
+HWC3::Error ComposerResources::removeLayer(int64_t displayId, int64_t layerId) {
+  DEBUG_LOG("%s: display:%" PRId64 " layer:%" PRId64, __FUNCTION__, displayId,
+            layerId);
+
+  ::android::hardware::graphics::composer::V2_1::Display display =
+      toHwc2Display(displayId);
+  ::android::hardware::graphics::composer::V2_1::Layer layer =
+      toHwc2Layer(layerId);
+
+  return toHwc3Error(mImpl->removeLayer(display, layer));
+}
+
+void ComposerResources::setDisplayMustValidateState(int64_t displayId,
+                                                    bool mustValidate) {
+  ::android::hardware::graphics::composer::V2_1::Display display =
+      toHwc2Display(displayId);
+  mImpl->setDisplayMustValidateState(display, mustValidate);
+}
+
+bool ComposerResources::mustValidateDisplay(int64_t displayId) {
+  ::android::hardware::graphics::composer::V2_1::Display display =
+      toHwc2Display(displayId);
+  return mImpl->mustValidateDisplay(display);
+}
+
+HWC3::Error ComposerResources::getDisplayReadbackBuffer(
+    int64_t displayId,
+    const aidl::android::hardware::common::NativeHandle& handle,
+    buffer_handle_t* outHandle, ComposerResourceReleaser* releaser) {
+  ::android::hardware::graphics::composer::V2_1::Display display =
+      toHwc2Display(displayId);
+  return toHwc3Error(mImpl->getDisplayReadbackBuffer(
+      display, ::android::makeFromAidl(handle), outHandle,
+      releaser->getReplacedHandle()));
+}
+
+HWC3::Error ComposerResources::getDisplayClientTarget(
+    int64_t displayId, const Buffer& buffer, buffer_handle_t* outHandle,
+    ComposerResourceReleaser* releaser) {
+  ::android::hardware::graphics::composer::V2_1::Display display =
+      toHwc2Display(displayId);
+
+  const bool useCache = !buffer.handle.has_value();
+
+  buffer_handle_t bufferHandle = nullptr;
+  if (buffer.handle.has_value()) {
+    bufferHandle = ::android::makeFromAidl(*buffer.handle);
+  }
+
+  return toHwc3Error(mImpl->getDisplayClientTarget(
+      display, buffer.slot, useCache, bufferHandle, outHandle,
+      releaser->getReplacedHandle()));
+}
+
+HWC3::Error ComposerResources::getDisplayOutputBuffer(
+    int64_t displayId, const Buffer& buffer, buffer_handle_t* outHandle,
+    ComposerResourceReleaser* releaser) {
+  ::android::hardware::graphics::composer::V2_1::Display display =
+      toHwc2Display(displayId);
+
+  const bool useCache = !buffer.handle.has_value();
+
+  buffer_handle_t bufferHandle = nullptr;
+  if (buffer.handle.has_value()) {
+    bufferHandle = ::android::makeFromAidl(*buffer.handle);
+  }
+
+  return toHwc3Error(mImpl->getDisplayOutputBuffer(
+      display, buffer.slot, useCache, bufferHandle, outHandle,
+      releaser->getReplacedHandle()));
+}
+
+HWC3::Error ComposerResources::getLayerBuffer(
+    int64_t displayId, int64_t layerId, const Buffer& buffer,
+    buffer_handle_t* outHandle, ComposerResourceReleaser* releaser) {
+  DEBUG_LOG("%s: display:%" PRId64 " layer:%" PRId64, __FUNCTION__, displayId,
+            layerId);
+
+  ::android::hardware::graphics::composer::V2_1::Display display =
+      toHwc2Display(displayId);
+  ::android::hardware::graphics::composer::V2_1::Layer layer =
+      toHwc2Layer(layerId);
+
+  const bool useCache = !buffer.handle.has_value();
+
+  buffer_handle_t bufferHandle = nullptr;
+  if (buffer.handle.has_value()) {
+    bufferHandle = ::android::makeFromAidl(*buffer.handle);
+  }
+
+  DEBUG_LOG("%s fromCache:%s", __FUNCTION__, (useCache ? "yes" : "no"));
+  return toHwc3Error(mImpl->getLayerBuffer(display, layer, buffer.slot,
+                                           useCache, bufferHandle, outHandle,
+                                           releaser->getReplacedHandle()));
+}
+
+HWC3::Error ComposerResources::getLayerSidebandStream(
+    int64_t displayId, int64_t layerId,
+    const aidl::android::hardware::common::NativeHandle& handle,
+    buffer_handle_t* outHandle, ComposerResourceReleaser* releaser) {
+  DEBUG_LOG("%s: display:%" PRId64 " layer:%" PRId64, __FUNCTION__, displayId,
+            layerId);
+
+  ::android::hardware::graphics::composer::V2_1::Display display =
+      toHwc2Display(displayId);
+  ::android::hardware::graphics::composer::V2_1::Layer layer =
+      toHwc2Layer(layerId);
+  return toHwc3Error(mImpl->getLayerSidebandStream(
+      display, layer, ::android::makeFromAidl(handle), outHandle,
+      releaser->getReplacedHandle()));
+}
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
\ No newline at end of file
diff --git a/system/hwc3/ComposerResources.h b/system/hwc3/ComposerResources.h
new file mode 100644
index 0000000..faf6a4d
--- /dev/null
+++ b/system/hwc3/ComposerResources.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+// Thin wrappers around V2_2::hal::ComposerResources related classes that
+// return HWC3 error codes and accept HWC3 argument types.
+
+#ifndef ANDROID_HWC_COMPOSERRESOURCES_H
+#define ANDROID_HWC_COMPOSERRESOURCES_H
+
+// Must include our LOG_TAG first:
+// clang-format off
+#include "Common.h"
+#include <composer-resources/2.2/ComposerResources.h>
+// clang-format on
+
+#include <memory>
+#include <optional>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class ComposerResourceReleaser {
+ public:
+  ComposerResourceReleaser(bool isBuffer) : mReplacedHandle(isBuffer) {}
+  virtual ~ComposerResourceReleaser() = default;
+
+  ::android::hardware::graphics::composer::V2_2::hal::ComposerResources::
+      ReplacedHandle*
+      getReplacedHandle() {
+    return &mReplacedHandle;
+  }
+
+ private:
+  ::android::hardware::graphics::composer::V2_2::hal::ComposerResources::
+      ReplacedHandle mReplacedHandle;
+};
+
+class ComposerResources {
+ public:
+  ComposerResources() = default;
+
+  HWC3::Error init();
+
+  std::unique_ptr<ComposerResourceReleaser> createReleaser(bool isBuffer);
+
+  void clear(::android::hardware::graphics::composer::V2_2::hal::
+                 ComposerResources::RemoveDisplay removeDisplay);
+
+  bool hasDisplay(int64_t display);
+
+  HWC3::Error addPhysicalDisplay(int64_t display);
+
+  HWC3::Error addVirtualDisplay(int64_t displayId,
+                                uint32_t outputBufferCacheSize);
+
+  HWC3::Error removeDisplay(int64_t display);
+
+  HWC3::Error setDisplayClientTargetCacheSize(int64_t displayId,
+                                              uint32_t clientTargetCacheSize);
+
+  HWC3::Error getDisplayClientTargetCacheSize(int64_t displayId,
+                                              size_t* outCacheSize);
+
+  HWC3::Error getDisplayOutputBufferCacheSize(int64_t displayId,
+                                              size_t* outCacheSize);
+
+  HWC3::Error addLayer(int64_t displayId, int64_t layerId,
+                       uint32_t bufferCacheSize);
+
+  HWC3::Error removeLayer(int64_t displayId, int64_t layer);
+
+  void setDisplayMustValidateState(int64_t displayId, bool mustValidate);
+
+  bool mustValidateDisplay(int64_t displayId);
+
+  HWC3::Error getDisplayReadbackBuffer(
+      int64_t displayId,
+      const aidl::android::hardware::common::NativeHandle& handle,
+      buffer_handle_t* outHandle, ComposerResourceReleaser* bufReleaser);
+
+  HWC3::Error getDisplayClientTarget(int64_t displayId, const Buffer& buffer,
+                                     buffer_handle_t* outHandle,
+                                     ComposerResourceReleaser* bufReleaser);
+
+  HWC3::Error getDisplayOutputBuffer(int64_t displayId, const Buffer& buffer,
+                                     buffer_handle_t* outHandle,
+                                     ComposerResourceReleaser* bufReleaser);
+
+  HWC3::Error getLayerBuffer(int64_t displayId, int64_t layerId,
+                             const Buffer& buffer,
+                             buffer_handle_t* outBufferHandle,
+                             ComposerResourceReleaser* bufReleaser);
+
+  HWC3::Error getLayerSidebandStream(
+      int64_t displayId, int64_t layerId,
+      const aidl::android::hardware::common::NativeHandle& rawHandle,
+      buffer_handle_t* outStreamHandle, ComposerResourceReleaser* bufReleaser);
+
+ private:
+  std::unique_ptr<
+      ::android::hardware::graphics::composer::V2_2::hal::ComposerResources>
+      mImpl;
+};
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
\ No newline at end of file
diff --git a/system/hwc3/Device.cpp b/system/hwc3/Device.cpp
new file mode 100644
index 0000000..cf4be42
--- /dev/null
+++ b/system/hwc3/Device.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include "Device.h"
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <json/json.h>
+
+#include "ClientFrameComposer.h"
+#include "FrameComposer.h"
+#include "GuestFrameComposer.h"
+#include "HostFrameComposer.h"
+#include "NoOpFrameComposer.h"
+
+ANDROID_SINGLETON_STATIC_INSTANCE(
+    aidl::android::hardware::graphics::composer3::impl::Device);
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+namespace {
+
+bool shouldUseGuestComposer() {
+  return ::android::base::GetProperty("ro.hardware.vulkan", "") == "pastel";
+}
+
+std::string getPmemPath() {
+  return ::android::base::GetProperty("ro.vendor.hwcomposer.pmem", "");
+}
+
+HWC3::Error loadPersistentKeyValues(Json::Value* dictionary) {
+  *dictionary = Json::Value(Json::ValueType::objectValue);
+
+  const std::string path = getPmemPath();
+  if (path.empty()) {
+    ALOGE("%s: persistent key-value store path not available.", __FUNCTION__);
+    return HWC3::Error::NoResources;
+  }
+
+  std::string content;
+  if (!::android::base::ReadFileToString(path, &content)) {
+    ALOGE("%s: failed to read key-value store from %s", __FUNCTION__,
+          path.c_str());
+    return HWC3::Error::NoResources;
+  }
+
+  if (content.empty() || content[0] == '\0') {
+    return HWC3::Error::None;
+  }
+
+  Json::Reader reader;
+  if (!reader.parse(content, *dictionary)) {
+    const std::string error = reader.getFormattedErrorMessages();
+    ALOGE("%s: failed to parse key-value store from %s:%s", __FUNCTION__,
+          path.c_str(), error.c_str());
+    return HWC3::Error::NoResources;
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error savePersistentKeyValues(const Json::Value& dictionary) {
+  const std::string path = getPmemPath();
+  if (path.empty()) {
+    ALOGE("%s: persistent key-value store path not available.", __FUNCTION__);
+    return HWC3::Error::NoResources;
+  }
+
+  const std::string contents = dictionary.toStyledString();
+  if (!::android::base::WriteStringToFile(contents, path)) {
+    ALOGE("%s: failed to write key-value store to %s", __FUNCTION__,
+          path.c_str());
+    return HWC3::Error::NoResources;
+  }
+
+  return HWC3::Error::None;
+}
+
+}  // namespace
+
+HWC3::Error Device::getComposer(FrameComposer** outComposer) {
+  std::unique_lock<std::mutex> lock(mMutex);
+
+  if (mComposer == nullptr) {
+    if (IsNoOpMode()) {
+      DEBUG_LOG("%s: using NoOpFrameComposer", __FUNCTION__);
+      mComposer = std::make_unique<NoOpFrameComposer>();
+    } else if (IsClientCompositionMode()) {
+      DEBUG_LOG("%s: using ClientFrameComposer", __FUNCTION__);
+      mComposer = std::make_unique<ClientFrameComposer>();
+    } else if (shouldUseGuestComposer()) {
+      DEBUG_LOG("%s: using GuestFrameComposer", __FUNCTION__);
+      mComposer = std::make_unique<GuestFrameComposer>();
+    } else {
+      DEBUG_LOG("%s: using HostFrameComposer", __FUNCTION__);
+      mComposer = std::make_unique<HostFrameComposer>();
+    }
+    if (!mComposer) {
+      ALOGE("%s failed to allocate FrameComposer", __FUNCTION__);
+      return HWC3::Error::NoResources;
+    }
+
+    HWC3::Error error = mComposer->init();
+    if (error != HWC3::Error::None) {
+      ALOGE("%s failed to init FrameComposer", __FUNCTION__);
+      return error;
+    }
+  }
+
+  *outComposer = mComposer.get();
+  return HWC3::Error::None;
+}
+
+HWC3::Error Device::getPersistentKeyValue(const std::string& key,
+                                          const std::string& defaultValue,
+                                          std::string* outValue) {
+  std::unique_lock<std::mutex> lock(mMutex);
+
+  Json::Value dictionary;
+
+  HWC3::Error error = loadPersistentKeyValues(&dictionary);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: failed to load pmem json", __FUNCTION__);
+    return error;
+  }
+
+  if (!dictionary.isMember(key)) {
+    *outValue = defaultValue;
+    return HWC3::Error::None;
+  }
+
+  *outValue = defaultValue;
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error Device::setPersistentKeyValue(const std::string& key,
+                                          const std::string& value) {
+  std::unique_lock<std::mutex> lock(mMutex);
+
+  Json::Value dictionary;
+
+  HWC3::Error error = loadPersistentKeyValues(&dictionary);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: failed to load pmem json", __FUNCTION__);
+    return error;
+  }
+
+  dictionary[key] = value;
+
+  error = savePersistentKeyValues(dictionary);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: failed to save pmem json", __FUNCTION__);
+    return error;
+  }
+
+  return HWC3::Error::None;
+}
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/Device.h b/system/hwc3/Device.h
new file mode 100644
index 0000000..6a3d064
--- /dev/null
+++ b/system/hwc3/Device.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWC_DEVICE_H
+#define ANDROID_HWC_DEVICE_H
+
+#include <utils/Singleton.h>
+
+#include <memory>
+#include <thread>
+
+#include "Common.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class FrameComposer;
+
+// Provides resources that are stable for the duration of the virtual
+// device.
+class Device : public ::android::Singleton<Device> {
+ public:
+  virtual ~Device() = default;
+
+  HWC3::Error getComposer(FrameComposer** outComposer);
+
+  HWC3::Error getPersistentKeyValue(const std::string& key,
+                                    const std::string& defaultVal,
+                                    std::string* outValue);
+
+  HWC3::Error setPersistentKeyValue(const std::string& key,
+                                    const std::string& outValue);
+
+ private:
+  friend class Singleton<Device>;
+  Device() = default;
+
+  std::mutex mMutex;
+  std::unique_ptr<FrameComposer> mComposer;
+};
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
\ No newline at end of file
diff --git a/system/hwc3/Display.cpp b/system/hwc3/Display.cpp
new file mode 100644
index 0000000..2281e49
--- /dev/null
+++ b/system/hwc3/Display.cpp
@@ -0,0 +1,1050 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#include "Display.h"
+
+#include <android-base/parseint.h>
+#include <android-base/unique_fd.h>
+#include <pthread.h>
+#include <sched.h>
+#include <sync/sync.h>
+#include <sys/types.h>
+
+#include <algorithm>
+#include <atomic>
+#include <numeric>
+#include <sstream>
+#include <thread>
+
+#include "Common.h"
+#include "Device.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+namespace {
+
+bool isValidColorMode(ColorMode mode) {
+  switch (mode) {
+    case ColorMode::NATIVE:
+    case ColorMode::STANDARD_BT601_625:
+    case ColorMode::STANDARD_BT601_625_UNADJUSTED:
+    case ColorMode::STANDARD_BT601_525:
+    case ColorMode::STANDARD_BT601_525_UNADJUSTED:
+    case ColorMode::STANDARD_BT709:
+    case ColorMode::DCI_P3:
+    case ColorMode::SRGB:
+    case ColorMode::ADOBE_RGB:
+    case ColorMode::DISPLAY_P3:
+    case ColorMode::BT2020:
+    case ColorMode::BT2100_PQ:
+    case ColorMode::BT2100_HLG:
+    case ColorMode::DISPLAY_BT2020:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool isValidRenderIntent(RenderIntent intent) {
+  switch (intent) {
+    case RenderIntent::COLORIMETRIC:
+    case RenderIntent::ENHANCE:
+    case RenderIntent::TONE_MAP_COLORIMETRIC:
+    case RenderIntent::TONE_MAP_ENHANCE:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool isValidPowerMode(PowerMode mode) {
+  switch (mode) {
+    case PowerMode::OFF:
+    case PowerMode::DOZE:
+    case PowerMode::DOZE_SUSPEND:
+    case PowerMode::ON:
+    case PowerMode::ON_SUSPEND:
+      return true;
+    default:
+      return false;
+  }
+}
+
+}  // namespace
+
+Display::Display(FrameComposer* composer, int64_t id)
+    : mComposer(composer), mId(id), mVsyncThread(id) {
+  setLegacyEdid();
+}
+
+Display::~Display() {}
+
+HWC3::Error Display::init(const std::vector<DisplayConfig>& configs,
+                          int32_t activeConfigId,
+                          const std::optional<std::vector<uint8_t>>& edid) {
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  for (const DisplayConfig& config : configs) {
+    mConfigs.emplace(config.getId(), config);
+  }
+
+  mActiveConfigId = activeConfigId;
+
+  auto bootConfigIdOpt = getBootConfigId();
+  if (bootConfigIdOpt) {
+    mActiveConfigId = *bootConfigIdOpt;
+  }
+
+  if (edid.has_value()) {
+    mEdid = *edid;
+  }
+
+  auto it = mConfigs.find(activeConfigId);
+  if (it == mConfigs.end()) {
+    ALOGE("%s: display:%" PRId64 "missing config:%" PRId32, __FUNCTION__, mId,
+          activeConfigId);
+    return HWC3::Error::NoResources;
+  }
+
+  const auto& activeConfig = it->second;
+  const auto activeConfigString = activeConfig.toString();
+  ALOGD("%s display:%" PRId64 " with config:%s", __FUNCTION__, mId,
+        activeConfigString.c_str());
+
+  mVsyncThread.start(activeConfig.getVsyncPeriod());
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::updateParameters(
+    uint32_t width, uint32_t height, uint32_t dpiX, uint32_t dpiY,
+    uint32_t refreshRateHz, const std::optional<std::vector<uint8_t>>& edid) {
+  DEBUG_LOG("%s: updating display:%" PRId64
+            " width:%d height:%d dpiX:%d dpiY:%d refreshRateHz:%d",
+            __FUNCTION__, mId, width, height, dpiX, dpiY, refreshRateHz);
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  auto it = mConfigs.find(*mActiveConfigId);
+  if (it == mConfigs.end()) {
+    ALOGE("%s: failed to find config %" PRId32, __func__, *mActiveConfigId);
+    return HWC3::Error::NoResources;
+  }
+  it->second.setAttribute(DisplayAttribute::VSYNC_PERIOD,
+                          1000 * 1000 * 1000 / refreshRateHz);
+  it->second.setAttribute(DisplayAttribute::WIDTH, width);
+  it->second.setAttribute(DisplayAttribute::HEIGHT, height);
+  it->second.setAttribute(DisplayAttribute::DPI_X, dpiX);
+  it->second.setAttribute(DisplayAttribute::DPI_Y, dpiY);
+
+  if (edid.has_value()) {
+    mEdid = *edid;
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::createLayer(int64_t* outLayerId) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  auto layer = std::make_unique<Layer>();
+
+  const int64_t layerId = layer->getId();
+  DEBUG_LOG("%s: created layer:%" PRId64, __FUNCTION__, layerId);
+
+  mLayers.emplace(layerId, std::move(layer));
+
+  *outLayerId = layerId;
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::destroyLayer(int64_t layerId) {
+  DEBUG_LOG("%s: destroy layer:%" PRId64, __FUNCTION__, layerId);
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  auto it = mLayers.find(layerId);
+  if (it == mLayers.end()) {
+    ALOGE("%s display:%" PRId64 " has no such layer:%." PRId64, __FUNCTION__,
+          mId, layerId);
+    return HWC3::Error::BadLayer;
+  }
+
+  mOrderedLayers.erase(std::remove_if(mOrderedLayers.begin(),  //
+                                      mOrderedLayers.end(),    //
+                                      [layerId](Layer* layer) {
+                                        return layer->getId() == layerId;
+                                      }),
+                       mOrderedLayers.end());
+
+  mLayers.erase(it);
+
+  DEBUG_LOG("%s: destroyed layer:%" PRId64, __FUNCTION__, layerId);
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::getActiveConfig(int32_t* outConfig) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  if (!mActiveConfigId) {
+    ALOGW("%s: display:%" PRId64 " has no active config.", __FUNCTION__, mId);
+    return HWC3::Error::BadConfig;
+  }
+
+  *outConfig = *mActiveConfigId;
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::getDisplayAttribute(int32_t configId,
+                                         DisplayAttribute attribute,
+                                         int32_t* outValue) {
+  auto attributeString = toString(attribute);
+  DEBUG_LOG("%s: display:%" PRId64 " attribute:%s", __FUNCTION__, mId,
+            attributeString.c_str());
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  auto it = mConfigs.find(configId);
+  if (it == mConfigs.end()) {
+    ALOGW("%s: display:%" PRId64 " bad config:%" PRId32, __FUNCTION__, mId,
+          configId);
+    return HWC3::Error::BadConfig;
+  }
+
+  const DisplayConfig& config = it->second;
+  *outValue = config.getAttribute(attribute);
+  DEBUG_LOG("%s: display:%" PRId64 " attribute:%s value is %" PRIi32,
+            __FUNCTION__, mId, attributeString.c_str(), *outValue);
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::getColorModes(std::vector<ColorMode>* outModes) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  outModes->clear();
+  outModes->insert(outModes->end(), mColorModes.begin(), mColorModes.end());
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::getDisplayCapabilities(
+    std::vector<DisplayCapability>* outCapabilities) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  outCapabilities->clear();
+  outCapabilities->push_back(DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::getDisplayConfigs(std::vector<int32_t>* outConfigIds) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  outConfigIds->clear();
+  outConfigIds->reserve(mConfigs.size());
+  for (const auto& [configId, _] : mConfigs) {
+    outConfigIds->push_back(configId);
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::getDisplayConnectionType(DisplayConnectionType* outType) {
+  if (IsCuttlefishFoldable()) {
+    // Workaround to force all displays to INTERNAL for cf_x86_64_foldable.
+    // TODO(b/193568008): Allow configuring internal/external per display.
+    *outType = DisplayConnectionType::INTERNAL;
+  } else {
+    // Other devices default to the first display INTERNAL, others EXTERNAL.
+    *outType = mId == 0 ? DisplayConnectionType::INTERNAL
+                        : DisplayConnectionType::EXTERNAL;
+  }
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::getDisplayIdentificationData(
+    DisplayIdentification* outIdentification) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  if (outIdentification == nullptr) {
+    return HWC3::Error::BadParameter;
+  }
+
+  outIdentification->port = mId;
+  outIdentification->data = mEdid;
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::getDisplayName(std::string* outName) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  *outName = mName;
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::getDisplayVsyncPeriod(int32_t* outVsyncPeriod) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  if (!mActiveConfigId) {
+    ALOGE("%s : display:%" PRId64 " no active config", __FUNCTION__, mId);
+    return HWC3::Error::BadConfig;
+  }
+
+  const auto it = mConfigs.find(*mActiveConfigId);
+  if (it == mConfigs.end()) {
+    ALOGE("%s : display:%" PRId64 " failed to find active config:%" PRId32,
+          __FUNCTION__, mId, *mActiveConfigId);
+    return HWC3::Error::BadConfig;
+  }
+  const DisplayConfig& activeConfig = it->second;
+
+  *outVsyncPeriod = activeConfig.getAttribute(DisplayAttribute::VSYNC_PERIOD);
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::getDisplayedContentSample(
+    int64_t /*maxFrames*/, int64_t /*timestamp*/,
+    DisplayContentSample* /*samples*/) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::getDisplayedContentSamplingAttributes(
+    DisplayContentSamplingAttributes* /*outAttributes*/) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::getDisplayPhysicalOrientation(
+    common::Transform* outOrientation) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  *outOrientation = common::Transform::NONE;
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::getHdrCapabilities(HdrCapabilities* outCapabilities) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  // No supported types.
+  outCapabilities->types.clear();
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::getPerFrameMetadataKeys(
+    std::vector<PerFrameMetadataKey>* outKeys) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  outKeys->clear();
+
+  return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::getReadbackBufferAttributes(
+    ReadbackBufferAttributes* outAttributes) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  outAttributes->format = common::PixelFormat::RGBA_8888;
+  outAttributes->dataspace = common::Dataspace::UNKNOWN;
+
+  return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::getReadbackBufferFence(
+    ndk::ScopedFileDescriptor* /*outAcquireFence*/) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::getRenderIntents(ColorMode mode,
+                                      std::vector<RenderIntent>* outIntents) {
+  const auto modeString = toString(mode);
+  DEBUG_LOG("%s: display:%" PRId64 "for mode:%s", __FUNCTION__, mId,
+            modeString.c_str());
+
+  outIntents->clear();
+
+  if (!isValidColorMode(mode)) {
+    DEBUG_LOG("%s: display:%" PRId64 "invalid mode:%s", __FUNCTION__, mId,
+              modeString.c_str());
+    return HWC3::Error::BadParameter;
+  }
+
+  outIntents->push_back(RenderIntent::COLORIMETRIC);
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::getSupportedContentTypes(
+    std::vector<ContentType>* outTypes) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  outTypes->clear();
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::getDecorationSupport(
+    std::optional<common::DisplayDecorationSupport>* outSupport) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  outSupport->reset();
+
+  return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::registerCallback(
+    const std::shared_ptr<IComposerCallback>& callback) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  mVsyncThread.setCallbacks(callback);
+
+  return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::setActiveConfig(int32_t configId) {
+  DEBUG_LOG("%s: display:%" PRId64 " setting active config to %" PRId32,
+            __FUNCTION__, mId, configId);
+
+  VsyncPeriodChangeConstraints constraints;
+  constraints.desiredTimeNanos = 0;
+  constraints.seamlessRequired = false;
+
+  VsyncPeriodChangeTimeline timeline;
+
+  return setActiveConfigWithConstraints(configId, constraints, &timeline);
+}
+
+HWC3::Error Display::setActiveConfigWithConstraints(
+    int32_t configId, const VsyncPeriodChangeConstraints& constraints,
+    VsyncPeriodChangeTimeline* outTimeline) {
+  DEBUG_LOG("%s: display:%" PRId64 " config:%" PRId32, __FUNCTION__, mId,
+            configId);
+
+  if (outTimeline == nullptr) {
+    return HWC3::Error::BadParameter;
+  }
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  if (mActiveConfigId == configId) {
+    return HWC3::Error::None;
+  }
+
+  DisplayConfig* newConfig = getConfig(configId);
+  if (newConfig == nullptr) {
+    ALOGE("%s: display:%" PRId64 " bad config:%" PRId32, __FUNCTION__, mId,
+          configId);
+    return HWC3::Error::BadConfig;
+  }
+
+  if (constraints.seamlessRequired) {
+    if (mActiveConfigId) {
+      DisplayConfig* oldConfig = getConfig(*mActiveConfigId);
+      if (oldConfig == nullptr) {
+        ALOGE("%s: display:%" PRId64 " missing config:%" PRId32, __FUNCTION__,
+              mId, *mActiveConfigId);
+        return HWC3::Error::NoResources;
+      }
+
+      const int32_t newConfigGroup = newConfig->getConfigGroup();
+      const int32_t oldConfigGroup = oldConfig->getConfigGroup();
+      if (newConfigGroup != oldConfigGroup) {
+        DEBUG_LOG("%s: display:%" PRId64 " config:%" PRId32
+                  " seamless not supported between different config groups "
+                  "old:%d vs new:%d",
+                  __FUNCTION__, mId, configId, oldConfigGroup, newConfigGroup);
+        return HWC3::Error::SeamlessNotAllowed;
+      }
+    }
+  }
+
+  mActiveConfigId = configId;
+
+  if (mComposer == nullptr) {
+    ALOGE("%s: display:%" PRId64 " missing composer", __FUNCTION__, mId);
+    return HWC3::Error::NoResources;
+  }
+
+  HWC3::Error error = mComposer->onActiveConfigChange(this);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRId64 " composer failed to handle config change",
+          __FUNCTION__, mId);
+    return error;
+  }
+
+  int32_t vsyncPeriod;
+  error = getDisplayVsyncPeriod(&vsyncPeriod);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRId64 " composer failed to handle config change",
+          __FUNCTION__, mId);
+    return error;
+  }
+
+  return mVsyncThread.scheduleVsyncUpdate(vsyncPeriod, constraints,
+                                          outTimeline);
+}
+
+std::optional<int32_t> Display::getBootConfigId() {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  std::string val;
+  HWC3::Error error = Device::getInstance().getPersistentKeyValue(
+      std::to_string(mId), "", &val);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRId64 " failed to get persistent boot config",
+          __FUNCTION__, mId);
+    return std::nullopt;
+  }
+
+  if (val.empty()) {
+    return std::nullopt;
+  }
+
+  int32_t configId = 0;
+  if (!::android::base::ParseInt(val, &configId)) {
+    ALOGE("%s: display:%" PRId64
+          " failed to parse persistent boot config from: %s",
+          __FUNCTION__, mId, val.c_str());
+    return std::nullopt;
+  }
+
+  if (!hasConfig(configId)) {
+    ALOGE("%s: display:%" PRId64 " invalid persistent boot config:%" PRId32,
+          __FUNCTION__, mId, configId);
+    return std::nullopt;
+  }
+
+  return configId;
+}
+
+HWC3::Error Display::setBootConfig(int32_t configId) {
+  DEBUG_LOG("%s: display:%" PRId64 " config:%" PRId32, __FUNCTION__, mId,
+            configId);
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  DisplayConfig* newConfig = getConfig(configId);
+  if (newConfig == nullptr) {
+    ALOGE("%s: display:%" PRId64 " bad config:%" PRId32, __FUNCTION__, mId,
+          configId);
+    return HWC3::Error::BadConfig;
+  }
+
+  const std::string key = std::to_string(mId);
+  const std::string val = std::to_string(configId);
+  HWC3::Error error = Device::getInstance().setPersistentKeyValue(key, val);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRId64 " failed to save persistent boot config",
+          __FUNCTION__, mId);
+    return error;
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::clearBootConfig() {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  const std::string key = std::to_string(mId);
+  const std::string val = "";
+  HWC3::Error error = Device::getInstance().setPersistentKeyValue(key, val);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRId64 " failed to save persistent boot config",
+          __FUNCTION__, mId);
+    return error;
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::getPreferredBootConfig(int32_t* outConfigId) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  std::vector<int32_t> configIds;
+  for (const auto [configId, _] : mConfigs) {
+    configIds.push_back(configId);
+  }
+  *outConfigId = *std::min_element(configIds.begin(), configIds.end());
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::setAutoLowLatencyMode(bool /*on*/) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::setColorMode(ColorMode mode, RenderIntent intent) {
+  const std::string modeString = toString(mode);
+  const std::string intentString = toString(intent);
+  DEBUG_LOG("%s: display:%" PRId64 " setting color mode:%s intent:%s",
+            __FUNCTION__, mId, modeString.c_str(), intentString.c_str());
+
+  if (!isValidColorMode(mode)) {
+    ALOGE("%s: display:%" PRId64 " invalid color mode:%s", __FUNCTION__, mId,
+          modeString.c_str());
+    return HWC3::Error::BadParameter;
+  }
+
+  if (!isValidRenderIntent(intent)) {
+    ALOGE("%s: display:%" PRId64 " invalid intent:%s", __FUNCTION__, mId,
+          intentString.c_str());
+    return HWC3::Error::BadParameter;
+  }
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  if (mColorModes.count(mode) == 0) {
+    ALOGE("%s: display %" PRId64 " mode %s not supported", __FUNCTION__, mId,
+          modeString.c_str());
+    return HWC3::Error::Unsupported;
+  }
+
+  mActiveColorMode = mode;
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::setContentType(ContentType contentType) {
+  auto contentTypeString = toString(contentType);
+  DEBUG_LOG("%s: display:%" PRId64 " content type:%s", __FUNCTION__, mId,
+            contentTypeString.c_str());
+
+  if (contentType != ContentType::NONE) {
+    return HWC3::Error::Unsupported;
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::setDisplayedContentSamplingEnabled(
+    bool /*enable*/, FormatColorComponent /*componentMask*/,
+    int64_t /*maxFrames*/) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::setPowerMode(PowerMode mode) {
+  auto modeString = toString(mode);
+  DEBUG_LOG("%s: display:%" PRId64 " to mode:%s", __FUNCTION__, mId,
+            modeString.c_str());
+
+  if (!isValidPowerMode(mode)) {
+    ALOGE("%s: display:%" PRId64 " invalid mode:%s", __FUNCTION__, mId,
+          modeString.c_str());
+    return HWC3::Error::BadParameter;
+  }
+
+  if (mode == PowerMode::DOZE || mode == PowerMode::DOZE_SUSPEND ||
+      mode == PowerMode::ON_SUSPEND) {
+    ALOGE("%s display %" PRId64 " mode:%s not supported", __FUNCTION__, mId,
+          modeString.c_str());
+    return HWC3::Error::Unsupported;
+  }
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  if (IsCuttlefish()) {
+    if (int fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC); fd != -1) {
+      std::ostringstream stream;
+      stream << "VIRTUAL_DEVICE_DISPLAY_POWER_MODE_CHANGED display=" << mId
+             << " mode=" << modeString;
+      std::string message = stream.str();
+      write(fd, message.c_str(), message.length());
+      close(fd);
+    }
+  }
+
+  mPowerMode = mode;
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::setReadbackBuffer(const buffer_handle_t buffer,
+                                       const ndk::ScopedFileDescriptor& fence) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  mReadbackBuffer.set(buffer, fence);
+
+  return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::setVsyncEnabled(bool enabled) {
+  DEBUG_LOG("%s: display:%" PRId64 " setting vsync %s", __FUNCTION__, mId,
+            (enabled ? "on" : "off"));
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  return mVsyncThread.setVsyncEnabled(enabled);
+}
+
+HWC3::Error Display::setIdleTimerEnabled(int32_t timeoutMs) {
+  DEBUG_LOG("%s: display:%" PRId64 " timeout:%" PRId32, __FUNCTION__, mId,
+            timeoutMs);
+
+  (void)timeoutMs;
+
+  return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::setColorTransform(
+    const std::vector<float>& transformMatrix) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  if (transformMatrix.size() < 16) {
+    ALOGE("%s: display:%" PRId64 " has non 4x4 matrix, size:%zu", __FUNCTION__,
+          mId, transformMatrix.size());
+    return HWC3::Error::BadParameter;
+  }
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  auto& colorTransform = mColorTransform.emplace();
+  std::copy_n(transformMatrix.data(), colorTransform.size(),
+              colorTransform.begin());
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::setBrightness(float brightness) {
+  DEBUG_LOG("%s: display:%" PRId64 " brightness:%f", __FUNCTION__, mId,
+            brightness);
+
+  if (brightness < 0.0f) {
+    ALOGE("%s: display:%" PRId64 " invalid brightness:%f", __FUNCTION__, mId,
+          brightness);
+    return HWC3::Error::BadParameter;
+  }
+
+  return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::setClientTarget(
+    buffer_handle_t buffer, const ndk::ScopedFileDescriptor& fence,
+    common::Dataspace /*dataspace*/,
+    const std::vector<common::Rect>& /*damage*/) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  mClientTarget.set(buffer, fence);
+
+  mComposer->onDisplayClientTargetSet(this);
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::setOutputBuffer(
+    buffer_handle_t /*buffer*/, const ndk::ScopedFileDescriptor& /*fence*/) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  // TODO: for virtual display
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::setExpectedPresentTime(
+    const std::optional<ClockMonotonicTimestamp>& expectedPresentTime) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  if (!expectedPresentTime.has_value()) {
+    return HWC3::Error::None;
+  }
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  mExpectedPresentTime.emplace(
+      asTimePoint(expectedPresentTime->timestampNanos));
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::validate(DisplayChanges* outChanges) {
+  ATRACE_CALL();
+
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  mPendingChanges.reset();
+
+  mOrderedLayers.clear();
+  mOrderedLayers.reserve(mLayers.size());
+  for (auto& [_, layerPtr] : mLayers) {
+    mOrderedLayers.push_back(layerPtr.get());
+  }
+  std::sort(mOrderedLayers.begin(), mOrderedLayers.end(),
+            [](const Layer* layerA, const Layer* layerB) {
+              const auto zA = layerA->getZOrder();
+              const auto zB = layerB->getZOrder();
+              if (zA != zB) {
+                return zA < zB;
+              }
+              return layerA->getId() < layerB->getId();
+            });
+
+  if (mComposer == nullptr) {
+    ALOGE("%s: display:%" PRId64 " missing composer", __FUNCTION__, mId);
+    return HWC3::Error::NoResources;
+  }
+
+  HWC3::Error error = mComposer->validateDisplay(this, &mPendingChanges);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRId64 " failed to validate", __FUNCTION__, mId);
+    return error;
+  }
+
+  if (mPendingChanges.hasAnyChanges()) {
+    mPresentFlowState = PresentFlowState::WAITING_FOR_ACCEPT;
+    DEBUG_LOG("%s: display:%" PRId64 " now WAITING_FOR_ACCEPT", __FUNCTION__,
+              mId);
+  } else {
+    mPresentFlowState = PresentFlowState::WAITING_FOR_PRESENT;
+    DEBUG_LOG("%s: display:%" PRId64 " now WAITING_FOR_PRESENT", __FUNCTION__,
+              mId);
+  }
+
+  *outChanges = mPendingChanges;
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::acceptChanges() {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  switch (mPresentFlowState) {
+    case PresentFlowState::WAITING_FOR_VALIDATE: {
+      ALOGE("%s: display %" PRId64 " failed, not validated", __FUNCTION__, mId);
+      return HWC3::Error::NotValidated;
+    }
+    case PresentFlowState::WAITING_FOR_ACCEPT:
+    case PresentFlowState::WAITING_FOR_PRESENT: {
+      break;
+    }
+  }
+
+  if (mPendingChanges.compositionChanges) {
+    const ChangedCompositionTypes& compositionChanges =
+        *mPendingChanges.compositionChanges;
+    for (const ChangedCompositionLayer& compositionChange :
+         compositionChanges.layers) {
+      const auto layerId = compositionChange.layer;
+      const auto layerComposition = compositionChange.composition;
+      auto* layer = getLayer(layerId);
+      if (layer == nullptr) {
+        ALOGE("%s: display:%" PRId64 " layer:%" PRId64
+              " dropped before acceptChanges()?",
+              __FUNCTION__, mId, layerId);
+        continue;
+      }
+
+      layer->setCompositionType(layerComposition);
+    }
+  }
+  mPendingChanges.reset();
+
+  mPresentFlowState = PresentFlowState::WAITING_FOR_PRESENT;
+  DEBUG_LOG("%s: display:%" PRId64 " now WAITING_FOR_PRESENT", __FUNCTION__,
+            mId);
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error Display::present(
+    ::android::base::unique_fd* outDisplayFence,
+    std::unordered_map<int64_t, ::android::base::unique_fd>* outLayerFences) {
+  ATRACE_CALL();
+
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  outDisplayFence->reset();
+  outLayerFences->clear();
+
+  std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+  switch (mPresentFlowState) {
+    case PresentFlowState::WAITING_FOR_VALIDATE: {
+      ALOGE("%s: display %" PRId64 " failed, not validated", __FUNCTION__, mId);
+      return HWC3::Error::NotValidated;
+    }
+    case PresentFlowState::WAITING_FOR_ACCEPT: {
+      ALOGE("%s: display %" PRId64 " failed, changes not accepted",
+            __FUNCTION__, mId);
+      return HWC3::Error::NotValidated;
+    }
+    case PresentFlowState::WAITING_FOR_PRESENT: {
+      break;
+    }
+  }
+  mPresentFlowState = PresentFlowState::WAITING_FOR_VALIDATE;
+  DEBUG_LOG("%s: display:%" PRId64 " now WAITING_FOR_VALIDATE", __FUNCTION__,
+            mId);
+
+  if (mComposer == nullptr) {
+    ALOGE("%s: display:%" PRId64 " missing composer", __FUNCTION__, mId);
+    return HWC3::Error::NoResources;
+  }
+
+  return mComposer->presentDisplay(this, outDisplayFence, outLayerFences);
+}
+
+bool Display::hasConfig(int32_t configId) const {
+  return mConfigs.find(configId) != mConfigs.end();
+}
+
+DisplayConfig* Display::getConfig(int32_t configId) {
+  auto it = mConfigs.find(configId);
+  if (it != mConfigs.end()) {
+    return &it->second;
+  }
+  return nullptr;
+}
+
+HWC3::Error Display::setEdid(std::vector<uint8_t> edid) {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  mEdid = edid;
+  return HWC3::Error::None;
+}
+
+void Display::setLegacyEdid() {
+  // thess EDIDs are carefully generated according to the EDID spec version 1.3,
+  // more info can be found from the following file:
+  //   frameworks/native/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
+  // approved pnp ids can be found here: https://uefi.org/pnp_id_list
+  // pnp id: GGL, name: EMU_display_0, last byte is checksum
+  // display id is local:8141603649153536
+  static constexpr const std::array<uint8_t, 128> kEdid0 = {
+      0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1c, 0xec, 0x01, 0x00,
+      0x01, 0x00, 0x00, 0x00, 0x1b, 0x10, 0x01, 0x03, 0x80, 0x50, 0x2d, 0x78,
+      0x0a, 0x0d, 0xc9, 0xa0, 0x57, 0x47, 0x98, 0x27, 0x12, 0x48, 0x4c, 0x00,
+      0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
+      0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0xfc, 0x00, 0x45, 0x4d, 0x55, 0x5f, 0x64, 0x69, 0x73,
+      0x70, 0x6c, 0x61, 0x79, 0x5f, 0x30, 0x00, 0x4b};
+
+  // pnp id: GGL, name: EMU_display_1
+  // display id is local:8140900251843329
+  static constexpr const std::array<uint8_t, 128> kEdid1 = {
+      0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1c, 0xec, 0x01, 0x00,
+      0x01, 0x00, 0x00, 0x00, 0x1b, 0x10, 0x01, 0x03, 0x80, 0x50, 0x2d, 0x78,
+      0x0a, 0x0d, 0xc9, 0xa0, 0x57, 0x47, 0x98, 0x27, 0x12, 0x48, 0x4c, 0x00,
+      0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
+      0x2d, 0x40, 0x58, 0x2c, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0xfc, 0x00, 0x45, 0x4d, 0x55, 0x5f, 0x64, 0x69, 0x73,
+      0x70, 0x6c, 0x61, 0x79, 0x5f, 0x31, 0x00, 0x3b};
+
+  // pnp id: GGL, name: EMU_display_2
+  // display id is local:8140940453066754
+  static constexpr const std::array<uint8_t, 128> kEdid2 = {
+      0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1c, 0xec, 0x01, 0x00,
+      0x01, 0x00, 0x00, 0x00, 0x1b, 0x10, 0x01, 0x03, 0x80, 0x50, 0x2d, 0x78,
+      0x0a, 0x0d, 0xc9, 0xa0, 0x57, 0x47, 0x98, 0x27, 0x12, 0x48, 0x4c, 0x00,
+      0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
+      0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0xfc, 0x00, 0x45, 0x4d, 0x55, 0x5f, 0x64, 0x69, 0x73,
+      0x70, 0x6c, 0x61, 0x79, 0x5f, 0x32, 0x00, 0x49};
+
+  mEdid.clear();
+  switch (mId) {
+    case 0: {
+      mEdid.insert(mEdid.end(), kEdid0.begin(), kEdid0.end());
+      break;
+    }
+    case 1: {
+      mEdid.insert(mEdid.end(), kEdid1.begin(), kEdid1.end());
+      break;
+    }
+    case 2: {
+      mEdid.insert(mEdid.end(), kEdid2.begin(), kEdid2.end());
+      break;
+    }
+    default: {
+      mEdid.insert(mEdid.end(), kEdid2.begin(), kEdid2.end());
+      const uint32_t size = mEdid.size();
+      // Update the name to EMU_display_<mID>
+      mEdid[size - 3] = '0' + (uint8_t)mId;
+      // Update the checksum byte
+      uint8_t checksum = -(uint8_t)std::accumulate(
+          mEdid.data(), mEdid.data() + size - 1, static_cast<uint8_t>(0));
+      mEdid[size - 1] = checksum;
+      break;
+    }
+  }
+}
+
+Layer* Display::getLayer(int64_t layerId) {
+  auto it = mLayers.find(layerId);
+  if (it == mLayers.end()) {
+    ALOGE("%s Unknown layer:%" PRId64, __FUNCTION__, layerId);
+    return nullptr;
+  }
+
+  return it->second.get();
+}
+
+buffer_handle_t Display::waitAndGetClientTargetBuffer() {
+  DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+  ::android::base::unique_fd fence = mClientTarget.getFence();
+  if (fence.ok()) {
+    int err = sync_wait(fence.get(), 3000);
+    if (err < 0 && errno == ETIME) {
+      ALOGE("%s waited on fence %" PRId32 " for 3000 ms", __FUNCTION__,
+            fence.get());
+    }
+  }
+
+  return mClientTarget.getBuffer();
+}
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/Display.h b/system/hwc3/Display.h
new file mode 100644
index 0000000..707bc19
--- /dev/null
+++ b/system/hwc3/Display.h
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWC_DISPLAY_H
+#define ANDROID_HWC_DISPLAY_H
+
+#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
+#include <aidl/android/hardware/graphics/composer3/ColorMode.h>
+#include <aidl/android/hardware/graphics/composer3/ContentType.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayAttribute.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayConnectionType.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayContentSample.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayIdentification.h>
+#include <aidl/android/hardware/graphics/composer3/HdrCapabilities.h>
+#include <aidl/android/hardware/graphics/composer3/PerFrameMetadataKey.h>
+#include <aidl/android/hardware/graphics/composer3/PowerMode.h>
+#include <aidl/android/hardware/graphics/composer3/ReadbackBufferAttributes.h>
+#include <aidl/android/hardware/graphics/composer3/RenderIntent.h>
+#include <aidl/android/hardware/graphics/composer3/VsyncPeriodChangeConstraints.h>
+#include <aidl/android/hardware/graphics/composer3/VsyncPeriodChangeTimeline.h>
+#include <android-base/unique_fd.h>
+
+#include <array>
+#include <mutex>
+#include <optional>
+#include <thread>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "Common.h"
+#include "DisplayChanges.h"
+#include "DisplayConfig.h"
+#include "DisplayFinder.h"
+#include "FencedBuffer.h"
+#include "FrameComposer.h"
+#include "Layer.h"
+#include "Time.h"
+#include "VsyncThread.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class FrameComposer;
+
+class Display {
+ public:
+  Display(FrameComposer* composer, int64_t id);
+  ~Display();
+
+  Display(const Display& display) = delete;
+  Display& operator=(const Display& display) = delete;
+
+  Display(Display&& display) = delete;
+  Display& operator=(Display&& display) = delete;
+
+  HWC3::Error init(
+      const std::vector<DisplayConfig>& configs, int32_t activeConfigId,
+      const std::optional<std::vector<uint8_t>>& edid = std::nullopt);
+
+  HWC3::Error updateParameters(
+      uint32_t width, uint32_t height, uint32_t dpiX, uint32_t dpiY,
+      uint32_t refreshRateHz,
+      const std::optional<std::vector<uint8_t>>& edid = std::nullopt);
+
+  // HWComposer3 interface.
+  HWC3::Error createLayer(int64_t* outLayerId);
+  HWC3::Error destroyLayer(int64_t layerId);
+  HWC3::Error getActiveConfig(int32_t* outConfigId);
+  HWC3::Error getDisplayAttribute(int32_t configId, DisplayAttribute attribute,
+                                  int32_t* outValue);
+  HWC3::Error getColorModes(std::vector<ColorMode>* outColorModes);
+  HWC3::Error getDisplayCapabilities(std::vector<DisplayCapability>* caps);
+  HWC3::Error getDisplayConfigs(std::vector<int32_t>* configs);
+  HWC3::Error getDisplayConnectionType(DisplayConnectionType* outType);
+  HWC3::Error getDisplayIdentificationData(
+      DisplayIdentification* outIdentification);
+  HWC3::Error getDisplayName(std::string* outName);
+  HWC3::Error getDisplayVsyncPeriod(int32_t* outVsyncPeriod);
+  HWC3::Error getDisplayedContentSample(int64_t maxFrames, int64_t timestamp,
+                                        DisplayContentSample* samples);
+  HWC3::Error getDisplayedContentSamplingAttributes(
+      DisplayContentSamplingAttributes* outAttributes);
+  HWC3::Error getDisplayPhysicalOrientation(common::Transform* outOrientation);
+  HWC3::Error getHdrCapabilities(HdrCapabilities* outCapabilities);
+  HWC3::Error getPerFrameMetadataKeys(
+      std::vector<PerFrameMetadataKey>* outKeys);
+  HWC3::Error getReadbackBufferAttributes(ReadbackBufferAttributes* attrs);
+  HWC3::Error getReadbackBufferFence(ndk::ScopedFileDescriptor* acquireFence);
+  HWC3::Error getRenderIntents(ColorMode mode,
+                               std::vector<RenderIntent>* intents);
+  HWC3::Error getSupportedContentTypes(std::vector<ContentType>* types);
+  HWC3::Error getDecorationSupport(
+      std::optional<common::DisplayDecorationSupport>* support);
+  HWC3::Error registerCallback(
+      const std::shared_ptr<IComposerCallback>& callback);
+  HWC3::Error setActiveConfig(int32_t configId);
+  HWC3::Error setActiveConfigWithConstraints(
+      int32_t config, const VsyncPeriodChangeConstraints& constraints,
+      VsyncPeriodChangeTimeline* outTimeline);
+  HWC3::Error setBootConfig(int32_t configId);
+  HWC3::Error clearBootConfig();
+  HWC3::Error getPreferredBootConfig(int32_t* outConfigId);
+  HWC3::Error setAutoLowLatencyMode(bool on);
+  HWC3::Error setColorMode(ColorMode mode, RenderIntent intent);
+  HWC3::Error setContentType(ContentType contentType);
+  HWC3::Error setDisplayedContentSamplingEnabled(
+      bool enable, FormatColorComponent componentMask, int64_t maxFrames);
+  HWC3::Error setPowerMode(PowerMode mode);
+  HWC3::Error setReadbackBuffer(const buffer_handle_t buffer,
+                                const ndk::ScopedFileDescriptor& releaseFence);
+  HWC3::Error setVsyncEnabled(bool enabled);
+  HWC3::Error setIdleTimerEnabled(int32_t timeoutMs);
+  HWC3::Error setColorTransform(const std::vector<float>& transform);
+  HWC3::Error setBrightness(float brightness);
+  HWC3::Error setClientTarget(buffer_handle_t buffer,
+                              const ndk::ScopedFileDescriptor& fence,
+                              common::Dataspace dataspace,
+                              const std::vector<common::Rect>& damage);
+  HWC3::Error setOutputBuffer(buffer_handle_t buffer,
+                              const ndk::ScopedFileDescriptor& fence);
+  HWC3::Error setExpectedPresentTime(
+      const std::optional<ClockMonotonicTimestamp>& expectedPresentTime);
+  HWC3::Error validate(DisplayChanges* outChanges);
+  HWC3::Error acceptChanges();
+  HWC3::Error present(
+      ::android::base::unique_fd* outDisplayFence,
+      std::unordered_map<int64_t, ::android::base::unique_fd>* outLayerFences);
+
+  // Non HWCComposer3 interface.
+  int64_t getId() const { return mId; }
+
+  Layer* getLayer(int64_t layerHandle);
+
+  HWC3::Error setEdid(std::vector<uint8_t> edid);
+
+  bool hasColorTransform() const { return mColorTransform.has_value(); }
+  std::array<float, 16> getColorTransform() const { return *mColorTransform; }
+
+  FencedBuffer& getClientTarget() { return mClientTarget; }
+  buffer_handle_t waitAndGetClientTargetBuffer();
+
+  const std::vector<Layer*>& getOrderedLayers() { return mOrderedLayers; }
+
+ private:
+  bool hasConfig(int32_t configId) const;
+  DisplayConfig* getConfig(int32_t configId);
+
+  std::optional<int32_t> getBootConfigId();
+
+  void setLegacyEdid();
+
+  // The state of this display should only be modified from
+  // SurfaceFlinger's main loop, with the exception of when dump is
+  // called. To prevent a bad state from crashing us during a dump
+  // call, all public calls into Display must acquire this mutex.
+  mutable std::recursive_mutex mStateMutex;
+
+  FrameComposer* mComposer = nullptr;
+  const int64_t mId;
+  std::string mName;
+  PowerMode mPowerMode = PowerMode::OFF;
+  VsyncThread mVsyncThread;
+  FencedBuffer mClientTarget;
+  FencedBuffer mReadbackBuffer;
+  // Will only be non-null after the Display has been validated and
+  // before it has been accepted.
+  enum class PresentFlowState {
+    WAITING_FOR_VALIDATE,
+    WAITING_FOR_ACCEPT,
+    WAITING_FOR_PRESENT,
+  };
+  PresentFlowState mPresentFlowState = PresentFlowState::WAITING_FOR_VALIDATE;
+  DisplayChanges mPendingChanges;
+  std::optional<TimePoint> mExpectedPresentTime;
+  std::unordered_map<int64_t, std::unique_ptr<Layer>> mLayers;
+  // Ordered layers available after validate().
+  std::vector<Layer*> mOrderedLayers;
+  std::optional<int32_t> mActiveConfigId;
+  std::unordered_map<int32_t, DisplayConfig> mConfigs;
+  std::unordered_set<ColorMode> mColorModes = {ColorMode::NATIVE};
+  ColorMode mActiveColorMode = ColorMode::NATIVE;
+  std::optional<std::array<float, 16>> mColorTransform;
+  std::vector<uint8_t> mEdid;
+};
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/DisplayChanges.h b/system/hwc3/DisplayChanges.h
new file mode 100644
index 0000000..2745a4d
--- /dev/null
+++ b/system/hwc3/DisplayChanges.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWC_DISPLAYCHANGES_H
+#define ANDROID_HWC_DISPLAYCHANGES_H
+
+#include <aidl/android/hardware/graphics/composer3/ChangedCompositionLayer.h>
+#include <aidl/android/hardware/graphics/composer3/ChangedCompositionTypes.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayRequest.h>
+
+#include <optional>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+struct DisplayChanges {
+  std::optional<ChangedCompositionTypes> compositionChanges;
+  std::optional<DisplayRequest> displayRequestChanges;
+
+  void addLayerCompositionChange(int64_t displayId, int64_t layerId,
+                                 Composition layerComposition) {
+    if (!compositionChanges) {
+      compositionChanges.emplace();
+      compositionChanges->display = displayId;
+    }
+
+    ChangedCompositionLayer compositionChange;
+    compositionChange.layer = layerId;
+    compositionChange.composition = layerComposition;
+    compositionChanges->layers.emplace_back(std::move(compositionChange));
+  }
+
+  void clearLayerCompositionChanges() { compositionChanges.reset(); }
+
+  bool hasAnyChanges() const {
+    return compositionChanges.has_value() || displayRequestChanges.has_value();
+  }
+
+  void reset() {
+    compositionChanges.reset();
+    displayRequestChanges.reset();
+  }
+};
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
\ No newline at end of file
diff --git a/system/hwc3/DisplayConfig.cpp b/system/hwc3/DisplayConfig.cpp
new file mode 100644
index 0000000..3605010
--- /dev/null
+++ b/system/hwc3/DisplayConfig.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#include "DisplayConfig.h"
+
+#include <unordered_map>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+namespace {
+
+template <class T>
+inline void hashCombine(size_t& hash, const T& value) {
+  std::hash<T> hasher;
+  hash ^= hasher(value) + 0x9e3779b9 + (hash << 6) + (hash >> 2);
+}
+
+}  // namespace
+
+void DisplayConfig::setAttribute(DisplayAttribute attribute, int32_t value) {
+  if (attribute == DisplayAttribute::WIDTH) {
+    mWidth = value;
+  }
+  if (attribute == DisplayAttribute::HEIGHT) {
+    mHeight = value;
+  }
+  if (attribute == DisplayAttribute::DPI_X) {
+    mDpiX = value;
+  }
+  if (attribute == DisplayAttribute::DPI_Y) {
+    mDpiY = value;
+  }
+  if (attribute == DisplayAttribute::VSYNC_PERIOD) {
+    mVsyncPeriodNanos = value;
+  }
+  if (attribute == DisplayAttribute::CONFIG_GROUP) {
+    mConfigGroup = value;
+  }
+}
+
+int32_t DisplayConfig::getAttribute(DisplayAttribute attribute) const {
+  if (attribute == DisplayAttribute::WIDTH) {
+    return mWidth;
+  }
+  if (attribute == DisplayAttribute::HEIGHT) {
+    return mHeight;
+  }
+  if (attribute == DisplayAttribute::DPI_X) {
+    // From hwcomposer2.h, HWC2_ATTRIBUTE_DPI_X returns "Dots per thousand
+    // inches (DPI * 1000)".
+    return getDotsPerThousandInchesX();
+  }
+  if (attribute == DisplayAttribute::DPI_Y) {
+    // From hwcomposer2.h, HWC2_ATTRIBUTE_DPI_Y returns "Dots per thousand
+    // inches (DPI * 1000)"
+    return getDotsPerThousandInchesY();
+  }
+  if (attribute == DisplayAttribute::VSYNC_PERIOD) {
+    return mVsyncPeriodNanos;
+  }
+  if (attribute == DisplayAttribute::CONFIG_GROUP) {
+    return mConfigGroup;
+  }
+  return -1;
+}
+
+std::string DisplayConfig::toString() const {
+  std::string output;
+  output += " id: " + std::to_string(mId);
+  output += " w:" + std::to_string(mWidth);
+  output += " h:" + std::to_string(mHeight);
+  output += " dpi-x:" + std::to_string(mDpiX);
+  output += " dpi-y:" + std::to_string(mDpiY);
+  output += " vsync:" + std::to_string(1e9 / mVsyncPeriodNanos);
+  output += " config-group:" + std::to_string(mConfigGroup);
+  return output;
+}
+
+/*static*/
+void DisplayConfig::addConfigGroups(std::vector<DisplayConfig>* configs) {
+  // From /hardware/interfaces/graphics/composer/2.4/IComposerClient.hal:
+  // "Configurations which share the same config group are similar in all
+  // attributes except for the vsync period."
+  struct ConfigForGroupHash {
+    size_t operator()(const DisplayConfig& config) const {
+      size_t hash = 0;
+      hashCombine(hash, config.mWidth);
+      hashCombine(hash, config.mHeight);
+      hashCombine(hash, config.mDpiX);
+      hashCombine(hash, config.mDpiY);
+      return hash;
+    }
+  };
+  struct ConfigForGroupEq {
+    size_t operator()(const DisplayConfig& a, const DisplayConfig& b) const {
+      if (a.mWidth != b.mWidth) {
+        return a.mWidth < b.mWidth;
+      }
+      if (a.mHeight != b.mHeight) {
+        return a.mHeight < b.mHeight;
+      }
+      if (a.mDpiX != b.mDpiX) {
+        return a.mDpiX < b.mDpiX;
+      }
+      return a.mDpiY < b.mDpiY;
+    }
+  };
+
+  std::unordered_map<DisplayConfig, int32_t, ConfigForGroupHash,
+                     ConfigForGroupEq>
+      configToConfigGroup;
+
+  for (auto& config : *configs) {
+    auto [it, inserted] =
+        configToConfigGroup.try_emplace(config, configToConfigGroup.size());
+    config.setConfigGroup(it->second);
+  }
+}
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/DisplayConfig.h b/system/hwc3/DisplayConfig.h
new file mode 100644
index 0000000..7130fff
--- /dev/null
+++ b/system/hwc3/DisplayConfig.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWC_DISPLAYCONFIG_H
+#define ANDROID_HWC_DISPLAYCONFIG_H
+
+#include <aidl/android/hardware/graphics/composer3/DisplayAttribute.h>
+
+#include <vector>
+
+#include "Common.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class DisplayConfig {
+ public:
+  DisplayConfig(int32_t configId) : mId(configId) {}
+
+  DisplayConfig(int32_t configId, int32_t width, int32_t height, int32_t dpiX,
+                int32_t dpiY, int32_t vsyncPeriodNanos)
+      : mId(configId),
+        mWidth(width),
+        mHeight(height),
+        mDpiX(dpiX),
+        mDpiY(dpiY),
+        mVsyncPeriodNanos(vsyncPeriodNanos) {}
+
+  DisplayConfig(const DisplayConfig& other) = default;
+  DisplayConfig& operator=(DisplayConfig& other) = default;
+
+  DisplayConfig(DisplayConfig&& other) = default;
+  DisplayConfig& operator=(DisplayConfig&& other) = default;
+
+  int32_t getId() const { return mId; }
+  void setId(int32_t id) { mId = id; }
+
+  int32_t getAttribute(DisplayAttribute attribute) const;
+  void setAttribute(DisplayAttribute attribute, int32_t value);
+
+  int32_t getWidth() const { return mWidth; }
+  void setWidth(int32_t width) { mWidth = width; }
+
+  int32_t getHeight() const { return mHeight; }
+  void getHeight(int32_t height) { mHeight = height; }
+
+  int32_t getDpiX() const { return mDpiX; }
+  void setDpiX(int32_t dpi) { mDpiX = dpi; }
+
+  int32_t getDpiY() const { return mDpiY; }
+  void setDpiY(int32_t dpi) { mDpiY = dpi; }
+
+  int32_t getDotsPerThousandInchesX() const { return mDpiX * 1000; }
+  int32_t getDotsPerThousandInchesY() const { return mDpiY * 1000; }
+
+  int32_t getVsyncPeriod() const { return mVsyncPeriodNanos; }
+  void setVsyncPeriod(int32_t vsync) { mVsyncPeriodNanos = vsync; }
+
+  int32_t getConfigGroup() const { return mConfigGroup; }
+  void setConfigGroup(int32_t group) { mConfigGroup = group; }
+
+  std::string toString() const;
+
+  static void addConfigGroups(std::vector<DisplayConfig>* configs);
+
+ private:
+  int32_t mId;
+  int32_t mWidth;
+  int32_t mHeight;
+  int32_t mDpiX;
+  int32_t mDpiY;
+  int32_t mVsyncPeriodNanos;
+  int32_t mConfigGroup;
+};
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
\ No newline at end of file
diff --git a/system/hwc3/DisplayFinder.cpp b/system/hwc3/DisplayFinder.cpp
new file mode 100644
index 0000000..8e252cb
--- /dev/null
+++ b/system/hwc3/DisplayFinder.cpp
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#include "DisplayFinder.h"
+
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <device_config_shared.h>
+
+#include "Common.h"
+#include "HostUtils.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+namespace {
+
+constexpr int32_t HertzToPeriodNanos(uint32_t hertz) {
+  return 1000 * 1000 * 1000 / hertz;
+}
+
+HWC3::Error findCuttlefishDisplays(std::vector<DisplayMultiConfigs>& displays) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  // TODO: replace with initializing directly from DRM info.
+  const auto deviceConfig = cuttlefish::GetDeviceConfig();
+
+  int64_t displayId = 0;
+  for (const auto& deviceDisplayConfig : deviceConfig.display_config()) {
+    const auto vsyncPeriodNanos =
+        HertzToPeriodNanos(deviceDisplayConfig.refresh_rate_hz());
+
+    DisplayMultiConfigs display = {
+        .displayId = displayId,
+        .activeConfigId = 0,
+        .configs =
+            {
+                DisplayConfig(0,                             //
+                              deviceDisplayConfig.width(),   //
+                              deviceDisplayConfig.height(),  //
+                              deviceDisplayConfig.dpi(),     //
+                              deviceDisplayConfig.dpi(),     //
+                              vsyncPeriodNanos),
+            },
+    };
+    displays.push_back(display);
+    ++displayId;
+  }
+
+  return HWC3::Error::None;
+}
+
+static int getVsyncHzFromProperty() {
+  static constexpr const auto kVsyncProp = "ro.boot.qemu.vsync";
+
+  const auto vsyncProp = ::android::base::GetProperty(kVsyncProp, "");
+  DEBUG_LOG("%s: prop value is: %s", __FUNCTION__, vsyncProp.c_str());
+
+  uint64_t vsyncPeriod;
+  if (!::android::base::ParseUint(vsyncProp, &vsyncPeriod)) {
+    ALOGE("%s: failed to parse vsync period '%s', returning default 60",
+          __FUNCTION__, vsyncProp.c_str());
+    return 60;
+  }
+
+  return static_cast<int>(vsyncPeriod);
+}
+
+HWC3::Error findGoldfishPrimaryDisplay(
+    std::vector<DisplayMultiConfigs>& displays) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  DEFINE_AND_VALIDATE_HOST_CONNECTION
+  hostCon->lock();
+  const int32_t vsyncPeriodNanos = HertzToPeriodNanos(getVsyncHzFromProperty());
+  DisplayMultiConfigs display;
+  display.displayId = 0;
+  if (rcEnc->hasHWCMultiConfigs()) {
+    int count = rcEnc->rcGetFBDisplayConfigsCount(rcEnc);
+    if (count <= 0) {
+      ALOGE("%s failed to allocate primary display, config count %d", __func__,
+            count);
+      return HWC3::Error::NoResources;
+    }
+    display.activeConfigId = rcEnc->rcGetFBDisplayActiveConfig(rcEnc);
+    for (int configId = 0; configId < count; configId++) {
+      display.configs.push_back(DisplayConfig(
+          configId,                                                       //
+          rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_WIDTH),   //
+          rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_HEIGHT),  //
+          rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_XDPI),    //
+          rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_YDPI),    //
+          vsyncPeriodNanos                                                //
+          ));
+    }
+  } else {
+    display.activeConfigId = 0;
+    display.configs.push_back(DisplayConfig(
+        0,                                      //
+        rcEnc->rcGetFBParam(rcEnc, FB_WIDTH),   //
+        rcEnc->rcGetFBParam(rcEnc, FB_HEIGHT),  //
+        rcEnc->rcGetFBParam(rcEnc, FB_XDPI),    //
+        rcEnc->rcGetFBParam(rcEnc, FB_YDPI),    //
+        vsyncPeriodNanos                        //
+        ));
+  }
+  hostCon->unlock();
+
+  displays.push_back(display);
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error findGoldfishSecondaryDisplays(
+    std::vector<DisplayMultiConfigs>& displays) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  static constexpr const char kExternalDisplayProp[] =
+      "hwservicemanager.external.displays";
+
+  const auto propString =
+      ::android::base::GetProperty(kExternalDisplayProp, "");
+  DEBUG_LOG("%s: prop value is: %s", __FUNCTION__, propString.c_str());
+
+  if (propString.empty()) {
+    return HWC3::Error::None;
+  }
+
+  const std::vector<std::string> propStringParts =
+      ::android::base::Split(propString, ",");
+  if (propStringParts.size() % 5 != 0) {
+    ALOGE("%s: Invalid syntax for system prop %s which is %s", __FUNCTION__,
+          kExternalDisplayProp, propString.c_str());
+    return HWC3::Error::BadParameter;
+  }
+
+  std::vector<int> propIntParts;
+  for (const std::string& propStringPart : propStringParts) {
+    int propIntPart;
+    if (!::android::base::ParseInt(propStringPart, &propIntPart)) {
+      ALOGE("%s: Invalid syntax for system prop %s which is %s", __FUNCTION__,
+            kExternalDisplayProp, propString.c_str());
+      return HWC3::Error::BadParameter;
+    }
+    propIntParts.push_back(propIntPart);
+  }
+
+  int64_t secondaryDisplayId = 1;
+  while (!propIntParts.empty()) {
+    DisplayMultiConfigs display;
+    display.displayId = secondaryDisplayId;
+    display.activeConfigId = 0;
+    display.configs.push_back(DisplayConfig(
+        0,                                       //
+        /*width=*/propIntParts[1],               //
+        /*heighth=*/propIntParts[2],             //
+        /*dpiXh=*/propIntParts[3],               //
+        /*dpiYh=*/propIntParts[3],               //
+        /*vsyncPeriod=*/HertzToPeriodNanos(160)  //
+        ));
+    displays.push_back(display);
+
+    ++secondaryDisplayId;
+
+    propIntParts.erase(propIntParts.begin(), propIntParts.begin() + 5);
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error findGoldfishDisplays(std::vector<DisplayMultiConfigs>& displays) {
+  HWC3::Error error = findGoldfishPrimaryDisplay(displays);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s failed to find Goldfish primary display", __FUNCTION__);
+    return error;
+  }
+
+  error = findGoldfishSecondaryDisplays(displays);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s failed to find Goldfish secondary displays", __FUNCTION__);
+  }
+
+  return error;
+}
+
+// This is currently only used for Gem5 bring-up where virtio-gpu and drm
+// are not currently available. For now, just return a placeholder display.
+HWC3::Error findNoOpDisplays(std::vector<DisplayMultiConfigs>& displays) {
+  displays.push_back(DisplayMultiConfigs{
+      .displayId = 0,
+      .activeConfigId = 0,
+      .configs = {DisplayConfig(0,
+                                /*width=*/720,                          //
+                                /*heighth=*/1280,                       //
+                                /*dpiXh=*/320,                          //
+                                /*dpiYh=*/320,                          //
+                                /*vsyncPeriod=*/HertzToPeriodNanos(30)  //
+                                )},
+  });
+
+  return HWC3::Error::None;
+}
+
+}  // namespace
+
+HWC3::Error findDisplays(std::vector<DisplayMultiConfigs>& displays) {
+  HWC3::Error error = HWC3::Error::None;
+  if (IsNoOpMode()) {
+    error = findNoOpDisplays(displays);
+  } else if (IsCuttlefish()) {
+    error = findCuttlefishDisplays(displays);
+  } else {
+    error = findGoldfishDisplays(displays);
+  }
+
+  if (error != HWC3::Error::None) {
+    ALOGE("%s failed to find displays", __FUNCTION__);
+    return error;
+  }
+
+  for (auto& display : displays) {
+    DisplayConfig::addConfigGroups(&display.configs);
+  }
+
+  return HWC3::Error::None;
+}
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/DisplayFinder.h b/system/hwc3/DisplayFinder.h
new file mode 100644
index 0000000..8197a82
--- /dev/null
+++ b/system/hwc3/DisplayFinder.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWC_DISPLAYFINDER_H
+#define ANDROID_HWC_DISPLAYFINDER_H
+
+#include <vector>
+
+#include "Common.h"
+#include "DisplayConfig.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+struct DisplayMultiConfigs {
+  int64_t displayId;
+  int32_t activeConfigId;
+  // Modes that this display can be configured to use.
+  std::vector<DisplayConfig> configs;
+};
+
+HWC3::Error findDisplays(std::vector<DisplayMultiConfigs>& displays);
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/Drm.cpp b/system/hwc3/Drm.cpp
new file mode 100644
index 0000000..6872eb4
--- /dev/null
+++ b/system/hwc3/Drm.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include "Drm.h"
+
+#include <drm_fourcc.h>
+#include <log/log.h>
+#include <system/graphics.h>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+const char* GetDrmFormatString(uint32_t drm_format) {
+  switch (drm_format) {
+    case DRM_FORMAT_ABGR1555:
+      return "DRM_FORMAT_ABGR1555";
+    case DRM_FORMAT_ABGR2101010:
+      return "DRM_FORMAT_ABGR2101010";
+    case DRM_FORMAT_ABGR4444:
+      return "DRM_FORMAT_ABGR4444";
+    case DRM_FORMAT_ABGR8888:
+      return "DRM_FORMAT_ABGR8888";
+    case DRM_FORMAT_ARGB1555:
+      return "DRM_FORMAT_ARGB1555";
+    case DRM_FORMAT_ARGB2101010:
+      return "DRM_FORMAT_ARGB2101010";
+    case DRM_FORMAT_ARGB4444:
+      return "DRM_FORMAT_ARGB4444";
+    case DRM_FORMAT_ARGB8888:
+      return "DRM_FORMAT_ARGB8888";
+    case DRM_FORMAT_AYUV:
+      return "DRM_FORMAT_AYUV";
+    case DRM_FORMAT_BGR233:
+      return "DRM_FORMAT_BGR233";
+    case DRM_FORMAT_BGR565:
+      return "DRM_FORMAT_BGR565";
+    case DRM_FORMAT_BGR888:
+      return "DRM_FORMAT_BGR888";
+    case DRM_FORMAT_BGRA1010102:
+      return "DRM_FORMAT_BGRA1010102";
+    case DRM_FORMAT_BGRA4444:
+      return "DRM_FORMAT_BGRA4444";
+    case DRM_FORMAT_BGRA5551:
+      return "DRM_FORMAT_BGRA5551";
+    case DRM_FORMAT_BGRA8888:
+      return "DRM_FORMAT_BGRA8888";
+    case DRM_FORMAT_BGRX1010102:
+      return "DRM_FORMAT_BGRX1010102";
+    case DRM_FORMAT_BGRX4444:
+      return "DRM_FORMAT_BGRX4444";
+    case DRM_FORMAT_BGRX5551:
+      return "DRM_FORMAT_BGRX5551";
+    case DRM_FORMAT_BGRX8888:
+      return "DRM_FORMAT_BGRX8888";
+    case DRM_FORMAT_C8:
+      return "DRM_FORMAT_C8";
+    case DRM_FORMAT_GR88:
+      return "DRM_FORMAT_GR88";
+    case DRM_FORMAT_NV12:
+      return "DRM_FORMAT_NV12";
+    case DRM_FORMAT_NV21:
+      return "DRM_FORMAT_NV21";
+    case DRM_FORMAT_R8:
+      return "DRM_FORMAT_R8";
+    case DRM_FORMAT_RG88:
+      return "DRM_FORMAT_RG88";
+    case DRM_FORMAT_RGB332:
+      return "DRM_FORMAT_RGB332";
+    case DRM_FORMAT_RGB565:
+      return "DRM_FORMAT_RGB565";
+    case DRM_FORMAT_RGB888:
+      return "DRM_FORMAT_RGB888";
+    case DRM_FORMAT_RGBA1010102:
+      return "DRM_FORMAT_RGBA1010102";
+    case DRM_FORMAT_RGBA4444:
+      return "DRM_FORMAT_RGBA4444";
+    case DRM_FORMAT_RGBA5551:
+      return "DRM_FORMAT_RGBA5551";
+    case DRM_FORMAT_RGBA8888:
+      return "DRM_FORMAT_RGBA8888";
+    case DRM_FORMAT_RGBX1010102:
+      return "DRM_FORMAT_RGBX1010102";
+    case DRM_FORMAT_RGBX4444:
+      return "DRM_FORMAT_RGBX4444";
+    case DRM_FORMAT_RGBX5551:
+      return "DRM_FORMAT_RGBX5551";
+    case DRM_FORMAT_RGBX8888:
+      return "DRM_FORMAT_RGBX8888";
+    case DRM_FORMAT_UYVY:
+      return "DRM_FORMAT_UYVY";
+    case DRM_FORMAT_VYUY:
+      return "DRM_FORMAT_VYUY";
+    case DRM_FORMAT_XBGR1555:
+      return "DRM_FORMAT_XBGR1555";
+    case DRM_FORMAT_XBGR2101010:
+      return "DRM_FORMAT_XBGR2101010";
+    case DRM_FORMAT_XBGR4444:
+      return "DRM_FORMAT_XBGR4444";
+    case DRM_FORMAT_XBGR8888:
+      return "DRM_FORMAT_XBGR8888";
+    case DRM_FORMAT_XRGB1555:
+      return "DRM_FORMAT_XRGB1555";
+    case DRM_FORMAT_XRGB2101010:
+      return "DRM_FORMAT_XRGB2101010";
+    case DRM_FORMAT_XRGB4444:
+      return "DRM_FORMAT_XRGB4444";
+    case DRM_FORMAT_XRGB8888:
+      return "DRM_FORMAT_XRGB8888";
+    case DRM_FORMAT_YUYV:
+      return "DRM_FORMAT_YUYV";
+    case DRM_FORMAT_YVU420:
+      return "DRM_FORMAT_YVU420";
+    case DRM_FORMAT_YVYU:
+      return "DRM_FORMAT_YVYU";
+  }
+  return "Unknown";
+}
+
+int GetDrmFormatBytesPerPixel(uint32_t drm_format) {
+  switch (drm_format) {
+    case DRM_FORMAT_ABGR8888:
+    case DRM_FORMAT_ARGB8888:
+    case DRM_FORMAT_XBGR8888:
+      return 4;
+    case DRM_FORMAT_BGR888:
+      return 3;
+    case DRM_FORMAT_RGB565:
+    case DRM_FORMAT_YVU420:
+#ifdef GRALLOC_MODULE_API_VERSION_0_2
+    case DRM_FORMAT_FLEX_YCbCr_420_888:
+#endif
+      return 2;
+    case DRM_FORMAT_R8:
+      return 1;
+  }
+  ALOGE("%s: format size unknown %d(%s)", __FUNCTION__, drm_format,
+        GetDrmFormatString(drm_format));
+  return 8;
+}
+
+int GetDrmFormatFromHalFormat(int hal_format) {
+  switch (hal_format) {
+    case HAL_PIXEL_FORMAT_RGBA_FP16:
+      return DRM_FORMAT_ABGR16161616F;
+    case HAL_PIXEL_FORMAT_RGBA_8888:
+      return DRM_FORMAT_ABGR8888;
+    case HAL_PIXEL_FORMAT_RGBX_8888:
+      return DRM_FORMAT_XBGR8888;
+    case HAL_PIXEL_FORMAT_BGRA_8888:
+      return DRM_FORMAT_ARGB8888;
+    case HAL_PIXEL_FORMAT_RGB_888:
+      return DRM_FORMAT_BGR888;
+    case HAL_PIXEL_FORMAT_RGB_565:
+      return DRM_FORMAT_BGR565;
+    case HAL_PIXEL_FORMAT_YV12:
+      return DRM_FORMAT_YVU420;
+    case HAL_PIXEL_FORMAT_YCbCr_420_888:
+      return DRM_FORMAT_YVU420;
+    case HAL_PIXEL_FORMAT_BLOB:
+      return DRM_FORMAT_R8;
+    default:
+      break;
+  }
+  ALOGE("%s unhandled hal format: %d", __FUNCTION__, hal_format);
+  return 0;
+}
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
\ No newline at end of file
diff --git a/system/hwc3/Drm.h b/system/hwc3/Drm.h
new file mode 100644
index 0000000..1d25033
--- /dev/null
+++ b/system/hwc3/Drm.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_HWC_DRM_H
+#define ANDROID_HWC_DRM_H
+
+#include <cstdlib>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+const char* GetDrmFormatString(uint32_t drm_format);
+
+int GetDrmFormatBytesPerPixel(uint32_t drm_format);
+
+int GetDrmFormatFromHalFormat(int hal_format);
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
\ No newline at end of file
diff --git a/system/hwc3/DrmPresenter.cpp b/system/hwc3/DrmPresenter.cpp
new file mode 100644
index 0000000..6e13209
--- /dev/null
+++ b/system/hwc3/DrmPresenter.cpp
@@ -0,0 +1,706 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#include "DrmPresenter.h"
+
+#include <cros_gralloc_handle.h>
+#include <linux/netlink.h>
+#include <sys/socket.h>
+
+#include <chrono>
+#include <thread>
+
+using ::android::base::guest::AutoReadLock;
+using ::android::base::guest::AutoWriteLock;
+using ::android::base::guest::ReadWriteLock;
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+namespace {
+
+uint64_t addressAsUint(int* pointer) {
+  return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(pointer));
+}
+
+}  // namespace
+
+DrmPresenter::~DrmPresenter() {
+  if (mFd > 0) {
+    drmDropMaster(mFd.get());
+  }
+}
+
+HWC3::Error DrmPresenter::init() {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  mFd = ::android::base::unique_fd(open("/dev/dri/card0", O_RDWR | O_CLOEXEC));
+  if (mFd < 0) {
+    ALOGE("%s: failed to open drm device: %s", __FUNCTION__, strerror(errno));
+    return HWC3::Error::NoResources;
+  }
+
+  int ret = drmSetClientCap(mFd.get(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+  if (ret) {
+    ALOGE("%s: failed to set cap universal plane %s\n", __FUNCTION__,
+          strerror(errno));
+    return HWC3::Error::NoResources;
+  }
+
+  ret = drmSetClientCap(mFd.get(), DRM_CLIENT_CAP_ATOMIC, 1);
+  if (ret) {
+    ALOGE("%s: failed to set cap atomic %s\n", __FUNCTION__, strerror(errno));
+    return HWC3::Error::NoResources;
+  }
+
+  drmSetMaster(mFd.get());
+
+  if (!drmIsMaster(mFd.get())) {
+    ALOGE("%s: failed to get master drm device", __FUNCTION__);
+    return HWC3::Error::NoResources;
+  }
+
+  {
+    AutoWriteLock lock(mStateMutex);
+    bool initDrmRet = initDrmElementsLocked();
+    if (initDrmRet) {
+      DEBUG_LOG("%s: Successfully initialized DRM backend", __FUNCTION__);
+    } else {
+      ALOGE("%s: Failed to initialize DRM backend", __FUNCTION__);
+      return HWC3::Error::NoResources;
+    }
+  }
+
+  mDrmEventListener = ::android::sp<DrmEventListener>::make(*this);
+  if (mDrmEventListener->init()) {
+    DEBUG_LOG("%s: Successfully initialized DRM event listener", __FUNCTION__);
+  } else {
+    ALOGE("%s: Failed to initialize DRM event listener", __FUNCTION__);
+  }
+  mDrmEventListener->run("", ANDROID_PRIORITY_URGENT_DISPLAY);
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error DrmPresenter::registerOnHotplugCallback(const HotplugCallback& cb) {
+  mHotplugCallback = cb;
+  return HWC3::Error::None;
+}
+
+HWC3::Error DrmPresenter::unregisterOnHotplugCallback() {
+  mHotplugCallback.reset();
+  return HWC3::Error::None;
+}
+
+bool DrmPresenter::initDrmElementsLocked() {
+  drmModeRes* res;
+  static const int32_t kUmPerInch = 25400;
+
+  res = drmModeGetResources(mFd.get());
+  if (res == nullptr) {
+    ALOGE("%s HWC3::Error reading drm resources: %d", __FUNCTION__, errno);
+    mFd.reset();
+    return false;
+  }
+
+  DEBUG_LOG(
+      "drmModeRes count fbs %d crtc %d connector %d encoder %d min w %d max w "
+      "%d min h %d max h %d",
+      res->count_fbs, res->count_crtcs, res->count_connectors,
+      res->count_encoders, res->min_width, res->max_width, res->min_height,
+      res->max_height);
+
+  for (uint32_t i = 0; i < res->count_crtcs; i++) {
+    DrmCrtc crtc = {};
+
+    drmModeCrtcPtr c = drmModeGetCrtc(mFd.get(), res->crtcs[i]);
+    crtc.mId = c->crtc_id;
+
+    drmModeObjectPropertiesPtr crtcProps =
+        drmModeObjectGetProperties(mFd.get(), c->crtc_id, DRM_MODE_OBJECT_CRTC);
+
+    for (uint32_t crtcPropsIndex = 0; crtcPropsIndex < crtcProps->count_props;
+         crtcPropsIndex++) {
+      drmModePropertyPtr crtcProp =
+          drmModeGetProperty(mFd.get(), crtcProps->props[crtcPropsIndex]);
+
+      if (!strcmp(crtcProp->name, "OUT_FENCE_PTR")) {
+        crtc.mOutFencePtrPropertyId = crtcProp->prop_id;
+      } else if (!strcmp(crtcProp->name, "ACTIVE")) {
+        crtc.mActivePropertyId = crtcProp->prop_id;
+      } else if (!strcmp(crtcProp->name, "MODE_ID")) {
+        crtc.mModePropertyId = crtcProp->prop_id;
+      }
+
+      drmModeFreeProperty(crtcProp);
+    }
+
+    drmModeFreeObjectProperties(crtcProps);
+
+    mCrtcs.push_back(crtc);
+  }
+
+  drmModePlaneResPtr planeRes = drmModeGetPlaneResources(mFd.get());
+  for (uint32_t i = 0; i < planeRes->count_planes; ++i) {
+    DrmPlane plane = {};
+
+    drmModePlanePtr p = drmModeGetPlane(mFd.get(), planeRes->planes[i]);
+    plane.mId = p->plane_id;
+
+    DEBUG_LOG(
+        "%s: plane id: %u crtcid %u fbid %u crtc xy %d %d xy %d %d "
+        "possible ctrcs 0x%x",
+        __FUNCTION__, p->plane_id, p->crtc_id, p->fb_id, p->crtc_x, p->crtc_y,
+        p->x, p->y, p->possible_crtcs);
+
+    drmModeObjectPropertiesPtr planeProps =
+        drmModeObjectGetProperties(mFd.get(), plane.mId, DRM_MODE_OBJECT_PLANE);
+
+    for (uint32_t planePropIndex = 0; planePropIndex < planeProps->count_props;
+         ++planePropIndex) {
+      drmModePropertyPtr planeProp =
+          drmModeGetProperty(mFd.get(), planeProps->props[planePropIndex]);
+
+      if (!strcmp(planeProp->name, "CRTC_ID")) {
+        plane.mCrtcPropertyId = planeProp->prop_id;
+      } else if (!strcmp(planeProp->name, "IN_FENCE_FD")) {
+        plane.mInFenceFdPropertyId = planeProp->prop_id;
+      } else if (!strcmp(planeProp->name, "FB_ID")) {
+        plane.mFbPropertyId = planeProp->prop_id;
+      } else if (!strcmp(planeProp->name, "CRTC_X")) {
+        plane.mCrtcXPropertyId = planeProp->prop_id;
+      } else if (!strcmp(planeProp->name, "CRTC_Y")) {
+        plane.mCrtcYPropertyId = planeProp->prop_id;
+      } else if (!strcmp(planeProp->name, "CRTC_W")) {
+        plane.mCrtcWPropertyId = planeProp->prop_id;
+      } else if (!strcmp(planeProp->name, "CRTC_H")) {
+        plane.mCrtcHPropertyId = planeProp->prop_id;
+      } else if (!strcmp(planeProp->name, "SRC_X")) {
+        plane.mSrcXPropertyId = planeProp->prop_id;
+      } else if (!strcmp(planeProp->name, "SRC_Y")) {
+        plane.mSrcYPropertyId = planeProp->prop_id;
+      } else if (!strcmp(planeProp->name, "SRC_W")) {
+        plane.mSrcWPropertyId = planeProp->prop_id;
+      } else if (!strcmp(planeProp->name, "SRC_H")) {
+        plane.mSrcHPropertyId = planeProp->prop_id;
+      } else if (!strcmp(planeProp->name, "type")) {
+        plane.mTypePropertyId = planeProp->prop_id;
+        uint64_t type = planeProp->values[0];
+        switch (type) {
+          case DRM_PLANE_TYPE_OVERLAY:
+            plane.mType = type;
+            DEBUG_LOG("%s: plane %" PRIu32 " is DRM_PLANE_TYPE_OVERLAY",
+                      __FUNCTION__, plane.mId);
+            break;
+          case DRM_PLANE_TYPE_PRIMARY:
+            plane.mType = type;
+            DEBUG_LOG("%s: plane %" PRIu32 " is DRM_PLANE_TYPE_PRIMARY",
+                      __FUNCTION__, plane.mId);
+            break;
+          default:
+            break;
+        }
+      }
+
+      drmModeFreeProperty(planeProp);
+    }
+
+    drmModeFreeObjectProperties(planeProps);
+
+    bool isPrimaryOrOverlay = plane.mType == DRM_PLANE_TYPE_OVERLAY ||
+                              plane.mType == DRM_PLANE_TYPE_PRIMARY;
+    if (isPrimaryOrOverlay) {
+      for (uint32_t j = 0; j < mCrtcs.size(); j++) {
+        if ((0x1 << j) & p->possible_crtcs) {
+          DEBUG_LOG("%s: plane %" PRIu32 " compatible with crtc mask %" PRIu32,
+                    __FUNCTION__, plane.mId, p->possible_crtcs);
+          if (mCrtcs[j].mPlaneId == -1) {
+            mCrtcs[j].mPlaneId = plane.mId;
+            DEBUG_LOG("%s: plane %" PRIu32 " associated with crtc %" PRIu32,
+                      __FUNCTION__, plane.mId, j);
+            break;
+          }
+        }
+      }
+    }
+
+    drmModeFreePlane(p);
+    mPlanes[plane.mId] = plane;
+  }
+  drmModeFreePlaneResources(planeRes);
+
+  for (uint32_t i = 0; i < res->count_connectors; ++i) {
+    DrmConnector connector = {};
+    connector.mId = res->connectors[i];
+
+    {
+      drmModeObjectPropertiesPtr connectorProps = drmModeObjectGetProperties(
+          mFd.get(), connector.mId, DRM_MODE_OBJECT_CONNECTOR);
+
+      for (uint32_t connectorPropIndex = 0;
+           connectorPropIndex < connectorProps->count_props;
+           ++connectorPropIndex) {
+        drmModePropertyPtr connectorProp = drmModeGetProperty(
+            mFd.get(), connectorProps->props[connectorPropIndex]);
+        if (!strcmp(connectorProp->name, "CRTC_ID")) {
+          connector.mCrtcPropertyId = connectorProp->prop_id;
+        } else if (!strcmp(connectorProp->name, "EDID")) {
+          connector.mEdidBlobId =
+              connectorProps->prop_values[connectorPropIndex];
+        }
+        drmModeFreeProperty(connectorProp);
+      }
+
+      drmModeFreeObjectProperties(connectorProps);
+    }
+    {
+      drmModeConnector* c = drmModeGetConnector(mFd.get(), connector.mId);
+      if (c == nullptr) {
+        ALOGE("%s: Failed to get connector %" PRIu32 ": %d", __FUNCTION__,
+              connector.mId, errno);
+        return false;
+      }
+      connector.connection = c->connection;
+      if (c->count_modes > 0) {
+        memcpy(&connector.mMode, &c->modes[0], sizeof(drmModeModeInfo));
+        drmModeCreatePropertyBlob(mFd.get(), &connector.mMode,
+                                  sizeof(connector.mMode),
+                                  &connector.mModeBlobId);
+
+        // Dots per 1000 inches
+        connector.dpiX =
+            c->mmWidth ? (c->modes[0].hdisplay * kUmPerInch) / (c->mmWidth)
+                       : -1;
+        // Dots per 1000 inches
+        connector.dpiY =
+            c->mmHeight ? (c->modes[0].vdisplay * kUmPerInch) / (c->mmHeight)
+                        : -1;
+      }
+      DEBUG_LOG("%s connector %" PRIu32 " dpiX %" PRIi32 " dpiY %" PRIi32
+                " connection %d",
+                __FUNCTION__, connector.mId, connector.dpiX, connector.dpiY,
+                connector.connection);
+
+      drmModeFreeConnector(c);
+
+      connector.mRefreshRateAsFloat =
+          1000.0f * connector.mMode.clock /
+          ((float)connector.mMode.vtotal * (float)connector.mMode.htotal);
+      connector.mRefreshRateAsInteger =
+          (uint32_t)(connector.mRefreshRateAsFloat + 0.5f);
+    }
+
+    mConnectors.push_back(connector);
+  }
+
+  drmModeFreeResources(res);
+  return true;
+}
+
+void DrmPresenter::resetDrmElementsLocked() {
+  for (auto& c : mConnectors) {
+    if (c.mModeBlobId) {
+      if (drmModeDestroyPropertyBlob(mFd.get(), c.mModeBlobId)) {
+        ALOGE("%s: Error destroy PropertyBlob %" PRIu32, __func__,
+              c.mModeBlobId);
+      }
+    }
+  }
+  mConnectors.clear();
+  mCrtcs.clear();
+  mPlanes.clear();
+}
+
+std::tuple<HWC3::Error, std::unique_ptr<DrmBuffer>> DrmPresenter::create(
+    const native_handle_t* handle) {
+  auto buffer = std::unique_ptr<DrmBuffer>(new DrmBuffer(*this));
+
+  cros_gralloc_handle* crosHandle = (cros_gralloc_handle*)handle;
+  if (crosHandle == nullptr) {
+    ALOGE("%s: invalid cros_gralloc_handle", __FUNCTION__);
+    return std::make_tuple(HWC3::Error::NoResources,
+                           std::unique_ptr<DrmBuffer>());
+  }
+
+  buffer->mWidth = crosHandle->width;
+  buffer->mHeight = crosHandle->height;
+  buffer->mDrmFormat = crosHandle->format;
+  buffer->mPlaneFds[0] = crosHandle->fds[0];
+  buffer->mPlanePitches[0] = crosHandle->strides[0];
+  buffer->mPlaneOffsets[0] = crosHandle->offsets[0];
+
+  HWC3::Error error = createDrmFramebuffer(buffer.get());
+  return std::make_tuple(error, std::move(buffer));
+}
+
+HWC3::Error DrmPresenter::createDrmFramebuffer(DrmBuffer* buffer) {
+  int ret;
+
+  ret = drmPrimeFDToHandle(mFd.get(), buffer->mPlaneFds[0],
+                           &buffer->mPlaneHandles[0]);
+  if (ret) {
+    ALOGE("%s: drmPrimeFDToHandle failed: %s (errno %d)", __FUNCTION__,
+          strerror(errno), errno);
+    return HWC3::Error::NoResources;
+  }
+
+  uint32_t framebuffer = 0;
+  ret = drmModeAddFB2(mFd.get(), buffer->mWidth, buffer->mHeight,
+                      buffer->mDrmFormat, buffer->mPlaneHandles,
+                      buffer->mPlanePitches, buffer->mPlaneOffsets,
+                      &framebuffer, 0);
+  if (ret) {
+    ALOGE("%s: drmModeAddFB2 failed: %s (errno %d)", __FUNCTION__,
+          strerror(errno), errno);
+    return HWC3::Error::NoResources;
+  }
+
+  DEBUG_LOG("%s: created framebuffer:%" PRIu32, __FUNCTION__, framebuffer);
+
+  buffer->mDrmFramebuffer.emplace(framebuffer);
+  return HWC3::Error::None;
+}
+
+HWC3::Error DrmPresenter::destroyDrmFramebuffer(DrmBuffer* buffer) {
+  if (buffer->mDrmFramebuffer) {
+    uint32_t framebuffer = *buffer->mDrmFramebuffer;
+    if (drmModeRmFB(mFd.get(), framebuffer)) {
+      ALOGE("%s: drmModeRmFB failed: %s (errno %d)", __FUNCTION__,
+            strerror(errno), errno);
+      return HWC3::Error::NoResources;
+    }
+    DEBUG_LOG("%s: destroyed framebuffer:%" PRIu32, __FUNCTION__, framebuffer);
+    buffer->mDrmFramebuffer.reset();
+  }
+  if (buffer->mPlaneHandles[0]) {
+    struct drm_gem_close gem_close = {};
+    gem_close.handle = buffer->mPlaneHandles[0];
+    if (drmIoctl(mFd.get(), DRM_IOCTL_GEM_CLOSE, &gem_close)) {
+      ALOGE("%s: DRM_IOCTL_GEM_CLOSE failed: %s (errno %d)", __FUNCTION__,
+            strerror(errno), errno);
+      return HWC3::Error::NoResources;
+    }
+  }
+
+  return HWC3::Error::None;
+}
+
+bool DrmPresenter::handleHotplug() {
+  std::vector<DrmConnector> oldConnectors(mConnectors);
+  {
+    AutoReadLock lock(mStateMutex);
+    oldConnectors.assign(mConnectors.begin(), mConnectors.end());
+  }
+  {
+    AutoWriteLock lock(mStateMutex);
+    resetDrmElementsLocked();
+    if (!initDrmElementsLocked()) {
+      ALOGE(
+          "%s: failed to initialize drm elements during hotplug. Displays may "
+          "not function correctly!",
+          __FUNCTION__);
+      return false;
+    }
+  }
+
+  AutoReadLock lock(mStateMutex);
+  for (int i = 0; i < mConnectors.size(); i++) {
+    bool changed =
+        oldConnectors[i].dpiX != mConnectors[i].dpiX ||
+        oldConnectors[i].dpiY != mConnectors[i].dpiY ||
+        oldConnectors[i].connection != mConnectors[i].connection ||
+        oldConnectors[i].mMode.hdisplay != mConnectors[i].mMode.hdisplay ||
+        oldConnectors[i].mMode.vdisplay != mConnectors[i].mMode.vdisplay;
+    if (changed) {
+      if (i == 0) {
+        ALOGE(
+            "%s: Ignoring changes to display:0 which is not configurable by "
+            "multi-display interface.",
+            __FUNCTION__);
+        continue;
+      }
+
+      const bool connected =
+          mConnectors[i].connection == DRM_MODE_CONNECTED ? true : false;
+      if (mHotplugCallback) {
+        (*mHotplugCallback)(connected,                      //
+                            i,                              //
+                            mConnectors[i].mMode.hdisplay,  //
+                            mConnectors[i].mMode.vdisplay,  //
+                            mConnectors[i].dpiX,            //
+                            mConnectors[i].dpiY,            //
+                            mConnectors[i].mRefreshRateAsInteger);
+      }
+    }
+  }
+  return true;
+}
+
+std::tuple<HWC3::Error, ::android::base::unique_fd>
+DrmPresenter::flushToDisplay(int display, const DrmBuffer& buffer,
+                             ::android::base::borrowed_fd inSyncFd) {
+  ATRACE_CALL();
+
+  if (!buffer.mDrmFramebuffer) {
+    ALOGE("%s: failed, no framebuffer created.", __FUNCTION__);
+    return std::make_tuple(HWC3::Error::NoResources,
+                           ::android::base::unique_fd());
+  }
+
+  AutoReadLock lock(mStateMutex);
+
+  DrmConnector& connector = mConnectors[display];
+  DrmCrtc& crtc = mCrtcs[display];
+
+  HWC3::Error error = HWC3::Error::None;
+
+  drmModeAtomicReqPtr pset = drmModeAtomicAlloc();
+
+  int ret;
+
+  if (!crtc.mDidSetCrtc) {
+    DEBUG_LOG("%s: Setting crtc.\n", __FUNCTION__);
+    ret = drmModeAtomicAddProperty(pset, crtc.mId, crtc.mActivePropertyId, 1);
+    if (ret < 0) {
+      ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+    }
+    ret = drmModeAtomicAddProperty(pset, crtc.mId, crtc.mModePropertyId,
+                                   connector.mModeBlobId);
+    if (ret < 0) {
+      ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+    }
+    ret = drmModeAtomicAddProperty(pset, connector.mId,
+                                   connector.mCrtcPropertyId, crtc.mId);
+    if (ret < 0) {
+      ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+    }
+
+    crtc.mDidSetCrtc = true;
+  } else {
+    DEBUG_LOG("%s: Already set crtc\n", __FUNCTION__);
+  }
+
+  int flushFenceFd = -1;
+  ret = drmModeAtomicAddProperty(pset, crtc.mId, crtc.mOutFencePtrPropertyId,
+                                 addressAsUint(&flushFenceFd));
+  if (ret < 0) {
+    ALOGE("%s:%d: set OUT_FENCE_PTR failed %d errno %d\n", __FUNCTION__,
+          __LINE__, ret, errno);
+  }
+
+  if (crtc.mPlaneId == -1) {
+    ALOGE("%s:%d: no plane available for crtc id %" PRIu32, __FUNCTION__,
+          __LINE__, crtc.mId);
+    return std::make_tuple(HWC3::Error::NoResources,
+                           ::android::base::unique_fd());
+  }
+
+  DrmPlane& plane = mPlanes[crtc.mPlaneId];
+
+  DEBUG_LOG("%s: set plane: plane id %d crtc id %d fbid %d bo w h %d %d\n",
+            __FUNCTION__, plane.mId, crtc.mId, *buffer.mDrmFramebuffer,
+            buffer.mWidth, buffer.mHeight);
+
+  ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mCrtcPropertyId,
+                                 crtc.mId);
+  if (ret < 0) {
+    ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+  }
+  ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mInFenceFdPropertyId,
+                                 inSyncFd.get());
+  if (ret < 0) {
+    ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+  }
+  ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mFbPropertyId,
+                                 *buffer.mDrmFramebuffer);
+  if (ret < 0) {
+    ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+  }
+  ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mCrtcXPropertyId, 0);
+  if (ret < 0) {
+    ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+  }
+  ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mCrtcYPropertyId, 0);
+  if (ret < 0) {
+    ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+  }
+  ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mCrtcWPropertyId,
+                                 buffer.mWidth);
+  if (ret < 0) {
+    ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+  }
+  ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mCrtcHPropertyId,
+                                 buffer.mHeight);
+  if (ret < 0) {
+    ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+  }
+  ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mSrcXPropertyId, 0);
+  if (ret < 0) {
+    ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+  }
+  ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mSrcYPropertyId, 0);
+  if (ret < 0) {
+    ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+  }
+  ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mSrcWPropertyId,
+                                 buffer.mWidth << 16);
+  if (ret < 0) {
+    ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+  }
+  ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mSrcHPropertyId,
+                                 buffer.mHeight << 16);
+  if (ret < 0) {
+    ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+  }
+
+  constexpr const uint32_t kCommitFlags = DRM_MODE_ATOMIC_ALLOW_MODESET;
+  ret = drmModeAtomicCommit(mFd.get(), pset, kCommitFlags, 0);
+
+  if (ret) {
+    ALOGE("%s:%d: atomic commit failed: %s\n", __FUNCTION__, __LINE__,
+          strerror(errno));
+    error = HWC3::Error::NoResources;
+    flushFenceFd = -1;
+  }
+
+  if (pset) {
+    drmModeAtomicFree(pset);
+  }
+
+  DEBUG_LOG("%s: flush fence:%d\n", __FUNCTION__, flushFenceFd);
+  return std::make_tuple(error, ::android::base::unique_fd(flushFenceFd));
+}
+
+std::optional<std::vector<uint8_t>> DrmPresenter::getEdid(uint32_t id) {
+  AutoReadLock lock(mStateMutex);
+
+  if (mConnectors[id].mEdidBlobId == -1) {
+    DEBUG_LOG("%s: EDID not supported", __func__);
+    return std::nullopt;
+  }
+  drmModePropertyBlobPtr blob =
+      drmModeGetPropertyBlob(mFd.get(), mConnectors[id].mEdidBlobId);
+  if (!blob) {
+    ALOGE("%s: fail to read EDID from DRM", __func__);
+    return std::nullopt;
+  }
+
+  std::vector<uint8_t> edid;
+  uint8_t* start = static_cast<uint8_t*>(blob->data);
+  edid.insert(edid.begin(), start, start + blob->length);
+
+  drmModeFreePropertyBlob(blob);
+
+  return edid;
+}
+
+DrmBuffer::DrmBuffer(DrmPresenter& DrmPresenter)
+    : mDrmPresenter(DrmPresenter) {}
+
+DrmBuffer::~DrmBuffer() { mDrmPresenter.destroyDrmFramebuffer(this); }
+
+DrmPresenter::DrmEventListener::DrmEventListener(DrmPresenter& presenter)
+    : mPresenter(presenter) {}
+
+DrmPresenter::DrmEventListener::~DrmEventListener() {}
+
+bool DrmPresenter::DrmEventListener::init() {
+  mEventFd = ::android::base::unique_fd(
+      socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT));
+  if (!mEventFd.ok()) {
+    ALOGE("Failed to open uevent socket: %s", strerror(errno));
+    return false;
+  }
+  struct sockaddr_nl addr;
+  memset(&addr, 0, sizeof(addr));
+  addr.nl_family = AF_NETLINK;
+  addr.nl_pid = 0;
+  addr.nl_groups = 0xFFFFFFFF;
+
+  int ret = bind(mEventFd, (struct sockaddr*)&addr, sizeof(addr));
+  if (ret) {
+    ALOGE("Failed to bind uevent socket: %s", strerror(errno));
+    return false;
+  }
+
+  FD_ZERO(&mMonitoredFds);
+  FD_SET(mPresenter.mFd.get(), &mMonitoredFds);
+  FD_SET(mEventFd.get(), &mMonitoredFds);
+  mMaxFd = std::max(mPresenter.mFd.get(), mEventFd.get());
+
+  return true;
+}
+
+bool DrmPresenter::DrmEventListener::threadLoop() {
+  int ret;
+  do {
+    ret = select(mMaxFd + 1, &mMonitoredFds, NULL, NULL, NULL);
+  } while (ret == -1 && errno == EINTR);
+
+  // if (FD_ISSET(mPresenter.mFd, &mFds)) {
+  //   TODO: handle drm related events
+  // }
+
+  if (FD_ISSET(mEventFd.get(), &mMonitoredFds)) {
+    eventThreadLoop();
+  }
+  return true;
+}
+
+void DrmPresenter::DrmEventListener::eventThreadLoop() {
+  char buffer[1024];
+  int ret;
+
+  struct timespec ts;
+  uint64_t timestamp = 0;
+  ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+  if (!ret) {
+    timestamp = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
+  } else {
+    ALOGE("Failed to get monotonic clock on hotplug %d", ret);
+  }
+
+  while (true) {
+    ret = read(mEventFd.get(), &buffer, sizeof(buffer));
+    if (ret == 0) {
+      return;
+    } else if (ret < 0) {
+      ALOGE("Got error reading uevent %d", ret);
+      return;
+    }
+
+    bool drmEvent = false, hotplugEvent = false;
+    for (int i = 0; i < ret;) {
+      char* event = buffer + i;
+      if (strcmp(event, "DEVTYPE=drm_minor")) {
+        drmEvent = true;
+      } else if (strcmp(event, "HOTPLUG=1")) {
+        hotplugEvent = true;
+      }
+
+      i += strlen(event) + 1;
+    }
+
+    if (drmEvent && hotplugEvent) {
+      processHotplug(timestamp);
+    }
+  }
+}
+
+void DrmPresenter::DrmEventListener::processHotplug(uint64_t timestamp) {
+  ALOGD("DrmEventListener detected hotplug event %" PRIu64, timestamp);
+  mPresenter.handleHotplug();
+}
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/DrmPresenter.h b/system/hwc3/DrmPresenter.h
new file mode 100644
index 0000000..61e546a
--- /dev/null
+++ b/system/hwc3/DrmPresenter.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWC_DRMPRESENTER_H
+#define ANDROID_HWC_DRMPRESENTER_H
+
+#include <android-base/unique_fd.h>
+#include <cutils/native_handle.h>
+#include <utils/Thread.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include <map>
+#include <memory>
+#include <tuple>
+#include <vector>
+
+#include "Common.h"
+#include "android/base/synchronization/AndroidLock.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class DrmBuffer;
+class DrmPresenter;
+
+// A RAII object that will clear a drm framebuffer upon destruction.
+class DrmBuffer {
+ public:
+  ~DrmBuffer();
+
+  DrmBuffer(const DrmBuffer&) = delete;
+  DrmBuffer& operator=(const DrmBuffer&) = delete;
+
+  DrmBuffer(DrmBuffer&&) = delete;
+  DrmBuffer& operator=(DrmBuffer&&) = delete;
+
+ private:
+  friend class DrmPresenter;
+  DrmBuffer(DrmPresenter& drmPresenter);
+
+  DrmPresenter& mDrmPresenter;
+
+  uint32_t mWidth = 0;
+  uint32_t mHeight = 0;
+  uint32_t mDrmFormat = 0;
+  uint32_t mPlaneFds[4] = {0, 0, 0, 0};
+  uint32_t mPlaneHandles[4] = {0, 0, 0, 0};
+  uint32_t mPlanePitches[4] = {0, 0, 0, 0};
+  uint32_t mPlaneOffsets[4] = {0, 0, 0, 0};
+
+  std::optional<uint32_t> mDrmFramebuffer;
+};
+
+class DrmPresenter {
+ public:
+  DrmPresenter() = default;
+  ~DrmPresenter();
+
+  DrmPresenter(const DrmPresenter&) = delete;
+  DrmPresenter& operator=(const DrmPresenter&) = delete;
+
+  DrmPresenter(DrmPresenter&&) = delete;
+  DrmPresenter& operator=(DrmPresenter&&) = delete;
+
+  HWC3::Error init();
+
+  using HotplugCallback = std::function<void(bool /*connected*/,   //
+                                             uint32_t /*id*/,      //
+                                             uint32_t /*width*/,   //
+                                             uint32_t /*height*/,  //
+                                             uint32_t /*dpiX*/,    //
+                                             uint32_t /*dpiY*/,    //
+                                             uint32_t /*refreshRate*/)>;
+
+  HWC3::Error registerOnHotplugCallback(const HotplugCallback& cb);
+  HWC3::Error unregisterOnHotplugCallback();
+
+  uint32_t refreshRate() const { return mConnectors[0].mRefreshRateAsInteger; }
+
+  std::tuple<HWC3::Error, std::unique_ptr<DrmBuffer>> create(
+      const native_handle_t* handle);
+
+  std::tuple<HWC3::Error, ::android::base::unique_fd> flushToDisplay(
+      int display, const DrmBuffer& buffer,
+      ::android::base::borrowed_fd inWaitSyncFd);
+
+  std::optional<std::vector<uint8_t>> getEdid(uint32_t id);
+
+ private:
+  // Grant visibility for createDrmFramebuffer and clearDrmFB to DrmBuffer.
+  friend class DrmBuffer;
+  HWC3::Error createDrmFramebuffer(DrmBuffer* buffer);
+  HWC3::Error destroyDrmFramebuffer(DrmBuffer* buffer);
+
+  // Grant visibility for handleHotplug to DrmEventListener.
+  bool handleHotplug();
+
+  bool initDrmElementsLocked();
+  void resetDrmElementsLocked();
+
+  // Drm device.
+  ::android::base::unique_fd mFd;
+
+  std::optional<HotplugCallback> mHotplugCallback;
+
+  // Protects access to the below drm structs.
+  ::android::base::guest::ReadWriteLock mStateMutex;
+
+  struct DrmPlane {
+    uint32_t mId = -1;
+    uint32_t mCrtcPropertyId = -1;
+    uint32_t mInFenceFdPropertyId = -1;
+    uint32_t mFbPropertyId = -1;
+    uint32_t mCrtcXPropertyId = -1;
+    uint32_t mCrtcYPropertyId = -1;
+    uint32_t mCrtcWPropertyId = -1;
+    uint32_t mCrtcHPropertyId = -1;
+    uint32_t mSrcXPropertyId = -1;
+    uint32_t mSrcYPropertyId = -1;
+    uint32_t mSrcWPropertyId = -1;
+    uint32_t mSrcHPropertyId = -1;
+    uint32_t mTypePropertyId = -1;
+    uint64_t mType = -1;
+  };
+  std::map<uint32_t, DrmPlane> mPlanes;
+
+  struct DrmCrtc {
+    uint32_t mId = -1;
+    uint32_t mActivePropertyId = -1;
+    uint32_t mModePropertyId = -1;
+    uint32_t mOutFencePtrPropertyId = -1;
+    uint32_t mPlaneId = -1;
+
+    bool mDidSetCrtc = false;
+  };
+  std::vector<DrmCrtc> mCrtcs;
+
+  struct DrmConnector {
+    uint32_t mId = -1;
+    uint32_t mCrtcPropertyId = -1;
+    drmModeModeInfo mMode;
+    int32_t dpiX;
+    int32_t dpiY;
+    drmModeConnection connection;
+    uint32_t mModeBlobId = 0;
+    float mRefreshRateAsFloat;
+    uint32_t mRefreshRateAsInteger;
+    uint64_t mEdidBlobId = -1;
+  };
+  std::vector<DrmConnector> mConnectors;
+
+  class DrmEventListener : public ::android::Thread {
+   public:
+    DrmEventListener(DrmPresenter& presenter);
+    virtual ~DrmEventListener();
+
+    bool init();
+
+   private:
+    bool threadLoop() final;
+    void eventThreadLoop();
+    void processHotplug(uint64_t timestamp);
+
+    DrmPresenter& mPresenter;
+    ::android::base::unique_fd mEventFd;
+    int mMaxFd;
+    fd_set mMonitoredFds;
+  };
+  ::android::sp<DrmEventListener> mDrmEventListener;
+};
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/FencedBuffer.h b/system/hwc3/FencedBuffer.h
new file mode 100644
index 0000000..50e8534
--- /dev/null
+++ b/system/hwc3/FencedBuffer.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWC_FENCEDBUFFER_H
+#define ANDROID_HWC_FENCEDBUFFER_H
+
+#include <aidlcommonsupport/NativeHandle.h>
+#include <android-base/unique_fd.h>
+#include <cutils/native_handle.h>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class FencedBuffer {
+ public:
+  FencedBuffer() : mBuffer(nullptr) {}
+
+  void set(buffer_handle_t buffer, const ndk::ScopedFileDescriptor& fence) {
+    mBuffer = buffer;
+    mFence = GetUniqueFd(fence);
+  }
+
+  buffer_handle_t getBuffer() const { return mBuffer; }
+
+  ::android::base::unique_fd getFence() const {
+    if (mFence.ok()) {
+      return ::android::base::unique_fd(dup(mFence.get()));
+    } else {
+      return ::android::base::unique_fd();
+    }
+  }
+
+ private:
+  static ::android::base::unique_fd GetUniqueFd(
+      const ndk::ScopedFileDescriptor& in) {
+    auto& sfd = const_cast<ndk::ScopedFileDescriptor&>(in);
+    ::android::base::unique_fd ret(sfd.get());
+    *sfd.getR() = -1;
+    return ret;
+  }
+
+  buffer_handle_t mBuffer;
+  ::android::base::unique_fd mFence;
+};
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/FrameComposer.h b/system/hwc3/FrameComposer.h
new file mode 100644
index 0000000..fdb6db3
--- /dev/null
+++ b/system/hwc3/FrameComposer.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWC_COMPOSER_H
+#define ANDROID_HWC_COMPOSER_H
+
+#include <android-base/unique_fd.h>
+
+#include <functional>
+#include <tuple>
+#include <unordered_map>
+#include <vector>
+
+#include "Common.h"
+#include "DisplayChanges.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class Display;
+
+class FrameComposer {
+ public:
+  virtual ~FrameComposer() {}
+
+  virtual HWC3::Error init() = 0;
+
+  using HotplugCallback = std::function<void(bool /*connected*/,   //
+                                             uint32_t /*id*/,      //
+                                             uint32_t /*width*/,   //
+                                             uint32_t /*height*/,  //
+                                             uint32_t /*dpiX*/,    //
+                                             uint32_t /*dpiY*/,    //
+                                             uint32_t /*refreshRate*/)>;
+
+  virtual HWC3::Error registerOnHotplugCallback(const HotplugCallback& cb) = 0;
+
+  virtual HWC3::Error unregisterOnHotplugCallback() = 0;
+
+  virtual HWC3::Error onDisplayCreate(Display* display) = 0;
+
+  virtual HWC3::Error onDisplayDestroy(Display* display) = 0;
+
+  virtual HWC3::Error onDisplayClientTargetSet(Display* display) = 0;
+
+  // Determines if this composer can compose the given layers and requests
+  // changes for layers that can't not be composed.
+  virtual HWC3::Error validateDisplay(Display* display,
+                                      DisplayChanges* outChanges) = 0;
+
+  // Performs the actual composition of layers and presents the composed result
+  // to the display.
+  virtual HWC3::Error presentDisplay(
+      Display* display, ::android::base::unique_fd* outDisplayFence,
+      std::unordered_map<int64_t, ::android::base::unique_fd>*
+          outLayerFences) = 0;
+
+  virtual HWC3::Error onActiveConfigChange(Display* display) = 0;
+};
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/Gralloc.cpp b/system/hwc3/Gralloc.cpp
new file mode 100644
index 0000000..9234df4
--- /dev/null
+++ b/system/hwc3/Gralloc.cpp
@@ -0,0 +1,493 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include "Gralloc.h"
+
+#include <aidl/android/hardware/graphics/common/PlaneLayoutComponent.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayoutComponentType.h>
+#include <drm_fourcc.h>
+#include <gralloctypes/Gralloc4.h>
+#include <hidl/ServiceManagement.h>
+#include <log/log.h>
+
+#include <algorithm>
+
+#include "Drm.h"
+
+using aidl::android::hardware::graphics::common::PlaneLayout;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
+using android::hardware::hidl_handle;
+using android::hardware::hidl_vec;
+using android::hardware::graphics::common::V1_2::BufferUsage;
+using android::hardware::graphics::mapper::V4_0::Error;
+using android::hardware::graphics::mapper::V4_0::IMapper;
+using MetadataType =
+    android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+Gralloc::Gralloc() {
+  ::android::hardware::preloadPassthroughService<IMapper>();
+
+  gralloc4_ = IMapper::getService();
+  if (gralloc4_ != nullptr) {
+    ALOGE("%s using Gralloc4.", __FUNCTION__);
+    return;
+  }
+  ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+
+  ALOGE("%s No Grallocs available!", __FUNCTION__);
+}
+
+Error Gralloc::GetMetadata(buffer_handle_t buffer, MetadataType type,
+                           hidl_vec<uint8_t>* metadata) {
+  if (gralloc4_ == nullptr) {
+    ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+    return Error::NO_RESOURCES;
+  }
+
+  if (metadata == nullptr) {
+    return Error::BAD_VALUE;
+  }
+
+  Error error = Error::NONE;
+
+  auto native_handle = const_cast<native_handle_t*>(buffer);
+
+  auto ret =
+      gralloc4_->get(native_handle, type,
+                     [&](const auto& get_error, const auto& get_metadata) {
+                       error = get_error;
+                       *metadata = get_metadata;
+                     });
+
+  if (!ret.isOk()) {
+    error = Error::NO_RESOURCES;
+  }
+
+  if (error != Error::NONE) {
+    ALOGE("%s failed to get metadata %s", __FUNCTION__, type.name.c_str());
+  }
+  return error;
+}
+
+std::optional<uint32_t> Gralloc::GetWidth(buffer_handle_t buffer) {
+  if (gralloc4_ == nullptr) {
+    ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+    return std::nullopt;
+  }
+
+  hidl_vec<uint8_t> encoded_width;
+
+  Error error = GetMetadata(buffer, ::android::gralloc4::MetadataType_Width,
+                            &encoded_width);
+  if (error != Error::NONE) {
+    return std::nullopt;
+  }
+
+  uint64_t width = 0;
+  ::android::gralloc4::decodeWidth(encoded_width, &width);
+  return static_cast<uint32_t>(width);
+}
+
+std::optional<uint32_t> Gralloc::GetHeight(buffer_handle_t buffer) {
+  if (gralloc4_ == nullptr) {
+    ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+    return std::nullopt;
+  }
+
+  hidl_vec<uint8_t> encoded_height;
+
+  Error error = GetMetadata(buffer, ::android::gralloc4::MetadataType_Height,
+                            &encoded_height);
+  if (error != Error::NONE) {
+    return std::nullopt;
+  }
+
+  uint64_t height = 0;
+  ::android::gralloc4::decodeHeight(encoded_height, &height);
+  return static_cast<uint32_t>(height);
+}
+
+std::optional<uint32_t> Gralloc::GetDrmFormat(buffer_handle_t buffer) {
+  if (gralloc4_ == nullptr) {
+    ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+    return std::nullopt;
+  }
+
+  hidl_vec<uint8_t> encoded_format;
+
+  Error error =
+      GetMetadata(buffer, ::android::gralloc4::MetadataType_PixelFormatFourCC,
+                  &encoded_format);
+  if (error != Error::NONE) {
+    return std::nullopt;
+  }
+
+  uint32_t format = 0;
+  ::android::gralloc4::decodePixelFormatFourCC(encoded_format, &format);
+  return static_cast<uint32_t>(format);
+}
+
+std::optional<std::vector<PlaneLayout>> Gralloc::GetPlaneLayouts(
+    buffer_handle_t buffer) {
+  if (gralloc4_ == nullptr) {
+    ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+    return std::nullopt;
+  }
+
+  hidl_vec<uint8_t> encoded_layouts;
+
+  Error error = GetMetadata(
+      buffer, ::android::gralloc4::MetadataType_PlaneLayouts, &encoded_layouts);
+  if (error != Error::NONE) {
+    return std::nullopt;
+  }
+
+  std::vector<PlaneLayout> plane_layouts;
+  ::android::gralloc4::decodePlaneLayouts(encoded_layouts, &plane_layouts);
+  return plane_layouts;
+}
+
+std::optional<uint32_t> Gralloc::GetMonoPlanarStrideBytes(
+    buffer_handle_t buffer) {
+  if (gralloc4_ == nullptr) {
+    ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+    return std::nullopt;
+  }
+
+  auto plane_layouts_opt = GetPlaneLayouts(buffer);
+  if (!plane_layouts_opt) {
+    return std::nullopt;
+  }
+
+  std::vector<PlaneLayout>& plane_layouts = *plane_layouts_opt;
+  if (plane_layouts.size() != 1) {
+    return std::nullopt;
+  }
+
+  return static_cast<uint32_t>(plane_layouts[0].strideInBytes);
+}
+
+std::optional<GrallocBuffer> Gralloc::Import(buffer_handle_t buffer) {
+  if (gralloc4_ == nullptr) {
+    ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+    return std::nullopt;
+  }
+
+  buffer_handle_t imported_buffer;
+
+  Error error;
+  auto ret =
+      gralloc4_->importBuffer(buffer, [&](const auto& err, const auto& buf) {
+        error = err;
+        if (err == Error::NONE) {
+          imported_buffer = static_cast<buffer_handle_t>(buf);
+        }
+      });
+
+  if (!ret.isOk() || error != Error::NONE) {
+    ALOGE("%s failed to import buffer", __FUNCTION__);
+    return std::nullopt;
+  }
+  return GrallocBuffer(this, imported_buffer);
+}
+
+void Gralloc::Release(buffer_handle_t buffer) {
+  if (gralloc4_ == nullptr) {
+    ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+    return;
+  }
+
+  auto native_buffer = const_cast<native_handle_t*>(buffer);
+  auto ret = gralloc4_->freeBuffer(native_buffer);
+
+  if (!ret.isOk()) {
+    ALOGE("%s failed to release buffer", __FUNCTION__);
+  }
+}
+
+std::optional<void*> Gralloc::Lock(buffer_handle_t buffer) {
+  if (gralloc4_ == nullptr) {
+    ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+    return std::nullopt;
+  }
+
+  auto native_buffer = const_cast<native_handle_t*>(buffer);
+
+  const auto buffer_usage = static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN |
+                                                  BufferUsage::CPU_WRITE_OFTEN);
+
+  auto width_opt = GetWidth(buffer);
+  if (!width_opt) {
+    return std::nullopt;
+  }
+
+  auto height_opt = GetHeight(buffer);
+  if (!height_opt) {
+    return std::nullopt;
+  }
+
+  IMapper::Rect buffer_region;
+  buffer_region.left = 0;
+  buffer_region.top = 0;
+  buffer_region.width = *width_opt;
+  buffer_region.height = *height_opt;
+
+  // Empty fence, lock immedietly.
+  hidl_handle fence;
+
+  Error error = Error::NONE;
+  void* data = nullptr;
+
+  auto ret =
+      gralloc4_->lock(native_buffer, buffer_usage, buffer_region, fence,
+                      [&](const auto& lock_error, const auto& lock_data) {
+                        error = lock_error;
+                        if (lock_error == Error::NONE) {
+                          data = lock_data;
+                        }
+                      });
+
+  if (!ret.isOk()) {
+    error = Error::NO_RESOURCES;
+  }
+
+  if (error != Error::NONE) {
+    ALOGE("%s failed to lock buffer", __FUNCTION__);
+    return std::nullopt;
+  }
+
+  return data;
+}
+
+std::optional<android_ycbcr> Gralloc::LockYCbCr(buffer_handle_t buffer) {
+  if (gralloc4_ == nullptr) {
+    ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+    return std::nullopt;
+  }
+
+  auto format_opt = GetDrmFormat(buffer);
+  if (!format_opt) {
+    ALOGE("%s failed to check format of buffer", __FUNCTION__);
+    return std::nullopt;
+  }
+
+  if (*format_opt != DRM_FORMAT_NV12 && *format_opt != DRM_FORMAT_NV21 &&
+      *format_opt != DRM_FORMAT_YVU420) {
+    ALOGE("%s called on non-ycbcr buffer", __FUNCTION__);
+    return std::nullopt;
+  }
+
+  auto lock_opt = Lock(buffer);
+  if (!lock_opt) {
+    ALOGE("%s failed to lock buffer", __FUNCTION__);
+    return std::nullopt;
+  }
+
+  auto plane_layouts_opt = GetPlaneLayouts(buffer);
+  if (!plane_layouts_opt) {
+    ALOGE("%s failed to get plane layouts", __FUNCTION__);
+    return std::nullopt;
+  }
+
+  android_ycbcr buffer_ycbcr;
+  buffer_ycbcr.y = nullptr;
+  buffer_ycbcr.cb = nullptr;
+  buffer_ycbcr.cr = nullptr;
+  buffer_ycbcr.ystride = 0;
+  buffer_ycbcr.cstride = 0;
+  buffer_ycbcr.chroma_step = 0;
+
+  for (const auto& plane_layout : *plane_layouts_opt) {
+    for (const auto& plane_layout_component : plane_layout.components) {
+      const auto& type = plane_layout_component.type;
+
+      if (!::android::gralloc4::isStandardPlaneLayoutComponentType(type)) {
+        continue;
+      }
+
+      auto* component_data = reinterpret_cast<uint8_t*>(*lock_opt) +
+                             plane_layout.offsetInBytes +
+                             plane_layout_component.offsetInBits / 8;
+
+      switch (static_cast<PlaneLayoutComponentType>(type.value)) {
+        case PlaneLayoutComponentType::Y:
+          buffer_ycbcr.y = component_data;
+          buffer_ycbcr.ystride = plane_layout.strideInBytes;
+          break;
+        case PlaneLayoutComponentType::CB:
+          buffer_ycbcr.cb = component_data;
+          buffer_ycbcr.cstride = plane_layout.strideInBytes;
+          buffer_ycbcr.chroma_step = plane_layout.sampleIncrementInBits / 8;
+          break;
+        case PlaneLayoutComponentType::CR:
+          buffer_ycbcr.cr = component_data;
+          buffer_ycbcr.cstride = plane_layout.strideInBytes;
+          buffer_ycbcr.chroma_step = plane_layout.sampleIncrementInBits / 8;
+          break;
+        default:
+          break;
+      }
+    }
+  }
+
+  return buffer_ycbcr;
+}
+
+void Gralloc::Unlock(buffer_handle_t buffer) {
+  if (gralloc4_ == nullptr) {
+    ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+    return;
+  }
+
+  auto native_handle = const_cast<native_handle_t*>(buffer);
+
+  Error error = Error::NONE;
+  auto ret = gralloc4_->unlock(
+      native_handle,
+      [&](const auto& unlock_error, const auto&) { error = unlock_error; });
+
+  if (!ret.isOk()) {
+    error = Error::NO_RESOURCES;
+  }
+
+  if (error != Error::NONE) {
+    ALOGE("%s failed to unlock buffer", __FUNCTION__);
+  }
+}
+
+GrallocBuffer::GrallocBuffer(Gralloc* gralloc, buffer_handle_t buffer)
+    : gralloc_(gralloc), buffer_(buffer) {}
+
+GrallocBuffer::~GrallocBuffer() { Release(); }
+
+GrallocBuffer::GrallocBuffer(GrallocBuffer&& rhs) { *this = std::move(rhs); }
+
+GrallocBuffer& GrallocBuffer::operator=(GrallocBuffer&& rhs) {
+  gralloc_ = rhs.gralloc_;
+  buffer_ = rhs.buffer_;
+  rhs.gralloc_ = nullptr;
+  rhs.buffer_ = nullptr;
+  return *this;
+}
+
+void GrallocBuffer::Release() {
+  if (gralloc_ && buffer_) {
+    gralloc_->Release(buffer_);
+    gralloc_ = nullptr;
+    buffer_ = nullptr;
+  }
+}
+
+std::optional<GrallocBufferView> GrallocBuffer::Lock() {
+  if (gralloc_ && buffer_) {
+    auto format_opt = GetDrmFormat();
+    if (!format_opt) {
+      ALOGE("%s failed to check format of buffer", __FUNCTION__);
+      return std::nullopt;
+    }
+    if (*format_opt != DRM_FORMAT_NV12 && *format_opt != DRM_FORMAT_NV21 &&
+        *format_opt != DRM_FORMAT_YVU420) {
+      auto locked_opt = gralloc_->Lock(buffer_);
+      if (!locked_opt) {
+        return std::nullopt;
+      }
+      return GrallocBufferView(this, *locked_opt);
+    } else {
+      auto locked_ycbcr_opt = gralloc_->LockYCbCr(buffer_);
+      if (!locked_ycbcr_opt) {
+        ALOGE("%s failed to lock ycbcr buffer", __FUNCTION__);
+        return std::nullopt;
+      }
+      return GrallocBufferView(this, *locked_ycbcr_opt);
+    }
+  }
+  return std::nullopt;
+}
+
+void GrallocBuffer::Unlock() {
+  if (gralloc_ && buffer_) {
+    gralloc_->Unlock(buffer_);
+  }
+}
+
+std::optional<uint32_t> GrallocBuffer::GetWidth() {
+  if (gralloc_ && buffer_) {
+    return gralloc_->GetWidth(buffer_);
+  }
+  return std::nullopt;
+}
+
+std::optional<uint32_t> GrallocBuffer::GetHeight() {
+  if (gralloc_ && buffer_) {
+    return gralloc_->GetHeight(buffer_);
+  }
+  return std::nullopt;
+}
+
+std::optional<uint32_t> GrallocBuffer::GetDrmFormat() {
+  if (gralloc_ && buffer_) {
+    return gralloc_->GetDrmFormat(buffer_);
+  }
+  return std::nullopt;
+}
+
+std::optional<std::vector<PlaneLayout>> GrallocBuffer::GetPlaneLayouts() {
+  if (gralloc_ && buffer_) {
+    return gralloc_->GetPlaneLayouts(buffer_);
+  }
+  return std::nullopt;
+}
+
+std::optional<uint32_t> GrallocBuffer::GetMonoPlanarStrideBytes() {
+  if (gralloc_ && buffer_) {
+    return gralloc_->GetMonoPlanarStrideBytes(buffer_);
+  }
+  return std::nullopt;
+}
+
+GrallocBufferView::GrallocBufferView(GrallocBuffer* buffer, void* raw)
+    : gralloc_buffer_(buffer), locked_(raw) {}
+
+GrallocBufferView::GrallocBufferView(GrallocBuffer* buffer, android_ycbcr raw)
+    : gralloc_buffer_(buffer), locked_ycbcr_(raw) {}
+
+GrallocBufferView::~GrallocBufferView() {
+  if (gralloc_buffer_) {
+    gralloc_buffer_->Unlock();
+  }
+}
+
+GrallocBufferView::GrallocBufferView(GrallocBufferView&& rhs) {
+  *this = std::move(rhs);
+}
+
+GrallocBufferView& GrallocBufferView::operator=(GrallocBufferView&& rhs) {
+  std::swap(gralloc_buffer_, rhs.gralloc_buffer_);
+  std::swap(locked_, rhs.locked_);
+  std::swap(locked_ycbcr_, rhs.locked_ycbcr_);
+  return *this;
+}
+
+const std::optional<void*> GrallocBufferView::Get() const { return locked_; }
+
+const std::optional<android_ycbcr>& GrallocBufferView::GetYCbCr() const {
+  return locked_ycbcr_;
+}
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/Gralloc.h b/system/hwc3/Gralloc.h
new file mode 100644
index 0000000..f259f29
--- /dev/null
+++ b/system/hwc3/Gralloc.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWC_GRALLOC_H
+#define ANDROID_HWC_GRALLOC_H
+
+#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
+#include <android/hardware/graphics/mapper/4.0/IMapper.h>
+#include <hardware/gralloc.h>
+#include <system/graphics.h>
+#include <utils/StrongPointer.h>
+
+#include <memory>
+#include <optional>
+#include <vector>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class Gralloc;
+class GrallocBuffer;
+
+// An RAII object that will Unlock() a GrallocBuffer upon destruction.
+class GrallocBufferView {
+ public:
+  virtual ~GrallocBufferView();
+
+  GrallocBufferView(const GrallocBufferView& rhs) = delete;
+  GrallocBufferView& operator=(const GrallocBufferView& rhs) = delete;
+
+  GrallocBufferView(GrallocBufferView&& rhs);
+  GrallocBufferView& operator=(GrallocBufferView&& rhs);
+
+  const std::optional<void*> Get() const;
+
+  const std::optional<android_ycbcr>& GetYCbCr() const;
+
+ private:
+  friend class GrallocBuffer;
+  GrallocBufferView(GrallocBuffer* buffer, void* raw);
+  GrallocBufferView(GrallocBuffer* buffer, android_ycbcr raw);
+
+  // The GrallocBuffer that should be unlocked upon destruction of this object.
+  GrallocBuffer* gralloc_buffer_ = nullptr;
+
+  std::optional<void*> locked_;
+  std::optional<android_ycbcr> locked_ycbcr_;
+};
+
+// A gralloc 4.0 buffer that has been imported in the current process and
+// that will be released upon destruction. Users must ensure that the Gralloc
+// instance that this buffer is created with out lives this buffer.
+class GrallocBuffer {
+ public:
+  GrallocBuffer(Gralloc* gralloc, buffer_handle_t buffer);
+  virtual ~GrallocBuffer();
+
+  GrallocBuffer(const GrallocBuffer& rhs) = delete;
+  GrallocBuffer& operator=(const GrallocBuffer& rhs) = delete;
+
+  GrallocBuffer(GrallocBuffer&& rhs);
+  GrallocBuffer& operator=(GrallocBuffer&& rhs);
+
+  // Locks the buffer for reading and returns a view if successful.
+  std::optional<GrallocBufferView> Lock();
+
+  std::optional<uint32_t> GetWidth();
+  std::optional<uint32_t> GetHeight();
+  std::optional<uint32_t> GetDrmFormat();
+
+  // Returns the stride of the buffer if it is a single plane buffer or fails
+  // and returns nullopt if the buffer is for a multi plane buffer.
+  std::optional<uint32_t> GetMonoPlanarStrideBytes();
+
+  std::optional<
+      std::vector<aidl::android::hardware::graphics::common::PlaneLayout>>
+  GetPlaneLayouts();
+
+ private:
+  // Internal visibility for Unlock().
+  friend class GrallocBufferView;
+
+  // Unlocks the buffer from reading.
+  void Unlock();
+
+  void Release();
+
+  Gralloc* gralloc_ = nullptr;
+  buffer_handle_t buffer_ = nullptr;
+};
+
+class Gralloc {
+ public:
+  Gralloc();
+  virtual ~Gralloc() = default;
+
+  // Imports the given buffer handle into the current process and returns an
+  // imported buffer which can be used for reading. Users must ensure that the
+  // Gralloc instance outlives any GrallocBuffers.
+  std::optional<GrallocBuffer> Import(buffer_handle_t buffer);
+
+ private:
+  // The below functions are made available only to GrallocBuffer so that
+  // users only call gralloc functions on *imported* buffers.
+  friend class GrallocBuffer;
+
+  // See GrallocBuffer::Release.
+  void Release(buffer_handle_t buffer);
+
+  // See GrallocBuffer::Lock.
+  std::optional<void*> Lock(buffer_handle_t buffer);
+
+  // See GrallocBuffer::LockYCbCr.
+  std::optional<android_ycbcr> LockYCbCr(buffer_handle_t buffer);
+
+  // See GrallocBuffer::Unlock.
+  void Unlock(buffer_handle_t buffer);
+
+  // See GrallocBuffer::GetWidth.
+  std::optional<uint32_t> GetWidth(buffer_handle_t buffer);
+
+  // See GrallocBuffer::GetHeight.
+  std::optional<uint32_t> GetHeight(buffer_handle_t buffer);
+
+  // See GrallocBuffer::GetDrmFormat.
+  std::optional<uint32_t> GetDrmFormat(buffer_handle_t buffer);
+
+  // See GrallocBuffer::GetPlaneLayouts.
+  std::optional<
+      std::vector<aidl::android::hardware::graphics::common::PlaneLayout>>
+  GetPlaneLayouts(buffer_handle_t buffer);
+
+  // Returns the stride of the buffer if it is a single plane buffer or fails
+  // and returns nullopt if the buffer is for a multi plane buffer.
+  std::optional<uint32_t> GetMonoPlanarStrideBytes(buffer_handle_t);
+
+  // See GrallocBuffer::GetMetadata.
+  ::android::hardware::graphics::mapper::V4_0::Error GetMetadata(
+      buffer_handle_t buffer,
+      ::android::hardware::graphics::mapper::V4_0::IMapper::MetadataType type,
+      ::android::hardware::hidl_vec<uint8_t>* metadata);
+
+  ::android::sp<::android::hardware::graphics::mapper::V4_0::IMapper> gralloc4_;
+};
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
\ No newline at end of file
diff --git a/system/hwc3/GuestFrameComposer.cpp b/system/hwc3/GuestFrameComposer.cpp
new file mode 100644
index 0000000..b7d7322
--- /dev/null
+++ b/system/hwc3/GuestFrameComposer.cpp
@@ -0,0 +1,1185 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#include "GuestFrameComposer.h"
+
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android/hardware/graphics/common/1.0/types.h>
+#include <device_config_shared.h>
+#include <drm_fourcc.h>
+#include <libyuv.h>
+#include <sync/sync.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicBufferAllocator.h>
+#include <ui/GraphicBufferMapper.h>
+
+#include "Display.h"
+#include "Drm.h"
+#include "Layer.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+namespace {
+
+using ::android::hardware::graphics::common::V1_0::ColorTransform;
+
+uint64_t AlignToPower2(uint64_t val, uint8_t align_log) {
+  uint64_t align = 1ULL << align_log;
+  return ((val + (align - 1)) / align) * align;
+}
+
+bool LayerNeedsScaling(const Layer& layer) {
+  common::Rect crop = layer.getSourceCropInt();
+  common::Rect frame = layer.getDisplayFrame();
+
+  int fromW = crop.right - crop.left;
+  int fromH = crop.bottom - crop.top;
+  int toW = frame.right - frame.left;
+  int toH = frame.bottom - frame.top;
+
+  bool not_rot_scale = fromW != toW || fromH != toH;
+  bool rot_scale = fromW != toH || fromH != toW;
+
+  bool needs_rot = static_cast<int32_t>(layer.getTransform()) &
+                   static_cast<int32_t>(common::Transform::ROT_90);
+
+  return needs_rot ? rot_scale : not_rot_scale;
+}
+
+bool LayerNeedsBlending(const Layer& layer) {
+  return layer.getBlendMode() != common::BlendMode::NONE;
+}
+
+bool LayerNeedsAttenuation(const Layer& layer) {
+  return layer.getBlendMode() == common::BlendMode::COVERAGE;
+}
+
+struct BufferSpec;
+typedef int (*ConverterFunction)(const BufferSpec& src, const BufferSpec& dst,
+                                 bool v_flip);
+int DoCopy(const BufferSpec& src, const BufferSpec& dst, bool vFlip);
+int ConvertFromRGB565(const BufferSpec& src, const BufferSpec& dst, bool vFlip);
+int ConvertFromYV12(const BufferSpec& src, const BufferSpec& dst, bool vFlip);
+
+ConverterFunction GetConverterForDrmFormat(uint32_t drmFormat) {
+  switch (drmFormat) {
+    case DRM_FORMAT_ABGR8888:
+    case DRM_FORMAT_XBGR8888:
+      return &DoCopy;
+    case DRM_FORMAT_RGB565:
+      return &ConvertFromRGB565;
+    case DRM_FORMAT_YVU420:
+      return &ConvertFromYV12;
+  }
+  DEBUG_LOG("Unsupported drm format: %d(%s), returning null converter",
+            drmFormat, GetDrmFormatString(drmFormat));
+  return nullptr;
+}
+
+bool IsDrmFormatSupported(uint32_t drmFormat) {
+  return GetConverterForDrmFormat(drmFormat) != nullptr;
+}
+
+// Libyuv's convert functions only allow the combination of any rotation
+// (multiple of 90 degrees) and a vertical flip, but not horizontal flips.
+// Surfaceflinger's transformations are expressed in terms of a vertical flip,
+// a horizontal flip and/or a single 90 degrees clockwise rotation (see
+// NATIVE_WINDOW_TRANSFORM_HINT documentation on system/window.h for more
+// insight). The following code allows to turn a horizontal flip into a 180
+// degrees rotation and a vertical flip.
+libyuv::RotationMode GetRotationFromTransform(common::Transform transform) {
+  uint32_t rotation = 0;
+  rotation += (static_cast<int32_t>(transform) &
+               static_cast<int32_t>(common::Transform::ROT_90))
+                  ? 1
+                  : 0;  // 1 * ROT90 bit
+  rotation += (static_cast<int32_t>(transform) &
+               static_cast<int32_t>(common::Transform::FLIP_H))
+                  ? 2
+                  : 0;  // 2 * VFLIP bit
+  return static_cast<libyuv::RotationMode>(90 * rotation);
+}
+
+bool GetVFlipFromTransform(common::Transform transform) {
+  // vertical flip xor horizontal flip
+  bool hasVFlip = static_cast<int32_t>(transform) &
+                  static_cast<int32_t>(common::Transform::FLIP_V);
+  bool hasHFlip = static_cast<int32_t>(transform) &
+                  static_cast<int32_t>(common::Transform::FLIP_H);
+  return hasVFlip ^ hasHFlip;
+}
+
+struct BufferSpec {
+  uint8_t* buffer;
+  std::optional<android_ycbcr> buffer_ycbcr;
+  int width;
+  int height;
+  int cropX;
+  int cropY;
+  int cropWidth;
+  int cropHeight;
+  uint32_t drmFormat;
+  int strideBytes;
+  int sampleBytes;
+
+  BufferSpec(uint8_t* buffer, std::optional<android_ycbcr> buffer_ycbcr,
+             int width, int height, int cropX, int cropY, int cropWidth,
+             int cropHeight, uint32_t drmFormat, int strideBytes,
+             int sampleBytes)
+      : buffer(buffer),
+        buffer_ycbcr(buffer_ycbcr),
+        width(width),
+        height(height),
+        cropX(cropX),
+        cropY(cropY),
+        cropWidth(cropWidth),
+        cropHeight(cropHeight),
+        drmFormat(drmFormat),
+        strideBytes(strideBytes),
+        sampleBytes(sampleBytes) {}
+
+  BufferSpec(uint8_t* buffer, int width, int height, int strideBytes)
+      : BufferSpec(buffer,
+                   /*buffer_ycbcr=*/std::nullopt, width, height,
+                   /*cropX=*/0,
+                   /*cropY=*/0,
+                   /*cropWidth=*/width,
+                   /*cropHeight=*/height,
+                   /*drmFormat=*/DRM_FORMAT_ABGR8888, strideBytes,
+                   /*sampleBytes=*/4) {}
+};
+
+int ConvertFromRGB565(const BufferSpec& src, const BufferSpec& dst,
+                      bool vFlip) {
+  ATRACE_CALL();
+
+  // Point to the upper left corner of the crop rectangle
+  uint8_t* srcBuffer =
+      src.buffer + src.cropY * src.strideBytes + src.cropX * src.sampleBytes;
+  uint8_t* dstBuffer =
+      dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes;
+
+  int width = src.cropWidth;
+  int height = src.cropHeight;
+  if (vFlip) {
+    height = -height;
+  }
+
+  return libyuv::RGB565ToARGB(srcBuffer, src.strideBytes,  //
+                              dstBuffer, dst.strideBytes,  //
+                              width, height);
+}
+
+int ConvertFromYV12(const BufferSpec& src, const BufferSpec& dst, bool vFlip) {
+  ATRACE_CALL();
+
+  // The following calculation of plane offsets and alignments are based on
+  // swiftshader's Sampler::setTextureLevel() implementation
+  // (Renderer/Sampler.cpp:225)
+
+  auto& srcBufferYCbCrOpt = src.buffer_ycbcr;
+  if (!srcBufferYCbCrOpt) {
+    ALOGE("%s called on non ycbcr buffer", __FUNCTION__);
+    return -1;
+  }
+  auto& srcBufferYCbCr = *srcBufferYCbCrOpt;
+
+  // The libyuv::I420ToARGB() function is for tri-planar.
+  if (srcBufferYCbCr.chroma_step != 1) {
+    ALOGE("%s called with bad chroma step", __FUNCTION__);
+    return -1;
+  }
+
+  uint8_t* srcY = reinterpret_cast<uint8_t*>(srcBufferYCbCr.y);
+  int strideY = srcBufferYCbCr.ystride;
+  uint8_t* srcU = reinterpret_cast<uint8_t*>(srcBufferYCbCr.cb);
+  int strideU = srcBufferYCbCr.cstride;
+  uint8_t* srcV = reinterpret_cast<uint8_t*>(srcBufferYCbCr.cr);
+  int strideV = srcBufferYCbCr.cstride;
+
+  // Adjust for crop
+  srcY += src.cropY * strideY + src.cropX;
+  srcV += (src.cropY / 2) * strideV + (src.cropX / 2);
+  srcU += (src.cropY / 2) * strideU + (src.cropX / 2);
+  uint8_t* dstBuffer =
+      dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes;
+
+  int width = dst.cropWidth;
+  int height = dst.cropHeight;
+
+  if (vFlip) {
+    height = -height;
+  }
+
+  // YV12 is the same as I420, with the U and V planes swapped
+  return libyuv::I420ToARGB(srcY, strideY, srcV, strideV, srcU, strideU,
+                            dstBuffer, dst.strideBytes, width, height);
+}
+
+int DoConversion(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+  ConverterFunction func = GetConverterForDrmFormat(src.drmFormat);
+  if (!func) {
+    // GetConverterForDrmFormat should've logged the issue for us.
+    return -1;
+  }
+  return func(src, dst, v_flip);
+}
+
+int DoCopy(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+  ATRACE_CALL();
+
+  // Point to the upper left corner of the crop rectangle
+  uint8_t* srcBuffer =
+      src.buffer + src.cropY * src.strideBytes + src.cropX * src.sampleBytes;
+  uint8_t* dstBuffer =
+      dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes;
+  int width = src.cropWidth;
+  int height = src.cropHeight;
+
+  if (v_flip) {
+    height = -height;
+  }
+
+  // HAL formats are named based on the order of the pixel components on the
+  // byte stream, while libyuv formats are named based on the order of those
+  // pixel components in an integer written from left to right. So
+  // libyuv::FOURCC_ARGB is equivalent to HAL_PIXEL_FORMAT_BGRA_8888.
+  auto ret = libyuv::ARGBCopy(srcBuffer, src.strideBytes, dstBuffer,
+                              dst.strideBytes, width, height);
+  return ret;
+}
+
+int DoRotation(const BufferSpec& src, const BufferSpec& dst,
+               libyuv::RotationMode rotation, bool v_flip) {
+  ATRACE_CALL();
+
+  // Point to the upper left corner of the crop rectangles
+  uint8_t* srcBuffer =
+      src.buffer + src.cropY * src.strideBytes + src.cropX * src.sampleBytes;
+  uint8_t* dstBuffer =
+      dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes;
+  int width = src.cropWidth;
+  int height = src.cropHeight;
+
+  if (v_flip) {
+    height = -height;
+  }
+
+  return libyuv::ARGBRotate(srcBuffer, src.strideBytes, dstBuffer,
+                            dst.strideBytes, width, height, rotation);
+}
+
+int DoScaling(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+  ATRACE_CALL();
+
+  // Point to the upper left corner of the crop rectangles
+  uint8_t* srcBuffer =
+      src.buffer + src.cropY * src.strideBytes + src.cropX * src.sampleBytes;
+  uint8_t* dstBuffer =
+      dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes;
+  int srcWidth = src.cropWidth;
+  int srcHeight = src.cropHeight;
+  int dstWidth = dst.cropWidth;
+  int dstHeight = dst.cropHeight;
+
+  if (v_flip) {
+    srcHeight = -srcHeight;
+  }
+
+  return libyuv::ARGBScale(srcBuffer, src.strideBytes, srcWidth, srcHeight,
+                           dstBuffer, dst.strideBytes, dstWidth, dstHeight,
+                           libyuv::kFilterBilinear);
+}
+
+int DoAttenuation(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+  ATRACE_CALL();
+
+  // Point to the upper left corner of the crop rectangles
+  uint8_t* srcBuffer =
+      src.buffer + src.cropY * src.strideBytes + src.cropX * src.sampleBytes;
+  uint8_t* dstBuffer =
+      dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes;
+  int width = dst.cropWidth;
+  int height = dst.cropHeight;
+
+  if (v_flip) {
+    height = -height;
+  }
+
+  return libyuv::ARGBAttenuate(srcBuffer, src.strideBytes, dstBuffer,
+                               dst.strideBytes, width, height);
+}
+
+int DoBlending(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+  ATRACE_CALL();
+
+  // Point to the upper left corner of the crop rectangles
+  uint8_t* srcBuffer =
+      src.buffer + src.cropY * src.strideBytes + src.cropX * src.sampleBytes;
+  uint8_t* dstBuffer =
+      dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes;
+  int width = dst.cropWidth;
+  int height = dst.cropHeight;
+
+  if (v_flip) {
+    height = -height;
+  }
+
+  // libyuv's ARGB format is hwcomposer's BGRA format, since blending only cares
+  // for the position of alpha in the pixel and not the position of the colors
+  // this function is perfectly usable.
+  return libyuv::ARGBBlend(srcBuffer, src.strideBytes, dstBuffer,
+                           dst.strideBytes, dstBuffer, dst.strideBytes, width,
+                           height);
+}
+
+std::optional<BufferSpec> GetBufferSpec(GrallocBuffer& buffer,
+                                        GrallocBufferView& bufferView,
+                                        const common::Rect& bufferCrop) {
+  auto bufferFormatOpt = buffer.GetDrmFormat();
+  if (!bufferFormatOpt) {
+    ALOGE("Failed to get gralloc buffer format.");
+    return std::nullopt;
+  }
+  uint32_t bufferFormat = *bufferFormatOpt;
+
+  auto bufferWidthOpt = buffer.GetWidth();
+  if (!bufferWidthOpt) {
+    ALOGE("Failed to get gralloc buffer width.");
+    return std::nullopt;
+  }
+  uint32_t bufferWidth = *bufferWidthOpt;
+
+  auto bufferHeightOpt = buffer.GetHeight();
+  if (!bufferHeightOpt) {
+    ALOGE("Failed to get gralloc buffer height.");
+    return std::nullopt;
+  }
+  uint32_t bufferHeight = *bufferHeightOpt;
+
+  uint8_t* bufferData = nullptr;
+  uint32_t bufferStrideBytes = 0;
+  std::optional<android_ycbcr> bufferYCbCrData;
+
+  if (bufferFormat == DRM_FORMAT_NV12 || bufferFormat == DRM_FORMAT_NV21 ||
+      bufferFormat == DRM_FORMAT_YVU420) {
+    bufferYCbCrData = bufferView.GetYCbCr();
+    if (!bufferYCbCrData) {
+      ALOGE("%s failed to get raw ycbcr from view.", __FUNCTION__);
+      return std::nullopt;
+    }
+  } else {
+    auto bufferDataOpt = bufferView.Get();
+    if (!bufferDataOpt) {
+      ALOGE("%s failed to lock gralloc buffer.", __FUNCTION__);
+      return std::nullopt;
+    }
+    bufferData = reinterpret_cast<uint8_t*>(*bufferDataOpt);
+
+    auto bufferStrideBytesOpt = buffer.GetMonoPlanarStrideBytes();
+    if (!bufferStrideBytesOpt) {
+      ALOGE("%s failed to get plane stride.", __FUNCTION__);
+      return std::nullopt;
+    }
+    bufferStrideBytes = *bufferStrideBytesOpt;
+  }
+
+  return BufferSpec(bufferData, bufferYCbCrData, bufferWidth, bufferHeight,
+                    bufferCrop.left, bufferCrop.top,
+                    bufferCrop.right - bufferCrop.left,
+                    bufferCrop.bottom - bufferCrop.top, bufferFormat,
+                    bufferStrideBytes, GetDrmFormatBytesPerPixel(bufferFormat));
+}
+
+}  // namespace
+
+HWC3::Error GuestFrameComposer::init() {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  HWC3::Error error = mDrmPresenter.init();
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: failed to initialize DrmPresenter", __FUNCTION__);
+    return error;
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error GuestFrameComposer::registerOnHotplugCallback(
+    const HotplugCallback& cb) {
+  return mDrmPresenter.registerOnHotplugCallback(cb);
+  return HWC3::Error::None;
+}
+
+HWC3::Error GuestFrameComposer::unregisterOnHotplugCallback() {
+  return mDrmPresenter.unregisterOnHotplugCallback();
+}
+
+HWC3::Error GuestFrameComposer::onDisplayCreate(Display* display) {
+  int64_t displayId = display->getId();
+  int32_t displayConfigId;
+  int32_t displayWidth;
+  int32_t displayHeight;
+
+  HWC3::Error error = display->getActiveConfig(&displayConfigId);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRIu64 " has no active config", __FUNCTION__,
+          displayId);
+    return error;
+  }
+
+  error = display->getDisplayAttribute(displayConfigId, DisplayAttribute::WIDTH,
+                                       &displayWidth);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRIu64 " failed to get width", __FUNCTION__,
+          displayId);
+    return error;
+  }
+
+  error = display->getDisplayAttribute(
+      displayConfigId, DisplayAttribute::HEIGHT, &displayHeight);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRIu64 " failed to get height", __FUNCTION__,
+          displayId);
+    return error;
+  }
+
+  auto it = mDisplayInfos.find(displayId);
+  if (it != mDisplayInfos.end()) {
+    ALOGE("%s: display:%" PRIu64 " already created?", __FUNCTION__, displayId);
+  }
+
+  DisplayInfo& displayInfo = mDisplayInfos[displayId];
+
+  uint32_t bufferStride;
+  buffer_handle_t bufferHandle;
+
+  auto status = ::android::GraphicBufferAllocator::get().allocate(
+      displayWidth,                       //
+      displayHeight,                      //
+      ::android::PIXEL_FORMAT_RGBA_8888,  //
+      /*layerCount=*/1,                   //
+      ::android::GraphicBuffer::USAGE_HW_COMPOSER |
+          ::android::GraphicBuffer::USAGE_SW_READ_OFTEN |
+          ::android::GraphicBuffer::USAGE_SW_WRITE_OFTEN,  //
+      &bufferHandle,                                       //
+      &bufferStride,                                       //
+      "RanchuHwc");
+  if (status != ::android::OK) {
+    ALOGE("%s: failed to allocate composition buffer for display:%" PRIu64,
+          __FUNCTION__, displayId);
+    return HWC3::Error::NoResources;
+  }
+
+  displayInfo.compositionResultBuffer = bufferHandle;
+
+  auto [drmBufferCreateError, drmBuffer] = mDrmPresenter.create(bufferHandle);
+  if (drmBufferCreateError != HWC3::Error::None) {
+    ALOGE("%s: failed to create drm buffer for display:%" PRIu64, __FUNCTION__,
+          displayId);
+    return drmBufferCreateError;
+  }
+  displayInfo.compositionResultDrmBuffer = std::move(drmBuffer);
+
+  if (displayId == 0) {
+    auto [flushError, flushSyncFd] = mDrmPresenter.flushToDisplay(
+        displayId, *displayInfo.compositionResultDrmBuffer, -1);
+    if (flushError != HWC3::Error::None) {
+      ALOGW(
+          "%s: Initial display flush failed. HWComposer assuming that we are "
+          "running in QEMU without a display and disabling presenting.",
+          __FUNCTION__);
+      mPresentDisabled = true;
+    }
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error GuestFrameComposer::onDisplayDestroy(Display* display) {
+  auto displayId = display->getId();
+
+  auto it = mDisplayInfos.find(displayId);
+  if (it == mDisplayInfos.end()) {
+    ALOGE("%s: display:%" PRIu64 " missing display buffers?", __FUNCTION__,
+          displayId);
+    return HWC3::Error::BadDisplay;
+  }
+
+  DisplayInfo& displayInfo = mDisplayInfos[displayId];
+
+  ::android::GraphicBufferAllocator::get().free(
+      displayInfo.compositionResultBuffer);
+
+  mDisplayInfos.erase(it);
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error GuestFrameComposer::onDisplayClientTargetSet(Display*) {
+  return HWC3::Error::None;
+}
+
+HWC3::Error GuestFrameComposer::onActiveConfigChange(Display* /*display*/) {
+  return HWC3::Error::None;
+};
+
+HWC3::Error GuestFrameComposer::getDisplayConfigsFromDeviceConfig(
+    std::vector<GuestFrameComposer::DisplayConfig>* configs) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  const auto deviceConfig = cuttlefish::GetDeviceConfig();
+  for (const auto& deviceDisplayConfig : deviceConfig.display_config()) {
+    DisplayConfig displayConfig = {
+        .width = deviceDisplayConfig.width(),
+        .height = deviceDisplayConfig.height(),
+        .dpiX = deviceDisplayConfig.dpi(),
+        .dpiY = deviceDisplayConfig.dpi(),
+        .refreshRateHz = deviceDisplayConfig.refresh_rate_hz(),
+    };
+
+    configs->push_back(displayConfig);
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error GuestFrameComposer::getDisplayConfigsFromSystemProp(
+    std::vector<GuestFrameComposer::DisplayConfig>* configs) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  static constexpr const char kExternalDisplayProp[] =
+      "hwservicemanager.external.displays";
+
+  const auto propString =
+      ::android::base::GetProperty(kExternalDisplayProp, "");
+  DEBUG_LOG("%s: prop value is: %s", __FUNCTION__, propString.c_str());
+
+  if (propString.empty()) {
+    return HWC3::Error::None;
+  }
+
+  const std::vector<std::string> propStringParts =
+      ::android::base::Split(propString, ",");
+  if (propStringParts.size() % 5 != 0) {
+    ALOGE("%s: Invalid syntax for system prop %s which is %s", __FUNCTION__,
+          kExternalDisplayProp, propString.c_str());
+    return HWC3::Error::BadParameter;
+  }
+
+  std::vector<int> propIntParts;
+  for (const std::string& propStringPart : propStringParts) {
+    int propIntPart;
+    if (!::android::base::ParseInt(propStringPart, &propIntPart)) {
+      ALOGE("%s: Invalid syntax for system prop %s which is %s", __FUNCTION__,
+            kExternalDisplayProp, propString.c_str());
+      return HWC3::Error::BadParameter;
+    }
+    propIntParts.push_back(propIntPart);
+  }
+
+  while (!propIntParts.empty()) {
+    DisplayConfig display_config = {
+        .width = propIntParts[1],
+        .height = propIntParts[2],
+        .dpiX = propIntParts[3],
+        .dpiY = propIntParts[3],
+        .refreshRateHz = 160,
+    };
+
+    configs->push_back(display_config);
+
+    propIntParts.erase(propIntParts.begin(), propIntParts.begin() + 5);
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error GuestFrameComposer::validateDisplay(Display* display,
+                                                DisplayChanges* outChanges) {
+  const auto displayId = display->getId();
+  DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+  const std::vector<Layer*>& layers = display->getOrderedLayers();
+
+  bool fallbackToClientComposition = false;
+  for (Layer* layer : layers) {
+    const auto layerId = layer->getId();
+    const auto layerCompositionType = layer->getCompositionType();
+    const auto layerCompositionTypeString = toString(layerCompositionType);
+
+    if (layerCompositionType == Composition::INVALID) {
+      ALOGE("%s display:%" PRIu64 " layer:%" PRIu64 " has Invalid composition",
+            __FUNCTION__, displayId, layerId);
+      continue;
+    }
+
+    if (layerCompositionType == Composition::CLIENT ||
+        layerCompositionType == Composition::CURSOR ||
+        layerCompositionType == Composition::SIDEBAND ||
+        layerCompositionType == Composition::SOLID_COLOR) {
+      DEBUG_LOG("%s: display:%" PRIu64 " layer:%" PRIu64
+                " has composition type %s, falling back to client composition",
+                __FUNCTION__, displayId, layerId,
+                layerCompositionTypeString.c_str());
+      fallbackToClientComposition = true;
+      break;
+    }
+
+    if (layerCompositionType == Composition::DISPLAY_DECORATION) {
+      return HWC3::Error::Unsupported;
+    }
+
+    if (!canComposeLayer(layer)) {
+      DEBUG_LOG(
+          "%s: display:%" PRIu64 " layer:%" PRIu64
+          " composition not supported, falling back to client composition",
+          __FUNCTION__, displayId, layerId);
+      fallbackToClientComposition = true;
+      break;
+    }
+  }
+
+  if (fallbackToClientComposition) {
+    for (Layer* layer : layers) {
+      const auto layerId = layer->getId();
+      const auto layerCompositionType = layer->getCompositionType();
+
+      if (layerCompositionType == Composition::INVALID) {
+        continue;
+      }
+
+      if (layerCompositionType != Composition::CLIENT) {
+        DEBUG_LOG("%s display:%" PRIu64 " layer:%" PRIu64
+                  "composition updated to Client",
+                  __FUNCTION__, displayId, layerId);
+
+        outChanges->addLayerCompositionChange(displayId, layerId,
+                                              Composition::CLIENT);
+      }
+    }
+  }
+
+  // We can not draw below a Client (SurfaceFlinger) composed layer. Change all
+  // layers below a Client composed layer to also be Client composed.
+  if (layers.size() > 1) {
+    for (std::size_t layerIndex = layers.size() - 1; layerIndex > 0;
+         layerIndex--) {
+      auto layer = layers[layerIndex];
+      auto layerCompositionType = layer->getCompositionType();
+
+      if (layerCompositionType == Composition::CLIENT) {
+        for (std::size_t lowerLayerIndex = 0; lowerLayerIndex < layerIndex;
+             lowerLayerIndex++) {
+          auto lowerLayer = layers[lowerLayerIndex];
+          auto lowerLayerId = lowerLayer->getId();
+          auto lowerLayerCompositionType = lowerLayer->getCompositionType();
+
+          if (lowerLayerCompositionType != Composition::CLIENT) {
+            DEBUG_LOG("%s: display:%" PRIu64 " changing layer:%" PRIu64
+                      " to Client because"
+                      "hwcomposer can not draw below the Client composed "
+                      "layer:%" PRIu64,
+                      __FUNCTION__, displayId, lowerLayerId, layer->getId());
+
+            outChanges->addLayerCompositionChange(displayId, lowerLayerId,
+                                                  Composition::CLIENT);
+          }
+        }
+      }
+    }
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error GuestFrameComposer::presentDisplay(
+    Display* display, ::android::base::unique_fd* outDisplayFence,
+    std::unordered_map<int64_t,
+                       ::android::base::unique_fd>* /*outLayerFences*/) {
+  const auto displayId = display->getId();
+  DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+  if (mPresentDisabled) {
+    return HWC3::Error::None;
+  }
+
+  auto it = mDisplayInfos.find(displayId);
+  if (it == mDisplayInfos.end()) {
+    ALOGE("%s: display:%" PRIu64 " not found", __FUNCTION__, displayId);
+    return HWC3::Error::NoResources;
+  }
+
+  DisplayInfo& displayInfo = it->second;
+
+  if (displayInfo.compositionResultBuffer == nullptr) {
+    ALOGE("%s: display:%" PRIu64 " missing composition result buffer",
+          __FUNCTION__, displayId);
+    return HWC3::Error::NoResources;
+  }
+
+  if (displayInfo.compositionResultDrmBuffer == nullptr) {
+    ALOGE("%s: display:%" PRIu64 " missing composition result drm buffer",
+          __FUNCTION__, displayId);
+    return HWC3::Error::NoResources;
+  }
+
+  std::optional<GrallocBuffer> compositionResultBufferOpt =
+      mGralloc.Import(displayInfo.compositionResultBuffer);
+  if (!compositionResultBufferOpt) {
+    ALOGE("%s: display:%" PRIu64 " failed to import buffer", __FUNCTION__,
+          displayId);
+    return HWC3::Error::NoResources;
+  }
+
+  std::optional<uint32_t> compositionResultBufferWidthOpt =
+      compositionResultBufferOpt->GetWidth();
+  if (!compositionResultBufferWidthOpt) {
+    ALOGE("%s: display:%" PRIu64 " failed to query buffer width", __FUNCTION__,
+          displayId);
+    return HWC3::Error::NoResources;
+  }
+
+  std::optional<uint32_t> compositionResultBufferHeightOpt =
+      compositionResultBufferOpt->GetHeight();
+  if (!compositionResultBufferHeightOpt) {
+    ALOGE("%s: display:%" PRIu64 " failed to query buffer height", __FUNCTION__,
+          displayId);
+    return HWC3::Error::NoResources;
+  }
+
+  std::optional<uint32_t> compositionResultBufferStrideOpt =
+      compositionResultBufferOpt->GetMonoPlanarStrideBytes();
+  if (!compositionResultBufferStrideOpt) {
+    ALOGE("%s: display:%" PRIu64 " failed to query buffer stride", __FUNCTION__,
+          displayId);
+    return HWC3::Error::NoResources;
+  }
+
+  std::optional<GrallocBufferView> compositionResultBufferViewOpt =
+      compositionResultBufferOpt->Lock();
+  if (!compositionResultBufferViewOpt) {
+    ALOGE("%s: display:%" PRIu64 " failed to get buffer view", __FUNCTION__,
+          displayId);
+    return HWC3::Error::NoResources;
+  }
+
+  const std::optional<void*> compositionResultBufferDataOpt =
+      compositionResultBufferViewOpt->Get();
+  if (!compositionResultBufferDataOpt) {
+    ALOGE("%s: display:%" PRIu64 " failed to get buffer data", __FUNCTION__,
+          displayId);
+    return HWC3::Error::NoResources;
+  }
+
+  uint32_t compositionResultBufferWidth = *compositionResultBufferWidthOpt;
+  uint32_t compositionResultBufferHeight = *compositionResultBufferHeightOpt;
+  uint32_t compositionResultBufferStride = *compositionResultBufferStrideOpt;
+  uint8_t* compositionResultBufferData =
+      reinterpret_cast<uint8_t*>(*compositionResultBufferDataOpt);
+
+  const std::vector<Layer*>& layers = display->getOrderedLayers();
+
+  const bool noOpComposition = layers.empty();
+  const bool allLayersClientComposed =
+      std::all_of(layers.begin(),  //
+                  layers.end(),    //
+                  [](const Layer* layer) {
+                    return layer->getCompositionType() == Composition::CLIENT;
+                  });
+
+  if (noOpComposition) {
+    ALOGW("%s: display:%" PRIu64 " empty composition", __FUNCTION__, displayId);
+  } else if (allLayersClientComposed) {
+    auto clientTargetBufferOpt =
+        mGralloc.Import(display->waitAndGetClientTargetBuffer());
+    if (!clientTargetBufferOpt) {
+      ALOGE("%s: failed to import client target buffer.", __FUNCTION__);
+      return HWC3::Error::NoResources;
+    }
+    GrallocBuffer& clientTargetBuffer = *clientTargetBufferOpt;
+
+    auto clientTargetBufferViewOpt = clientTargetBuffer.Lock();
+    if (!clientTargetBufferViewOpt) {
+      ALOGE("%s: failed to lock client target buffer.", __FUNCTION__);
+      return HWC3::Error::NoResources;
+    }
+    GrallocBufferView& clientTargetBufferView = *clientTargetBufferViewOpt;
+
+    auto clientTargetPlaneLayoutsOpt = clientTargetBuffer.GetPlaneLayouts();
+    if (!clientTargetPlaneLayoutsOpt) {
+      ALOGE("Failed to get client target buffer plane layouts.");
+      return HWC3::Error::NoResources;
+    }
+    auto& clientTargetPlaneLayouts = *clientTargetPlaneLayoutsOpt;
+
+    if (clientTargetPlaneLayouts.size() != 1) {
+      ALOGE("Unexpected number of plane layouts for client target buffer.");
+      return HWC3::Error::NoResources;
+    }
+
+    std::size_t clientTargetPlaneSize =
+        clientTargetPlaneLayouts[0].totalSizeInBytes;
+
+    auto clientTargetDataOpt = clientTargetBufferView.Get();
+    if (!clientTargetDataOpt) {
+      ALOGE("%s failed to lock gralloc buffer.", __FUNCTION__);
+      return HWC3::Error::NoResources;
+    }
+    auto* clientTargetData = reinterpret_cast<uint8_t*>(*clientTargetDataOpt);
+
+    std::memcpy(compositionResultBufferData, clientTargetData,
+                clientTargetPlaneSize);
+  } else {
+    for (Layer* layer : layers) {
+      const auto layerId = layer->getId();
+      const auto layerCompositionType = layer->getCompositionType();
+      if (layerCompositionType != Composition::DEVICE) {
+        continue;
+      }
+
+      HWC3::Error error = composeLayerInto(layer,                          //
+                                           compositionResultBufferData,    //
+                                           compositionResultBufferWidth,   //
+                                           compositionResultBufferHeight,  //
+                                           compositionResultBufferStride,  //
+                                           4);
+      if (error != HWC3::Error::None) {
+        ALOGE("%s: display:%" PRIu64 " failed to compose layer:%" PRIu64,
+              __FUNCTION__, displayId, layerId);
+        return error;
+      }
+    }
+  }
+
+  if (display->hasColorTransform()) {
+    HWC3::Error error =
+        applyColorTransformToRGBA(display->getColorTransform(),   //
+                                  compositionResultBufferData,    //
+                                  compositionResultBufferWidth,   //
+                                  compositionResultBufferHeight,  //
+                                  compositionResultBufferStride);
+    if (error != HWC3::Error::None) {
+      ALOGE("%s: display:%" PRIu64 " failed to apply color transform",
+            __FUNCTION__, displayId);
+      return error;
+    }
+  }
+
+  DEBUG_LOG("%s display:%" PRIu64 " flushing drm buffer", __FUNCTION__,
+            displayId);
+
+  auto [error, fence] = mDrmPresenter.flushToDisplay(
+      displayId, *displayInfo.compositionResultDrmBuffer, -1);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRIu64 " failed to flush drm buffer" PRIu64,
+          __FUNCTION__, displayId);
+  }
+
+  *outDisplayFence = std::move(fence);
+  return error;
+}
+
+bool GuestFrameComposer::canComposeLayer(Layer* layer) {
+  buffer_handle_t bufferHandle = layer->getBuffer().getBuffer();
+  if (bufferHandle == nullptr) {
+    ALOGW("%s received a layer with a null handle", __FUNCTION__);
+    return false;
+  }
+
+  auto bufferOpt = mGralloc.Import(bufferHandle);
+  if (!bufferOpt) {
+    ALOGE("Failed to import layer buffer.");
+    return false;
+  }
+  GrallocBuffer& buffer = *bufferOpt;
+
+  auto bufferFormatOpt = buffer.GetDrmFormat();
+  if (!bufferFormatOpt) {
+    ALOGE("Failed to get layer buffer format.");
+    return false;
+  }
+  uint32_t bufferFormat = *bufferFormatOpt;
+
+  if (!IsDrmFormatSupported(bufferFormat)) {
+    return false;
+  }
+
+  return true;
+}
+
+HWC3::Error GuestFrameComposer::composeLayerInto(
+    Layer* srcLayer,                     //
+    std::uint8_t* dstBuffer,             //
+    std::uint32_t dstBufferWidth,        //
+    std::uint32_t dstBufferHeight,       //
+    std::uint32_t dstBufferStrideBytes,  //
+    std::uint32_t dstBufferBytesPerPixel) {
+  ATRACE_CALL();
+
+  libyuv::RotationMode rotation =
+      GetRotationFromTransform(srcLayer->getTransform());
+
+  auto srcBufferOpt = mGralloc.Import(srcLayer->waitAndGetBuffer());
+  if (!srcBufferOpt) {
+    ALOGE("%s: failed to import layer buffer.", __FUNCTION__);
+    return HWC3::Error::NoResources;
+  }
+  GrallocBuffer& srcBuffer = *srcBufferOpt;
+
+  auto srcBufferViewOpt = srcBuffer.Lock();
+  if (!srcBufferViewOpt) {
+    ALOGE("%s: failed to lock import layer buffer.", __FUNCTION__);
+    return HWC3::Error::NoResources;
+  }
+  GrallocBufferView& srcBufferView = *srcBufferViewOpt;
+
+  common::Rect srcLayerCrop = srcLayer->getSourceCropInt();
+  common::Rect srcLayerDisplayFrame = srcLayer->getDisplayFrame();
+
+  auto srcLayerSpecOpt = GetBufferSpec(srcBuffer, srcBufferView, srcLayerCrop);
+  if (!srcLayerSpecOpt) {
+    return HWC3::Error::NoResources;
+  }
+  BufferSpec srcLayerSpec = *srcLayerSpecOpt;
+
+  // TODO(jemoreira): Remove the hardcoded fomat.
+  bool needsConversion = srcLayerSpec.drmFormat != DRM_FORMAT_XBGR8888 &&
+                         srcLayerSpec.drmFormat != DRM_FORMAT_ABGR8888;
+  bool needsScaling = LayerNeedsScaling(*srcLayer);
+  bool needsRotation = rotation != libyuv::kRotate0;
+  bool needsTranspose = needsRotation && rotation != libyuv::kRotate180;
+  bool needsVFlip = GetVFlipFromTransform(srcLayer->getTransform());
+  bool needsAttenuation = LayerNeedsAttenuation(*srcLayer);
+  bool needsBlending = LayerNeedsBlending(*srcLayer);
+  bool needsCopy = !(needsConversion || needsScaling || needsRotation ||
+                     needsVFlip || needsAttenuation || needsBlending);
+
+  BufferSpec dstLayerSpec(
+      dstBuffer,
+      /*buffer_ycbcr=*/std::nullopt, dstBufferWidth, dstBufferHeight,
+      srcLayerDisplayFrame.left, srcLayerDisplayFrame.top,
+      srcLayerDisplayFrame.right - srcLayerDisplayFrame.left,
+      srcLayerDisplayFrame.bottom - srcLayerDisplayFrame.top,
+      DRM_FORMAT_XBGR8888, dstBufferStrideBytes, dstBufferBytesPerPixel);
+
+  // Add the destination layer to the bottom of the buffer stack
+  std::vector<BufferSpec> dstBufferStack(1, dstLayerSpec);
+
+  // If more than operation is to be performed, a temporary buffer is needed for
+  // each additional operation
+
+  // N operations need N destination buffers, the destination layer (the
+  // framebuffer) is one of them, so only N-1 temporary buffers are needed.
+  // Vertical flip is not taken into account because it can be done together
+  // with any other operation.
+  int neededScratchBuffers = (needsConversion ? 1 : 0) +
+                             (needsScaling ? 1 : 0) + (needsRotation ? 1 : 0) +
+                             (needsAttenuation ? 1 : 0) +
+                             (needsBlending ? 1 : 0) + (needsCopy ? 1 : 0) - 1;
+
+  int mScratchBufferWidth =
+      srcLayerDisplayFrame.right - srcLayerDisplayFrame.left;
+  int mScratchBufferHeight =
+      srcLayerDisplayFrame.bottom - srcLayerDisplayFrame.top;
+  int mScratchBufferStrideBytes =
+      AlignToPower2(mScratchBufferWidth * dstBufferBytesPerPixel, 4);
+  int mScratchBufferSizeBytes =
+      mScratchBufferHeight * mScratchBufferStrideBytes;
+
+  for (int i = 0; i < neededScratchBuffers; i++) {
+    BufferSpec mScratchBufferspec(
+        getRotatingScratchBuffer(mScratchBufferSizeBytes, i),
+        mScratchBufferWidth, mScratchBufferHeight, mScratchBufferStrideBytes);
+    dstBufferStack.push_back(mScratchBufferspec);
+  }
+
+  // Conversion and scaling should always be the first operations, so that every
+  // other operation works on equally sized frames (guaranteed to fit in the
+  // scratch buffers).
+
+  // TODO(jemoreira): We are converting to ARGB as the first step under the
+  // assumption that scaling ARGB is faster than scaling I420 (the most common).
+  // This should be confirmed with testing.
+  if (needsConversion) {
+    BufferSpec& dstBufferSpec = dstBufferStack.back();
+    if (needsScaling || needsTranspose) {
+      // If a rotation or a scaling operation are needed the dimensions at the
+      // top of the buffer stack are wrong (wrong sizes for scaling, swapped
+      // width and height for 90 and 270 rotations).
+      // Make width and height match the crop sizes on the source
+      int srcWidth = srcLayerSpec.cropWidth;
+      int srcHeight = srcLayerSpec.cropHeight;
+      int dst_stride_bytes =
+          AlignToPower2(srcWidth * dstBufferBytesPerPixel, 4);
+      size_t needed_size = dst_stride_bytes * srcHeight;
+      dstBufferSpec.width = srcWidth;
+      dstBufferSpec.height = srcHeight;
+      // Adjust the stride accordingly
+      dstBufferSpec.strideBytes = dst_stride_bytes;
+      // Crop sizes also need to be adjusted
+      dstBufferSpec.cropWidth = srcWidth;
+      dstBufferSpec.cropHeight = srcHeight;
+      // cropX and y are fine at 0, format is already set to match destination
+
+      // In case of a scale, the source frame may be bigger than the default tmp
+      // buffer size
+      dstBufferSpec.buffer = getSpecialScratchBuffer(needed_size);
+    }
+
+    int retval = DoConversion(srcLayerSpec, dstBufferSpec, needsVFlip);
+    if (retval) {
+      ALOGE("Got error code %d from DoConversion function", retval);
+    }
+    needsVFlip = false;
+    srcLayerSpec = dstBufferSpec;
+    dstBufferStack.pop_back();
+  }
+
+  if (needsScaling) {
+    BufferSpec& dstBufferSpec = dstBufferStack.back();
+    if (needsTranspose) {
+      // If a rotation is needed, the temporary buffer has the correct size but
+      // needs to be transposed and have its stride updated accordingly. The
+      // crop sizes also needs to be transposed, but not the x and y since they
+      // are both zero in a temporary buffer (and it is a temporary buffer
+      // because a rotation will be performed next).
+      std::swap(dstBufferSpec.width, dstBufferSpec.height);
+      std::swap(dstBufferSpec.cropWidth, dstBufferSpec.cropHeight);
+      // TODO (jemoreira): Aligment (To align here may cause the needed size to
+      // be bigger than the buffer, so care should be taken)
+      dstBufferSpec.strideBytes = dstBufferSpec.width * dstBufferBytesPerPixel;
+    }
+    int retval = DoScaling(srcLayerSpec, dstBufferSpec, needsVFlip);
+    needsVFlip = false;
+    if (retval) {
+      ALOGE("Got error code %d from DoScaling function", retval);
+    }
+    srcLayerSpec = dstBufferSpec;
+    dstBufferStack.pop_back();
+  }
+
+  if (needsRotation) {
+    int retval =
+        DoRotation(srcLayerSpec, dstBufferStack.back(), rotation, needsVFlip);
+    needsVFlip = false;
+    if (retval) {
+      ALOGE("Got error code %d from DoTransform function", retval);
+    }
+    srcLayerSpec = dstBufferStack.back();
+    dstBufferStack.pop_back();
+  }
+
+  if (needsAttenuation) {
+    int retval = DoAttenuation(srcLayerSpec, dstBufferStack.back(), needsVFlip);
+    needsVFlip = false;
+    if (retval) {
+      ALOGE("Got error code %d from DoBlending function", retval);
+    }
+    srcLayerSpec = dstBufferStack.back();
+    dstBufferStack.pop_back();
+  }
+
+  if (needsCopy) {
+    int retval = DoCopy(srcLayerSpec, dstBufferStack.back(), needsVFlip);
+    needsVFlip = false;
+    if (retval) {
+      ALOGE("Got error code %d from DoBlending function", retval);
+    }
+    srcLayerSpec = dstBufferStack.back();
+    dstBufferStack.pop_back();
+  }
+
+  // Blending (if needed) should always be the last operation, so that it reads
+  // and writes in the destination layer and not some temporary buffer.
+  if (needsBlending) {
+    int retval = DoBlending(srcLayerSpec, dstBufferStack.back(), needsVFlip);
+    needsVFlip = false;
+    if (retval) {
+      ALOGE("Got error code %d from DoBlending function", retval);
+    }
+    // Don't need to assign destination to source in the last one
+    dstBufferStack.pop_back();
+  }
+
+  return HWC3::Error::None;
+}
+
+namespace {
+
+// Returns a color matrix that can be used with libyuv by converting values
+// in -1 to 1 into -64 to 64 and transposing.
+std::array<std::int8_t, 16> ToLibyuvColorMatrix(
+    const std::array<float, 16>& in) {
+  std::array<std::int8_t, 16> out;
+
+  for (int r = 0; r < 4; r++) {
+    for (int c = 0; c < 4; c++) {
+      int indexIn = (4 * r) + c;
+      int indexOut = (4 * c) + r;
+
+      out[indexOut] = std::max(
+          -128, std::min(127, static_cast<int>(in[indexIn] * 64.0f + 0.5f)));
+    }
+  }
+
+  return out;
+}
+
+}  // namespace
+
+HWC3::Error GuestFrameComposer::applyColorTransformToRGBA(
+    const std::array<float, 16>& transfromMatrix,  //
+    std::uint8_t* buffer,                          //
+    std::uint32_t bufferWidth,                     //
+    std::uint32_t bufferHeight,                    //
+    std::uint32_t bufferStrideBytes) {
+  ATRACE_CALL();
+
+  const auto transformMatrixLibyuv = ToLibyuvColorMatrix(transfromMatrix);
+  libyuv::ARGBColorMatrix(buffer, bufferStrideBytes,     // in buffer params
+                          buffer, bufferStrideBytes,     // out buffer params
+                          transformMatrixLibyuv.data(),  //
+                          bufferWidth,                   //
+                          bufferHeight);
+
+  return HWC3::Error::None;
+}
+
+uint8_t* GuestFrameComposer::getRotatingScratchBuffer(std::size_t neededSize,
+                                                      std::uint32_t order) {
+  static constexpr const int kNumScratchBufferPieces = 2;
+
+  std::size_t totalNeededSize = neededSize * kNumScratchBufferPieces;
+  if (mScratchBuffer.size() < totalNeededSize) {
+    mScratchBuffer.resize(totalNeededSize);
+  }
+
+  std::size_t bufferIndex = order % kNumScratchBufferPieces;
+  std::size_t bufferOffset = bufferIndex * neededSize;
+  return &mScratchBuffer[bufferOffset];
+}
+
+uint8_t* GuestFrameComposer::getSpecialScratchBuffer(size_t neededSize) {
+  if (mSpecialScratchBuffer.size() < neededSize) {
+    mSpecialScratchBuffer.resize(neededSize);
+  }
+
+  return &mSpecialScratchBuffer[0];
+}
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/GuestFrameComposer.h b/system/hwc3/GuestFrameComposer.h
new file mode 100644
index 0000000..af46b3d
--- /dev/null
+++ b/system/hwc3/GuestFrameComposer.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWC_GUESTFRAMECOMPOSER_H
+#define ANDROID_HWC_GUESTFRAMECOMPOSER_H
+
+#include "Common.h"
+#include "Display.h"
+#include "DrmPresenter.h"
+#include "FrameComposer.h"
+#include "Gralloc.h"
+#include "Layer.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class GuestFrameComposer : public FrameComposer {
+ public:
+  GuestFrameComposer() = default;
+
+  GuestFrameComposer(const GuestFrameComposer&) = delete;
+  GuestFrameComposer& operator=(const GuestFrameComposer&) = delete;
+
+  GuestFrameComposer(GuestFrameComposer&&) = delete;
+  GuestFrameComposer& operator=(GuestFrameComposer&&) = delete;
+
+  HWC3::Error init() override;
+
+  HWC3::Error registerOnHotplugCallback(const HotplugCallback& cb) override;
+
+  HWC3::Error unregisterOnHotplugCallback() override;
+
+  HWC3::Error onDisplayCreate(Display*) override;
+
+  HWC3::Error onDisplayDestroy(Display*) override;
+
+  HWC3::Error onDisplayClientTargetSet(Display*) override;
+
+  // Determines if this composer can compose the given layers on the given
+  // display and requests changes for layers that can't not be composed.
+  HWC3::Error validateDisplay(Display* display,
+                              DisplayChanges* outChanges) override;
+
+  // Performs the actual composition of layers and presents the composed result
+  // to the display.
+  HWC3::Error presentDisplay(
+      Display* display, ::android::base::unique_fd* outDisplayFence,
+      std::unordered_map<int64_t, ::android::base::unique_fd>* outLayerFences)
+      override;
+
+  HWC3::Error onActiveConfigChange(Display* /*display*/) override;
+
+ private:
+  struct DisplayConfig {
+    int width;
+    int height;
+    int dpiX;
+    int dpiY;
+    int refreshRateHz;
+  };
+
+  HWC3::Error getDisplayConfigsFromDeviceConfig(
+      std::vector<DisplayConfig>* configs);
+
+  HWC3::Error getDisplayConfigsFromSystemProp(
+      std::vector<DisplayConfig>* configs);
+
+  // Returns true if the given layer's buffer has supported format.
+  bool canComposeLayer(Layer* layer);
+
+  // Composes the given layer into the given destination buffer.
+  HWC3::Error composeLayerInto(Layer* layer, std::uint8_t* dstBuffer,
+                               std::uint32_t dstBufferWidth,
+                               std::uint32_t dstBufferHeight,
+                               std::uint32_t dstBufferStrideBytes,
+                               std::uint32_t dstBufferBytesPerPixel);
+
+  struct DisplayInfo {
+    // Additional per display buffer for the composition result.
+    buffer_handle_t compositionResultBuffer = nullptr;
+
+    std::unique_ptr<DrmBuffer> compositionResultDrmBuffer;
+  };
+
+  std::unordered_map<int64_t, DisplayInfo> mDisplayInfos;
+
+  Gralloc mGralloc;
+
+  DrmPresenter mDrmPresenter;
+
+  // Cuttlefish on QEMU does not have a display. Disable presenting to avoid
+  // spamming logcat with DRM commit failures.
+  bool mPresentDisabled = false;
+
+  uint8_t* getRotatingScratchBuffer(std::size_t neededSize,
+                                    std::uint32_t order);
+  uint8_t* getSpecialScratchBuffer(std::size_t neededSize);
+
+  HWC3::Error applyColorTransformToRGBA(
+      const std::array<float, 16>& colorTransform,  //
+      std::uint8_t* buffer,                         //
+      std::uint32_t bufferWidth,                    //
+      std::uint32_t bufferHeight,                   //
+      std::uint32_t bufferStrideBytes);
+
+  std::vector<uint8_t> mScratchBuffer;
+  std::vector<uint8_t> mSpecialScratchBuffer;
+};
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/HostFrameComposer.cpp b/system/hwc3/HostFrameComposer.cpp
new file mode 100644
index 0000000..7b090c2
--- /dev/null
+++ b/system/hwc3/HostFrameComposer.cpp
@@ -0,0 +1,800 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#include "HostFrameComposer.h"
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <hardware/hwcomposer2.h>
+#include <poll.h>
+#include <sync/sync.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicBufferAllocator.h>
+#include <ui/GraphicBufferMapper.h>
+
+#include <optional>
+#include <tuple>
+
+#include "../egl/goldfish_sync.h"
+#include "Display.h"
+#include "HostUtils.h"
+#include "virtgpu_drm.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+namespace {
+
+hwc_rect AsHwcRect(const common::Rect& rect) {
+  hwc_rect out;
+  out.left = rect.left;
+  out.top = rect.top;
+  out.right = rect.right;
+  out.bottom = rect.bottom;
+  return out;
+}
+
+hwc_frect AsHwcFrect(const common::FRect& rect) {
+  hwc_frect out;
+  out.left = rect.left;
+  out.top = rect.top;
+  out.right = rect.right;
+  out.bottom = rect.bottom;
+  return out;
+}
+
+hwc_color AsHwcColor(const Color& color) {
+  hwc_color out;
+  out.r = color.r;
+  out.g = color.g;
+  out.b = color.b;
+  out.a = color.a;
+  return out;
+}
+
+hwc_transform_t AsHwcTransform(const common::Transform& transform) {
+  switch (transform) {
+    case common::Transform::NONE:
+      return static_cast<hwc_transform_t>(0);
+    case common::Transform::FLIP_H:
+      return HWC_TRANSFORM_FLIP_H;
+    case common::Transform::FLIP_V:
+      return HWC_TRANSFORM_FLIP_V;
+    case common::Transform::ROT_90:
+      return HWC_TRANSFORM_ROT_90;
+    case common::Transform::ROT_180:
+      return HWC_TRANSFORM_ROT_180;
+    case common::Transform::ROT_270:
+      return HWC_TRANSFORM_ROT_270;
+  }
+}
+
+static bool isMinigbmFromProperty() {
+  static constexpr const auto kGrallocProp = "ro.hardware.gralloc";
+
+  const auto grallocProp = ::android::base::GetProperty(kGrallocProp, "");
+  DEBUG_LOG("%s: prop value is: %s", __FUNCTION__, grallocProp.c_str());
+
+  if (grallocProp == "minigbm") {
+    DEBUG_LOG("%s: Using minigbm, in minigbm mode.\n", __FUNCTION__);
+    return true;
+  } else {
+    DEBUG_LOG("%s: Is not using minigbm, in goldfish mode.\n", __FUNCTION__);
+    return false;
+  }
+}
+
+static bool useAngleFromProperty() {
+  static constexpr const auto kEglProp = "ro.hardware.egl";
+
+  const auto eglProp = ::android::base::GetProperty(kEglProp, "");
+  DEBUG_LOG("%s: prop value is: %s", __FUNCTION__, eglProp.c_str());
+
+  if (eglProp == "angle") {
+    DEBUG_LOG("%s: Using ANGLE.\n", __FUNCTION__);
+    return true;
+  } else {
+    DEBUG_LOG("%s: Not using ANGLE.\n", __FUNCTION__);
+    return false;
+  }
+}
+
+typedef struct compose_layer {
+  uint32_t cbHandle;
+  hwc2_composition_t composeMode;
+  hwc_rect_t displayFrame;
+  hwc_frect_t crop;
+  int32_t blendMode;
+  float alpha;
+  hwc_color_t color;
+  hwc_transform_t transform;
+} ComposeLayer;
+
+typedef struct compose_device {
+  uint32_t version;
+  uint32_t targetHandle;
+  uint32_t numLayers;
+  struct compose_layer layer[0];
+} ComposeDevice;
+
+typedef struct compose_device_v2 {
+  uint32_t version;
+  uint32_t displayId;
+  uint32_t targetHandle;
+  uint32_t numLayers;
+  struct compose_layer layer[0];
+} ComposeDevice_v2;
+
+class ComposeMsg {
+ public:
+  ComposeMsg(uint32_t layerCnt = 0)
+      : mData(sizeof(ComposeDevice) + layerCnt * sizeof(ComposeLayer)) {
+    mComposeDevice = reinterpret_cast<ComposeDevice*>(mData.data());
+    mLayerCnt = layerCnt;
+  }
+
+  ComposeDevice* get() { return mComposeDevice; }
+
+  uint32_t getLayerCnt() { return mLayerCnt; }
+
+ private:
+  std::vector<uint8_t> mData;
+  uint32_t mLayerCnt;
+  ComposeDevice* mComposeDevice;
+};
+
+class ComposeMsg_v2 {
+ public:
+  ComposeMsg_v2(uint32_t layerCnt = 0)
+      : mData(sizeof(ComposeDevice_v2) + layerCnt * sizeof(ComposeLayer)) {
+    mComposeDevice = reinterpret_cast<ComposeDevice_v2*>(mData.data());
+    mLayerCnt = layerCnt;
+  }
+
+  ComposeDevice_v2* get() { return mComposeDevice; }
+
+  uint32_t getLayerCnt() { return mLayerCnt; }
+
+ private:
+  std::vector<uint8_t> mData;
+  uint32_t mLayerCnt;
+  ComposeDevice_v2* mComposeDevice;
+};
+
+const native_handle_t* AllocateDisplayColorBuffer(int width, int height) {
+  const uint32_t layerCount = 1;
+  const uint64_t graphicBufferId = 0;  // not used
+  buffer_handle_t handle;
+  uint32_t stride;
+
+  if (::android::GraphicBufferAllocator::get().allocate(
+          width, height, ::android::PIXEL_FORMAT_RGBA_8888, layerCount,
+          ::android::GraphicBuffer::USAGE_HW_COMPOSER |
+              ::android::GraphicBuffer::USAGE_HW_RENDER,
+          &handle, &stride, graphicBufferId, "EmuHWC2") == ::android::OK) {
+    return static_cast<const native_handle_t*>(handle);
+  } else {
+    return nullptr;
+  }
+}
+
+void FreeDisplayColorBuffer(const native_handle_t* h) {
+  ::android::GraphicBufferAllocator::get().free(h);
+}
+
+}  // namespace
+
+HWC3::Error HostFrameComposer::init() {
+  mIsMinigbm = isMinigbmFromProperty();
+  mUseAngle = useAngleFromProperty();
+
+  if (mIsMinigbm) {
+    mDrmPresenter.emplace();
+
+    HWC3::Error error = mDrmPresenter->init();
+    if (error != HWC3::Error::None) {
+      ALOGE("%s: failed to initialize DrmPresenter", __FUNCTION__);
+      return error;
+    }
+  } else {
+    mSyncDeviceFd = goldfish_sync_open();
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error HostFrameComposer::registerOnHotplugCallback(
+    const HotplugCallback& cb) {
+  if (mDrmPresenter) {
+    mDrmPresenter->registerOnHotplugCallback(cb);
+  }
+  return HWC3::Error::None;
+}
+
+HWC3::Error HostFrameComposer::unregisterOnHotplugCallback() {
+  if (mDrmPresenter) {
+    mDrmPresenter->unregisterOnHotplugCallback();
+  }
+  return HWC3::Error::None;
+}
+
+HWC3::Error HostFrameComposer::createHostComposerDisplayInfo(
+    Display* display, uint32_t hostDisplayId) {
+  HWC3::Error error = HWC3::Error::None;
+
+  int64_t displayId = display->getId();
+  int32_t displayConfigId;
+  int32_t displayWidth;
+  int32_t displayHeight;
+
+  error = display->getActiveConfig(&displayConfigId);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRIu64 " has no active config", __FUNCTION__,
+          displayId);
+    return error;
+  }
+
+  error = display->getDisplayAttribute(displayConfigId, DisplayAttribute::WIDTH,
+                                       &displayWidth);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRIu64 " failed to get width", __FUNCTION__,
+          displayId);
+    return error;
+  }
+
+  error = display->getDisplayAttribute(
+      displayConfigId, DisplayAttribute::HEIGHT, &displayHeight);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRIu64 " failed to get height", __FUNCTION__,
+          displayId);
+    return error;
+  }
+
+  HostComposerDisplayInfo& displayInfo = mDisplayInfos[displayId];
+
+  displayInfo.hostDisplayId = hostDisplayId;
+
+  if (displayInfo.compositionResultBuffer) {
+    FreeDisplayColorBuffer(displayInfo.compositionResultBuffer);
+  }
+  displayInfo.compositionResultBuffer =
+      AllocateDisplayColorBuffer(displayWidth, displayHeight);
+  if (displayInfo.compositionResultBuffer == nullptr) {
+    ALOGE("%s: display:%" PRIu64 " failed to create target buffer",
+          __FUNCTION__, displayId);
+    return HWC3::Error::NoResources;
+  }
+
+  if (mDrmPresenter) {
+    auto [drmBufferCreateError, drmBuffer] =
+        mDrmPresenter->create(displayInfo.compositionResultBuffer);
+    if (drmBufferCreateError != HWC3::Error::None) {
+      ALOGE("%s: display:%" PRIu64 " failed to create target drm buffer",
+            __FUNCTION__, displayId);
+      return HWC3::Error::NoResources;
+    }
+    displayInfo.compositionResultDrmBuffer = std::move(drmBuffer);
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error HostFrameComposer::onDisplayCreate(Display* display) {
+  HWC3::Error error = HWC3::Error::None;
+
+  int64_t displayId = display->getId();
+  int32_t displayConfigId;
+  int32_t displayWidth;
+  int32_t displayHeight;
+  int32_t displayDpiX;
+
+  error = display->getActiveConfig(&displayConfigId);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRIu64 " has no active config", __FUNCTION__,
+          displayId);
+    return error;
+  }
+
+  error = display->getDisplayAttribute(displayConfigId, DisplayAttribute::WIDTH,
+                                       &displayWidth);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRIu64 " failed to get width", __FUNCTION__,
+          displayId);
+    return error;
+  }
+
+  error = display->getDisplayAttribute(
+      displayConfigId, DisplayAttribute::HEIGHT, &displayHeight);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRIu64 " failed to get height", __FUNCTION__,
+          displayId);
+    return error;
+  }
+
+  error = display->getDisplayAttribute(displayConfigId, DisplayAttribute::DPI_X,
+                                       &displayDpiX);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s: display:%" PRIu64 " failed to get height", __FUNCTION__,
+          displayId);
+    return error;
+  }
+
+  uint32_t hostDisplayId = 0;
+
+  DEFINE_AND_VALIDATE_HOST_CONNECTION
+  if (displayId == 0) {
+    // Primary display:
+    hostCon->lock();
+    if (rcEnc->rcCreateDisplayById(rcEnc, displayId)) {
+      ALOGE("%s host failed to create display %" PRIu64, __func__, displayId);
+      hostCon->unlock();
+      return HWC3::Error::NoResources;
+    }
+    if (rcEnc->rcSetDisplayPoseDpi(rcEnc, displayId, -1, -1, displayWidth,
+                                   displayHeight, displayDpiX / 1000)) {
+      ALOGE("%s host failed to set display %" PRIu64, __func__, displayId);
+      hostCon->unlock();
+      return HWC3::Error::NoResources;
+    }
+    hostCon->unlock();
+  } else {
+    // Secondary display:
+    static constexpr const uint32_t kHostDisplayIdStart = 6;
+
+    uint32_t expectedHostDisplayId = kHostDisplayIdStart + displayId - 1;
+    uint32_t actualHostDisplayId = 0;
+
+    hostCon->lock();
+    rcEnc->rcDestroyDisplay(rcEnc, expectedHostDisplayId);
+    rcEnc->rcCreateDisplay(rcEnc, &actualHostDisplayId);
+    rcEnc->rcSetDisplayPose(rcEnc, actualHostDisplayId, -1, -1, displayWidth,
+                            displayHeight);
+    hostCon->unlock();
+
+    if (actualHostDisplayId != expectedHostDisplayId) {
+      ALOGE(
+          "Something wrong with host displayId allocation, expected %d "
+          "but received %d",
+          expectedHostDisplayId, actualHostDisplayId);
+    }
+
+    hostDisplayId = actualHostDisplayId;
+  }
+
+  error = createHostComposerDisplayInfo(display, hostDisplayId);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s failed to initialize host info for display:%" PRIu64,
+          __FUNCTION__, displayId);
+    return error;
+  }
+
+  std::optional<std::vector<uint8_t>> edid;
+  if (mDrmPresenter) {
+    edid = mDrmPresenter->getEdid(displayId);
+    if (edid) {
+      display->setEdid(*edid);
+    }
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error HostFrameComposer::onDisplayDestroy(Display* display) {
+  int64_t displayId = display->getId();
+
+  auto it = mDisplayInfos.find(displayId);
+  if (it == mDisplayInfos.end()) {
+    ALOGE("%s: display:%" PRIu64 " missing display buffers?", __FUNCTION__,
+          displayId);
+    return HWC3::Error::BadDisplay;
+  }
+
+  HostComposerDisplayInfo& displayInfo = mDisplayInfos[displayId];
+
+  if (displayId != 0) {
+    DEFINE_AND_VALIDATE_HOST_CONNECTION
+    hostCon->lock();
+    rcEnc->rcDestroyDisplay(rcEnc, displayInfo.hostDisplayId);
+    hostCon->unlock();
+  }
+
+  FreeDisplayColorBuffer(displayInfo.compositionResultBuffer);
+
+  mDisplayInfos.erase(it);
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error HostFrameComposer::onDisplayClientTargetSet(Display* display) {
+  int64_t displayId = display->getId();
+
+  auto it = mDisplayInfos.find(displayId);
+  if (it == mDisplayInfos.end()) {
+    ALOGE("%s: display:%" PRIu64 " missing display buffers?", __FUNCTION__,
+          displayId);
+    return HWC3::Error::BadDisplay;
+  }
+
+  HostComposerDisplayInfo& displayInfo = mDisplayInfos[displayId];
+
+  if (mIsMinigbm) {
+    FencedBuffer& clientTargetFencedBuffer = display->getClientTarget();
+
+    auto [drmBufferCreateError, drmBuffer] =
+        mDrmPresenter->create(clientTargetFencedBuffer.getBuffer());
+    if (drmBufferCreateError != HWC3::Error::None) {
+      ALOGE("%s: display:%" PRIu64 " failed to create client target drm buffer",
+            __FUNCTION__, displayId);
+      return HWC3::Error::NoResources;
+    }
+    displayInfo.clientTargetDrmBuffer = std::move(drmBuffer);
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error HostFrameComposer::validateDisplay(Display* display,
+                                               DisplayChanges* outChanges) {
+  const auto& displayId = display->getId();
+
+  DEFINE_AND_VALIDATE_HOST_CONNECTION
+  hostCon->lock();
+  bool hostCompositionV1 = rcEnc->hasHostCompositionV1();
+  bool hostCompositionV2 = rcEnc->hasHostCompositionV2();
+  hostCon->unlock();
+
+  const std::vector<Layer*> layers = display->getOrderedLayers();
+  for (const auto& layer : layers) {
+    switch (layer->getCompositionType()) {
+      case Composition::INVALID:
+        // Log error for unused layers, layer leak?
+        ALOGE("%s layer:%" PRIu64 " CompositionType not set", __FUNCTION__,
+              layer->getId());
+        break;
+      case Composition::DISPLAY_DECORATION:
+        return HWC3::Error::Unsupported;
+      default:
+        break;
+    }
+  }
+
+  // If one layer requires a fall back to the client composition type, all
+  // layers will fall back to the client composition type.
+  bool fallBackToClient = (!hostCompositionV1 && !hostCompositionV2) ||
+                          display->hasColorTransform();
+
+  if (!fallBackToClient) {
+    for (const auto& layer : layers) {
+      const auto& layerId = layer->getId();
+      const auto& layerCompositionType = layer->getCompositionType();
+
+      std::optional<Composition> layerFallBackTo = std::nullopt;
+      switch (layerCompositionType) {
+        case Composition::CLIENT:
+        case Composition::SIDEBAND:
+          ALOGI("%s: layer %" PRIu32 " CompositionType %d, fallback to client",
+                __FUNCTION__, static_cast<uint32_t>(layer->getId()),
+                layerCompositionType);
+          layerFallBackTo = Composition::CLIENT;
+          break;
+        case Composition::CURSOR:
+          ALOGI("%s: layer %" PRIu32 " CompositionType %d, fallback to device",
+                __FUNCTION__, static_cast<uint32_t>(layer->getId()),
+                layerCompositionType);
+          layerFallBackTo = Composition::DEVICE;
+          break;
+        case Composition::INVALID:
+        case Composition::DEVICE:
+        case Composition::SOLID_COLOR:
+          layerFallBackTo = std::nullopt;
+          break;
+        default:
+          ALOGE("%s: layer %" PRIu32 " has an unknown composition type: %d",
+                __FUNCTION__, static_cast<uint32_t>(layer->getId()),
+                layerCompositionType);
+      }
+      if (layerFallBackTo == Composition::CLIENT) {
+        fallBackToClient = true;
+      }
+      if (layerFallBackTo.has_value()) {
+        outChanges->addLayerCompositionChange(displayId, layerId,
+                                              *layerFallBackTo);
+      }
+    }
+  }
+
+  if (fallBackToClient) {
+    outChanges->clearLayerCompositionChanges();
+    for (auto& layer : layers) {
+      const auto& layerId = layer->getId();
+      if (layer->getCompositionType() == Composition::INVALID) {
+        continue;
+      }
+      if (layer->getCompositionType() != Composition::CLIENT) {
+        outChanges->addLayerCompositionChange(displayId, layerId,
+                                              Composition::CLIENT);
+      }
+    }
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error HostFrameComposer::presentDisplay(
+    Display* display, ::android::base::unique_fd* outDisplayFence,
+    std::unordered_map<int64_t, ::android::base::unique_fd>* outLayerFences) {
+  auto displayId = display->getId();
+  auto displayInfoIt = mDisplayInfos.find(displayId);
+  if (displayInfoIt == mDisplayInfos.end()) {
+    ALOGE("%s: failed to find display buffers for display:%" PRIu64,
+          __FUNCTION__, displayId);
+    return HWC3::Error::BadDisplay;
+  }
+
+  HostComposerDisplayInfo& displayInfo = displayInfoIt->second;
+
+  HostConnection* hostCon;
+  ExtendedRCEncoderContext* rcEnc;
+  HWC3::Error error = getAndValidateHostConnection(&hostCon, &rcEnc);
+  if (error != HWC3::Error::None) {
+    return error;
+  }
+  hostCon->lock();
+  bool hostCompositionV1 = rcEnc->hasHostCompositionV1();
+  bool hostCompositionV2 = rcEnc->hasHostCompositionV2();
+  hostCon->unlock();
+
+  // Ff we supports v2, then discard v1
+  if (hostCompositionV2) {
+    hostCompositionV1 = false;
+  }
+
+  const std::vector<Layer*> layers = display->getOrderedLayers();
+  if (hostCompositionV2 || hostCompositionV1) {
+    uint32_t numLayer = 0;
+    for (auto layer : layers) {
+      if (layer->getCompositionType() == Composition::DEVICE ||
+          layer->getCompositionType() == Composition::SOLID_COLOR) {
+        numLayer++;
+      }
+    }
+
+    DEBUG_LOG("%s: presenting display:%" PRIu64 " with %d layers", __FUNCTION__,
+              displayId, static_cast<int>(layers.size()));
+
+    if (numLayer == 0) {
+      ALOGW(
+          "%s display has no layers to compose, flushing client target buffer.",
+          __FUNCTION__);
+
+      FencedBuffer& displayClientTarget = display->getClientTarget();
+      if (displayClientTarget.getBuffer() != nullptr) {
+        ::android::base::unique_fd fence = displayClientTarget.getFence();
+        if (mIsMinigbm) {
+          auto [_, flushCompleteFence] = mDrmPresenter->flushToDisplay(
+              displayId, *displayInfo.clientTargetDrmBuffer, fence);
+
+          *outDisplayFence = std::move(flushCompleteFence);
+        } else {
+          post(hostCon, rcEnc, displayClientTarget.getBuffer());
+          *outDisplayFence = std::move(fence);
+        }
+      }
+      return HWC3::Error::None;
+    }
+
+    std::unique_ptr<ComposeMsg> composeMsg;
+    std::unique_ptr<ComposeMsg_v2> composeMsgV2;
+
+    if (hostCompositionV1) {
+      composeMsg.reset(new ComposeMsg(numLayer));
+    } else {
+      composeMsgV2.reset(new ComposeMsg_v2(numLayer));
+    }
+
+    // Handle the composition
+    ComposeDevice* p;
+    ComposeDevice_v2* p2;
+    ComposeLayer* l;
+
+    if (hostCompositionV1) {
+      p = composeMsg->get();
+      l = p->layer;
+    } else {
+      p2 = composeMsgV2->get();
+      l = p2->layer;
+    }
+
+    std::vector<int64_t> releaseLayerIds;
+    for (auto layer : layers) {
+      // TODO: use local var composisitonType to store getCompositionType()
+      if (layer->getCompositionType() != Composition::DEVICE &&
+          layer->getCompositionType() != Composition::SOLID_COLOR) {
+        ALOGE("%s: Unsupported composition types %d layer %u", __FUNCTION__,
+              layer->getCompositionType(), (uint32_t)layer->getId());
+        continue;
+      }
+      // send layer composition command to host
+      if (layer->getCompositionType() == Composition::DEVICE) {
+        releaseLayerIds.emplace_back(layer->getId());
+
+        ::android::base::unique_fd fence = layer->getBuffer().getFence();
+        if (fence.ok()) {
+          int err = sync_wait(fence.get(), 3000);
+          if (err < 0 && errno == ETIME) {
+            ALOGE("%s waited on fence %d for 3000 ms", __FUNCTION__,
+                  fence.get());
+          }
+        } else {
+          ALOGV("%s: acquire fence not set for layer %u", __FUNCTION__,
+                (uint32_t)layer->getId());
+        }
+        const native_handle_t* cb = layer->getBuffer().getBuffer();
+        if (cb != nullptr) {
+          l->cbHandle = hostCon->grallocHelper()->getHostHandle(cb);
+        } else {
+          ALOGE("%s null buffer for layer %d", __FUNCTION__,
+                (uint32_t)layer->getId());
+        }
+      } else {
+        // solidcolor has no buffer
+        l->cbHandle = 0;
+      }
+      l->composeMode = (hwc2_composition_t)layer->getCompositionType();
+      l->displayFrame = AsHwcRect(layer->getDisplayFrame());
+      l->crop = AsHwcFrect(layer->getSourceCrop());
+      l->blendMode = static_cast<int32_t>(layer->getBlendMode());
+      l->alpha = layer->getPlaneAlpha();
+      l->color = AsHwcColor(layer->getColor());
+      l->transform = AsHwcTransform(layer->getTransform());
+      ALOGV(
+          "   cb %d blendmode %d alpha %f %d %d %d %d z %d"
+          " composeMode %d, transform %d",
+          l->cbHandle, l->blendMode, l->alpha, l->displayFrame.left,
+          l->displayFrame.top, l->displayFrame.right, l->displayFrame.bottom,
+          layer->getZOrder(), l->composeMode, l->transform);
+      l++;
+    }
+    if (hostCompositionV1) {
+      p->version = 1;
+      p->targetHandle = hostCon->grallocHelper()->getHostHandle(
+          displayInfo.compositionResultBuffer);
+      p->numLayers = numLayer;
+    } else {
+      p2->version = 2;
+      p2->displayId = displayInfo.hostDisplayId;
+      p2->targetHandle = hostCon->grallocHelper()->getHostHandle(
+          displayInfo.compositionResultBuffer);
+      p2->numLayers = numLayer;
+    }
+
+    void* buffer;
+    uint32_t bufferSize;
+    if (hostCompositionV1) {
+      buffer = (void*)p;
+      bufferSize = sizeof(ComposeDevice) + numLayer * sizeof(ComposeLayer);
+    } else {
+      bufferSize = sizeof(ComposeDevice_v2) + numLayer * sizeof(ComposeLayer);
+      buffer = (void*)p2;
+    }
+
+    ::android::base::unique_fd retire_fd;
+    hostCon->lock();
+    if (rcEnc->hasAsyncFrameCommands()) {
+      if (mIsMinigbm) {
+        rcEnc->rcComposeAsyncWithoutPost(rcEnc, bufferSize, buffer);
+      } else {
+        rcEnc->rcComposeAsync(rcEnc, bufferSize, buffer);
+      }
+    } else {
+      if (mIsMinigbm) {
+        rcEnc->rcComposeWithoutPost(rcEnc, bufferSize, buffer);
+      } else {
+        rcEnc->rcCompose(rcEnc, bufferSize, buffer);
+      }
+    }
+    hostCon->unlock();
+
+    // Send a retire fence and use it as the release fence for all layers,
+    // since media expects it
+    EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_ANDROID,
+                        EGL_NO_NATIVE_FENCE_FD_ANDROID};
+
+    uint64_t sync_handle, thread_handle;
+
+    // We don't use rc command to sync if we are using ANGLE on the guest with
+    // virtio-gpu.
+    bool useRcCommandToSync = !(mUseAngle && mIsMinigbm);
+
+    if (useRcCommandToSync) {
+      hostCon->lock();
+      rcEnc->rcCreateSyncKHR(
+          rcEnc, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs, 2 * sizeof(EGLint),
+          true /* destroy when signaled */, &sync_handle, &thread_handle);
+      hostCon->unlock();
+    }
+
+    if (mIsMinigbm) {
+      auto [_, fence] = mDrmPresenter->flushToDisplay(
+          displayId, *displayInfo.compositionResultDrmBuffer, -1);
+      retire_fd = std::move(fence);
+    } else {
+      int fd;
+      goldfish_sync_queue_work(mSyncDeviceFd, sync_handle, thread_handle, &fd);
+      retire_fd = ::android::base::unique_fd(fd);
+    }
+
+    for (int64_t layerId : releaseLayerIds) {
+      (*outLayerFences)[layerId] =
+          ::android::base::unique_fd(dup(retire_fd.get()));
+    }
+    *outDisplayFence = ::android::base::unique_fd(dup(retire_fd.get()));
+
+    if (useRcCommandToSync) {
+      hostCon->lock();
+      if (rcEnc->hasAsyncFrameCommands()) {
+        rcEnc->rcDestroySyncKHRAsync(rcEnc, sync_handle);
+      } else {
+        rcEnc->rcDestroySyncKHR(rcEnc, sync_handle);
+      }
+      hostCon->unlock();
+    }
+
+  } else {
+    // we set all layers Composition::CLIENT, so do nothing.
+    FencedBuffer& displayClientTarget = display->getClientTarget();
+    ::android::base::unique_fd displayClientTargetFence =
+        displayClientTarget.getFence();
+    if (mIsMinigbm) {
+      auto [_, flushFence] = mDrmPresenter->flushToDisplay(
+          displayId, *displayInfo.compositionResultDrmBuffer,
+          displayClientTargetFence);
+      *outDisplayFence = std::move(flushFence);
+    } else {
+      post(hostCon, rcEnc, displayClientTarget.getBuffer());
+      *outDisplayFence = std::move(displayClientTargetFence);
+    }
+    ALOGV("%s fallback to post, returns outRetireFence %d", __FUNCTION__,
+          outDisplayFence->get());
+  }
+  return HWC3::Error::None;
+}
+
+void HostFrameComposer::post(HostConnection* hostCon,
+                             ExtendedRCEncoderContext* rcEnc,
+                             buffer_handle_t h) {
+  assert(cb && "native_handle_t::from(h) failed");
+
+  hostCon->lock();
+  rcEnc->rcFBPost(rcEnc, hostCon->grallocHelper()->getHostHandle(h));
+  hostCon->flush();
+  hostCon->unlock();
+}
+
+HWC3::Error HostFrameComposer::onActiveConfigChange(Display* display) {
+  DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, display->getId());
+  HWC3::Error error = createHostComposerDisplayInfo(display, display->getId());
+  if (error != HWC3::Error::None) {
+    ALOGE("%s failed to update host info for display:%" PRIu64, __FUNCTION__,
+          display->getId());
+    return error;
+  }
+  return HWC3::Error::None;
+}
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/HostFrameComposer.h b/system/hwc3/HostFrameComposer.h
new file mode 100644
index 0000000..e99e6f2
--- /dev/null
+++ b/system/hwc3/HostFrameComposer.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWC_HOSTFRAMECOMPOSER_H
+#define ANDROID_HWC_HOSTFRAMECOMPOSER_H
+
+#include <android-base/unique_fd.h>
+
+#include <optional>
+#include <tuple>
+
+#include "Common.h"
+#include "DrmPresenter.h"
+#include "FrameComposer.h"
+#include "HostConnection.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class HostFrameComposer : public FrameComposer {
+ public:
+  HostFrameComposer() = default;
+
+  HostFrameComposer(const HostFrameComposer&) = delete;
+  HostFrameComposer& operator=(const HostFrameComposer&) = delete;
+
+  HostFrameComposer(HostFrameComposer&&) = delete;
+  HostFrameComposer& operator=(HostFrameComposer&&) = delete;
+
+  HWC3::Error init() override;
+
+  HWC3::Error registerOnHotplugCallback(const HotplugCallback& cb) override;
+
+  HWC3::Error unregisterOnHotplugCallback() override;
+
+  HWC3::Error onDisplayCreate(Display* display) override;
+
+  HWC3::Error onDisplayDestroy(Display* display) override;
+
+  HWC3::Error onDisplayClientTargetSet(Display* display) override;
+
+  // Determines if this composer can compose the given layers on the given
+  // display and requests changes for layers that can't not be composed.
+  HWC3::Error validateDisplay(Display* display,
+                              DisplayChanges* outChanges) override;
+
+  // Performs the actual composition of layers and presents the composed result
+  // to the display.
+  HWC3::Error presentDisplay(
+      Display* display, ::android::base::unique_fd* outDisplayFence,
+      std::unordered_map<int64_t, ::android::base::unique_fd>* outLayerFences)
+      override;
+
+  HWC3::Error onActiveConfigChange(Display* display) override;
+
+ private:
+  HWC3::Error createHostComposerDisplayInfo(Display* display,
+                                            uint32_t hostDisplayId);
+
+  void post(HostConnection* hostCon, ExtendedRCEncoderContext* rcEnc,
+            buffer_handle_t h);
+
+  bool mIsMinigbm = false;
+
+  bool mUseAngle = false;
+
+  int mSyncDeviceFd = -1;
+
+  struct HostComposerDisplayInfo {
+    uint32_t hostDisplayId = 0;
+
+    // Additional per display buffer for the composition result.
+    const native_handle_t* compositionResultBuffer = nullptr;
+
+    // Drm info for the additional composition result buffer.
+    std::unique_ptr<DrmBuffer> compositionResultDrmBuffer;
+
+    // Drm info for the displays client target buffer.
+    std::unique_ptr<DrmBuffer> clientTargetDrmBuffer;
+  };
+
+  std::unordered_map<int64_t, HostComposerDisplayInfo> mDisplayInfos;
+
+  std::optional<DrmPresenter> mDrmPresenter;
+};
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/HostUtils.cpp b/system/hwc3/HostUtils.cpp
new file mode 100644
index 0000000..ea0e807
--- /dev/null
+++ b/system/hwc3/HostUtils.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#include "HostUtils.h"
+
+#include <memory>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+HostConnection* createOrGetHostConnection() {
+  static std::unique_ptr<HostConnection> sHostCon;
+
+  if (!sHostCon) {
+    sHostCon = HostConnection::createUnique();
+  }
+  return sHostCon.get();
+}
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
\ No newline at end of file
diff --git a/system/hwc3/HostUtils.h b/system/hwc3/HostUtils.h
new file mode 100644
index 0000000..ef02389
--- /dev/null
+++ b/system/hwc3/HostUtils.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWC_HOSTUTILS_H
+#define ANDROID_HWC_HOSTUTILS_H
+
+#include "Common.h"
+#include "HostConnection.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+HostConnection* createOrGetHostConnection();
+
+inline HWC3::Error getAndValidateHostConnection(
+    HostConnection** ppHostCon, ExtendedRCEncoderContext** ppRcEnc) {
+  *ppHostCon = nullptr;
+  *ppRcEnc = nullptr;
+
+  HostConnection* hostCon = createOrGetHostConnection();
+  if (!hostCon) {
+    ALOGE("%s: Failed to get host connection\n", __FUNCTION__);
+    return HWC3::Error::NoResources;
+  }
+  ExtendedRCEncoderContext* rcEnc = hostCon->rcEncoder();
+  if (!rcEnc) {
+    ALOGE("%s: Failed to get renderControl encoder context\n", __FUNCTION__);
+    return HWC3::Error::NoResources;
+  }
+
+  *ppHostCon = hostCon;
+  *ppRcEnc = rcEnc;
+  return HWC3::Error::None;
+}
+
+#define DEFINE_AND_VALIDATE_HOST_CONNECTION                           \
+  HostConnection* hostCon;                                            \
+  ExtendedRCEncoderContext* rcEnc;                                    \
+  {                                                                   \
+    HWC3::Error res = getAndValidateHostConnection(&hostCon, &rcEnc); \
+    if (res != HWC3::Error::None) {                                   \
+      return res;                                                     \
+    }                                                                 \
+  }
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/Layer.cpp b/system/hwc3/Layer.cpp
new file mode 100644
index 0000000..f58d5a5
--- /dev/null
+++ b/system/hwc3/Layer.cpp
@@ -0,0 +1,339 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#include "Layer.h"
+
+#include <android-base/unique_fd.h>
+#include <sync/sync.h>
+
+#include <atomic>
+#include <cmath>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+namespace {
+
+std::atomic<int64_t> sNextId{1};
+
+}  // namespace
+
+Layer::Layer() : mId(sNextId++) {}
+
+HWC3::Error Layer::setCursorPosition(const common::Point& position) {
+  DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+  if (mCompositionType != Composition::CURSOR) {
+    ALOGE("%s: CompositionType not Cursor type", __FUNCTION__);
+    return HWC3::Error::BadLayer;
+  }
+
+  mCursorPosition = position;
+  return HWC3::Error::None;
+}
+
+common::Point Layer::getCursorPosition() const {
+  DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+  return mCursorPosition;
+}
+
+HWC3::Error Layer::setBuffer(buffer_handle_t buffer,
+                             const ndk::ScopedFileDescriptor& fence) {
+  DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+  if (buffer == nullptr) {
+    ALOGE("%s: missing handle", __FUNCTION__);
+    return HWC3::Error::BadParameter;
+  }
+
+  mBuffer.set(buffer, fence);
+  return HWC3::Error::None;
+}
+
+FencedBuffer& Layer::getBuffer() {
+  DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+  return mBuffer;
+}
+
+buffer_handle_t Layer::waitAndGetBuffer() {
+  DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+  ::android::base::unique_fd fence = mBuffer.getFence();
+  if (fence.ok()) {
+    int err = sync_wait(fence.get(), 3000);
+    if (err < 0 && errno == ETIME) {
+      ALOGE("%s waited on fence %" PRId32 " for 3000 ms", __FUNCTION__,
+            fence.get());
+    }
+  }
+
+  return mBuffer.getBuffer();
+}
+
+HWC3::Error Layer::setSurfaceDamage(
+    const std::vector<std::optional<common::Rect>>& /*damage*/) {
+  DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error Layer::setBlendMode(common::BlendMode blendMode) {
+  const auto blendModeString = toString(blendMode);
+  DEBUG_LOG("%s: layer:%" PRId64 " blend mode:%s", __FUNCTION__, mId,
+            blendModeString.c_str());
+
+  mBlendMode = blendMode;
+  return HWC3::Error::None;
+}
+
+common::BlendMode Layer::getBlendMode() const {
+  const auto blendMode = mBlendMode;
+  const auto blendModeString = toString(blendMode);
+  DEBUG_LOG("%s: layer:%" PRId64 " blend mode:%s", __FUNCTION__, mId,
+            blendModeString.c_str());
+
+  return blendMode;
+}
+
+HWC3::Error Layer::setColor(Color color) {
+  DEBUG_LOG("%s: layer:%" PRId64
+            " color-r:%d color-g:%d color-b:%d color-a:%d)",
+            __FUNCTION__, mId, color.r, color.g, color.b, color.a);
+
+  mColor = color;
+  return HWC3::Error::None;
+}
+
+Color Layer::getColor() const {
+  auto color = mColor;
+  DEBUG_LOG("%s: layer:%" PRId64
+            " color-r:%d color-g:%d color-b:%d color-a:%d)",
+            __FUNCTION__, mId, color.r, color.g, color.b, color.a);
+
+  return color;
+}
+
+HWC3::Error Layer::setCompositionType(Composition compositionType) {
+  const auto compositionTypeString = toString(compositionType);
+  DEBUG_LOG("%s: layer:%" PRId64 " composition type:%s", __FUNCTION__, mId,
+            compositionTypeString.c_str());
+
+  mCompositionType = compositionType;
+  return HWC3::Error::None;
+}
+
+Composition Layer::getCompositionType() const {
+  const auto compositionTypeString = toString(mCompositionType);
+  DEBUG_LOG("%s: layer:%" PRId64 " composition type:%s", __FUNCTION__, mId,
+            compositionTypeString.c_str());
+
+  return mCompositionType;
+}
+
+HWC3::Error Layer::setDataspace(common::Dataspace dataspace) {
+  const auto dataspaceString = toString(dataspace);
+  DEBUG_LOG("%s: layer:%" PRId64 " dataspace:%s", __FUNCTION__, mId,
+            dataspaceString.c_str());
+
+  mDataspace = dataspace;
+  return HWC3::Error::None;
+}
+
+common::Dataspace Layer::getDataspace() const {
+  const auto dataspaceString = toString(mDataspace);
+  DEBUG_LOG("%s: layer:%" PRId64 " dataspace:%s", __FUNCTION__, mId,
+            dataspaceString.c_str());
+
+  return mDataspace;
+}
+
+HWC3::Error Layer::setDisplayFrame(common::Rect frame) {
+  DEBUG_LOG("%s: layer:%" PRId64
+            " display frame rect-left:%d rect-top:%d rect-right:%d rect-bot:%d",
+            __FUNCTION__, mId, frame.left, frame.top, frame.right,
+            frame.bottom);
+
+  mDisplayFrame = frame;
+  return HWC3::Error::None;
+}
+
+common::Rect Layer::getDisplayFrame() const {
+  auto frame = mDisplayFrame;
+  DEBUG_LOG("%s: layer:%" PRId64
+            " display frame rect-left:%d rect-top:%d rect-right:%d rect-bot:%d",
+            __FUNCTION__, mId, frame.left, frame.top, frame.right,
+            frame.bottom);
+
+  return frame;
+}
+
+HWC3::Error Layer::setPlaneAlpha(float alpha) {
+  DEBUG_LOG("%s: layer:%" PRId64 "alpha:%f", __FUNCTION__, mId, alpha);
+
+  mPlaneAlpha = alpha;
+  return HWC3::Error::None;
+}
+
+float Layer::getPlaneAlpha() const {
+  auto alpha = mPlaneAlpha;
+  DEBUG_LOG("%s: layer:%" PRId64 "alpha:%f", __FUNCTION__, mId, alpha);
+
+  return alpha;
+}
+
+HWC3::Error Layer::setSidebandStream(buffer_handle_t /*stream*/) {
+  DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error Layer::setSourceCrop(common::FRect crop) {
+  DEBUG_LOG("%s: layer:%" PRId64
+            "crop rect-left:%f rect-top:%f rect-right:%f rect-bot:%f",
+            __FUNCTION__, mId, crop.left, crop.top, crop.right, crop.bottom);
+
+  mSourceCrop = crop;
+  return HWC3::Error::None;
+}
+
+common::FRect Layer::getSourceCrop() const {
+  common::FRect crop = mSourceCrop;
+  DEBUG_LOG("%s: layer:%" PRId64
+            "crop rect-left:%f rect-top:%f rect-right:%f rect-bot:%f",
+            __FUNCTION__, mId, crop.left, crop.top, crop.right, crop.bottom);
+
+  return crop;
+}
+
+common::Rect Layer::getSourceCropInt() const {
+  common::Rect crop = {};
+  crop.left = static_cast<int>(mSourceCrop.left);
+  crop.top = static_cast<int>(mSourceCrop.top);
+  crop.right = static_cast<int>(mSourceCrop.right);
+  crop.bottom = static_cast<int>(mSourceCrop.bottom);
+  DEBUG_LOG("%s: layer:%" PRId64
+            "crop rect-left:%d rect-top:%d rect-right:%d rect-bot:%d",
+            __FUNCTION__, mId, crop.left, crop.top, crop.right, crop.bottom);
+
+  return crop;
+}
+
+HWC3::Error Layer::setTransform(common::Transform transform) {
+  const auto transformString = toString(transform);
+  DEBUG_LOG("%s: layer:%" PRId64 " transform:%s", __FUNCTION__, mId,
+            transformString.c_str());
+
+  mTransform = transform;
+  return HWC3::Error::None;
+}
+
+common::Transform Layer::getTransform() const {
+  const auto transformString = toString(mTransform);
+  DEBUG_LOG("%s: layer:%" PRId64 " transform:%s", __FUNCTION__, mId,
+            transformString.c_str());
+
+  return mTransform;
+}
+
+HWC3::Error Layer::setVisibleRegion(
+    const std::vector<std::optional<common::Rect>>& visible) {
+  DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+  mVisibleRegion.clear();
+  mVisibleRegion.reserve(visible.size());
+  for (const auto& rectOption : visible) {
+    if (rectOption) {
+      mVisibleRegion.push_back(*rectOption);
+    }
+  }
+
+  return HWC3::Error::None;
+}
+
+std::size_t Layer::getNumVisibleRegions() const {
+  const std::size_t num = mVisibleRegion.size();
+  DEBUG_LOG("%s: layer:%" PRId64 " number of visible regions: %zu",
+            __FUNCTION__, mId, num);
+
+  return num;
+}
+
+HWC3::Error Layer::setZOrder(int32_t z) {
+  DEBUG_LOG("%s: layer:%" PRId64 " z:%d", __FUNCTION__, mId, z);
+
+  mZOrder = z;
+  return HWC3::Error::None;
+}
+
+int32_t Layer::getZOrder() const {
+  DEBUG_LOG("%s: layer:%" PRId64 " z:%d", __FUNCTION__, mId, mZOrder);
+
+  return mZOrder;
+}
+
+HWC3::Error Layer::setPerFrameMetadata(
+    const std::vector<std::optional<PerFrameMetadata>>& /*perFrameMetadata*/) {
+  DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error Layer::setColorTransform(const std::vector<float>& colorTransform) {
+  DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+  if (colorTransform.size() < 16) {
+    return HWC3::Error::BadParameter;
+  }
+
+  mColorTransform.emplace();
+  std::copy_n(colorTransform.data(), 16, mColorTransform->data());
+  return HWC3::Error::None;
+}
+
+const std::optional<std::array<float, 16>>& Layer::getColorTransform() const {
+  DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+  return mColorTransform;
+}
+
+HWC3::Error Layer::setBrightness(float brightness) {
+  DEBUG_LOG("%s: layer:%" PRId64 " brightness:%f", __FUNCTION__, mId,
+            brightness);
+
+  if (std::isnan(brightness) || brightness < 0.0f || brightness > 1.0f) {
+    ALOGE("%s: layer:%" PRId64 " brightness:%f", __FUNCTION__, mId, brightness);
+    return HWC3::Error::BadParameter;
+  }
+
+  mBrightness = brightness;
+  return HWC3::Error::None;
+}
+
+float Layer::getBrightness() const {
+  DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+  return mBrightness;
+}
+
+HWC3::Error Layer::setPerFrameMetadataBlobs(
+    const std::vector<
+        std::optional<PerFrameMetadataBlob>>& /*perFrameMetadata*/) {
+  DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+  return HWC3::Error::None;
+}
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/Layer.h b/system/hwc3/Layer.h
new file mode 100644
index 0000000..7d6a9ed
--- /dev/null
+++ b/system/hwc3/Layer.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWC_LAYER_H
+#define ANDROID_HWC_LAYER_H
+
+#include <vector>
+
+#include "Common.h"
+#include "FencedBuffer.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class Layer {
+ public:
+  explicit Layer();
+
+  Layer(const Layer&) = delete;
+  Layer& operator=(const Layer&) = delete;
+
+  Layer(Layer&&) = default;
+  Layer& operator=(Layer&&) = default;
+
+  int64_t getId() const { return mId; }
+
+  HWC3::Error setCursorPosition(const common::Point& cursorPosition);
+  common::Point getCursorPosition() const;
+
+  HWC3::Error setBuffer(buffer_handle_t buffer,
+                        const ndk::ScopedFileDescriptor& fence);
+  FencedBuffer& getBuffer();
+  buffer_handle_t waitAndGetBuffer();
+
+  HWC3::Error setSurfaceDamage(
+      const std::vector<std::optional<common::Rect>>& damage);
+
+  HWC3::Error setBlendMode(common::BlendMode mode);
+  common::BlendMode getBlendMode() const;
+
+  HWC3::Error setColor(Color color);
+  Color getColor() const;
+
+  HWC3::Error setCompositionType(Composition composition);
+  Composition getCompositionType() const;
+
+  HWC3::Error setDataspace(common::Dataspace dataspace);
+  common::Dataspace getDataspace() const;
+
+  HWC3::Error setDisplayFrame(common::Rect frame);
+  common::Rect getDisplayFrame() const;
+
+  HWC3::Error setPlaneAlpha(float alpha);
+  float getPlaneAlpha() const;
+
+  HWC3::Error setSidebandStream(buffer_handle_t stream);
+
+  HWC3::Error setSourceCrop(common::FRect crop);
+  common::FRect getSourceCrop() const;
+  common::Rect getSourceCropInt() const;
+
+  HWC3::Error setTransform(common::Transform transform);
+  common::Transform getTransform() const;
+
+  HWC3::Error setVisibleRegion(
+      const std::vector<std::optional<common::Rect>>& visible);
+  std::size_t getNumVisibleRegions() const;
+
+  HWC3::Error setZOrder(int32_t z);
+  int32_t getZOrder() const;
+
+  HWC3::Error setPerFrameMetadata(
+      const std::vector<std::optional<PerFrameMetadata>>& perFrameMetadata);
+
+  HWC3::Error setColorTransform(const std::vector<float>& colorTransform);
+  const std::optional<std::array<float, 16>>& getColorTransform() const;
+
+  HWC3::Error setBrightness(float brightness);
+  float getBrightness() const;
+
+  HWC3::Error setPerFrameMetadataBlobs(
+      const std::vector<std::optional<PerFrameMetadataBlob>>& perFrameMetadata);
+
+ private:
+  const int64_t mId;
+  common::Point mCursorPosition;
+  FencedBuffer mBuffer;
+  common::BlendMode mBlendMode = common::BlendMode::NONE;
+  Color mColor = {0, 0, 0, 0};
+  Composition mCompositionType = Composition::INVALID;
+  common::Dataspace mDataspace = common::Dataspace::UNKNOWN;
+  common::Rect mDisplayFrame = {0, 0, -1, -1};
+  float mPlaneAlpha = 0.0f;
+  common::FRect mSourceCrop = {0.0f, 0.0f, -1.0f, -1.0f};
+  common::Transform mTransform = common::Transform{0};
+  std::vector<common::Rect> mVisibleRegion;
+  int32_t mZOrder = 0;
+  std::optional<std::array<float, 16>> mColorTransform;
+  float mBrightness = 1.0f;
+};
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
\ No newline at end of file
diff --git a/system/hwc3/Main.cpp b/system/hwc3/Main.cpp
new file mode 100644
index 0000000..440a1c2
--- /dev/null
+++ b/system/hwc3/Main.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2022, 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.
+ */
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <binder/ProcessState.h>
+#include <sched.h>
+
+#include "Composer.h"
+
+using aidl::android::hardware::graphics::composer3::impl::Composer;
+
+int main(int /*argc*/, char** /*argv*/) {
+  ALOGI("RanchuHWC (HWComposer3/HWC3) starting up...");
+
+  // same as SF main thread
+  struct sched_param param = {0};
+  param.sched_priority = 2;
+  if (sched_setscheduler(0, SCHED_FIFO | SCHED_RESET_ON_FORK, &param) != 0) {
+    ALOGE("%s: failed to set priority: %s", __FUNCTION__, strerror(errno));
+  }
+
+  auto composer = ndk::SharedRefBase::make<Composer>();
+  CHECK(composer != nullptr);
+
+  const std::string instance =
+      std::string() + Composer::descriptor + "/default";
+  binder_status_t status =
+      AServiceManager_addService(composer->asBinder().get(), instance.c_str());
+  CHECK(status == STATUS_OK);
+
+  // Thread pool for vendor libbinder for internal vendor services
+  android::ProcessState::self()->setThreadPoolMaxThreadCount(2);
+  android::ProcessState::self()->startThreadPool();
+
+  // Thread pool for system libbinder (via libbinder_ndk) for aidl services
+  // IComposer and IDisplay
+  ABinderProcess_setThreadPoolMaxThreadCount(5);
+  ABinderProcess_startThreadPool();
+  ABinderProcess_joinThreadPool();
+
+  return EXIT_FAILURE;
+}
\ No newline at end of file
diff --git a/system/hwc3/NoOpFrameComposer.cpp b/system/hwc3/NoOpFrameComposer.cpp
new file mode 100644
index 0000000..e988d46
--- /dev/null
+++ b/system/hwc3/NoOpFrameComposer.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#include "NoOpFrameComposer.h"
+
+#include "Display.h"
+#include "Drm.h"
+#include "Layer.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+HWC3::Error NoOpFrameComposer::init() {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error NoOpFrameComposer::registerOnHotplugCallback(
+    const HotplugCallback&) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error NoOpFrameComposer::unregisterOnHotplugCallback() {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error NoOpFrameComposer::onDisplayCreate(Display*) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error NoOpFrameComposer::onDisplayDestroy(Display*) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error NoOpFrameComposer::onDisplayClientTargetSet(Display*) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error NoOpFrameComposer::onActiveConfigChange(Display*) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  return HWC3::Error::None;
+};
+
+HWC3::Error NoOpFrameComposer::validateDisplay(Display*, DisplayChanges*) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error NoOpFrameComposer::presentDisplay(
+    Display*, ::android::base::unique_fd*,
+    std::unordered_map<int64_t,
+                       ::android::base::unique_fd>* /*outLayerFences*/) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  return HWC3::Error::None;
+}
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/NoOpFrameComposer.h b/system/hwc3/NoOpFrameComposer.h
new file mode 100644
index 0000000..4722477
--- /dev/null
+++ b/system/hwc3/NoOpFrameComposer.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWC_NOOPFRAMECOMPOSER_H
+#define ANDROID_HWC_NOOPFRAMECOMPOSER_H
+
+#include "Common.h"
+#include "Display.h"
+#include "DrmPresenter.h"
+#include "FrameComposer.h"
+#include "Gralloc.h"
+#include "Layer.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class NoOpFrameComposer : public FrameComposer {
+ public:
+  NoOpFrameComposer() = default;
+
+  NoOpFrameComposer(const NoOpFrameComposer&) = delete;
+  NoOpFrameComposer& operator=(const NoOpFrameComposer&) = delete;
+
+  NoOpFrameComposer(NoOpFrameComposer&&) = delete;
+  NoOpFrameComposer& operator=(NoOpFrameComposer&&) = delete;
+
+  HWC3::Error init() override;
+
+  HWC3::Error registerOnHotplugCallback(const HotplugCallback& cb) override;
+
+  HWC3::Error unregisterOnHotplugCallback() override;
+
+  HWC3::Error onDisplayCreate(Display*) override;
+
+  HWC3::Error onDisplayDestroy(Display*) override;
+
+  HWC3::Error onDisplayClientTargetSet(Display*) override;
+
+  // Determines if this composer can compose the given layers on the given
+  // display and requests changes for layers that can't not be composed.
+  HWC3::Error validateDisplay(Display* display,
+                              DisplayChanges* outChanges) override;
+
+  // Performs the actual composition of layers and presents the composed result
+  // to the display.
+  HWC3::Error presentDisplay(
+      Display* display, ::android::base::unique_fd* outDisplayFence,
+      std::unordered_map<int64_t, ::android::base::unique_fd>* outLayerFences)
+      override;
+
+  HWC3::Error onActiveConfigChange(Display* /*display*/) override;
+};
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/Time.h b/system/hwc3/Time.h
new file mode 100644
index 0000000..79473a5
--- /dev/null
+++ b/system/hwc3/Time.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWC_TIME_H
+#define ANDROID_HWC_TIME_H
+
+#include <utils/Timers.h>
+
+#include <chrono>
+
+#include "Common.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+using Nanoseconds = std::chrono::nanoseconds;
+
+using TimePoint = std::chrono::time_point<std::chrono::steady_clock>;
+
+inline TimePoint asTimePoint(int64_t nanos) {
+  return TimePoint(Nanoseconds(nanos));
+}
+
+inline TimePoint now() {
+  return asTimePoint(systemTime(SYSTEM_TIME_MONOTONIC));
+}
+
+inline int32_t asNanosDuration(Nanoseconds duration) {
+  return duration.count();
+}
+
+inline int64_t asNanosTimePoint(TimePoint time) {
+  TimePoint zero(Nanoseconds(0));
+  return std::chrono::duration_cast<Nanoseconds>(time - zero).count();
+}
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/VsyncThread.cpp b/system/hwc3/VsyncThread.cpp
new file mode 100644
index 0000000..350a906
--- /dev/null
+++ b/system/hwc3/VsyncThread.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#include "VsyncThread.h"
+
+#include <utils/ThreadDefs.h>
+
+#include <thread>
+
+#include "Time.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+namespace {
+
+// Returns the timepoint of the next vsync after the 'now' timepoint that is
+// a multiple of 'vsyncPeriod' in-phase/offset-from 'previousSync'.
+//
+// Some examples:
+//  * vsyncPeriod=50ns previousVsync=500ns now=510ns => 550ns
+//  * vsyncPeriod=50ns previousVsync=300ns now=510ns => 550ns
+//  * vsyncPeriod=50ns previousVsync=500ns now=550ns => 550ns
+TimePoint GetNextVsyncInPhase(Nanoseconds vsyncPeriod, TimePoint previousVsync,
+                              TimePoint now) {
+  const auto elapsed = Nanoseconds(now - previousVsync);
+  const auto nextMultiple = (elapsed / vsyncPeriod) + 1;
+  return previousVsync + (nextMultiple * vsyncPeriod);
+}
+
+}  // namespace
+
+VsyncThread::VsyncThread(int64_t displayId) : mDisplayId(displayId) {
+  mPreviousVsync = std::chrono::steady_clock::now() - mVsyncPeriod;
+}
+
+VsyncThread::~VsyncThread() { stop(); }
+
+HWC3::Error VsyncThread::start(int32_t vsyncPeriodNanos) {
+  DEBUG_LOG("%s for display:%" PRIu64, __FUNCTION__, mDisplayId);
+
+  mVsyncPeriod = Nanoseconds(vsyncPeriodNanos);
+
+  mThread = std::thread([this]() { threadLoop(); });
+
+  const std::string name =
+      "display_" + std::to_string(mDisplayId) + "_vsync_thread";
+
+  int ret = pthread_setname_np(mThread.native_handle(), name.c_str());
+  if (ret != 0) {
+    ALOGE("%s: failed to set Vsync thread name: %s", __FUNCTION__,
+          strerror(ret));
+  }
+
+  struct sched_param param = {
+      .sched_priority = ANDROID_PRIORITY_DISPLAY,
+  };
+  ret = pthread_setschedparam(mThread.native_handle(), SCHED_FIFO, &param);
+  if (ret != 0) {
+    ALOGE("%s: failed to set Vsync thread priority: %s", __FUNCTION__,
+          strerror(ret));
+  }
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error VsyncThread::stop() {
+  mShuttingDown.store(true);
+  mThread.join();
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error VsyncThread::setCallbacks(
+    const std::shared_ptr<IComposerCallback>& callback) {
+  DEBUG_LOG("%s for display:%" PRIu64, __FUNCTION__, mDisplayId);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  mCallbacks = callback;
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error VsyncThread::setVsyncEnabled(bool enabled) {
+  DEBUG_LOG("%s for display:%" PRIu64 " enabled:%d", __FUNCTION__, mDisplayId,
+            enabled);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+
+  mVsyncEnabled = enabled;
+
+  return HWC3::Error::None;
+}
+
+HWC3::Error VsyncThread::scheduleVsyncUpdate(
+    int32_t newVsyncPeriod, const VsyncPeriodChangeConstraints& constraints,
+    VsyncPeriodChangeTimeline* outTimeline) {
+  DEBUG_LOG("%s for display:%" PRIu64, __FUNCTION__, mDisplayId);
+
+  PendingUpdate update;
+  update.period = Nanoseconds(newVsyncPeriod);
+  update.updateAfter = asTimePoint(constraints.desiredTimeNanos);
+
+  std::unique_lock<std::mutex> lock(mStateMutex);
+  mPendingUpdate.emplace(std::move(update));
+
+  TimePoint nextVsync =
+      GetNextVsyncInPhase(mVsyncPeriod, mPreviousVsync, update.updateAfter);
+
+  outTimeline->newVsyncAppliedTimeNanos = asNanosTimePoint(nextVsync);
+  outTimeline->refreshRequired = false;
+  outTimeline->refreshTimeNanos = 0;
+
+  return HWC3::Error::None;
+}
+
+Nanoseconds VsyncThread::updateVsyncPeriodLocked(TimePoint now) {
+  if (mPendingUpdate && now > mPendingUpdate->updateAfter) {
+    mVsyncPeriod = mPendingUpdate->period;
+    mPendingUpdate.reset();
+  }
+
+  return mVsyncPeriod;
+}
+
+void VsyncThread::threadLoop() {
+  ALOGI("Vsync thread for display:%" PRId64 " starting", mDisplayId);
+
+  Nanoseconds vsyncPeriod = mVsyncPeriod;
+
+  int vsyncs = 0;
+  TimePoint previousLog = std::chrono::steady_clock::now();
+
+  while (!mShuttingDown.load()) {
+    TimePoint now = std::chrono::steady_clock::now();
+    TimePoint nextVsync = GetNextVsyncInPhase(vsyncPeriod, mPreviousVsync, now);
+
+    std::this_thread::sleep_until(nextVsync);
+    {
+      std::unique_lock<std::mutex> lock(mStateMutex);
+
+      mPreviousVsync = nextVsync;
+
+      // Display has finished refreshing at previous vsync period. Update the
+      // vsync period if there was a pending update.
+      vsyncPeriod = updateVsyncPeriodLocked(mPreviousVsync);
+    }
+
+    if (mVsyncEnabled) {
+      if (mCallbacks) {
+        DEBUG_LOG("%s: for display:%" PRIu64 " calling vsync", __FUNCTION__,
+                  mDisplayId);
+        mCallbacks->onVsync(mDisplayId, asNanosTimePoint(nextVsync),
+                            asNanosDuration(vsyncPeriod));
+      }
+    }
+
+    static constexpr const int kLogIntervalSeconds = 60;
+    if (now > (previousLog + std::chrono::seconds(kLogIntervalSeconds))) {
+      DEBUG_LOG("%s: for display:%" PRIu64 " send %" PRIu32
+                " in last %d seconds",
+                __FUNCTION__, mDisplayId, vsyncs, kLogIntervalSeconds);
+      previousLog = now;
+      vsyncs = 0;
+    }
+    ++vsyncs;
+  }
+
+  ALOGI("Vsync thread for display:%" PRId64 " finished", mDisplayId);
+}
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/VsyncThread.h b/system/hwc3/VsyncThread.h
new file mode 100644
index 0000000..5738b5d
--- /dev/null
+++ b/system/hwc3/VsyncThread.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWC_VSYNCTHREAD_H
+#define ANDROID_HWC_VSYNCTHREAD_H
+
+#include <aidl/android/hardware/graphics/composer3/VsyncPeriodChangeConstraints.h>
+#include <aidl/android/hardware/graphics/composer3/VsyncPeriodChangeTimeline.h>
+#include <android/hardware/graphics/common/1.0/types.h>
+
+#include <chrono>
+#include <mutex>
+#include <optional>
+#include <thread>
+
+#include "Common.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+// Generates Vsync signals in software.
+class VsyncThread {
+ public:
+  VsyncThread(int64_t id);
+  virtual ~VsyncThread();
+
+  VsyncThread(const VsyncThread&) = default;
+  VsyncThread& operator=(const VsyncThread&) = default;
+
+  VsyncThread(VsyncThread&&) = default;
+  VsyncThread& operator=(VsyncThread&&) = default;
+
+  HWC3::Error start(int32_t periodNanos);
+
+  HWC3::Error setCallbacks(const std::shared_ptr<IComposerCallback>& callback);
+
+  HWC3::Error setVsyncEnabled(bool enabled);
+
+  HWC3::Error scheduleVsyncUpdate(
+      int32_t newVsyncPeriod,
+      const VsyncPeriodChangeConstraints& newVsyncPeriodChangeConstraints,
+      VsyncPeriodChangeTimeline* timeline);
+
+ private:
+  HWC3::Error stop();
+
+  void threadLoop();
+
+  std::chrono::nanoseconds updateVsyncPeriodLocked(
+      std::chrono::time_point<std::chrono::steady_clock> now);
+
+  const int64_t mDisplayId;
+
+  std::thread mThread;
+
+  std::mutex mStateMutex;
+
+  std::atomic<bool> mShuttingDown{false};
+
+  std::shared_ptr<IComposerCallback> mCallbacks;
+
+  bool mVsyncEnabled = false;
+  std::chrono::nanoseconds mVsyncPeriod;
+  std::chrono::time_point<std::chrono::steady_clock> mPreviousVsync;
+
+  struct PendingUpdate {
+    std::chrono::nanoseconds period;
+    std::chrono::time_point<std::chrono::steady_clock> updateAfter;
+  };
+  std::optional<PendingUpdate> mPendingUpdate;
+};
+
+}  // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
\ No newline at end of file
diff --git a/system/hwc3/hwc3.rc b/system/hwc3/hwc3.rc
new file mode 100644
index 0000000..ebb2948
--- /dev/null
+++ b/system/hwc3/hwc3.rc
@@ -0,0 +1,7 @@
+service vendor.hwcomposer-3 /vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu
+    class hal animation
+    user system
+    group graphics drmrpc
+    capabilities SYS_NICE
+    onrestart restart surfaceflinger
+    task_profiles ServiceCapacityLow
\ No newline at end of file
diff --git a/system/hwc3/hwc3.xml b/system/hwc3/hwc3.xml
new file mode 100644
index 0000000..79d6a8d
--- /dev/null
+++ b/system/hwc3/hwc3.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.graphics.composer3</name>
+        <version>1</version>
+        <interface>
+            <name>IComposer</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
\ No newline at end of file
diff --git a/system/hwc3/virtgpu_drm.h b/system/hwc3/virtgpu_drm.h
new file mode 100644
index 0000000..a13e20c
--- /dev/null
+++ b/system/hwc3/virtgpu_drm.h
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2013 Red Hat
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef VIRTGPU_DRM_H
+#define VIRTGPU_DRM_H
+
+#include "drm.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* Please note that modifications to all structs defined here are
+ * subject to backwards-compatibility constraints.
+ *
+ * Do not use pointers, use __u64 instead for 32 bit / 64 bit user/kernel
+ * compatibility Keep fields aligned to their size
+ */
+
+#define DRM_VIRTGPU_MAP         0x01
+#define DRM_VIRTGPU_EXECBUFFER  0x02
+#define DRM_VIRTGPU_GETPARAM    0x03
+#define DRM_VIRTGPU_RESOURCE_CREATE 0x04
+#define DRM_VIRTGPU_RESOURCE_INFO     0x05
+#define DRM_VIRTGPU_TRANSFER_FROM_HOST 0x06
+#define DRM_VIRTGPU_TRANSFER_TO_HOST 0x07
+#define DRM_VIRTGPU_WAIT     0x08
+#define DRM_VIRTGPU_GET_CAPS  0x09
+#define DRM_VIRTGPU_RESOURCE_CREATE_BLOB 0x0a
+#define DRM_VIRTGPU_CONTEXT_INIT 0x0b
+
+#define VIRTGPU_EXECBUF_FENCE_FD_IN	0x01
+#define VIRTGPU_EXECBUF_FENCE_FD_OUT	0x02
+#define VIRTGPU_EXECBUF_RING_IDX	0x04
+#define VIRTGPU_EXECBUF_FLAGS  (\
+		VIRTGPU_EXECBUF_FENCE_FD_IN |\
+		VIRTGPU_EXECBUF_FENCE_FD_OUT |\
+		VIRTGPU_EXECBUF_RING_IDX |\
+		0)
+
+struct drm_virtgpu_map {
+	__u64 offset; /* use for mmap system call */
+	__u32 handle;
+	__u32 pad;
+};
+
+struct drm_virtgpu_execbuffer {
+	__u32 flags;
+	__u32 size;
+	__u64 command; /* void* */
+	__u64 bo_handles;
+	__u32 num_bo_handles;
+	__s32 fence_fd; /* in/out fence fd (see VIRTGPU_EXECBUF_FENCE_FD_IN/OUT) */
+	__u32 ring_idx; /* command ring index (see VIRTGPU_EXECBUF_RING_IDX) */
+	__u32 pad;
+};
+
+#define VIRTGPU_PARAM_3D_FEATURES 1 /* do we have 3D features in the hw */
+#define VIRTGPU_PARAM_CAPSET_QUERY_FIX 2 /* do we have the capset fix */
+#define VIRTGPU_PARAM_RESOURCE_BLOB 3 /* DRM_VIRTGPU_RESOURCE_CREATE_BLOB */
+#define VIRTGPU_PARAM_HOST_VISIBLE 4 /* Host blob resources are mappable */
+#define VIRTGPU_PARAM_CROSS_DEVICE 5 /* Cross virtio-device resource sharing  */
+#define VIRTGPU_PARAM_CONTEXT_INIT 6 /* DRM_VIRTGPU_CONTEXT_INIT */
+#define VIRTGPU_PARAM_SUPPORTED_CAPSET_IDs 7 /* Bitmask of supported capability set ids */
+
+struct drm_virtgpu_getparam {
+	__u64 param;
+	__u64 value;
+};
+
+/* NO_BO flags? NO resource flag? */
+/* resource flag for y_0_top */
+struct drm_virtgpu_resource_create {
+	__u32 target;
+	__u32 format;
+	__u32 bind;
+	__u32 width;
+	__u32 height;
+	__u32 depth;
+	__u32 array_size;
+	__u32 last_level;
+	__u32 nr_samples;
+	__u32 flags;
+	__u32 bo_handle; /* if this is set - recreate a new resource attached to this bo ? */
+	__u32 res_handle;  /* returned by kernel */
+	__u32 size;        /* validate transfer in the host */
+	__u32 stride;      /* validate transfer in the host */
+};
+
+struct drm_virtgpu_resource_info {
+	__u32 bo_handle;
+	__u32 res_handle;
+	__u32 size;
+	__u32 blob_mem;
+};
+
+struct drm_virtgpu_3d_box {
+	__u32 x;
+	__u32 y;
+	__u32 z;
+	__u32 w;
+	__u32 h;
+	__u32 d;
+};
+
+struct drm_virtgpu_3d_transfer_to_host {
+	__u32 bo_handle;
+	struct drm_virtgpu_3d_box box;
+	__u32 level;
+	__u32 offset;
+	__u32 stride;
+	__u32 layer_stride;
+};
+
+struct drm_virtgpu_3d_transfer_from_host {
+	__u32 bo_handle;
+	struct drm_virtgpu_3d_box box;
+	__u32 level;
+	__u32 offset;
+	__u32 stride;
+	__u32 layer_stride;
+};
+
+#define VIRTGPU_WAIT_NOWAIT 1 /* like it */
+struct drm_virtgpu_3d_wait {
+	__u32 handle; /* 0 is an invalid handle */
+	__u32 flags;
+};
+
+struct drm_virtgpu_get_caps {
+	__u32 cap_set_id;
+	__u32 cap_set_ver;
+	__u64 addr;
+	__u32 size;
+	__u32 pad;
+};
+
+struct drm_virtgpu_resource_create_blob {
+#define VIRTGPU_BLOB_MEM_GUEST             0x0001
+#define VIRTGPU_BLOB_MEM_HOST3D            0x0002
+#define VIRTGPU_BLOB_MEM_HOST3D_GUEST      0x0003
+
+#define VIRTGPU_BLOB_FLAG_USE_MAPPABLE     0x0001
+#define VIRTGPU_BLOB_FLAG_USE_SHAREABLE    0x0002
+#define VIRTGPU_BLOB_FLAG_USE_CROSS_DEVICE 0x0004
+	/* zero is invalid blob_mem */
+	__u32 blob_mem;
+	__u32 blob_flags;
+	__u32 bo_handle;
+	__u32 res_handle;
+	__u64 size;
+
+	/*
+	 * for 3D contexts with VIRTGPU_BLOB_MEM_HOST3D_GUEST and
+	 * VIRTGPU_BLOB_MEM_HOST3D otherwise, must be zero.
+	 */
+	__u32 pad;
+	__u32 cmd_size;
+	__u64 cmd;
+	__u64 blob_id;
+};
+
+#define VIRTGPU_CONTEXT_PARAM_CAPSET_ID       0x0001
+#define VIRTGPU_CONTEXT_PARAM_NUM_RINGS       0x0002
+#define VIRTGPU_CONTEXT_PARAM_POLL_RINGS_MASK 0x0003
+struct drm_virtgpu_context_set_param {
+	__u64 param;
+	__u64 value;
+};
+
+struct drm_virtgpu_context_init {
+	__u32 num_params;
+	__u32 pad;
+
+	/* pointer to drm_virtgpu_context_set_param array */
+	__u64 ctx_set_params;
+};
+
+#define DRM_IOCTL_VIRTGPU_MAP \
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_MAP, struct drm_virtgpu_map)
+
+#define DRM_IOCTL_VIRTGPU_EXECBUFFER \
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_EXECBUFFER,\
+		struct drm_virtgpu_execbuffer)
+
+#define DRM_IOCTL_VIRTGPU_GETPARAM \
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_GETPARAM,\
+		struct drm_virtgpu_getparam)
+
+#define DRM_IOCTL_VIRTGPU_RESOURCE_CREATE			\
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_RESOURCE_CREATE,	\
+		struct drm_virtgpu_resource_create)
+
+#define DRM_IOCTL_VIRTGPU_RESOURCE_INFO \
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_RESOURCE_INFO, \
+		 struct drm_virtgpu_resource_info)
+
+#define DRM_IOCTL_VIRTGPU_TRANSFER_FROM_HOST \
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_TRANSFER_FROM_HOST,	\
+		struct drm_virtgpu_3d_transfer_from_host)
+
+#define DRM_IOCTL_VIRTGPU_TRANSFER_TO_HOST \
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_TRANSFER_TO_HOST,	\
+		struct drm_virtgpu_3d_transfer_to_host)
+
+#define DRM_IOCTL_VIRTGPU_WAIT				\
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_WAIT,	\
+		struct drm_virtgpu_3d_wait)
+
+#define DRM_IOCTL_VIRTGPU_GET_CAPS \
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_GET_CAPS, \
+	struct drm_virtgpu_get_caps)
+
+#define DRM_IOCTL_VIRTGPU_RESOURCE_CREATE_BLOB				\
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_RESOURCE_CREATE_BLOB,	\
+		struct drm_virtgpu_resource_create_blob)
+
+#define DRM_IOCTL_VIRTGPU_CONTEXT_INIT					\
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_CONTEXT_INIT,		\
+		struct drm_virtgpu_context_init)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif