Add C2VdaPooledBlockPool class

V4L2 stateful API will require the caller passing a fixed output
buffer to each slot of buffer queue. However, the default
C2PooledBlockPool cannot achieve this requirement.

This CL adds C2VdaPooledBlockPool for replacing C2PooledBlockPool in
the following CL.

Bug: 161770200
Test: m -j32
Change-Id: Icdad63ace87b49793d0f7215f8d735a8c950ee60
diff --git a/plugin_store/Android.bp b/plugin_store/Android.bp
index 3d3d040..ed9d784 100644
--- a/plugin_store/Android.bp
+++ b/plugin_store/Android.bp
@@ -8,6 +8,7 @@
 
     srcs: [
         "C2VdaBqBlockPool.cpp",
+        "C2VdaPooledBlockPool.cpp",
         "V4L2PluginStore.cpp",
         "VendorAllocatorLoader.cpp",
     ],
@@ -25,6 +26,7 @@
         "libhidlbase",
         "libnativewindow",
         "liblog",
+        "libstagefright_bufferpool@1.0",
         "libstagefright_bufferqueue_helper",
         "libstagefright_foundation",
         "libui",
@@ -33,5 +35,6 @@
     cflags: [
       "-Werror",
       "-Wall",
+      "-Wthread-safety",
     ],
 }
