Merge "APC: Add possible deadlock fix when creating track" into main
diff --git a/media/codec2/fuzzer/C2Fuzzer.cpp b/media/codec2/fuzzer/C2Fuzzer.cpp
index d6793e0..719962d 100644
--- a/media/codec2/fuzzer/C2Fuzzer.cpp
+++ b/media/codec2/fuzzer/C2Fuzzer.cpp
@@ -239,6 +239,7 @@
 }
 
 void Codec2Fuzzer::decodeFrames(const uint8_t* data, size_t size) {
+  static const size_t kPageSize = getpagesize();
   std::unique_ptr<BufferSource> bufferSource = std::make_unique<BufferSource>(data, size);
   if (!bufferSource) {
     return;
@@ -270,7 +271,7 @@
     work->input.ordinal.timestamp = 0;
     work->input.ordinal.frameIndex = ++numFrames;
     work->input.buffers.clear();
-    int32_t alignedSize = C2FUZZER_ALIGN(frameSize, PAGE_SIZE);
+    int32_t alignedSize = C2FUZZER_ALIGN(frameSize, kPageSize);
 
     std::shared_ptr<C2LinearBlock> block;
     status = mLinearPool->fetchLinearBlock(
diff --git a/media/codec2/hal/client/Android.bp b/media/codec2/hal/client/Android.bp
index 1a2377c..7a0525b 100644
--- a/media/codec2/hal/client/Android.bp
+++ b/media/codec2/hal/client/Android.bp
@@ -23,6 +23,7 @@
     name: "libcodec2_client",
 
     srcs: [
+        "GraphicsTracker.cpp",
         "client.cpp",
         "output.cpp",
     ],
@@ -50,6 +51,7 @@
         "libgui",
         "libhidlbase",
         "liblog",
+        "libnativewindow",
         "libstagefright_bufferpool@2.0.1",
         "libui",
         "libutils",
diff --git a/media/codec2/hal/client/GraphicsTracker.cpp b/media/codec2/hal/client/GraphicsTracker.cpp
new file mode 100644
index 0000000..5a2cb86
--- /dev/null
+++ b/media/codec2/hal/client/GraphicsTracker.cpp
@@ -0,0 +1,836 @@
+/*
+ * Copyright (C) 2023 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 <sys/eventfd.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <private/android/AHardwareBufferHelpers.h>
+#include <vndk/hardware_buffer.h>
+
+#include <codec2/aidl/GraphicsTracker.h>
+
+namespace aidl::android::hardware::media::c2::implementation {
+
+namespace {
+
+c2_status_t retrieveAHardwareBufferId(const C2ConstGraphicBlock &blk, uint64_t *bid) {
+    // TODO
+    (void)blk;
+    (void)bid;
+    return C2_OK;
+}
+
+} // anonymous namespace
+
+GraphicsTracker::BufferItem::BufferItem(
+        uint32_t generation, int slot, const sp<GraphicBuffer>& buf, const sp<Fence>& fence) :
+        mInit{false}, mGeneration{generation}, mSlot{slot} {
+    if (!buf) {
+        return;
+    }
+    AHardwareBuffer *pBuf = AHardwareBuffer_from_GraphicBuffer(buf.get());
+    int ret = AHardwareBuffer_getId(pBuf, &mId);
+    if (ret != ::android::OK) {
+        return;
+    }
+    mUsage = buf->getUsage();
+    AHardwareBuffer_acquire(pBuf);
+    mBuf = pBuf;
+    mFence = fence;
+    mInit = true;
+}
+
+GraphicsTracker::BufferItem::BufferItem(
+        uint32_t generation,
+        AHardwareBuffer_Desc *desc, AHardwareBuffer *pBuf) :
+        mInit{true}, mGeneration{generation}, mSlot{-1},
+        mBuf{pBuf}, mUsage{::android::AHardwareBuffer_convertToGrallocUsageBits(desc->usage)},
+        mFence{Fence::NO_FENCE} {
+}
+
+GraphicsTracker::BufferItem::~BufferItem() {
+    if (mInit) {
+        AHardwareBuffer_release(mBuf);
+    }
+}
+
+sp<GraphicBuffer> GraphicsTracker::BufferItem::updateBuffer(
+        uint64_t newUsage, uint32_t newGeneration) {
+    if (!mInit) {
+        return nullptr;
+    }
+    newUsage |= mUsage;
+    uint64_t ahbUsage = ::android::AHardwareBuffer_convertFromGrallocUsageBits(newUsage);
+    AHardwareBuffer_Desc desc;
+    AHardwareBuffer_describe(mBuf, &desc);
+    // TODO: we need well-established buffer migration features from graphics.
+    // (b/273776738)
+    desc.usage = ahbUsage;
+    const native_handle_t *handle = AHardwareBuffer_getNativeHandle(mBuf);
+    if (!handle) {
+        return nullptr;
+    }
+
+    AHardwareBuffer *newBuf;
+    int err = AHardwareBuffer_createFromHandle(&desc, handle,
+                                     AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE,
+                                     &newBuf);
+    if (err != ::android::NO_ERROR) {
+        return nullptr;
+    }
+
+    GraphicBuffer *gb = ::android::AHardwareBuffer_to_GraphicBuffer(newBuf);
+    if (!gb) {
+        AHardwareBuffer_release(newBuf);
+        return nullptr;
+    }
+
+    gb->setGenerationNumber(newGeneration);
+    mUsage = newUsage;
+    mGeneration = newGeneration;
+    AHardwareBuffer_release(mBuf);
+    // acquire is already done when creating.
+    mBuf = newBuf;
+    return gb;
+}
+
+void GraphicsTracker::BufferCache::waitOnSlot(int slot) {
+    // TODO: log
+    CHECK(0 <= slot && slot < kNumSlots);
+    BlockedSlot *p = &mBlockedSlots[slot];
+    std::unique_lock<std::mutex> l(p->l);
+    while (p->blocked) {
+        p->cv.wait(l);
+    }
+}
+
+void GraphicsTracker::BufferCache::blockSlot(int slot) {
+    CHECK(0 <= slot && slot < kNumSlots);
+    BlockedSlot *p = &mBlockedSlots[slot];
+    std::unique_lock<std::mutex> l(p->l);
+    p->blocked = true;
+}
+
+void GraphicsTracker::BufferCache::unblockSlot(int slot) {
+    CHECK(0 <= slot && slot < kNumSlots);
+    BlockedSlot *p = &mBlockedSlots[slot];
+    std::unique_lock<std::mutex> l(p->l);
+    p->blocked = false;
+    l.unlock();
+    p->cv.notify_one();
+}
+
+GraphicsTracker::GraphicsTracker(int maxDequeueCount)
+    : mMaxDequeue{maxDequeueCount}, mMaxDequeueRequested{maxDequeueCount},
+    mMaxDequeueCommitted{maxDequeueCount},
+    mMaxDequeueRequestedSeqId{0UL}, mMaxDequeueCommittedSeqId{0ULL},
+    mDequeueable{maxDequeueCount},
+    mTotalDequeued{0}, mTotalCancelled{0}, mTotalDropped{0}, mTotalReleased{0},
+    mInConfig{false}, mStopped{false} {
+    if (maxDequeueCount <= 0) {
+        mMaxDequeue = kDefaultMaxDequeue;
+        mMaxDequeueRequested = kDefaultMaxDequeue;
+        mMaxDequeueCommitted = kDefaultMaxDequeue;
+        mDequeueable = kDefaultMaxDequeue;
+    }
+    int allocEventFd = ::eventfd(mDequeueable, EFD_CLOEXEC | EFD_NONBLOCK | EFD_SEMAPHORE);
+    int statusEventFd = ::eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
+
+    mAllocEventFd.reset(allocEventFd);
+    mStopEventFd.reset(statusEventFd);
+
+    mEventQueueThread = std::thread([this](){processEvent();});
+
+    CHECK(allocEventFd >= 0 && statusEventFd >= 0);
+    CHECK(mEventQueueThread.joinable());
+}
+
+GraphicsTracker::~GraphicsTracker() {
+    stop();
+    if (mEventQueueThread.joinable()) {
+        std::unique_lock<std::mutex> l(mEventLock);
+        mStopEventThread = true;
+        l.unlock();
+        mEventCv.notify_one();
+        mEventQueueThread.join();
+    }
+}
+
+bool GraphicsTracker::adjustDequeueConfLocked(bool *updateDequeue) {
+    // TODO: can't we adjust during config? not committing it may safe?
+    *updateDequeue = false;
+    if (!mInConfig && mMaxDequeueRequested < mMaxDequeue) {
+        int delta = mMaxDequeue - mMaxDequeueRequested;
+        // Since we are supposed to increase mDequeuable by one already
+        int adjustable = mDequeueable + 1;
+        if (adjustable >= delta) {
+            mMaxDequeue = mMaxDequeueRequested;
+            mDequeueable -= (delta - 1);
+        } else {
+            mMaxDequeue -= adjustable;
+            mDequeueable = 0;
+        }
+        if (mMaxDequeueRequested == mMaxDequeue && mMaxDequeueRequested != mMaxDequeueCommitted) {
+            *updateDequeue = true;
+        }
+        return true;
+    }
+    return false;
+}
+
+c2_status_t GraphicsTracker::configureGraphics(
+        const sp<IGraphicBufferProducer>& igbp, uint32_t generation) {
+    std::shared_ptr<BufferCache> prevCache;
+    int prevDequeueCommitted;
+
+    std::unique_lock<std::mutex> cl(mConfigLock);
+    {
+        std::unique_lock<std::mutex> l(mLock);
+        mInConfig = true;
+        prevCache = mBufferCache;
+        prevDequeueCommitted = mMaxDequeueCommitted;
+    }
+    // NOTE: Switching to the same surface is blocked from MediaCodec.
+    // Switching to the same surface might not work if tried, since disconnect()
+    // to the old surface in MediaCodec and allocate from the new surface from
+    // GraphicsTracker cannot be synchronized properly.
+    uint64_t bqId{0ULL};
+    ::android::status_t ret = ::android::OK;
+    if (igbp) {
+        ret = igbp->getUniqueId(&bqId);
+    }
+    if (ret != ::android::OK || prevCache->mGeneration == generation || prevCache->mBqId == bqId) {
+        return C2_BAD_VALUE;
+    }
+    ret = igbp->setMaxDequeuedBufferCount(prevDequeueCommitted);
+    if (ret != ::android::OK) {
+        // TODO: sort out the error from igbp and return an error accordingly.
+        return C2_CORRUPTED;
+    }
+    std::shared_ptr<BufferCache> newCache = std::make_shared<BufferCache>(bqId, generation, igbp);
+    {
+        std::unique_lock<std::mutex> l(mLock);
+        mInConfig = false;
+        mBufferCache = newCache;
+    }
+    return C2_OK;
+}
+
+c2_status_t GraphicsTracker::configureMaxDequeueCount(int maxDequeueCount) {
+    std::shared_ptr<BufferCache> cache;
+
+    // max dequeue count which can be committed to IGBP.
+    // (Sometimes maxDequeueCount cannot be committed if the number of
+    // dequeued buffer count is bigger.)
+    int maxDequeueToCommit;
+    // max dequeue count which is committed to IGBP currently
+    // (actually mMaxDequeueCommitted, but needs to be read outside lock.)
+    int curMaxDequeueCommitted;
+    std::unique_lock<std::mutex> cl(mConfigLock);
+    {
+        std::unique_lock<std::mutex> l(mLock);
+        if (mMaxDequeueRequested == maxDequeueCount) {
+            return C2_OK;
+        }
+        mInConfig = true;
+        mMaxDequeueRequested = maxDequeueCount;
+        cache = mBufferCache;
+        curMaxDequeueCommitted = mMaxDequeueCommitted;
+        if (mMaxDequeue <= maxDequeueCount) {
+            maxDequeueToCommit = maxDequeueCount;
+        } else {
+            // Since mDequeuable is decreasing,
+            // a delievered ready to allocate event may not be fulfilled.
+            // Another waiting via a waitable object may be necessary in the case.
+            int delta = mMaxDequeue - maxDequeueCount;
+            if (delta <= mDequeueable) {
+                maxDequeueToCommit = maxDequeueCount;
+                mDequeueable -= delta;
+            } else {
+                maxDequeueToCommit = mMaxDequeue - mDequeueable;
+                mDequeueable = 0;
+            }
+        }
+    }
+
+    bool committed = true;
+    if (cache->mIgbp && maxDequeueToCommit != curMaxDequeueCommitted) {
+        ::android::status_t ret = cache->mIgbp->setMaxDequeuedBufferCount(maxDequeueToCommit);
+        committed = (ret == ::android::OK);
+        if (!committed) {
+            // This should not happen.
+            ALOGE("dequeueCount failed with error(%d)", (int)ret);
+        }
+    }
+
+    {
+        std::unique_lock<std::mutex> l(mLock);
+        mInConfig = false;
+        if (committed) {
+            mMaxDequeueCommitted = maxDequeueToCommit;
+            int delta = mMaxDequeueCommitted - mMaxDequeue;
+            if (delta > 0) {
+                mDequeueable += delta;
+                l.unlock();
+                writeIncDequeueable(delta);
+            }
+        }
+    }
+
+    if (!committed) {
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+void GraphicsTracker::updateDequeueConf() {
+    std::shared_ptr<BufferCache> cache;
+    int dequeueCommit;
+    std::unique_lock<std::mutex> cl(mConfigLock);
+    {
+        std::unique_lock<std::mutex> l(mLock);
+        if (mMaxDequeue == mMaxDequeueRequested && mMaxDequeueCommitted != mMaxDequeueRequested) {
+            dequeueCommit = mMaxDequeue;
+            mInConfig = true;
+            cache = mBufferCache;
+        } else {
+            return;
+        }
+    }
+    bool committed = true;
+    if (cache->mIgbp) {
+        ::android::status_t ret = cache->mIgbp->setMaxDequeuedBufferCount(dequeueCommit);
+        committed = (ret == ::android::OK);
+        if (!committed) {
+            // This should not happen.
+            ALOGE("dequeueCount failed with error(%d)", (int)ret);
+        }
+    }
+    int cleared = 0;
+    {
+        // cache == mCache here, since we locked config.
+        std::unique_lock<std::mutex> l(mLock);
+        mInConfig = false;
+        if (committed) {
+            if (cache->mIgbp && dequeueCommit < mMaxDequeueCommitted) {
+                // we are shrinking # of buffers, so clearing the cache.
+                for (auto it = cache->mBuffers.begin(); it != cache->mBuffers.end();) {
+                    uint64_t bid = it->second->mId;
+                    if (mDequeued.count(bid) == 0 || mDeallocating.count(bid) > 0) {
+                        ++cleared;
+                        it = cache->mBuffers.erase(it);
+                    } else {
+                        ++it;
+                    }
+                }
+            }
+            mMaxDequeueCommitted = dequeueCommit;
+        }
+    }
+    if (cleared > 0) {
+        ALOGD("%d buffers are cleared from cache, due to IGBP capacity change", cleared);
+    }
+
+}
+
+void GraphicsTracker::stop() {
+    bool expected = false;
+    bool updated = mStopped.compare_exchange_strong(expected, true);
+    if (updated) {
+        uint64_t val = 1ULL;
+        int ret = ::write(mStopEventFd.get(), &val, 8);
+        if (ret < 0) {
+            // EINTR maybe
+            std::unique_lock<std::mutex> l(mEventLock);
+            mStopRequest = true;
+            l.unlock();
+            mEventCv.notify_one();
+            ALOGW("stop() status update pending");
+        }
+    }
+}
+
+void GraphicsTracker::writeIncDequeueable(int inc) {
+    uint64_t val = inc;
+    int ret = ::write(mAllocEventFd.get(), &val, 8);
+    if (ret < 0) {
+        // EINTR due to signal handling maybe, this should be rare
+        std::unique_lock<std::mutex> l(mEventLock);
+        mIncDequeueable += inc;
+        l.unlock();
+        mEventCv.notify_one();
+        ALOGW("updating dequeueable to eventfd pending");
+    }
+}
+
+void GraphicsTracker::processEvent() {
+    // This is for write() failure of eventfds.
+    // write() failure other than EINTR should not happen.
+    int64_t acc = 0;
+    bool stopRequest = false;
+    bool stopCommitted = false;
+
+    while (true) {
+        {
+            std::unique_lock<std::mutex> l(mEventLock);
+            acc += mIncDequeueable;
+            mIncDequeueable = 0;
+            stopRequest |= mStopRequest;
+            mStopRequest = false;
+            if (acc == 0 && stopRequest == stopCommitted) {
+                if (mStopEventThread) {
+                    break;
+                }
+                mEventCv.wait(l);
+                continue;
+            }
+        }
+
+        if (acc > 0) {
+            int ret = ::write(mAllocEventFd.get(), &acc, 8);
+            if (ret > 0) {
+                acc = 0;
+            }
+        }
+        if (stopRequest && !stopCommitted) {
+            uint64_t val = 1ULL;
+            int ret = ::write(mStopEventFd.get(), &val, 8);
+            if (ret > 0) {
+                stopCommitted = true;
+            }
+        }
+        if (mStopEventThread) {
+            break;
+        }
+    }
+}
+
+c2_status_t GraphicsTracker::getWaitableFds(int *allocFd, int *statusFd) {
+    *allocFd = ::dup(mAllocEventFd.get());
+    *statusFd = ::dup(mStopEventFd.get());
+
+    if (*allocFd < 0 || *statusFd < 0) {
+        if (*allocFd >= 0) {
+            ::close(*allocFd);
+            *allocFd = -1;
+        }
+        if (*statusFd >= 0) {
+            ::close(*statusFd);
+            *statusFd = -1;
+        }
+        return C2_NO_MEMORY;
+    }
+    return C2_OK;
+}
+
+c2_status_t GraphicsTracker::requestAllocate(std::shared_ptr<BufferCache> *cache) {
+    std::lock_guard<std::mutex> l(mLock);
+    if (mDequeueable > 0) {
+        uint64_t val;
+        int ret = ::read(mAllocEventFd.get(), &val, 8);
+        if (ret < 0) {
+            if (errno == EINTR) {
+                // Do we really need to care for cancel due to signal handling?
+                return C2_CANCELED;
+            }
+            if (errno == EAGAIN) {
+                // proper usage of waitable object should not return this.
+                // but there could be alloc requests from HAL ignoring the internal status.
+                return C2_BLOCKING;
+            }
+            CHECK(errno != 0);
+        }
+        mDequeueable--;
+        *cache = mBufferCache;
+        return C2_OK;
+    }
+    return C2_BLOCKING;
+}
+
+// If {@code cached} is {@code true}, {@code pBuffer} should be read from the
+// current cached status. Otherwise, {@code pBuffer} should be written to
+// current caches status.
+void GraphicsTracker::commitAllocate(c2_status_t res, const std::shared_ptr<BufferCache> &cache,
+                    bool cached, int slot, const sp<Fence> &fence,
+                    std::shared_ptr<BufferItem> *pBuffer, bool *updateDequeue) {
+    std::unique_lock<std::mutex> l(mLock);
+    if (res == C2_OK) {
+        if (cached) {
+            auto it = cache->mBuffers.find(slot);
+            CHECK(it != cache->mBuffers.end());
+            it->second->mFence = fence;
+            *pBuffer = it->second;
+        } else if (cache.get() == mBufferCache.get() && mBufferCache->mIgbp) {
+            // Cache the buffer if it is allocated from the current IGBP
+            CHECK(slot >= 0);
+            auto ret = mBufferCache->mBuffers.emplace(slot, *pBuffer);
+            if (!ret.second) {
+                ret.first->second = *pBuffer;
+            }
+        }
+        uint64_t bid = (*pBuffer)->mId;
+        auto mapRet = mDequeued.emplace(bid, *pBuffer);
+        CHECK(mapRet.second);
+    } else {
+        if (adjustDequeueConfLocked(updateDequeue)) {
+            return;
+        }
+        mDequeueable++;
+        l.unlock();
+        writeIncDequeueable(1);
+    }
+}
+
+
+// if a buffer is newly allocated, {@code cached} is {@code false},
+// and the buffer is in the {@code buffer}
+// otherwise, {@code cached} is {@code false} and the buffer should be
+// retrieved by commitAllocate();
+c2_status_t GraphicsTracker::_allocate(const std::shared_ptr<BufferCache> &cache,
+                                      uint32_t width, uint32_t height, PixelFormat format,
+                                      int64_t usage,
+                                      bool *cached,
+                                      int *rSlotId,
+                                      sp<Fence> *rFence,
+                                      std::shared_ptr<BufferItem> *buffer) {
+    ::android::sp<IGraphicBufferProducer> igbp = cache->mIgbp;
+    uint32_t generation = cache->mGeneration;
+    if (!igbp) {
+        // allocate directly
+        AHardwareBuffer_Desc desc;
+        desc.width = width;
+        desc.height = height;
+        desc.layers = 1u;
+        desc.format = ::android::AHardwareBuffer_convertFromPixelFormat(format);
+        desc.usage = ::android::AHardwareBuffer_convertFromGrallocUsageBits(usage);
+        desc.rfu0 = 0;
+        desc.rfu1 = 0;
+
+        AHardwareBuffer *buf;
+        int ret = AHardwareBuffer_allocate(&desc, &buf);
+        if (ret != ::android::OK) {
+            ALOGE("direct allocation of AHB failed(%d)", ret);
+            return ret == ::android::NO_MEMORY ? C2_NO_MEMORY : C2_CORRUPTED;
+        }
+        *cached = false;
+        *buffer = std::make_shared<BufferItem>(generation, &desc, buf);
+        if (!*buffer) {
+            AHardwareBuffer_release(buf);
+            return C2_NO_MEMORY;
+        }
+        return C2_OK;
+    }
+
+    int slotId;
+    uint64_t outBufferAge;
+    ::android::FrameEventHistoryDelta outTimestamps;
+    sp<Fence> fence;
+
+    ::android::status_t status = igbp->dequeueBuffer(
+            &slotId, &fence, width, height, format, usage, &outBufferAge, &outTimestamps);
+    if (status < ::android::OK) {
+        ALOGE("dequeueBuffer() error %d", (int)status);
+        return C2_CORRUPTED;
+    }
+    cache->waitOnSlot(slotId);
+    bool exists = false;
+    {
+        std::unique_lock<std::mutex> l(mLock);
+        if (cache.get() == mBufferCache.get() &&
+            cache->mBuffers.find(slotId) != cache->mBuffers.end()) {
+            exists = true;
+        }
+    }
+    bool needsRealloc = status & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION;
+    if (needsRealloc || !exists) {
+        sp<GraphicBuffer> realloced;
+        status = igbp->requestBuffer(slotId, &realloced);
+        if (status != ::android::OK) {
+            igbp->cancelBuffer(slotId, fence);
+            return C2_CORRUPTED;
+        }
+        *buffer = std::make_shared<BufferItem>(generation, slotId, realloced, fence);
+        if (!(*buffer)->mInit) {
+            buffer->reset();
+            igbp->cancelBuffer(slotId, fence);
+            return C2_CORRUPTED;
+        }
+        *cached = false;
+        return C2_OK;
+    }
+    *cached = true;
+    *rSlotId = slotId;
+    *rFence = fence;
+    return C2_OK;
+}
+
+c2_status_t GraphicsTracker::allocate(
+        uint32_t width, uint32_t height, PixelFormat format, uint64_t usage,
+        AHardwareBuffer **buf, sp<Fence> *rFence) {
+    if (mStopped.load() == true) {
+        return C2_BAD_STATE;
+    }
+    std::shared_ptr<BufferCache> cache;
+    c2_status_t res = requestAllocate(&cache);
+    if (res != C2_OK) {
+        return res;
+    }
+
+    bool cached = false;
+    int slotId;
+    sp<Fence> fence;
+    std::shared_ptr<BufferItem> buffer;
+    bool updateDequeue;
+    res = _allocate(cache, width, height, format, usage, &cached, &slotId, &fence, &buffer);
+    commitAllocate(res, cache, cached, slotId, fence, &buffer, &updateDequeue);
+    if (res == C2_OK) {
+        *buf = buffer->mBuf;
+        *rFence = buffer->mFence;
+        // *buf should be valid even if buffer is dtor-ed.
+        AHardwareBuffer_acquire(*buf);
+    }
+    if (updateDequeue) {
+        updateDequeueConf();
+    }
+    return res;
+}
+
+c2_status_t GraphicsTracker::requestDeallocate(uint64_t bid, const sp<Fence> &fence,
+                                              bool *completed, bool *updateDequeue,
+                                              std::shared_ptr<BufferCache> *cache, int *slotId,
+                                              sp<Fence> *rFence) {
+    std::unique_lock<std::mutex> l(mLock);
+    if (mDeallocating.find(bid) != mDeallocating.end()) {
+        ALOGE("Tries to deallocate a buffer which is already deallocating or rendering");
+        return C2_DUPLICATE;
+    }
+    auto it = mDequeued.find(bid);
+    if (it == mDequeued.end()) {
+        ALOGE("Tried to deallocate non dequeued buffer");
+        return C2_NOT_FOUND;
+    }
+
+    std::shared_ptr<BufferItem> buffer = it->second;
+    if (buffer->mGeneration == mBufferCache->mGeneration && mBufferCache->mIgbp) {
+        auto it = mBufferCache->mBuffers.find(buffer->mSlot);
+        CHECK(it != mBufferCache->mBuffers.end() && it->second.get() == buffer.get());
+        *cache = mBufferCache;
+        *slotId = buffer->mSlot;
+        *rFence = ( fence == Fence::NO_FENCE) ? buffer->mFence : fence;
+        // mark this deallocating
+        mDeallocating.emplace(bid);
+        mBufferCache->blockSlot(buffer->mSlot);
+        *completed = false;
+    } else { // buffer is not from the current underlying Graphics.
+        mDequeued.erase(bid);
+        *completed = true;
+        if (adjustDequeueConfLocked(updateDequeue)) {
+            return C2_OK;
+        }
+        mDequeueable++;
+        l.unlock();
+        writeIncDequeueable(1);
+    }
+    return C2_OK;
+}
+
+void GraphicsTracker::commitDeallocate(
+        std::shared_ptr<BufferCache> &cache, int slotId, uint64_t bid) {
+    std::lock_guard<std::mutex> l(mLock);
+    size_t del1 = mDequeued.erase(bid);
+    size_t del2 = mDeallocating.erase(bid);
+    CHECK(del1 > 0 && del2 > 0);
+    mDequeueable++;
+    if (cache) {
+        cache->unblockSlot(slotId);
+    }
+}
+
+
+c2_status_t GraphicsTracker::deallocate(uint64_t bid, const sp<Fence> &fence) {
+    bool completed;
+    bool updateDequeue;
+    std::shared_ptr<BufferCache> cache;
+    int slotId;
+    sp<Fence> rFence;
+    c2_status_t res = requestDeallocate(bid, fence, &completed, &updateDequeue,
+                                        &cache, &slotId, &rFence);
+    if (res != C2_OK) {
+        return res;
+    }
+    if (completed == true) {
+        if (updateDequeue) {
+            updateDequeueConf();
+        }
+        return C2_OK;
+    }
+
+    // ignore return value since IGBP could be already stale.
+    // cache->mIgbp is not null, if completed is false.
+    (void)cache->mIgbp->cancelBuffer(slotId, rFence);
+
+    commitDeallocate(cache, slotId, bid);
+    return C2_OK;
+}
+
+c2_status_t GraphicsTracker::requestRender(uint64_t bid, std::shared_ptr<BufferCache> *cache,
+                                          std::shared_ptr<BufferItem> *pBuffer,
+                                          bool *updateDequeue) {
+    std::unique_lock<std::mutex> l(mLock);
+    if (mDeallocating.find(bid) != mDeallocating.end()) {
+        ALOGE("Tries to render a buffer which is already deallocating or rendering");
+        return C2_DUPLICATE;
+    }
+    auto it = mDequeued.find(bid);
+    if (it == mDequeued.end()) {
+        ALOGE("Tried to render non dequeued buffer");
+        return C2_NOT_FOUND;
+    }
+    if (!mBufferCache->mIgbp) {
+        // Render requested without surface.
+        // reclaim the buffer for dequeue.
+        // TODO: is this correct for API wise?
+        mDequeued.erase(it);
+        if (adjustDequeueConfLocked(updateDequeue)) {
+            return C2_BAD_STATE;
+        }
+        mDequeueable++;
+        l.unlock();
+        writeIncDequeueable(1);
+        return C2_BAD_STATE;
+    }
+    std::shared_ptr<BufferItem> buffer = it->second;
+    *cache = mBufferCache;
+    if (buffer->mGeneration == mBufferCache->mGeneration) {
+        auto it = mBufferCache->mBuffers.find(buffer->mSlot);
+        CHECK(it != mBufferCache->mBuffers.end() && it->second.get() == buffer.get());
+        mBufferCache->blockSlot(buffer->mSlot);
+    }
+    *pBuffer = buffer;
+    mDeallocating.emplace(bid);
+    return C2_OK;
+}
+
+void GraphicsTracker::commitRender(uint64_t origBid,
+                                  const std::shared_ptr<BufferCache> &cache,
+                                  const std::shared_ptr<BufferItem> &buffer,
+                                  bool *updateDequeue) {
+    std::unique_lock<std::mutex> l(mLock);
+    uint64_t bid = buffer->mId;
+
+    if (cache.get() != mBufferCache.get()) {
+        // Surface changed, no need to wait for buffer being released.
+        mDeallocating.erase(bid);
+        mDequeued.erase(bid);
+        if (adjustDequeueConfLocked(updateDequeue)) {
+            return;
+        }
+        mDequeueable++;
+        l.unlock();
+        writeIncDequeueable(1);
+        return;
+    }
+
+    if (origBid != bid) {
+        // migration happened, need to register the buffer to Cache
+        mBufferCache->mBuffers.emplace(buffer->mSlot, buffer);
+    }
+    mDeallocating.erase(bid);
+    mDequeued.erase(bid);
+}
+
+c2_status_t GraphicsTracker::render(const C2ConstGraphicBlock& blk,
+                                   const IGraphicBufferProducer::QueueBufferInput &input,
+                                   IGraphicBufferProducer::QueueBufferOutput *output) {
+    uint64_t bid;
+    c2_status_t res = retrieveAHardwareBufferId(blk, &bid);
+    if (res != C2_OK) {
+        ALOGE("retrieving AHB-ID for GraphicBlock failed");
+        return C2_CORRUPTED;
+    }
+    std::shared_ptr<BufferCache> cache;
+    std::shared_ptr<BufferItem> buffer;
+    bool updateDequeue = false;
+    res = requestRender(bid, &cache, &buffer, &updateDequeue);
+    if (res != C2_OK) {
+        if (updateDequeue) {
+            updateDequeueConf();
+        }
+        return res;
+    }
+    ::android::status_t migrateRes = ::android::OK;
+    ::android::status_t renderRes = ::android::OK;
+    if (cache->mGeneration != buffer->mGeneration) {
+        uint64_t newUsage = 0ULL;
+        int slotId = -1;;
+
+        (void) cache->mIgbp->getConsumerUsage(&newUsage);
+        sp<GraphicBuffer> gb = buffer->updateBuffer(newUsage, cache->mGeneration);
+        if (gb) {
+            migrateRes = cache->mIgbp->attachBuffer(&(buffer->mSlot), gb);
+        } else {
+            ALOGW("realloc-ing a new buffer for migration failed");
+            migrateRes = ::android::INVALID_OPERATION;
+        }
+    }
+    if (migrateRes == ::android::OK) {
+        renderRes = cache->mIgbp->queueBuffer(buffer->mSlot, input, output);
+        if (renderRes != ::android::OK) {
+            CHECK(renderRes != ::android::BAD_VALUE);
+        }
+    }
+    if (migrateRes != ::android::OK || renderRes != ::android::OK) {
+        // since it is not renderable, just de-allocate
+        if (migrateRes != ::android::OK) {
+            std::shared_ptr<BufferCache> nullCache;
+            commitDeallocate(nullCache, -1, bid);
+        } else {
+            (void) cache->mIgbp->cancelBuffer(buffer->mSlot, input.fence);
+            commitDeallocate(cache, buffer->mSlot, bid);
+        }
+        ALOGE("migration error(%d), render error(%d)", (int)migrateRes, (int)renderRes);
+        return C2_REFUSED;
+    }
+
+    updateDequeue = false;
+    commitRender(bid, cache, buffer, &updateDequeue);
+    if (updateDequeue) {
+        updateDequeueConf();
+    }
+    if (output->bufferReplaced) {
+        // in case of buffer drop during render
+        onReleased(cache->mGeneration);
+    }
+    return C2_OK;
+}
+
+void GraphicsTracker::onReleased(uint32_t generation) {
+    bool updateDequeue = false;
+    {
+        std::unique_lock<std::mutex> l(mLock);
+        if (mBufferCache->mGeneration == generation) {
+            if (!adjustDequeueConfLocked(&updateDequeue)) {
+                mDequeueable++;
+                l.unlock();
+                writeIncDequeueable(1);
+            }
+        }
+    }
+    if (updateDequeue) {
+        updateDequeueConf();
+    }
+}
+
+} // namespace aidl::android::hardware::media::c2::implementation
diff --git a/media/codec2/hal/client/client.cpp b/media/codec2/hal/client/client.cpp
index 01fc593..076a4cc 100644
--- a/media/codec2/hal/client/client.cpp
+++ b/media/codec2/hal/client/client.cpp
@@ -770,16 +770,9 @@
 
 // Codec2Client
 Codec2Client::Codec2Client(sp<Base> const& base,
+                           sp<c2_hidl::IConfigurable> const& configurable,
                            size_t serviceIndex)
-      : Configurable{
-            [base]() -> sp<c2_hidl::IConfigurable> {
-                Return<sp<c2_hidl::IConfigurable>> transResult =
-                        base->getConfigurable();
-                return transResult.isOk() ?
-                        static_cast<sp<c2_hidl::IConfigurable>>(transResult) :
-                        nullptr;
-            }()
-        },
+      : Configurable{configurable},
         mBase1_0{base},
         mBase1_1{Base1_1::castFrom(base)},
         mBase1_2{Base1_2::castFrom(base)},
@@ -1163,7 +1156,11 @@
     CHECK(baseStore) << "Codec2 service \"" << name << "\""
                         " inaccessible for unknown reasons.";
     LOG(VERBOSE) << "Client to Codec2 service \"" << name << "\" created";
-    return std::make_shared<Codec2Client>(baseStore, index);
+    Return<sp<IConfigurable>> transResult = baseStore->getConfigurable();
+    CHECK(transResult.isOk()) << "Codec2 service \"" << name << "\""
+                                "does not have IConfigurable.";
+    sp<IConfigurable> configurable = static_cast<sp<IConfigurable>>(transResult);
+    return std::make_shared<Codec2Client>(baseStore, configurable, index);
 }
 
 c2_status_t Codec2Client::ForAllServices(
@@ -1722,6 +1719,8 @@
                     static_cast<uint64_t>(blockPoolId),
                     bqId == 0 ? nullHgbp : igbp);
 
+    mOutputBufferQueue->expireOldWaiters();
+
     if (!transStatus.isOk()) {
         LOG(ERROR) << "setOutputSurface -- transaction failed.";
         return C2_TRANSACTION_FAILED;
@@ -1767,6 +1766,7 @@
                        << status << ".";
         }
     }
+    mOutputBufferQueue->expireOldWaiters();
 }
 
 c2_status_t Codec2Client::Component::connectToInputSurface(
diff --git a/media/codec2/hal/client/include/codec2/aidl/GraphicsTracker.h b/media/codec2/hal/client/include/codec2/aidl/GraphicsTracker.h
new file mode 100644
index 0000000..681b7e8
--- /dev/null
+++ b/media/codec2/hal/client/include/codec2/aidl/GraphicsTracker.h
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2023 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 <android/hardware_buffer.h>
+#include <android-base/unique_fd.h>
+#include <gui/IGraphicBufferProducer.h>
+
+#include <atomic>
+#include <condition_variable>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <set>
+#include <thread>
+
+#include <C2Buffer.h>
+
+namespace aidl::android::hardware::media::c2::implementation {
+
+using ::android::IGraphicBufferProducer;
+using ::android::GraphicBuffer;
+using ::android::Fence;
+using ::android::PixelFormat;
+using ::android::sp;
+/**
+ * The class allocates AHardwareBuffer(GraphicBuffer)s using BufferQueue.
+ *
+ * The class tracks and manages outstanding # of allocations for buffer
+ * recycling. So Graphics operations which affects # of outstanding allocation
+ * should be done via the class. (e.g. rendering a buffer to display)
+ *
+ * The class is supposed to be wrapped into IGraphicBufferAllocator AIDL interface,
+ * and the interface will be passed to HAL for a specific BlockPool instance.
+ *
+ * The class has one to one relation with HAL side Graphic C2BlockPool.
+ * The life cycle of the class is tied to a HAL side BlockPool object.
+ *
+ * So, reset()/stop() of HAL which related to blokcpool destruction will terminate the
+ * use of the class. And a new instance should be created in order for start()
+ * of HAL.
+ */
+class GraphicsTracker {
+public:
+    static std::shared_ptr<GraphicsTracker> CreateGraphicsTracker(int maxDequeueCount) {
+        GraphicsTracker *p = new GraphicsTracker(maxDequeueCount);
+        std::shared_ptr<GraphicsTracker> sp(p);
+        return sp;
+    }
+
+    ~GraphicsTracker();
+
+    /**
+     * Configure a new surface to render/allocate graphic blocks.
+     *
+     * Graphic  blocks from the old surface will be migrated to the new surface,
+     * if possible. Configuring to a null surface is possible in the case,
+     * an allocation request will be fulfilled by a direct allocation(not using
+     * BQ). generation should be different to the previous generations.
+     *
+     * @param[in] igbp        the new surface to configure
+     * @param[in] generation  identifier for each configured surface
+     */
+    c2_status_t configureGraphics(const sp<IGraphicBufferProducer>& igbp, uint32_t generation);
+
+    /**
+     * Configure max # of outstanding allocations at any given time.
+     *
+     * @param[in] maxDequeueCount    max # of outstanding allocation to configure
+     */
+    c2_status_t configureMaxDequeueCount(int maxDequeueCount);
+
+    /**
+     * Allocates a AHardwareBuffer.
+     *
+     * @param[in] width       width
+     * @param[in] height      height
+     * @param[in] PixelFormat pixel format which describes color format and etc
+     * @param[in] usage       gralloc usage bits
+     * @param[out] buf        the allocated buffer
+     * @param[out] fence      fence for the allocated buffer
+     * @return  C2_OK         the buffer is allocated
+     *          C2_BAD_STATE  stop() is called and in stopped state
+     *          C2_BLOCKING   should be waited to allocate
+     *          C2_NO_MEMORY  out of memory
+     *          C2_CORRUPTED
+     */
+    c2_status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint64_t usage,
+                         AHardwareBuffer **buf, sp<Fence> *fence);
+
+    /**
+     * Deallocates a AHardwareBuffer
+     *
+     * @param[in] bufId         id of the buffer to deallocate
+     * @param[in] fence         i/o fence for the buffer
+     * @return  C2_OK           the buffer is successfully deallocated.
+     *          C2_DUPLICATE    deallocation/render request is pending already.
+     *          C2_NOT_FOUND    the buffer with the id is not allocated.
+     */
+    c2_status_t deallocate(uint64_t bufId, const sp<Fence> &fence);
+
+    /**
+     * Render a GraphicBlock which is associated to a pending allocated buffer
+     *
+     * @param[in] block         GraphicBlock
+     * @param[in] input         render input params to Graphics
+     * @param[out] output       render output params from Graphics
+     * @return  C2_OK           the buffer is now ready to render
+     *          C2_BAD_STATE    there is no surface to render.
+     *                          (null surface mode or life cycle ends)
+     *          C2_DUPLICATE    deallocation/render request is pending already.
+     *          C2_NOT_FOUND    the buffer with the id is not allocated.
+     *          C2_REFUSED      the buffer is refused to render from Graphics
+     *          C2_CORRUPTED
+     */
+    c2_status_t render(const C2ConstGraphicBlock& block,
+                       const IGraphicBufferProducer::QueueBufferInput& input,
+                       IGraphicBufferProducer::QueueBufferOutput *output);
+
+    /**
+     * Notifies when a Buffer is ready to allocate from Graphics.
+     * If generation does not match to the current, notifications via the interface
+     * will be ignored. (In the case, the notifications are from one of the old surfaces
+     * which is no longer used.)
+     *
+     * @param[in] generation    generation id for specifying Graphics(BQ)
+     */
+    void onReleased(uint32_t generation);
+
+    /**
+     * Get waitable fds for events.(allocate is ready, end of life cycle)
+     *
+     * @param[out]  allocFd     eventFd which signals being ready to allocate
+     * @param[out]  statusFd    eventFd which signals end of life cycle.
+     *                          When signaled no more allocate is possible.
+     * @return  C2_OK
+     *          C2_NO_MEMORY    Max # of fd reached.(not really a memory issue)
+     */
+    c2_status_t getWaitableFds(int *allocFd, int *statusFd);
+
+    /**
+     *  Ends to use the class. after the call, allocate will fail.
+     */
+    void stop();
+
+private:
+    static constexpr int kDefaultMaxDequeue = 2;
+
+    struct BufferCache;
+
+    struct BufferItem {
+        bool mInit;
+        uint64_t mId;
+        uint32_t mGeneration;
+        int mSlot;
+        AHardwareBuffer *mBuf;
+        uint64_t mUsage; // Gralloc usage format, not AHB
+        sp<Fence> mFence;
+
+        // Create from a GraphicBuffer
+        BufferItem(uint32_t generation, int slot,
+                   const sp<GraphicBuffer>& buf,
+                   const sp<Fence> &fence);
+
+        // Create from an AHB (no slot information)
+        // Should be attached to IGBP for rendering
+        BufferItem(uint32_t generation,
+                   AHardwareBuffer_Desc *desc,
+                   AHardwareBuffer *pBuf);
+
+        ~BufferItem();
+
+        sp<GraphicBuffer> updateBuffer(uint64_t newUsage, uint32_t newGeneration);
+    };
+
+    struct BufferCache {
+        static constexpr int kNumSlots = ::android::BufferQueueDefs::NUM_BUFFER_SLOTS;
+
+        uint64_t mBqId;
+        uint32_t mGeneration;
+        ::android::sp<IGraphicBufferProducer> mIgbp;
+
+        // Maps slotId to buffer
+        // IGBP::dequeueBuffer(), IGBP::queueBuffer() and IGBP::cancelBuffer()
+        // require slotId.
+        std::map<int, std::shared_ptr<BufferItem>> mBuffers;
+
+        // block slot use, while deallocating(cancel, render and etc)
+        struct BlockedSlot {
+            std::mutex l;
+            std::condition_variable cv;
+            bool blocked;
+            BlockedSlot() : blocked{false} {}
+            ~BlockedSlot() = default;
+        };
+
+        BlockedSlot mBlockedSlots[kNumSlots];
+
+        BufferCache() : mBqId{0ULL}, mGeneration{0}, mIgbp{nullptr} {}
+        BufferCache(uint64_t bqId, uint32_t generation, const sp<IGraphicBufferProducer>& igbp) :
+            mBqId{bqId}, mGeneration{generation}, mIgbp{igbp} {}
+
+        void waitOnSlot(int slot);
+
+        void blockSlot(int slot);
+
+        void unblockSlot(int slot);
+    };
+
+    std::shared_ptr<BufferCache> mBufferCache;
+    // Maps bufferId to buffer
+    std::map<uint64_t, std::shared_ptr<BufferItem>> mDequeued;
+    std::set<uint64_t> mDeallocating;
+
+    int mMaxDequeue;
+    int mMaxDequeueRequested;
+    int mMaxDequeueCommitted;
+
+    uint32_t mMaxDequeueRequestedSeqId;
+    uint32_t mMaxDequeueCommittedSeqId;
+
+    int mDequeueable;
+
+    // TODO: statistics
+    uint64_t mTotalDequeued;
+    //uint64_t mTotalQueued;
+    uint64_t mTotalCancelled;
+    uint64_t mTotalDropped;
+    uint64_t mTotalReleased;
+
+    bool mInConfig;
+    std::mutex mLock; // locks for data synchronization
+    std::mutex mConfigLock; // locks for configuration change.
+
+    std::atomic<bool> mStopped;
+
+    ::android::base::unique_fd mAllocEventFd; // eventfd in semaphore mode which
+                                              // mirrors mDqueueable.
+    ::android::base::unique_fd mStopEventFd; // eventfd which indicates the life
+                                             // cycle of the class being stopped.
+
+    std::thread mEventQueueThread; // Thread to handle interrupted
+                                   // writes to eventfd{s}.
+    std::mutex mEventLock;
+    std::condition_variable mEventCv;
+
+    bool mStopEventThread;
+    int mIncDequeueable; // pending # of write to increase dequeueable eventfd
+    bool mStopRequest; // pending write to statusfd
+
+private:
+    explicit GraphicsTracker(int maxDequeueCount);
+
+    // return {@code true} only when dequeue config adjust happened.
+    // {@code updateDequeueConf} is an output parameter, and returns
+    // {@code true} only when the current dequeue conf is required to be
+    // updated to IGBP(BQ) as a result of the adjust.
+    bool adjustDequeueConfLocked(bool *updateDequeueConf);
+
+    void updateDequeueConf();
+
+    c2_status_t requestAllocate(std::shared_ptr<BufferCache> *cache);
+    c2_status_t requestDeallocate(uint64_t bid, const sp<Fence> &fence,
+                                  bool *completed, bool *updateDequeue,
+                                  std::shared_ptr<BufferCache> *cache, int *slotId,
+                                  sp<Fence> *rFence);
+    c2_status_t requestRender(uint64_t bid, std::shared_ptr<BufferCache> *cache,
+                              std::shared_ptr<BufferItem> *pBuffer,
+                              bool *updateDequeue);
+
+    void commitAllocate(c2_status_t res,
+                        const std::shared_ptr<BufferCache> &cache,
+                        bool cached, int slotId, const sp<Fence> &fence,
+                        std::shared_ptr<BufferItem> *buffer,
+                        bool *updateDequeue);
+    void commitDeallocate(std::shared_ptr<BufferCache> &cache, int slotId, uint64_t bid);
+    void commitRender(uint64_t origBid,
+                      const std::shared_ptr<BufferCache> &cache,
+                      const std::shared_ptr<BufferItem> &buffer,
+                      bool *updateDequeue);
+
+    c2_status_t _allocate(
+            const std::shared_ptr<BufferCache> &cache,
+            uint32_t width, uint32_t height, PixelFormat format, int64_t usage,
+            bool *cached, int *rSlotId, sp<Fence> *rFence,
+            std::shared_ptr<BufferItem> *buffer);
+
+    void writeIncDequeueable(int inc);
+    void processEvent();
+};
+
+} // namespace aidl::android::hardware::media::c2::implementation
diff --git a/media/codec2/hal/client/include/codec2/hidl/client.h b/media/codec2/hal/client/include/codec2/hidl/client.h
index 8c60f55..8a4aa48 100644
--- a/media/codec2/hal/client/include/codec2/hidl/client.h
+++ b/media/codec2/hal/client/include/codec2/hidl/client.h
@@ -178,6 +178,8 @@
     typedef ::android::hardware::media::c2::V1_2::IComponentStore Base1_2;
     typedef Base1_0 Base;
 
+    typedef ::android::hardware::media::c2::V1_0::IConfigurable IConfigurable;
+
     struct Listener;
 
     typedef Codec2ConfigurableClient Configurable;
@@ -262,8 +264,11 @@
     static std::shared_ptr<InputSurface> CreateInputSurface(
             char const* serviceName = nullptr);
 
-    // base cannot be null.
-    Codec2Client(sp<Base> const& base, size_t serviceIndex);
+    // base and/or configurable cannot be null.
+    Codec2Client(
+            sp<Base> const& base,
+            sp<IConfigurable> const& configurable,
+            size_t serviceIndex);
 
 protected:
     sp<Base1_0> mBase1_0;
diff --git a/media/codec2/hal/client/include/codec2/hidl/output.h b/media/codec2/hal/client/include/codec2/hidl/output.h
index 35a0224..2e89c3b 100644
--- a/media/codec2/hal/client/include/codec2/hidl/output.h
+++ b/media/codec2/hal/client/include/codec2/hidl/output.h
@@ -51,6 +51,10 @@
                    int maxDequeueBufferCount,
                    std::shared_ptr<V1_2::SurfaceSyncObj> *syncObj);
 
