Merge pie-platform-release to aosp-master - DO NOT MERGE

Change-Id: I7992ad34499f84f164aedb02f25d7be31210f969
diff --git a/Android.bp b/Android.bp
deleted file mode 100644
index db4d39f..0000000
--- a/Android.bp
+++ /dev/null
@@ -1,32 +0,0 @@
-cc_library_shared {
-    name: "libv4l2_codec2_arcva_factory",
-    vendor_available: true,
-    product_variables: {
-        arc: {
-            srcs: ["C2ArcVideoAcceleratorFactory.cpp"],
-
-            shared_libs: [
-                "libarcbridge",
-                "libarcbridgeservice",
-                "libarcvideobridge",
-                "libbinder",
-                "libchrome",
-                "liblog",
-                "libmojo",
-                "libutils",
-            ],
-
-            // -Wno-unused-parameter is needed for libchrome/base codes
-            cflags: [
-                "-Wall",
-                "-Werror",
-                "-Wno-unused-parameter",
-                "-std=c++14",
-            ],
-        },
-    },
-    clang: true,
-    export_include_dirs: [
-        "include",
-    ],
-}
diff --git a/Android.mk b/Android.mk
index bff4409..b2304fb 100644
--- a/Android.mk
+++ b/Android.mk
@@ -29,6 +29,8 @@
                           liblog \
                           libmedia \
                           libstagefright \
+                          libstagefright_bufferqueue_helper \
+                          libstagefright_ccodec_ext \
                           libstagefright_codec2 \
                           libstagefright_codec2_vndk \
                           libstagefright_simple_c2component \
@@ -55,7 +57,7 @@
 LOCAL_SHARED_LIBRARIES += libarcbridge \
                           libarcbridgeservice \
                           libmojo \
-                          libv4l2_codec2_arcva_factory \
+                          libcodec2_arcva_factory \
 
 endif # ifneq (,$(findstring cheets_,$(TARGET_PRODUCT)))
 
diff --git a/C2ArcVideoAcceleratorFactory.cpp b/C2ArcVideoAcceleratorFactory.cpp
deleted file mode 100644
index 07997d1..0000000
--- a/C2ArcVideoAcceleratorFactory.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2018 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 "C2ArcVideoAcceleratorFactory"
-
-#include <C2ArcVideoAcceleratorFactory.h>
-
-#include <base/bind.h>
-#include <binder/IServiceManager.h>
-#include <mojo/edk/embedder/embedder.h>
-#include <mojo/public/cpp/bindings/interface_request.h>
-#include <mojo/public/cpp/system/handle.h>
-#include <utils/Log.h>
-
-namespace android {
-
-ANDROID_SINGLETON_STATIC_INSTANCE(C2ArcVideoAcceleratorFactory)
-
-bool C2ArcVideoAcceleratorFactory::createVideoDecodeAccelerator(
-        ::arc::mojom::VideoDecodeAcceleratorRequest request) {
-    if (!mRemoteFactory) {
-        ALOGE("Factory is not ready");
-        return false;
-    }
-    mRemoteFactory->CreateDecodeAccelerator(std::move(request));
-    return true;
-}
-
-bool C2ArcVideoAcceleratorFactory::createVideoEncodeAccelerator(
-        ::arc::mojom::VideoEncodeAcceleratorRequest request) {
-    if (!mRemoteFactory) {
-        ALOGE("Factory is not ready");
-        return false;
-    }
-    mRemoteFactory->CreateEncodeAccelerator(std::move(request));
-    return true;
-}
-
-bool C2ArcVideoAcceleratorFactory::createVideoProtectedBufferAllocator(
-        ::arc::mojom::VideoProtectedBufferAllocatorRequest request) {
-    if (!mRemoteFactory) {
-        ALOGE("Factory is not ready");
-        return false;
-    }
-    mRemoteFactory->CreateProtectedBufferAllocator(std::move(request));
-    return true;
-}
-
-int32_t C2ArcVideoAcceleratorFactory::hostVersion() const {
-    return mHostVersion;
-}
-
-C2ArcVideoAcceleratorFactory::C2ArcVideoAcceleratorFactory() : mHostVersion(0) {
-    sp<IBinder> binder =
-            defaultServiceManager()->getService(String16("android.os.IArcVideoBridge"));
-    if (binder == nullptr) {
-        ALOGE("Failed to find IArcVideoBridge service");
-        return;
-    }
-    mArcVideoBridge = interface_cast<IArcVideoBridge>(binder);
-    mHostVersion = mArcVideoBridge->hostVersion();
-    if (mHostVersion < 4) {
-        ALOGW("HostVersion(%d) is outdated", mHostVersion);
-        return;
-    }
-
-    ALOGV("HostVersion: %d", mHostVersion);
-
-    ::arc::MojoBootstrapResult bootstrapResult =
-            mArcVideoBridge->bootstrapVideoAcceleratorFactory();
-    if (!bootstrapResult.is_valid()) {
-        ALOGE("bootstrapVideoAcceleratorFactory returns invalid result");
-        return;
-    }
-    mojo::edk::ScopedPlatformHandle handle(
-            mojo::edk::PlatformHandle(bootstrapResult.releaseFd().release()));
-    ALOGV("SetParentPipeHandle(fd=%d)", handle.get().handle);
-    mojo::edk::SetParentPipeHandle(std::move(handle));
-    mojo::ScopedMessagePipeHandle server_pipe =
-            mojo::edk::CreateChildMessagePipe(bootstrapResult.releaseToken());
-    mRemoteFactory.Bind(mojo::InterfacePtrInfo<::arc::mojom::VideoAcceleratorFactory>(
-            std::move(server_pipe), 7u));
-}
-
-}  // namespace android
diff --git a/C2VDAAdaptorProxy.cpp b/C2VDAAdaptorProxy.cpp
index 2c44e6b..4c493d1 100644
--- a/C2VDAAdaptorProxy.cpp
+++ b/C2VDAAdaptorProxy.cpp
@@ -64,7 +64,7 @@
 }
 
 void C2VDAAdaptorProxy::establishChannelOnMojoThread(std::shared_ptr<::arc::Future<bool>> future) {
-    C2ArcVideoAcceleratorFactory& factory = ::android::C2ArcVideoAcceleratorFactory::getInstance();
+    auto& factory = ::android::GetC2ArcVideoAcceleratorFactory();
 
     if (!factory.createVideoDecodeAccelerator(mojo::MakeRequest(&mVDAPtr))) {
         future->set(false);
diff --git a/C2VDAComponent.cpp b/C2VDAComponent.cpp
index 2b3abde..5eec1ef 100644
--- a/C2VDAComponent.cpp
+++ b/C2VDAComponent.cpp
@@ -12,11 +12,14 @@
 #endif
 
 #define __C2_GENERATE_GLOBAL_VARS__
+#include <C2VDAAllocatorStore.h>
+#include <C2VdaBqBlockPool.h>
 #include <C2VDAComponent.h>
 #include <C2VDASupport.h>  // to getParamReflector from vda store
 
 #include <videodev2.h>
 
+#include <C2AllocatorGralloc.h>
 #include <C2ComponentFactory.h>
 #include <C2PlatformSupport.h>
 
@@ -52,6 +55,16 @@
 const C2String kVP8DecoderName = "c2.vda.vp8.decoder";
 const C2String kVP9DecoderName = "c2.vda.vp9.decoder";
 
+const uint32_t kDpbOutputBufferExtraCount = 3;  // Use the same number as ACodec.
+const int kDequeueRetryDelayUs = 10000;  // Wait time of dequeue buffer retry in microseconds.
+
+// Hack(b/79239042): Max size of mMockBufferQueueInClient.
+// This value is empirically picked from previous CTS try-run. If this value is too big, it may
+// cause VDA deadlock when it requires more buffers to decode and dequeue a new one. On the other
+// hand, too small value may produce wrong display picture because recycling goes faster than
+// rendering.
+const size_t kMockMaxBuffersInClient = 5;
+
 }  // namespace
 
 C2VDAComponent::IntfImpl::IntfImpl(C2String name, const std::shared_ptr<C2ReflectorHelper>& helper)
@@ -134,7 +147,7 @@
                          .build());
 
     C2Allocator::id_t inputAllocators[] = {C2PlatformAllocatorStore::ION};
