Merge 50689913a4f8deb43cc2b24d47f70a621fb1d8a1 on remote branch

Change-Id: I274bdf821cd5efae07bcae22e189f72862ac285f
diff --git a/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.cpp b/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.cpp
index 90b5653..cbc7069 100644
--- a/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.cpp
+++ b/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.cpp
@@ -42,6 +42,8 @@
 
 #include "C2GoldfishAvcDec.h"
 
+#include <mutex>
+
 #define DEBUG 0
 #if DEBUG
 #define DDD(...) ALOGD(__VA_ARGS__)
@@ -64,6 +66,35 @@
    So total maximum output delay is 34 */
 constexpr uint32_t kMaxOutputDelay = 34;
 constexpr uint32_t kMinInputBytes = 4;
+
+static std::mutex s_decoder_count_mutex;
+static int s_decoder_count = 0;
+
+int allocateDecoderId() {
+  DDD("calling %s", __func__);
+  std::lock_guard<std::mutex> lock(s_decoder_count_mutex);
+  if (s_decoder_count >= 32 || s_decoder_count < 0) {
+    ALOGE("calling %s failed", __func__);
+    return -1;
+  }
+  ++ s_decoder_count;
+  DDD("calling %s success total decoder %d", __func__, s_decoder_count);
+  return s_decoder_count;;
+}
+
+bool deAllocateDecoderId() {
+  DDD("calling %s", __func__);
+  std::lock_guard<std::mutex> lock(s_decoder_count_mutex);
+  if (s_decoder_count < 1) {
+    ALOGE("calling %s failed ", __func__);
+    return false;
+  }
+  -- s_decoder_count;
+  DDD("calling %s success total decoder %d", __func__, s_decoder_count);
+  return true;
+}
+
+
 } // namespace
 
 class C2GoldfishAvcDec::IntfImpl : public SimpleInterface<void>::BaseParams {
@@ -307,6 +338,8 @@
         if (me.v.matrix > C2Color::MATRIX_OTHER) {
             me.set().matrix = C2Color::MATRIX_OTHER;
         }
+        DDD("default primaries %d default range %d", me.set().primaries,
+            me.set().range);
         return C2R::Ok();
     }
 
@@ -326,6 +359,8 @@
         if (me.v.matrix > C2Color::MATRIX_OTHER) {
             me.set().matrix = C2Color::MATRIX_OTHER;
         }
+        DDD("coded primaries %d coded range %d", me.set().primaries,
+            me.set().range);
         return C2R::Ok();
     }
 
@@ -336,6 +371,7 @@
         (void)mayBlock;
         // take default values for all unspecified fields, and coded values for
         // specified ones
+        DDD("before change primaries %d range %d", me.v.primaries, me.v.range);
         me.set().range =
             coded.v.range == RANGE_UNSPECIFIED ? def.v.range : coded.v.range;
         me.set().primaries = coded.v.primaries == PRIMARIES_UNSPECIFIED
@@ -346,6 +382,8 @@
                                 : coded.v.transfer;
         me.set().matrix = coded.v.matrix == MATRIX_UNSPECIFIED ? def.v.matrix
                                                                : coded.v.matrix;
+
+        DDD("after change primaries %d range %d", me.v.primaries, me.v.range);
         return C2R::Ok();
     }
 
@@ -357,7 +395,13 @@
 
     int height() const { return mSize->height; }
 
-  private:
+    int primaries() const { return mColorAspects->primaries; }
+
+    int range() const { return mColorAspects->range; }
+
+    int transfer() const { return mColorAspects->transfer; }
+
+   private:
     std::shared_ptr<C2StreamProfileLevelInfo::input> mProfileLevel;
     std::shared_ptr<C2StreamPictureSizeInfo::output> mSize;
     std::shared_ptr<C2StreamMaxPictureSizeTuning::output> mMaxSize;
@@ -393,6 +437,9 @@
 C2GoldfishAvcDec::~C2GoldfishAvcDec() { onRelease(); }
 
 c2_status_t C2GoldfishAvcDec::onInit() {
+    ALOGD("calling onInit");
+    mId = allocateDecoderId();
+    if (mId <= 0) return C2_NO_MEMORY;
     status_t err = initDecoder();
     return err == OK ? C2_OK : C2_CORRUPTED;
 }
@@ -407,6 +454,11 @@
 void C2GoldfishAvcDec::onReset() { (void)onStop(); }
 
 void C2GoldfishAvcDec::onRelease() {
+    DDD("calling onRelease");
+    if (mId > 0) {
+      deAllocateDecoderId();
+      mId = -1;
+    }
     deleteContext();
     if (mOutBlock) {
         mOutBlock.reset();
@@ -457,6 +509,30 @@
     return C2_OK;
 }
 
+void C2GoldfishAvcDec::sendMetadata() {
+    // compare and send if changed
+    MetaDataColorAspects currentMetaData = {1, 0, 0, 0};
+    currentMetaData.primaries = mIntf->primaries();
+    currentMetaData.range = mIntf->range();
+    currentMetaData.transfer = mIntf->transfer();
+
+    DDD("metadata primaries %d range %d transfer %d",
+            (int)(currentMetaData.primaries),
+            (int)(currentMetaData.range),
+            (int)(currentMetaData.transfer)
+       );
+
+    if (mSentMetadata.primaries == currentMetaData.primaries &&
+        mSentMetadata.range == currentMetaData.range &&
+        mSentMetadata.transfer == currentMetaData.transfer) {
+        DDD("metadata is the same, no need to update");
+        return;
+    }
+    std::swap(mSentMetadata, currentMetaData);
+
+    mContext->sendMetadata(&(mSentMetadata));
+}
+
 status_t C2GoldfishAvcDec::createDecoder() {
 
     DDD("creating avc context now w %d h %d", mWidth, mHeight);
@@ -476,7 +552,6 @@
 }
 
 status_t C2GoldfishAvcDec::initDecoder() {
-    //    if (OK != createDecoder()) return UNKNOWN_ERROR;
     mStride = ALIGN2(mWidth);
     mSignalledError = false;
     resetPlugin();
@@ -682,7 +757,6 @@
 }
 
 void C2GoldfishAvcDec::getVuiParams(h264_image_t &img) {
-
     VuiColorAspects vuiColorAspects;
     vuiColorAspects.primaries = img.color_primaries;
     vuiColorAspects.transfer = img.color_trc;
@@ -931,6 +1005,8 @@
                 } // end of whChanged
             } // end of isSpsFrame
 
+            sendMetadata();
+
             uint32_t delay;
             GETTIME(&mTimeStart, nullptr);
             TIME_DIFF(mTimeEnd, mTimeStart, delay);
diff --git a/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.h b/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.h
index afa27f5..d90b11a 100644
--- a/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.h
+++ b/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.h
@@ -142,6 +142,10 @@
         }
     } mBitstreamColorAspects;
 
+    MetaDataColorAspects mSentMetadata = {1, 0, 0, 0};
+
+    void sendMetadata();
+
     // profile
     struct timeval mTimeStart;
     struct timeval mTimeEnd;
@@ -155,6 +159,7 @@
 
     std::unique_ptr<GoldfishH264Helper> mH264Helper;
 
+    int mId = -1;
     C2_DO_NOT_COPY(C2GoldfishAvcDec);
 };
 
diff --git a/system/codecs/c2/decoders/avcdec/MediaH264Decoder.cpp b/system/codecs/c2/decoders/avcdec/MediaH264Decoder.cpp
index 7909aa9..6560772 100644
--- a/system/codecs/c2/decoders/avcdec/MediaH264Decoder.cpp
+++ b/system/codecs/c2/decoders/avcdec/MediaH264Decoder.cpp
@@ -49,7 +49,7 @@
         }
         mSlot = slot;
         mAddressOffSet = static_cast<unsigned int>(mSlot) * (1 << 20);