+    // If there are waiters to allocate from the old surface, wake up and expire
+    // them.
+    void expireOldWaiters();
+
     // Stop using the current output surface. Pending buffer opeations will not
     // perform anymore.
     void stop();
@@ -90,6 +94,8 @@
     std::weak_ptr<_C2BlockPoolData> mPoolDatas[BufferQueueDefs::NUM_BUFFER_SLOTS];
     std::shared_ptr<C2SurfaceSyncMemory> mSyncMem;
     bool mStopped;
+    std::mutex mOldMutex;
+    std::shared_ptr<C2SurfaceSyncMemory> mOldMem;
 
     bool registerBuffer(const C2ConstGraphicBlock& block);
 };
diff --git a/media/codec2/hal/client/output.cpp b/media/codec2/hal/client/output.cpp
index dd10691..a42229f 100644
--- a/media/codec2/hal/client/output.cpp
+++ b/media/codec2/hal/client/output.cpp
@@ -217,6 +217,7 @@
     sp<GraphicBuffer> buffers[BufferQueueDefs::NUM_BUFFER_SLOTS];
     std::weak_ptr<_C2BlockPoolData>
             poolDatas[BufferQueueDefs::NUM_BUFFER_SLOTS];
+    std::shared_ptr<C2SurfaceSyncMemory> oldMem;
     {
         std::scoped_lock<std::mutex> l(mMutex);
         bool stopped = mStopped;
@@ -238,7 +239,7 @@
             }
             return false;
         }