-    C2Allocator::id_t outputAllocators[] = {C2PlatformAllocatorStore::GRALLOC};
+    C2Allocator::id_t outputAllocators[] = {C2VDAAllocatorStore::V4L2_BUFFERQUEUE};
 
     addParameter(
             DefineParam(mInputAllocatorIds, C2_PARAMKEY_INPUT_ALLOCATORS)
@@ -170,28 +183,6 @@
         CHECK_NE(mComponentState, ComponentState::UNINITIALIZED); \
     } while (0)
 
-class C2VDAGraphicBuffer : public C2Buffer {
-public:
-    C2VDAGraphicBuffer(const std::shared_ptr<C2GraphicBlock>& block, const media::Rect& visibleRect,
-                       const base::Closure& releaseCB);
-    ~C2VDAGraphicBuffer() override;
-
-private:
-    base::Closure mReleaseCB;
-};
-
-C2VDAGraphicBuffer::C2VDAGraphicBuffer(const std::shared_ptr<C2GraphicBlock>& block,
-                                       const media::Rect& visibleRect,
-                                       const base::Closure& releaseCB)
-      : C2Buffer({block->share(C2Rect(visibleRect.width(), visibleRect.height()), C2Fence())}),
-        mReleaseCB(releaseCB) {}
-
-C2VDAGraphicBuffer::~C2VDAGraphicBuffer() {
-    if (!mReleaseCB.is_null()) {
-        mReleaseCB.Run();
-    }
-}
-
 C2VDAComponent::VideoFormat::VideoFormat(HalPixelFormat pixelFormat, uint32_t minNumBuffers,
                                          media::Size codedSize, media::Rect visibleRect)
       : mPixelFormat(pixelFormat),
@@ -199,15 +190,27 @@
         mCodedSize(codedSize),
         mVisibleRect(visibleRect) {}
 
+static uint32_t getSlotFromGraphicBlockHandle(const C2Handle* const handle) {
+    uint32_t width, height, format, stride, igbp_slot, generation;
+    uint64_t usage, igbp_id;
+    _UnwrapNativeCodec2GrallocMetadata(
+            handle, &width, &height, &format, &usage, &stride, &generation, &igbp_id, &igbp_slot);
+    ALOGV("Unwrap Metadata: igbp[%" PRIu64 ", %u] (%u*%u, fmt %#x, usage %" PRIx64 ", stride %u)",
+          igbp_id, igbp_slot, width, height, format, usage, stride);
+    return igbp_slot;
+}
+
 C2VDAComponent::C2VDAComponent(C2String name, c2_node_id_t id,
                                const std::shared_ptr<C2ReflectorHelper>& helper)
       : mIntfImpl(std::make_shared<IntfImpl>(name, helper)),
         mIntf(std::make_shared<SimpleInterface<IntfImpl>>(name.c_str(), id, mIntfImpl)),
         mThread("C2VDAComponentThread"),
+        mDequeueThread("C2VDAComponentDequeueThread"),
         mVDAInitResult(VideoDecodeAcceleratorAdaptor::Result::ILLEGAL_STATE),
         mComponentState(ComponentState::UNINITIALIZED),
-        mDrainWithEOS(false),
+        mPendingOutputEOS(false),
         mLastOutputTimestamp(-1),
+        mSurfaceMode(true),
         mCodecProfile(media::VIDEO_CODEC_PROFILE_UNKNOWN),
         mState(State::UNLOADED),
         mWeakThisFactory(this) {
@@ -229,7 +232,7 @@
 
     if (mThread.IsRunning()) {
         mTaskRunner->PostTask(FROM_HERE,
-                              base::Bind(&C2VDAComponent::onDestroy, base::Unretained(this)));
+                              ::base::Bind(&C2VDAComponent::onDestroy, ::base::Unretained(this)));
         mThread.Stop();
     }
 }
@@ -241,9 +244,10 @@
         mVDAAdaptor->destroy();
         mVDAAdaptor.reset(nullptr);
     }
+    stopDequeueThread();
 }
 
