Merge "AudioRecord: Fix uninitialized use of identity" into sc-dev
diff --git a/camera/aidl/android/hardware/ICameraServiceProxy.aidl b/camera/aidl/android/hardware/ICameraServiceProxy.aidl
index d428b4e..bbb0289 100644
--- a/camera/aidl/android/hardware/ICameraServiceProxy.aidl
+++ b/camera/aidl/android/hardware/ICameraServiceProxy.aidl
@@ -35,4 +35,10 @@
      * Update the status of a camera device.
      */
     oneway void notifyCameraState(in CameraSessionStats cameraSessionStats);
+
+    /**
+     * Reports whether the top activity needs a rotate and crop override.
+     */
+    boolean isRotateAndCropOverrideNeeded(String packageName, int sensorOrientation,
+            int lensFacing);
 }
diff --git a/media/codec2/components/avc/C2SoftAvcEnc.cpp b/media/codec2/components/avc/C2SoftAvcEnc.cpp
index 940f57c..bf9e5ff 100644
--- a/media/codec2/components/avc/C2SoftAvcEnc.cpp
+++ b/media/codec2/components/avc/C2SoftAvcEnc.cpp
@@ -1401,13 +1401,13 @@
             ps_inp_raw_buf->apv_bufs[1] = uPlane;
             ps_inp_raw_buf->apv_bufs[2] = vPlane;
 
-            ps_inp_raw_buf->au4_wd[0] = input->width();
-            ps_inp_raw_buf->au4_wd[1] = input->width() / 2;
-            ps_inp_raw_buf->au4_wd[2] = input->width() / 2;
+            ps_inp_raw_buf->au4_wd[0] = mSize->width;
+            ps_inp_raw_buf->au4_wd[1] = mSize->width / 2;
+            ps_inp_raw_buf->au4_wd[2] = mSize->width / 2;
 
-            ps_inp_raw_buf->au4_ht[0] = input->height();
-            ps_inp_raw_buf->au4_ht[1] = input->height() / 2;
-            ps_inp_raw_buf->au4_ht[2] = input->height() / 2;
+            ps_inp_raw_buf->au4_ht[0] = mSize->height;
+            ps_inp_raw_buf->au4_ht[1] = mSize->height / 2;
+            ps_inp_raw_buf->au4_ht[2] = mSize->height / 2;
 
             ps_inp_raw_buf->au4_strd[0] = yStride;
             ps_inp_raw_buf->au4_strd[1] = uStride;
@@ -1432,11 +1432,11 @@
             ps_inp_raw_buf->apv_bufs[0] = yPlane;
             ps_inp_raw_buf->apv_bufs[1] = uPlane;
 
-            ps_inp_raw_buf->au4_wd[0] = input->width();
-            ps_inp_raw_buf->au4_wd[1] = input->width();
+            ps_inp_raw_buf->au4_wd[0] = mSize->width;
+            ps_inp_raw_buf->au4_wd[1] = mSize->width;
 
-            ps_inp_raw_buf->au4_ht[0] = input->height();
-            ps_inp_raw_buf->au4_ht[1] = input->height() / 2;
+            ps_inp_raw_buf->au4_ht[0] = mSize->height;
+            ps_inp_raw_buf->au4_ht[1] = mSize->height / 2;
 
             ps_inp_raw_buf->au4_strd[0] = yStride;
             ps_inp_raw_buf->au4_strd[1] = uStride;
diff --git a/media/codec2/hidl/1.0/utils/Android.bp b/media/codec2/hidl/1.0/utils/Android.bp
index 008def8..122aacd 100644
--- a/media/codec2/hidl/1.0/utils/Android.bp
+++ b/media/codec2/hidl/1.0/utils/Android.bp
@@ -15,7 +15,6 @@
     defaults: ["hidl_defaults"],
 
     srcs: [
-        "OutputBufferQueue.cpp",
         "types.cpp",
     ],
 
diff --git a/media/codec2/hidl/1.1/utils/Android.bp b/media/codec2/hidl/1.1/utils/Android.bp
index 71a113d..0eeedb6 100644
--- a/media/codec2/hidl/1.1/utils/Android.bp
+++ b/media/codec2/hidl/1.1/utils/Android.bp
@@ -15,7 +15,6 @@
     defaults: ["hidl_defaults"],
 
     srcs: [
-        "OutputBufferQueue.cpp",
         "types.cpp",
     ],
 
diff --git a/media/codec2/hidl/1.1/utils/OutputBufferQueue.cpp b/media/codec2/hidl/1.1/utils/OutputBufferQueue.cpp
deleted file mode 100644
index 65756e8..0000000
--- a/media/codec2/hidl/1.1/utils/OutputBufferQueue.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright 2019 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 <codec2/hidl/1.1/OutputBufferQueue.h>
diff --git a/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/OutputBufferQueue.h b/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/OutputBufferQueue.h
deleted file mode 100644
index f77852d..0000000
--- a/media/codec2/hidl/1.1/utils/include/codec2/hidl/1.1/OutputBufferQueue.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef CODEC2_HIDL_V1_1_UTILS_OUTPUT_BUFFER_QUEUE
-#define CODEC2_HIDL_V1_1_UTILS_OUTPUT_BUFFER_QUEUE
-
-#include <codec2/hidl/1.0/OutputBufferQueue.h>
-#include <codec2/hidl/1.1/types.h>
-
-namespace android {
-namespace hardware {
-namespace media {
-namespace c2 {
-namespace V1_1 {
-namespace utils {
-
-using ::android::hardware::media::c2::V1_0::utils::OutputBufferQueue;
-
-} // namespace utils
-} // namespace V1_1
-} // namespace c2
-} // namespace media
-} // namespace hardware
-} // namespace android
-
-#endif // CODEC2_HIDL_V1_1_UTILS_OUTPUT_BUFFER_QUEUE
diff --git a/media/codec2/hidl/1.2/utils/Android.bp b/media/codec2/hidl/1.2/utils/Android.bp
index 0a8d256..e4e4ad5 100644
--- a/media/codec2/hidl/1.2/utils/Android.bp
+++ b/media/codec2/hidl/1.2/utils/Android.bp
@@ -15,7 +15,6 @@
     defaults: ["hidl_defaults"],
 
     srcs: [
-        "OutputBufferQueue.cpp",
         "types.cpp",
     ],
 
diff --git a/media/codec2/hidl/1.2/utils/Component.cpp b/media/codec2/hidl/1.2/utils/Component.cpp
index 1de33b4..8924e6d 100644
--- a/media/codec2/hidl/1.2/utils/Component.cpp
+++ b/media/codec2/hidl/1.2/utils/Component.cpp
@@ -480,9 +480,31 @@
 Return<Status> Component::setOutputSurfaceWithSyncObj(
         uint64_t blockPoolId, const sp<HGraphicBufferProducer2>& surface,
         const SurfaceSyncObj& syncObject) {
-    (void) blockPoolId;
-    (void) surface;
-    (void) syncObject;
+    std::shared_ptr<C2BlockPool> pool;
+    GetCodec2BlockPool(blockPoolId, mComponent, &pool);
+    if (pool && pool->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
+        std::shared_ptr<C2BufferQueueBlockPool> bqPool =
+                std::static_pointer_cast<C2BufferQueueBlockPool>(pool);
+        C2BufferQueueBlockPool::OnRenderCallback cb =
+            [this](uint64_t producer, int32_t slot, int64_t nsecs) {
+                // TODO: batch this
+                hidl_vec<IComponentListener::RenderedFrame> rendered;
+                rendered.resize(1);
+                rendered[0] = { producer, slot, nsecs };
+                (void)mListener->onFramesRendered(rendered).isOk();
+        };
+        if (bqPool) {
+            const native_handle_t *h = syncObject.syncMemory;
+            native_handle_t *syncMemory = h ? native_handle_clone(h) : nullptr;
+            uint64_t bqId = syncObject.bqId;
+            uint32_t generationId = syncObject.generationId;
+            uint64_t consumerUsage = syncObject.consumerUsage;
+
+            bqPool->setRenderCallback(cb);
+            bqPool->configureProducer(surface, syncMemory, bqId,
+                                      generationId, consumerUsage);
+        }
+    }
     return Status::OK;
 }
 