-        std::shared_ptr<C2SurfaceSyncMemory> oldMem = mSyncMem;
+        oldMem = mSyncMem;
         C2SyncVariables *oldSync = mSyncMem ? mSyncMem->mem() : nullptr;
         if (oldSync) {
             oldSync->lock();
@@ -314,11 +315,26 @@
             newSync->unlock();
         }
     }
+    {
+        std::scoped_lock<std::mutex> l(mOldMutex);
+        mOldMem = oldMem;
+    }
     ALOGD("remote graphic buffer migration %zu/%zu",
           success, tryNum);
     return true;
 }
 
+void OutputBufferQueue::expireOldWaiters() {
+    std::scoped_lock<std::mutex> l(mOldMutex);
+    if (mOldMem) {
+        C2SyncVariables *oldSync = mOldMem->mem();
+        if (oldSync) {
+            oldSync->notifyAll();
+        }
+        mOldMem.reset();
+    }
+}
+
 void OutputBufferQueue::stop() {
     std::scoped_lock<std::mutex> l(mMutex);
     mStopped = true;
diff --git a/media/codec2/hal/hidl/1.0/utils/Component.cpp b/media/codec2/hal/hidl/1.0/utils/Component.cpp
index df30dba..0aeed08 100644
--- a/media/codec2/hal/hidl/1.0/utils/Component.cpp
+++ b/media/codec2/hal/hidl/1.0/utils/Component.cpp
@@ -222,6 +222,21 @@
     return mInit;
 }
 