-void C2VDAComponent::onStart(media::VideoCodecProfile profile, base::WaitableEvent* done) {
+void C2VDAComponent::onStart(media::VideoCodecProfile profile, ::base::WaitableEvent* done) {
     DCHECK(mTaskRunner->BelongsToCurrentThread());
     ALOGV("onStart");
     CHECK_EQ(mComponentState, ComponentState::UNINITIALIZED);
@@ -269,8 +273,6 @@
     ALOGV("onQueueWork: flags=0x%x, index=%llu, timestamp=%llu", work->input.flags,
           work->input.ordinal.frameIndex.peekull(), work->input.ordinal.timestamp.peekull());
     EXPECT_RUNNING_OR_RETURN_ON_ERROR();
-    // It is illegal for client to put new works while component is still flushing.
-    CHECK_NE(mComponentState, ComponentState::FLUSHING);
 
     uint32_t drainMode = NO_DRAIN;
     if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
@@ -280,7 +282,7 @@
     // TODO(johnylin): set a maximum size of mQueue and check if mQueue is already full.
 
     mTaskRunner->PostTask(FROM_HERE,
-                          base::Bind(&C2VDAComponent::onDequeueWork, base::Unretained(this)));
+                          ::base::Bind(&C2VDAComponent::onDequeueWork, ::base::Unretained(this)));
 }
 
 void C2VDAComponent::onDequeueWork() {
@@ -290,8 +292,9 @@
     if (mQueue.empty()) {
         return;
     }
-    if (mComponentState == ComponentState::DRAINING) {
-        ALOGV("Temporarily stop dequeueing works since component is draining.");
+    if (mComponentState == ComponentState::DRAINING ||
+        mComponentState == ComponentState::FLUSHING) {
+        ALOGV("Temporarily stop dequeueing works since component is draining/flushing.");
         return;
     }
     if (mComponentState != ComponentState::STARTED) {
@@ -304,10 +307,15 @@
     auto drainMode = mQueue.front().mDrainMode;
     mQueue.pop();
 
-    CHECK_EQ(work->input.buffers.size(), 1u);
-    C2ConstLinearBlock linearBlock = work->input.buffers.front()->data().linearBlocks().front();
-    // linearBlock.size() == 0 means this is a dummy work. No decode needed.
-    if (linearBlock.size() > 0) {
+    CHECK_LE(work->input.buffers.size(), 1u);
+    if (work->input.buffers.empty()) {
+        // Client may queue an EOS work with no input buffer, otherwise every work must have one
+        // input buffer.
+        CHECK(drainMode != NO_DRAIN);
+    } else {
+        // If input.buffers is not empty, the buffer should have meaningful content inside.
+        C2ConstLinearBlock linearBlock = work->input.buffers.front()->data().linearBlocks().front();
+        CHECK_GT(linearBlock.size(), 0u);
         // Send input buffer to VDA for decode.
         // Use frameIndex as bitstreamId.
         int32_t bitstreamId = frameIndexToBitstreamId(work->input.ordinal.frameIndex);
@@ -322,15 +330,15 @@
     if (drainMode != NO_DRAIN) {
         mVDAAdaptor->flush();
         mComponentState = ComponentState::DRAINING;
-        mDrainWithEOS = drainMode == DRAIN_COMPONENT_WITH_EOS;
+        mPendingOutputEOS = drainMode == DRAIN_COMPONENT_WITH_EOS;
     }
 
     // Put work to mPendingWorks.
     mPendingWorks.emplace_back(std::move(work));
 
     if (!mQueue.empty()) {
-        mTaskRunner->PostTask(FROM_HERE,
-                              base::Bind(&C2VDAComponent::onDequeueWork, base::Unretained(this)));
+        mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onDequeueWork,
+                                                      ::base::Unretained(this)));
     }
 }
 
@@ -351,16 +359,9 @@
     reportFinishedWorkIfAny();
 }
 
-// This is used as callback while output buffer is released by client.
-// TODO(johnylin): consider to use C2Buffer::registerOnDestroyNotify instead
-void C2VDAComponent::returnOutputBuffer(int32_t pictureBufferId) {
-    mTaskRunner->PostTask(FROM_HERE, base::Bind(&C2VDAComponent::onOutputBufferReturned,
-                                                base::Unretained(this), pictureBufferId));
-}
-
-void C2VDAComponent::onOutputBufferReturned(int32_t pictureBufferId) {
+void C2VDAComponent::onOutputBufferReturned(uint32_t slotId) {
     DCHECK(mTaskRunner->BelongsToCurrentThread());
-    ALOGV("onOutputBufferReturned: picture id=%d", pictureBufferId);
+    ALOGV("onOutputBufferReturned: slot id=%u", slotId);
     if (mComponentState == ComponentState::UNINITIALIZED) {
         // Output buffer is returned from client after component is stopped. Just let the buffer be
         // released.
@@ -369,7 +370,7 @@
 
     // TODO(johnylin): when buffer is returned, we should confirm that output format is not changed
     //                 yet. If changed, just let the buffer be released.
-    GraphicBlockInfo* info = getGraphicBlockById(pictureBufferId);
+    GraphicBlockInfo* info = getGraphicBlockBySlot(slotId);
     if (!info) {
         reportError(C2_CORRUPTED);
         return;
@@ -402,15 +403,28 @@
     CHECK_EQ(info->mState, GraphicBlockInfo::State::OWNED_BY_ACCELERATOR);
     // Output buffer will be passed to client soon along with mListener->onWorkDone_nb().
     info->mState = GraphicBlockInfo::State::OWNED_BY_CLIENT;
+    if (mSurfaceMode) {
+        mBuffersInClient++;
+    } else {  // byte-buffer mode
+        // Hack(b/79239042)
+        mMockBufferQueueInClient.push_back(info->mSlotId);
+        if (mMockBufferQueueInClient.size() > kMockMaxBuffersInClient) {
+            mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onOutputBufferReturned,
+                                                          ::base::Unretained(this),
+                                                          mMockBufferQueueInClient.front()));
+            mMockBufferQueueInClient.pop_front();
+        }
+    }
 
     // Attach output buffer to the work corresponded to bitstreamId.
-    work->worklets.front()->output.buffers.emplace_back(std::make_shared<C2VDAGraphicBuffer>(
-            info->mGraphicBlock, mOutputFormat.mVisibleRect,
-            base::Bind(&C2VDAComponent::returnOutputBuffer, mWeakThisFactory.GetWeakPtr(),
-                       pictureBufferId)));
+    auto block = info->mGraphicBlock;
+    work->worklets.front()->output.buffers.emplace_back(C2Buffer::CreateGraphicBuffer(
+            block->share(C2Rect(mOutputFormat.mVisibleRect.width(),
+                                mOutputFormat.mVisibleRect.height()),
+                         C2Fence())));
 
     // TODO: this does not work for timestamps as they can wrap around
-    int64_t currentTimestamp = base::checked_cast<int64_t>(work->input.ordinal.timestamp.peek());
+    int64_t currentTimestamp = ::base::checked_cast<int64_t>(work->input.ordinal.timestamp.peek());
     CHECK_GE(currentTimestamp, mLastOutputTimestamp);
     mLastOutputTimestamp = currentTimestamp;
 
@@ -434,7 +448,7 @@
         if (mComponentState == ComponentState::STARTED) {
             mVDAAdaptor->flush();
             mComponentState = ComponentState::DRAINING;
-            mDrainWithEOS = drainMode == DRAIN_COMPONENT_WITH_EOS;
+            mPendingOutputEOS = drainMode == DRAIN_COMPONENT_WITH_EOS;
         } else {
             ALOGV("Neglect drain. Component in state: %d", mComponentState);
         }
@@ -452,13 +466,15 @@
     } else if (mComponentState == ComponentState::STOPPING) {
         // The client signals stop right before VDA notifies drain done. Let stop process goes.
         return;
-    } else {
+    } else if (mComponentState != ComponentState::FLUSHING) {
+        // It is reasonable to get onDrainDone in FLUSHING, which means flush is already signaled
+        // and component should still expect onFlushDone callback from VDA.
         ALOGE("Unexpected state while onDrainDone(). State=%d", mComponentState);
         reportError(C2_BAD_STATE);
         return;
     }
 
-    if (mDrainWithEOS) {
+    if (mPendingOutputEOS) {
         // Return EOS work.
         reportEOSWork();
     }
@@ -470,27 +486,28 @@
 
     // Work dequeueing was stopped while component draining. Restart it.
     mTaskRunner->PostTask(FROM_HERE,
-                          base::Bind(&C2VDAComponent::onDequeueWork, base::Unretained(this)));
+                          ::base::Bind(&C2VDAComponent::onDequeueWork, ::base::Unretained(this)));
 }
 
 void C2VDAComponent::onFlush() {
     DCHECK(mTaskRunner->BelongsToCurrentThread());
     ALOGV("onFlush");
-    if (mComponentState == ComponentState::FLUSHING) {
-        return;  // Ignore other flush request when component is flushing.
+    if (mComponentState == ComponentState::FLUSHING ||
+        mComponentState == ComponentState::STOPPING) {
+        return;  // Ignore other flush request when component is flushing or stopping.
     }
-    EXPECT_STATE_OR_RETURN_ON_ERROR(STARTED);
+    EXPECT_RUNNING_OR_RETURN_ON_ERROR();
 
     mVDAAdaptor->reset();
-    // Pop all works in mQueue and put into mPendingWorks.
+    // Pop all works in mQueue and put into mAbandonedWorks.
     while (!mQueue.empty()) {
-        mPendingWorks.emplace_back(std::move(mQueue.front().mWork));
+        mAbandonedWorks.emplace_back(std::move(mQueue.front().mWork));
         mQueue.pop();
     }
     mComponentState = ComponentState::FLUSHING;
 }
 
-void C2VDAComponent::onStop(base::WaitableEvent* done) {
+void C2VDAComponent::onStop(::base::WaitableEvent* done) {
     DCHECK(mTaskRunner->BelongsToCurrentThread());
     ALOGV("onStop");
     EXPECT_RUNNING_OR_RETURN_ON_ERROR();
@@ -501,9 +518,9 @@
         mVDAAdaptor->reset();
     }
 
-    // Pop all works in mQueue and put into mPendingWorks.
+    // Pop all works in mQueue and put into mAbandonedWorks.
     while (!mQueue.empty()) {
-        mPendingWorks.emplace_back(std::move(mQueue.front().mWork));
+        mAbandonedWorks.emplace_back(std::move(mQueue.front().mWork));
         mQueue.pop();
     }
 
@@ -531,15 +548,16 @@
     // Reset the timestamp record.
     mLastOutputTimestamp = -1;
     mComponentState = ComponentState::STARTED;
+
+    // Work dequeueing was stopped while component flushing. Restart it.
+    mTaskRunner->PostTask(FROM_HERE,
+                          ::base::Bind(&C2VDAComponent::onDequeueWork, ::base::Unretained(this)));
 }
 
 void C2VDAComponent::onStopDone() {
     ALOGV("onStopDone");
     CHECK(mStopDoneEvent);
 
-    // Release the graphic block allocator object.
-    mOutputBlockPool.reset();
-
     // TODO(johnylin): At this moment, there may be C2Buffer still owned by client, do we need to
     // do something for them?
     reportAbandonedWorks();
@@ -552,6 +570,9 @@
 
     mGraphicBlocks.clear();
 
+    mMockBufferQueueInClient.clear();  // Hack(b/79239042)
+    stopDequeueThread();
+
     mStopDoneEvent->Signal();
     mStopDoneEvent = nullptr;
     mComponentState = ComponentState::UNINITIALIZED;
@@ -606,6 +627,19 @@
     return &mGraphicBlocks[blockId];
 }
 
+C2VDAComponent::GraphicBlockInfo* C2VDAComponent::getGraphicBlockBySlot(uint32_t slotId) {
+    auto blockIter = std::find_if(mGraphicBlocks.begin(), mGraphicBlocks.end(),
+                                  [slotId](const GraphicBlockInfo& gb) {
+                                      return gb.mSlotId == slotId;
+                                  });
+
+    if (blockIter == mGraphicBlocks.end()) {
+        ALOGE("getGraphicBlockBySlot failed: slot=%u", slotId);
+        return nullptr;
+    }
+    return &(*blockIter);
+}
+
 void C2VDAComponent::onOutputFormatChanged(std::unique_ptr<VideoFormat> format) {
     DCHECK(mTaskRunner->BelongsToCurrentThread());
     ALOGV("onOutputFormatChanged");
@@ -666,30 +700,55 @@
                                                               uint32_t pixelFormat) {
     ALOGV("allocateBuffersFromBlockAllocator(%s, 0x%x)", size.ToString().c_str(), pixelFormat);
 
+    mMockBufferQueueInClient.clear();  // Hack(b/79239042)
+    stopDequeueThread();
+
     size_t bufferCount = mOutputFormat.mMinNumBuffers + kDpbOutputBufferExtraCount;
 
     // Allocate the output buffers.
     mVDAAdaptor->assignPictureBuffers(bufferCount);
 
     // Get block pool ID configured from the client.
+    std::shared_ptr<C2BlockPool> blockPool;
     auto poolId = mIntfImpl->getBlockPoolId();
     ALOGI("Using C2BlockPool ID = %" PRIu64 " for allocating output buffers", poolId);
-    c2_status_t err;
-    if (!mOutputBlockPool || mOutputBlockPool->getLocalId() != poolId) {
-        err = GetCodec2BlockPool(poolId, shared_from_this(), &mOutputBlockPool);
-        if (err != C2_OK) {
-            ALOGE("Graphic block allocator is invalid");
-            reportError(err);
-            return err;
-        }
+    auto err = GetCodec2BlockPool(poolId, shared_from_this(), &blockPool);
+    if (err != C2_OK) {
+        ALOGE("Graphic block allocator is invalid");
+        reportError(err);
+        return err;
     }
 
     mGraphicBlocks.clear();
+
+    if (blockPool->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
+        // Set requested buffer count to C2VdaBqBlockPool.
+        std::shared_ptr<C2VdaBqBlockPool> bqPool =
+                std::static_pointer_cast<C2VdaBqBlockPool>(blockPool);
+        if (bqPool) {
+            err = bqPool->requestNewBufferSet(static_cast<int32_t>(bufferCount));
+            if (err == C2_NO_INIT) {
+                ALOGD("No surface in block pool, output is byte-buffer mode...");
+                mSurfaceMode = false;
+            } else if (err != C2_OK) {
+                ALOGE("failed to set buffer count magic to block pool: %d", err);
+                reportError(err);
+                return err;
+            }
+        } else {
+            ALOGE("static_pointer_cast C2VdaBqBlockPool failed...");
+            reportError(C2_CORRUPTED);
+            return C2_CORRUPTED;
+        }
+    } else {  // CCodec falls back to use C2BasicGraphicBlockPool
+        ALOGD("CCodec falls back to use C2BasicGraphicBlockPool...");
+        mSurfaceMode = false;
+    }
+
     for (size_t i = 0; i < bufferCount; ++i) {
         std::shared_ptr<C2GraphicBlock> block;
         C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, 0};
-        err = mOutputBlockPool->fetchGraphicBlock(size.width(), size.height(), pixelFormat, usage,
-                                                  &block);
+        err = blockPool->fetchGraphicBlock(size.width(), size.height(), pixelFormat, usage, &block);
         if (err != C2_OK) {
             mGraphicBlocks.clear();
             ALOGE("failed to allocate buffer: %d", err);
@@ -699,6 +758,11 @@
         appendOutputBuffer(std::move(block));
     }
     mOutputFormat.mMinNumBuffers = bufferCount;
+
+    if (mSurfaceMode && !startDequeueThread(size, pixelFormat, std::move(blockPool))) {
+        reportError(C2_CORRUPTED);
+        return C2_CORRUPTED;
+    }
     return C2_OK;
 }
 
@@ -752,7 +816,7 @@
 #endif
     ALOGV("HAL pixel format: 0x%x", static_cast<uint32_t>(info.mPixelFormat));
 
-    base::ScopedFD passedHandle(dup(info.mGraphicBlock->handle()->data[0]));
+    ::base::ScopedFD passedHandle(dup(info.mGraphicBlock->handle()->data[0]));
     if (!passedHandle.is_valid()) {
         ALOGE("Failed to dup(%d), errno=%d", info.mGraphicBlock->handle()->data[0], errno);
         reportError(C2_CORRUPTED);
@@ -766,6 +830,12 @@
     info.mHandle = std::move(passedHandle);
     info.mPlanes = std::move(passedPlanes);
 
+    if (mSurfaceMode) {
+        info.mSlotId = getSlotFromGraphicBlockHandle(info.mGraphicBlock->handle());
+    } else {  // byte-buffer mode
+        info.mSlotId = static_cast<uint32_t>(info.mBlockId);
+    }
+
     mGraphicBlocks.push_back(std::move(info));
 }
 
@@ -808,8 +878,8 @@
     }
     while (!items->empty()) {
         mTaskRunner->PostTask(FROM_HERE,
-                              base::Bind(&C2VDAComponent::onQueueWork, base::Unretained(this),
-                                         base::Passed(&items->front())));
+                              ::base::Bind(&C2VDAComponent::onQueueWork, ::base::Unretained(this),
+                                           ::base::Passed(&items->front())));
         items->pop_front();
     }
     return C2_OK;
@@ -828,7 +898,8 @@
     if (mState.load() != State::RUNNING) {
         return C2_BAD_STATE;
     }
-    mTaskRunner->PostTask(FROM_HERE, base::Bind(&C2VDAComponent::onFlush, base::Unretained(this)));
+    mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onFlush,
+                                                  ::base::Unretained(this)));
     // Instead of |flushedWork|, abandoned works will be returned via onWorkDone_nb() callback.
     return C2_OK;
 }