diff --git a/media/codec2/hidl/1.2/utils/OutputBufferQueue.cpp b/media/codec2/hidl/1.2/utils/OutputBufferQueue.cpp
deleted file mode 100644
index 12b5f5b..0000000
--- a/media/codec2/hidl/1.2/utils/OutputBufferQueue.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright 2021 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 <codec2/hidl/1.2/OutputBufferQueue.h>
diff --git a/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/OutputBufferQueue.h b/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/OutputBufferQueue.h
deleted file mode 100644
index 9fd5f07..0000000
--- a/media/codec2/hidl/1.2/utils/include/codec2/hidl/1.2/OutputBufferQueue.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef CODEC2_HIDL_V1_2_UTILS_OUTPUT_BUFFER_QUEUE
-#define CODEC2_HIDL_V1_2_UTILS_OUTPUT_BUFFER_QUEUE
-
-#include <codec2/hidl/1.0/OutputBufferQueue.h>
-#include <codec2/hidl/1.2/types.h>
-
-namespace android {
-namespace hardware {
-namespace media {
-namespace c2 {
-namespace V1_2 {
-namespace utils {
-
-using ::android::hardware::media::c2::V1_0::utils::OutputBufferQueue;
-
-} // namespace utils
-} // namespace V1_2
-} // namespace c2
-} // namespace media
-} // namespace hardware
-} // namespace android
-
-#endif // CODEC2_HIDL_V1_2_UTILS_OUTPUT_BUFFER_QUEUE
diff --git a/media/codec2/hidl/client/Android.bp b/media/codec2/hidl/client/Android.bp
index b3ca5b1..0e52813 100644
--- a/media/codec2/hidl/client/Android.bp
+++ b/media/codec2/hidl/client/Android.bp
@@ -12,6 +12,11 @@
 
     srcs: [
         "client.cpp",
+        "output.cpp",
+    ],
+
+    header_libs: [
+        "libcodec2_internal", // private
     ],
 
     shared_libs: [
diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp
index 0a61fe2..0296004 100644
--- a/media/codec2/hidl/client/client.cpp
+++ b/media/codec2/hidl/client/client.cpp
@@ -33,17 +33,17 @@
 
 #include <android-base/properties.h>
 #include <bufferpool/ClientManager.h>
-#include <codec2/hidl/1.0/OutputBufferQueue.h>
 #include <codec2/hidl/1.0/types.h>
-#include <codec2/hidl/1.1/OutputBufferQueue.h>
 #include <codec2/hidl/1.1/types.h>
 #include <codec2/hidl/1.2/types.h>
+#include <codec2/hidl/output.h>
 
 #include <cutils/native_handle.h>
 #include <gui/bufferqueue/2.0/B2HGraphicBufferProducer.h>
 #include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h>
 #include <hidl/HidlSupport.h>
 
+
 #include <deque>
 #include <iterator>
 #include <limits>
@@ -74,6 +74,7 @@
         V2_0::utils::B2HGraphicBufferProducer;
 using H2BGraphicBufferProducer2 = ::android::hardware::graphics::bufferqueue::
         V2_0::utils::H2BGraphicBufferProducer;
+using ::android::hardware::media::c2::V1_2::SurfaceSyncObj;
 
 namespace /* unnamed */ {
 
@@ -593,9 +594,9 @@
 
 // Codec2Client::Component::OutputBufferQueue
 struct Codec2Client::Component::OutputBufferQueue :
-        hardware::media::c2::V1_1::utils::OutputBufferQueue {
+        hardware::media::c2::OutputBufferQueue {
     OutputBufferQueue()
-          : hardware::media::c2::V1_1::utils::OutputBufferQueue() {
+          : hardware::media::c2::OutputBufferQueue() {
     }
 };
 
@@ -1492,22 +1493,29 @@
         igbp = new B2HGraphicBufferProducer2(surface);
     }
 
+    std::shared_ptr<SurfaceSyncObj> syncObj;
+
     if (!surface) {
-        mOutputBufferQueue->configure(nullIgbp, generation, 0);
+        mOutputBufferQueue->configure(nullIgbp, generation, 0, nullptr);
     } else if (surface->getUniqueId(&bqId) != OK) {
         LOG(ERROR) << "setOutputSurface -- "
                    "cannot obtain bufferqueue id.";
         bqId = 0;
-        mOutputBufferQueue->configure(nullIgbp, generation, 0);
+        mOutputBufferQueue->configure(nullIgbp, generation, 0, nullptr);
     } else {
-        mOutputBufferQueue->configure(surface, generation, bqId);
+        mOutputBufferQueue->configure(surface, generation, bqId,
+                                      mBase1_2 ? &syncObj : nullptr);
     }
-    ALOGD("generation remote change %u", generation);
+    ALOGD("surface generation remote change %u HAL ver: %s",
+          generation, syncObj ? "1.2" : "1.0");
 
-    (void)mBase1_2;
-    Return<Status> transStatus = mBase1_0->setOutputSurface(
-            static_cast<uint64_t>(blockPoolId),
-            bqId == 0 ? nullHgbp : igbp);
+    Return<Status> transStatus = syncObj ?
+            mBase1_2->setOutputSurfaceWithSyncObj(
+                    static_cast<uint64_t>(blockPoolId),
+                    bqId == 0 ? nullHgbp : igbp, *syncObj) :
+            mBase1_0->setOutputSurface(
+                    static_cast<uint64_t>(blockPoolId),
+                    bqId == 0 ? nullHgbp : igbp);
     if (!transStatus.isOk()) {
         LOG(ERROR) << "setOutputSurface -- transaction failed.";
         return C2_TRANSACTION_FAILED;
@@ -1517,6 +1525,7 @@
     if (status != C2_OK) {
         LOG(DEBUG) << "setOutputSurface -- call failed: " << status << ".";
     }
+    ALOGD("Surface configure completed");
     return status;
 }
 
@@ -1527,6 +1536,11 @@
     return mOutputBufferQueue->outputBuffer(block, input, output);
 }
 
+void Codec2Client::Component::setOutputSurfaceMaxDequeueCount(
+        int maxDequeueCount) {
+    mOutputBufferQueue->updateMaxDequeueBufferCount(maxDequeueCount);
+}
+
 c2_status_t Codec2Client::Component::connectToInputSurface(
         const std::shared_ptr<InputSurface>& inputSurface,
         std::shared_ptr<InputSurfaceConnection>* connection) {
diff --git a/media/codec2/hidl/client/include/codec2/hidl/client.h b/media/codec2/hidl/client/include/codec2/hidl/client.h
index b574829..eca268e 100644
--- a/media/codec2/hidl/client/include/codec2/hidl/client.h
+++ b/media/codec2/hidl/client/include/codec2/hidl/client.h
@@ -407,6 +407,9 @@
             const QueueBufferInput& input,
             QueueBufferOutput* output);
 
+    // Set max dequeue count for output surface.
+    void setOutputSurfaceMaxDequeueCount(int maxDequeueCount);
+
     // Connect to a given InputSurface.
     c2_status_t connectToInputSurface(
             const std::shared_ptr<InputSurface>& inputSurface,
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/OutputBufferQueue.h b/media/codec2/hidl/client/include/codec2/hidl/output.h
similarity index 83%
rename from media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/OutputBufferQueue.h
rename to media/codec2/hidl/client/include/codec2/hidl/output.h
index 80368f7..0f03b36 100644
--- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/OutputBufferQueue.h
+++ b/media/codec2/hidl/client/include/codec2/hidl/output.h
@@ -19,16 +19,17 @@
 
 #include <gui/IGraphicBufferProducer.h>
 #include <codec2/hidl/1.0/types.h>
+#include <codec2/hidl/1.2/types.h>
 #include <C2Work.h>
 
 struct C2_HIDE _C2BlockPoolData;
+class C2SurfaceSyncMemory;
 
 namespace android {
 namespace hardware {
 namespace media {
 namespace c2 {
-namespace V1_0 {
-namespace utils {
+
 
 // BufferQueue-Based Block Operations
 // ==================================
@@ -45,7 +46,8 @@
     // Graphic blocks from older surface will be migrated to new surface.
     bool configure(const sp<IGraphicBufferProducer>& igbp,
                    uint32_t generation,
-                   uint64_t bqId);
+                   uint64_t bqId,
+                   std::shared_ptr<V1_2::SurfaceSyncObj> *syncObj);
 
     // Render a graphic block to current surface.
     status_t outputBuffer(
@@ -61,22 +63,27 @@
     void holdBufferQueueBlocks(
             const std::list<std::unique_ptr<C2Work>>& workList);
 
+    // Update # of max dequeue buffer from BQ. If # of max dequeued buffer is shared
+    // via shared memory between HAL and framework, Update # of max dequeued buffer
+    // and synchronize.
+    void updateMaxDequeueBufferCount(int maxDequeueBufferCount);
+
 private:
 
     std::mutex mMutex;
     sp<IGraphicBufferProducer> mIgbp;
     uint32_t mGeneration;
     uint64_t mBqId;
+    int32_t mMaxDequeueBufferCount;
     std::shared_ptr<int> mOwner;
     // To migrate existing buffers
     sp<GraphicBuffer> mBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS]; // find a better way
     std::weak_ptr<_C2BlockPoolData> mPoolDatas[BufferQueueDefs::NUM_BUFFER_SLOTS];
+    std::shared_ptr<C2SurfaceSyncMemory> mSyncMem;
 
     bool registerBuffer(const C2ConstGraphicBlock& block);
 };
 
-}  // namespace utils
-}  // namespace V1_0
 }  // namespace c2
 }  // namespace media
 }  // namespace hardware
diff --git a/media/codec2/hidl/1.0/utils/OutputBufferQueue.cpp b/media/codec2/hidl/client/output.cpp
similarity index 71%
rename from media/codec2/hidl/1.0/utils/OutputBufferQueue.cpp
rename to media/codec2/hidl/client/output.cpp
index 2b235f2..7df0da2 100644
--- a/media/codec2/hidl/1.0/utils/OutputBufferQueue.cpp
+++ b/media/codec2/hidl/client/output.cpp
@@ -19,13 +19,16 @@
 #include <android-base/logging.h>
 
 #include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h>
-#include <codec2/hidl/1.0/OutputBufferQueue.h>
+#include <codec2/hidl/output.h>
+#include <cutils/ashmem.h>
 #include <gui/bufferqueue/2.0/B2HGraphicBufferProducer.h>
+#include <sys/mman.h>
 
 #include <C2AllocatorGralloc.h>
 #include <C2BlockInternal.h>
 #include <C2Buffer.h>
 #include <C2PlatformSupport.h>
+#include <C2SurfaceSyncObj.h>
 
 #include <iomanip>
 
@@ -33,8 +36,6 @@
 namespace hardware {
 namespace media {
 namespace c2 {
-namespace V1_0 {
-namespace utils {
 
 using HGraphicBufferProducer = ::android::hardware::graphics::bufferqueue::
         V2_0::IGraphicBufferProducer;
@@ -105,7 +106,8 @@
 status_t attachToBufferQueue(const C2ConstGraphicBlock& block,
                              const sp<IGraphicBufferProducer>& igbp,
                              uint32_t generation,
-                             int32_t* bqSlot) {
+                             int32_t* bqSlot,
+                             std::shared_ptr<C2SurfaceSyncMemory> syncMem) {
     if (!igbp) {
         LOG(WARNING) << "attachToBufferQueue -- null producer.";
         return NO_INIT;
@@ -126,7 +128,25 @@
             << ", stride " << graphicBuffer->getStride()
             << ", generation " << graphicBuffer->getGenerationNumber();
 
-    status_t result = igbp->attachBuffer(bqSlot, graphicBuffer);
+    C2SyncVariables *syncVar = syncMem ? syncMem->mem() : nullptr;
+    status_t result = OK;
+    if (syncVar) {
+        syncVar->lock();
+        if (!syncVar->isDequeueableLocked() ||
+            syncVar->getSyncStatusLocked() == C2SyncVariables::STATUS_SWITCHING) {
+            syncVar->unlock();
+            LOG(WARNING) << "attachToBufferQueue -- attachBuffer failed: "
+                            "status = " << INVALID_OPERATION << ".";
+            return INVALID_OPERATION;
+        }
+        result = igbp->attachBuffer(bqSlot, graphicBuffer);
+        if (result == OK) {
+            syncVar->notifyDequeuedLocked();
+        }
+        syncVar->unlock();
+    } else {
+        result = igbp->attachBuffer(bqSlot, graphicBuffer);
+    }
     if (result != OK) {
         LOG(WARNING) << "attachToBufferQueue -- attachBuffer failed: "
                         "status = " << result << ".";
@@ -157,12 +177,40 @@
 
 bool OutputBufferQueue::configure(const sp<IGraphicBufferProducer>& igbp,
                                   uint32_t generation,
-                                  uint64_t bqId) {
+                                  uint64_t bqId,
+                                  std::shared_ptr<V1_2::SurfaceSyncObj> *syncObj) {
     uint64_t consumerUsage = 0;
     if (igbp->getConsumerUsage(&consumerUsage) != OK) {
         ALOGW("failed to get consumer usage");
     }
 
+    // TODO : Abstract creation process into C2SurfaceSyncMemory class.
+    // use C2LinearBlock instead ashmem.
+    std::shared_ptr<C2SurfaceSyncMemory> syncMem;
+    if (syncObj && igbp) {
+        bool mapped = false;
+        int memFd = ashmem_create_region("C2SurfaceMem", sizeof(C2SyncVariables));
+        size_t memSize = memFd < 0 ? 0 : ashmem_get_size_region(memFd);
+        if (memSize > 0) {
+            syncMem = C2SurfaceSyncMemory::Create(memFd, memSize);
+            if (syncMem) {
+                mapped = true;
+                *syncObj = std::make_shared<V1_2::SurfaceSyncObj>();
+                (*syncObj)->syncMemory = syncMem->handle();
+                (*syncObj)->bqId = bqId;
+                (*syncObj)->generationId = generation;
+                (*syncObj)->consumerUsage = consumerUsage;
+                ALOGD("C2SurfaceSyncMemory created %zu(%zu)", sizeof(C2SyncVariables), memSize);
+            }
+        }
+        if (!mapped) {
+            if (memFd >= 0) {
+                ::close(memFd);
+            }
+            ALOGW("SurfaceSyncObj creation failure");
+        }
+    }
+
     size_t tryNum = 0;
     size_t success = 0;
     sp<GraphicBuffer> buffers[BufferQueueDefs::NUM_BUFFER_SLOTS];
@@ -173,6 +221,19 @@
         if (generation == mGeneration) {
             return false;
         }
+        std::shared_ptr<C2SurfaceSyncMemory> oldMem = mSyncMem;
+        C2SyncVariables *oldSync = mSyncMem ? mSyncMem->mem() : nullptr;
+        if (oldSync) {
+            oldSync->lock();
+            oldSync->setSyncStatusLocked(C2SyncVariables::STATUS_SWITCHING);
+            oldSync->unlock();
+        }
+        mSyncMem.reset();
+        if (syncMem) {
+            mSyncMem = syncMem;
+        }
+        C2SyncVariables *newSync = mSyncMem ? mSyncMem->mem() : nullptr;
+
         mIgbp = igbp;
         mGeneration = generation;
         mBqId = bqId;
@@ -212,7 +273,7 @@
             }
             bool attach =
                     _C2BlockFactory::EndAttachBlockToBufferQueue(
-                            data, mOwner, getHgbp(mIgbp),
+                            data, mOwner, getHgbp(mIgbp), mSyncMem,
                             generation, bqId, bqSlot);
             if (!attach) {
                 igbp->cancelBuffer(bqSlot, Fence::NO_FENCE);
@@ -226,8 +287,12 @@
             mBuffers[i] = buffers[i];
             mPoolDatas[i] = poolDatas[i];
         }
+        if (newSync) {
+            newSync->setInitialDequeueCount(mMaxDequeueBufferCount, success);
+        }
     }
-    ALOGD("remote graphic buffer migration %zu/%zu", success, tryNum);
+    ALOGD("remote graphic buffer migration %zu/%zu",
+          success, tryNum);
     return true;
 }
 
@@ -258,7 +323,7 @@
                      << ", bqSlot " << oldSlot
                      << ", generation " << mGeneration
                      << ".";
-        _C2BlockFactory::HoldBlockFromBufferQueue(data, mOwner, getHgbp(mIgbp));
+        _C2BlockFactory::HoldBlockFromBufferQueue(data, mOwner, getHgbp(mIgbp), mSyncMem);
         mPoolDatas[oldSlot] = data;
         mBuffers[oldSlot] = createGraphicBuffer(block);
         mBuffers[oldSlot]->setGenerationNumber(mGeneration);
@@ -278,25 +343,39 @@
     uint32_t generation;
     uint64_t bqId;
     int32_t bqSlot;
-    bool display = displayBufferQueueBlock(block);
+    bool display = V1_0::utils::displayBufferQueueBlock(block);
     if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot) ||
         bqId == 0) {
         // Block not from bufferqueue -- it must be attached before queuing.
 
+        std::shared_ptr<C2SurfaceSyncMemory> syncMem;
         mMutex.lock();
         sp<IGraphicBufferProducer> outputIgbp = mIgbp;
         uint32_t outputGeneration = mGeneration;
+        syncMem = mSyncMem;
         mMutex.unlock();
 
         status_t status = attachToBufferQueue(
-                block, outputIgbp, outputGeneration, &bqSlot);
+                block, outputIgbp, outputGeneration, &bqSlot, syncMem);
+
         if (status != OK) {
             LOG(WARNING) << "outputBuffer -- attaching failed.";
             return INVALID_OPERATION;
         }
 
-        status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
-                                     input, output);
+        auto syncVar = syncMem ? syncMem->mem() : nullptr;
+        if(syncVar) {
+            syncVar->lock();
+            status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
+                                         input, output);
+            if (status == OK) {
+                syncVar->notifyQueuedLocked();
+            }
+            syncVar->unlock();
+        } else {
+            status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
+                                         input, output);
+        }
         if (status != OK) {
             LOG(ERROR) << "outputBuffer -- queueBuffer() failed "
                        "on non-bufferqueue-based block. "
@@ -306,10 +385,12 @@
         return OK;
     }
 
+    std::shared_ptr<C2SurfaceSyncMemory> syncMem;
     mMutex.lock();
     sp<IGraphicBufferProducer> outputIgbp = mIgbp;
     uint32_t outputGeneration = mGeneration;
     uint64_t outputBqId = mBqId;
+    syncMem = mSyncMem;
     mMutex.unlock();
 
     if (!outputIgbp) {
@@ -330,8 +411,21 @@
         return DEAD_OBJECT;
     }
 
-    status_t status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
-                                          input, output);
+    auto syncVar = syncMem ? syncMem->mem() : nullptr;
+    status_t status = OK;
+    if (syncVar) {
+        syncVar->lock();
+        status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
+                                                  input, output);
+        if (status == OK) {
+            syncVar->notifyQueuedLocked();
+        }
+        syncVar->unlock();
+    } else {
+        status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
+                                                  input, output);
+    }
+
     if (status != OK) {
         LOG(ERROR) << "outputBuffer -- queueBuffer() failed "
                    "on bufferqueue-based block. "
@@ -348,8 +442,18 @@
                            this, std::placeholders::_1));
 }
 