+void Component::onDeathReceived() {
+    {
+        std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
+        mClientDied = true;
+        for (auto it = mBlockPools.begin(); it != mBlockPools.end(); ++it) {
+            if (it->second->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
+                std::shared_ptr<C2BufferQueueBlockPool> bqPool =
+                        std::static_pointer_cast<C2BufferQueueBlockPool>(it->second);
+                bqPool->invalidate();
+            }
+        }
+    }
+    release();
+}
+
 // Methods from ::android::hardware::media::c2::V1_0::IComponent
 Return<Status> Component::queue(const WorkBundle& workBundle) {
     std::list<std::unique_ptr<C2Work>> c2works;
@@ -409,9 +424,19 @@
         blockPool = nullptr;
     }
     if (blockPool) {
-        mBlockPoolsMutex.lock();
-        mBlockPools.emplace(blockPool->getLocalId(), blockPool);
-        mBlockPoolsMutex.unlock();
+        bool emplaced = false;
+        {
+            mBlockPoolsMutex.lock();
+            if (!mClientDied) {
+                mBlockPools.emplace(blockPool->getLocalId(), blockPool);
+                emplaced = true;
+            }
+            mBlockPoolsMutex.unlock();
+        }
+        if (!emplaced) {
+            blockPool.reset();
+            status = C2_BAD_STATE;
+        }
     } else if (status == C2_OK) {
         status = C2_CORRUPTED;
     }