@@ -840,8 +911,9 @@
     if (mState.load() != State::RUNNING) {
         return C2_BAD_STATE;
     }
-    mTaskRunner->PostTask(FROM_HERE, base::Bind(&C2VDAComponent::onDrain, base::Unretained(this),
-                                                static_cast<uint32_t>(mode)));
+    mTaskRunner->PostTask(FROM_HERE,
+                          ::base::Bind(&C2VDAComponent::onDrain, ::base::Unretained(this),
+                                       static_cast<uint32_t>(mode)));
     return C2_OK;
 }
 
@@ -856,10 +928,11 @@
     mCodecProfile = mIntfImpl->getCodecProfile();
     ALOGI("get parameter: mCodecProfile = %d", static_cast<int>(mCodecProfile));
 
-    base::WaitableEvent done(base::WaitableEvent::ResetPolicy::AUTOMATIC,
-                             base::WaitableEvent::InitialState::NOT_SIGNALED);
-    mTaskRunner->PostTask(FROM_HERE, base::Bind(&C2VDAComponent::onStart, base::Unretained(this),
-                                                mCodecProfile, &done));
+    ::base::WaitableEvent done(::base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                               ::base::WaitableEvent::InitialState::NOT_SIGNALED);
+    mTaskRunner->PostTask(FROM_HERE,
+                          ::base::Bind(&C2VDAComponent::onStart, ::base::Unretained(this),
+                                       mCodecProfile, &done));
     done.Wait();
     if (mVDAInitResult != VideoDecodeAcceleratorAdaptor::Result::SUCCESS) {
         ALOGE("Failed to start component due to VDA error: %d", static_cast<int>(mVDAInitResult));
@@ -878,10 +951,10 @@
         return C2_OK;  // Component is already in stopped state.
     }
 