-}  // namespace utils
-}  // namespace V1_0
+void OutputBufferQueue::updateMaxDequeueBufferCount(int maxDequeueBufferCount) {
+    mMutex.lock();
+    mMaxDequeueBufferCount = maxDequeueBufferCount;
+    auto syncVar = mSyncMem ? mSyncMem->mem() : nullptr;
+    if (syncVar) {
+        syncVar->lock();
+        syncVar->updateMaxDequeueCountLocked(maxDequeueBufferCount);
+        syncVar->unlock();
+    }
+    mMutex.unlock();
+}
+
 }  // namespace c2
 }  // namespace media
 }  // namespace hardware
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 3f717c9..c4f9d84 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -1177,9 +1177,10 @@
     if (outputFormat != nullptr) {
         sp<IGraphicBufferProducer> outputSurface;
         uint32_t outputGeneration;
+        int maxDequeueCount = 0;
         {
             Mutexed<OutputSurface>::Locked output(mOutputSurface);
-            output->maxDequeueBuffers = numOutputSlots +
+            maxDequeueCount = output->maxDequeueBuffers = numOutputSlots +
                     reorderDepth.value + kRenderingDepth;
             outputSurface = output->surface ?
                     output->surface->getIGraphicBufferProducer() : nullptr;
@@ -1188,6 +1189,9 @@
             }
             outputGeneration = output->generation;
         }
+        if (maxDequeueCount > 0) {
+            mComponent->setOutputSurfaceMaxDequeueCount(maxDequeueCount);
+        }
 
         bool graphic = (oStreamFormat.value == C2BufferData::GRAPHIC);
         C2BlockPool::local_id_t outputPoolId_;
@@ -1766,15 +1770,22 @@
     if (needMaxDequeueBufferCountUpdate) {
         size_t numOutputSlots = 0;
         uint32_t reorderDepth = 0;
+        int maxDequeueCount = 0;
         {
             Mutexed<Output>::Locked output(mOutput);
             numOutputSlots = output->numSlots;
             reorderDepth = output->buffers->getReorderDepth();
         }
-        Mutexed<OutputSurface>::Locked output(mOutputSurface);
-        output->maxDequeueBuffers = numOutputSlots + reorderDepth + kRenderingDepth;
-        if (output->surface) {
-            output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers);
+        {
+            Mutexed<OutputSurface>::Locked output(mOutputSurface);
+            maxDequeueCount = output->maxDequeueBuffers =
+                    numOutputSlots + reorderDepth + kRenderingDepth;
+            if (output->surface) {
+                output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers);
+            }
+        }
+        if (maxDequeueCount > 0) {
+            mComponent->setOutputSurfaceMaxDequeueCount(maxDequeueCount);
         }
     }
 
diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp
index 0401c1d..be81c84 100644
--- a/media/codec2/vndk/Android.bp
+++ b/media/codec2/vndk/Android.bp
@@ -36,9 +36,11 @@
         "C2Buffer.cpp",
         "C2Config.cpp",
         "C2DmaBufAllocator.cpp",
+        "C2Fence.cpp",
         "C2PlatformStorePluginLoader.cpp",
         "C2Store.cpp",
         "platform/C2BqBuffer.cpp",
+        "platform/C2SurfaceSyncObj.cpp",
         "types.cpp",
         "util/C2Debug.cpp",
         "util/C2InterfaceHelper.cpp",