@@ -494,8 +519,8 @@
                 ) override {
             auto strongComponent = mComponent.promote();
             if (strongComponent) {
-                LOG(INFO) << "Client died ! release the component !!";
-                strongComponent->release();
+                LOG(INFO) << "Client died ! notify and release the component !!";
+                strongComponent->onDeathReceived();
             } else {
                 LOG(ERROR) << "Client died ! no component to release !!";
             }
diff --git a/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h b/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h
index e343655..3f55618 100644
--- a/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h
+++ b/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h
@@ -66,6 +66,8 @@
             const sp<::android::hardware::media::bufferpool::V2_0::
                 IClientManager>& clientPoolManager);
     c2_status_t status() const;
+    // Receives a death notification of the client.
+    void onDeathReceived();
 
     typedef ::android::hardware::graphics::bufferqueue::V1_0::
             IGraphicBufferProducer HGraphicBufferProducer1;
@@ -135,6 +137,7 @@
 
     using HwDeathRecipient = ::android::hardware::hidl_death_recipient;
     sp<HwDeathRecipient> mDeathRecipient;
+    bool mClientDied{false};
 };
 
 }  // namespace utils
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp b/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
index 117d9ca..3dc5b29 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
+++ b/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
@@ -438,6 +438,7 @@
                    std::ifstream& eleStream, android::Vector<FrameInfo>* Info, int offset,
                    int range, bool signalEOS = true) {
     typedef std::unique_lock<std::mutex> ULock;
+    static const size_t kPageSize = getpagesize();
     int frameID = offset;
     int maxRetry = 0;
     while (1) {
@@ -479,7 +480,7 @@
         ASSERT_EQ(eleStream.gcount(), size);
 
         work->input.buffers.clear();
-        auto alignedSize = ALIGN(size, PAGE_SIZE);
+        auto alignedSize = ALIGN(size, kPageSize);
         if (size) {
             std::shared_ptr<C2LinearBlock> block;
             ASSERT_EQ(C2_OK, linearPool->fetchLinearBlock(
diff --git a/media/codec2/hal/hidl/1.1/utils/Component.cpp b/media/codec2/hal/hidl/1.1/utils/Component.cpp
index 2dd922f..d0f4f19 100644
--- a/media/codec2/hal/hidl/1.1/utils/Component.cpp
+++ b/media/codec2/hal/hidl/1.1/utils/Component.cpp
@@ -222,6 +222,21 @@
     return mInit;
 }
 
+void Component::onDeathReceived() {
+    {
+        std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
+        mClientDied = true;
+        for (auto it = mBlockPools.begin(); it != mBlockPools.end(); ++it) {
+            if (it->second->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
+                std::shared_ptr<C2BufferQueueBlockPool> bqPool =
+                        std::static_pointer_cast<C2BufferQueueBlockPool>(it->second);
+                bqPool->invalidate();
+            }
+        }
+    }
+    release();
+}
+
 // Methods from ::android::hardware::media::c2::V1_1::IComponent
 Return<Status> Component::queue(const WorkBundle& workBundle) {
     std::list<std::unique_ptr<C2Work>> c2works;
@@ -409,9 +424,19 @@
         blockPool = nullptr;
     }
     if (blockPool) {
-        mBlockPoolsMutex.lock();
-        mBlockPools.emplace(blockPool->getLocalId(), blockPool);
-        mBlockPoolsMutex.unlock();
+        bool emplaced = false;
+        {
+            mBlockPoolsMutex.lock();
+            if (!mClientDied) {
+                mBlockPools.emplace(blockPool->getLocalId(), blockPool);
+                emplaced = true;
+            }
+            mBlockPoolsMutex.unlock();
+        }
+        if (!emplaced) {
+            blockPool.reset();
+            status = C2_BAD_STATE;
+        }
     } else if (status == C2_OK) {
         status = C2_CORRUPTED;
     }
@@ -501,8 +526,8 @@
                 ) override {
             auto strongComponent = component.promote();
             if (strongComponent) {
-                LOG(INFO) << "Client died ! release the component !!";
-                strongComponent->release();
+                LOG(INFO) << "Client died ! notify and release the component !!";
+                strongComponent->onDeathReceived();
             } else {
                 LOG(ERROR) << "Client died ! no component to release !!";
             }
diff --git a/media/codec2/hal/hidl/1.1/utils/include/codec2/hidl/1.1/Component.h b/media/codec2/hal/hidl/1.1/utils/include/codec2/hidl/1.1/Component.h
index 1c8c20c..f16de24 100644
--- a/media/codec2/hal/hidl/1.1/utils/include/codec2/hidl/1.1/Component.h
+++ b/media/codec2/hal/hidl/1.1/utils/include/codec2/hidl/1.1/Component.h
@@ -69,6 +69,8 @@
             const sp<::android::hardware::media::bufferpool::V2_0::
                 IClientManager>& clientPoolManager);
     c2_status_t status() const;
+    // Receives a death notification of the client.
+    void onDeathReceived();
 
     typedef ::android::hardware::graphics::bufferqueue::V1_0::
             IGraphicBufferProducer HGraphicBufferProducer1;
@@ -140,6 +142,7 @@
 
     using HwDeathRecipient = ::android::hardware::hidl_death_recipient;
     sp<HwDeathRecipient> mDeathRecipient;
+    bool mClientDied{false};
 };
 
 } // namespace utils
diff --git a/media/codec2/hal/hidl/1.2/utils/Component.cpp b/media/codec2/hal/hidl/1.2/utils/Component.cpp
index 7994d32..036c900 100644
--- a/media/codec2/hal/hidl/1.2/utils/Component.cpp
+++ b/media/codec2/hal/hidl/1.2/utils/Component.cpp
@@ -222,6 +222,21 @@
     return mInit;
 }
 