-        DDD("got memory lot %d addrr %x", mSlot, mAddressOffSet);
+        DDD("got memory lot %d addrr %lu", mSlot, mAddressOffSet);
         mHasAddressSpaceMemory = true;
     }
     transport->writeParam(mVersion, 0, mAddressOffSet);
@@ -62,7 +62,7 @@
                              MediaOperation::InitContext, mAddressOffSet);
     auto *retptr = transport->getReturnAddr(mAddressOffSet);
     mHostHandle = *(uint64_t *)(retptr);
-    DDD("initH264Context: got handle to host %lld", mHostHandle);
+    DDD("initH264Context: got handle to host %lu", mHostHandle);
 }
 
 void MediaH264Decoder::resetH264Context(unsigned int width, unsigned int height,
@@ -87,7 +87,7 @@
 
 void MediaH264Decoder::destroyH264Context() {
 
-    DDD("return memory lot %d addrr %x", (int)(mAddressOffSet >> 23),
+    DDD("return memory lot %d addrr %lu", (int)(mAddressOffSet >> 23),
         mAddressOffSet);
     auto transport = GoldfishMediaTransport::getInstance();
     transport->writeParam((uint64_t)mHostHandle, 0, mAddressOffSet);
@@ -99,7 +99,7 @@
 
 h264_result_t MediaH264Decoder::decodeFrame(uint8_t *img, size_t szBytes,
                                             uint64_t pts) {
-    DDD("decode frame: use handle to host %lld", mHostHandle);
+    DDD("decode frame: use handle to host %lu", mHostHandle);
     h264_result_t res = {0, 0};
     if (!mHasAddressSpaceMemory) {
         ALOGE("%s no address space memory", __func__);
@@ -126,12 +126,28 @@
     return res;
 }
 
+void MediaH264Decoder::sendMetadata(MetaDataColorAspects *ptr) {
+    DDD("send metadata to host %p", ptr);
+    if (!mHasAddressSpaceMemory) {
+        ALOGE("%s no address space memory", __func__);
+        return;
+    }
+    MetaDataColorAspects& meta = *ptr;
+    auto transport = GoldfishMediaTransport::getInstance();
+    transport->writeParam((uint64_t)mHostHandle, 0, mAddressOffSet);
+    transport->writeParam(meta.type, 1, mAddressOffSet);
+    transport->writeParam(meta.primaries, 2, mAddressOffSet);
+    transport->writeParam(meta.range, 3, mAddressOffSet);
+    transport->writeParam(meta.transfer, 4, mAddressOffSet);
+    transport->sendOperation(MediaCodecType::H264Codec, MediaOperation::SendMetadata, mAddressOffSet);
+}
+
 void MediaH264Decoder::flush() {
     if (!mHasAddressSpaceMemory) {
         ALOGE("%s no address space memory", __func__);
         return;
     }
-    DDD("flush: use handle to host %lld", mHostHandle);
+    DDD("flush: use handle to host %lu", mHostHandle);
     auto transport = GoldfishMediaTransport::getInstance();
     transport->writeParam((uint64_t)mHostHandle, 0, mAddressOffSet);
     transport->sendOperation(MediaCodecType::H264Codec, MediaOperation::Flush,
@@ -139,7 +155,7 @@
 }
 
 h264_image_t MediaH264Decoder::getImage() {
-    DDD("getImage: use handle to host %lld", mHostHandle);
+    DDD("getImage: use handle to host %lu", mHostHandle);
     h264_image_t res{};
     if (!mHasAddressSpaceMemory) {
         ALOGE("%s no address space memory", __func__);
@@ -174,7 +190,7 @@
 
 h264_image_t
 MediaH264Decoder::renderOnHostAndReturnImageMetadata(int hostColorBufferId) {
-    DDD("%s: use handle to host %lld", __func__, mHostHandle);
+    DDD("%s: use handle to host %lu", __func__, mHostHandle);
     h264_image_t res{};
     if (hostColorBufferId < 0) {
         ALOGE("%s negative color buffer id %d", __func__, hostColorBufferId);
diff --git a/system/codecs/c2/decoders/avcdec/MediaH264Decoder.h b/system/codecs/c2/decoders/avcdec/MediaH264Decoder.h
index 1c1b262..e184cbd 100644
--- a/system/codecs/c2/decoders/avcdec/MediaH264Decoder.h
+++ b/system/codecs/c2/decoders/avcdec/MediaH264Decoder.h
@@ -17,6 +17,8 @@
 #ifndef GOLDFISH_MEDIA_H264_DEC_H_
 #define GOLDFISH_MEDIA_H264_DEC_H_
 
+#include "goldfish_media_utils.h"
+
 struct h264_init_result_t {
     uint64_t host_handle;
     int ret;
@@ -89,5 +91,14 @@
     // ask host to render to hostColorBufferId, return only image metadata back
     // to guest
     h264_image_t renderOnHostAndReturnImageMetadata(int hostColorBufferId);
+
+    // send metadata about the bitstream to host, such as color aspects that
+    // are set by the framework, e.g., color primaries (601, 709 etc), range
+    // (full range or limited range), transfer etc. given metadata could be
+    // of all kinds of types, the convention is that the first field server as
+    // metadata type id. host will check the type id to decide what to do with
+    // it; unrecognized typeid will be discarded by host side.
+
+    void sendMetadata(MetaDataColorAspects *ptr);
 };
 #endif
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 efa8859..a45cda9 100644
--- a/system/codecs/c2/decoders/base/include/goldfish_media_utils.h
+++ b/system/codecs/c2/decoders/base/include/goldfish_media_utils.h
@@ -26,6 +26,13 @@
     Max = 4,
 };
 
+struct MetaDataColorAspects {
+    uint64_t type = 1;
+    uint64_t primaries;
+    uint64_t range;
+    uint64_t transfer;
+};
+
 enum class MediaOperation : __u8 {
     InitContext = 0,
     DestroyContext = 1,
@@ -33,7 +40,8 @@
     GetImage = 3,
     Flush = 4,
     Reset = 5,
-    Max = 6,
+    SendMetadata = 6,
+    Max = 7,
 };
 
 // This class will abstract away the knowledge required to send media codec data
diff --git a/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.cpp b/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.cpp
index 7008bd5..13e9515 100644
--- a/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.cpp
+++ b/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.cpp
@@ -347,6 +347,13 @@
 
     int height() const { return mSize->height; }
 
+    int primaries() const { return mColorAspects->primaries; }
+
+    int range() const { return mColorAspects->range; }
+
+    int transfer() const { return mColorAspects->transfer; }
+
+
   private:
     std::shared_ptr<C2StreamProfileLevelInfo::input> mProfileLevel;
     std::shared_ptr<C2StreamPictureSizeInfo::output> mSize;
@@ -404,10 +411,11 @@
 }
 
 void C2GoldfishHevcDec::decodeHeaderAfterFlush() {
-    if (mContext && !mCsd0.empty() && !mCsd1.empty()) {
+        DDD("calling %s", __func__);
+    if (mContext && !mCsd0.empty()) {
         mContext->decodeFrame(&(mCsd0[0]), mCsd0.size(), 0);
-        mContext->decodeFrame(&(mCsd1[0]), mCsd1.size(), 0);
-        DDD("resending csd0 and csd1");
+        DDD("resending csd0");
+        DDD("calling %s success", __func__);
     }
 }
 
@@ -447,6 +455,30 @@
     return C2_OK;
 }
 
+void C2GoldfishHevcDec::sendMetadata() {
+    // compare and send if changed
+    MetaDataColorAspects currentMetaData = {1, 0, 0, 0};
+    currentMetaData.primaries = mIntf->primaries();
+    currentMetaData.range = mIntf->range();
+    currentMetaData.transfer = mIntf->transfer();
+
+    DDD("metadata primaries %d range %d transfer %d",
+            (int)(currentMetaData.primaries),
+            (int)(currentMetaData.range),
+            (int)(currentMetaData.transfer)
+       );
+
+    if (mSentMetadata.primaries == currentMetaData.primaries &&
+        mSentMetadata.range == currentMetaData.range &&
+        mSentMetadata.transfer == currentMetaData.transfer) {
+        DDD("metadata is the same, no need to update");
+        return;
+    }
+    std::swap(mSentMetadata, currentMetaData);
+
+    mContext->sendMetadata(&(mSentMetadata));
+}
+
 status_t C2GoldfishHevcDec::createDecoder() {
 
     DDD("creating hevc context now w %d h %d", mWidth, mHeight);
@@ -874,9 +906,6 @@
                 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);
@@ -885,7 +914,15 @@
             bool whChanged = false;
             if (GoldfishHevcHelper::isVpsFrame(mInPBuffer, mInPBufferSize)) {
                 mHevcHelper.reset(new GoldfishHevcHelper(mWidth, mHeight));
-                whChanged = mHevcHelper->decodeHeader(mInPBuffer, mInPBufferSize);
+                bool headerStatus = true;
+                whChanged = mHevcHelper->decodeHeader(
+                    mInPBuffer, mInPBufferSize, headerStatus);
+                if (!headerStatus) {
+                    mSignalledError = true;
+                    work->workletsProcessed = 1u;
+                    work->result = C2_CORRUPTED;
+                    return;
+                }
                 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());
@@ -922,6 +959,8 @@
                 } // end of whChanged
             } // end of isVpsFrame
 
+            sendMetadata();
+
             uint32_t delay;
             GETTIME(&mTimeStart, nullptr);
             TIME_DIFF(mTimeEnd, mTimeStart, delay);
diff --git a/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.h b/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.h
index fe080cf..bc3d65b 100644
--- a/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.h
+++ b/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.h
@@ -142,6 +142,10 @@
         }
     } mBitstreamColorAspects;
 
+    MetaDataColorAspects mSentMetadata = {1, 0, 0, 0};
+
+    void sendMetadata();
+
     // profile
     struct timeval mTimeStart;
     struct timeval mTimeEnd;
diff --git a/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.cpp b/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.cpp
index 1b93a0d..d3117a7 100644
--- a/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.cpp
+++ b/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.cpp
@@ -188,7 +188,9 @@
     }
 }
 
-bool GoldfishHevcHelper::decodeHeader(const uint8_t *frame, int inSize) {
+bool GoldfishHevcHelper::decodeHeader(const uint8_t *frame, int inSize,
+                                      bool &helperstatus) {
+    helperstatus = true;
     // should we check the header for vps/sps/pps frame ? otherwise
     // there is no point calling decoder
     if (!isVpsFrame(frame, inSize)) {
@@ -220,6 +222,8 @@
         ALOGE("failed to call decoder function for header\n");
         ALOGE("error in %s: 0x%x", __func__,
               ps_decode_op->u4_error_code);
+        helperstatus = false;
+        return false;
     }
 
     if (IVD_RES_CHANGED == (ps_decode_op->u4_error_code & IVD_ERROR_MASK)) {
diff --git a/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.h b/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.h
index 09de830..36a496b 100644
--- a/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.h
+++ b/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.h
@@ -37,9 +37,9 @@
   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; }
+   bool decodeHeader(const uint8_t *frame, int inSize, bool &status);
+   int getWidth() const { return mWidth; }
+   int getHeight() const { return mHeight; }
 
   private:
     void createDecoder();
diff --git a/system/codecs/c2/decoders/hevcdec/MediaHevcDecoder.cpp b/system/codecs/c2/decoders/hevcdec/MediaHevcDecoder.cpp
index bb2fbfa..f1bc356 100644
--- a/system/codecs/c2/decoders/hevcdec/MediaHevcDecoder.cpp
+++ b/system/codecs/c2/decoders/hevcdec/MediaHevcDecoder.cpp
@@ -126,6 +126,22 @@
     return res;
 }
 
+void MediaHevcDecoder::sendMetadata(MetaDataColorAspects *ptr) {
+    DDD("send metadata to host %p", ptr);
+    if (!mHasAddressSpaceMemory) {
+        ALOGE("%s no address space memory", __func__);
+        return;
+    }
+    MetaDataColorAspects& meta = *ptr;
+    auto transport = GoldfishMediaTransport::getInstance();
+    transport->writeParam((uint64_t)mHostHandle, 0, mAddressOffSet);
+    transport->writeParam(meta.type, 1, mAddressOffSet);
+    transport->writeParam(meta.primaries, 2, mAddressOffSet);
+    transport->writeParam(meta.range, 3, mAddressOffSet);
+    transport->writeParam(meta.transfer, 4, mAddressOffSet);
+    transport->sendOperation(MediaCodecType::HevcCodec, MediaOperation::SendMetadata, mAddressOffSet);
+}
+
 void MediaHevcDecoder::flush() {
     if (!mHasAddressSpaceMemory) {
         ALOGE("%s no address space memory", __func__);
diff --git a/system/codecs/c2/decoders/hevcdec/MediaHevcDecoder.h b/system/codecs/c2/decoders/hevcdec/MediaHevcDecoder.h
index 8dfb0cf..878950e 100644
--- a/system/codecs/c2/decoders/hevcdec/MediaHevcDecoder.h
+++ b/system/codecs/c2/decoders/hevcdec/MediaHevcDecoder.h
@@ -17,6 +17,8 @@
 #ifndef GOLDFISH_MEDIA_Hevc_DEC_H_
 #define GOLDFISH_MEDIA_Hevc_DEC_H_
 
+#include "goldfish_media_utils.h"
+
 struct hevc_init_result_t {
     uint64_t host_handle;
     int ret;
@@ -89,5 +91,8 @@
     // ask host to render to hostColorBufferId, return only image metadata back
     // to guest
     hevc_image_t renderOnHostAndReturnImageMetadata(int hostColorBufferId);
+
+    void sendMetadata(MetaDataColorAspects *ptr);
+
 };
 #endif
diff --git a/system/codecs/c2/decoders/vpxdec/C2GoldfishVpxDec.cpp b/system/codecs/c2/decoders/vpxdec/C2GoldfishVpxDec.cpp
index 99f0469..be6428e 100644
--- a/system/codecs/c2/decoders/vpxdec/C2GoldfishVpxDec.cpp
+++ b/system/codecs/c2/decoders/vpxdec/C2GoldfishVpxDec.cpp
@@ -324,6 +324,12 @@
 
     int height() const { return mSize->height; }
 
+    int primaries() const { return mDefaultColorAspects->primaries; }
+
+    int range() const { return mDefaultColorAspects->range; }
+
+    int transfer() const { return mDefaultColorAspects->transfer; }
+
     static C2R Hdr10PlusInfoInputSetter(bool mayBlock,
                                         C2P<C2StreamHdr10PlusInfo::input> &me) {
         (void)mayBlock;
@@ -416,6 +422,30 @@
 
 void C2GoldfishVpxDec::onRelease() { destroyDecoder(); }
 
+void C2GoldfishVpxDec::sendMetadata() {
+    // compare and send if changed
+    MetaDataColorAspects currentMetaData = {1, 0, 0, 0};
+    currentMetaData.primaries = mIntf->primaries();
+    currentMetaData.range = mIntf->range();
+    currentMetaData.transfer = mIntf->transfer();
+
+    DDD("metadata primaries %d range %d transfer %d",
+            (int)(currentMetaData.primaries),
+            (int)(currentMetaData.range),
+            (int)(currentMetaData.transfer)
+       );
+
+    if (mSentMetadata.primaries == currentMetaData.primaries &&
+        mSentMetadata.range == currentMetaData.range &&
+        mSentMetadata.transfer == currentMetaData.transfer) {
+        DDD("metadata is the same, no need to update");
+        return;
+    }
+    std::swap(mSentMetadata, currentMetaData);
+
+    vpx_codec_send_metadata(mCtx, &(mSentMetadata));
+}
+
 c2_status_t C2GoldfishVpxDec::onFlush_sm() {
     if (mFrameParallelMode) {
         // Flush decoder by passing nullptr data ptr and 0 size.
@@ -609,6 +639,8 @@
         }
     }
 
+    sendMetadata();
+
     if (inSize) {
         uint8_t *bitstream = const_cast<uint8_t *>(rView.data() + inOffset);
         vpx_codec_err_t err = vpx_codec_decode(
diff --git a/system/codecs/c2/decoders/vpxdec/C2GoldfishVpxDec.h b/system/codecs/c2/decoders/vpxdec/C2GoldfishVpxDec.h
index 4b356da..738d9fc 100644
--- a/system/codecs/c2/decoders/vpxdec/C2GoldfishVpxDec.h
+++ b/system/codecs/c2/decoders/vpxdec/C2GoldfishVpxDec.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include "goldfish_media_utils.h"
 #include "goldfish_vpx_defs.h"
 #include <SimpleC2Component.h>
 
@@ -95,6 +96,9 @@
                               const std::shared_ptr<C2BlockPool> &pool,
                               const std::unique_ptr<C2Work> &work);
 
+    MetaDataColorAspects mSentMetadata = {1, 0, 0, 0};
+    void sendMetadata();
+
     C2_DO_NOT_COPY(C2GoldfishVpxDec);
 };
 
diff --git a/system/codecs/c2/decoders/vpxdec/goldfish_vpx_defs.h b/system/codecs/c2/decoders/vpxdec/goldfish_vpx_defs.h
index bbcc805..1be05c9 100644
--- a/system/codecs/c2/decoders/vpxdec/goldfish_vpx_defs.h
+++ b/system/codecs/c2/decoders/vpxdec/goldfish_vpx_defs.h
@@ -61,4 +61,6 @@
 int vpx_codec_decode(vpx_codec_ctx_t *ctx, const uint8_t *data,
                      unsigned int data_sz, void *user_priv, long deadline);
 
+void vpx_codec_send_metadata(vpx_codec_ctx_t *ctx, void*ptr);
+
 #endif // MY_VPX_DEFS_H_
diff --git a/system/codecs/c2/decoders/vpxdec/goldfish_vpx_impl.cpp b/system/codecs/c2/decoders/vpxdec/goldfish_vpx_impl.cpp
index d008efe..e1fa879 100644
--- a/system/codecs/c2/decoders/vpxdec/goldfish_vpx_impl.cpp
+++ b/system/codecs/c2/decoders/vpxdec/goldfish_vpx_impl.cpp
@@ -142,6 +142,17 @@
     return &(ctx->myImg);
 }
 
+void vpx_codec_send_metadata(vpx_codec_ctx_t *ctx, void *ptr) {
+    MetaDataColorAspects& meta = *(MetaDataColorAspects*)ptr;
+    auto transport = GoldfishMediaTransport::getInstance();
+    transport->writeParam(ctx->id, 0, ctx->address_offset);
+    transport->writeParam(meta.type, 1, ctx->address_offset);
+    transport->writeParam(meta.primaries, 2, ctx->address_offset);
+    transport->writeParam(meta.range, 3, ctx->address_offset);
+    transport->writeParam(meta.transfer, 4, ctx->address_offset);
+    sendVpxOperation(ctx, MediaOperation::SendMetadata);
+}
+
 int vpx_codec_flush(vpx_codec_ctx_t *ctx) {
     DDD("%s %d", __func__, __LINE__);
     if (!ctx) {
diff --git a/system/hwc2/Android.mk b/system/hwc2/Android.mk
index b53456c..f2c4f25 100644
--- a/system/hwc2/Android.mk
+++ b/system/hwc2/Android.mk
@@ -58,6 +58,7 @@
 emulator_hwcomposer_relative_path := hw
 
 emulator_hwcomposer2_src_files := \
+    ClientComposer.cpp \
     Common.cpp \
     Device.cpp \
     Display.cpp \
diff --git a/system/hwc2/ClientComposer.cpp b/system/hwc2/ClientComposer.cpp
new file mode 100644
index 0000000..211667b
--- /dev/null
+++ b/system/hwc2/ClientComposer.cpp
@@ -0,0 +1,144 @@
+/*
+ * 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 "ClientComposer.h"
+
+#include "Device.h"
+#include "Display.h"
+#include "Drm.h"
+#include "Layer.h"
+
+namespace android {
+
+ClientComposer::ClientComposer(DrmPresenter* drmPresenter)
+    : mDrmPresenter(drmPresenter) {}
+
+HWC2::Error ClientComposer::init() {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  return HWC2::Error::None;
+}
+
+HWC2::Error ClientComposer::onDisplayCreate(Display* display) {
+  const auto displayId = display->getId();
+  DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+  // Ensure created.
+  mDisplayInfos.emplace(displayId, DisplayInfo{});
+
+  return HWC2::Error::None;
+}
+
+HWC2::Error ClientComposer::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 HWC2::Error::BadDisplay;
+  }
+
+  mDisplayInfos.erase(it);
+
+  return HWC2::Error::None;
+}
+
+HWC2::Error ClientComposer::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 HWC2::Error::BadDisplay;
+  }
+
+  DisplayInfo& displayInfo = it->second;
+
+  auto clientTargetNativeBuffer = display->getClientTarget().getBuffer();
+  auto clientTargetDrmBuffer =
+    std::make_unique<DrmBuffer>(clientTargetNativeBuffer, mDrmPresenter);
+  if (!clientTargetDrmBuffer) {
+    ALOGE("%s: display:%" PRIu64 " failed to create client target drm buffer",
+          __FUNCTION__, displayId);
+    return HWC2::Error::NoResources;
+  }
+
+  displayInfo.clientTargetDrmBuffer = std::move(clientTargetDrmBuffer);
+
+  return HWC2::Error::None;
+}
+
+HWC2::Error ClientComposer::onActiveConfigChange(Display*) {
+  DEBUG_LOG("%s", __FUNCTION__);
+
+  return HWC2::Error::None;
+};
+
+HWC2::Error ClientComposer::validateDisplay(
+    Display* display, std::unordered_map<hwc2_layer_t, HWC2::Composition>* changes) {
+  const auto displayId = display->getId();
+  DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+  (void)displayId;
+
+  const std::vector<Layer*>& layers = display->getOrderedLayers();
+
+  for (Layer* layer : layers) {
+    const auto layerId = layer->getId();
+    const auto layerCompositionType = layer->getCompositionType();
+
+    if (layerCompositionType != HWC2::Composition::Client) {
+      (*changes)[layerId] = HWC2::Composition::Client;
+    }
+  }
+
+  return HWC2::Error::None;
+}
+
+std::tuple<HWC2::Error, base::unique_fd> ClientComposer::presentDisplay(
+    Display* display) {
+  ATRACE_CALL();
+
+  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 std::make_tuple(HWC2::Error::BadDisplay, base::unique_fd());
+  }
+
+  DisplayInfo& displayInfo = displayInfoIt->second;
+
+  auto clientTargetFence = display->getClientTarget().getFence();
+
+  auto [error, presentFence] =
+      displayInfo.clientTargetDrmBuffer->flushToDisplay(
+          static_cast<int>(displayId), clientTargetFence);
+  if (error != HWC2::Error::None) {
+    ALOGE("%s: display:%" PRIu64 " failed to flush drm buffer" PRIu64,
+          __FUNCTION__, displayId);
+    return std::make_tuple(error, base::unique_fd());
+  }
+
+  return std::make_tuple(HWC2::Error::None, std::move(presentFence));
+}
+
+}  // namespace android
\ No newline at end of file
diff --git a/system/hwc2/ClientComposer.h b/system/hwc2/ClientComposer.h
new file mode 100644
index 0000000..58d4cce
--- /dev/null
+++ b/system/hwc2/ClientComposer.h
@@ -0,0 +1,73 @@
+/*
+ * 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_CLIENTCOMPOSER_H
+#define ANDROID_HWC_CLIENTCOMPOSER_H
+
+#include <unordered_map>
+
+#include "Common.h"
+#include "Composer.h"
+#include "Display.h"
+#include "DrmPresenter.h"
+#include "Layer.h"
+
+namespace android {
+
+class ClientComposer : public Composer {
+ public:
+  ClientComposer(DrmPresenter* drmPresenter);
+
+  ClientComposer(const ClientComposer&) = delete;
+  ClientComposer& operator=(const ClientComposer&) = delete;
+
+  ClientComposer(ClientComposer&&) = delete;
+  ClientComposer& operator=(ClientComposer&&) = delete;
+
+  HWC2::Error init() override;
+
+  HWC2::Error onDisplayCreate(Display*) override;
+
+  HWC2::Error onDisplayDestroy(Display*) override;
+
+  HWC2::Error onDisplayClientTargetSet(Display*) override;
+
+  HWC2::Error onActiveConfigChange(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.
+  HWC2::Error validateDisplay(
+      Display* display, std::unordered_map<hwc2_layer_t, HWC2::Composition>*
+                            outLayerCompositionChanges) override;
+
+  // Performs the actual composition of layers and presents the composed result
+  // to the display.
+  std::tuple<HWC2::Error, base::unique_fd> presentDisplay(
+      Display* display) override;
+
+ private:
+  struct DisplayInfo {
+    std::unique_ptr<DrmBuffer> clientTargetDrmBuffer;
+  };
+
+  std::unordered_map<int64_t, DisplayInfo> mDisplayInfos;
+
+  DrmPresenter* mDrmPresenter = nullptr;
+};
+
+}  // namespace android
+
+#endif
\ No newline at end of file
diff --git a/system/hwc2/Common.cpp b/system/hwc2/Common.cpp
index 3e465c2..021d032 100644
--- a/system/hwc2/Common.cpp
+++ b/system/hwc2/Common.cpp
@@ -31,3 +31,7 @@
 bool IsNoOpMode() {
   return android::base::GetProperty("ro.vendor.hwcomposer.mode", "") == "noop";
 }
+
+bool IsClientCompositionMode() {
+  return android::base::GetProperty("ro.vendor.hwcomposer.mode", "") == "client";
+}
diff --git a/system/hwc2/Common.h b/system/hwc2/Common.h
index b0e0770..f13553b 100644
--- a/system/hwc2/Common.h
+++ b/system/hwc2/Common.h
@@ -45,5 +45,6 @@
 bool IsCuttlefish();
 bool IsCuttlefishFoldable();
 bool IsNoOpMode();
+bool IsClientCompositionMode();
 
 #endif
diff --git a/system/hwc2/Device.cpp b/system/hwc2/Device.cpp
index 1be15b3..f49905d 100644
--- a/system/hwc2/Device.cpp
+++ b/system/hwc2/Device.cpp
@@ -18,6 +18,7 @@
 
 #include <android-base/properties.h>
 
+#include "ClientComposer.h"
 #include "DisplayFinder.h"
 #include "GuestComposer.h"
 #include "HostComposer.h"
@@ -78,6 +79,9 @@
   if (IsNoOpMode()) {
     DEBUG_LOG("%s: using NoOpComposer", __FUNCTION__);
     mComposer = std::make_unique<NoOpComposer>();
+  } else if (IsClientCompositionMode()) {
+    DEBUG_LOG("%s: using ClientComposer", __FUNCTION__);
+    mComposer = std::make_unique<ClientComposer>(mDrmPresenter.get());
   } else if (ShouldUseGuestComposer()) {
     DEBUG_LOG("%s: using GuestComposer", __FUNCTION__);
     mComposer = std::make_unique<GuestComposer>(mDrmPresenter.get());
diff --git a/system/hwc2/DisplayFinder.cpp b/system/hwc2/DisplayFinder.cpp
index a55a49d..2016c4b 100644
--- a/system/hwc2/DisplayFinder.cpp
+++ b/system/hwc2/DisplayFinder.cpp
@@ -110,7 +110,7 @@
           rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_HEIGHT),  //
           rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_XDPI),    //
           rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_YDPI),    //
-          getVsyncForDisplay(drmPresenter, configId)                      //
+          getVsyncForDisplay(drmPresenter, display.displayId)             //
           ));
     }
   } else {
diff --git a/system/hwc2/HostComposer.cpp b/system/hwc2/HostComposer.cpp
index 5bf324e..472e782 100644
--- a/system/hwc2/HostComposer.cpp
+++ b/system/hwc2/HostComposer.cpp
@@ -570,7 +570,7 @@
     display->clearReleaseFencesAndIdsLocked();
 
     if (numLayer == 0) {
-      ALOGW(
+      ALOGV(
           "%s display has no layers to compose, flushing client target buffer.",
           __FUNCTION__);
 
diff --git a/system/hwc3/ClientFrameComposer.cpp b/system/hwc3/ClientFrameComposer.cpp
index d7f8b55..284a46f 100644
--- a/system/hwc3/ClientFrameComposer.cpp
+++ b/system/hwc3/ClientFrameComposer.cpp
@@ -146,6 +146,11 @@
   }
 
   DisplayInfo& displayInfo = displayInfoIt->second;
+  if (!displayInfo.clientTargetDrmBuffer) {
+    ALOGW("%s: display:%" PRIu64 " no client target set, nothing to present.",
+          __FUNCTION__, displayId);
+    return HWC3::Error::None;
+  }
 
   ::android::base::unique_fd fence = display->getClientTarget().getFence();
 
diff --git a/system/hwc3/ClientFrameComposer.h b/system/hwc3/ClientFrameComposer.h
index 3fb0ba8..28199b3 100644
--- a/system/hwc3/ClientFrameComposer.h
+++ b/system/hwc3/ClientFrameComposer.h
@@ -64,9 +64,13 @@
       std::unordered_map<int64_t, ::android::base::unique_fd>* outLayerFences)
       override;
 
+  const DrmPresenter* getDrmPresenter() const override {
+    return &mDrmPresenter;
+  }
+
  private:
   struct DisplayInfo {
-    std::unique_ptr<DrmBuffer> clientTargetDrmBuffer;;
+    std::shared_ptr<DrmBuffer> clientTargetDrmBuffer;
   };
 
   std::unordered_map<int64_t, DisplayInfo> mDisplayInfos;
diff --git a/system/hwc3/Common.cpp b/system/hwc3/Common.cpp
index 497f764..e9ac507 100644
--- a/system/hwc3/Common.cpp
+++ b/system/hwc3/Common.cpp
@@ -30,18 +30,34 @@
              std::string::npos;
 }
 
-bool IsNoOpMode() {
+bool IsInNoOpCompositionMode() {
   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() {
+bool IsInClientCompositionMode() {
   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";
 }
 
+bool IsInNoOpDisplayFinderMode() {
+  const std::string mode =
+    ::android::base::GetProperty("ro.vendor.hwcomposer.display_finder_mode", "");
+  DEBUG_LOG("%s: sysprop ro.vendor.hwcomposer.display_finder_mode is %s",
+            __FUNCTION__, mode.c_str());
+  return mode == "noop";
+}
+
+bool IsInDrmDisplayFinderMode() {
+  const std::string mode =
+    ::android::base::GetProperty("ro.vendor.hwcomposer.display_finder_mode", "");
+  DEBUG_LOG("%s: sysprop ro.vendor.hwcomposer.display_finder_mode is %s",
+            __FUNCTION__, mode.c_str());
+  return mode == "drm";
+}
+
 std::string toString(HWC3::Error error) {
   switch (error) {
     case HWC3::Error::None:
diff --git a/system/hwc3/Common.h b/system/hwc3/Common.h
index 5b6587e..493e5d0 100644
--- a/system/hwc3/Common.h
+++ b/system/hwc3/Common.h
@@ -44,8 +44,12 @@
 
 bool IsCuttlefish();
 bool IsCuttlefishFoldable();
-bool IsNoOpMode();
-bool IsClientCompositionMode();
+
+bool IsInNoOpCompositionMode();
+bool IsInClientCompositionMode();
+
+bool IsInNoOpDisplayFinderMode();
+bool IsInDrmDisplayFinderMode();
 
 namespace HWC3 {
 enum class Error : int32_t {
diff --git a/system/hwc3/ComposerClient.cpp b/system/hwc3/ComposerClient.cpp
index db05383..2098435 100644
--- a/system/hwc3/ComposerClient.cpp
+++ b/system/hwc3/ComposerClient.cpp
@@ -1237,7 +1237,7 @@
 
   std::vector<DisplayMultiConfigs> displays;
 
-  HWC3::Error error = findDisplays(displays);
+  HWC3::Error error = findDisplays(mComposer->getDrmPresenter(), &displays);
   if (error != HWC3::Error::None) {
     ALOGE("%s failed to find display configs", __FUNCTION__);
     return error;
diff --git a/system/hwc3/Device.cpp b/system/hwc3/Device.cpp
index cf4be42..894d48b 100644
--- a/system/hwc3/Device.cpp
+++ b/system/hwc3/Device.cpp
@@ -94,10 +94,10 @@
   std::unique_lock<std::mutex> lock(mMutex);
 
   if (mComposer == nullptr) {
-    if (IsNoOpMode()) {
+    if (IsInNoOpCompositionMode()) {
       DEBUG_LOG("%s: using NoOpFrameComposer", __FUNCTION__);
       mComposer = std::make_unique<NoOpFrameComposer>();
-    } else if (IsClientCompositionMode()) {
+    } else if (IsInClientCompositionMode()) {
       DEBUG_LOG("%s: using ClientFrameComposer", __FUNCTION__);
       mComposer = std::make_unique<ClientFrameComposer>();
     } else if (shouldUseGuestComposer()) {
diff --git a/system/hwc3/DisplayFinder.cpp b/system/hwc3/DisplayFinder.cpp
index 8e252cb..10bd5fd 100644
--- a/system/hwc3/DisplayFinder.cpp
+++ b/system/hwc3/DisplayFinder.cpp
@@ -31,7 +31,7 @@
   return 1000 * 1000 * 1000 / hertz;
 }
 
-HWC3::Error findCuttlefishDisplays(std::vector<DisplayMultiConfigs>& displays) {
+HWC3::Error findCuttlefishDisplays(std::vector<DisplayMultiConfigs>* outDisplays) {
   DEBUG_LOG("%s", __FUNCTION__);
 
   // TODO: replace with initializing directly from DRM info.
@@ -55,7 +55,7 @@
                               vsyncPeriodNanos),
             },
     };
-    displays.push_back(display);
+    outDisplays->push_back(display);
     ++displayId;
   }
 
@@ -79,7 +79,7 @@
 }
 
 HWC3::Error findGoldfishPrimaryDisplay(
-    std::vector<DisplayMultiConfigs>& displays) {
+    std::vector<DisplayMultiConfigs>* outDisplays) {
   DEBUG_LOG("%s", __FUNCTION__);
 
   DEFINE_AND_VALIDATE_HOST_CONNECTION
@@ -118,13 +118,13 @@
   }
   hostCon->unlock();
 
-  displays.push_back(display);
+  outDisplays->push_back(display);
 
   return HWC3::Error::None;
 }
 
 HWC3::Error findGoldfishSecondaryDisplays(
-    std::vector<DisplayMultiConfigs>& displays) {
+    std::vector<DisplayMultiConfigs>* outDisplays) {
   DEBUG_LOG("%s", __FUNCTION__);
 
   static constexpr const char kExternalDisplayProp[] =
@@ -170,7 +170,7 @@
         /*dpiYh=*/propIntParts[3],               //
         /*vsyncPeriod=*/HertzToPeriodNanos(160)  //
         ));
-    displays.push_back(display);
+    outDisplays->push_back(display);
 
     ++secondaryDisplayId;
 
@@ -180,14 +180,14 @@
   return HWC3::Error::None;
 }
 
-HWC3::Error findGoldfishDisplays(std::vector<DisplayMultiConfigs>& displays) {
-  HWC3::Error error = findGoldfishPrimaryDisplay(displays);
+HWC3::Error findGoldfishDisplays(std::vector<DisplayMultiConfigs>* outDisplays) {
+  HWC3::Error error = findGoldfishPrimaryDisplay(outDisplays);
   if (error != HWC3::Error::None) {
     ALOGE("%s failed to find Goldfish primary display", __FUNCTION__);
     return error;
   }
 
-  error = findGoldfishSecondaryDisplays(displays);
+  error = findGoldfishSecondaryDisplays(outDisplays);
   if (error != HWC3::Error::None) {
     ALOGE("%s failed to find Goldfish secondary displays", __FUNCTION__);
   }
@@ -197,8 +197,8 @@
 
 // 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{
+HWC3::Error findNoOpDisplays(std::vector<DisplayMultiConfigs>* outDisplays) {
+  outDisplays->push_back(DisplayMultiConfigs{
       .displayId = 0,
       .activeConfigId = 0,
       .configs = {DisplayConfig(0,
@@ -213,16 +213,54 @@
   return HWC3::Error::None;
 }
 
+HWC3::Error findDrmDisplays(const DrmPresenter& drm,
+                            std::vector<DisplayMultiConfigs>* outDisplays) {
+  outDisplays->clear();
+
+  std::vector<DrmPresenter::DisplayConfig> drmDisplayConfigs;
+
+  HWC3::Error error = drm.getDisplayConfigs(&drmDisplayConfigs);
+  if (error != HWC3::Error::None) {
+    ALOGE("%s failed to find displays from DRM.", __FUNCTION__);
+    return error;
+  }
+
+  for (const DrmPresenter::DisplayConfig drmDisplayConfig : drmDisplayConfigs) {
+    outDisplays->push_back(DisplayMultiConfigs{
+      .displayId = drmDisplayConfig.id,
+      .activeConfigId = static_cast<int32_t>(drmDisplayConfig.id),
+      .configs = {
+        DisplayConfig(static_cast<int32_t>(drmDisplayConfig.id),
+                      drmDisplayConfig.width,
+                      drmDisplayConfig.height,
+                      drmDisplayConfig.dpiX,
+                      drmDisplayConfig.dpiY,
+                      HertzToPeriodNanos(drmDisplayConfig.refreshRateHz)),
+      },
+    });
+  }
+
+  return HWC3::Error::None;
+}
+
 }  // namespace
 
-HWC3::Error findDisplays(std::vector<DisplayMultiConfigs>& displays) {
+HWC3::Error findDisplays(const DrmPresenter* drm,
+                         std::vector<DisplayMultiConfigs>* outDisplays) {
   HWC3::Error error = HWC3::Error::None;
-  if (IsNoOpMode()) {
-    error = findNoOpDisplays(displays);
+  if (IsInNoOpCompositionMode()) {
+    error = findNoOpDisplays(outDisplays);
+  } else if (IsInDrmDisplayFinderMode()) {
+    if (drm == nullptr) {
+      ALOGE("%s asked to find displays from DRM, but DRM not available.",
+            __FUNCTION__);
+      return HWC3::Error::NoResources;
+    }
+    error = findDrmDisplays(*drm, outDisplays);
   } else if (IsCuttlefish()) {
-    error = findCuttlefishDisplays(displays);
+    error = findCuttlefishDisplays(outDisplays);
   } else {
-    error = findGoldfishDisplays(displays);
+    error = findGoldfishDisplays(outDisplays);
   }
 
   if (error != HWC3::Error::None) {
@@ -230,7 +268,7 @@
     return error;
   }
 
-  for (auto& display : displays) {
+  for (auto& display : *outDisplays) {
     DisplayConfig::addConfigGroups(&display.configs);
   }
 
diff --git a/system/hwc3/DisplayFinder.h b/system/hwc3/DisplayFinder.h
index 8197a82..171d204 100644
--- a/system/hwc3/DisplayFinder.h
+++ b/system/hwc3/DisplayFinder.h
@@ -17,10 +17,12 @@
 #ifndef ANDROID_HWC_DISPLAYFINDER_H
 #define ANDROID_HWC_DISPLAYFINDER_H
 
+#include <optional>
 #include <vector>
 
 #include "Common.h"
 #include "DisplayConfig.h"
+#include "DrmPresenter.h"
 
 namespace aidl::android::hardware::graphics::composer3::impl {
 
@@ -31,7 +33,8 @@
   std::vector<DisplayConfig> configs;
 };
 
-HWC3::Error findDisplays(std::vector<DisplayMultiConfigs>& displays);
+HWC3::Error findDisplays(const DrmPresenter* drm,
+                         std::vector<DisplayMultiConfigs>* outDisplays);
 
 }  // namespace aidl::android::hardware::graphics::composer3::impl
 
diff --git a/system/hwc3/DrmPresenter.cpp b/system/hwc3/DrmPresenter.cpp
index 6e13209..b512277 100644
--- a/system/hwc3/DrmPresenter.cpp
+++ b/system/hwc3/DrmPresenter.cpp
@@ -80,6 +80,18 @@
       ALOGE("%s: Failed to initialize DRM backend", __FUNCTION__);
       return HWC3::Error::NoResources;
     }
+
+    constexpr const std::size_t kCachedBuffersPerDisplay = 3;
+    std::size_t numDisplays = 0;
+    for (const DrmConnector& connector : mConnectors) {
+      if (connector.connection == DRM_MODE_CONNECTED) {
+        ++numDisplays;
+      }
+    }
+    const std::size_t bufferCacheSize = kCachedBuffersPerDisplay * numDisplays;
+    DEBUG_LOG("%s: initializing DRM buffer cache to size %zu",
+              __FUNCTION__, bufferCacheSize);
+    mBufferCache = std::make_unique<DrmBufferCache>(bufferCacheSize);
   }
 
   mDrmEventListener = ::android::sp<DrmEventListener>::make(*this);
@@ -93,6 +105,31 @@
   return HWC3::Error::None;
 }
 
+HWC3::Error DrmPresenter::getDisplayConfigs(std::vector<DisplayConfig>* configs) const {
+  AutoReadLock lock(mStateMutex);
+
+  configs->clear();
+
+  for (uint32_t i = 0; i < mConnectors.size(); i++) {
+    const auto& connector = mConnectors[i];
+
+    if (connector.connection != DRM_MODE_CONNECTED) {
+      continue;
+    }
+
+    configs->emplace_back(DisplayConfig{
+        .id = i,
+        .width = connector.mMode.hdisplay,
+        .height = connector.mMode.vdisplay,
+        .dpiX = 160, //static_cast<uint32_t>(connector.dpiX),
+        .dpiY = 160, //static_cast<uint32_t>(connector.dpiY),
+        .refreshRateHz = connector.mRefreshRateAsInteger,
+    });
+  }
+
+  return HWC3::Error::None;
+}
+
 HWC3::Error DrmPresenter::registerOnHotplugCallback(const HotplugCallback& cb) {
   mHotplugCallback = cb;
   return HWC3::Error::None;
@@ -322,54 +359,59 @@
   mPlanes.clear();
 }
 
-std::tuple<HWC3::Error, std::unique_ptr<DrmBuffer>> DrmPresenter::create(
+std::tuple<HWC3::Error, std::shared_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>());
+    return std::make_tuple(HWC3::Error::NoResources, nullptr);
   }
 
+  DrmPrimeBufferHandle primeHandle = 0;
+  int ret = drmPrimeFDToHandle(mFd.get(), crosHandle->fds[0], &primeHandle);
+  if (ret) {
+    ALOGE("%s: drmPrimeFDToHandle failed: %s (errno %d)", __FUNCTION__,
+          strerror(errno), errno);
+    return std::make_tuple(HWC3::Error::NoResources, nullptr);
+  }
+
+  auto drmBufferPtr = mBufferCache->get(primeHandle);
+  if (drmBufferPtr != nullptr) {
+    return std::make_tuple(HWC3::Error::None,
+                           std::shared_ptr<DrmBuffer>(*drmBufferPtr));
+  }
+
+  auto buffer = std::shared_ptr<DrmBuffer>(new DrmBuffer(*this));
   buffer->mWidth = crosHandle->width;
   buffer->mHeight = crosHandle->height;
   buffer->mDrmFormat = crosHandle->format;
   buffer->mPlaneFds[0] = crosHandle->fds[0];
+  buffer->mPlaneHandles[0] = primeHandle;
   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);
+  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;
+    return std::make_tuple(HWC3::Error::NoResources, nullptr);
   }
-
   DEBUG_LOG("%s: created framebuffer:%" PRIu32, __FUNCTION__, framebuffer);
+  buffer->mDrmFramebuffer = framebuffer;
 
-  buffer->mDrmFramebuffer.emplace(framebuffer);
-  return HWC3::Error::None;
+  mBufferCache->set(primeHandle, std::shared_ptr<DrmBuffer>(buffer));
+
+  return std::make_tuple(HWC3::Error::None,
+                         std::shared_ptr<DrmBuffer>(buffer));
 }
 
 HWC3::Error DrmPresenter::destroyDrmFramebuffer(DrmBuffer* buffer) {
@@ -391,6 +433,8 @@
             strerror(errno), errno);
       return HWC3::Error::NoResources;
     }
+
+    mBufferCache->remove(buffer->mPlaneHandles[0]);
   }
 
   return HWC3::Error::None;
diff --git a/system/hwc3/DrmPresenter.h b/system/hwc3/DrmPresenter.h
index 61e546a..72e3f5a 100644
--- a/system/hwc3/DrmPresenter.h
+++ b/system/hwc3/DrmPresenter.h
@@ -29,6 +29,7 @@
 #include <vector>
 
 #include "Common.h"
+#include "LruCache.h"
 #include "android/base/synchronization/AndroidLock.h"
 
 namespace aidl::android::hardware::graphics::composer3::impl {
@@ -60,7 +61,6 @@
   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;
 };
 
@@ -77,6 +77,17 @@
 
   HWC3::Error init();
 
+  struct DisplayConfig {
+    uint32_t id;
+    uint32_t width;
+    uint32_t height;
+    uint32_t dpiX;
+    uint32_t dpiY;
+    uint32_t refreshRateHz;
+  };
+
+  HWC3::Error getDisplayConfigs(std::vector<DisplayConfig>* configs) const;
+
   using HotplugCallback = std::function<void(bool /*connected*/,   //
                                              uint32_t /*id*/,      //
                                              uint32_t /*width*/,   //
@@ -90,7 +101,7 @@
 
   uint32_t refreshRate() const { return mConnectors[0].mRefreshRateAsInteger; }
 
-  std::tuple<HWC3::Error, std::unique_ptr<DrmBuffer>> create(
+  std::tuple<HWC3::Error, std::shared_ptr<DrmBuffer>> create(
       const native_handle_t* handle);
 
   std::tuple<HWC3::Error, ::android::base::unique_fd> flushToDisplay(
@@ -100,9 +111,13 @@
   std::optional<std::vector<uint8_t>> getEdid(uint32_t id);
 
  private:
-  // Grant visibility for createDrmFramebuffer and clearDrmFB to DrmBuffer.
+  // TODO: make this cache per display when enabling hotplug support.
+  using DrmPrimeBufferHandle = uint32_t;
+  using DrmBufferCache = LruCache<DrmPrimeBufferHandle, std::shared_ptr<DrmBuffer>>;
+  std::unique_ptr<DrmBufferCache> mBufferCache;
+
+  // Grant visibility to destroyDrmFramebuffer to DrmBuffer.
   friend class DrmBuffer;
-  HWC3::Error createDrmFramebuffer(DrmBuffer* buffer);
   HWC3::Error destroyDrmFramebuffer(DrmBuffer* buffer);
 
   // Grant visibility for handleHotplug to DrmEventListener.
@@ -117,7 +132,7 @@
   std::optional<HotplugCallback> mHotplugCallback;
 
   // Protects access to the below drm structs.
-  ::android::base::guest::ReadWriteLock mStateMutex;
+  mutable ::android::base::guest::ReadWriteLock mStateMutex;
 
   struct DrmPlane {
     uint32_t mId = -1;
diff --git a/system/hwc3/FrameComposer.h b/system/hwc3/FrameComposer.h
index fdb6db3..794855b 100644
--- a/system/hwc3/FrameComposer.h
+++ b/system/hwc3/FrameComposer.h
@@ -26,6 +26,7 @@
 
 #include "Common.h"
 #include "DisplayChanges.h"
+#include "DrmPresenter.h"
 
 namespace aidl::android::hardware::graphics::composer3::impl {
 
@@ -68,6 +69,10 @@
           outLayerFences) = 0;
 
   virtual HWC3::Error onActiveConfigChange(Display* display) = 0;
+
+  virtual const DrmPresenter* getDrmPresenter() const {
+    return nullptr;
+  }
 };
 
 }  // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/GuestFrameComposer.h b/system/hwc3/GuestFrameComposer.h
index af46b3d..e37ff87 100644
--- a/system/hwc3/GuestFrameComposer.h
+++ b/system/hwc3/GuestFrameComposer.h
@@ -62,6 +62,10 @@
 
   HWC3::Error onActiveConfigChange(Display* /*display*/) override;
 
+  const DrmPresenter* getDrmPresenter() const override {
+    return &mDrmPresenter;
+  }
+
  private:
   struct DisplayConfig {
     int width;
@@ -91,7 +95,7 @@
     // Additional per display buffer for the composition result.
     buffer_handle_t compositionResultBuffer = nullptr;
 
-    std::unique_ptr<DrmBuffer> compositionResultDrmBuffer;
+    std::shared_ptr<DrmBuffer> compositionResultDrmBuffer;
   };
 
   std::unordered_map<int64_t, DisplayInfo> mDisplayInfos;
diff --git a/system/hwc3/HostFrameComposer.cpp b/system/hwc3/HostFrameComposer.cpp
index 7b090c2..f976e05 100644
--- a/system/hwc3/HostFrameComposer.cpp
+++ b/system/hwc3/HostFrameComposer.cpp
@@ -578,7 +578,7 @@
               displayId, static_cast<int>(layers.size()));
 
     if (numLayer == 0) {
-      ALOGW(
+      ALOGV(
           "%s display has no layers to compose, flushing client target buffer.",
           __FUNCTION__);
 
diff --git a/system/hwc3/HostFrameComposer.h b/system/hwc3/HostFrameComposer.h
index e99e6f2..42eb43a 100644
--- a/system/hwc3/HostFrameComposer.h
+++ b/system/hwc3/HostFrameComposer.h
@@ -65,6 +65,13 @@
 
   HWC3::Error onActiveConfigChange(Display* display) override;
 
+  const DrmPresenter* getDrmPresenter() const override {
+    if (mDrmPresenter) {
+      return &*mDrmPresenter;
+    }
+    return nullptr;
+  }
+
  private:
   HWC3::Error createHostComposerDisplayInfo(Display* display,
                                             uint32_t hostDisplayId);
@@ -85,10 +92,10 @@
     const native_handle_t* compositionResultBuffer = nullptr;
 
     // Drm info for the additional composition result buffer.
-    std::unique_ptr<DrmBuffer> compositionResultDrmBuffer;
+    std::shared_ptr<DrmBuffer> compositionResultDrmBuffer;
 
     // Drm info for the displays client target buffer.
-    std::unique_ptr<DrmBuffer> clientTargetDrmBuffer;
+    std::shared_ptr<DrmBuffer> clientTargetDrmBuffer;
   };
 
   std::unordered_map<int64_t, HostComposerDisplayInfo> mDisplayInfos;
diff --git a/system/hwc3/LruCache.h b/system/hwc3/LruCache.h
new file mode 100644
index 0000000..9ffca46
--- /dev/null
+++ b/system/hwc3/LruCache.h
@@ -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.
+
+#pragma once
+
+#include <list>
+#include <unordered_map>
+
+template <typename Key, typename Value>
+class LruCache {
+  public:
+    LruCache(std::size_t maxSize) : m_maxSize(maxSize) {
+        m_table.reserve(maxSize);
+    }
+
+    Value* get(const Key& key) {
+        auto tableIt = m_table.find(key);
+        if (tableIt == m_table.end()) {
+            return nullptr;
+        }
+
+        // Move to front.
+        auto elementsIt = tableIt->second;
+        m_elements.splice(elementsIt, m_elements, m_elements.begin());
+        return &elementsIt->value;
+    }
+
+    void set(const Key& key, Value&& value) {
+        auto tableIt = m_table.find(key);
+        if (tableIt == m_table.end()) {
+            if (m_table.size() >= m_maxSize) {
+                auto& kv = m_elements.back();
+                m_table.erase(kv.key);
+                m_elements.pop_back();
+            }
+        } else {
+            auto elementsIt = tableIt->second;
+            m_elements.erase(elementsIt);
+        }
+        m_elements.emplace_front(KeyValue{
+            key,
+            std::forward<Value>(value),
+        });
+        m_table[key] = m_elements.begin();
+    }
+
+    void remove(const Key& key) {
+        auto tableIt = m_table.find(key);
+        if (tableIt == m_table.end()) {
+            return;
+        }
+        auto elementsIt = tableIt->second;
+        m_elements.erase(elementsIt);
+        m_table.erase(tableIt);
+    }
+
+    void clear() {
+        m_elements.clear();
+        m_table.clear();
+    }
+
+  private:
+    struct KeyValue {
+        Key key;
+        Value value;
+    };
+
+    const std::size_t m_maxSize;
+    // Front is the most recently used and back is the least recently used.
+    std::list<KeyValue> m_elements;
+    std::unordered_map<Key, typename std::list<KeyValue>::iterator> m_table;
+};
diff --git a/system/vulkan_enc/ResourceTracker.cpp b/system/vulkan_enc/ResourceTracker.cpp
index 6cc7010..c9a1606 100644
--- a/system/vulkan_enc/ResourceTracker.cpp
+++ b/system/vulkan_enc/ResourceTracker.cpp
@@ -6690,7 +6690,7 @@
                     }
 #endif
 #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__)
-                    if (semInfo.syncFd >= 0) {
+                    if (semInfo.syncFd != 0) {
                         pre_signal_sync_fds.push_back(semInfo.syncFd);
                         pre_signal_semaphores.push_back(pSubmits[i].pWaitSemaphores[j]);
                     }
@@ -6755,13 +6755,19 @@
 #endif
 #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__)
             for (auto fd : pre_signal_sync_fds) {
-                preSignalTasks.push_back([fd] {
-                    sync_wait(fd, 3000);
-                });
+                // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImportSemaphoreFdInfoKHR.html
+                // fd == -1 is treated as already signaled
+                if (fd != -1) {
+                    preSignalTasks.push_back([fd] {
+                        sync_wait(fd, 3000);
+                    });
+                }
             }
 #endif
-            auto waitGroupHandle = mWorkPool.schedule(preSignalTasks);
-            mWorkPool.waitAll(waitGroupHandle);
+            if (!preSignalTasks.empty()) {
+                auto waitGroupHandle = mWorkPool.schedule(preSignalTasks);
+                mWorkPool.waitAll(waitGroupHandle);
+            }
 
             VkSubmitInfo submit_info = {
                 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,