diff --git a/media/codec2/vndk/C2Fence.cpp b/media/codec2/vndk/C2Fence.cpp
new file mode 100644
index 0000000..9c5183e
--- /dev/null
+++ b/media/codec2/vndk/C2Fence.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2FenceFactory"
+#include <utils/Log.h>
+
+#include <C2FenceFactory.h>
+#include <C2SurfaceSyncObj.h>
+
+class C2Fence::Impl {
+public:
+    virtual c2_status_t wait(c2_nsecs_t timeoutNs) = 0;
+
+    virtual bool valid() const = 0;
+
+    virtual bool ready() const = 0;
+
+    virtual int fd() const = 0;
+
+    virtual bool isHW() const = 0;
+
+    virtual ~Impl() = default;
+
+    Impl() = default;
+};
+
+c2_status_t C2Fence::wait(c2_nsecs_t timeoutNs) {
+    if (mImpl) {
+        return mImpl->wait(timeoutNs);
+    }
+    // null fence is always signalled.
+    return C2_OK;
+}
+
+bool C2Fence::valid() const {
+    if (mImpl) {
+        return mImpl->valid();
+    }
+    // null fence is always valid.
+    return true;
+}
+
+bool C2Fence::ready() const {
+    if (mImpl) {
+        return mImpl->ready();
+    }
+    // null fence is always signalled.
+    return true;
+}
+
+int C2Fence::fd() const {
+    if (mImpl) {
+        return mImpl->fd();
+    }
+    // null fence does not have fd.
+    return -1;
+}
+
+bool C2Fence::isHW() const {
+    if (mImpl) {
+        return mImpl->isHW();
+    }
+    return false;
+}
+
+/**
+ * Fence implementation for C2BufferQueueBlockPool based block allocation.
+ * The implementation supports all C2Fence interface except fd().
+ */
+class _C2FenceFactory::SurfaceFenceImpl: public C2Fence::Impl {
+public:
+    virtual c2_status_t wait(c2_nsecs_t timeoutNs) {
+        if (mPtr) {
+            return mPtr->waitForChange(mWaitId, timeoutNs);
+        }
+        return C2_OK;
+    }
+
+    virtual bool valid() const {
+        return mPtr;
+    }
+
+    virtual bool ready() const {
+        uint32_t status;
+        if (mPtr) {
+            mPtr->lock();
+            status = mPtr->getWaitIdLocked();
+            mPtr->unlock();
+
+            return status != mWaitId;
+        }
+        return true;
+    }
+
+    virtual int fd() const {
+        // does not support fd, since this is shared mem and futex based
+        return -1;
+    }
+
+    virtual bool isHW() const {
+        return false;
+    }
+
+    virtual ~SurfaceFenceImpl() {};
+
+    SurfaceFenceImpl(std::shared_ptr<C2SurfaceSyncMemory> syncMem, uint32_t waitId) :
+            mSyncMem(syncMem),
+            mPtr(syncMem ? syncMem->mem() : nullptr),
+            mWaitId(syncMem ? waitId : 0) {}
+private:
+    const std::shared_ptr<const C2SurfaceSyncMemory> mSyncMem; // This is for life-cycle guarantee
+    C2SyncVariables *const mPtr;
+    const uint32_t mWaitId;
+};
+
+C2Fence::C2Fence(std::shared_ptr<Impl> impl) : mImpl(impl) {}
+
+C2Fence _C2FenceFactory::CreateSurfaceFence(
+        std::shared_ptr<C2SurfaceSyncMemory> syncMem,
+        uint32_t waitId) {
+    if (syncMem) {
+        C2Fence::Impl *p
+                = new _C2FenceFactory::SurfaceFenceImpl(syncMem, waitId);
+        if (p->valid()) {
+            return C2Fence(std::shared_ptr<C2Fence::Impl>(p));
+        } else {
+            delete p;
+        }
+    }
+    return C2Fence();
+}
diff --git a/media/codec2/vndk/include/C2BqBufferPriv.h b/media/codec2/vndk/include/C2BqBufferPriv.h
index 066f1e1..b2636e9 100644
--- a/media/codec2/vndk/include/C2BqBufferPriv.h
+++ b/media/codec2/vndk/include/C2BqBufferPriv.h
@@ -49,6 +49,14 @@
             C2MemoryUsage usage,
             std::shared_ptr<C2GraphicBlock> *block /* nonnull */) override;
 
+    virtual c2_status_t fetchGraphicBlock(
+            uint32_t width,
+            uint32_t height,
+            uint32_t format,
+            C2MemoryUsage usage,
+            std::shared_ptr<C2GraphicBlock> *block /* nonnull */,
+            C2Fence *fence /* nonnull */) override;
+
     typedef std::function<void(uint64_t producer, int32_t slot, int64_t nsecs)> OnRenderCallback;
 
     /**
@@ -72,6 +80,27 @@
      */
     virtual void configureProducer(const android::sp<HGraphicBufferProducer> &producer);
 
+    /**
+     * Configures an IGBP in order to create blocks. A newly created block is
+     * dequeued from the configured IGBP. Unique Id of IGBP and the slot number of
+     * blocks are passed via native_handle. Managing IGBP is responsibility of caller.
+     * When IGBP is not configured, block will be created via allocator.
+     * Since zero is not used for Unique Id of IGBP, if IGBP is not configured or producer
+     * is configured as nullptr, unique id which is bundled in native_handle is zero.
+     *
+     * \param producer      the IGBP, which will be used to fetch blocks
+     * \param syncMemory    Shared memory for synchronization of allocation & deallocation.
+     * \param bqId          Id of IGBP
+     * \param generationId  Generation Id for rendering output
+     * \param consumerUsage consumerUsage flagof the IGBP
+     */
+    virtual void configureProducer(
+            const android::sp<HGraphicBufferProducer> &producer,
+            native_handle_t *syncMemory,
+            uint64_t bqId,
+            uint32_t generationId,
+            uint64_t consumerUsage);
+
 private:
     const std::shared_ptr<C2Allocator> mAllocator;
     const local_id_t mLocalId;
@@ -82,6 +111,7 @@
     friend struct C2BufferQueueBlockPoolData;
 };
 
+class C2SurfaceSyncMemory;
 
 struct C2BufferQueueBlockPoolData : public _C2BlockPoolData {
 public:
@@ -97,7 +127,8 @@
     // Create a local BlockPoolData.
     C2BufferQueueBlockPoolData(
             uint32_t generation, uint64_t bqId, int32_t bqSlot,
-            const android::sp<HGraphicBufferProducer>& producer);
+            const android::sp<HGraphicBufferProducer>& producer,
+            std::shared_ptr<C2SurfaceSyncMemory>, int noUse);
 
     virtual ~C2BufferQueueBlockPoolData() override;
 
@@ -105,7 +136,8 @@
 
     int migrate(const android::sp<HGraphicBufferProducer>& producer,
                 uint32_t toGeneration, uint64_t toUsage, uint64_t toBqId,
-                android::sp<android::GraphicBuffer>& graphicBuffer, uint32_t oldGeneration);
+                android::sp<android::GraphicBuffer>& graphicBuffer, uint32_t oldGeneration,
+                std::shared_ptr<C2SurfaceSyncMemory> syncMem);
 
 private:
     friend struct _C2BlockFactory;
@@ -113,12 +145,14 @@
     // Methods delegated from _C2BlockFactory.
     void getBufferQueueData(uint32_t* generation, uint64_t* bqId, int32_t* bqSlot) const;
     bool holdBlockFromBufferQueue(const std::shared_ptr<int>& owner,
-                                  const android::sp<HGraphicBufferProducer>& igbp);
+                                  const android::sp<HGraphicBufferProducer>& igbp,
+                                  std::shared_ptr<C2SurfaceSyncMemory> syncMem);
     bool beginTransferBlockToClient();
     bool endTransferBlockToClient(bool transfer);
     bool beginAttachBlockToBufferQueue();
     bool endAttachBlockToBufferQueue(const std::shared_ptr<int>& owner,
                                      const android::sp<HGraphicBufferProducer>& igbp,
+                                     std::shared_ptr<C2SurfaceSyncMemory> syncMem,
                                      uint32_t generation, uint64_t bqId, int32_t bqSlot);
     bool displayBlockToBufferQueue();
 
@@ -141,6 +175,7 @@
     bool mDisplay; // display on remote;
     std::weak_ptr<int> mOwner;
     android::sp<HGraphicBufferProducer> mIgbp;
+    std::shared_ptr<C2SurfaceSyncMemory> mSyncMem;
     mutable std::mutex mLock;
 };
 
diff --git a/media/codec2/vndk/include/C2FenceFactory.h b/media/codec2/vndk/include/C2FenceFactory.h
new file mode 100644
index 0000000..d4bed26
--- /dev/null
+++ b/media/codec2/vndk/include/C2FenceFactory.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef STAGEFRIGHT_CODEC2_FENCE_FACTORY_H_
+#define STAGEFRIGHT_CODEC2_FENCE_FACTORY_H_
+
+
+#include <C2Buffer.h>
+
+class C2SurfaceSyncMemory;
+
+/**
+ * C2Fence implementation factory
+ */
+struct _C2FenceFactory {
+
+    class SurfaceFenceImpl;
+
+    /*
+     * Create C2Fence for BufferQueueBased blockpool.
+     *
+     * \param syncMem           Shared memory object for synchronization between processes.
+     * \param waitId            wait id for tracking status change for C2Fence.
+     */
+    static C2Fence CreateSurfaceFence(
+            std::shared_ptr<C2SurfaceSyncMemory> syncMem,
+            uint32_t waitId);
+};
+
+
+#endif // STAGEFRIGHT_CODEC2_FENCE_FACTORY_H_
diff --git a/media/codec2/vndk/include/C2SurfaceSyncObj.h b/media/codec2/vndk/include/C2SurfaceSyncObj.h
new file mode 100644
index 0000000..16e9a9d
--- /dev/null
+++ b/media/codec2/vndk/include/C2SurfaceSyncObj.h
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef STAGEFRIGHT_CODEC2_SURFACE_SYNC_OBJ_H_
+#define STAGEFRIGHT_CODEC2_SURFACE_SYNC_OBJ_H_
+
+#include <cutils/native_handle.h>
+#include <memory>
+#include <atomic>
+
+#include <C2Buffer.h>
+
+/**
+ * Futex based lock / wait implementation for sharing output buffer allocation
+ * information between Framework and HAL.
+ */
+struct C2SyncVariables {
+    enum SyncStatus : uint32_t {
+           STATUS_INIT = 0,         // When surface configuration starts.
+           STATUS_ACTIVE = 1,       // When surface configuration finishs.
+                                    // STATUS_INIT -> STATUS_ACTIVE
+           STATUS_SWITCHING = 2,    // When the surface is replaced by a new surface
+                                    // during surface configuration.
+                                    // STATUS_ACTIVE -> STATUS_SWITCHING
+    };
+
+    /**
+     * Lock the memory region
+     */
+    int lock();
+
+    /**
+     * Unlock the memory region
+     */
+    int unlock();
+
+    /**
+     * Set initial dequeued buffer count.
+     *
+     * \param maxDequeueCount           Initial value of # of max dequeued buffer count
+     * \param curDequeueCount           Initial value of # of current dequeued buffer count
+     */
+    void setInitialDequeueCount(int32_t maxDequeueCount, int32_t curDequeueCount);
+
+    /**
+     * Get a waitId which will be used to implement fence.
+     */
+    uint32_t getWaitIdLocked();
+
+    /**
+     * Return whether the upcoming dequeue operation is not blocked.
+     * if it's blocked and waitId is non-null, waitId is returned to be used for waiting.
+     *
+     * \retval false    dequeue operation is blocked now.
+     * \retval true     dequeue operation is possible.
+     */
+    bool isDequeueableLocked(uint32_t *waitId = nullptr);
+
+    /**
+     * Notify a buffer is queued. Return whether the upcoming dequeue operation
+     * is not blocked. if it's blocked and waitId is non-null, waitId is returned
+     * to be used for waiting.
+     *
+     * \retval false    dequeue operation is blocked now.
+     * \retval true     dequeue operation is possible.
+     */
+    bool notifyQueuedLocked(uint32_t *waitId = nullptr);
+
+    /**
+     * Notify a buffer is dequeued.
+     */
+    void notifyDequeuedLocked();
+
+    /**
+     * Set sync status.
+     */
+    void setSyncStatusLocked(SyncStatus status);
+
+    /**
+     * Get sync status.
+     */
+    C2SyncVariables::SyncStatus getSyncStatusLocked();
+
+    /**
+     * Update current max dequeue count.
+     */
+    void updateMaxDequeueCountLocked(int32_t maxDequeueCount);
+
+    /**
+     * Wait until status is no longer equal to waitId, or until timeout.
+     *
+     * \param waitId            internal status for waiting until it is changed.
+     * \param timeousNs         nano seconds to timeout.
+     *
+     * \retval C2_TIMEDOUT      change does not happen during waiting.
+     * \retval C2_BAD_VALUE     invalid event waiting.
+     * \retval C2_OK            change was signalled.
+     */
+    c2_status_t waitForChange(uint32_t waitId, c2_nsecs_t timeoutNs);
+
+    C2SyncVariables() {}
+
+private:
+    /**
+     * signal one waiter to wake up.
+     */
+    int signal();
+
+    /**
+     * signal all waiter to wake up.
+     */
+    int broadcast();
+
+    /**
+     * wait for signal or broadcast.
+     */
+    int wait();
+
+    std::atomic<uint32_t> mLock;
+    std::atomic<uint32_t> mCond;
+    int32_t mMaxDequeueCount;
+    int32_t mCurDequeueCount;
+    SyncStatus mStatus;
+};
+
+/**
+ * Shared memory in order to synchronize information for Surface(IGBP)
+ * based output buffer allocation.
+ */
+class C2SurfaceSyncMemory {
+public:
+    /**
+     * Shared memory handle in order to synchronize information for
+     * Surface based output buffer allocation.
+     */
+    struct HandleSyncMem : public native_handle_t {
+        HandleSyncMem(int fd, size_t size) :
+            native_handle_t(cHeader),
+            mFds{fd},
+            mInts{int(size & 0xFFFFFFFF),
+                int((uint64_t(size) >> 32) & 0xFFFFFFFF), kMagic} {}
+
+        /** Returns a file descriptor of the shared memory
+         * \return a file descriptor representing the shared memory
+         */
+        int memFd() const {return mFds.mMem;}
+
+        /** Returns the size of the shared memory */
+        size_t size() const {
+            return size_t(unsigned(mInts.mSizeLo))
+                    | size_t(uint64_t(unsigned(mInts.mSizeHi)) << 32);
+        }
+
+        /** Check whether the native handle is in the form of HandleSyncMem
+         *
+         * \return whether the native handle is compatible
+         */
+        static bool isValid(const native_handle_t * const o);
+
+    protected:
+        struct {
+            int mMem;
+        } mFds;
+        struct {
+            int mSizeLo;
+            int mSizeHi;
+            int mMagic;
+        } mInts;
+    private:
+        enum {
+            kMagic = 'ssm\x00',
+            numFds = sizeof(mFds) / sizeof(int),
+            numInts = sizeof(mInts) / sizeof(int),
+            version = sizeof(native_handle_t)
+        };
+        const static native_handle_t cHeader;
+    };
+
+    /**
+     * Imports a shared memory object from a native handle(The shared memory is already existing).
+     * This is usually used after native_handle_t is passed via RPC.
+     *
+     * \param handle        handle representing shared memory for output buffer allocation.
+     */
+    static std::shared_ptr<C2SurfaceSyncMemory> Import(native_handle_t *handle);
+
+    /**
+     * Creats a shared memory object for synchronization of output buffer allocation.
+     * Shared memory creation should be done explicitly.
+     *
+     * \param fd            file descriptor to shared memory
+     * \param size          size of the shared memory
+     */
+    static std::shared_ptr<C2SurfaceSyncMemory> Create(int fd, size_t size);
+
+    /**
+     * Returns a handle representing the shread memory for synchronization of
+     * output buffer allocation.
+     */
+    native_handle_t *handle();
+
+    /**
+     * Returns synchronization object which will provide synchronization primitives.
+     *
+     * \return a ptr to synchronization primitive class
+     */
+    C2SyncVariables *mem();
+
+    ~C2SurfaceSyncMemory();
+
+private:
+    bool mInit;
+    HandleSyncMem *mHandle;
+    C2SyncVariables *mMem;
+
+    C2SurfaceSyncMemory();
+};
+
+#endif // STAGEFRIGHT_CODEC2_SURFACE_SYNC_OBJ_H_
diff --git a/media/codec2/vndk/internal/C2BlockInternal.h b/media/codec2/vndk/internal/C2BlockInternal.h
index 4ae946a..c510fca 100644
--- a/media/codec2/vndk/internal/C2BlockInternal.h
+++ b/media/codec2/vndk/internal/C2BlockInternal.h
@@ -52,6 +52,8 @@
 
 struct C2BufferQueueBlockPoolData;
 