+void Component::onDeathReceived() {
+    {
+        std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
+        mClientDied = true;
+        for (auto it = mBlockPools.begin(); it != mBlockPools.end(); ++it) {
+            if (it->second->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
+                std::shared_ptr<C2BufferQueueBlockPool> bqPool =
+                        std::static_pointer_cast<C2BufferQueueBlockPool>(it->second);
+                bqPool->invalidate();
+            }
+        }
+    }
+    release();
+}
+
 // Methods from ::android::hardware::media::c2::V1_1::IComponent
 Return<Status> Component::queue(const WorkBundle& workBundle) {
     std::list<std::unique_ptr<C2Work>> c2works;
@@ -409,9 +424,19 @@
         blockPool = nullptr;
     }
     if (blockPool) {
-        mBlockPoolsMutex.lock();
-        mBlockPools.emplace(blockPool->getLocalId(), blockPool);
-        mBlockPoolsMutex.unlock();
+        bool emplaced = false;
+        {
+            mBlockPoolsMutex.lock();
+            if (!mClientDied) {
+                mBlockPools.emplace(blockPool->getLocalId(), blockPool);
+                emplaced = true;
+            }
+            mBlockPoolsMutex.unlock();
+        }
+        if (!emplaced) {
+            blockPool.reset();
+            status = C2_BAD_STATE;
+        }
     } else if (status == C2_OK) {
         status = C2_CORRUPTED;
     }
@@ -532,8 +557,8 @@
                 ) override {
             auto strongComponent = component.promote();
             if (strongComponent) {
-                LOG(INFO) << "Client died ! release the component !!";
-                strongComponent->release();
+                LOG(INFO) << "Client died ! notify and release the component !!";
+                strongComponent->onDeathReceived();
             } else {
                 LOG(ERROR) << "Client died ! no component to release !!";
             }
diff --git a/media/codec2/hal/hidl/1.2/utils/include/codec2/hidl/1.2/Component.h b/media/codec2/hal/hidl/1.2/utils/include/codec2/hidl/1.2/Component.h
index d0972ee..6a73392 100644
--- a/media/codec2/hal/hidl/1.2/utils/include/codec2/hidl/1.2/Component.h
+++ b/media/codec2/hal/hidl/1.2/utils/include/codec2/hidl/1.2/Component.h
@@ -69,6 +69,8 @@
             const sp<::android::hardware::media::bufferpool::V2_0::
                 IClientManager>& clientPoolManager);
     c2_status_t status() const;
+    // Receives a death notification of the client.
+    void onDeathReceived();
 
     typedef ::android::hardware::graphics::bufferqueue::V1_0::
             IGraphicBufferProducer HGraphicBufferProducer1;
@@ -145,7 +147,7 @@
 
     using HwDeathRecipient = ::android::hardware::hidl_death_recipient;
     sp<HwDeathRecipient> mDeathRecipient;
-
+    bool mClientDied{false};
 };
 
 } // namespace utils
diff --git a/media/codec2/sfplugin/utils/Codec2CommonUtils.cpp b/media/codec2/sfplugin/utils/Codec2CommonUtils.cpp
index f428fce..43533fd 100644
--- a/media/codec2/sfplugin/utils/Codec2CommonUtils.cpp
+++ b/media/codec2/sfplugin/utils/Codec2CommonUtils.cpp
@@ -31,11 +31,19 @@
 
 namespace android {
 
-bool isAtLeastT() {
+
+static bool isAtLeast(int version, const char *codeName) {
     char deviceCodeName[PROP_VALUE_MAX];
     __system_property_get("ro.build.version.codename", deviceCodeName);
-    return android_get_device_api_level() >= __ANDROID_API_T__ ||
-           !strcmp(deviceCodeName, "Tiramisu");
+    return android_get_device_api_level() >= version || !strcmp(deviceCodeName, codeName);
+}
+
+bool isAtLeastT() {
+    return isAtLeast(__ANDROID_API_T__, "Tiramisu");
+}
+
+bool isAtLeastU() {
+    return isAtLeast(__ANDROID_API_U__, "UpsideDownCake");
 }
 
 static bool isP010Allowed() {
@@ -127,10 +135,16 @@
             .rfu0 = 0,
             .rfu1 = 0,
     };
-
-    return AHardwareBuffer_isSupported(&consumableForDisplayOrGpu)
-            && AHardwareBuffer_isSupported(&consumableForHwEncoder)
-            && AHardwareBuffer_isSupported(&consumableForSwEncoder);
+    // Some devices running versions prior to Android U aren't guaranteed to advertise support
+    // for some color formats when the consumer is an encoder. Hence limit these checks to
+    // Android U and beyond.
+    if (isAtLeastU()) {
+        return AHardwareBuffer_isSupported(&consumableForDisplayOrGpu)
+                && AHardwareBuffer_isSupported(&consumableForHwEncoder)
+                && AHardwareBuffer_isSupported(&consumableForSwEncoder);
+    } else {
+        return AHardwareBuffer_isSupported(&consumableForDisplayOrGpu);
+    }
 }
 
 }  // namespace android
diff --git a/media/codec2/sfplugin/utils/Codec2CommonUtils.h b/media/codec2/sfplugin/utils/Codec2CommonUtils.h
index 98dd65b..9bb52bd 100644
--- a/media/codec2/sfplugin/utils/Codec2CommonUtils.h
+++ b/media/codec2/sfplugin/utils/Codec2CommonUtils.h
@@ -23,6 +23,8 @@
 
 bool isAtLeastT();
 
