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;
};