-    base::WaitableEvent done(base::WaitableEvent::ResetPolicy::AUTOMATIC,
-                             base::WaitableEvent::InitialState::NOT_SIGNALED);
+    ::base::WaitableEvent done(::base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                               ::base::WaitableEvent::InitialState::NOT_SIGNALED);
     mTaskRunner->PostTask(FROM_HERE,
-                          base::Bind(&C2VDAComponent::onStop, base::Unretained(this), &done));
+                          ::base::Bind(&C2VDAComponent::onStop, ::base::Unretained(this), &done));
     done.Wait();
     mState.store(State::LOADED);
     return C2_OK;
@@ -910,8 +983,9 @@
     // Set mRequestedVisibleRect to default.
     mRequestedVisibleRect = media::Rect();
 
-    mTaskRunner->PostTask(FROM_HERE, base::Bind(&C2VDAComponent::onOutputFormatChanged,
-                                                base::Unretained(this), base::Passed(&format)));
+    mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onOutputFormatChanged,
+                                                  ::base::Unretained(this),
+                                                  ::base::Passed(&format)));
 }
 
 void C2VDAComponent::dismissPictureBuffer(int32_t pictureBufferId) {
@@ -926,28 +1000,28 @@
 
     if (mRequestedVisibleRect != cropRect) {
         mRequestedVisibleRect = cropRect;
-        mTaskRunner->PostTask(FROM_HERE, base::Bind(&C2VDAComponent::onVisibleRectChanged,
-                                                    base::Unretained(this), cropRect));
+        mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onVisibleRectChanged,
+                                                      ::base::Unretained(this), cropRect));
     }
 