+bool isAtLeastU();
+
 bool isVendorApiOrFirstApiAtLeastT();
 
 /**
diff --git a/media/codec2/vndk/C2AllocatorIon.cpp b/media/codec2/vndk/C2AllocatorIon.cpp
index a6a733e..582a993 100644
--- a/media/codec2/vndk/C2AllocatorIon.cpp
+++ b/media/codec2/vndk/C2AllocatorIon.cpp
@@ -179,6 +179,7 @@
     static Impl *Alloc(int ionFd, size_t size, size_t align, unsigned heapMask, unsigned flags, C2Allocator::id_t id);
 
     c2_status_t map(size_t offset, size_t size, C2MemoryUsage usage, C2Fence *fence, void **addr) {
+        static const size_t kPageSize = getpagesize();
         (void)fence; // TODO: wait for fence
         *addr = nullptr;
         if (!mMappings.lock()->empty()) {
@@ -201,7 +202,7 @@
             prot |= PROT_WRITE;
         }
 
-        size_t alignmentBytes = offset % PAGE_SIZE;
+        size_t alignmentBytes = offset % kPageSize;
         size_t mapOffset = offset - alignmentBytes;
         size_t mapSize = size + alignmentBytes;
         Mapping map = { nullptr, alignmentBytes, mapSize };
diff --git a/media/codec2/vndk/C2DmaBufAllocator.cpp b/media/codec2/vndk/C2DmaBufAllocator.cpp
index c470171..9baf8d8 100644
--- a/media/codec2/vndk/C2DmaBufAllocator.cpp
+++ b/media/codec2/vndk/C2DmaBufAllocator.cpp
@@ -170,6 +170,7 @@
 
 c2_status_t C2DmaBufAllocation::map(size_t offset, size_t size, C2MemoryUsage usage, C2Fence* fence,
                                     void** addr) {
+    static const size_t kPageSize = getpagesize();
     (void)fence;  // TODO: wait for fence
     *addr = nullptr;
     if (!mMappings.lock()->empty()) {
@@ -192,7 +193,7 @@
         prot |= PROT_WRITE;
     }
 
-    size_t alignmentBytes = offset % PAGE_SIZE;
+    size_t alignmentBytes = offset % kPageSize;
     size_t mapOffset = offset - alignmentBytes;
     size_t mapSize = size + alignmentBytes;
     Mapping map = {nullptr, alignmentBytes, mapSize};
diff --git a/media/codec2/vndk/include/C2BqBufferPriv.h b/media/codec2/vndk/include/C2BqBufferPriv.h
index 29aad5e..320b192 100644
--- a/media/codec2/vndk/include/C2BqBufferPriv.h
+++ b/media/codec2/vndk/include/C2BqBufferPriv.h
@@ -103,6 +103,13 @@
 
     virtual void getConsumerUsage(uint64_t *consumerUsage);
 
+    /**
+     * Invalidate the class.
+     *
+     * After the call, fetchGraphicBlock() will return C2_BAD_STATE.
+     */
+    virtual void invalidate();
+
 private:
     const std::shared_ptr<C2Allocator> mAllocator;
     const local_id_t mLocalId;
diff --git a/media/codec2/vndk/include/C2SurfaceSyncObj.h b/media/codec2/vndk/include/C2SurfaceSyncObj.h
index d858f27..b193b4a 100644
--- a/media/codec2/vndk/include/C2SurfaceSyncObj.h
+++ b/media/codec2/vndk/include/C2SurfaceSyncObj.h
@@ -112,6 +112,11 @@
      */
     c2_status_t waitForChange(uint32_t waitId, c2_nsecs_t timeoutNs);
 
+    /**
+     * Wake up and expire all waitors.
+     */
+    void notifyAll();
+
     C2SyncVariables() {}
 
 private:
diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp
index f2cd585..5fb0c8f 100644
--- a/media/codec2/vndk/platform/C2BqBuffer.cpp
+++ b/media/codec2/vndk/platform/C2BqBuffer.cpp
@@ -601,6 +601,9 @@
         static int kMaxIgbpRetryDelayUs = 10000;
 
         std::unique_lock<std::mutex> lock(mMutex);
+        if (mInvalidated) {
+            return C2_BAD_STATE;
+        }
         if (mLastDqLogTs == 0) {
             mLastDqLogTs = getTimestampNow();
         } else {
@@ -746,6 +749,11 @@
         *consumeUsage = mConsumerUsage;
     }
 
+    void invalidate() {
+        std::scoped_lock<std::mutex> lock(mMutex);
+        mInvalidated = true;
+    }
+
 private:
     friend struct C2BufferQueueBlockPoolData;
 
@@ -783,6 +791,7 @@
     // if the token has been expired, the buffers will not call IGBP::cancelBuffer()
     // when they are no longer used.
     std::shared_ptr<int> mIgbpValidityToken;
+    bool mInvalidated{false};
 };
 
 C2BufferQueueBlockPoolData::C2BufferQueueBlockPoolData(
@@ -1103,3 +1112,9 @@
     }
 }
 
+void C2BufferQueueBlockPool::invalidate() {
+    if (mImpl) {
+        mImpl->invalidate();
+    }
+}
+
diff --git a/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp b/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp
index 01fd30b..d8c2292 100644
--- a/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp
+++ b/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp
@@ -278,6 +278,12 @@
     return C2_BAD_VALUE;
 }
 
+void C2SyncVariables::notifyAll() {
+    this->lock();
+    this->broadcast();
+    this->unlock();
+}
+
 int C2SyncVariables::signal() {
     mCond++;
 
diff --git a/media/janitors/avic_OWNERS b/media/janitors/avic_OWNERS
index eca9978..81aac3a 100644
--- a/media/janitors/avic_OWNERS
+++ b/media/janitors/avic_OWNERS
@@ -3,4 +3,3 @@
 arifdikici@google.com
 dichenzhang@google.com
 kyslov@google.com
-richardxie@google.com
diff --git a/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp b/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp
index f00b1a0..a8f9f7e 100644
--- a/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp
+++ b/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp
@@ -14,7 +14,10 @@
  * limitations under the License.
  */
 
+#include <algorithm>
+#include <map>
 #include <memory>
+#include <string>
 
 #define LOG_TAG "DevicesFactoryHalAidl"
 //#define LOG_NDEBUG 0
@@ -75,6 +78,21 @@
                 if (strcmp(instance, "default") == 0) instance = "primary";
                 static_cast<decltype(names)>(context)->push_back(instance);
             });
+    std::sort(names->begin(), names->end(), [](const std::string& lhs,
+                    const std::string& rhs) {
+        // This order corresponds to the canonical order of modules as specified in
+        // the reference 'audio_policy_configuration_7_0.xml' file.
+        static const std::map<std::string, int> kPriorities{
+            { "primary", 0 }, { "a2dp", 1 }, { "usb", 2 }, { "r_submix", 3 },
+            { "bluetooth", 4 }, { "hearing_aid", 5 }, { "msd", 6 }, { "stub", 7 }
+        };
+        auto lhsIt = kPriorities.find(lhs);
+        auto rhsIt = kPriorities.find(rhs);
+        if (lhsIt != kPriorities.end() && rhsIt != kPriorities.end()) {
+            return lhsIt->second < rhsIt->second;
+        }
+        return lhsIt != kPriorities.end();
+    });
     return OK;
 }
 
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 19fc1c9..8be9c48 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -330,7 +330,7 @@
         std::scoped_lock lock{mLock};
         // Unregistering from DeathRecipient notification.
         if (mService != nullptr) {
-            AIBinder_unlinkToDeath(mService->asBinder().get(), mDeathRecipient.get(), this);
+            AIBinder_unlinkToDeath(mService->asBinder().get(), mDeathRecipient.get(), mCookie);
             mService = nullptr;
         }
     }
@@ -370,13 +370,15 @@
     std::shared_ptr<IResourceManagerClient> mClient;
     ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
     std::shared_ptr<IResourceManagerService> mService;