+class C2SurfaceSyncMemory;
+
 /**
  * Internal only interface for creating blocks by block pool/buffer passing implementations.
  *
@@ -279,6 +281,8 @@
      *                 anymore.
      * \param igbp     \c IGraphicBufferProducer instance to be assigned to the
      *                 block. This is not needed when the block is local.
+     * \param syncMem  Memory block which will support synchronization
+     *                 between Framework and HAL.
      *
      * \return The previous held status.
      */
@@ -287,7 +291,8 @@
             const std::shared_ptr<_C2BlockPoolData>& poolData,
             const std::shared_ptr<int>& owner,
             const ::android::sp<::android::hardware::graphics::bufferqueue::
-                                V2_0::IGraphicBufferProducer>& igbp = nullptr);
+                                V2_0::IGraphicBufferProducer>& igbp = nullptr,
+            std::shared_ptr<C2SurfaceSyncMemory> syncMem = nullptr);
 
     /**
      * Prepare a block to be transferred to other process. This blocks
@@ -358,6 +363,7 @@
             const std::shared_ptr<int>& owner,
             const ::android::sp<::android::hardware::graphics::bufferqueue::
                                 V2_0::IGraphicBufferProducer>& igbp,
+            std::shared_ptr<C2SurfaceSyncMemory>,
             uint32_t generation,
             uint64_t bqId,
             int32_t bqSlot);
diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp
index 3f6fa7d..2944925 100644
--- a/media/codec2/vndk/platform/C2BqBuffer.cpp
+++ b/media/codec2/vndk/platform/C2BqBuffer.cpp
@@ -29,6 +29,8 @@
 #include <C2AllocatorGralloc.h>
 #include <C2BqBufferPriv.h>
 #include <C2BlockInternal.h>
+#include <C2FenceFactory.h>
+#include <C2SurfaceSyncObj.h>
 
 #include <list>
 #include <map>
@@ -69,10 +71,11 @@
 bool _C2BlockFactory::HoldBlockFromBufferQueue(
         const std::shared_ptr<_C2BlockPoolData>& data,
         const std::shared_ptr<int>& owner,
-        const sp<HGraphicBufferProducer>& igbp) {
+        const sp<HGraphicBufferProducer>& igbp,
+        std::shared_ptr<C2SurfaceSyncMemory> syncMem) {
     const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
             std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
-    return poolData->holdBlockFromBufferQueue(owner, igbp);
+    return poolData->holdBlockFromBufferQueue(owner, igbp, syncMem);
 }
 
 bool _C2BlockFactory::BeginTransferBlockToClient(
@@ -102,12 +105,13 @@
         const std::shared_ptr<_C2BlockPoolData>& data,
         const std::shared_ptr<int>& owner,
         const sp<HGraphicBufferProducer>& igbp,
+        std::shared_ptr<C2SurfaceSyncMemory> syncMem,
         uint32_t generation,
         uint64_t bqId,
         int32_t bqSlot) {
     const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
             std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
-    return poolData->endAttachBlockToBufferQueue(owner, igbp, generation, bqId, bqSlot);
+    return poolData->endAttachBlockToBufferQueue(owner, igbp, syncMem, generation, bqId, bqSlot);
 }
 
 bool _C2BlockFactory::DisplayBlockToBufferQueue(
@@ -231,12 +235,58 @@
 class C2BufferQueueBlockPool::Impl
         : public std::enable_shared_from_this<C2BufferQueueBlockPool::Impl> {
 private:
+    c2_status_t dequeueBuffer(
+            uint32_t width,
+            uint32_t height,
+            uint32_t format,
+            C2AndroidMemoryUsage androidUsage,
+            int *slot, bool *needsRealloc, sp<Fence> *fence) {
+        status_t status{};
+        using Input = HGraphicBufferProducer::DequeueBufferInput;
+        using Output = HGraphicBufferProducer::DequeueBufferOutput;
+        Return<void> transResult = mProducer->dequeueBuffer(
+                Input{
+                    width,
+                    height,
+                    format,
+                    androidUsage.asGrallocUsage()},
+                [&status, slot, needsRealloc,
+                 fence](HStatus hStatus,
+                         int32_t hSlot,
+                         Output const& hOutput) {
+                    *slot = static_cast<int>(hSlot);
+                    if (!h2b(hStatus, &status) ||
+                            !h2b(hOutput.fence, fence)) {
+                        status = ::android::BAD_VALUE;
+                    } else {
+                        *needsRealloc =
+                                hOutput.bufferNeedsReallocation;
+                    }
+                });
+        if (!transResult.isOk() || status != android::OK) {
+            if (transResult.isOk()) {
+                ++mDqFailure;
+                if (status == android::INVALID_OPERATION ||
+                    status == android::TIMED_OUT ||
+                    status == android::WOULD_BLOCK) {
+                    // Dequeue buffer is blocked temporarily. Retrying is
+                    // required.
+                    return C2_BLOCKING;
+                }
+            }
+            ALOGD("cannot dequeue buffer %d", status);
+            return C2_BAD_VALUE;
+        }
+        return C2_OK;
+    }
+
     c2_status_t fetchFromIgbp_l(
             uint32_t width,
             uint32_t height,
             uint32_t format,
             C2MemoryUsage usage,
-            std::shared_ptr<C2GraphicBlock> *block /* nonnull */) {
+            std::shared_ptr<C2GraphicBlock> *block /* nonnull */,
+            C2Fence *c2Fence) {
         // We have an IGBP now.
         C2AndroidMemoryUsage androidUsage = usage;
         status_t status{};
@@ -245,41 +295,39 @@
         sp<Fence> fence = new Fence();
         ALOGV("tries to dequeue buffer");
 
+        C2SyncVariables *syncVar = mSyncMem ? mSyncMem->mem(): nullptr;
         { // Call dequeueBuffer().
-            using Input = HGraphicBufferProducer::DequeueBufferInput;
-            using Output = HGraphicBufferProducer::DequeueBufferOutput;
-            Return<void> transResult = mProducer->dequeueBuffer(
-                    Input{
-                        width,
-                        height,
-                        format,
-                        androidUsage.asGrallocUsage()},
-                    [&status, &slot, &bufferNeedsReallocation,
-                     &fence](HStatus hStatus,
-                             int32_t hSlot,
-                             Output const& hOutput) {
-                        slot = static_cast<int>(hSlot);
-                        if (!h2b(hStatus, &status) ||
-                                !h2b(hOutput.fence, &fence)) {
-                            status = ::android::BAD_VALUE;
-                        } else {
-                            bufferNeedsReallocation =
-                                    hOutput.bufferNeedsReallocation;
-                        }
-                    });
-            if (!transResult.isOk() || status != android::OK) {
-                if (transResult.isOk()) {
-                    ++mDqFailure;
-                    if (status == android::INVALID_OPERATION ||
-                        status == android::TIMED_OUT ||
-                        status == android::WOULD_BLOCK) {
-                        // Dequeue buffer is blocked temporarily. Retrying is
-                        // required.
-                        return C2_BLOCKING;
+            c2_status_t c2Status;
+            if (syncVar) {
+                uint32_t waitId;
+                syncVar->lock();
+                if (!syncVar->isDequeueableLocked(&waitId)) {
+                    syncVar->unlock();
+                    if (c2Fence) {
+                        *c2Fence = _C2FenceFactory::CreateSurfaceFence(mSyncMem, waitId);
                     }
+                    return C2_BLOCKING;
                 }
-                ALOGD("cannot dequeue buffer %d", status);
-                return C2_BAD_VALUE;
+                if (syncVar->getSyncStatusLocked() != C2SyncVariables::STATUS_ACTIVE) {
+                    waitId = syncVar->getWaitIdLocked();
+                    syncVar->unlock();
+                    if (c2Fence) {
+                        *c2Fence = _C2FenceFactory::CreateSurfaceFence(mSyncMem, waitId);
+                    }
+                    return C2_BLOCKING;
+                }
+                c2Status = dequeueBuffer(width, height, format, androidUsage,
+                              &slot, &bufferNeedsReallocation, &fence);
+                if (c2Status == C2_OK) {
+                    syncVar->notifyDequeuedLocked();
+                }
+                syncVar->unlock();
+            } else {
+                c2Status = dequeueBuffer(width, height, format, usage,
+                              &slot, &bufferNeedsReallocation, &fence);
+            }
+            if (c2Status != C2_OK) {
+                return c2Status;
             }
             mDqFailure = 0;
             mLastDqTs = getTimestampNow();
@@ -290,18 +338,41 @@
             return C2_BAD_VALUE;
         }
         ALOGV("dequeued a buffer successfully");
+        bool dequeueable = false;
+        uint32_t waitId;
         if (fence) {
             static constexpr int kFenceWaitTimeMs = 10;
 
             status_t status = fence->wait(kFenceWaitTimeMs);
             if (status == -ETIME) {
                 // fence is not signalled yet.
-                (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+                if (syncVar) {
+                    syncVar->lock();
+                    (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+                    dequeueable = syncVar->notifyQueuedLocked(&waitId);
+                    syncVar->unlock();
+                    if (c2Fence) {
+                        *c2Fence = dequeueable ? C2Fence() :
+                                _C2FenceFactory::CreateSurfaceFence(mSyncMem, waitId);
+                    }
+                } else {
+                    (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+                }
                 return C2_BLOCKING;
             }
             if (status != android::NO_ERROR) {
                 ALOGD("buffer fence wait error %d", status);
-                (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+                if (syncVar) {
+                    syncVar->lock();
+                    (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+                    syncVar->notifyQueuedLocked();
+                    syncVar->unlock();
+                    if (c2Fence) {
+                        *c2Fence = C2Fence();
+                    }
+                } else {
+                    (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+                }
                 return C2_BAD_VALUE;
             } else if (mRenderCallback) {
                 nsecs_t signalTime = fence->getSignalTime();
@@ -341,7 +412,17 @@
                 return C2_BAD_VALUE;
             } else if (status != android::NO_ERROR) {
                 slotBuffer.clear();
-                (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+                if (syncVar) {
+                    syncVar->lock();
+                    (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+                    syncVar->notifyQueuedLocked();
+                    syncVar->unlock();
+                    if (c2Fence) {
+                        *c2Fence = C2Fence();
+                    }
+                } else {
+                    (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+                }
                 return C2_BAD_VALUE;
             }
             if (mGeneration == 0) {
@@ -372,14 +453,28 @@
                         std::make_shared<C2BufferQueueBlockPoolData>(
                                 slotBuffer->getGenerationNumber(),
                                 mProducerId, slot,
-                                mProducer);
+                                mProducer, mSyncMem, 0);
                 mPoolDatas[slot] = poolData;
                 *block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
                 return C2_OK;
             }
             // Block was not created. call requestBuffer# again next time.
             slotBuffer.clear();
-            (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+            if (syncVar) {
+                syncVar->lock();
+                (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+                syncVar->notifyQueuedLocked();
+                syncVar->unlock();
+                if (c2Fence) {
+                    *c2Fence = C2Fence();
+                }
+            } else {
+                (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
+            }
+            return C2_BAD_VALUE;
+        }
+        if (c2Fence) {
+            *c2Fence = C2Fence();
         }
         return C2_BAD_VALUE;
     }
@@ -409,7 +504,8 @@
             uint32_t height,
             uint32_t format,
             C2MemoryUsage usage,
-            std::shared_ptr<C2GraphicBlock> *block /* nonnull */) {
+            std::shared_ptr<C2GraphicBlock> *block /* nonnull */,
+            C2Fence *fence) {
         block->reset();
         if (mInit != C2_OK) {
             return mInit;
@@ -440,17 +536,19 @@
             }
             std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
                     std::make_shared<C2BufferQueueBlockPoolData>(
-                            0, (uint64_t)0, ~0, nullptr);
+                            0, (uint64_t)0, ~0, nullptr, nullptr, 0);
             *block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
             ALOGV("allocated a buffer successfully");
 
             return C2_OK;
         }
-        c2_status_t status = fetchFromIgbp_l(width, height, format, usage, block);
+        c2_status_t status = fetchFromIgbp_l(width, height, format, usage, block, fence);
         if (status == C2_BLOCKING) {
             lock.unlock();
-            // in order not to drain cpu from component's spinning
-            ::usleep(kMaxIgbpRetryDelayUs);
+            if (!fence) {
+                // in order not to drain cpu from component's spinning
+                ::usleep(kMaxIgbpRetryDelayUs);
+            }
         }
         return status;
     }
@@ -460,11 +558,12 @@
         mRenderCallback = renderCallback;
     }
 
+    /* This is for Old HAL request for compatibility */
     void configureProducer(const sp<HGraphicBufferProducer> &producer) {
         uint64_t producerId = 0;
         uint32_t generation = 0;
         uint64_t usage = 0;
-        bool haveGeneration = false;
+        bool bqInformation = false;
         if (producer) {
             Return<uint64_t> transResult = producer->getUniqueId();
             if (!transResult.isOk()) {
@@ -472,14 +571,32 @@
                 return;
             }
             producerId = static_cast<uint64_t>(transResult);
-            // TODO: provide gneration number from parameter.
-            haveGeneration = getGenerationNumberAndUsage(producer, &generation, &usage);
-            if (!haveGeneration) {
+            bqInformation = getGenerationNumberAndUsage(producer, &generation, &usage);
+            if (!bqInformation) {
                 ALOGW("get generationNumber failed %llu",
                       (unsigned long long)producerId);
             }
         }
+        configureProducer(producer, nullptr, producerId, generation, usage, bqInformation);
+    }
+
+    void configureProducer(const sp<HGraphicBufferProducer> &producer,
+                           native_handle_t *syncHandle,
+                           uint64_t producerId,
+                           uint32_t generation,
+                           uint64_t usage,
+                           bool bqInformation) {
+        std::shared_ptr<C2SurfaceSyncMemory> c2SyncMem;
+        if (syncHandle) {
+            if (!producer) {
+                native_handle_close(syncHandle);
+                native_handle_delete(syncHandle);
+            } else {
+                c2SyncMem = C2SurfaceSyncMemory::Import(syncHandle);
+            }
+        }
         int migrated = 0;
+        std::shared_ptr<C2SurfaceSyncMemory> oldMem;
         // poolDatas dtor should not be called during lock is held.
         std::shared_ptr<C2BufferQueueBlockPoolData>
                 poolDatas[NUM_BUFFER_SLOTS];
@@ -499,22 +616,30 @@
             if (producer) {
                 mProducer = producer;
                 mProducerId = producerId;
-                mGeneration = haveGeneration ? generation : 0;
+                mGeneration = bqInformation ? generation : 0;
             } else {
                 mProducer = nullptr;
                 mProducerId = 0;
                 mGeneration = 0;
                 ALOGW("invalid producer producer(%d), generation(%d)",
-                      (bool)producer, haveGeneration);
+                      (bool)producer, bqInformation);
             }
-            if (mProducer && haveGeneration) { // migrate buffers
+            oldMem = mSyncMem; // preven destruction while locked.
+            mSyncMem = c2SyncMem;
+            C2SyncVariables *syncVar = mSyncMem ? mSyncMem->mem() : nullptr;
+            if (syncVar) {
+                syncVar->lock();
+                syncVar->setSyncStatusLocked(C2SyncVariables::STATUS_ACTIVE);
+                syncVar->unlock();
+            }
+            if (mProducer && bqInformation) { // migrate buffers
                 for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
                     std::shared_ptr<C2BufferQueueBlockPoolData> data =
                             mPoolDatas[i].lock();
                     if (data) {
                         int slot = data->migrate(
                                 mProducer, generation, usage,
-                                producerId, mBuffers[i], oldGeneration);
+                                producerId, mBuffers[i], oldGeneration, mSyncMem);
                         if (slot >= 0) {
                             buffers[slot] = mBuffers[i];
                             poolDatas[slot] = data;
@@ -528,7 +653,7 @@
                 mPoolDatas[i] = poolDatas[i];
             }
         }
-        if (producer && haveGeneration) {
+        if (producer && bqInformation) {
             ALOGD("local generation change %u , "
                   "bqId: %llu migrated buffers # %d",
                   generation, (unsigned long long)producerId, migrated);
@@ -555,6 +680,8 @@
 
     sp<GraphicBuffer> mBuffers[NUM_BUFFER_SLOTS];
     std::weak_ptr<C2BufferQueueBlockPoolData> mPoolDatas[NUM_BUFFER_SLOTS];
+
+    std::shared_ptr<C2SurfaceSyncMemory> mSyncMem;
 };
 
 C2BufferQueueBlockPoolData::C2BufferQueueBlockPoolData(
@@ -570,11 +697,14 @@
 
 C2BufferQueueBlockPoolData::C2BufferQueueBlockPoolData(
         uint32_t generation, uint64_t bqId, int32_t bqSlot,
-        const android::sp<HGraphicBufferProducer>& producer) :
+        const android::sp<HGraphicBufferProducer>& producer,
+        std::shared_ptr<C2SurfaceSyncMemory> syncMem, int noUse) :
         mLocal(true), mHeld(true),
         mGeneration(generation), mBqId(bqId), mBqSlot(bqSlot),
         mCurrentGeneration(generation), mCurrentBqId(bqId),
-        mTransfer(false), mAttach(false), mDisplay(false), mIgbp(producer) {
+        mTransfer(false), mAttach(false), mDisplay(false),
+        mIgbp(producer), mSyncMem(syncMem) {
+            (void)noUse;
 }
 
 C2BufferQueueBlockPoolData::~C2BufferQueueBlockPoolData() {
@@ -584,10 +714,30 @@
 
     if (mLocal) {
         if (mGeneration == mCurrentGeneration && mBqId == mCurrentBqId) {
-            mIgbp->cancelBuffer(mBqSlot, hidl_handle{}).isOk();
+            C2SyncVariables *syncVar = mSyncMem ? mSyncMem->mem() : nullptr;
+            if (syncVar) {
+                syncVar->lock();
+                if (syncVar->getSyncStatusLocked() == C2SyncVariables::STATUS_ACTIVE) {
+                    mIgbp->cancelBuffer(mBqSlot, hidl_handle{}).isOk();
+                    syncVar->notifyQueuedLocked();
+                }
+                syncVar->unlock();
+            } else {
+                mIgbp->cancelBuffer(mBqSlot, hidl_handle{}).isOk();
+            }
         }
     } else if (!mOwner.expired()) {
-        mIgbp->cancelBuffer(mBqSlot, hidl_handle{}).isOk();
+        C2SyncVariables *syncVar = mSyncMem ? mSyncMem->mem() : nullptr;
+        if (syncVar) {
+            syncVar->lock();
+            if (syncVar->getSyncStatusLocked() != C2SyncVariables::STATUS_SWITCHING) {
+                mIgbp->cancelBuffer(mBqSlot, hidl_handle{}).isOk();
+                syncVar->notifyQueuedLocked();
+            }
+            syncVar->unlock();
+        } else {
+            mIgbp->cancelBuffer(mBqSlot, hidl_handle{}).isOk();
+        }
     }
 }
 
@@ -598,7 +748,8 @@
 int C2BufferQueueBlockPoolData::migrate(
         const sp<HGraphicBufferProducer>& producer,
         uint32_t toGeneration, uint64_t toUsage, uint64_t toBqId,
-        sp<GraphicBuffer>& graphicBuffer, uint32_t oldGeneration) {
+        sp<GraphicBuffer>& graphicBuffer, uint32_t oldGeneration,
+        std::shared_ptr<C2SurfaceSyncMemory> syncMem) {
     std::scoped_lock<std::mutex> l(mLock);
 
     mCurrentBqId = toBqId;
@@ -626,6 +777,7 @@
     }
     if (oldGeneration != mGeneration) {
         ALOGV("cannot migrate stale buffer");
+        return -1;
     }
     if (mTransfer) {
         // either transferred or detached.
@@ -678,6 +830,14 @@
     mGeneration = toGeneration;
     mBqId = toBqId;
     mBqSlot = slot;
+    mSyncMem = syncMem;
+
+    C2SyncVariables *syncVar = syncMem ? syncMem->mem() : nullptr;
+    if (syncVar) {
+        syncVar->lock();
+        syncVar->notifyDequeuedLocked();
+        syncVar->unlock();
+    }
     return slot;
 }
 
@@ -697,11 +857,13 @@
 
 bool C2BufferQueueBlockPoolData::holdBlockFromBufferQueue(
         const std::shared_ptr<int>& owner,
-        const sp<HGraphicBufferProducer>& igbp) {
+        const sp<HGraphicBufferProducer>& igbp,
+        std::shared_ptr<C2SurfaceSyncMemory> syncMem) {
     std::scoped_lock<std::mutex> lock(mLock);
     if (!mLocal) {
         mOwner = owner;
         mIgbp = igbp;
+        mSyncMem = syncMem;
     }
     if (mHeld) {
         return false;
@@ -741,6 +903,7 @@
 bool C2BufferQueueBlockPoolData::endAttachBlockToBufferQueue(
         const std::shared_ptr<int>& owner,
         const sp<HGraphicBufferProducer>& igbp,
+        std::shared_ptr<C2SurfaceSyncMemory> syncMem,
         uint32_t generation,
         uint64_t bqId,
         int32_t bqSlot) {
@@ -757,6 +920,7 @@
     mHeld = true;
     mOwner = owner;
     mIgbp = igbp;
+    mSyncMem = syncMem;
     mGeneration = generation;
     mBqId = bqId;
     mBqSlot = bqSlot;
@@ -792,7 +956,20 @@
         C2MemoryUsage usage,
         std::shared_ptr<C2GraphicBlock> *block /* nonnull */) {
     if (mImpl) {
-        return mImpl->fetchGraphicBlock(width, height, format, usage, block);
+        return mImpl->fetchGraphicBlock(width, height, format, usage, block, nullptr);
+    }
+    return C2_CORRUPTED;
+}
+
+c2_status_t C2BufferQueueBlockPool::fetchGraphicBlock(
+        uint32_t width,
+        uint32_t height,
+        uint32_t format,
+        C2MemoryUsage usage,
+        std::shared_ptr<C2GraphicBlock> *block /* nonnull */,
+        C2Fence *fence /* nonnull */) {
+    if (mImpl) {
+        return mImpl->fetchGraphicBlock(width, height, format, usage, block, fence);
     }
     return C2_CORRUPTED;
 }
@@ -803,6 +980,18 @@
     }
 }
 
+void C2BufferQueueBlockPool::configureProducer(
+        const sp<HGraphicBufferProducer> &producer,
+        native_handle_t *syncMemory,
+        uint64_t bqId,
+        uint32_t generationId,
+        uint64_t consumerUsage) {
+    if (mImpl) {
+        mImpl->configureProducer(
+               producer, syncMemory, bqId, generationId, consumerUsage, true);
+    }
+}
+
 void C2BufferQueueBlockPool::setRenderCallback(const OnRenderCallback &renderCallback) {
     if (mImpl) {
         mImpl->setRenderCallback(renderCallback);
diff --git a/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp b/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp
new file mode 100644
index 0000000..587992e
--- /dev/null
+++ b/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2SurfaceSyncObj"
+#include <limits.h>
+#include <linux/futex.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <utils/Log.h>
+
+#include <chrono>
+#include <C2SurfaceSyncObj.h>
+
+const native_handle_t C2SurfaceSyncMemory::HandleSyncMem::cHeader = {
+    C2SurfaceSyncMemory::HandleSyncMem::version,
+    C2SurfaceSyncMemory::HandleSyncMem::numFds,
+    C2SurfaceSyncMemory::HandleSyncMem::numInts,
+    {}
+};
+
+bool C2SurfaceSyncMemory::HandleSyncMem::isValid(const native_handle_t * const o) {
+    if (!o || memcmp(o, &cHeader, sizeof(cHeader))) {
+        return false;
+    }
+
+    const HandleSyncMem *other = static_cast<const HandleSyncMem*>(o);
+    return other->mInts.mMagic == kMagic;
+}
+
+C2SurfaceSyncMemory::C2SurfaceSyncMemory()
+    : mInit(false), mHandle(nullptr), mMem(nullptr) {}
+
+C2SurfaceSyncMemory::~C2SurfaceSyncMemory() {
+    if (mInit) {
+        if (mMem) {
+            munmap(static_cast<void *>(mMem), mHandle->size());
+        }
+        if (mHandle) {
+            native_handle_close(mHandle);
+            native_handle_delete(mHandle);
+        }
+    }
+}
+
+std::shared_ptr<C2SurfaceSyncMemory> C2SurfaceSyncMemory::Import(
+        native_handle_t *handle) {
+    if (!HandleSyncMem::isValid(handle)) {
+        return nullptr;
+    }
+
+    HandleSyncMem *o = static_cast<HandleSyncMem*>(handle);
+    void *ptr = mmap(NULL, o->size(), PROT_READ | PROT_WRITE, MAP_SHARED, o->memFd(), 0);
+
+    if (ptr == MAP_FAILED) {
+        native_handle_close(handle);
+        native_handle_delete(handle);
+        return nullptr;
+    }
+
+    std::shared_ptr<C2SurfaceSyncMemory> syncMem(new C2SurfaceSyncMemory);
+    syncMem->mInit = true;
+    syncMem->mHandle = o;
+    syncMem->mMem = static_cast<C2SyncVariables*>(ptr);
+    return syncMem;
+}
+
+std::shared_ptr<C2SurfaceSyncMemory> C2SurfaceSyncMemory::Create(int fd, size_t size) {
+    if (fd < 0 || size == 0) {
+        return nullptr;
+    }
+    HandleSyncMem *handle = new HandleSyncMem(fd, size);
+
+    void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+    if (ptr == MAP_FAILED) {
+        native_handle_close(handle);
+        native_handle_delete(handle);
+        return nullptr;
+    }
+    memset(ptr, 0, size);
+
+    std::shared_ptr<C2SurfaceSyncMemory> syncMem(new C2SurfaceSyncMemory);
+    syncMem->mInit = true;
+    syncMem->mHandle = handle;
+    syncMem->mMem = static_cast<C2SyncVariables*>(ptr);
+    return syncMem;
+}
+
+native_handle_t *C2SurfaceSyncMemory::handle() {
+    return !mInit ? nullptr : mHandle;
+}
+
+C2SyncVariables *C2SurfaceSyncMemory::mem() {
+    return !mInit ? nullptr : mMem;
+}
+
+namespace {
+    constexpr int kSpinNumForLock = 100;
+    constexpr int kSpinNumForUnlock = 200;
+
+    enum : uint32_t {
+        FUTEX_UNLOCKED = 0,
+        FUTEX_LOCKED_UNCONTENDED = 1,  // user-space locking
+        FUTEX_LOCKED_CONTENDED = 2,    // futex locking
+    };
+}
+
+int C2SyncVariables::lock() {
+    uint32_t old;
+    for (int i = 0; i < kSpinNumForLock; i++) {
+        old = 0;
+        if (mLock.compare_exchange_strong(old, FUTEX_LOCKED_UNCONTENDED)) {
+            return 0;
+        }
+        sched_yield();
+    }
+
+    if (old == FUTEX_LOCKED_UNCONTENDED)
+        old = mLock.exchange(FUTEX_LOCKED_CONTENDED);
+
+    while (old) {
+        (void) syscall(__NR_futex, &mLock, FUTEX_WAIT, FUTEX_LOCKED_CONTENDED, NULL, NULL, 0);
+        old = mLock.exchange(FUTEX_LOCKED_CONTENDED);
+    }
+    return 0;
+}
+
+int C2SyncVariables::unlock() {
+    if (mLock.exchange(FUTEX_UNLOCKED) == FUTEX_LOCKED_UNCONTENDED) return 0;
+
+    for (int i = 0; i < kSpinNumForUnlock; i++) {
+        if (mLock.load()) {
+            uint32_t old = FUTEX_LOCKED_UNCONTENDED;
+            mLock.compare_exchange_strong(old, FUTEX_LOCKED_CONTENDED);
+            if (old) {
+                return 0;
+            }
+        }
+        sched_yield();
+    }
+
+    (void) syscall(__NR_futex, &mLock, FUTEX_WAKE, 1, NULL, NULL, 0);
+    return 0;
+}
+
+void C2SyncVariables::setInitialDequeueCount(
+        int32_t maxDequeueCount, int32_t curDequeueCount) {
+    lock();
+    mMaxDequeueCount = maxDequeueCount;
+    mCurDequeueCount = curDequeueCount;
+    unlock();
+}
+
+uint32_t C2SyncVariables::getWaitIdLocked() {
+    return mCond.load();
+}
+
+bool C2SyncVariables::isDequeueableLocked(uint32_t *waitId) {
+    if (mMaxDequeueCount <= mCurDequeueCount) {
+        if (waitId) {
+            *waitId = getWaitIdLocked();
+        }
+        return false;
+    }
+    return true;
+}
+
+bool C2SyncVariables::notifyQueuedLocked(uint32_t *waitId) {
+    // Note. thundering herds may occur. Edge trigged signalling.
+    // But one waiter will guarantee to dequeue. others may wait again.
+    // Minimize futex syscall(trap) for the main use case(one waiter case).
+    if (mMaxDequeueCount == mCurDequeueCount--) {
+        broadcast();
+        return true;
+    }
+
+    if (mCurDequeueCount >= mMaxDequeueCount) {
+        if (waitId) {
+            *waitId = getWaitIdLocked();
+        }
+        ALOGV("dequeue blocked %d/%d", mCurDequeueCount, mMaxDequeueCount);
+        return false;
+    }
+    return true;
+}
+
+void C2SyncVariables::notifyDequeuedLocked() {
+    mCurDequeueCount++;
+    ALOGV("dequeue successful %d/%d", mCurDequeueCount, mMaxDequeueCount);
+}
+
+void C2SyncVariables::setSyncStatusLocked(SyncStatus status) {
+    mStatus = status;
+    if (mStatus == STATUS_ACTIVE) {
+        broadcast();
+    }
+}
+
+C2SyncVariables::SyncStatus C2SyncVariables::getSyncStatusLocked() {
+    return mStatus;
+}
+
+void C2SyncVariables::updateMaxDequeueCountLocked(int32_t maxDequeueCount) {
+    mMaxDequeueCount = maxDequeueCount;
+    if (mStatus == STATUS_ACTIVE) {
+        broadcast();
+    }
+}
+
+c2_status_t C2SyncVariables::waitForChange(uint32_t waitId, c2_nsecs_t timeoutNs) {
+    if (timeoutNs < 0) {
+        timeoutNs = 0;
+    }
+    struct timespec tv;
+    tv.tv_sec = timeoutNs / 1000000000;
+    tv.tv_nsec = timeoutNs % 1000000000;
+
+    int ret =  syscall(__NR_futex, &mCond, FUTEX_WAIT, waitId, &tv, NULL, 0);
+    if (ret == 0 || ret == EAGAIN) {
+        return C2_OK;
+    }
+    if (ret == EINTR || ret == ETIMEDOUT) {
+        return C2_TIMED_OUT;
+    }
+    return C2_BAD_VALUE;
+}
+
+int C2SyncVariables::signal() {
+    mCond++;
+
+    (void) syscall(__NR_futex, &mCond, FUTEX_WAKE, 1, NULL, NULL, 0);
+    return 0;
+}
+
+int C2SyncVariables::broadcast() {
+    mCond++;
+
+    (void) syscall(__NR_futex, &mCond, FUTEX_REQUEUE, 1, (void *)INT_MAX, &mLock, 0);
+    return 0;
+}
+
+int C2SyncVariables::wait() {
+    uint32_t old = mCond.load();
+    unlock();
+
+    (void) syscall(__NR_futex, &mCond, FUTEX_WAIT, old, NULL, NULL, 0);
+    while (mLock.exchange(FUTEX_LOCKED_CONTENDED)) {
+        (void) syscall(__NR_futex, &mLock, FUTEX_WAIT, FUTEX_LOCKED_CONTENDED, NULL, NULL, 0);
+    }
+    return 0;
+}
diff --git a/media/libmediatranscoding/TranscoderWrapper.cpp b/media/libmediatranscoding/TranscoderWrapper.cpp
index a063565..b19e711 100644
--- a/media/libmediatranscoding/TranscoderWrapper.cpp
+++ b/media/libmediatranscoding/TranscoderWrapper.cpp
@@ -366,6 +366,12 @@
         return AMEDIA_ERROR_INVALID_OPERATION;
     }
 
+    // Unwrap the callback and send heartbeats to the client after each operation during setup.
+    auto callback = mCallback.lock();
+    if (callback == nullptr) {
+        return AMEDIA_ERROR_INVALID_OPERATION;
+    }
+
     Status status;
     ::ndk::ScopedFileDescriptor srcFd, dstFd;
     int srcFdInt = request.sourceFd.get();
@@ -379,6 +385,8 @@
         srcFdInt = srcFd.get();
     }
 
+    callback->onHeartBeat(clientId, sessionId);
+
     int dstFdInt = request.destinationFd.get();
     if (dstFdInt < 0) {
         // Open dest file with "rw", as the transcoder could potentially reuse part of it
@@ -393,6 +401,8 @@
         dstFdInt = dstFd.get();
     }
 
+    callback->onHeartBeat(clientId, sessionId);
+
     mCurrentClientId = clientId;
     mCurrentSessionId = sessionId;
     mCurrentCallingUid = callingUid;
@@ -405,6 +415,8 @@
         return AMEDIA_ERROR_UNKNOWN;
     }
 
+    callback->onHeartBeat(clientId, sessionId);
+
     media_status_t err = mTranscoder->configureSource(srcFdInt);
     if (err != AMEDIA_OK) {
         ALOGE("failed to configure source: %d", err);
@@ -412,6 +424,8 @@
         return err;
     }
 
+    callback->onHeartBeat(clientId, sessionId);
+
     std::vector<std::shared_ptr<AMediaFormat>> trackFormats = mTranscoder->getTrackFormats();
     if (trackFormats.size() == 0) {
         ALOGE("failed to get track formats!");
@@ -419,6 +433,8 @@
         return AMEDIA_ERROR_MALFORMED;
     }
 
+    callback->onHeartBeat(clientId, sessionId);
+
     for (int i = 0; i < trackFormats.size(); ++i) {
         std::shared_ptr<AMediaFormat> format;
         const char* mime = nullptr;
@@ -437,6 +453,8 @@
             *failureReason = TranscodingLogger::SessionEndedReason::CONFIG_TRACK_FAILED;
             return err;
         }
+
+        callback->onHeartBeat(clientId, sessionId);
     }
 
     err = mTranscoder->configureDestination(dstFdInt);
@@ -446,6 +464,8 @@
         return err;
     }
 
+    callback->onHeartBeat(clientId, sessionId);
+
     return AMEDIA_OK;
 }
 
diff --git a/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp b/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
index 88c1c42..10b2e80 100644
--- a/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
+++ b/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
@@ -328,8 +328,8 @@
                 }
                 lastProgressUpdate = progress;
             }
-            progressSinceLastReport = true;
         }
+        progressSinceLastReport = true;
     }
 
     return AMEDIA_OK;
diff --git a/media/libmediatranscoding/transcoder/MediaTranscoder.cpp b/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
index 413f049..879241e 100644
--- a/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
+++ b/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
@@ -158,6 +158,11 @@
         return;
     }
 
+    // The sample writer is not yet started so notify the caller that progress is still made.
+    if (mHeartBeatIntervalUs > 0) {
+        mCallbacks->onHeartBeat(this);
+    }
+
     MediaTrackTranscoder* mutableTranscoder = const_cast<MediaTrackTranscoder*>(transcoder);
     mutableTranscoder->setSampleConsumer(consumer);
 
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 99d10d2..6cd20a1 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -743,7 +743,7 @@
     return Status::ok();
 }
 
-int CameraService::getDeviceVersion(const String8& cameraId, int* facing) {
+int CameraService::getDeviceVersion(const String8& cameraId, int* facing, int* orientation) {
     ATRACE_CALL();
 
     int deviceVersion = 0;
@@ -760,6 +760,9 @@
         res = mCameraProviderManager->getCameraInfo(cameraId.string(), &info);
         if (res != OK) return -1;
         *facing = info.facing;
+        if (orientation) {
+            *orientation = info.orientation;
+        }
     }
 
     return deviceVersion;
@@ -1555,6 +1558,7 @@
 
     sp<CLIENT> client = nullptr;
     int facing = -1;
+    int orientation = 0;
     bool isNdk = (clientPackageName.size() == 0);
     {
         // Acquire mServiceLock and prevent other clients from connecting
@@ -1620,7 +1624,7 @@
         // give flashlight a chance to close devices if necessary.
         mFlashlight->prepareDeviceOpen(cameraId);
 
-        int deviceVersion = getDeviceVersion(cameraId, /*out*/&facing);
+        int deviceVersion = getDeviceVersion(cameraId, /*out*/&facing, /*out*/&orientation);
         if (facing == -1) {
             ALOGE("%s: Unable to get camera device \"%s\"  facing", __FUNCTION__, cameraId.string());
             return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
@@ -1688,6 +1692,9 @@
         // Set rotate-and-crop override behavior
         if (mOverrideRotateAndCropMode != ANDROID_SCALER_ROTATE_AND_CROP_AUTO) {
             client->setRotateAndCropOverride(mOverrideRotateAndCropMode);
+        } else if (CameraServiceProxyWrapper::isRotateAndCropOverrideNeeded(clientPackageName,
+                    orientation, facing)) {
+            client->setRotateAndCropOverride(ANDROID_SCALER_ROTATE_AND_CROP_90);
         }
 
         // Set camera muting behavior
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index dbfc6c3..092d916 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -213,7 +213,8 @@
 
     /////////////////////////////////////////////////////////////////////
     // CameraDeviceFactory functionality
-    int                 getDeviceVersion(const String8& cameraId, int* facing = NULL);
+    int                 getDeviceVersion(const String8& cameraId, int* facing = nullptr,
+            int* orientation = nullptr);
 
     /////////////////////////////////////////////////////////////////////
     // Shared utilities
diff --git a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
index 0557fcc..76927c0 100644
--- a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
+++ b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
@@ -120,6 +120,21 @@
     proxyBinder->pingForUserUpdate();
 }
 
+bool CameraServiceProxyWrapper::isRotateAndCropOverrideNeeded(
+        String16 packageName, int sensorOrientation, int lensFacing) {
+    sp<ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
+    if (proxyBinder == nullptr) return true;
+    bool ret = true;
+    auto status = proxyBinder->isRotateAndCropOverrideNeeded(packageName, sensorOrientation,
+            lensFacing, &ret);
+    if (!status.isOk()) {
+        ALOGE("%s: Failed during top activity orientation query: %s", __FUNCTION__,
+                status.exceptionMessage().c_str());
+    }
+
+    return ret;
+}
+
 void CameraServiceProxyWrapper::updateProxyDeviceState(const CameraSessionStats& sessionStats) {
     sp<ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
     if (proxyBinder == nullptr) return;
diff --git a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
index 9525935..ad9db68 100644
--- a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
+++ b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
@@ -90,6 +90,10 @@
 
     // Ping camera service proxy for user update
     static void pingCameraServiceProxy();
+
+    // Check whether the current top activity needs a rotate and crop override.
+    static bool isRotateAndCropOverrideNeeded(String16 packageName, int sensorOrientation,
+            int lensFacing);
 };
 
 } // android