-    mTaskRunner->PostTask(FROM_HERE,
-                          base::Bind(&C2VDAComponent::onOutputBufferDone, base::Unretained(this),
-                                     pictureBufferId, bitstreamId));
+    mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onOutputBufferDone,
+                                                  ::base::Unretained(this),
+                                                  pictureBufferId, bitstreamId));
 }
 
 void C2VDAComponent::notifyEndOfBitstreamBuffer(int32_t bitstreamId) {
-    mTaskRunner->PostTask(FROM_HERE, base::Bind(&C2VDAComponent::onInputBufferDone,
-                                                base::Unretained(this), bitstreamId));
+    mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onInputBufferDone,
+                                                  ::base::Unretained(this), bitstreamId));
 }
 
 void C2VDAComponent::notifyFlushDone() {
     mTaskRunner->PostTask(FROM_HERE,
-                          base::Bind(&C2VDAComponent::onDrainDone, base::Unretained(this)));
+                          ::base::Bind(&C2VDAComponent::onDrainDone, ::base::Unretained(this)));
 }
 
 void C2VDAComponent::notifyResetDone() {
     mTaskRunner->PostTask(FROM_HERE,
-                          base::Bind(&C2VDAComponent::onResetDone, base::Unretained(this)));
+                          ::base::Bind(&C2VDAComponent::onResetDone, ::base::Unretained(this)));
 }
 
 void C2VDAComponent::notifyError(VideoDecodeAcceleratorAdaptor::Result error) {
@@ -1003,16 +1077,17 @@
 }
 
 bool C2VDAComponent::isWorkDone(const C2Work* work) const {
-    if (work->input.buffers.front()) {
-        // Input buffer is still owned by VDA.
-        // This condition could also recognize dummy EOS work since it won't get
-        // onInputBufferDone(), input buffer won't be reset until reportEOSWork().
+    if (work->input.buffers.empty()) {
+        // This is EOS work with no input buffer and should be processed by reportEOSWork().
         return false;
     }
-    if (mComponentState == ComponentState::DRAINING && mDrainWithEOS &&
-        mPendingWorks.size() == 1u) {
-        // If component is in DRAINING state and mDrainWithEOS is true. The last returned work
-        // should be marked EOS flag and returned by reportEOSWork() instead.
+    if (work->input.buffers.front()) {
+        // Input buffer is still owned by VDA.
+        return false;
+    }
+    if (mPendingOutputEOS && mPendingWorks.size() == 1u) {
+        // If mPendingOutputEOS is true, the last returned work should be marked EOS flag and
+        // returned by reportEOSWork() instead.
         return false;
     }
     if (mLastOutputTimestamp < 0) {
@@ -1034,9 +1109,13 @@
         return;
     }
 
+    mPendingOutputEOS = false;
+
     std::unique_ptr<C2Work> eosWork(std::move(mPendingWorks.front()));
     mPendingWorks.pop_front();
-    eosWork->input.buffers.front().reset();
+    if (!eosWork->input.buffers.empty()) {
+        eosWork->input.buffers.front().reset();
+    }
     eosWork->result = C2_OK;
     eosWork->workletsProcessed = static_cast<uint32_t>(eosWork->worklets.size());
     eosWork->worklets.front()->output.flags = C2FrameData::FLAG_END_OF_STREAM;
@@ -1056,11 +1135,27 @@
 
         // TODO: correlate the definition of flushed work result to framework.
         work->result = C2_NOT_FOUND;
-        // When the work is abandoned, the input.buffers.front() shall reset by component.
-        work->input.buffers.front().reset();
+        // When the work is abandoned, buffer in input.buffers shall reset by component.
+        if (!work->input.buffers.empty()) {
+            work->input.buffers.front().reset();
+        }
         abandonedWorks.emplace_back(std::move(work));
     }
 
+    for (auto& work : mAbandonedWorks) {
+        // TODO: correlate the definition of flushed work result to framework.
+        work->result = C2_NOT_FOUND;
+        // When the work is abandoned, buffer in input.buffers shall reset by component.
+        if (!work->input.buffers.empty()) {
+            work->input.buffers.front().reset();
+        }
+        abandonedWorks.emplace_back(std::move(work));
+    }
+    mAbandonedWorks.clear();
+
+    // Pending EOS work will be abandoned here due to component flush if any.
+    mPendingOutputEOS = false;
+
     if (!abandonedWorks.empty()) {
         mListener->onWorkDone_nb(shared_from_this(), std::move(abandonedWorks));
     }
@@ -1070,6 +1165,58 @@
     mListener->onError_nb(shared_from_this(), static_cast<uint32_t>(error));
 }
 
+bool C2VDAComponent::startDequeueThread(const media::Size& size, uint32_t pixelFormat,
+                                        std::shared_ptr<C2BlockPool> blockPool) {
+    CHECK(!mDequeueThread.IsRunning());
+    if (!mDequeueThread.Start()) {
+        ALOGE("failed to start dequeue thread!!");
+        return false;
+    }
+    mDequeueLoopStop.store(false);
+    mBuffersInClient.store(0u);
+    mDequeueThread.task_runner()->PostTask(
+            FROM_HERE, ::base::Bind(&C2VDAComponent::dequeueThreadLoop, ::base::Unretained(this),
+                                    size, pixelFormat, std::move(blockPool)));
+    return true;
+}
+
+void C2VDAComponent::stopDequeueThread() {
+    if (mDequeueThread.IsRunning()) {
+        mDequeueLoopStop.store(true);
+        mDequeueThread.Stop();
+    }
+}
+
+void C2VDAComponent::dequeueThreadLoop(const media::Size& size, uint32_t pixelFormat,
+                                       std::shared_ptr<C2BlockPool> blockPool) {
+    ALOGV("dequeueThreadLoop starts");
+    DCHECK(mDequeueThread.task_runner()->BelongsToCurrentThread());
+
+    while (!mDequeueLoopStop.load()) {
+        if (mBuffersInClient.load() == 0) {
+            ::usleep(kDequeueRetryDelayUs);  // wait for retry
+            continue;
+        }
+        std::shared_ptr<C2GraphicBlock> block;
+        C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, 0};
+        auto err = blockPool->fetchGraphicBlock(size.width(), size.height(), pixelFormat, usage,
+                                                &block);
+        if (err == C2_TIMED_OUT) {
+            continue;  // wait for retry
+        }
+        if (err == C2_OK) {
+            auto slot = getSlotFromGraphicBlockHandle(block->handle());
+            mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onOutputBufferReturned,
+                                                          ::base::Unretained(this), slot));
+            mBuffersInClient--;
+        } else {
+            ALOGE("dequeueThreadLoop got error: %d", err);
+            break;
+        }
+    }
+    ALOGV("dequeueThreadLoop terminates");
+}
+
 class C2VDAComponentFactory : public C2ComponentFactory {
 public:
     C2VDAComponentFactory(C2String decoderName)
diff --git a/include/C2ArcVideoAcceleratorFactory.h b/include/C2ArcVideoAcceleratorFactory.h
deleted file mode 100644
index 9cdaf37..0000000
--- a/include/C2ArcVideoAcceleratorFactory.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2018 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_C2_ARC_VIDEO_ACCELERATOR_FACTORY_H
-#define ANDROID_C2_ARC_VIDEO_ACCELERATOR_FACTORY_H
-
-#include <media/arcvideobridge/IArcVideoBridge.h>
-#include <utils/Singleton.h>
-
-#include <components/arc/common/video.mojom.h>
-#include <components/arc/common/video_decode_accelerator.mojom.h>
-#include <components/arc/common/video_encode_accelerator.mojom.h>
-
-namespace android {
-// Helper class to create message pipe to the ArcVideoAccelerator.
-// This class should only be used in the Mojo thread.
-class C2ArcVideoAcceleratorFactory : public Singleton<C2ArcVideoAcceleratorFactory> {
-public:
-    bool createVideoDecodeAccelerator(::arc::mojom::VideoDecodeAcceleratorRequest request);
-    bool createVideoEncodeAccelerator(::arc::mojom::VideoEncodeAcceleratorRequest request);
-    bool createVideoProtectedBufferAllocator(
-            ::arc::mojom::VideoProtectedBufferAllocatorRequest request);
-    int32_t hostVersion() const;
-
-private:
-    C2ArcVideoAcceleratorFactory();
-
-    uint32_t mHostVersion;
-    sp<IArcVideoBridge> mArcVideoBridge;
-    ::arc::mojom::VideoAcceleratorFactoryPtr mRemoteFactory;
-
-    friend class Singleton<C2ArcVideoAcceleratorFactory>;
-};
-}  // namespace android
-
-#endif  // ANDROID_C2_ARC_VIDEO_ACCELERATOR_FACTORY_H
diff --git a/include/C2VDAComponent.h b/include/C2VDAComponent.h
index 6df3fe7..fcca0c9 100644
--- a/include/C2VDAComponent.h
+++ b/include/C2VDAComponent.h
@@ -132,10 +132,6 @@
         ERROR,
     };
 