+    BinderDiedContext* mCookie;
 };
 
 MediaCodec::ResourceManagerServiceProxy::ResourceManagerServiceProxy(
         pid_t pid, uid_t uid, const std::shared_ptr<IResourceManagerClient> &client) :
     mPid(pid), mUid(uid), mClient(client),
     mDeathRecipient(::ndk::ScopedAIBinder_DeathRecipient(
-            AIBinder_DeathRecipient_new(BinderDiedCallback))) {
+            AIBinder_DeathRecipient_new(BinderDiedCallback))),
+    mCookie(nullptr) {
     if (mUid == MediaCodec::kNoUid) {
         mUid = AIBinder_getCallingUid();
     }
@@ -434,9 +436,9 @@
 
     // Create the context that is passed as cookie to the binder death notification.
     // The context gets deleted at BinderUnlinkedCallback.
-    BinderDiedContext* context = new BinderDiedContext{.mRMServiceProxy = weak_from_this()};
+    mCookie = new BinderDiedContext{.mRMServiceProxy = weak_from_this()};
     // Register for the callbacks by linking to death notification.
-    AIBinder_linkToDeath(mService->asBinder().get(), mDeathRecipient.get(), context);
+    AIBinder_linkToDeath(mService->asBinder().get(), mDeathRecipient.get(), mCookie);
 
     // If the RM was restarted, re-register all the resources.
     if (mBinderDied) {
diff --git a/media/module/codecs/m4v_h263/OWNERS b/media/module/codecs/m4v_h263/OWNERS
new file mode 100644
index 0000000..e537138
--- /dev/null
+++ b/media/module/codecs/m4v_h263/OWNERS
@@ -0,0 +1,4 @@
+# owners for frameworks/av/media/module/codecs/m4v_h263
+include platform/frameworks/av:/media/janitors/avic_OWNERS
+include platform/frameworks/av:/media/janitors/codec_OWNERS
+essick@google.com
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyConfig.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyConfig.cpp
index e214ae9..8c7a7de 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyConfig.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyConfig.cpp
@@ -89,7 +89,12 @@
         if (aidlPort.ext.getTag() == AudioPortExt::mix) {
             auto mixPort = sp<IOProfile>::make("", AUDIO_PORT_ROLE_NONE);
             RETURN_STATUS_IF_ERROR(mixPort->readFromParcelable(fwPort));
-            sortAudioProfiles(mixPort->getAudioProfiles());
+            auto& profiles = mixPort->getAudioProfiles();
+            if (profiles.empty()) {
+                profiles.add(AudioProfile::createFullDynamic(gDynamicFormat));
+            } else {
+                sortAudioProfiles(mixPort->getAudioProfiles());
+            }
             mixPorts.add(mixPort);
             ports.emplace(aidlPort.id, mixPort);
         } else if (aidlPort.ext.getTag() == AudioPortExt::device) {
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index b2971c4..f718406 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -44,47 +44,82 @@
 
 namespace android {
 
-//static
-std::mutex ResourceManagerService::sCookieLock;
-//static
-uintptr_t ResourceManagerService::sCookieCounter = 0;
-//static
-std::map<uintptr_t, sp<DeathNotifier> > ResourceManagerService::sCookieToDeathNotifierMap;
+class DeathNotifier : public std::enable_shared_from_this<DeathNotifier> {
 
-class DeathNotifier : public RefBase {
+    // BinderDiedContext defines the cookie that is passed as DeathRecipient.
+    // Since this can maintain more context than a raw pointer, we can
+    // validate the scope of DeathNotifier, before deferencing it upon the binder death.
+    struct BinderDiedContext {
+        std::weak_ptr<DeathNotifier> mDeathNotifier;
+    };
 public:
-    DeathNotifier(const std::shared_ptr<ResourceManagerService> &service,
-                  const ClientInfoParcel& clientInfo);
+    DeathNotifier(const std::shared_ptr<IResourceManagerClient>& client,
+                  const std::shared_ptr<ResourceManagerService>& service,
+                  const ClientInfoParcel& clientInfo,
+                  AIBinder_DeathRecipient* recipient);
 
-    virtual ~DeathNotifier() {}
+    virtual ~DeathNotifier() {
+        unlink();
+    }
+
+    void unlink() {
+        if (mClient != nullptr) {
+            // Register for the callbacks by linking to death notification.
+            AIBinder_unlinkToDeath(mClient->asBinder().get(), mRecipient, mCookie);
+            mClient = nullptr;
+        }
+    }
 
     // Implement death recipient
     static void BinderDiedCallback(void* cookie);
+    static void BinderUnlinkedCallback(void* cookie);
     virtual void binderDied();
 
+private:
+    void link() {
+        // Create the context that is passed as cookie to the binder death notification.
+        // The context gets deleted at BinderUnlinkedCallback.
+        mCookie = new BinderDiedContext{.mDeathNotifier = weak_from_this()};
+        // Register for the callbacks by linking to death notification.
+        AIBinder_linkToDeath(mClient->asBinder().get(), mRecipient, mCookie);
+    }
+
 protected:
+    std::shared_ptr<IResourceManagerClient> mClient;
     std::weak_ptr<ResourceManagerService> mService;
     const ClientInfoParcel mClientInfo;
+    AIBinder_DeathRecipient* mRecipient;
+    BinderDiedContext* mCookie;
 };
 
-DeathNotifier::DeathNotifier(const std::shared_ptr<ResourceManagerService> &service,
-                             const ClientInfoParcel& clientInfo)
-    : mService(service), mClientInfo(clientInfo) {}
+DeathNotifier::DeathNotifier(const std::shared_ptr<IResourceManagerClient>& client,
+                             const std::shared_ptr<ResourceManagerService>& service,
+                             const ClientInfoParcel& clientInfo,
+                             AIBinder_DeathRecipient* recipient)
+    : mClient(client), mService(service), mClientInfo(clientInfo),
+      mRecipient(recipient), mCookie(nullptr) {
+    link();
+}
+
+//static
+void DeathNotifier::BinderUnlinkedCallback(void* cookie) {
+    BinderDiedContext* context = reinterpret_cast<BinderDiedContext*>(cookie);
+    // Since we don't need the context anymore, we are deleting it now.
+    delete context;
+}
 
 //static
 void DeathNotifier::BinderDiedCallback(void* cookie) {
-    sp<DeathNotifier> notifier;
-    {
-        std::scoped_lock lock{ResourceManagerService::sCookieLock};
-        auto it = ResourceManagerService::sCookieToDeathNotifierMap.find(
-                reinterpret_cast<uintptr_t>(cookie));
-        if (it == ResourceManagerService::sCookieToDeathNotifierMap.end()) {
-            return;
+    BinderDiedContext* context = reinterpret_cast<BinderDiedContext*>(cookie);
+
+    // Validate the context and check if the DeathNotifier object is still in scope.
+    if (context != nullptr) {
+        std::shared_ptr<DeathNotifier> thiz = context->mDeathNotifier.lock();
+        if (thiz != nullptr) {
+            thiz->binderDied();
+        } else {
+            ALOGI("DeathNotifier is out of scope already");
         }
-        notifier = it->second;
-    }
-    if (notifier.get() != nullptr) {
-        notifier->binderDied();
     }
 }
 
@@ -103,9 +138,11 @@
 
 class OverrideProcessInfoDeathNotifier : public DeathNotifier {
 public:
-    OverrideProcessInfoDeathNotifier(const std::shared_ptr<ResourceManagerService> &service,
-                                     const ClientInfoParcel& clientInfo)
-            : DeathNotifier(service, clientInfo) {}
+    OverrideProcessInfoDeathNotifier(const std::shared_ptr<IResourceManagerClient>& client,
+                                     const std::shared_ptr<ResourceManagerService>& service,
+                                     const ClientInfoParcel& clientInfo,
+                                     AIBinder_DeathRecipient* recipient)
+            : DeathNotifier(client, service, clientInfo, recipient) {}
 
     virtual ~OverrideProcessInfoDeathNotifier() {}
 
@@ -195,7 +232,7 @@
                           .clientId = clientId,
                           .name = name.empty()? "<unknown client>" : name,
                           .client = client,
-                          .cookie = 0,
+                          .deathNotifier = nullptr,
                           .pendingRemoval = false};
         auto [it, inserted] = infos.emplace(clientId, info);
         found = it;
@@ -341,7 +378,8 @@
       mSupportsMultipleSecureCodecs(true),
       mSupportsSecureWithNonSecureCodec(true),
       mCpuBoostCount(0),
-      mDeathRecipient(AIBinder_DeathRecipient_new(DeathNotifier::BinderDiedCallback)) {
+      mDeathRecipient(::ndk::ScopedAIBinder_DeathRecipient(
+                      AIBinder_DeathRecipient_new(DeathNotifier::BinderDiedCallback))) {
     mSystemCB->noteResetVideo();
     // Create ResourceManagerMetrics that handles all the metrics.
     mResourceManagerMetrics = std::make_unique<ResourceManagerMetrics>(mProcessInfo);
@@ -496,9 +534,9 @@
             mergeResources(it->second, res);
         }
     }
-    if (info.cookie == 0 && client != nullptr) {
-        info.cookie = addCookieAndLink_l(client,
-                new DeathNotifier(ref<ResourceManagerService>(), clientInfo));
+    if (info.deathNotifier == nullptr && client != nullptr) {
+        info.deathNotifier = std::make_shared<DeathNotifier>(
+            client, ref<ResourceManagerService>(), clientInfo, mDeathRecipient.get());
     }
     if (mObserverService != nullptr && !resourceAdded.empty()) {
         mObserverService->onResourceAdded(uid, pid, resourceAdded);
@@ -615,8 +653,6 @@
     // Since this client has been removed, update the metrics collector.
     mResourceManagerMetrics->notifyClientReleased(clientInfo);
 
-    removeCookieAndUnlink_l(info.client, info.cookie);
-
     if (mObserverService != nullptr && !info.resources.empty()) {
         mObserverService->onResourceRemoved(info.uid, pid, info.resources);
     }
@@ -869,40 +905,14 @@
                                 .uid = 0,
                                 .id = 0,
                                 .name = "<unknown client>"};
-    uintptr_t cookie = addCookieAndLink_l(client,
-            new OverrideProcessInfoDeathNotifier(ref<ResourceManagerService>(), clientInfo));
+    auto deathNotifier = std::make_shared<OverrideProcessInfoDeathNotifier>(
+            client, ref<ResourceManagerService>(), clientInfo, mDeathRecipient.get());
 
-    mProcessInfoOverrideMap.emplace(pid, ProcessInfoOverride{cookie, client});
+    mProcessInfoOverrideMap.emplace(pid, ProcessInfoOverride{deathNotifier, client});
 
     return Status::ok();
 }
 
-uintptr_t ResourceManagerService::addCookieAndLink_l(
-        const std::shared_ptr<IResourceManagerClient>& client, const sp<DeathNotifier>& notifier) {
-    if (client == nullptr) {
-        return 0;
-    }
-    std::scoped_lock lock{sCookieLock};
-
-    uintptr_t cookie;
-    // Need to skip cookie 0 (if it wraps around). ResourceInfo has cookie initialized to 0
-    // indicating the death notifier is not created yet.
-    while ((cookie = ++sCookieCounter) == 0);
-    AIBinder_linkToDeath(client->asBinder().get(), mDeathRecipient.get(), (void*)cookie);
-    sCookieToDeathNotifierMap.emplace(cookie, notifier);
-
-    return cookie;
-}
-
-void ResourceManagerService::removeCookieAndUnlink_l(
-         const std::shared_ptr<IResourceManagerClient>& client, uintptr_t cookie) {
-    std::scoped_lock lock{sCookieLock};
-    if (client != nullptr) {
-        AIBinder_unlinkToDeath(client->asBinder().get(), mDeathRecipient.get(), (void*)cookie);
-    }
-    sCookieToDeathNotifierMap.erase(cookie);
-}
-
 void ResourceManagerService::removeProcessInfoOverride(int pid) {
     std::scoped_lock lock{mLock};
 
@@ -916,9 +926,6 @@
     }
 
     mProcessInfo->removeProcessInfoOverride(pid);
-
-    removeCookieAndUnlink_l(it->second.client, it->second.cookie);
-
     mProcessInfoOverrideMap.erase(pid);
 }
 
diff --git a/services/mediaresourcemanager/ResourceManagerService.h b/services/mediaresourcemanager/ResourceManagerService.h
index a05a346..aa88ac6 100644
--- a/services/mediaresourcemanager/ResourceManagerService.h
+++ b/services/mediaresourcemanager/ResourceManagerService.h
@@ -56,7 +56,7 @@
     int64_t clientId;
     std::string name;
     std::shared_ptr<IResourceManagerClient> client;
-    uintptr_t cookie{0};
+    std::shared_ptr<DeathNotifier> deathNotifier = nullptr;
     ResourceList resources;
     bool pendingRemoval{false};
 };
@@ -186,10 +186,6 @@
     void removeProcessInfoOverride(int pid);
 
     void removeProcessInfoOverride_l(int pid);
-    uintptr_t addCookieAndLink_l(const std::shared_ptr<IResourceManagerClient>& client,
-                                 const sp<DeathNotifier>& notifier);
-    void removeCookieAndUnlink_l(const std::shared_ptr<IResourceManagerClient>& client,
-                                 uintptr_t cookie);
 
     void pushReclaimAtom(const ClientInfoParcel& clientInfo,
                          const std::vector<std::shared_ptr<IResourceManagerClient>>& clients,
@@ -210,15 +206,11 @@
     int32_t mCpuBoostCount;
     ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
     struct ProcessInfoOverride {
-        uintptr_t cookie;
+        std::shared_ptr<DeathNotifier> deathNotifier = nullptr;
         std::shared_ptr<IResourceManagerClient> client;
     };
     std::map<int, int> mOverridePidMap;
     std::map<pid_t, ProcessInfoOverride> mProcessInfoOverrideMap;
-    static std::mutex sCookieLock;
-    static uintptr_t sCookieCounter GUARDED_BY(sCookieLock);
-    static std::map<uintptr_t, sp<DeathNotifier> > sCookieToDeathNotifierMap
-            GUARDED_BY(sCookieLock);
     std::shared_ptr<ResourceObserverService> mObserverService;
     std::unique_ptr<ResourceManagerMetrics> mResourceManagerMetrics;
 };