diff --git a/plugin_store/C2VdaPooledBlockPool.cpp b/plugin_store/C2VdaPooledBlockPool.cpp
new file mode 100644
index 0000000..19c9cfc
--- /dev/null
+++ b/plugin_store/C2VdaPooledBlockPool.cpp
@@ -0,0 +1,112 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2VdaPooledBlockPool"
+
+#include <v4l2_codec2/plugin_store/C2VdaPooledBlockPool.h>
+
+#include <time.h>
+
+#include <C2AllocatorGralloc.h>
+#include <C2BlockInternal.h>
+#include <bufferpool/BufferPoolTypes.h>
+#include <log/log.h>
+
+namespace android {
+namespace {
+// The wait time for another try to fetch a buffer from bufferpool.
+const int64_t kFetchRetryDelayUs = 10 * 1000;
+
+int64_t GetNowUs() {
+    struct timespec t;
+    t.tv_sec = 0;
+    t.tv_nsec = 0;
+    clock_gettime(CLOCK_MONOTONIC, &t);
+    int64_t nsecs = static_cast<int64_t>(t.tv_sec) * 1000000000LL + t.tv_nsec;
+    return nsecs / 1000ll;
+}
+}  // namespace
+
+using android::hardware::media::bufferpool::BufferPoolData;
+
+// static
+c2_status_t C2VdaPooledBlockPool::getPoolIdFromGraphicBlock(
+        const std::shared_ptr<C2GraphicBlock>& block, uint32_t* poolId) {
+    std::shared_ptr<_C2BlockPoolData> blockPoolData =
+            _C2BlockFactory::GetGraphicBlockPoolData(*block);
+    if (blockPoolData->getType() != _C2BlockPoolData::TYPE_BUFFERPOOL) {
+        ALOGE("Obtained C2GraphicBlock is not bufferpool-backed.");
+        return C2_CORRUPTED;
+    }
+    std::shared_ptr<BufferPoolData> bpData;
+    if (!_C2BlockFactory::GetBufferPoolData(blockPoolData, &bpData) || !bpData) {
+        ALOGE("BufferPoolData unavailable in block.");
+        return C2_CORRUPTED;
+    }
+    *poolId = bpData->mId;
+    return C2_OK;
+}
+
+// Tries to fetch a buffer from bufferpool. When the size of |mBufferIds| is smaller than
+// |mBufferCount|, pass the obtained buffer to caller and record its ID in BufferPoolData to
+// |mBufferIds|. When the size of |mBufferIds| is equal to |mBufferCount|, pass the obtained
+// buffer only if its ID is included in |mBufferIds|. Otherwise, discard the buffer and
+// return C2_TIMED_OUT.
+c2_status_t C2VdaPooledBlockPool::fetchGraphicBlock(uint32_t width, uint32_t height,
+                                                    uint32_t format, C2MemoryUsage usage,
+                                                    std::shared_ptr<C2GraphicBlock>* block) {
+    ALOG_ASSERT(block != nullptr);
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    if (mNextFetchTimeUs != 0) {
+        int delayUs = GetNowUs() - mNextFetchTimeUs;
+        if (delayUs > 0) {
+            ::usleep(delayUs);
+        }
+        mNextFetchTimeUs = 0;
+    }
+
+    std::shared_ptr<C2GraphicBlock> fetchBlock;
+    c2_status_t err =
+            C2PooledBlockPool::fetchGraphicBlock(width, height, format, usage, &fetchBlock);
+    if (err != C2_OK) {
+        ALOGE("Failed at C2PooledBlockPool::fetchGraphicBlock: %d", err);
+        return err;
+    }
+
+    uint32_t bufferId;
+    err = getPoolIdFromGraphicBlock(fetchBlock, &bufferId);
+    if (err != C2_OK) {
+        ALOGE("Failed to getPoolIdFromGraphicBlock: %d", err);
+        return err;
+    }
+
+    if (mBufferIds.size() < mBufferCount) {
+        mBufferIds.insert(bufferId);
+    }
+
+    if (mBufferIds.find(bufferId) != mBufferIds.end()) {
+        ALOGV("Returned buffer id = %u", bufferId);
+        *block = std::move(fetchBlock);
+        return C2_OK;
+    }
+    ALOGV("No buffer could be recycled now, wait for another try...");
+    mNextFetchTimeUs = GetNowUs() + kFetchRetryDelayUs;
+    return C2_TIMED_OUT;
+}
+
+c2_status_t C2VdaPooledBlockPool::requestNewBufferSet(int32_t bufferCount) {
+    if (bufferCount <= 0) {
+        ALOGE("Invalid requested buffer count = %d", bufferCount);
+        return C2_BAD_VALUE;
+    }
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    mBufferIds.clear();
+    mBufferCount = bufferCount;
+    return C2_OK;
+}
+
+}  // namespace android
diff --git a/plugin_store/include/v4l2_codec2/plugin_store/C2VdaPooledBlockPool.h b/plugin_store/include/v4l2_codec2/plugin_store/C2VdaPooledBlockPool.h
new file mode 100644
index 0000000..b3ea988
--- /dev/null
+++ b/plugin_store/include/v4l2_codec2/plugin_store/C2VdaPooledBlockPool.h
@@ -0,0 +1,57 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ANDROID_V4L2_CODEC2_PLUGIN_STORE_C2_VDA_POOLED_BLOCK_POOL_H
+#define ANDROID_V4L2_CODEC2_PLUGIN_STORE_C2_VDA_POOLED_BLOCK_POOL_H
+
+#include <memory>
+#include <mutex>
+#include <set>
+
+#include <C2Buffer.h>
+#include <C2BufferPriv.h>
+#include <C2PlatformSupport.h>
+#include <android-base/thread_annotations.h>
+
+namespace android {
+
+class C2VdaPooledBlockPool : public C2PooledBlockPool {
+public:
+    using C2PooledBlockPool::C2PooledBlockPool;
+    ~C2VdaPooledBlockPool() override = default;
+
+    // Extracts buffer ID from BufferPoolData of the graphic block.
+    // |block| is the graphic block allocated by bufferpool block pool.
+    // |poolId| is an output parameter to store the buffer ID into.
+    static c2_status_t getPoolIdFromGraphicBlock(const std::shared_ptr<C2GraphicBlock>& block,
+                                                 uint32_t* poolId);
+
+    // Allocate the specified number of buffers.
+    // |bufferCount| is the number of requested buffers.
+    c2_status_t requestNewBufferSet(int32_t bufferCount);
+
+    // Return C2_OK and store a buffer in |block| if a buffer is successfully fetched.
+    // Return C2_TIMED_OUT if the pool already allocated |mBufferCount| buffers but they are all in
+    // use.
+    // Return C2_NO_MEMORY if the pool fails to allocate a new buffer.
+    c2_status_t fetchGraphicBlock(uint32_t width, uint32_t height, uint32_t format,
+                                  C2MemoryUsage usage,
+                                  std::shared_ptr<C2GraphicBlock>* block /* nonnull */) override;
+
+private:
+    // Function mutex to lock at the start of each API function call for protecting the
+    // synchronization of all member variables.
+    std::mutex mMutex;
+
+    // The ids of all allocated buffers.
+    std::set<uint32_t> mBufferIds GUARDED_BY(mMutex);
+    // The maximum count of allocated buffers.
+    size_t mBufferCount GUARDED_BY(mMutex){0};
+    // The timestamp for the next fetchGraphicBlock() call.
+    // Set when the previous fetchGraphicBlock() call timed out.
+    int64_t mNextFetchTimeUs GUARDED_BY(mMutex){0};
+};
+
+}  // namespace android
+#endif  // ANDROID_V4L2_CODEC2_PLUGIN_STORE_C2_VDA_POOLED_BLOCK_POOL_H