-    enum {
-        kDpbOutputBufferExtraCount = 3,  // Use the same number as ACodec.
-    };
-
     // This constant is used to tell apart from drain_mode_t enumerations in C2Component.h, which
     // means no drain request.
     // Note: this value must be different than all enumerations in drain_mode_t.
@@ -156,13 +152,14 @@
         };
 
         int32_t mBlockId = -1;
+        uint32_t mSlotId = 0;
         State mState = State::OWNED_BY_COMPONENT;
         // Graphic block buffer allocated from allocator. This should be reused.
         std::shared_ptr<C2GraphicBlock> mGraphicBlock;
         // HAL pixel format used while importing to VDA.
         HalPixelFormat mPixelFormat;
         // The handle dupped from graphic block for importing to VDA.
-        base::ScopedFD mHandle;
+        ::base::ScopedFD mHandle;
         // VideoFramePlane information for importing to VDA.
         std::vector<VideoFramePlane> mPlanes;
     };
@@ -178,12 +175,9 @@
                     media::Rect visibleRect);
     };
 
-    // Used as the release callback for C2VDAGraphicBuffer to get back the output buffer.
-    void returnOutputBuffer(int32_t pictureBufferId);
-
     // These tasks should be run on the component thread |mThread|.
     void onDestroy();
-    void onStart(media::VideoCodecProfile profile, base::WaitableEvent* done);
+    void onStart(media::VideoCodecProfile profile, ::base::WaitableEvent* done);
     void onQueueWork(std::unique_ptr<C2Work> work);
     void onDequeueWork();
     void onInputBufferDone(int32_t bitstreamId);
@@ -191,13 +185,13 @@
     void onDrain(uint32_t drainMode);
     void onDrainDone();
     void onFlush();
-    void onStop(base::WaitableEvent* done);
+    void onStop(::base::WaitableEvent* done);
     void onResetDone();
     void onFlushDone();
     void onStopDone();
     void onOutputFormatChanged(std::unique_ptr<VideoFormat> format);
     void onVisibleRectChanged(const media::Rect& cropRect);
-    void onOutputBufferReturned(int32_t pictureBufferId);
+    void onOutputBufferReturned(uint32_t slotId);
 
     // Send input buffer to accelerator with specified bitstream id.
     void sendInputBufferToAccelerator(const C2ConstLinearBlock& input, int32_t bitstreamId);
@@ -207,6 +201,8 @@
     void setOutputFormatCrop(const media::Rect& cropRect);
     // Helper function to get the specified GraphicBlockInfo object by its id.
     GraphicBlockInfo* getGraphicBlockById(int32_t blockId);
+    // Helper function to get the specified GraphicBlockInfo object by its slot index.
+    GraphicBlockInfo* getGraphicBlockBySlot(uint32_t slotId);
     // Helper function to get the specified work in mPendingWorks by bitstream id.
     C2Work* getPendingWorkByBitstreamId(int32_t bitstreamId);
     // Try to apply the output format change.
@@ -220,13 +216,22 @@
     void reportFinishedWorkIfAny();
     // Make onWorkDone call to listener for reporting EOS work in mPendingWorks.
     void reportEOSWork();
-    // Abandon all works in mPendingWorks.
+    // Abandon all works in mPendingWorks and mAbandonedWorks.
     void reportAbandonedWorks();
     // Make onError call to listener for reporting errors.
     void reportError(c2_status_t error);
     // Helper function to determine if the work is finished.
     bool isWorkDone(const C2Work* work) const;
 
+    // Start dequeue thread, return true on success.
+    bool startDequeueThread(const media::Size& size, uint32_t pixelFormat,
+                            std::shared_ptr<C2BlockPool> blockPool);
+    // Stop dequeue thread.
+    void stopDequeueThread();
+    // The rountine task running on dequeue thread.
+    void dequeueThreadLoop(const media::Size& size, uint32_t pixelFormat,
+                           std::shared_ptr<C2BlockPool> blockPool);
+
     // The pointer of component interface implementation.
     std::shared_ptr<IntfImpl> mIntfImpl;
     // The pointer of component interface.
@@ -235,9 +240,16 @@
     std::shared_ptr<Listener> mListener;
 
     // The main component thread.
-    base::Thread mThread;
+    ::base::Thread mThread;
     // The task runner on component thread.
-    scoped_refptr<base::SingleThreadTaskRunner> mTaskRunner;
+    scoped_refptr<::base::SingleThreadTaskRunner> mTaskRunner;
+
+    // The dequeue buffer loop thread.
+    ::base::Thread mDequeueThread;
+    // The stop signal for dequeue loop which should be atomic (toggled by main thread).
+    std::atomic<bool> mDequeueLoopStop;
+    // The count of buffers owned by client which should be atomic.
+    std::atomic<uint32_t> mBuffersInClient;
 
     // The following members should be utilized on component thread |mThread|.
 
@@ -247,12 +259,13 @@
     std::unique_ptr<VideoDecodeAcceleratorAdaptor> mVDAAdaptor;
     // The done event pointer of stop procedure. It should be restored in onStop() and signaled in
     // onStopDone().
-    base::WaitableEvent* mStopDoneEvent;
+    ::base::WaitableEvent* mStopDoneEvent;
     // The state machine on component thread.
     ComponentState mComponentState;
-    // The indicator of drain mode (true for draining with EOS). This should be always set along
-    // with component going to DRAINING state, and only regarded under DRAINING state.
-    bool mDrainWithEOS;
+    // The indicator of draining with EOS. This should be always set along with component going to
+    // DRAINING state, and will be unset either after reportEOSWork() (EOS is outputted), or
+    // reportAbandonedWorks() (drain is cancelled and works are abandoned).
+    bool mPendingOutputEOS;
     // The vector of storing allocated output graphic block information.
     std::vector<GraphicBlockInfo> mGraphicBlocks;
     // The work queue. Works are queued along with drain mode from component API queue_nb and
@@ -261,6 +274,9 @@
     // Store all pending works. The dequeued works are placed here until they are finished and then
     // sent out by onWorkDone call to listener.
     std::deque<std::unique_ptr<C2Work>> mPendingWorks;
+    // Store all abandoned works. When component gets flushed/stopped, remaining works in queue are
+    // dumped here and sent out by onWorkDone call to listener after flush/stop is finished.
+    std::vector<std::unique_ptr<C2Work>> mAbandonedWorks;
     // Store the visible rect provided from VDA. If this is changed, component should issue a
     // visible size change event.
     media::Rect mRequestedVisibleRect;
@@ -272,8 +288,13 @@
     // Record the timestamp of the last output buffer. This is used to determine if the work is
     // finished.
     int64_t mLastOutputTimestamp;
-    // The pointer of output block pool.
-    std::shared_ptr<C2BlockPool> mOutputBlockPool;
+    // Hack(b/79239042): We do not have a solution to recycle buffers in byte-buffer mode now. This
+    // is a fake buffer queue to record buffers outputted to client, and regard buffer is returned
+    // when it is popped by a new push of the queue (size: kMockMaxBuffersInClient).
+    // TODO: provide proper solution and get rid of this hack.
+    std::list<uint32_t> mMockBufferQueueInClient;
+    // The indicator of whether output has surface.
+    bool mSurfaceMode;
 
     // The following members should be utilized on parent thread.
 
@@ -285,7 +306,7 @@
     std::mutex mStartStopLock;
 
     // The WeakPtrFactory for getting weak pointer of this.
-    base::WeakPtrFactory<C2VDAComponent> mWeakThisFactory;
+    ::base::WeakPtrFactory<C2VDAComponent> mWeakThisFactory;
 
     DISALLOW_COPY_AND_ASSIGN(C2VDAComponent);
 };
diff --git a/tests/Android.mk b/tests/Android.mk
index 4bafb4a..3b384b4 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -21,6 +21,7 @@
   libv4l2_codec2_vda \
 
 LOCAL_C_INCLUDES += \
+  $(TOP)/device/google/cheets2/codec2/vdastore/include \
   $(TOP)/external/v4l2_codec2/include \
   $(TOP)/external/v4l2_codec2/vda \
   $(TOP)/hardware/google/av/codec2/include \
diff --git a/tests/C2VDACompIntf_test.cpp b/tests/C2VDACompIntf_test.cpp
index 6ed0753..f08ee50 100644
--- a/tests/C2VDACompIntf_test.cpp
+++ b/tests/C2VDACompIntf_test.cpp
@@ -5,6 +5,7 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "C2VDACompIntf_test"
 
+#include <C2VDAAllocatorStore.h>
 #include <C2VDAComponent.h>
 
 #include <C2PlatformSupport.h>
@@ -30,7 +31,7 @@
 const char* MEDIA_MIMETYPE_VIDEO_AVC = "video/avc";
 
 const C2Allocator::id_t kInputAllocators[] = {C2PlatformAllocatorStore::ION};
-const C2Allocator::id_t kOutputAllocators[] = {C2PlatformAllocatorStore::GRALLOC};
+const C2Allocator::id_t kOutputAllocators[] = {C2VDAAllocatorStore::V4L2_BUFFERQUEUE};
 const C2BlockPool::local_id_t kDefaultOutputBlockPool = C2BlockPool::BASIC_GRAPHIC;
 
 class C2VDACompIntfTest : public ::testing::Test {
diff --git a/vda/Android.bp b/vda/Android.bp
index 5f84535..adba0ef 100644
--- a/vda/Android.bp
+++ b/vda/Android.bp
@@ -36,6 +36,7 @@
         "-Wall",
         "-Werror",
         "-Wno-unused-parameter",
+        "-Wno-implicit-fallthrough", // at h264_decoder.cc:1374
     ],
     clang: true,
     sanitize: {