Merge "MTP: add friend class for fuzz testing"
diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp
index 25a81eb..d24cb81 100644
--- a/camera/ndk/impl/ACameraDevice.cpp
+++ b/camera/ndk/impl/ACameraDevice.cpp
@@ -28,6 +28,10 @@
 
 #include "ACameraCaptureSession.inc"
 
+ACameraDevice::~ACameraDevice() {
+    mDevice->stopLooper();
+}
+
 namespace android {
 namespace acam {
 
@@ -116,14 +120,10 @@
         if (!isClosed()) {
             disconnectLocked(session);
         }
+        LOG_ALWAYS_FATAL_IF(mCbLooper != nullptr,
+                "CameraDevice looper should've been stopped before ~CameraDevice");
         mCurrentSession = nullptr;
-        if (mCbLooper != nullptr) {
-            mCbLooper->unregisterHandler(mHandler->id());
-            mCbLooper->stop();
-        }
     }
-    mCbLooper.clear();
-    mHandler.clear();
 }
 
 void
@@ -892,6 +892,16 @@
     return;
 }
 
+void CameraDevice::stopLooper() {
+    Mutex::Autolock _l(mDeviceLock);
+    if (mCbLooper != nullptr) {
+      mCbLooper->unregisterHandler(mHandler->id());
+      mCbLooper->stop();
+    }
+    mCbLooper.clear();
+    mHandler.clear();
+}
+
 CameraDevice::CallbackHandler::CallbackHandler(const char* id) : mId(id) {
 }
 
diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h
index c92a95f..7a35bf0 100644
--- a/camera/ndk/impl/ACameraDevice.h
+++ b/camera/ndk/impl/ACameraDevice.h
@@ -109,6 +109,9 @@
 
     inline ACameraDevice* getWrapper() const { return mWrapper; };
 
+    // Stop the looper thread and unregister the handler
+    void stopLooper();
+
   private:
     friend ACameraCaptureSession;
     camera_status_t checkCameraClosedOrErrorLocked() const;
@@ -354,7 +357,7 @@
                   sp<ACameraMetadata> chars) :
             mDevice(new android::acam::CameraDevice(id, cb, chars, this)) {}
 
-    ~ACameraDevice() {};
+    ~ACameraDevice();
 
     /*******************
      * NDK public APIs *
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 9ec64e1..4a801a7 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -7696,14 +7696,14 @@
      * case, when the application configures a RAW stream, the camera device will make sure
      * the active physical camera will remain active to ensure consistent RAW output
      * behavior, and not switch to other physical cameras.</p>
-     * <p>To maintain backward compatibility, the capture request and result metadata tags
-     * required for basic camera functionalities will be solely based on the
-     * logical camera capabiltity. Other request and result metadata tags, on the other
-     * hand, will be based on current active physical camera. For example, the physical
-     * cameras' sensor sensitivity and lens capability could be different from each other.
-     * So when the application manually controls sensor exposure time/gain, or does manual
-     * focus control, it must checks the current active physical camera's exposure, gain,
-     * and focus distance range.</p>
+     * <p>The capture request and result metadata tags required for backward compatible camera
+     * functionalities will be solely based on the logical camera capabiltity. On the other
+     * hand, the use of manual capture controls (sensor or post-processing) with a
+     * logical camera may result in unexpected behavior when the HAL decides to switch
+     * between physical cameras with different characteristics under the hood. For example,
+     * when the application manually sets exposure time and sensitivity while zooming in,
+     * the brightness of the camera images may suddenly change because HAL switches from one
+     * physical camera to the other.</p>
      *
      * @see ACAMERA_LENS_DISTORTION
      * @see ACAMERA_LENS_INFO_FOCUS_DISTANCE_CALIBRATION
diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
index a43d707..35c8355 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
+++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
@@ -44,6 +44,10 @@
 
 using namespace android;
 
+ACameraDevice::~ACameraDevice() {
+    mDevice->stopLooper();
+}
+
 namespace android {
 namespace acam {
 
@@ -130,13 +134,9 @@
             disconnectLocked(session);
         }
         mCurrentSession = nullptr;
-        if (mCbLooper != nullptr) {
-            mCbLooper->unregisterHandler(mHandler->id());
-            mCbLooper->stop();
-        }
+        LOG_ALWAYS_FATAL_IF(mCbLooper != nullptr,
+            "CameraDevice looper should've been stopped before ~CameraDevice");
     }
-    mCbLooper.clear();
-    mHandler.clear();
 }
 
 void
@@ -1400,6 +1400,16 @@
     }
 }
 
+void CameraDevice::stopLooper() {
+    Mutex::Autolock _l(mDeviceLock);
+    if (mCbLooper != nullptr) {
+      mCbLooper->unregisterHandler(mHandler->id());
+      mCbLooper->stop();
+    }
+    mCbLooper.clear();
+    mHandler.clear();
+}
+
 /**
   * Camera service callback implementation
   */
diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.h b/camera/ndk/ndk_vendor/impl/ACameraDevice.h
index 829b084..9e034c4 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraDevice.h
+++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.h
@@ -133,8 +133,12 @@
     bool setDeviceMetadataQueues();
     inline ACameraDevice* getWrapper() const { return mWrapper; };
 
+    // Stop the looper thread and unregister the handler
+    void stopLooper();
+
   private:
     friend ACameraCaptureSession;
+    friend ACameraDevice;
 
     camera_status_t checkCameraClosedOrErrorLocked() const;
 
@@ -383,8 +387,7 @@
                   sp<ACameraMetadata> chars) :
             mDevice(new android::acam::CameraDevice(id, cb, std::move(chars), this)) {}
 
-    ~ACameraDevice() {};
-
+    ~ACameraDevice();
     /*******************
      * NDK public APIs *
      *******************/
diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp
index 8fe029a..f07a1e6 100644
--- a/camera/tests/CameraBinderTests.cpp
+++ b/camera/tests/CameraBinderTests.cpp
@@ -57,7 +57,7 @@
 #include <algorithm>
 
 using namespace android;
-using ::android::hardware::ICameraServiceDefault;
+using ::android::hardware::ICameraService;
 using ::android::hardware::camera2::ICameraDeviceUser;
 
 #define ASSERT_NOT_NULL(x) \
@@ -507,7 +507,7 @@
         bool queryStatus;
         res = device->isSessionConfigurationSupported(sessionConfiguration, &queryStatus);
         EXPECT_TRUE(res.isOk() ||
-                (res.serviceSpecificErrorCode() == ICameraServiceDefault::ERROR_INVALID_OPERATION))
+                (res.serviceSpecificErrorCode() == ICameraService::ERROR_INVALID_OPERATION))
                 << res;
         if (res.isOk()) {
             EXPECT_TRUE(queryStatus);
diff --git a/media/codec2/components/aom/C2SoftAomDec.cpp b/media/codec2/components/aom/C2SoftAomDec.cpp
index 769895c..0cf277f 100644
--- a/media/codec2/components/aom/C2SoftAomDec.cpp
+++ b/media/codec2/components/aom/C2SoftAomDec.cpp
@@ -340,6 +340,7 @@
     aom_codec_flags_t flags;
     memset(&flags, 0, sizeof(aom_codec_flags_t));
 
+    ALOGV("Using libaom AV1 software decoder.");
     aom_codec_err_t err;
     if ((err = aom_codec_dec_init(mCodecCtx, aom_codec_av1_dx(), &cfg, 0))) {
         ALOGE("av1 decoder failed to initialize. (%d)", err);
diff --git a/media/codec2/components/gav1/Android.bp b/media/codec2/components/gav1/Android.bp
index da76e9d..0a0545d 100644
--- a/media/codec2/components/gav1/Android.bp
+++ b/media/codec2/components/gav1/Android.bp
@@ -6,4 +6,9 @@
     ],
 
     srcs: ["C2SoftGav1Dec.cpp"],
+    static_libs: ["libgav1"],
+
+    include_dirs: [
+        "external/libgav1/libgav1/",
+    ],
 }
diff --git a/media/codec2/components/gav1/C2SoftGav1Dec.cpp b/media/codec2/components/gav1/C2SoftGav1Dec.cpp
index f6dd14a..f5321ba 100644
--- a/media/codec2/components/gav1/C2SoftGav1Dec.cpp
+++ b/media/codec2/components/gav1/C2SoftGav1Dec.cpp
@@ -27,8 +27,6 @@
 
 namespace android {
 
-// TODO(vigneshv): This will be changed to c2.android.av1.decoder once this
-// component is fully functional.
 constexpr char COMPONENT_NAME[] = "c2.android.gav1.decoder";
 
 class C2SoftGav1Dec::IntfImpl : public SimpleInterface<void>::BaseParams {
@@ -256,20 +254,497 @@
                              const std::shared_ptr<IntfImpl> &intfImpl)
     : SimpleC2Component(
           std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
-      mIntf(intfImpl) {}
+      mIntf(intfImpl),
+      mCodecCtx(nullptr) {
+  gettimeofday(&mTimeStart, nullptr);
+  gettimeofday(&mTimeEnd, nullptr);
+}
 
-c2_status_t C2SoftGav1Dec::onInit() { return C2_OK; }
-c2_status_t C2SoftGav1Dec::onStop() { return C2_OK; }
-void C2SoftGav1Dec::onReset() {}
-void C2SoftGav1Dec::onRelease(){};
-c2_status_t C2SoftGav1Dec::onFlush_sm() { return C2_OK; }
-void C2SoftGav1Dec::process(const std::unique_ptr<C2Work> & /*work*/,
-                            const std::shared_ptr<C2BlockPool> & /*pool*/) {}
-c2_status_t C2SoftGav1Dec::drain(
-    uint32_t /*drainMode*/, const std::shared_ptr<C2BlockPool> & /*pool*/) {
+C2SoftGav1Dec::~C2SoftGav1Dec() { onRelease(); }
+
+c2_status_t C2SoftGav1Dec::onInit() {
+  return initDecoder() ? C2_OK : C2_CORRUPTED;
+}
+
+c2_status_t C2SoftGav1Dec::onStop() {
+  mSignalledError = false;
+  mSignalledOutputEos = false;
   return C2_OK;
 }
 
+void C2SoftGav1Dec::onReset() {
+  (void)onStop();
+  c2_status_t err = onFlush_sm();
+  if (err != C2_OK) {
+    ALOGW("Failed to flush the av1 decoder. Trying to hard reset.");
+    destroyDecoder();
+    if (!initDecoder()) {
+      ALOGE("Hard reset failed.");
+    }
+  }
+}
+
+void C2SoftGav1Dec::onRelease() { destroyDecoder(); }
+
+c2_status_t C2SoftGav1Dec::onFlush_sm() {
+  Libgav1StatusCode status =
+      mCodecCtx->EnqueueFrame(/*data=*/nullptr, /*size=*/0,
+                              /*user_private_data=*/0);
+  if (status != kLibgav1StatusOk) {
+    ALOGE("Failed to flush av1 decoder. status: %d.", status);
+    return C2_CORRUPTED;
+  }
+
+  // Dequeue frame (if any) that was enqueued previously.
+  const libgav1::DecoderBuffer *buffer;
+  status = mCodecCtx->DequeueFrame(&buffer);
+  if (status != kLibgav1StatusOk) {
+    ALOGE("Failed to dequeue frame after flushing the av1 decoder. status: %d",
+          status);
+    return C2_CORRUPTED;
+  }
+
+  mSignalledError = false;
+  mSignalledOutputEos = false;
+
+  return C2_OK;
+}
+
+static int GetCPUCoreCount() {
+  int cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+  cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+  // _SC_NPROC_ONLN must be defined...
+  cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+  CHECK(cpuCoreCount >= 1);
+  ALOGV("Number of CPU cores: %d", cpuCoreCount);
+  return cpuCoreCount;
+}
+
+bool C2SoftGav1Dec::initDecoder() {
+  mSignalledError = false;
+  mSignalledOutputEos = false;
+  mCodecCtx.reset(new libgav1::Decoder());
+
+  if (mCodecCtx == nullptr) {
+    ALOGE("mCodecCtx is null");
+    return false;
+  }
+
+  libgav1::DecoderSettings settings = {};
+  settings.threads = GetCPUCoreCount();
+
+  ALOGV("Using libgav1 AV1 software decoder.");
+  Libgav1StatusCode status = mCodecCtx->Init(&settings);
+  if (status != kLibgav1StatusOk) {
+    ALOGE("av1 decoder failed to initialize. status: %d.", status);
+    return false;
+  }
+
+  return true;
+}
+
+void C2SoftGav1Dec::destroyDecoder() { mCodecCtx = nullptr; }
+
+void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+  uint32_t flags = 0;
+  if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+    flags |= C2FrameData::FLAG_END_OF_STREAM;
+    ALOGV("signalling eos");
+  }
+  work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+  work->worklets.front()->output.buffers.clear();
+  work->worklets.front()->output.ordinal = work->input.ordinal;
+  work->workletsProcessed = 1u;
+}
+
+void C2SoftGav1Dec::finishWork(uint64_t index,
+                               const std::unique_ptr<C2Work> &work,
+                               const std::shared_ptr<C2GraphicBlock> &block) {
+  std::shared_ptr<C2Buffer> buffer =
+      createGraphicBuffer(block, C2Rect(mWidth, mHeight));
+  auto fillWork = [buffer, index](const std::unique_ptr<C2Work> &work) {
+    uint32_t flags = 0;
+    if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) &&
+        (c2_cntr64_t(index) == work->input.ordinal.frameIndex)) {
+      flags |= C2FrameData::FLAG_END_OF_STREAM;
+      ALOGV("signalling eos");
+    }
+    work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.buffers.push_back(buffer);
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->workletsProcessed = 1u;
+  };
+  if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
+    fillWork(work);
+  } else {
+    finish(index, fillWork);
+  }
+}
+
+void C2SoftGav1Dec::process(const std::unique_ptr<C2Work> &work,
+                            const std::shared_ptr<C2BlockPool> &pool) {
+  work->result = C2_OK;
+  work->workletsProcessed = 0u;
+  work->worklets.front()->output.configUpdate.clear();
+  work->worklets.front()->output.flags = work->input.flags;
+  if (mSignalledError || mSignalledOutputEos) {
+    work->result = C2_BAD_VALUE;
+    return;
+  }
+
+  size_t inOffset = 0u;
+  size_t inSize = 0u;
+  C2ReadView rView = mDummyReadView;
+  if (!work->input.buffers.empty()) {
+    rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+    inSize = rView.capacity();
+    if (inSize && rView.error()) {
+      ALOGE("read view map failed %d", rView.error());
+      work->result = C2_CORRUPTED;
+      return;
+    }
+  }
+
+  bool codecConfig =
+      ((work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) != 0);
+  bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+
+  ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x", inSize,
+        (int)work->input.ordinal.timestamp.peeku(),
+        (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
+
+  if (codecConfig) {
+    fillEmptyWork(work);
+    return;
+  }
+
+  int64_t frameIndex = work->input.ordinal.frameIndex.peekll();
+  if (inSize) {
+    uint8_t *bitstream = const_cast<uint8_t *>(rView.data() + inOffset);
+    int32_t decodeTime = 0;
+    int32_t delay = 0;
+
+    GETTIME(&mTimeStart, nullptr);
+    TIME_DIFF(mTimeEnd, mTimeStart, delay);
+
+    const Libgav1StatusCode status =
+        mCodecCtx->EnqueueFrame(bitstream, inSize, frameIndex);
+
+    GETTIME(&mTimeEnd, nullptr);
+    TIME_DIFF(mTimeStart, mTimeEnd, decodeTime);
+    ALOGV("decodeTime=%4d delay=%4d\n", decodeTime, delay);
+
+    if (status != kLibgav1StatusOk) {
+      ALOGE("av1 decoder failed to decode frame. status: %d.", status);
+      work->result = C2_CORRUPTED;
+      work->workletsProcessed = 1u;
+      mSignalledError = true;
+      return;
+    }
+
+  } else {
+    const Libgav1StatusCode status =
+        mCodecCtx->EnqueueFrame(/*data=*/nullptr, /*size=*/0,
+                                /*user_private_data=*/0);
+    if (status != kLibgav1StatusOk) {
+      ALOGE("Failed to flush av1 decoder. status: %d.", status);
+      work->result = C2_CORRUPTED;
+      work->workletsProcessed = 1u;
+      mSignalledError = true;
+      return;
+    }
+  }
+
+  (void)outputBuffer(pool, work);
+
+  if (eos) {
+    drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
+    mSignalledOutputEos = true;
+  } else if (!inSize) {
+    fillEmptyWork(work);
+  }
+}
+
+static void copyOutputBufferToYV12Frame(uint8_t *dst, const uint8_t *srcY,
+                                        const uint8_t *srcU,
+                                        const uint8_t *srcV, size_t srcYStride,
+                                        size_t srcUStride, size_t srcVStride,
+                                        uint32_t width, uint32_t height) {
+  const size_t dstYStride = align(width, 16);
+  const size_t dstUVStride = align(dstYStride / 2, 16);
+  uint8_t *const dstStart = dst;
+
+  for (size_t i = 0; i < height; ++i) {
+    memcpy(dst, srcY, width);
+    srcY += srcYStride;
+    dst += dstYStride;
+  }
+
+  dst = dstStart + dstYStride * height;
+  for (size_t i = 0; i < height / 2; ++i) {
+    memcpy(dst, srcV, width / 2);
+    srcV += srcVStride;
+    dst += dstUVStride;
+  }
+
+  dst = dstStart + (dstYStride * height) + (dstUVStride * height / 2);
+  for (size_t i = 0; i < height / 2; ++i) {
+    memcpy(dst, srcU, width / 2);
+    srcU += srcUStride;
+    dst += dstUVStride;
+  }
+}
+
+static void convertYUV420Planar16ToY410(uint32_t *dst, const uint16_t *srcY,
+                                        const uint16_t *srcU,
+                                        const uint16_t *srcV, size_t srcYStride,
+                                        size_t srcUStride, size_t srcVStride,
+                                        size_t dstStride, size_t width,
+                                        size_t height) {
+  // Converting two lines at a time, slightly faster
+  for (size_t y = 0; y < height; y += 2) {
+    uint32_t *dstTop = (uint32_t *)dst;
+    uint32_t *dstBot = (uint32_t *)(dst + dstStride);
+    uint16_t *ySrcTop = (uint16_t *)srcY;
+    uint16_t *ySrcBot = (uint16_t *)(srcY + srcYStride);
+    uint16_t *uSrc = (uint16_t *)srcU;
+    uint16_t *vSrc = (uint16_t *)srcV;
+
+    uint32_t u01, v01, y01, y23, y45, y67, uv0, uv1;
+    size_t x = 0;
+    for (; x < width - 3; x += 4) {
+      u01 = *((uint32_t *)uSrc);
+      uSrc += 2;
+      v01 = *((uint32_t *)vSrc);
+      vSrc += 2;
+
+      y01 = *((uint32_t *)ySrcTop);
+      ySrcTop += 2;
+      y23 = *((uint32_t *)ySrcTop);
+      ySrcTop += 2;
+      y45 = *((uint32_t *)ySrcBot);
+      ySrcBot += 2;
+      y67 = *((uint32_t *)ySrcBot);
+      ySrcBot += 2;
+
+      uv0 = (u01 & 0x3FF) | ((v01 & 0x3FF) << 20);
+      uv1 = (u01 >> 16) | ((v01 >> 16) << 20);
+
+      *dstTop++ = 3 << 30 | ((y01 & 0x3FF) << 10) | uv0;
+      *dstTop++ = 3 << 30 | ((y01 >> 16) << 10) | uv0;
+      *dstTop++ = 3 << 30 | ((y23 & 0x3FF) << 10) | uv1;
+      *dstTop++ = 3 << 30 | ((y23 >> 16) << 10) | uv1;
+
+      *dstBot++ = 3 << 30 | ((y45 & 0x3FF) << 10) | uv0;
+      *dstBot++ = 3 << 30 | ((y45 >> 16) << 10) | uv0;
+      *dstBot++ = 3 << 30 | ((y67 & 0x3FF) << 10) | uv1;
+      *dstBot++ = 3 << 30 | ((y67 >> 16) << 10) | uv1;
+    }
+
+    // There should be at most 2 more pixels to process. Note that we don't
+    // need to consider odd case as the buffer is always aligned to even.
+    if (x < width) {
+      u01 = *uSrc;
+      v01 = *vSrc;
+      y01 = *((uint32_t *)ySrcTop);
+      y45 = *((uint32_t *)ySrcBot);
+      uv0 = (u01 & 0x3FF) | ((v01 & 0x3FF) << 20);
+      *dstTop++ = ((y01 & 0x3FF) << 10) | uv0;
+      *dstTop++ = ((y01 >> 16) << 10) | uv0;
+      *dstBot++ = ((y45 & 0x3FF) << 10) | uv0;
+      *dstBot++ = ((y45 >> 16) << 10) | uv0;
+    }
+
+    srcY += srcYStride * 2;
+    srcU += srcUStride;
+    srcV += srcVStride;
+    dst += dstStride * 2;
+  }
+}
+
+static void convertYUV420Planar16ToYUV420Planar(
+    uint8_t *dst, const uint16_t *srcY, const uint16_t *srcU,
+    const uint16_t *srcV, size_t srcYStride, size_t srcUStride,
+    size_t srcVStride, size_t dstStride, size_t width, size_t height) {
+  uint8_t *dstY = (uint8_t *)dst;
+  size_t dstYSize = dstStride * height;
+  size_t dstUVStride = align(dstStride / 2, 16);
+  size_t dstUVSize = dstUVStride * height / 2;
+  uint8_t *dstV = dstY + dstYSize;
+  uint8_t *dstU = dstV + dstUVSize;
+
+  for (size_t y = 0; y < height; ++y) {
+    for (size_t x = 0; x < width; ++x) {
+      dstY[x] = (uint8_t)(srcY[x] >> 2);
+    }
+
+    srcY += srcYStride;
+    dstY += dstStride;
+  }
+
+  for (size_t y = 0; y < (height + 1) / 2; ++y) {
+    for (size_t x = 0; x < (width + 1) / 2; ++x) {
+      dstU[x] = (uint8_t)(srcU[x] >> 2);
+      dstV[x] = (uint8_t)(srcV[x] >> 2);
+    }
+
+    srcU += srcUStride;
+    srcV += srcVStride;
+    dstU += dstUVStride;
+    dstV += dstUVStride;
+  }
+}
+
+bool C2SoftGav1Dec::outputBuffer(const std::shared_ptr<C2BlockPool> &pool,
+                                 const std::unique_ptr<C2Work> &work) {
+  if (!(work && pool)) return false;
+
+  const libgav1::DecoderBuffer *buffer;
+  const Libgav1StatusCode status = mCodecCtx->DequeueFrame(&buffer);
+
+  if (status != kLibgav1StatusOk) {
+    ALOGE("av1 decoder DequeueFrame failed. status: %d.", status);
+    return false;
+  }
+
+  // |buffer| can be NULL if status was equal to kLibgav1StatusOk. This is not
+  // an error. This could mean one of two things:
+  //  - The EnqueueFrame() call was either a flush (called with nullptr).
+  //  - The enqueued frame did not have any displayable frames.
+  if (!buffer) {
+    return false;
+  }
+
+  const int width = buffer->displayed_width[0];
+  const int height = buffer->displayed_height[0];
+  if (width != mWidth || height != mHeight) {
+    mWidth = width;
+    mHeight = height;
+
+    C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight);
+    std::vector<std::unique_ptr<C2SettingResult>> failures;
+    c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures);
+    if (err == C2_OK) {
+      work->worklets.front()->output.configUpdate.push_back(
+          C2Param::Copy(size));
+    } else {
+      ALOGE("Config update size failed");
+      mSignalledError = true;
+      work->result = C2_CORRUPTED;
+      work->workletsProcessed = 1u;
+      return false;
+    }
+  }
+
+  // TODO(vigneshv): Add support for monochrome videos since AV1 supports it.
+  CHECK(buffer->image_format == libgav1::kImageFormatYuv420);
+
+  std::shared_ptr<C2GraphicBlock> block;
+  uint32_t format = HAL_PIXEL_FORMAT_YV12;
+  if (buffer->bitdepth == 10) {
+    IntfImpl::Lock lock = mIntf->lock();
+    std::shared_ptr<C2StreamColorAspectsTuning::output> defaultColorAspects =
+        mIntf->getDefaultColorAspects_l();
+
+    if (defaultColorAspects->primaries == C2Color::PRIMARIES_BT2020 &&
+        defaultColorAspects->matrix == C2Color::MATRIX_BT2020 &&
+        defaultColorAspects->transfer == C2Color::TRANSFER_ST2084) {
+      format = HAL_PIXEL_FORMAT_RGBA_1010102;
+    }
+  }
+  C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+
+  c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 16), mHeight, format,
+                                            usage, &block);
+
+  if (err != C2_OK) {
+    ALOGE("fetchGraphicBlock for Output failed with status %d", err);
+    work->result = err;
+    return false;
+  }
+
+  C2GraphicView wView = block->map().get();
+
+  if (wView.error()) {
+    ALOGE("graphic view map failed %d", wView.error());
+    work->result = C2_CORRUPTED;
+    return false;
+  }
+
+  ALOGV("provided (%dx%d) required (%dx%d), out frameindex %d", block->width(),
+        block->height(), mWidth, mHeight, (int)buffer->user_private_data);
+
+  uint8_t *dst = const_cast<uint8_t *>(wView.data()[C2PlanarLayout::PLANE_Y]);
+  size_t srcYStride = buffer->stride[0];
+  size_t srcUStride = buffer->stride[1];
+  size_t srcVStride = buffer->stride[2];
+
+  if (buffer->bitdepth == 10) {
+    const uint16_t *srcY = (const uint16_t *)buffer->plane[0];
+    const uint16_t *srcU = (const uint16_t *)buffer->plane[1];
+    const uint16_t *srcV = (const uint16_t *)buffer->plane[2];
+
+    if (format == HAL_PIXEL_FORMAT_RGBA_1010102) {
+      convertYUV420Planar16ToY410(
+          (uint32_t *)dst, srcY, srcU, srcV, srcYStride / 2, srcUStride / 2,
+          srcVStride / 2, align(mWidth, 16), mWidth, mHeight);
+    } else {
+      convertYUV420Planar16ToYUV420Planar(dst, srcY, srcU, srcV, srcYStride / 2,
+                                          srcUStride / 2, srcVStride / 2,
+                                          align(mWidth, 16), mWidth, mHeight);
+    }
+  } else {
+    const uint8_t *srcY = (const uint8_t *)buffer->plane[0];
+    const uint8_t *srcU = (const uint8_t *)buffer->plane[1];
+    const uint8_t *srcV = (const uint8_t *)buffer->plane[2];
+    copyOutputBufferToYV12Frame(dst, srcY, srcU, srcV, srcYStride, srcUStride,
+                                srcVStride, mWidth, mHeight);
+  }
+  finishWork(buffer->user_private_data, work, std::move(block));
+  block = nullptr;
+  return true;
+}
+
+c2_status_t C2SoftGav1Dec::drainInternal(
+    uint32_t drainMode, const std::shared_ptr<C2BlockPool> &pool,
+    const std::unique_ptr<C2Work> &work) {
+  if (drainMode == NO_DRAIN) {
+    ALOGW("drain with NO_DRAIN: no-op");
+    return C2_OK;
+  }
+  if (drainMode == DRAIN_CHAIN) {
+    ALOGW("DRAIN_CHAIN not supported");
+    return C2_OMITTED;
+  }
+
+  Libgav1StatusCode status =
+      mCodecCtx->EnqueueFrame(/*data=*/nullptr, /*size=*/0,
+                              /*user_private_data=*/0);
+  if (status != kLibgav1StatusOk) {
+    ALOGE("Failed to flush av1 decoder. status: %d.", status);
+    return C2_CORRUPTED;
+  }
+
+  while (outputBuffer(pool, work)) {
+  }
+
+  if (drainMode == DRAIN_COMPONENT_WITH_EOS && work &&
+      work->workletsProcessed == 0u) {
+    fillEmptyWork(work);
+  }
+
+  return C2_OK;
+}
+
+c2_status_t C2SoftGav1Dec::drain(uint32_t drainMode,
+                                 const std::shared_ptr<C2BlockPool> &pool) {
+  return drainInternal(drainMode, pool, nullptr);
+}
+
 class C2SoftGav1Factory : public C2ComponentFactory {
  public:
   C2SoftGav1Factory()
diff --git a/media/codec2/components/gav1/C2SoftGav1Dec.h b/media/codec2/components/gav1/C2SoftGav1Dec.h
index 40b217c..a7c08bb 100644
--- a/media/codec2/components/gav1/C2SoftGav1Dec.h
+++ b/media/codec2/components/gav1/C2SoftGav1Dec.h
@@ -18,6 +18,13 @@
 #define ANDROID_C2_SOFT_GAV1_DEC_H_
 
 #include <SimpleC2Component.h>
+#include "libgav1/src/decoder.h"
+#include "libgav1/src/decoder_settings.h"
+
+#define GETTIME(a, b) gettimeofday(a, b);
+#define TIME_DIFF(start, end, diff)     \
+    diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \
+            ((end).tv_usec - (start).tv_usec);
 
 namespace android {
 
@@ -26,6 +33,7 @@
 
   C2SoftGav1Dec(const char* name, c2_node_id_t id,
                 const std::shared_ptr<IntfImpl>& intfImpl);
+  ~C2SoftGav1Dec();
 
   // Begin SimpleC2Component overrides.
   c2_status_t onInit() override;
@@ -41,6 +49,7 @@
 
  private:
   std::shared_ptr<IntfImpl> mIntf;
+  std::unique_ptr<libgav1::Decoder> mCodecCtx;
 
   uint32_t mWidth;
   uint32_t mHeight;
diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.cpp b/media/codec2/components/vpx/C2SoftVpxEnc.cpp
index 6509a88..6dab70b 100644
--- a/media/codec2/components/vpx/C2SoftVpxEnc.cpp
+++ b/media/codec2/components/vpx/C2SoftVpxEnc.cpp
@@ -127,14 +127,14 @@
     }
 
     switch (mBitrateMode->value) {
-        case C2Config::BITRATE_VARIABLE:
-            mBitrateControlMode = VPX_VBR;
-            break;
         case C2Config::BITRATE_CONST:
-        default:
             mBitrateControlMode = VPX_CBR;
             break;
-        break;
+        case C2Config::BITRATE_VARIABLE:
+        [[fallthrough]];
+        default:
+            mBitrateControlMode = VPX_VBR;
+            break;
     }
 
     setCodecSpecificInterface();
diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.h b/media/codec2/components/vpx/C2SoftVpxEnc.h
index 90758f9..62ccd1b 100644
--- a/media/codec2/components/vpx/C2SoftVpxEnc.h
+++ b/media/codec2/components/vpx/C2SoftVpxEnc.h
@@ -275,7 +275,7 @@
         addParameter(
             DefineParam(mBitrateMode, C2_PARAMKEY_BITRATE_MODE)
                 .withDefault(new C2StreamBitrateModeTuning::output(
-                        0u, C2Config::BITRATE_CONST))
+                        0u, C2Config::BITRATE_VARIABLE))
                 .withFields({
                     C2F(mBitrateMode, value).oneOf({
                         C2Config::BITRATE_CONST, C2Config::BITRATE_VARIABLE })
diff --git a/media/codec2/components/xaac/C2SoftXaacDec.cpp b/media/codec2/components/xaac/C2SoftXaacDec.cpp
index a3ebadb..60ae93c 100644
--- a/media/codec2/components/xaac/C2SoftXaacDec.cpp
+++ b/media/codec2/components/xaac/C2SoftXaacDec.cpp
@@ -1309,69 +1309,84 @@
                                 &ui_exec_done);
     RETURN_IF_FATAL(err_code,  "IA_CMD_TYPE_DONE_QUERY");
 
-    if (ui_exec_done != 1) {
-        VOID* p_array;        // ITTIAM:buffer to handle gain payload
-        WORD32 buf_size = 0;  // ITTIAM:gain payload length
-        WORD32 bit_str_fmt = 1;
-        WORD32 gain_stream_flag = 1;
-
-        err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
-                                    IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN, &buf_size);
-        RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN");
-
-        err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
-                                    IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF, &p_array);
-        RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF");
-
-        if (buf_size > 0) {
-            /*Set bitstream_split_format */
-            err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
-                                      IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT, &bit_str_fmt);
-            RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
-
-            memcpy(mDrcInBuf, p_array, buf_size);
-            /* Set number of bytes to be processed */
-            err_code =
-                ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES_BS, 0, &buf_size);
-            RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
-
-            err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
-                                      IA_DRC_DEC_CONFIG_GAIN_STREAM_FLAG, &gain_stream_flag);
-            RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
-
-            /* Execute process */
-            err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
-                                      IA_CMD_TYPE_INIT_CPY_BSF_BUFF, nullptr);
-            RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
-
-            mMpegDDRCPresent = 1;
-        }
-    }
-
-    /* How much buffer is used in input buffers */
+    int32_t num_preroll = 0;
     err_code = ixheaacd_dec_api(mXheaacCodecHandle,
-                                IA_API_CMD_GET_CURIDX_INPUT_BUF,
-                                0,
-                                bytesConsumed);
-    RETURN_IF_FATAL(err_code,  "IA_API_CMD_GET_CURIDX_INPUT_BUF");
+                                IA_API_CMD_GET_CONFIG_PARAM,
+                                IA_ENHAACPLUS_DEC_CONFIG_GET_NUM_PRE_ROLL_FRAMES,
+                                &num_preroll);
+    RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GET_NUM_PRE_ROLL_FRAMES");
 
-    /* Get the output bytes */
-    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
-                                IA_API_CMD_GET_OUTPUT_BYTES,
-                                0,
-                                outBytes);
-    RETURN_IF_FATAL(err_code,  "IA_API_CMD_GET_OUTPUT_BYTES");
+    {
+      int32_t preroll_frame_offset = 0;
 
-    if (mMpegDDRCPresent == 1) {
-        memcpy(mDrcInBuf, mOutputBuffer, *outBytes);
-        err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES, 0, outBytes);
-        RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_INPUT_BYTES");
+        do {
+            if (ui_exec_done != 1) {
+                VOID* p_array;        // ITTIAM:buffer to handle gain payload
+                WORD32 buf_size = 0;  // ITTIAM:gain payload length
+                WORD32 bit_str_fmt = 1;
+                WORD32 gain_stream_flag = 1;
 
-        err_code =
-            ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_EXECUTE, IA_CMD_TYPE_DO_EXECUTE, nullptr);
-        RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DO_EXECUTE");
+                err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+                                            IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN, &buf_size);
+                RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN");
 
-        memcpy(mOutputBuffer, mDrcOutBuf, *outBytes);
+                err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+                                            IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF, &p_array);
+                RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF");
+
+                if (buf_size > 0) {
+                    /*Set bitstream_split_format */
+                    err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                                            IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT, &bit_str_fmt);
+                    RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+
+                    memcpy(mDrcInBuf, p_array, buf_size);
+                    /* Set number of bytes to be processed */
+                    err_code =
+                        ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES_BS, 0, &buf_size);
+                    RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+
+                    err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                                            IA_DRC_DEC_CONFIG_GAIN_STREAM_FLAG, &gain_stream_flag);
+                    RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+
+                    /* Execute process */
+                    err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
+                                            IA_CMD_TYPE_INIT_CPY_BSF_BUFF, nullptr);
+                    RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+
+                    mMpegDDRCPresent = 1;
+                }
+            }
+
+            /* How much buffer is used in input buffers */
+            err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                        IA_API_CMD_GET_CURIDX_INPUT_BUF,
+                                        0,
+                                        bytesConsumed);
+            RETURN_IF_FATAL(err_code,  "IA_API_CMD_GET_CURIDX_INPUT_BUF");
+
+            /* Get the output bytes */
+            err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                        IA_API_CMD_GET_OUTPUT_BYTES,
+                                        0,
+                                        outBytes);
+            RETURN_IF_FATAL(err_code,  "IA_API_CMD_GET_OUTPUT_BYTES");
+
+            if (mMpegDDRCPresent == 1) {
+                memcpy(mDrcInBuf, mOutputBuffer + preroll_frame_offset, *outBytes);
+                preroll_frame_offset += *outBytes;
+                err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES, 0, outBytes);
+                RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_INPUT_BYTES");
+
+                err_code =
+                    ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_EXECUTE, IA_CMD_TYPE_DO_EXECUTE, nullptr);
+                RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DO_EXECUTE");
+
+                memcpy(mOutputBuffer, mDrcOutBuf, *outBytes);
+            }
+            num_preroll--;
+        } while (num_preroll > 0);
     }
     return IA_NO_ERROR;
 }
diff --git a/media/codec2/core/include/C2Param.h b/media/codec2/core/include/C2Param.h
index cc8c17a..51d417a 100644
--- a/media/codec2/core/include/C2Param.h
+++ b/media/codec2/core/include/C2Param.h
@@ -176,9 +176,9 @@
             DIR_INPUT      = 0x00000000,
             DIR_OUTPUT     = 0x10000000,
 
-            IS_STREAM_FLAG  = 0x00100000,
-            STREAM_ID_MASK  = 0x03E00000,
-            STREAM_ID_SHIFT = 21,
+            IS_STREAM_FLAG  = 0x02000000,
+            STREAM_ID_MASK  = 0x01F00000,
+            STREAM_ID_SHIFT = 20,
             MAX_STREAM_ID   = STREAM_ID_MASK >> STREAM_ID_SHIFT,
             STREAM_MASK     = IS_STREAM_FLAG | STREAM_ID_MASK,
 
diff --git a/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp b/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp
index 1786c69..50790bc 100644
--- a/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp
+++ b/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp
@@ -82,21 +82,14 @@
 
 template <typename BlockProcessor>
 void forEachBlock(const std::list<std::unique_ptr<C2Work>>& workList,
-                  BlockProcessor process,
-                  bool processInput, bool processOutput) {
+                  BlockProcessor process) {
     for (const std::unique_ptr<C2Work>& work : workList) {
         if (!work) {
             continue;
         }
-        if (processInput) {
-            forEachBlock(work->input, process);
-        }
-        if (processOutput) {
-            for (const std::unique_ptr<C2Worklet>& worklet : work->worklets) {
-                if (worklet) {
-                    forEachBlock(worklet->output,
-                                 process);
-                }
+        for (const std::unique_ptr<C2Worklet>& worklet : work->worklets) {
+            if (worklet) {
+                forEachBlock(worklet->output, process);
             }
         }
     }
@@ -109,8 +102,6 @@
             new B2HGraphicBufferProducer(igbp);
 }
 
-} // unnamed namespace
-
 status_t attachToBufferQueue(const C2ConstGraphicBlock& block,
                              const sp<IGraphicBufferProducer>& igbp,
                              uint32_t generation,
@@ -154,73 +145,221 @@
             _C2BlockFactory::GetGraphicBlockPoolData(block),
             generation, bqId, bqSlot);
 }
+} // unnamed namespace
 
-bool holdBufferQueueBlock(const C2ConstGraphicBlock& block,
-                            const sp<IGraphicBufferProducer>& igbp,
-                            uint64_t bqId,
-                            uint32_t generation) {
-    std::shared_ptr<_C2BlockPoolData> data =
-            _C2BlockFactory::GetGraphicBlockPoolData(block);
-    if (!data) {
-        return false;
-    }
+class OutputBufferQueue::Impl {
+    std::mutex mMutex;
+    sp<IGraphicBufferProducer> mIgbp;
+    uint32_t mGeneration;
+    uint64_t mBqId;
+    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];
 
-    uint32_t oldGeneration;
-    uint64_t oldId;
-    int32_t oldSlot;
-    // If the block is not bufferqueue-based, do nothing.
-    if (!_C2BlockFactory::GetBufferQueueData(
-            data, &oldGeneration, &oldId, &oldSlot) ||
-            (oldId == 0)) {
-        return false;
-    }
+public:
+    Impl(): mGeneration(0), mBqId(0) {}
 
-    // If the block's bqId is the same as the desired bqId, just hold.
-    if ((oldId == bqId) && (oldGeneration == generation)) {
-        LOG(VERBOSE) << "holdBufferQueueBlock -- import without attaching:"
-                     << " bqId " << oldId
-                     << ", bqSlot " << oldSlot
-                     << ", generation " << generation
-                     << ".";
-        _C2BlockFactory::HoldBlockFromBufferQueue(data, getHgbp(igbp));
+    bool configure(const sp<IGraphicBufferProducer>& igbp,
+                   uint32_t generation,
+                   uint64_t bqId) {
+        size_t tryNum = 0;
+        size_t success = 0;
+        sp<GraphicBuffer> buffers[BufferQueueDefs::NUM_BUFFER_SLOTS];
+        std::weak_ptr<_C2BlockPoolData>
+                poolDatas[BufferQueueDefs::NUM_BUFFER_SLOTS];
+        {
+            std::scoped_lock<std::mutex> l(mMutex);
+            if (generation == mGeneration) {
+                return false;
+            }
+            mIgbp = igbp;
+            mGeneration = generation;
+            mBqId = bqId;
+            mOwner = std::make_shared<int>(0);
+            for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; ++i) {
+                if (mBqId == 0 || !mBuffers[i]) {
+                    continue;
+                }
+                std::shared_ptr<_C2BlockPoolData> data = mPoolDatas[i].lock();
+                if (!data ||
+                    !_C2BlockFactory::BeginAttachBlockToBufferQueue(data)) {
+                    continue;
+                }
+                ++tryNum;
+                int bqSlot;
+                mBuffers[i]->setGenerationNumber(generation);
+                status_t result = igbp->attachBuffer(&bqSlot, mBuffers[i]);
+                if (result != OK) {
+                    continue;
+                }
+                bool attach =
+                        _C2BlockFactory::EndAttachBlockToBufferQueue(
+                                data, mOwner, getHgbp(mIgbp),
+                                generation, bqId, bqSlot);
+                if (!attach) {
+                    igbp->cancelBuffer(bqSlot, Fence::NO_FENCE);
+                    continue;
+                }
+                buffers[bqSlot] = mBuffers[i];
+                poolDatas[bqSlot] = data;
+                ++success;
+            }
+            for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; ++i) {
+                mBuffers[i] = buffers[i];
+                mPoolDatas[i] = poolDatas[i];
+            }
+        }
+        ALOGD("remote graphic buffer migration %zu/%zu", success, tryNum);
         return true;
     }
 
-    // Otherwise, attach to the given igbp, which must not be null.
-    if (!igbp) {
+    bool registerBuffer(const C2ConstGraphicBlock& block) {
+        std::shared_ptr<_C2BlockPoolData> data =
+                _C2BlockFactory::GetGraphicBlockPoolData(block);
+        if (!data) {
+            return false;
+        }
+        std::scoped_lock<std::mutex> l(mMutex);
+
+        if (!mIgbp) {
+            return false;
+        }
+
+        uint32_t oldGeneration;
+        uint64_t oldId;
+        int32_t oldSlot;
+        // If the block is not bufferqueue-based, do nothing.
+        if (!_C2BlockFactory::GetBufferQueueData(
+                data, &oldGeneration, &oldId, &oldSlot) || (oldId == 0)) {
+            return false;
+        }
+        // If the block's bqId is the same as the desired bqId, just hold.
+        if ((oldId == mBqId) && (oldGeneration == mGeneration)) {
+            LOG(VERBOSE) << "holdBufferQueueBlock -- import without attaching:"
+                         << " bqId " << oldId
+                         << ", bqSlot " << oldSlot
+                         << ", generation " << mGeneration
+                         << ".";
+            _C2BlockFactory::HoldBlockFromBufferQueue(data, mOwner, getHgbp(mIgbp));
+            mPoolDatas[oldSlot] = data;
+            mBuffers[oldSlot] = createGraphicBuffer(block);
+            mBuffers[oldSlot]->setGenerationNumber(mGeneration);
+            return true;
+        }
+        int32_t d = (int32_t) mGeneration - (int32_t) oldGeneration;
+        LOG(WARNING) << "receiving stale buffer: generation "
+                     << mGeneration << " , diff " << d  << " : slot "
+                     << oldSlot;
         return false;
     }
 
-    int32_t bqSlot;
-    status_t result = attachToBufferQueue(block, igbp, generation, &bqSlot);
+    status_t outputBuffer(
+            const C2ConstGraphicBlock& block,
+            const BnGraphicBufferProducer::QueueBufferInput& input,
+            BnGraphicBufferProducer::QueueBufferOutput* output) {
+        uint32_t generation;
+        uint64_t bqId;
+        int32_t bqSlot;
+        bool display = displayBufferQueueBlock(block);
+        if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot) ||
+            bqId == 0) {
+            // Block not from bufferqueue -- it must be attached before queuing.
 
-    if (result != OK) {
-        LOG(ERROR) << "holdBufferQueueBlock -- fail to attach:"
-                   << " target bqId " << bqId
-                   << ", generation " << generation
-                   << ".";
-        return false;
+            mMutex.lock();
+            sp<IGraphicBufferProducer> outputIgbp = mIgbp;
+            uint32_t outputGeneration = mGeneration;
+            mMutex.unlock();
+
+            status_t status = attachToBufferQueue(
+                    block, outputIgbp, outputGeneration, &bqSlot);
+            if (status != OK) {
+                LOG(WARNING) << "outputBuffer -- attaching failed.";
+                return INVALID_OPERATION;
+            }
+
+            status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
+                                         input, output);
+            if (status != OK) {
+                LOG(ERROR) << "outputBuffer -- queueBuffer() failed "
+                           "on non-bufferqueue-based block. "
+                           "Error = " << status << ".";
+                return status;
+            }
+            return OK;
+        }
+
+        mMutex.lock();
+        sp<IGraphicBufferProducer> outputIgbp = mIgbp;
+        uint32_t outputGeneration = mGeneration;
+        uint64_t outputBqId = mBqId;
+        mMutex.unlock();
+
+        if (!outputIgbp) {
+            LOG(VERBOSE) << "outputBuffer -- output surface is null.";
+            return NO_INIT;
+        }
+
+        if (!display) {
+            LOG(WARNING) << "outputBuffer -- cannot display "
+                         "bufferqueue-based block to the bufferqueue.";
+            return UNKNOWN_ERROR;
+        }
+        if (bqId != outputBqId || generation != outputGeneration) {
+            int32_t diff = (int32_t) outputGeneration - (int32_t) generation;
+            LOG(WARNING) << "outputBuffer -- buffers from old generation to "
+                         << outputGeneration << " , diff: " << diff
+                         << " , slot: " << bqSlot;
+            return DEAD_OBJECT;
+        }
+
+        status_t status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
+                                              input, output);
+        if (status != OK) {
+            LOG(ERROR) << "outputBuffer -- queueBuffer() failed "
+                       "on bufferqueue-based block. "
+                       "Error = " << status << ".";
+            return status;
+        }
+        return OK;
     }
 
-    LOG(VERBOSE) << "holdBufferQueueBlock -- attached:"
-                 << " bqId " << bqId
-                 << ", bqSlot " << bqSlot
-                 << ", generation " << generation
-                 << ".";
-    _C2BlockFactory::AssignBlockToBufferQueue(
-            data, getHgbp(igbp), generation, bqId, bqSlot, true);
-    return true;
+    Impl *getPtr() {
+        return this;
+    }
+
+    ~Impl() {}
+};
+
+OutputBufferQueue::OutputBufferQueue(): mImpl(new Impl()) {}
+
+OutputBufferQueue::~OutputBufferQueue() {}
+
+bool OutputBufferQueue::configure(const sp<IGraphicBufferProducer>& igbp,
+                                  uint32_t generation,
+                                  uint64_t bqId) {
+    return mImpl && mImpl->configure(igbp, generation, bqId);
 }
 
-void holdBufferQueueBlocks(const std::list<std::unique_ptr<C2Work>>& workList,
-                           const sp<IGraphicBufferProducer>& igbp,
-                           uint64_t bqId,
-                           uint32_t generation,
-                           bool forInput) {
+status_t OutputBufferQueue::outputBuffer(
+    const C2ConstGraphicBlock& block,
+    const BnGraphicBufferProducer::QueueBufferInput& input,
+    BnGraphicBufferProducer::QueueBufferOutput* output) {
+    if (mImpl) {
+        return mImpl->outputBuffer(block, input, output);
+    }
+    return DEAD_OBJECT;
+}
+
+void OutputBufferQueue::holdBufferQueueBlocks(
+        const std::list<std::unique_ptr<C2Work>>& workList) {
+    if (!mImpl) {
+        return;
+    }
     forEachBlock(workList,
-                 std::bind(holdBufferQueueBlock,
-                           std::placeholders::_1, igbp, bqId, generation),
-                 forInput, !forInput);
+                 std::bind(&OutputBufferQueue::Impl::registerBuffer,
+                           mImpl->getPtr(), std::placeholders::_1));
 }
 
 }  // namespace utils
diff --git a/media/codec2/hidl/1.0/utils/Component.cpp b/media/codec2/hidl/1.0/utils/Component.cpp
index 5897dce..a9f20a4 100644
--- a/media/codec2/hidl/1.0/utils/Component.cpp
+++ b/media/codec2/hidl/1.0/utils/Component.cpp
@@ -107,19 +107,22 @@
             WorkBundle workBundle;
 
             sp<Component> strongComponent = mComponent.promote();
+            beginTransferBufferQueueBlocks(c2workItems, true);
             if (!objcpy(&workBundle, c2workItems, strongComponent ?
                     &strongComponent->mBufferPoolSender : nullptr)) {
                 LOG(ERROR) << "Component::Listener::onWorkDone_nb -- "
                            << "received corrupted work items.";
+                endTransferBufferQueueBlocks(c2workItems, false, true);
                 return;
             }
             Return<void> transStatus = listener->onWorkDone(workBundle);
             if (!transStatus.isOk()) {
                 LOG(ERROR) << "Component::Listener::onWorkDone_nb -- "
                            << "transaction failed.";
+                endTransferBufferQueueBlocks(c2workItems, false, true);
                 return;
             }
-            yieldBufferQueueBlocks(c2workItems, true);
+            endTransferBufferQueueBlocks(c2workItems, true, true);
         }
     }
 
@@ -254,13 +257,14 @@
 
     WorkBundle flushedWorkBundle;
     Status res = static_cast<Status>(c2res);
+    beginTransferBufferQueueBlocks(c2flushedWorks, true);
     if (c2res == C2_OK) {
         if (!objcpy(&flushedWorkBundle, c2flushedWorks, &mBufferPoolSender)) {
             res = Status::CORRUPTED;
         }
     }
     _hidl_cb(res, flushedWorkBundle);
-    yieldBufferQueueBlocks(c2flushedWorks, true);
+    endTransferBufferQueueBlocks(c2flushedWorks, true, true);
     return Void();
 }
 
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h
index be429ac..0a2298c 100644
--- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h
@@ -28,62 +28,42 @@
 namespace V1_0 {
 namespace utils {
 
-
 // BufferQueue-Based Block Operations
 // ==================================
 
-// Create a GraphicBuffer object from a graphic block and attach it to an
-// IGraphicBufferProducer.
-status_t attachToBufferQueue(const C2ConstGraphicBlock& block,
-                             const sp<IGraphicBufferProducer>& igbp,
-                             uint32_t generation,
-                             int32_t* bqSlot);
+// Manage BufferQueue and graphic blocks for both component and codec.
+// Manage graphic blocks ownership consistently during surface change.
+struct OutputBufferQueue {
 
-// Return false if block does not come from a bufferqueue-based blockpool.
-// Otherwise, extract generation, bqId and bqSlot and return true.
-bool getBufferQueueAssignment(const C2ConstGraphicBlock& block,
-                              uint32_t* generation,
-                              uint64_t* bqId,
-                              int32_t* bqSlot);
+    OutputBufferQueue();
 
-// Assign the given block to a bufferqueue so that when the block is destroyed,
-// cancelBuffer() will be called.
-//
-// If the block does not come from a bufferqueue-based blockpool, this function
-// returns false.
-//
-// If the block already has a bufferqueue assignment that matches the given one,
-// the function returns true.
-//
-// If the block already has a bufferqueue assignment that does not match the
-// given one, the block will be reassigned to the given bufferqueue. This
-// will call attachBuffer() on the given igbp. The function then returns true on
-// success or false on any failure during the operation.
-//
-// Note: This function should be called after detachBuffer() or dequeueBuffer()
-// is called manually.
-bool holdBufferQueueBlock(const C2ConstGraphicBlock& block,
-                          const sp<IGraphicBufferProducer>& igbp,
-                          uint64_t bqId,
-                          uint32_t generation);
+    ~OutputBufferQueue();
 
-// Call holdBufferQueueBlock() on input or output blocks in the given workList.
-// Since the bufferqueue assignment for input and output buffers can be
-// different, this function takes forInput to determine whether the given
-// bufferqueue is for input buffers or output buffers. (The default value of
-// forInput is false.)
-//
-// In the (rare) case that both input and output buffers are bufferqueue-based,
-// this function must be called twice, once for the input buffers and once for
-// the output buffers.
-//
-// Note: This function should be called after WorkBundle has been received from
-// another process.
-void holdBufferQueueBlocks(const std::list<std::unique_ptr<C2Work>>& workList,
-                           const sp<IGraphicBufferProducer>& igbp,
-                           uint64_t bqId,
-                           uint32_t generation,
-                           bool forInput = false);
+    // Configure a new surface to render graphic blocks.
+    // Graphic blocks from older surface will be migrated to new surface.
+    bool configure(const sp<IGraphicBufferProducer>& igbp,
+                   uint32_t generation,
+                   uint64_t bqId);
+
+    // Render a graphic block to current surface.
+    status_t outputBuffer(
+            const C2ConstGraphicBlock& block,
+            const BnGraphicBufferProducer::QueueBufferInput& input,
+            BnGraphicBufferProducer::QueueBufferOutput* output);
+
+    // Call holdBufferQueueBlock() on output blocks in the given workList.
+    // The OutputBufferQueue will take the ownership of output blocks.
+    //
+    // Note: This function should be called after WorkBundle has been received
+    // from another process.
+    void holdBufferQueueBlocks(
+            const std::list<std::unique_ptr<C2Work>>& workList);
+
+private:
+
+    class Impl;
+    std::unique_ptr<Impl> mImpl;
+};
 
 }  // namespace utils
 }  // namespace V1_0
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
index 0ddfec5..a9928b3 100644
--- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
@@ -299,26 +299,46 @@
 // BufferQueue-Based Block Operations
 // ==================================
 
-// Disassociate the given block with its designated bufferqueue so that
-// cancelBuffer() will not be called when the block is destroyed. If the block
-// does not have a designated bufferqueue, the function returns false.
-// Otherwise, it returns true.
+// Call before transferring block to other processes.
 //
-// Note: This function should be called after attachBuffer() or queueBuffer() is
-// called manually.
-bool yieldBufferQueueBlock(const C2ConstGraphicBlock& block);
+// The given block is ready to transfer to other processes. This will guarantee
+// the given block data is not mutated by bufferqueue migration.
+bool beginTransferBufferQueueBlock(const C2ConstGraphicBlock& block);
 
-// Call yieldBufferQueueBlock() on blocks in the given workList. processInput
-// determines whether input blocks are yielded. processOutput works similarly on
-// output blocks. (The default value of processInput is false while the default
-// value of processOutput is true. This implies that in most cases, only output
-// buffers contain bufferqueue-based blocks.)
+// Call beginTransferBufferQueueBlock() on blocks in the given workList.
+// processInput determines whether input blocks are yielded. processOutput
+// works similarly on output blocks. (The default value of processInput is
+// false while the default value of processOutput is true. This implies that in
+// most cases, only output buffers contain bufferqueue-based blocks.)
+void beginTransferBufferQueueBlocks(
+        const std::list<std::unique_ptr<C2Work>>& workList,
+        bool processInput = false,
+        bool processOutput = true);
+
+// Call after transferring block is finished and make sure that
+// beginTransferBufferQueueBlock() is called before.
 //
-// Note: This function should be called after WorkBundle has been successfully
-// sent over the Treble boundary to another process.
-void yieldBufferQueueBlocks(const std::list<std::unique_ptr<C2Work>>& workList,
-                            bool processInput = false,
-                            bool processOutput = true);
+// The transfer of given block is finished. If transfer is successful the given
+// block is not owned by process anymore. Since transfer is finished the given
+// block data is OK to mutate by bufferqueue migration after this call.
+bool endTransferBufferQueueBlock(const C2ConstGraphicBlock& block,
+                                 bool transfer);
+
+// Call endTransferBufferQueueBlock() on blocks in the given workList.
+// processInput determines whether input blocks are yielded. processOutput
+// works similarly on output blocks. (The default value of processInput is
+// false while the default value of processOutput is true. This implies that in
+// most cases, only output buffers contain bufferqueue-based blocks.)
+void endTransferBufferQueueBlocks(
+        const std::list<std::unique_ptr<C2Work>>& workList,
+        bool transfer,
+        bool processInput = false,
+        bool processOutput = true);
+
+// The given block is ready to be rendered. the given block is not owned by
+// process anymore. If migration is in progress, this returns false in order
+// not to render.
+bool displayBufferQueueBlock(const C2ConstGraphicBlock& block);
 
 }  // namespace utils
 }  // namespace V1_0
diff --git a/media/codec2/hidl/1.0/utils/types.cpp b/media/codec2/hidl/1.0/utils/types.cpp
index 3fd2f92..07dbf67 100644
--- a/media/codec2/hidl/1.0/utils/types.cpp
+++ b/media/codec2/hidl/1.0/utils/types.cpp
@@ -737,7 +737,15 @@
                     bufferPoolSender, baseBlocks, baseBlockIndices);
         }
     case _C2BlockPoolData::TYPE_BUFFERQUEUE:
-        // Do the same thing as a NATIVE block.
+        uint32_t gen;
+        uint64_t bqId;
+        int32_t bqSlot;
+        // Update handle if migration happened.
+        if (_C2BlockFactory::GetBufferQueueData(
+                blockPoolData, &gen, &bqId, &bqSlot)) {
+            android::MigrateNativeCodec2GrallocHandle(
+                    const_cast<native_handle_t*>(handle), gen, bqId, bqSlot);
+        }
         return _addBaseBlock(
                 index, handle,
                 baseBlocks, baseBlockIndices);
@@ -1772,20 +1780,53 @@
 
 } // unnamed namespace
 
-bool yieldBufferQueueBlock(const C2ConstGraphicBlock& block) {
+bool beginTransferBufferQueueBlock(const C2ConstGraphicBlock& block) {
     std::shared_ptr<_C2BlockPoolData> data =
             _C2BlockFactory::GetGraphicBlockPoolData(block);
     if (data && _C2BlockFactory::GetBufferQueueData(data)) {
-        _C2BlockFactory::YieldBlockToBufferQueue(data);
+        _C2BlockFactory::BeginTransferBlockToClient(data);
         return true;
     }
     return false;
 }
 
-void yieldBufferQueueBlocks(
+void beginTransferBufferQueueBlocks(
         const std::list<std::unique_ptr<C2Work>>& workList,
         bool processInput, bool processOutput) {
-    forEachBlock(workList, yieldBufferQueueBlock, processInput, processOutput);
+    forEachBlock(workList, beginTransferBufferQueueBlock,
+                 processInput, processOutput);
+}
+
+bool endTransferBufferQueueBlock(
+        const C2ConstGraphicBlock& block,
+        bool transfer) {
+    std::shared_ptr<_C2BlockPoolData> data =
+            _C2BlockFactory::GetGraphicBlockPoolData(block);
+    if (data && _C2BlockFactory::GetBufferQueueData(data)) {
+        _C2BlockFactory::EndTransferBlockToClient(data, transfer);
+        return true;
+    }
+    return false;
+}
+
+void endTransferBufferQueueBlocks(
+        const std::list<std::unique_ptr<C2Work>>& workList,
+        bool transfer,
+        bool processInput, bool processOutput) {
+    forEachBlock(workList,
+                 std::bind(endTransferBufferQueueBlock,
+                           std::placeholders::_1, transfer),
+                 processInput, processOutput);
+}
+
+bool displayBufferQueueBlock(const C2ConstGraphicBlock& block) {
+    std::shared_ptr<_C2BlockPoolData> data =
+            _C2BlockFactory::GetGraphicBlockPoolData(block);
+    if (data && _C2BlockFactory::GetBufferQueueData(data)) {
+        _C2BlockFactory::DisplayBlockToBufferQueue(data);
+        return true;
+    }
+    return false;
 }
 
 }  // namespace utils
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/Android.bp b/media/codec2/hidl/1.0/vts/functional/audio/Android.bp
index 687754b..65f0d09 100644
--- a/media/codec2/hidl/1.0/vts/functional/audio/Android.bp
+++ b/media/codec2/hidl/1.0/vts/functional/audio/Android.bp
@@ -15,19 +15,19 @@
  */
 
 cc_test {
-    name: "VtsHidlC2V1_0TargetAudioDecTest",
-    defaults: ["VtsMediaC2V1_0Defaults"],
+    name: "VtsHalMediaC2V1_0TargetAudioDecTest",
+    defaults: ["VtsHalMediaC2V1_0Defaults"],
     srcs: [
-        "VtsHidlC2V1_0TargetAudioDecTest.cpp",
+        "VtsHalMediaC2V1_0TargetAudioDecTest.cpp",
         //"media_audio_hidl_test_common.cpp"
     ],
 }
 
 cc_test {
-    name: "VtsHidlC2V1_0TargetAudioEncTest",
-    defaults: ["VtsMediaC2V1_0Defaults"],
+    name: "VtsHalMediaC2V1_0TargetAudioEncTest",
+    defaults: ["VtsHalMediaC2V1_0Defaults"],
     srcs: [
-        "VtsHidlC2V1_0TargetAudioEncTest.cpp",
+        "VtsHalMediaC2V1_0TargetAudioEncTest.cpp",
         //"media_audio_hidl_test_common.cpp"
     ],
 }
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
similarity index 100%
rename from media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp
rename to media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
similarity index 100%
rename from media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp
rename to media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
diff --git a/media/codec2/hidl/1.0/vts/functional/common/Android.bp b/media/codec2/hidl/1.0/vts/functional/common/Android.bp
index da0061a..a011ba3 100644
--- a/media/codec2/hidl/1.0/vts/functional/common/Android.bp
+++ b/media/codec2/hidl/1.0/vts/functional/common/Android.bp
@@ -1,5 +1,5 @@
 cc_library_static {
-    name: "VtsMediaC2V1_0CommonUtil",
+    name: "VtsHalMediaC2V1_0CommonUtil",
     defaults: [
         "VtsHalTargetTestDefaults",
         "libcodec2-hidl-client-defaults",
@@ -17,14 +17,14 @@
 }
 
 cc_defaults {
-    name: "VtsMediaC2V1_0Defaults",
+    name: "VtsHalMediaC2V1_0Defaults",
     defaults: [
         "VtsHalTargetTestDefaults",
         "libcodec2-hidl-client-defaults",
     ],
 
     static_libs: [
-        "VtsMediaC2V1_0CommonUtil",
+        "VtsHalMediaC2V1_0CommonUtil",
     ],
 
     shared_libs: [
diff --git a/media/codec2/hidl/1.0/vts/functional/common/README.md b/media/codec2/hidl/1.0/vts/functional/common/README.md
index 3deab10..50e8356 100644
--- a/media/codec2/hidl/1.0/vts/functional/common/README.md
+++ b/media/codec2/hidl/1.0/vts/functional/common/README.md
@@ -3,29 +3,29 @@
 #### master :
 Functionality of master is to enumerate all the Codec2 components available in C2 media service.
 
-usage: VtsHidlC2V1\_0TargetMasterTest -I default
+usage: VtsHalMediaC2V1\_0TargetMasterTest -I default
 
 #### component :
 Functionality of component test is to validate common functionality across all the Codec2 components available in C2 media service. For a standard C2 component, these tests are expected to pass.
 
-usage: VtsHidlC2V1\_0TargetComponentTest -I software -C <comp name>
-example: VtsHidlC2V1\_0TargetComponentTest -I software -C c2.android.vorbis.decoder
+usage: VtsHalMediaC2V1\_0TargetComponentTest -I software -C <comp name>
+example: VtsHalMediaC2V1\_0TargetComponentTest -I software -C c2.android.vorbis.decoder
 
 #### audio :
 Functionality of audio test is to validate audio specific functionality Codec2 components. The resource files for this test are taken from media/codec2/hidl/1.0/vts/functional/res. The path to these files on the device is required to be given for bitstream tests.
 
-usage: VtsHidlC2V1\_0TargetAudioDecTest -I default -C <comp name> -P /sdcard/res/
-usage: VtsHidlC2V1\_0TargetAudioEncTest -I software -C <comp name> -P /sdcard/res/
+usage: VtsHalMediaC2V1\_0TargetAudioDecTest -I default -C <comp name> -P /sdcard/media/
+usage: VtsHalMediaC2V1\_0TargetAudioEncTest -I software -C <comp name> -P /sdcard/media/
 
-example: VtsHidlC2V1\_0TargetAudioDecTest -I software -C c2.android.flac.decoder -P /sdcard/res/
-example: VtsHidlC2V1\_0TargetAudioEncTest -I software -C c2.android.opus.encoder -P /sdcard/res/
+example: VtsHalMediaC2V1\_0TargetAudioDecTest -I software -C c2.android.flac.decoder -P /sdcard/media/
+example: VtsHalMediaC2V1\_0TargetAudioEncTest -I software -C c2.android.opus.encoder -P /sdcard/media/
 
 #### video :
 Functionality of video test is to validate video specific functionality Codec2 components. The resource files for this test are taken from media/codec2/hidl/1.0/vts/functional/res. The path to these files on the device is required to be given for bitstream tests.
 
-usage: VtsHidlC2V1\_0TargetVideoDecTest -I default -C <comp name> -P /sdcard/res/
-usage: VtsHidlC2V1\_0TargetVideoEncTest -I software -C <comp name> -P /sdcard/res/
+usage: VtsHalMediaC2V1\_0TargetVideoDecTest -I default -C <comp name> -P /sdcard/media/
+usage: VtsHalMediaC2V1\_0TargetVideoEncTest -I software -C <comp name> -P /sdcard/media/
 
-example: VtsHidlC2V1\_0TargetVideoDecTest -I software -C c2.android.avc.decoder -P /sdcard/res/
-example: VtsHidlC2V1\_0TargetVideoEncTest -I software -C c2.android.vp9.encoder -P /sdcard/res/
+example: VtsHalMediaC2V1\_0TargetVideoDecTest -I software -C c2.android.avc.decoder -P /sdcard/media/
+example: VtsHalMediaC2V1\_0TargetVideoEncTest -I software -C c2.android.vp9.encoder -P /sdcard/media/
 
diff --git a/media/codec2/hidl/1.0/vts/functional/component/Android.bp b/media/codec2/hidl/1.0/vts/functional/component/Android.bp
index 4b913b6..7ec64ee 100644
--- a/media/codec2/hidl/1.0/vts/functional/component/Android.bp
+++ b/media/codec2/hidl/1.0/vts/functional/component/Android.bp
@@ -15,8 +15,8 @@
  */
 
 cc_test {
-    name: "VtsHidlC2V1_0TargetComponentTest",
-    defaults: ["VtsMediaC2V1_0Defaults"],
-    srcs: ["VtsHidlC2V1_0TargetComponentTest.cpp"],
+    name: "VtsHalMediaC2V1_0TargetComponentTest",
+    defaults: ["VtsHalMediaC2V1_0Defaults"],
+    srcs: ["VtsHalMediaC2V1_0TargetComponentTest.cpp"],
 }
 
diff --git a/media/codec2/hidl/1.0/vts/functional/component/VtsHidlC2V1_0TargetComponentTest.cpp b/media/codec2/hidl/1.0/vts/functional/component/VtsHalMediaC2V1_0TargetComponentTest.cpp
similarity index 95%
rename from media/codec2/hidl/1.0/vts/functional/component/VtsHidlC2V1_0TargetComponentTest.cpp
rename to media/codec2/hidl/1.0/vts/functional/component/VtsHalMediaC2V1_0TargetComponentTest.cpp
index 74548b5..9dc541c 100644
--- a/media/codec2/hidl/1.0/vts/functional/component/VtsHidlC2V1_0TargetComponentTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/component/VtsHalMediaC2V1_0TargetComponentTest.cpp
@@ -144,8 +144,7 @@
 
     // Queueing an empty WorkBundle
     std::list<std::unique_ptr<C2Work>> workList;
-    err = mComponent->queue(&workList);
-    ASSERT_EQ(err, C2_OK);
+    mComponent->queue(&workList);
 
     err = mComponent->reset();
     ASSERT_EQ(err, C2_OK);
@@ -183,33 +182,23 @@
 // Test Multiple Start Stop Reset Test
 TEST_F(Codec2ComponentHidlTest, MultipleStartStopReset) {
     ALOGV("Multiple Start Stop and Reset Test");
-    c2_status_t err = C2_OK;
 
     for (size_t i = 0; i < MAX_RETRY; i++) {
-        err = mComponent->start();
-        ASSERT_EQ(err, C2_OK);
-
-        err = mComponent->stop();
-        ASSERT_EQ(err, C2_OK);
+        mComponent->start();
+        mComponent->stop();
     }
 
-    err = mComponent->start();
-    ASSERT_EQ(err, C2_OK);
+    ASSERT_EQ(mComponent->start(), C2_OK);
 
     for (size_t i = 0; i < MAX_RETRY; i++) {
-        err = mComponent->reset();
-        ASSERT_EQ(err, C2_OK);
+        mComponent->reset();
     }
 
-    err = mComponent->start();
-    ASSERT_EQ(err, C2_OK);
-
-    err = mComponent->stop();
-    ASSERT_EQ(err, C2_OK);
+    ASSERT_EQ(mComponent->start(), C2_OK);
+    ASSERT_EQ(mComponent->stop(), C2_OK);
 
     // Second stop should return error
-    err = mComponent->stop();
-    ASSERT_NE(err, C2_OK);
+    ASSERT_NE(mComponent->stop(), C2_OK);
 }
 
 // Test Component Release API
@@ -233,8 +222,7 @@
     ASSERT_EQ(failures.size(), 0u);
 
     for (size_t i = 0; i < MAX_RETRY; i++) {
-        err = mComponent->release();
-        ASSERT_EQ(err, C2_OK);
+        mComponent->release();
     }
 }
 
@@ -332,14 +320,12 @@
     timeConsumed = getNowUs() - startTime;
     ALOGV("mComponent->queue() timeConsumed=%" PRId64 " us", timeConsumed);
     CHECK_TIMEOUT(timeConsumed, QUEUE_TIME_OUT, "queue()");
-    ASSERT_EQ(err, C2_OK);
 
     startTime = getNowUs();
     err = mComponent->flush(C2Component::FLUSH_COMPONENT, &workList);
     timeConsumed = getNowUs() - startTime;
     ALOGV("mComponent->flush() timeConsumed=%" PRId64 " us", timeConsumed);
     CHECK_TIMEOUT(timeConsumed, FLUSH_TIME_OUT, "flush()");
-    ASSERT_EQ(err, C2_OK);
 
     startTime = getNowUs();
     err = mComponent->stop();
diff --git a/media/codec2/hidl/1.0/vts/functional/master/Android.bp b/media/codec2/hidl/1.0/vts/functional/master/Android.bp
index e164d68..53e323e 100644
--- a/media/codec2/hidl/1.0/vts/functional/master/Android.bp
+++ b/media/codec2/hidl/1.0/vts/functional/master/Android.bp
@@ -15,8 +15,8 @@
  */
 
 cc_test {
-    name: "VtsHidlC2V1_0TargetMasterTest",
-    defaults: ["VtsMediaC2V1_0Defaults"],
-    srcs: ["VtsHidlC2V1_0TargetMasterTest.cpp"],
+    name: "VtsHalMediaC2V1_0TargetMasterTest",
+    defaults: ["VtsHalMediaC2V1_0Defaults"],
+    srcs: ["VtsHalMediaC2V1_0TargetMasterTest.cpp"],
 }
 
diff --git a/media/codec2/hidl/1.0/vts/functional/master/VtsHidlC2V1_0TargetMasterTest.cpp b/media/codec2/hidl/1.0/vts/functional/master/VtsHalMediaC2V1_0TargetMasterTest.cpp
similarity index 100%
rename from media/codec2/hidl/1.0/vts/functional/master/VtsHidlC2V1_0TargetMasterTest.cpp
rename to media/codec2/hidl/1.0/vts/functional/master/VtsHalMediaC2V1_0TargetMasterTest.cpp
diff --git a/media/codec2/hidl/1.0/vts/functional/video/Android.bp b/media/codec2/hidl/1.0/vts/functional/video/Android.bp
index 6e57ee7..be35b02 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/Android.bp
+++ b/media/codec2/hidl/1.0/vts/functional/video/Android.bp
@@ -15,14 +15,14 @@
  */
 
 cc_test {
-    name: "VtsHidlC2V1_0TargetVideoDecTest",
-    defaults: ["VtsMediaC2V1_0Defaults"],
-    srcs: ["VtsHidlC2V1_0TargetVideoDecTest.cpp"],
+    name: "VtsHalMediaC2V1_0TargetVideoDecTest",
+    defaults: ["VtsHalMediaC2V1_0Defaults"],
+    srcs: ["VtsHalMediaC2V1_0TargetVideoDecTest.cpp"],
 }
 
 cc_test {
-    name: "VtsHidlC2V1_0TargetVideoEncTest",
-    defaults: ["VtsMediaC2V1_0Defaults"],
-    srcs: ["VtsHidlC2V1_0TargetVideoEncTest.cpp"],
+    name: "VtsHalMediaC2V1_0TargetVideoEncTest",
+    defaults: ["VtsHalMediaC2V1_0Defaults"],
+    srcs: ["VtsHalMediaC2V1_0TargetVideoEncTest.cpp"],
 }
 
diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
similarity index 97%
rename from media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp
rename to media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
index 33fa848..5e28750 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
@@ -46,6 +46,10 @@
     explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block)
         : C2Buffer(
               {block->share(block->offset(), block->size(), ::C2Fence())}) {}
+
+    explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block, size_t size)
+        : C2Buffer(
+              {block->share(block->offset(), size, ::C2Fence())}) {}
 };
 
 static ComponentTestEnvironment* gEnv = nullptr;
@@ -120,6 +124,13 @@
         mTimestampUs = 0u;
         mTimestampDevTest = false;
         if (mCompName == unknown_comp) mDisableTest = true;
+
+        C2SecureModeTuning secureModeTuning{};
+        mComponent->query({ &secureModeTuning }, {}, C2_MAY_BLOCK, nullptr);
+        if (secureModeTuning.value == C2Config::SM_READ_PROTECTED) {
+            mDisableTest = true;
+        }
+
         if (mDisableTest) std::cout << "[   WARN   ] Test Disabled \n";
     }
 
@@ -371,11 +382,12 @@
         ASSERT_EQ(eleStream.gcount(), size);
 
         work->input.buffers.clear();
+        auto alignedSize = ALIGN(size, PAGE_SIZE);
         if (size) {
             std::shared_ptr<C2LinearBlock> block;
             ASSERT_EQ(C2_OK,
                     linearPool->fetchLinearBlock(
-                        size, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE},
+                        alignedSize, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE},
                         &block));
             ASSERT_TRUE(block);
 
@@ -385,13 +397,13 @@
                 fprintf(stderr, "C2LinearBlock::map() failed : %d", view.error());
                 break;
             }
-            ASSERT_EQ((size_t)size, view.capacity());
+            ASSERT_EQ((size_t)alignedSize, view.capacity());
             ASSERT_EQ(0u, view.offset());
-            ASSERT_EQ((size_t)size, view.size());
+            ASSERT_EQ((size_t)alignedSize, view.size());
 
             memcpy(view.base(), data, size);
 
-            work->input.buffers.emplace_back(new LinearBuffer(block));
+            work->input.buffers.emplace_back(new LinearBuffer(block, size));
             free(data);
         }
         work->worklets.clear();
diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
similarity index 89%
rename from media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp
rename to media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
index 6bcf840..c1f5a92 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
@@ -284,15 +284,16 @@
                    std::list<std::unique_ptr<C2Work>>& workQueue,
                    std::list<uint64_t>& flushedIndices,
                    std::shared_ptr<C2BlockPool>& graphicPool,
-                   std::ifstream& eleStream, uint32_t frameID,
-                   uint32_t nFrames, uint32_t nWidth, int32_t nHeight,
-                   bool flushed = false,bool signalEOS = true) {
+                   std::ifstream& eleStream, bool& disableTest,
+                   uint32_t frameID, uint32_t nFrames, uint32_t nWidth,
+                   int32_t nHeight, bool flushed = false, bool signalEOS = true) {
     typedef std::unique_lock<std::mutex> ULock;
 
     uint32_t maxRetry = 0;
     int bytesCount = nWidth * nHeight * 3 >> 1;
     int32_t timestampIncr = ENCODER_TIMESTAMP_INCREMENT;
     uint64_t timestamp = 0;
+    c2_status_t err = C2_OK;
     while (1) {
         if (nFrames == 0) break;
         uint32_t flags = 0;
@@ -333,16 +334,21 @@
             ASSERT_EQ(eleStream.gcount(), bytesCount);
         }
         std::shared_ptr<C2GraphicBlock> block;
-        ASSERT_EQ(
-            C2_OK,
-            graphicPool->fetchGraphicBlock(
+        err = graphicPool->fetchGraphicBlock(
                 nWidth, nHeight, HAL_PIXEL_FORMAT_YV12,
-                {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block));
+                {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
+        if (err != C2_OK) {
+            fprintf(stderr, "fetchGraphicBlock failed : %d\n", err);
+            disableTest = true;
+            break;
+        }
+
         ASSERT_TRUE(block);
         // Graphic View
         C2GraphicView view = block->map().get();
         if (view.error() != C2_OK) {
             fprintf(stderr, "C2GraphicBlock::map() failed : %d", view.error());
+            disableTest = true;
             break;
         }
 
@@ -420,8 +426,16 @@
     ASSERT_EQ(mComponent->start(), C2_OK);
     ASSERT_NO_FATAL_FAILURE(
         encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
-                      mFlushedIndices, mGraphicPool, eleStream,
+                      mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
                       0, ENC_NUM_FRAMES, nWidth, nHeight, false, signalEOS));
+    // mDisableTest will be set if buffer was not fetched properly.
+    // This may happen when resolution is not proper but config suceeded
+    // In this cases, we skip encoding the input stream
+    if (mDisableTest) {
+        std::cout << "[   WARN   ] Test Disabled \n";
+        ASSERT_EQ(mComponent->stop(), C2_OK);
+        return;
+    }
 
     // If EOS is not sent, sending empty input with EOS flag
     inputFrames = ENC_NUM_FRAMES;
@@ -531,8 +545,17 @@
     ALOGV("mURL : %s", mURL);
     ASSERT_NO_FATAL_FAILURE(
         encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
-                      mFlushedIndices, mGraphicPool, eleStream,
+                      mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
                       0, numFramesFlushed, nWidth, nHeight));
+    // mDisableTest will be set if buffer was not fetched properly.
+    // This may happen when resolution is not proper but config suceeded
+    // In this cases, we skip encoding the input stream
+    if (mDisableTest) {
+        std::cout << "[   WARN   ] Test Disabled \n";
+        ASSERT_EQ(mComponent->stop(), C2_OK);
+        return;
+    }
+
     std::list<std::unique_ptr<C2Work>> flushedWork;
     c2_status_t err =
         mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
@@ -561,10 +584,19 @@
     mFlushedIndices.clear();
     ASSERT_NO_FATAL_FAILURE(
         encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
-                      mFlushedIndices, mGraphicPool, eleStream,
+                      mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
                       numFramesFlushed, numFrames - numFramesFlushed,
                       nWidth, nHeight, true));
     eleStream.close();
+    // mDisableTest will be set if buffer was not fetched properly.
+    // This may happen when resolution is not proper but config suceeded
+    // In this cases, we skip encoding the input stream
+    if (mDisableTest) {
+        std::cout << "[   WARN   ] Test Disabled \n";
+        ASSERT_EQ(mComponent->stop(), C2_OK);
+        return;
+    }
+
     err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
     ASSERT_EQ(err, C2_OK);
     ASSERT_NO_FATAL_FAILURE(
@@ -607,19 +639,19 @@
 
     ASSERT_NO_FATAL_FAILURE(
         encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
-                      mFlushedIndices, mGraphicPool, eleStream,
+                      mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
                       0, 1, nWidth, nHeight, false, false));
 
     // Feed larger input buffer.
     ASSERT_NO_FATAL_FAILURE(
         encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
-                      mFlushedIndices, mGraphicPool, eleStream,
+                      mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
                       1, 1, nWidth*2, nHeight*2, false, false));
 
     // Feed smaller input buffer.
     ASSERT_NO_FATAL_FAILURE(
         encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
-                      mFlushedIndices, mGraphicPool, eleStream,
+                      mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
                       2, 1, nWidth/2, nHeight/2, false, true));
 
     // blocking call to ensures application to Wait till all the inputs are
@@ -629,15 +661,13 @@
         waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
 
     if (mFramesReceived != 3) {
-        ALOGE("Input buffer count and Output buffer count mismatch");
-        ALOGE("framesReceived : %d inputFrames : 3", mFramesReceived);
-        ASSERT_TRUE(false);
+        std::cout << "[   WARN   ] Component didn't receive all buffers back \n";
+        ALOGW("framesReceived : %d inputFrames : 3", mFramesReceived);
     }
 
     if (mFailedWorkReceived == 0) {
-        ALOGE("Expected failed frame count mismatch");
-        ALOGE("failedFramesReceived : %d", mFailedWorkReceived);
-        ASSERT_TRUE(false);
+        std::cout << "[   WARN   ] Expected failed frame count mismatch \n";
+        ALOGW("failedFramesReceived : %d", mFailedWorkReceived);
     }
 
     ASSERT_EQ(mComponent->stop(), C2_OK);
@@ -665,8 +695,17 @@
 
     ASSERT_NO_FATAL_FAILURE(
         encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
-                      mFlushedIndices, mGraphicPool, eleStream, 0,
-                      MAX_INPUT_BUFFERS, nWidth, nHeight));
+                      mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
+                      0, MAX_INPUT_BUFFERS, nWidth, nHeight, false, true));
+
+    // mDisableTest will be set if buffer was not fetched properly.
+    // This may happen when resolution is not proper but config suceeded
+    // In this cases, we skip encoding the input stream
+    if (mDisableTest) {
+        std::cout << "[   WARN   ] Test Disabled \n";
+        ASSERT_EQ(mComponent->stop(), C2_OK);
+        return;
+    }
 
     ALOGD("Waiting for input consumption");
     ASSERT_NO_FATAL_FAILURE(
@@ -676,6 +715,7 @@
     ASSERT_EQ(mComponent->stop(), C2_OK);
     ASSERT_EQ(mComponent->reset(), C2_OK);
 }
+
 INSTANTIATE_TEST_CASE_P(NonStdSizes, Codec2VideoEncResolutionTest, ::testing::Values(
     std::make_pair(52, 18),
     std::make_pair(365, 365),
diff --git a/media/codec2/hidl/1.0/vts/functional/video/media_c2_video_hidl_test_common.h b/media/codec2/hidl/1.0/vts/functional/video/media_c2_video_hidl_test_common.h
index dd45557..e37ca38 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/media_c2_video_hidl_test_common.h
+++ b/media/codec2/hidl/1.0/vts/functional/video/media_c2_video_hidl_test_common.h
@@ -23,6 +23,8 @@
 #define ENC_DEFAULT_FRAME_HEIGHT 288
 #define MAX_ITERATIONS 128
 
+#define ALIGN(_sz, _align) ((_sz + (_align - 1)) & ~(_align - 1))
+
 /*
  * Common video utils
  */
diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp
index 6aca4a3..5ed54f1 100644
--- a/media/codec2/hidl/client/client.cpp
+++ b/media/codec2/hidl/client/client.cpp
@@ -959,9 +959,9 @@
 
 std::shared_ptr<Codec2Client::InputSurface> Codec2Client::CreateInputSurface(
         char const* serviceName) {
-    uint32_t inputSurfaceSetting = ::android::base::GetUintProperty(
-            "debug.stagefright.c2inputsurface", uint32_t(0));
-    if (inputSurfaceSetting == 0) {
+    int32_t inputSurfaceSetting = ::android::base::GetIntProperty(
+            "debug.stagefright.c2inputsurface", int32_t(0));
+    if (inputSurfaceSetting <= 0) {
         return nullptr;
     }
     size_t index = GetServiceNames().size();
@@ -1080,15 +1080,7 @@
 void Codec2Client::Component::handleOnWorkDone(
         const std::list<std::unique_ptr<C2Work>> &workItems) {
     // Output bufferqueue-based blocks' lifetime management
-    mOutputBufferQueueMutex.lock();
-    sp<IGraphicBufferProducer> igbp = mOutputIgbp;
-    uint64_t bqId = mOutputBqId;
-    uint32_t generation = mOutputGeneration;
-    mOutputBufferQueueMutex.unlock();
-
-    if (igbp) {
-        holdBufferQueueBlocks(workItems, igbp, bqId, generation);
-    }
+    mOutputBufferQueue.holdBufferQueueBlocks(workItems);
 }
 
 c2_status_t Codec2Client::Component::queue(
@@ -1151,15 +1143,7 @@
     }
 
     // Output bufferqueue-based blocks' lifetime management
-    mOutputBufferQueueMutex.lock();
-    sp<IGraphicBufferProducer> igbp = mOutputIgbp;
-    uint64_t bqId = mOutputBqId;
-    uint32_t generation = mOutputGeneration;
-    mOutputBufferQueueMutex.unlock();
-
-    if (igbp) {
-        holdBufferQueueBlocks(*flushedWork, igbp, bqId, generation);
-    }
+    mOutputBufferQueue.holdBufferQueueBlocks(*flushedWork);
 
     return status;
 }
@@ -1239,15 +1223,31 @@
         C2BlockPool::local_id_t blockPoolId,
         const sp<IGraphicBufferProducer>& surface,
         uint32_t generation) {
-    sp<HGraphicBufferProducer2> igbp =
-            surface->getHalInterface<HGraphicBufferProducer2>();
+    uint64_t bqId = 0;
+    sp<IGraphicBufferProducer> nullIgbp;
+    sp<HGraphicBufferProducer2> nullHgbp;
 
-    if (!igbp) {
+    sp<HGraphicBufferProducer2> igbp = surface ?
+            surface->getHalInterface<HGraphicBufferProducer2>() : nullHgbp;
+    if (surface && !igbp) {
         igbp = new B2HGraphicBufferProducer2(surface);
     }
 
+    if (!surface) {
+        mOutputBufferQueue.configure(nullIgbp, generation, 0);
+    } else if (surface->getUniqueId(&bqId) != OK) {
+        LOG(ERROR) << "setOutputSurface -- "
+                   "cannot obtain bufferqueue id.";
+        bqId = 0;
+        mOutputBufferQueue.configure(nullIgbp, generation, 0);
+    } else {
+        mOutputBufferQueue.configure(surface, generation, bqId);
+    }
+    ALOGD("generation remote change %u", generation);
+
     Return<Status> transStatus = mBase->setOutputSurface(
-            static_cast<uint64_t>(blockPoolId), igbp);
+            static_cast<uint64_t>(blockPoolId),
+            bqId == 0 ? nullHgbp : igbp);
     if (!transStatus.isOk()) {
         LOG(ERROR) << "setOutputSurface -- transaction failed.";
         return C2_TRANSACTION_FAILED;
@@ -1256,18 +1256,6 @@
             static_cast<c2_status_t>(static_cast<Status>(transStatus));
     if (status != C2_OK) {
         LOG(DEBUG) << "setOutputSurface -- call failed: " << status << ".";
-    } else {
-        std::lock_guard<std::mutex> lock(mOutputBufferQueueMutex);
-        if (mOutputIgbp != surface) {
-            mOutputIgbp = surface;
-            if (!surface) {
-                mOutputBqId = 0;
-            } else if (surface->getUniqueId(&mOutputBqId) != OK) {
-                LOG(ERROR) << "setOutputSurface -- "
-                              "cannot obtain bufferqueue id.";
-            }
-        }
-        mOutputGeneration = generation;
     }
     return status;
 }
@@ -1276,74 +1264,7 @@
         const C2ConstGraphicBlock& block,
         const QueueBufferInput& input,
         QueueBufferOutput* output) {
-    uint32_t generation;
-    uint64_t bqId;
-    int32_t bqSlot;
-    if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot) ||
-            bqId == 0) {
-        // Block not from bufferqueue -- it must be attached before queuing.
-
-        mOutputBufferQueueMutex.lock();
-        sp<IGraphicBufferProducer> outputIgbp = mOutputIgbp;
-        uint32_t outputGeneration = mOutputGeneration;
-        mOutputBufferQueueMutex.unlock();
-
-        status_t status = attachToBufferQueue(block,
-                                               outputIgbp,
-                                               outputGeneration,
-                                               &bqSlot);
-        if (status != OK) {
-            LOG(WARNING) << "queueToOutputSurface -- attaching failed.";
-            return INVALID_OPERATION;
-        }
-
-        status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
-                                         input, output);
-        if (status != OK) {
-            LOG(ERROR) << "queueToOutputSurface -- queueBuffer() failed "
-                          "on non-bufferqueue-based block. "
-                          "Error = " << status << ".";
-            return status;
-        }
-        return OK;
-    }
-
-    mOutputBufferQueueMutex.lock();
-    sp<IGraphicBufferProducer> outputIgbp = mOutputIgbp;
-    uint64_t outputBqId = mOutputBqId;
-    uint32_t outputGeneration = mOutputGeneration;
-    mOutputBufferQueueMutex.unlock();
-
-    if (!outputIgbp) {
-        LOG(VERBOSE) << "queueToOutputSurface -- output surface is null.";
-        return NO_INIT;
-    }
-
-    if (bqId != outputBqId || generation != outputGeneration) {
-        if (!holdBufferQueueBlock(block, mOutputIgbp, mOutputBqId, mOutputGeneration)) {
-            LOG(ERROR) << "queueToOutputSurface -- migration failed.";
-            return DEAD_OBJECT;
-        }
-        if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot)) {
-            LOG(ERROR) << "queueToOutputSurface -- corrupted bufferqueue assignment.";
-            return UNKNOWN_ERROR;
-        }
-    }
-
-    status_t status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
-                                              input, output);
-    if (status != OK) {
-        LOG(DEBUG) << "queueToOutputSurface -- queueBuffer() failed "
-                      "on bufferqueue-based block. "
-                      "Error = " << status << ".";
-        return status;
-    }
-    if (!yieldBufferQueueBlock(block)) {
-        LOG(DEBUG) << "queueToOutputSurface -- cannot yield "
-                      "bufferqueue-based block to the bufferqueue.";
-        return UNKNOWN_ERROR;
-    }
-    return OK;
+    return mOutputBufferQueue.outputBuffer(block, input, output);
 }
 
 c2_status_t Codec2Client::Component::connectToInputSurface(
diff --git a/media/codec2/hidl/client/include/codec2/hidl/client.h b/media/codec2/hidl/client/include/codec2/hidl/client.h
index 03db515..b8a7fb5 100644
--- a/media/codec2/hidl/client/include/codec2/hidl/client.h
+++ b/media/codec2/hidl/client/include/codec2/hidl/client.h
@@ -369,10 +369,8 @@
     ::android::hardware::media::c2::V1_0::utils::DefaultBufferPoolSender
             mBufferPoolSender;
 
-    std::mutex mOutputBufferQueueMutex;
-    sp<IGraphicBufferProducer> mOutputIgbp;
-    uint64_t mOutputBqId;
-    uint32_t mOutputGeneration;
+    ::android::hardware::media::c2::V1_0::utils::OutputBufferQueue
+            mOutputBufferQueue;
 
     static c2_status_t setDeathListener(
             const std::shared_ptr<Component>& component,
diff --git a/media/codec2/sfplugin/Android.bp b/media/codec2/sfplugin/Android.bp
index 8ae80ee..9c84c71 100644
--- a/media/codec2/sfplugin/Android.bp
+++ b/media/codec2/sfplugin/Android.bp
@@ -9,6 +9,7 @@
         "CCodecConfig.cpp",
         "Codec2Buffer.cpp",
         "Codec2InfoBuilder.cpp",
+        "Omx2IGraphicBufferSource.cpp",
         "PipelineWatcher.cpp",
         "ReflectedParamUpdater.cpp",
         "SkipCutBuffer.cpp",
@@ -41,8 +42,10 @@
         "libmedia",
         "libmedia_omx",
         "libsfplugin_ccodec_utils",
+        "libstagefright_bufferqueue_helper",
         "libstagefright_codecbase",
         "libstagefright_foundation",
+        "libstagefright_omx",
         "libstagefright_omx_utils",
         "libstagefright_xmlparser",
         "libui",
diff --git a/media/codec2/sfplugin/C2OMXNode.cpp b/media/codec2/sfplugin/C2OMXNode.cpp
index 3a93c2a..78d221e 100644
--- a/media/codec2/sfplugin/C2OMXNode.cpp
+++ b/media/codec2/sfplugin/C2OMXNode.cpp
@@ -36,6 +36,7 @@
 #include <media/stagefright/MediaErrors.h>
 #include <ui/Fence.h>
 #include <ui/GraphicBuffer.h>
+#include <utils/Thread.h>
 
 #include "C2OMXNode.h"
 
@@ -50,16 +51,128 @@
 
 }  // namespace
 
+class C2OMXNode::QueueThread : public Thread {
+public:
+    QueueThread() : Thread(false) {}
+    ~QueueThread() override = default;
+    void queue(
+            const std::shared_ptr<Codec2Client::Component> &comp,
+            int fenceFd,
+            std::unique_ptr<C2Work> &&work,
+            android::base::unique_fd &&fd0,
+            android::base::unique_fd &&fd1) {
+        Mutexed<Jobs>::Locked jobs(mJobs);
+        auto it = jobs->queues.try_emplace(comp, comp, systemTime()).first;
+        it->second.workList.emplace_back(
+                std::move(work), fenceFd, std::move(fd0), std::move(fd1));
+        jobs->cond.broadcast();
+    }
+
+protected:
+    bool threadLoop() override {
+        constexpr nsecs_t kIntervalNs = nsecs_t(10) * 1000 * 1000;  // 10ms
+        constexpr nsecs_t kWaitNs = kIntervalNs * 2;
+        for (int i = 0; i < 2; ++i) {
+            Mutexed<Jobs>::Locked jobs(mJobs);
+            nsecs_t nowNs = systemTime();
+            bool queued = false;
+            for (auto it = jobs->queues.begin(); it != jobs->queues.end(); ) {
+                Queue &queue = it->second;
+                if (queue.workList.empty()
+                        || nowNs - queue.lastQueuedTimestampNs < kIntervalNs) {
+                    ++it;
+                    continue;
+                }
+                std::shared_ptr<Codec2Client::Component> comp = queue.component.lock();
+                if (!comp) {
+                    it = jobs->queues.erase(it);
+                    continue;
+                }
+                std::list<std::unique_ptr<C2Work>> items;
+                std::vector<int> fenceFds;
+                std::vector<android::base::unique_fd> uniqueFds;
+                while (!queue.workList.empty()) {
+                    items.push_back(std::move(queue.workList.front().work));
+                    fenceFds.push_back(queue.workList.front().fenceFd);
+                    uniqueFds.push_back(std::move(queue.workList.front().fd0));
+                    uniqueFds.push_back(std::move(queue.workList.front().fd1));
+                    queue.workList.pop_front();
+                }
+
+                jobs.unlock();
+                for (int fenceFd : fenceFds) {
+                    sp<Fence> fence(new Fence(fenceFd));
+                    fence->waitForever(LOG_TAG);
+                }
+                comp->queue(&items);
+                for (android::base::unique_fd &ufd : uniqueFds) {
+                    (void)ufd.release();
+                }
+                jobs.lock();
+
+                it = jobs->queues.upper_bound(comp);
+                queued = true;
+            }
+            if (queued) {
+                return true;
+            }
+            if (i == 0) {
+                jobs.waitForConditionRelative(jobs->cond, kWaitNs);
+            }
+        }
+        return true;
+    }
+
+private:
+    struct WorkFence {
+        WorkFence(std::unique_ptr<C2Work> &&w, int fd) : work(std::move(w)), fenceFd(fd) {}
+
+        WorkFence(
+                std::unique_ptr<C2Work> &&w,
+                int fd,
+                android::base::unique_fd &&uniqueFd0,
+                android::base::unique_fd &&uniqueFd1)
+            : work(std::move(w)),
+              fenceFd(fd),
+              fd0(std::move(uniqueFd0)),
+              fd1(std::move(uniqueFd1)) {}
+
+        std::unique_ptr<C2Work> work;
+        int fenceFd;
+        android::base::unique_fd fd0;
+        android::base::unique_fd fd1;
+    };
+    struct Queue {
+        Queue(const std::shared_ptr<Codec2Client::Component> &comp, nsecs_t timestamp)
+            : component(comp), lastQueuedTimestampNs(timestamp) {}
+        Queue(const Queue &) = delete;
+        Queue &operator =(const Queue &) = delete;
+
+        std::weak_ptr<Codec2Client::Component> component;
+        std::list<WorkFence> workList;
+        nsecs_t lastQueuedTimestampNs;
+    };
+    struct Jobs {
+        std::map<std::weak_ptr<Codec2Client::Component>,
+                 Queue,
+                 std::owner_less<std::weak_ptr<Codec2Client::Component>>> queues;
+        Condition cond;
+    };
+    Mutexed<Jobs> mJobs;
+};
+
 C2OMXNode::C2OMXNode(const std::shared_ptr<Codec2Client::Component> &comp)
     : mComp(comp), mFrameIndex(0), mWidth(0), mHeight(0), mUsage(0),
-      mAdjustTimestampGapUs(0), mFirstInputFrame(true) {
+      mAdjustTimestampGapUs(0), mFirstInputFrame(true),
+      mQueueThread(new QueueThread) {
     android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ALWAYS);
+    mQueueThread->run("C2OMXNode", PRIORITY_AUDIO);
 }
 
 status_t C2OMXNode::freeNode() {
     mComp.reset();
     android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ONCE);
-    return OK;
+    return mQueueThread->requestExitAndWait();
 }
 
 status_t C2OMXNode::sendCommand(OMX_COMMANDTYPE cmd, OMX_S32 param) {
@@ -216,11 +329,6 @@
 status_t C2OMXNode::emptyBuffer(
         buffer_id buffer, const OMXBuffer &omxBuf,
         OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
-    // TODO: better fence handling
-    if (fenceFd >= 0) {
-        sp<Fence> fence = new Fence(fenceFd);
-        fence->waitForever(LOG_TAG);
-    }
     std::shared_ptr<Codec2Client::Component> comp = mComp.lock();
     if (!comp) {
         return NO_INIT;
@@ -299,22 +407,8 @@
     }
     work->worklets.clear();
     work->worklets.emplace_back(new C2Worklet);
-    std::list<std::unique_ptr<C2Work>> items;
-    uint64_t index = work->input.ordinal.frameIndex.peeku();
-    items.push_back(std::move(work));
-
-    c2_status_t err = comp->queue(&items);
-    if (err != C2_OK) {
-        (void)fd0.release();
-        (void)fd1.release();
-        return UNKNOWN_ERROR;
-    }
-
-    mBufferIdsInUse.lock()->emplace(index, buffer);
-
-    // release ownership of the fds
-    (void)fd0.release();
-    (void)fd1.release();
+    mBufferIdsInUse.lock()->emplace(work->input.ordinal.frameIndex.peeku(), buffer);
+    mQueueThread->queue(comp, fenceFd, std::move(work), std::move(fd0), std::move(fd1));
 
     return OK;
 }
diff --git a/media/codec2/sfplugin/C2OMXNode.h b/media/codec2/sfplugin/C2OMXNode.h
index 3ca6c0a..1717c96 100644
--- a/media/codec2/sfplugin/C2OMXNode.h
+++ b/media/codec2/sfplugin/C2OMXNode.h
@@ -113,6 +113,9 @@
     c2_cntr64_t mPrevCodecTimestamp; // adjusted (codec) timestamp for previous frame
 
     Mutexed<std::map<uint64_t, buffer_id>> mBufferIdsInUse;
+
+    class QueueThread;
+    sp<QueueThread> mQueueThread;
 };
 
 }  // namespace android
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index aa7189c..8223273 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -45,6 +45,7 @@
 #include "CCodec.h"
 #include "CCodecBufferChannel.h"
 #include "InputSurfaceWrapper.h"
+#include "Omx2IGraphicBufferSource.h"
 
 extern "C" android::PersistentSurface *CreateInputSurface();
 
@@ -374,7 +375,11 @@
 
         // consumer usage is queried earlier.
 
-        ALOGD("ISConfig%s", status.str().c_str());
+        if (status.str().empty()) {
+            ALOGD("ISConfig not changed");
+        } else {
+            ALOGD("ISConfig%s", status.str().c_str());
+        }
         return err;
     }
 
@@ -1067,6 +1072,7 @@
     OmxStatus s;
     android::sp<HGraphicBufferProducer> gbp;
     android::sp<HGraphicBufferSource> gbs;
+
     using ::android::hardware::Return;
     Return<void> transStatus = omx->createInputSurface(
             [&s, &gbp, &gbs](
@@ -1852,15 +1858,30 @@
 
 // Create Codec 2.0 input surface
 extern "C" android::PersistentSurface *CreateInputSurface() {
+    using namespace android;
     // Attempt to create a Codec2's input surface.
-    std::shared_ptr<android::Codec2Client::InputSurface> inputSurface =
-            android::Codec2Client::CreateInputSurface();
+    std::shared_ptr<Codec2Client::InputSurface> inputSurface =
+            Codec2Client::CreateInputSurface();
     if (!inputSurface) {
-        return nullptr;
+        if (property_get_int32("debug.stagefright.c2inputsurface", 0) == -1) {
+            sp<IGraphicBufferProducer> gbp;
+            sp<OmxGraphicBufferSource> gbs = new OmxGraphicBufferSource();
+            status_t err = gbs->initCheck();
+            if (err != OK) {
+                ALOGE("Failed to create persistent input surface: error %d", err);
+                return nullptr;
+            }
+            return new PersistentSurface(
+                    gbs->getIGraphicBufferProducer(),
+                    sp<IGraphicBufferSource>(
+                        new Omx2IGraphicBufferSource(gbs)));
+        } else {
+            return nullptr;
+        }
     }
-    return new android::PersistentSurface(
+    return new PersistentSurface(
             inputSurface->getGraphicBufferProducer(),
-            static_cast<android::sp<android::hidl::base::V1_0::IBase>>(
+            static_cast<sp<android::hidl::base::V1_0::IBase>>(
             inputSurface->getHalInterface()));
 }
 
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index eb20b20..1548a89 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -220,7 +220,6 @@
         const std::shared_ptr<CCodecCallback> &callback)
     : mHeapSeqNum(-1),
       mCCodecCallback(callback),
-      mDelay(0),
       mFrameIndex(0u),
       mFirstValidFrameIndex(0u),
       mMetaMode(MODE_NONE),
@@ -814,7 +813,6 @@
 
     size_t numInputSlots = inputDelayValue + pipelineDelayValue + kSmoothnessFactor;
     size_t numOutputSlots = outputDelayValue + kSmoothnessFactor;
-    mDelay = inputDelayValue + pipelineDelayValue + outputDelayValue;
 
     // TODO: get this from input format
     bool secure = mComponent->getName().find(".secure") != std::string::npos;
@@ -888,6 +886,8 @@
 
         bool forceArrayMode = false;
         Mutexed<Input>::Locked input(mInput);
+        input->inputDelay = inputDelayValue;
+        input->pipelineDelay = pipelineDelayValue;
         input->numSlots = numInputSlots;
         input->extraBuffers.flush();
         input->numExtraSlots = 0u;
@@ -896,6 +896,9 @@
                 input->buffers.reset(new DummyInputBuffers(mName));
             } else if (mMetaMode == MODE_ANW) {
                 input->buffers.reset(new GraphicMetadataInputBuffers(mName));
+                // This is to ensure buffers do not get released prematurely.
+                // TODO: handle this without going into array mode
+                forceArrayMode = true;
             } else {
                 input->buffers.reset(new GraphicInputBuffers(numInputSlots, mName));
             }
@@ -1054,6 +1057,7 @@
         }
 
         Mutexed<Output>::Locked output(mOutput);
+        output->outputDelay = outputDelayValue;
         output->numSlots = numOutputSlots;
         if (graphic) {
             if (outputSurface) {
@@ -1075,8 +1079,7 @@
                     outputGeneration);
         }
 
-        if (oStreamFormat.value == C2BufferData::LINEAR
-                && mComponentName.find("c2.qti.") == std::string::npos) {
+        if (oStreamFormat.value == C2BufferData::LINEAR) {
             // WORKAROUND: if we're using early CSD workaround we convert to
             //             array mode, to appease apps assuming the output
             //             buffers to be of the same size.
@@ -1128,8 +1131,9 @@
     }
 
     C2StreamBufferTypeSetting::output oStreamFormat(0u);
-    c2_status_t err = mComponent->query({ &oStreamFormat }, {}, C2_DONT_BLOCK, nullptr);
-    if (err != C2_OK) {
+    C2PrependHeaderModeSetting prepend(PREPEND_HEADER_TO_NONE);
+    c2_status_t err = mComponent->query({ &oStreamFormat, &prepend }, {}, C2_DONT_BLOCK, nullptr);
+    if (err != C2_OK && err != C2_BAD_INDEX) {
         return UNKNOWN_ERROR;
     }
     size_t numInputSlots = mInput.lock()->numSlots;
@@ -1169,7 +1173,7 @@
                             mName, buffer->capacity(), config->size());
                 }
             } else if (oStreamFormat.value == C2BufferData::LINEAR && i == 0
-                    && mComponentName.find("c2.qti.") == std::string::npos) {
+                        && (!prepend || prepend.value == PREPEND_HEADER_TO_NONE)) {
                 // WORKAROUND: Some apps expect CSD available without queueing
                 //             any input. Queue an empty buffer to get the CSD.
                 buffer->setRange(0, 0);
@@ -1377,25 +1381,35 @@
                         (void)mPipelineWatcher.lock()->outputDelay(outputDelay.value);
 
                         bool outputBuffersChanged = false;
-                        Mutexed<Output>::Locked output(mOutput);
-                        output->outputDelay = outputDelay.value;
-                        size_t numOutputSlots = outputDelay.value + kSmoothnessFactor;
-                        if (output->numSlots < numOutputSlots) {
-                            output->numSlots = numOutputSlots;
-                            if (output->buffers->isArrayMode()) {
-                                OutputBuffersArray *array =
-                                    (OutputBuffersArray *)output->buffers.get();
-                                ALOGV("[%s] onWorkDone: growing output buffer array to %zu",
-                                      mName, numOutputSlots);
-                                array->grow(numOutputSlots);
-                                outputBuffersChanged = true;
+                        size_t numOutputSlots = 0;
+                        {
+                            Mutexed<Output>::Locked output(mOutput);
+                            output->outputDelay = outputDelay.value;
+                            numOutputSlots = outputDelay.value + kSmoothnessFactor;
+                            if (output->numSlots < numOutputSlots) {
+                                output->numSlots = numOutputSlots;
+                                if (output->buffers->isArrayMode()) {
+                                    OutputBuffersArray *array =
+                                        (OutputBuffersArray *)output->buffers.get();
+                                    ALOGV("[%s] onWorkDone: growing output buffer array to %zu",
+                                          mName, numOutputSlots);
+                                    array->grow(numOutputSlots);
+                                    outputBuffersChanged = true;
+                                }
                             }
+                            numOutputSlots = output->numSlots;
                         }
-                        output.unlock();
 
                         if (outputBuffersChanged) {
                             mCCodecCallback->onOutputBuffersChanged();
                         }
+
+                        uint32_t depth = mReorderStash.lock()->depth();
+                        Mutexed<OutputSurface>::Locked output(mOutputSurface);
+                        output->maxDequeueBuffers = numOutputSlots + depth + kRenderingDepth;
+                        if (output->surface) {
+                            output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers);
+                        }
                     }
                 }
                 break;
@@ -1581,6 +1595,7 @@
     if (newSurface) {
         newSurface->setScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
         newSurface->setDequeueTimeout(kDequeueTimeoutNs);
+        newSurface->setMaxDequeuedBufferCount(mOutputSurface.lock()->maxDequeueBuffers);
         producer = newSurface->getIGraphicBufferProducer();
         producer->setGenerationNumber(generation);
     } else {
@@ -1608,7 +1623,6 @@
 
     {
         Mutexed<OutputSurface>::Locked output(mOutputSurface);
-        newSurface->setMaxDequeuedBufferCount(output->maxDequeueBuffers);
         output->surface = newSurface;
         output->generation = generation;
     }
@@ -1620,7 +1634,12 @@
     // When client pushed EOS, we want all the work to be done quickly.
     // Otherwise, component may have stalled work due to input starvation up to
     // the sum of the delay in the pipeline.
-    size_t n = mInputMetEos ? 0 : mDelay;
+    size_t n = 0;
+    if (!mInputMetEos) {
+        size_t outputDelay = mOutput.lock()->outputDelay;
+        Mutexed<Input>::Locked input(mInput);
+        n = input->inputDelay + input->pipelineDelay + outputDelay;
+    }
     return mPipelineWatcher.lock()->elapsed(PipelineWatcher::Clock::now(), n);
 }
 
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index ae57678..ee3455d 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -228,8 +228,6 @@
     QueueSync mQueueSync;
     std::vector<std::unique_ptr<C2Param>> mParamsToBeSet;
 
-    size_t mDelay;
-
     struct Input {
         Input();
 
@@ -306,6 +304,7 @@
                 const C2WorkOrdinalStruct &ordinal);
         void defer(const Entry &entry);
         bool hasPending() const;
+        uint32_t depth() const { return mDepth; }
 
     private:
         std::list<Entry> mPending;
diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp
index 5ebd5bd..26c702d 100644
--- a/media/codec2/sfplugin/CCodecBuffers.cpp
+++ b/media/codec2/sfplugin/CCodecBuffers.cpp
@@ -439,6 +439,10 @@
             });
 }
 
+size_t BuffersArrayImpl::arraySize() const {
+    return mBuffers.size();
+}
+
 // InputBuffersArray
 
 void InputBuffersArray::initialize(
@@ -883,11 +887,24 @@
             mAlloc = [format = mFormat, size] {
                 return new LocalLinearBuffer(format, new ABuffer(size));
             };
+            ALOGD("[%s] reallocating with linear buffer of size %u", mName, size);
             break;
         }
 
-        // TODO: add support
-        case C2BufferData::GRAPHIC:         [[fallthrough]];
+        case C2BufferData::GRAPHIC: {
+            // This is only called for RawGraphicOutputBuffers.
+            mAlloc = [format = mFormat,
+                      lbp = LocalBufferPool::Create(kMaxLinearBufferSize * mImpl.arraySize())] {
+                return ConstGraphicBlockBuffer::AllocateEmpty(
+                        format,
+                        [lbp](size_t capacity) {
+                            return lbp->newBuffer(capacity);
+                        });
+            };
+            ALOGD("[%s] reallocating with graphic buffer: format = %s",
+                  mName, mFormat->debugString().c_str());
+            break;
+        }
 
         case C2BufferData::INVALID:         [[fallthrough]];
         case C2BufferData::LINEAR_CHUNKS:   [[fallthrough]];
diff --git a/media/codec2/sfplugin/CCodecBuffers.h b/media/codec2/sfplugin/CCodecBuffers.h
index e4f2809..2cb6b81 100644
--- a/media/codec2/sfplugin/CCodecBuffers.h
+++ b/media/codec2/sfplugin/CCodecBuffers.h
@@ -478,6 +478,11 @@
      */
     size_t numClientBuffers() const;
 
+    /**
+     * Return the size of the array.
+     */
+    size_t arraySize() const;
+
 private:
     std::string mImplName; ///< name for debugging
     const char *mName; ///< C-string version of name
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index 1cfdc19..5adcd94 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -235,7 +235,10 @@
     const std::vector<ConfigMapper> &getConfigMappersForSdkKey(std::string key) const {
         auto it = mConfigMappers.find(key);
         if (it == mConfigMappers.end()) {
-            ALOGD("no c2 equivalents for %s", key.c_str());
+            if (mComplained.count(key) == 0) {
+                ALOGD("no c2 equivalents for %s", key.c_str());
+                mComplained.insert(key);
+            }
             return NO_MAPPERS;
         }
         ALOGV("found %zu eqs for %s", it->second.size(), key.c_str());
@@ -304,6 +307,7 @@
 
 private:
     std::map<SdkKey, std::vector<ConfigMapper>> mConfigMappers;
+    mutable std::set<std::string> mComplained;
 };
 
 const std::vector<ConfigMapper> StandardParams::NO_MAPPERS;
@@ -508,7 +512,8 @@
                .limitTo(D::ENCODER & D::VIDEO));
     // convert to timestamp base
     add(ConfigMapper(KEY_I_FRAME_INTERVAL, C2_PARAMKEY_SYNC_FRAME_INTERVAL, "value")
-        .withMappers([](C2Value v) -> C2Value {
+        .limitTo(D::VIDEO & D::ENCODER & D::CONFIG)
+        .withMapper([](C2Value v) -> C2Value {
             // convert from i32 to float
             int32_t i32Value;
             float fpValue;
@@ -518,12 +523,6 @@
                 return int64_t(c2_min(1000000 * fpValue + 0.5, (double)INT64_MAX));
             }
             return C2Value();
-        }, [](C2Value v) -> C2Value {
-            int64_t i64;
-            if (v.get(&i64)) {
-                return float(i64) / 1000000;
-            }
-            return C2Value();
         }));
     // remove when codecs switch to proper coding.gop (add support for calculating gop)
     deprecated(ConfigMapper("i-frame-period", "coding.gop", "intra-period")
@@ -1033,7 +1032,25 @@
     }
 
     ReflectedParamUpdater::Dict reflected = mParamUpdater->getParams(paramPointers);
-    ALOGD("c2 config is %s", reflected.debugString().c_str());
+    std::string config = reflected.debugString();
+    std::set<std::string> configLines;
+    std::string diff;
+    for (size_t start = 0; start != std::string::npos; ) {
+        size_t end = config.find('\n', start);
+        size_t count = (end == std::string::npos)
+                ? std::string::npos
+                : end - start + 1;
+        std::string line = config.substr(start, count);
+        configLines.insert(line);
+        if (mLastConfig.count(line) == 0) {
+            diff.append(line);
+        }
+        start = (end == std::string::npos) ? std::string::npos : end + 1;
+    }
+    if (!diff.empty()) {
+        ALOGD("c2 config diff is %s", diff.c_str());
+    }
+    mLastConfig.swap(configLines);
 
     bool changed = false;
     if (domain & mInputDomain) {
diff --git a/media/codec2/sfplugin/CCodecConfig.h b/media/codec2/sfplugin/CCodecConfig.h
index 3bafe3f..a61c8b7 100644
--- a/media/codec2/sfplugin/CCodecConfig.h
+++ b/media/codec2/sfplugin/CCodecConfig.h
@@ -134,6 +134,8 @@
     /// For now support a validation function.
     std::map<C2Param::Index, LocalParamValidator> mLocalParams;
 
+    std::set<std::string> mLastConfig;
+
     CCodecConfig();
 
     /// initializes the members required to manage the format: descriptors, reflector,
diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp
index 702ad6f..5c8ad56 100644
--- a/media/codec2/sfplugin/Codec2Buffer.cpp
+++ b/media/codec2/sfplugin/Codec2Buffer.cpp
@@ -25,6 +25,7 @@
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/foundation/AUtils.h>
 #include <nativebase/nativebase.h>
+#include <ui/Fence.h>
 
 #include <C2AllocatorGralloc.h>
 #include <C2BlockInternal.h>
@@ -590,7 +591,12 @@
     std::shared_ptr<C2GraphicBlock> block = _C2BlockFactory::CreateGraphicBlock(alloc);
 
     meta->pBuffer = 0;
-    // TODO: fence
+    // TODO: wrap this in C2Fence so that the component can wait when it
+    //       actually starts processing.
+    if (meta->nFenceFd >= 0) {
+        sp<Fence> fence(new Fence(meta->nFenceFd));
+        fence->waitForever(LOG_TAG);
+    }
     return C2Buffer::CreateGraphicBuffer(
             block->share(C2Rect(buffer->width, buffer->height), C2Fence()));
 #else
diff --git a/media/codec2/sfplugin/Omx2IGraphicBufferSource.cpp b/media/codec2/sfplugin/Omx2IGraphicBufferSource.cpp
new file mode 100644
index 0000000..764fa00
--- /dev/null
+++ b/media/codec2/sfplugin/Omx2IGraphicBufferSource.cpp
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+#ifdef __LP64__
+#define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
+#endif
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Omx2IGraphicBufferSource"
+#include <android-base/logging.h>
+
+#include "Omx2IGraphicBufferSource.h"
+
+#include <android/BnOMXBufferSource.h>
+#include <media/OMXBuffer.h>
+#include <media/stagefright/omx/OMXUtils.h>
+
+#include <OMX_Component.h>
+#include <OMX_Index.h>
+#include <OMX_IndexExt.h>
+
+namespace android {
+
+namespace /* unnamed */ {
+
+// OmxGraphicBufferSource -> IOMXBufferSource
+
+struct OmxGbs2IOmxBs : public BnOMXBufferSource {
+    sp<OmxGraphicBufferSource> mBase;
+    OmxGbs2IOmxBs(sp<OmxGraphicBufferSource> const& base) : mBase{base} {}
+    BnStatus onOmxExecuting() override {
+        return mBase->onOmxExecuting();
+    }
+    BnStatus onOmxIdle() override {
+        return mBase->onOmxIdle();
+    }
+    BnStatus onOmxLoaded() override {
+        return mBase->onOmxLoaded();
+    }
+    BnStatus onInputBufferAdded(int32_t bufferId) override {
+        return mBase->onInputBufferAdded(bufferId);
+    }
+    BnStatus onInputBufferEmptied(
+            int32_t bufferId,
+            OMXFenceParcelable const& fenceParcel) override {
+        return mBase->onInputBufferEmptied(bufferId, fenceParcel.get());
+    }
+};
+
+struct OmxNodeWrapper : public IOmxNodeWrapper {
+    sp<IOMXNode> mBase;
+    OmxNodeWrapper(sp<IOMXNode> const& base) : mBase{base} {}
+    status_t emptyBuffer(
+            int32_t bufferId, uint32_t flags,
+            const sp<GraphicBuffer> &buffer,
+            int64_t timestamp, int fenceFd) override {
+        return mBase->emptyBuffer(bufferId, buffer, flags, timestamp, fenceFd);
+    }
+    void dispatchDataSpaceChanged(
+            int32_t dataSpace, int32_t aspects, int32_t pixelFormat) override {
+        omx_message msg{};
+        msg.type = omx_message::EVENT;
+        msg.fenceFd = -1;
+        msg.u.event_data.event = OMX_EventDataSpaceChanged;
+        msg.u.event_data.data1 = dataSpace;
+        msg.u.event_data.data2 = aspects;
+        msg.u.event_data.data3 = pixelFormat;
+        mBase->dispatchMessage(msg);
+    }
+};
+
+} // unnamed namespace
+
+// Omx2IGraphicBufferSource
+Omx2IGraphicBufferSource::Omx2IGraphicBufferSource(
+        sp<OmxGraphicBufferSource> const& base)
+      : mBase{base},
+        mOMXBufferSource{new OmxGbs2IOmxBs(base)} {
+}
+
+BnStatus Omx2IGraphicBufferSource::setSuspend(
+        bool suspend, int64_t timeUs) {
+    return BnStatus::fromStatusT(mBase->setSuspend(suspend, timeUs));
+}
+
+BnStatus Omx2IGraphicBufferSource::setRepeatPreviousFrameDelayUs(
+        int64_t repeatAfterUs) {
+    return BnStatus::fromStatusT(mBase->setRepeatPreviousFrameDelayUs(repeatAfterUs));
+}
+
+BnStatus Omx2IGraphicBufferSource::setMaxFps(float maxFps) {
+    return BnStatus::fromStatusT(mBase->setMaxFps(maxFps));
+}
+
+BnStatus Omx2IGraphicBufferSource::setTimeLapseConfig(
+        double fps, double captureFps) {
+    return BnStatus::fromStatusT(mBase->setTimeLapseConfig(fps, captureFps));
+}
+
+BnStatus Omx2IGraphicBufferSource::setStartTimeUs(
+        int64_t startTimeUs) {
+    return BnStatus::fromStatusT(mBase->setStartTimeUs(startTimeUs));
+}
+
+BnStatus Omx2IGraphicBufferSource::setStopTimeUs(
+        int64_t stopTimeUs) {
+    return BnStatus::fromStatusT(mBase->setStopTimeUs(stopTimeUs));
+}
+
+BnStatus Omx2IGraphicBufferSource::getStopTimeOffsetUs(
+        int64_t *stopTimeOffsetUs) {
+    return BnStatus::fromStatusT(mBase->getStopTimeOffsetUs(stopTimeOffsetUs));
+}
+
+BnStatus Omx2IGraphicBufferSource::setColorAspects(
+        int32_t aspects) {
+    return BnStatus::fromStatusT(mBase->setColorAspects(aspects));
+}
+
+BnStatus Omx2IGraphicBufferSource::setTimeOffsetUs(
+        int64_t timeOffsetsUs) {
+    return BnStatus::fromStatusT(mBase->setTimeOffsetUs(timeOffsetsUs));
+}
+
+BnStatus Omx2IGraphicBufferSource::signalEndOfInputStream() {
+    return BnStatus::fromStatusT(mBase->signalEndOfInputStream());
+}
+
+BnStatus Omx2IGraphicBufferSource::configure(
+        const sp<IOMXNode>& omxNode, int32_t dataSpace) {
+    if (omxNode == NULL) {
+        return BnStatus::fromServiceSpecificError(BAD_VALUE);
+    }
+
+    // Do setInputSurface() first, the node will try to enable metadata
+    // mode on input, and does necessary error checking. If this fails,
+    // we can't use this input surface on the node.
+    status_t err = omxNode->setInputSurface(mOMXBufferSource);
+    if (err != NO_ERROR) {
+        ALOGE("Unable to set input surface: %d", err);
+        return BnStatus::fromServiceSpecificError(err);
+    }
+
+    uint32_t consumerUsage;
+    if (omxNode->getParameter(
+            (OMX_INDEXTYPE)OMX_IndexParamConsumerUsageBits,
+            &consumerUsage, sizeof(consumerUsage)) != OK) {
+        consumerUsage = 0;
+    }
+
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+    def.nPortIndex = 0; // kPortIndexInput
+
+    err = omxNode->getParameter(
+            OMX_IndexParamPortDefinition, &def, sizeof(def));
+    if (err != NO_ERROR) {
+        ALOGE("Failed to get port definition: %d", err);
+        return BnStatus::fromServiceSpecificError(UNKNOWN_ERROR);
+    }
+
+    return BnStatus::fromStatusT(mBase->configure(
+            new OmxNodeWrapper(omxNode),
+            dataSpace,
+            def.nBufferCountActual,
+            def.format.video.nFrameWidth,
+            def.format.video.nFrameHeight,
+            consumerUsage));
+}
+
+} // namespace android
+
diff --git a/media/codec2/sfplugin/Omx2IGraphicBufferSource.h b/media/codec2/sfplugin/Omx2IGraphicBufferSource.h
new file mode 100644
index 0000000..20fd1ec
--- /dev/null
+++ b/media/codec2/sfplugin/Omx2IGraphicBufferSource.h
@@ -0,0 +1,47 @@
+/*
+ * 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 OMX_2_IGRAPHICBUFFERSOURCE_H_
+#define OMX_2_IGRAPHICBUFFERSOURCE_H_
+
+#include <android/BnGraphicBufferSource.h>
+#include <media/stagefright/omx/OmxGraphicBufferSource.h>
+
+namespace android {
+
+using BnStatus = ::android::binder::Status;
+
+struct Omx2IGraphicBufferSource : public BnGraphicBufferSource {
+    sp<OmxGraphicBufferSource> mBase;
+    sp<IOMXBufferSource> mOMXBufferSource;
+    Omx2IGraphicBufferSource(sp<OmxGraphicBufferSource> const& base);
+    BnStatus configure(const sp<IOMXNode>& omxNode, int32_t dataSpace) override;
+    BnStatus setSuspend(bool suspend, int64_t timeUs) override;
+    BnStatus setRepeatPreviousFrameDelayUs(int64_t repeatAfterUs) override;
+    BnStatus setMaxFps(float maxFps) override;
+    BnStatus setTimeLapseConfig(double fps, double captureFps) override;
+    BnStatus setStartTimeUs(int64_t startTimeUs) override;
+    BnStatus setStopTimeUs(int64_t stopTimeUs) override;
+    BnStatus getStopTimeOffsetUs(int64_t *stopTimeOffsetUs) override;
+    BnStatus setColorAspects(int32_t aspects) override;
+    BnStatus setTimeOffsetUs(int64_t timeOffsetsUs) override;
+    BnStatus signalEndOfInputStream() override;
+};
+
+} // namespace android
+
+#endif // OMX_2_IGRAPHICBUFFERSOURCE_H_
+
diff --git a/media/codec2/sfplugin/utils/Codec2Mapper.cpp b/media/codec2/sfplugin/utils/Codec2Mapper.cpp
index 40160c7..7334834 100644
--- a/media/codec2/sfplugin/utils/Codec2Mapper.cpp
+++ b/media/codec2/sfplugin/utils/Codec2Mapper.cpp
@@ -629,7 +629,7 @@
 // static
 std::shared_ptr<C2Mapper::ProfileLevelMapper>
 C2Mapper::GetProfileLevelMapper(std::string mediaType) {
-    std::transform(mediaType.begin(), mediaType.begin(), mediaType.end(), ::tolower);
+    std::transform(mediaType.begin(), mediaType.end(), mediaType.begin(), ::tolower);
     if (mediaType == MIMETYPE_AUDIO_AAC) {
         return std::make_shared<AacProfileLevelMapper>();
     } else if (mediaType == MIMETYPE_VIDEO_AVC) {
@@ -657,7 +657,7 @@
 // static
 std::shared_ptr<C2Mapper::ProfileLevelMapper>
 C2Mapper::GetHdrProfileLevelMapper(std::string mediaType, bool isHdr10Plus) {
-    std::transform(mediaType.begin(), mediaType.begin(), mediaType.end(), ::tolower);
+    std::transform(mediaType.begin(), mediaType.end(), mediaType.begin(), ::tolower);
     if (mediaType == MIMETYPE_VIDEO_HEVC) {
         return std::make_shared<HevcProfileLevelMapper>(true, isHdr10Plus);
     } else if (mediaType == MIMETYPE_VIDEO_VP9) {
diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp
index dab4697..af97e61 100644
--- a/media/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/codec2/vndk/C2AllocatorGralloc.cpp
@@ -223,6 +223,22 @@
         return res;
     }
 
+    static bool MigrateNativeHandle(
+            native_handle_t *handle,
+            uint32_t generation, uint64_t igbp_id, uint32_t igbp_slot) {
+        if (handle == nullptr || !isValid(handle)) {
+            return false;
+        }
+        ExtraData *ed = getExtraData(handle);
+        if (!ed) return false;
+        ed->generation = generation;
+        ed->igbp_id_lo = uint32_t(igbp_id & 0xFFFFFFFF);
+        ed->igbp_id_hi = uint32_t(igbp_id >> 32);
+        ed->igbp_slot = igbp_slot;
+        return true;
+    }
+
+
     static native_handle_t* UnwrapNativeHandle(
             const C2Handle *const handle) {
         const ExtraData *xd = getExtraData(handle);
@@ -271,6 +287,13 @@
                                              generation, igbp_id, igbp_slot);
 }
 
+bool MigrateNativeCodec2GrallocHandle(
+        native_handle_t *handle,
+        uint32_t generation, uint64_t igbp_id, uint32_t igbp_slot) {
+    return C2HandleGralloc::MigrateNativeHandle(handle, generation, igbp_id, igbp_slot);
+}
+
+
 class C2AllocationGralloc : public C2GraphicAllocation {
 public:
     virtual ~C2AllocationGralloc() override;
diff --git a/media/codec2/vndk/include/C2AllocatorGralloc.h b/media/codec2/vndk/include/C2AllocatorGralloc.h
index 05d989e..ee7524e 100644
--- a/media/codec2/vndk/include/C2AllocatorGralloc.h
+++ b/media/codec2/vndk/include/C2AllocatorGralloc.h
@@ -45,6 +45,16 @@
         uint32_t generation = 0, uint64_t igbp_id = 0, uint32_t igbp_slot = 0);
 
 /**
+ * When the gralloc handle is migrated to another bufferqueue, update
+ * bufferqueue information.
+ *
+ * @return {@code true} when native_handle is a wrapped codec2 handle.
+ */
+bool MigrateNativeCodec2GrallocHandle(
+        native_handle_t *handle,
+        uint32_t generation, uint64_t igbp_id, uint32_t igbp_slot);
+
+/**
  * \todo Get this from the buffer
  */
 void _UnwrapNativeCodec2GrallocMetadata(
diff --git a/media/codec2/vndk/internal/C2BlockInternal.h b/media/codec2/vndk/internal/C2BlockInternal.h
index 84ce70a..4ae946a 100644
--- a/media/codec2/vndk/internal/C2BlockInternal.h
+++ b/media/codec2/vndk/internal/C2BlockInternal.h
@@ -206,23 +206,19 @@
      *
      *   - GetBufferQueueData(): Returns generation, bqId and bqSlot.
      *   - HoldBlockFromBufferQueue(): Sets "held" status to true.
-     *   - YieldBlockToBufferQueue(): Sets "held" status to false.
-     *   - AssignBlockToBufferQueue(): Sets the bufferqueue assignment and
-     *     "held" status.
+     *   - BeginTransferBlockToClient()/EndTransferBlockToClient():
+     *     Clear "held" status to false if transfer was successful,
+     *     otherwise "held" status remains true.
+     *   - BeginAttachBlockToBufferQueue()/EndAttachBlockToBufferQueue():
+     *     The will keep "held" status true if attach was eligible.
+     *     Otherwise, "held" status is cleared to false. In that case,
+     *     ownership of buffer should be transferred to bufferqueue.
+     *   - DisplayBlockToBufferQueue()
+     *     This will clear "held" status to false.
      *
      * All these functions operate on _C2BlockPoolData, which can be obtained by
      * calling GetGraphicBlockPoolData().
      *
-     * HoldBlockFromBufferQueue() will mark the block as held, while
-     * YieldBlockToBufferQueue() will do the opposite. These two functions do
-     * not modify the bufferqueue assignment, so it is not wrong to call
-     * HoldBlockFromBufferQueue() after YieldBlockToBufferQueue() if it can be
-     * guaranteed that the block is not destroyed during the period between the
-     * two calls.
-     *
-     * AssingBlockToBufferQueue() has a "held" status as an optional argument.
-     * The default value is true.
-     *
      * Maintaining Consistency with IGraphicBufferProducer Operations
      * ==============================================================
      *
@@ -232,16 +228,20 @@
      *     information for _C2BlockPoolData, with "held" status set to true.
      *
      * queueBuffer()
-     *   - After queueBuffer() is called, YieldBlockToBufferQueue() should be
-     *     called.
+     *   - Before queueBuffer() is called, DisplayBlockToBufferQueue() should be
+     *     called to test eligibility. If it's not eligible, do not call
+     *     queueBuffer().
      *
-     * attachBuffer()
-     *   - After attachBuffer() is called, AssignBlockToBufferQueue() should be
-     *     called with "held" status set to true.
+     * attachBuffer() - remote migration only.
+     *   - Local migration on blockpool side will be done automatically by
+     *     blockpool.
+     *   - Before attachBuffer(), BeginAttachBlockToBufferQueue() should be called
+     *     to test eligiblity.
+     *   - After attachBuffer() is called, EndAttachBlockToBufferQueue() should
+     *     be called. This will set "held" status to true. If it returned
+     *     false, cancelBuffer() should be called.
      *
-     * detachBuffer()
-     *   - After detachBuffer() is called, HoldBlockFromBufferQueue() should be
-     *     called.
+     * detachBuffer() - no-op.
      */
 
     /**
@@ -261,43 +261,12 @@
      */
     static
     bool GetBufferQueueData(
-            const std::shared_ptr<_C2BlockPoolData>& poolData,
+            const std::shared_ptr<const _C2BlockPoolData>& poolData,
             uint32_t* generation = nullptr,
             uint64_t* bqId = nullptr,
             int32_t* bqSlot = nullptr);
 
     /**
-     * Set bufferqueue assignment and "held" status to a block created by a
-     * bufferqueue-based blockpool.
-     *
-     * \param poolData blockpool data associated to the block.
-     * \param igbp       \c IGraphicBufferProducer instance from the designated
-     *                   bufferqueue.
-     * \param generation Generation number that the buffer belongs to.
-     * \param bqId       Id of the bufferqueue that will own the buffer (block).
-     * \param bqSlot     Slot number of the buffer.
-     * \param held       Whether the block is held. This "held" status can be
-     *                   changed later by calling YieldBlockToBufferQueue() or
-     *                   HoldBlockFromBufferQueue().
-     *
-     * \return \c true if \p poolData is valid bufferqueue data;
-     *         \c false otherwise.
-     *
-     * Note: \p generation should match the latest generation number set on the
-     * bufferqueue, and \p bqId should match the unique id for the bufferqueue
-     * (obtainable by calling igbp->getUniqueId()).
-     */
-    static
-    bool AssignBlockToBufferQueue(
-            const std::shared_ptr<_C2BlockPoolData>& poolData,
-            const ::android::sp<::android::hardware::graphics::bufferqueue::
-                                V2_0::IGraphicBufferProducer>& igbp,
-            uint32_t generation,
-            uint64_t bqId,
-            int32_t bqSlot,
-            bool held = true);
-
-    /**
      * Hold a block from the designated bufferqueue. This causes the destruction
      * of the block to trigger a call to cancelBuffer().
      *
@@ -305,6 +274,9 @@
      * block. It does not check if that is the case.
      *
      * \param poolData blockpool data associated to the block.
+     * \param owner    block owner from client bufferqueue manager.
+     *                 If this is expired, the block is not owned by client
+     *                 anymore.
      * \param igbp     \c IGraphicBufferProducer instance to be assigned to the
      *                 block. This is not needed when the block is local.
      *
@@ -313,24 +285,96 @@
     static
     bool HoldBlockFromBufferQueue(
             const std::shared_ptr<_C2BlockPoolData>& poolData,
+            const std::shared_ptr<int>& owner,
             const ::android::sp<::android::hardware::graphics::bufferqueue::
                                 V2_0::IGraphicBufferProducer>& igbp = nullptr);
 
     /**
-     * Yield a block to the designated bufferqueue. This causes the destruction
-     * of the block not to trigger a call to cancelBuffer();
+     * Prepare a block to be transferred to other process. This blocks
+     * bufferqueue migration from happening. The block should be in held.
      *
      * This function assumes that \p poolData comes from a bufferqueue-based
      * block. It does not check if that is the case.
      *
      * \param poolData blockpool data associated to the block.
      *
-     * \return The previous held status.
+     * \return true if transfer is eligible, false otherwise.
      */
     static
-    bool YieldBlockToBufferQueue(
+    bool BeginTransferBlockToClient(
             const std::shared_ptr<_C2BlockPoolData>& poolData);
 
+    /**
+     * Called after transferring the specified block is finished. Make sure
+     * that BeginTransferBlockToClient() was called before this call.
+     *
+     * This will unblock bufferqueue migration. If transfer result was
+     * successful, this causes the destruction of the block not to trigger a
+     * call to cancelBuffer().
+     * This function assumes that \p poolData comes from a bufferqueue-based
+     * block. It does not check if that is the case.
+     *
+     * \param poolData blockpool data associated to the block.
+     *
+     * \return true if transfer began before, false otherwise.
+     */
+    static
+    bool EndTransferBlockToClient(
+            const std::shared_ptr<_C2BlockPoolData>& poolData,
+            bool transferred);
+
+    /**
+     * Prepare a block to be migrated to another bufferqueue. This blocks
+     * rendering until migration has been finished.  The block should be in
+     * held.
+     *
+     * This function assumes that \p poolData comes from a bufferqueue-based
+     * block. It does not check if that is the case.
+     *
+     * \param poolData blockpool data associated to the block.
+     *
+     * \return true if migration is eligible, false otherwise.
+     */
+    static
+    bool BeginAttachBlockToBufferQueue(
+            const std::shared_ptr<_C2BlockPoolData>& poolData);
+
+    /**
+     * Called after migration of the specified block is finished. Make sure
+     * that BeginAttachBlockToBufferQueue() was called before this call.
+     *
+     * This will unblock rendering. if redering is tried during migration,
+     * this returns false. In that case, cancelBuffer() should be called.
+     * This function assumes that \p poolData comes from a bufferqueue-based
+     * block. It does not check if that is the case.
+     *
+     * \param poolData blockpool data associated to the block.
+     *
+     * \return true if migration is eligible, false otherwise.
+     */
+    static
+    bool EndAttachBlockToBufferQueue(
+            const std::shared_ptr<_C2BlockPoolData>& poolData,
+            const std::shared_ptr<int>& owner,
+            const ::android::sp<::android::hardware::graphics::bufferqueue::
+                                V2_0::IGraphicBufferProducer>& igbp,
+            uint32_t generation,
+            uint64_t bqId,
+            int32_t bqSlot);
+
+    /**
+     * Indicates a block to be rendered very soon.
+     *
+     * This function assumes that \p poolData comes from a bufferqueue-based
+     * block. It does not check if that is the case.
+     *
+     * \param poolData blockpool data associated to the block.
+     *
+     * \return true if migration is eligible, false otherwise.
+     */
+    static
+    bool DisplayBlockToBufferQueue(
+            const std::shared_ptr<_C2BlockPoolData>& poolData);
 };
 
 #endif // ANDROID_STAGEFRIGHT_C2BLOCK_INTERNAL_H_
diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp
index 3f40e56..8304f74 100644
--- a/media/codec2/vndk/platform/C2BqBuffer.cpp
+++ b/media/codec2/vndk/platform/C2BqBuffer.cpp
@@ -61,8 +61,13 @@
     uint32_t generation;
     uint64_t bqId;
     int32_t bqSlot;
+    bool transfer; // local transfer to remote
+    bool attach; // attach on remote
+    bool display; // display on remote;
+    std::weak_ptr<int> owner;
     sp<HGraphicBufferProducer> igbp;
     std::shared_ptr<C2BufferQueueBlockPool::Impl> localPool;
+    mutable std::mutex lock;
 
     virtual type_t getType() const override {
         return TYPE_BUFFERQUEUE;
@@ -71,7 +76,8 @@
     // Create a remote BlockPoolData.
     C2BufferQueueBlockPoolData(
             uint32_t generation, uint64_t bqId, int32_t bqSlot,
-            const sp<HGraphicBufferProducer>& producer = nullptr);
+            const std::shared_ptr<int> &owner,
+            const sp<HGraphicBufferProducer>& producer);
 
     // Create a local BlockPoolData.
     C2BufferQueueBlockPoolData(
@@ -80,15 +86,19 @@
 
     virtual ~C2BufferQueueBlockPoolData() override;
 
+    int migrate(const sp<HGraphicBufferProducer>& producer,
+                uint32_t toGeneration, uint64_t toBqId,
+                sp<GraphicBuffer> *buffers, uint32_t oldGeneration);
 };
 
 bool _C2BlockFactory::GetBufferQueueData(
-        const std::shared_ptr<_C2BlockPoolData>& data,
+        const std::shared_ptr<const _C2BlockPoolData>& data,
         uint32_t* generation, uint64_t* bqId, int32_t* bqSlot) {
     if (data && data->getType() == _C2BlockPoolData::TYPE_BUFFERQUEUE) {
         if (generation) {
-            const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
-                    std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
+            const std::shared_ptr<const C2BufferQueueBlockPoolData> poolData =
+                    std::static_pointer_cast<const C2BufferQueueBlockPoolData>(data);
+            std::scoped_lock<std::mutex> lock(poolData->lock);
             *generation = poolData->generation;
             if (bqId) {
                 *bqId = poolData->bqId;
@@ -102,32 +112,15 @@
     return false;
 }
 
-bool _C2BlockFactory::AssignBlockToBufferQueue(
-        const std::shared_ptr<_C2BlockPoolData>& data,
-        const sp<HGraphicBufferProducer>& igbp,
-        uint32_t generation,
-        uint64_t bqId,
-        int32_t bqSlot,
-        bool held) {
-    if (data && data->getType() == _C2BlockPoolData::TYPE_BUFFERQUEUE) {
-        const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
-                std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
-        poolData->igbp = igbp;
-        poolData->generation = generation;
-        poolData->bqId = bqId;
-        poolData->bqSlot = bqSlot;
-        poolData->held = held;
-        return true;
-    }
-    return false;
-}
-
 bool _C2BlockFactory::HoldBlockFromBufferQueue(
         const std::shared_ptr<_C2BlockPoolData>& data,
+        const std::shared_ptr<int>& owner,
         const sp<HGraphicBufferProducer>& igbp) {
     const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
             std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
+    std::scoped_lock<std::mutex> lock(poolData->lock);
     if (!poolData->local) {
+        poolData->owner = owner;
         poolData->igbp = igbp;
     }
     if (poolData->held) {
@@ -138,12 +131,86 @@
     return true;
 }
 
-bool _C2BlockFactory::YieldBlockToBufferQueue(
+bool _C2BlockFactory::BeginTransferBlockToClient(
         const std::shared_ptr<_C2BlockPoolData>& data) {
     const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
             std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
-    if (!poolData->held) {
+    std::scoped_lock<std::mutex> lock(poolData->lock);
+    poolData->transfer = true;
+    return true;
+}
+
+bool _C2BlockFactory::EndTransferBlockToClient(
+        const std::shared_ptr<_C2BlockPoolData>& data,
+        bool transfer) {
+    const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
+            std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
+    std::scoped_lock<std::mutex> lock(poolData->lock);
+    poolData->transfer = false;
+    if (transfer) {
         poolData->held = false;
+    }
+    return true;
+}
+
+bool _C2BlockFactory::BeginAttachBlockToBufferQueue(
+        const std::shared_ptr<_C2BlockPoolData>& data) {
+    const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
+            std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
+    std::scoped_lock<std::mutex> lock(poolData->lock);
+    if (poolData->local || poolData->display ||
+        poolData->attach || !poolData->held) {
+        return false;
+    }
+    if (poolData->bqId == 0) {
+        return false;
+    }
+    poolData->attach = true;
+    return true;
+}
+
+// if display was tried during attach, buffer should be retired ASAP.
+bool _C2BlockFactory::EndAttachBlockToBufferQueue(
+        const std::shared_ptr<_C2BlockPoolData>& data,
+        const std::shared_ptr<int>& owner,
+        const sp<HGraphicBufferProducer>& igbp,
+        uint32_t generation,
+        uint64_t bqId,
+        int32_t bqSlot) {
+    const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
+            std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
+    std::scoped_lock<std::mutex> lock(poolData->lock);
+    if (poolData->local || !poolData->attach ) {
+        return false;
+    }
+    if (poolData->display) {
+        poolData->attach = false;
+        poolData->held = false;
+        return false;
+    }
+    poolData->attach = false;
+    poolData->held = true;
+    poolData->owner = owner;
+    poolData->igbp = igbp;
+    poolData->generation = generation;
+    poolData->bqId = bqId;
+    poolData->bqSlot = bqSlot;
+    return true;
+}
+
+bool _C2BlockFactory::DisplayBlockToBufferQueue(
+        const std::shared_ptr<_C2BlockPoolData>& data) {
+    const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
+            std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
+    std::scoped_lock<std::mutex> lock(poolData->lock);
+    if (poolData->local || poolData->display || !poolData->held) {
+        return false;
+    }
+    if (poolData->bqId == 0) {
+        return false;
+    }
+    poolData->display = true;
+    if (poolData->attach) {
         return false;
     }
     poolData->held = false;
@@ -175,7 +242,9 @@
                 std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
                         std::make_shared<C2BufferQueueBlockPoolData>(generation,
                                                                      bqId,
-                                                                     (int32_t)bqSlot);
+                                                                     (int32_t)bqSlot,
+                                                                     nullptr,
+                                                                     nullptr);
                 block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
             } else {
                 block = _C2BlockFactory::CreateGraphicBlock(alloc);
@@ -186,6 +255,78 @@
     return nullptr;
 }
 
+namespace {
+
+int64_t getTimestampNow() {
+    int64_t stamp;
+    struct timespec ts;
+    // TODO: CLOCK_MONOTONIC_COARSE?
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    stamp = ts.tv_nsec / 1000;
+    stamp += (ts.tv_sec * 1000000LL);
+    return stamp;
+}
+
+bool getGenerationNumber(const sp<HGraphicBufferProducer> &producer,
+                         uint32_t *generation) {
+    status_t status{};
+    int slot{};
+    bool bufferNeedsReallocation{};
+    sp<Fence> fence = new Fence();
+
+    using Input = HGraphicBufferProducer::DequeueBufferInput;
+    using Output = HGraphicBufferProducer::DequeueBufferOutput;
+    Return<void> transResult = producer->dequeueBuffer(
+            Input{640, 480, HAL_PIXEL_FORMAT_YCBCR_420_888, 0},
+            [&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) {
+        return false;
+    }
+    HFenceWrapper hFenceWrapper{};
+    if (!b2h(fence, &hFenceWrapper)) {
+        (void)producer->detachBuffer(static_cast<int32_t>(slot)).isOk();
+        ALOGE("Invalid fence received from dequeueBuffer.");
+        return false;
+    }
+    sp<GraphicBuffer> slotBuffer = new GraphicBuffer();
+    // N.B. This assumes requestBuffer# returns an existing allocation
+    // instead of a new allocation.
+    transResult = producer->requestBuffer(
+            slot,
+            [&status, &slotBuffer, &generation](
+                    HStatus hStatus,
+                    HBuffer const& hBuffer,
+                    uint32_t generationNumber){
+                if (h2b(hStatus, &status) &&
+                        h2b(hBuffer, &slotBuffer) &&
+                        slotBuffer) {
+                    *generation = generationNumber;
+                    slotBuffer->setGenerationNumber(generationNumber);
+                } else {
+                    status = android::BAD_VALUE;
+                }
+            });
+    if (!transResult.isOk()) {
+        return false;
+    } else if (status != android::NO_ERROR) {
+        (void)producer->detachBuffer(static_cast<int32_t>(slot)).isOk();
+        return false;
+    }
+    (void)producer->detachBuffer(static_cast<int32_t>(slot)).isOk();
+    return true;
+}
+
+};
+
 class C2BufferQueueBlockPool::Impl
         : public std::enable_shared_from_this<C2BufferQueueBlockPool::Impl> {
 private:
@@ -227,6 +368,7 @@
                     });
             if (!transResult.isOk() || status != android::OK) {
                 if (transResult.isOk()) {
+                    ++mDqFailure;
                     if (status == android::INVALID_OPERATION ||
                         status == android::TIMED_OUT ||
                         status == android::WOULD_BLOCK) {
@@ -238,6 +380,8 @@
                 ALOGD("cannot dequeue buffer %d", status);
                 return C2_BAD_VALUE;
             }
+            mDqFailure = 0;
+            mLastDqTs = getTimestampNow();
         }
         HFenceWrapper hFenceWrapper{};
         if (!b2h(fence, &hFenceWrapper)) {
@@ -269,6 +413,7 @@
         }
 
         sp<GraphicBuffer> &slotBuffer = mBuffers[slot];
+        uint32_t outGeneration;
         if (bufferNeedsReallocation || !slotBuffer) {
             if (!slotBuffer) {
                 slotBuffer = new GraphicBuffer();
@@ -277,7 +422,7 @@
             // instead of a new allocation.
             Return<void> transResult = mProducer->requestBuffer(
                     slot,
-                    [&status, &slotBuffer](
+                    [&status, &slotBuffer, &outGeneration](
                             HStatus hStatus,
                             HBuffer const& hBuffer,
                             uint32_t generationNumber){
@@ -285,17 +430,23 @@
                                 h2b(hBuffer, &slotBuffer) &&
                                 slotBuffer) {
                             slotBuffer->setGenerationNumber(generationNumber);
+                            outGeneration = generationNumber;
                         } else {
                             status = android::BAD_VALUE;
                         }
                     });
             if (!transResult.isOk()) {
+                slotBuffer.clear();
                 return C2_BAD_VALUE;
             } else if (status != android::NO_ERROR) {
                 slotBuffer.clear();
                 (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
                 return C2_BAD_VALUE;
             }
+            if (mGeneration == 0) {
+                // getting generation # lazily due to dequeue failure.
+                mGeneration = outGeneration;
+            }
         }
         if (slotBuffer) {
             ALOGV("buffer wraps %llu %d", (unsigned long long)mProducerId, slot);
@@ -319,6 +470,7 @@
                                 slotBuffer->getGenerationNumber(),
                                 mProducerId, slot,
                                 shared_from_this());
+                mPoolDatas[slot] = poolData;
                 *block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
                 return C2_OK;
             }
@@ -331,7 +483,9 @@
 
 public:
     Impl(const std::shared_ptr<C2Allocator> &allocator)
-        : mInit(C2_OK), mProducerId(0), mAllocator(allocator) {
+        : mInit(C2_OK), mProducerId(0), mGeneration(0),
+          mDqFailure(0), mLastDqTs(0), mLastDqLogTs(0),
+          mAllocator(allocator) {
     }
 
     ~Impl() {
@@ -361,6 +515,19 @@
         static int kMaxIgbpRetryDelayUs = 10000;
 
         std::unique_lock<std::mutex> lock(mMutex);
+        if (mLastDqLogTs == 0) {
+            mLastDqLogTs = getTimestampNow();
+        } else {
+            int64_t now = getTimestampNow();
+            if (now >= mLastDqLogTs + 5000000) {
+                if (now >= mLastDqTs + 1000000 || mDqFailure > 5) {
+                    ALOGW("last successful dequeue was %lld us ago, "
+                          "%zu consecutive failures",
+                          (long long)(now - mLastDqTs), mDqFailure);
+                }
+                mLastDqLogTs = now;
+            }
+        }
         if (mProducerId == 0) {
             std::shared_ptr<C2GraphicAllocation> alloc;
             c2_status_t err = mAllocator->newGraphicAllocation(
@@ -386,12 +553,14 @@
     }
 
     void setRenderCallback(const OnRenderCallback &renderCallback) {
-        std::lock_guard<std::mutex> lock(mMutex);
+        std::scoped_lock<std::mutex> lock(mMutex);
         mRenderCallback = renderCallback;
     }
 
     void configureProducer(const sp<HGraphicBufferProducer> &producer) {
         uint64_t producerId = 0;
+        uint32_t generation = 0;
+        bool haveGeneration = false;
         if (producer) {
             Return<uint64_t> transResult = producer->getUniqueId();
             if (!transResult.isOk()) {
@@ -399,9 +568,19 @@
                 return;
             }
             producerId = static_cast<uint64_t>(transResult);
+            // TODO: provide gneration number from parameter.
+            haveGeneration = getGenerationNumber(producer, &generation);
+            if (!haveGeneration) {
+                ALOGW("get generationNumber failed %llu",
+                      (unsigned long long)producerId);
+            }
         }
+        int migrated = 0;
         {
-            std::lock_guard<std::mutex> lock(mMutex);
+            sp<GraphicBuffer> buffers[NUM_BUFFER_SLOTS];
+            std::weak_ptr<C2BufferQueueBlockPoolData>
+                    poolDatas[NUM_BUFFER_SLOTS];
+            std::scoped_lock<std::mutex> lock(mMutex);
             bool noInit = false;
             for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
                 if (!noInit && mProducer) {
@@ -410,46 +589,88 @@
                     noInit = !transResult.isOk() ||
                              static_cast<HStatus>(transResult) == HStatus::NO_INIT;
                 }
-                mBuffers[i].clear();
             }
+            int32_t oldGeneration = mGeneration;
             if (producer) {
                 mProducer = producer;
                 mProducerId = producerId;
+                mGeneration = haveGeneration ? generation : 0;
             } else {
                 mProducer = nullptr;
                 mProducerId = 0;
+                mGeneration = 0;
+                ALOGW("invalid producer producer(%d), generation(%d)",
+                      (bool)producer, haveGeneration);
             }
+            if (mProducer && haveGeneration) { // 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,
+                                producerId, mBuffers, oldGeneration);
+                        if (slot >= 0) {
+                            buffers[slot] = mBuffers[i];
+                            poolDatas[slot] = data;
+                            ++migrated;
+                        }
+                    }
+                }
+            }
+            for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
+                mBuffers[i] = buffers[i];
+                mPoolDatas[i] = poolDatas[i];
+            }
+        }
+        if (producer && haveGeneration) {
+            ALOGD("local generation change %u , "
+                  "bqId: %llu migrated buffers # %d",
+                  generation, (unsigned long long)producerId, migrated);
         }
     }
 
 private:
     friend struct C2BufferQueueBlockPoolData;
 
-    void cancel(uint64_t igbp_id, int32_t igbp_slot) {
-        std::lock_guard<std::mutex> lock(mMutex);
-        if (igbp_id == mProducerId && mProducer) {
+    void cancel(uint32_t generation, uint64_t igbp_id, int32_t igbp_slot) {
+        bool cancelled = false;
+        {
+        std::scoped_lock<std::mutex> lock(mMutex);
+        if (generation == mGeneration && igbp_id == mProducerId && mProducer) {
             (void)mProducer->cancelBuffer(igbp_slot, hidl_handle{}).isOk();
+            cancelled = true;
+        }
         }
     }
 
     c2_status_t mInit;
     uint64_t mProducerId;
+    uint32_t mGeneration;
     OnRenderCallback mRenderCallback;
 
+    size_t mDqFailure;
+    int64_t mLastDqTs;
+    int64_t mLastDqLogTs;
+
     const std::shared_ptr<C2Allocator> mAllocator;
 
     std::mutex mMutex;
     sp<HGraphicBufferProducer> mProducer;
+    sp<HGraphicBufferProducer> mSavedProducer;
 
     sp<GraphicBuffer> mBuffers[NUM_BUFFER_SLOTS];
+    std::weak_ptr<C2BufferQueueBlockPoolData> mPoolDatas[NUM_BUFFER_SLOTS];
 };
 
 C2BufferQueueBlockPoolData::C2BufferQueueBlockPoolData(
         uint32_t generation, uint64_t bqId, int32_t bqSlot,
+        const std::shared_ptr<int>& owner,
         const sp<HGraphicBufferProducer>& producer) :
         held(producer && bqId != 0), local(false),
         generation(generation), bqId(bqId), bqSlot(bqSlot),
-        igbp(producer),
+        transfer(false), attach(false), display(false),
+        owner(owner), igbp(producer),
         localPool() {
 }
 
@@ -458,6 +679,7 @@
         const std::shared_ptr<C2BufferQueueBlockPool::Impl>& pool) :
         held(true), local(true),
         generation(generation), bqId(bqId), bqSlot(bqSlot),
+        transfer(false), attach(false), display(false),
         igbp(pool ? pool->mProducer : nullptr),
         localPool(pool) {
 }
@@ -466,12 +688,78 @@
     if (!held || bqId == 0) {
         return;
     }
-    if (local && localPool) {
-        localPool->cancel(bqId, bqSlot);
-    } else if (igbp) {
+    if (local) {
+        if (localPool) {
+            localPool->cancel(generation, bqId, bqSlot);
+        }
+    } else if (igbp && !owner.expired()) {
         igbp->cancelBuffer(bqSlot, hidl_handle{}).isOk();
     }
 }
+int C2BufferQueueBlockPoolData::migrate(
+        const sp<HGraphicBufferProducer>& producer,
+        uint32_t toGeneration, uint64_t toBqId,
+        sp<GraphicBuffer> *buffers, uint32_t oldGeneration) {
+    std::scoped_lock<std::mutex> l(lock);
+    if (!held || bqId == 0) {
+        ALOGV("buffer is not owned");
+        return -1;
+    }
+    if (!local || !localPool) {
+        ALOGV("pool is not local");
+        return -1;
+    }
+    if (bqSlot < 0 || bqSlot >= NUM_BUFFER_SLOTS || !buffers[bqSlot]) {
+        ALOGV("slot is not in effect");
+        return -1;
+    }
+    if (toGeneration == generation && bqId == toBqId) {
+        ALOGV("cannot migrate to same bufferqueue");
+        return -1;
+    }
+    if (oldGeneration != generation) {
+        ALOGV("cannot migrate stale buffer");
+    }
+    if (transfer) {
+        // either transferred or detached.
+        ALOGV("buffer is in transfer");
+        return -1;
+    }
+    sp<GraphicBuffer> const& graphicBuffer = buffers[bqSlot];
+    graphicBuffer->setGenerationNumber(toGeneration);
+
+    HBuffer hBuffer{};
+    uint32_t hGenerationNumber{};
+    if (!b2h(graphicBuffer, &hBuffer, &hGenerationNumber)) {
+        ALOGD("I to O conversion failed");
+        return -1;
+    }
+
+    bool converted{};
+    status_t bStatus{};
+    int slot;
+    int *outSlot = &slot;
+    Return<void> transResult =
+            producer->attachBuffer(hBuffer, hGenerationNumber,
+                    [&converted, &bStatus, outSlot](
+                            HStatus hStatus, int32_t hSlot, bool releaseAll) {
+                        converted = h2b(hStatus, &bStatus);
+                        *outSlot = static_cast<int>(hSlot);
+                        if (converted && releaseAll && bStatus == android::OK) {
+                            bStatus = android::INVALID_OPERATION;
+                        }
+                    });
+    if (!transResult.isOk() || !converted || bStatus != android::OK) {
+        ALOGD("attach failed %d", static_cast<int>(bStatus));
+        return -1;
+    }
+    ALOGV("local migration from gen %u : %u slot %d : %d",
+          generation, toGeneration, bqSlot, slot);
+    generation = toGeneration;
+    bqId = toBqId;
+    bqSlot = slot;
+    return slot;
+}
 
 C2BufferQueueBlockPool::C2BufferQueueBlockPool(
         const std::shared_ptr<C2Allocator> &allocator, const local_id_t localId)
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 8004e75..36cab1d 100755
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -5053,15 +5053,15 @@
     mCurrentSampleInfoCount = smplcnt;
     offset += 4;
     size -= 4;
+    if (mCurrentDefaultSampleInfoSize != 0) {
+        ALOGV("@@@@ using default sample info size of %d", mCurrentDefaultSampleInfoSize);
+        return OK;
+    }
     if(smplcnt > size) {
         ALOGW("b/124525515 - smplcnt(%u) > size(%ld)", (unsigned int)smplcnt, (unsigned long)size);
         android_errorWriteLog(0x534e4554, "124525515");
         return -EINVAL;
     }
-    if (mCurrentDefaultSampleInfoSize != 0) {
-        ALOGV("@@@@ using default sample info size of %d", mCurrentDefaultSampleInfoSize);
-        return OK;
-    }
     if (smplcnt > mCurrentSampleInfoAllocSize) {
         uint8_t * newPtr =  (uint8_t*) realloc(mCurrentSampleInfoSizes, smplcnt);
         if (newPtr == NULL) {
diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp
index 4090286..1eedb12 100644
--- a/media/libaaudio/src/Android.bp
+++ b/media/libaaudio/src/Android.bp
@@ -73,6 +73,5 @@
         "libcutils",
         "libutils",
         "libbinder",
-        "libaudiomanager",
     ],
 }
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index a6cc45b..366cc87 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -89,7 +89,11 @@
     if (mAudioEndpoint.isFreeRunning()) {
         //ALOGD("AudioStreamInternalCapture::processDataNow() - update remote counter");
         // Update data queue based on the timing model.
-        int64_t estimatedRemoteCounter = mClockModel.convertTimeToPosition(currentNanoTime);
+        // Jitter in the DSP can cause late writes to the FIFO.
+        // This might be caused by resampling.
+        // We want to read the FIFO after the latest possible time
+        // that the DSP could have written the data.
+        int64_t estimatedRemoteCounter = mClockModel.convertLatestTimeToPosition(currentNanoTime);
         // TODO refactor, maybe use setRemoteCounter()
         mAudioEndpoint.setDataWriteCounter(estimatedRemoteCounter);
     }
@@ -139,7 +143,7 @@
                 // the writeCounter might have just advanced in the background,
                 // causing us to sleep until a later burst.
                 int64_t nextPosition = mAudioEndpoint.getDataReadCounter() + mFramesPerBurst;
-                wakeTime = mClockModel.convertPositionToTime(nextPosition);
+                wakeTime = mClockModel.convertPositionToLatestTime(nextPosition);
             }
                 break;
             default:
diff --git a/media/libaaudio/src/client/IsochronousClockModel.cpp b/media/libaaudio/src/client/IsochronousClockModel.cpp
index d26b352..9abdf53 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.cpp
+++ b/media/libaaudio/src/client/IsochronousClockModel.cpp
@@ -19,12 +19,11 @@
 #include <log/log.h>
 
 #include <stdint.h>
+#include <algorithm>
 
 #include "utility/AudioClock.h"
 #include "IsochronousClockModel.h"
 
-#define MIN_LATENESS_NANOS (10 * AAUDIO_NANOS_PER_MICROSECOND)
-
 using namespace aaudio;
 
 IsochronousClockModel::IsochronousClockModel()
@@ -32,7 +31,7 @@
         , mMarkerNanoTime(0)
         , mSampleRate(48000)
         , mFramesPerBurst(64)
-        , mMaxLatenessInNanos(0)
+        , mMaxMeasuredLatenessNanos(0)
         , mState(STATE_STOPPED)
 {
 }
@@ -41,8 +40,7 @@
 }
 
 void IsochronousClockModel::setPositionAndTime(int64_t framePosition, int64_t nanoTime) {
-    ALOGV("setPositionAndTime(%lld, %lld)",
-          (long long) framePosition, (long long) nanoTime);
+    ALOGV("setPositionAndTime, %lld, %lld", (long long) framePosition, (long long) nanoTime);
     mMarkerFramePosition = framePosition;
     mMarkerNanoTime = nanoTime;
 }
@@ -54,7 +52,9 @@
 }
 
 void IsochronousClockModel::stop(int64_t nanoTime) {
-    ALOGV("stop(nanos = %lld)\n", (long long) nanoTime);
+    ALOGD("stop(nanos = %lld) max lateness = %d micros\n",
+        (long long) nanoTime,
+        (int) (mMaxMeasuredLatenessNanos / 1000));
     setPositionAndTime(convertTimeToPosition(nanoTime), nanoTime);
     // TODO should we set position?
     mState = STATE_STOPPED;
@@ -69,9 +69,10 @@
 }
 
 void IsochronousClockModel::processTimestamp(int64_t framePosition, int64_t nanoTime) {
-//    ALOGD("processTimestamp() - framePosition = %lld at nanoTime %llu",
-//         (long long)framePosition,
-//         (long long)nanoTime);
+    mTimestampCount++;
+// Log position and time in CSV format so we can import it easily into spreadsheets.
+    //ALOGD("%s() CSV, %d, %lld, %lld", __func__,
+          //mTimestampCount, (long long)framePosition, (long long)nanoTime);
     int64_t framesDelta = framePosition - mMarkerFramePosition;
     int64_t nanosDelta = nanoTime - mMarkerNanoTime;
     if (nanosDelta < 1000) {
@@ -110,22 +111,54 @@
             // Earlier than expected timestamp.
             // This data is probably more accurate, so use it.
             // Or we may be drifting due to a fast HW clock.
-//            int microsDelta = (int) (nanosDelta / 1000);
-//            int expectedMicrosDelta = (int) (expectedNanosDelta / 1000);
-//            ALOGD("processTimestamp() - STATE_RUNNING - %7d < %7d so %4d micros EARLY",
-//                 microsDelta, expectedMicrosDelta, (expectedMicrosDelta - microsDelta));
+            //int microsDelta = (int) (nanosDelta / 1000);
+            //int expectedMicrosDelta = (int) (expectedNanosDelta / 1000);
+            //ALOGD("%s() - STATE_RUNNING - #%d, %4d micros EARLY",
+                //__func__, mTimestampCount, expectedMicrosDelta - microsDelta);
 
             setPositionAndTime(framePosition, nanoTime);
-        } else if (nanosDelta > (expectedNanosDelta + mMaxLatenessInNanos)) {
-            // Later than expected timestamp.
-//            int microsDelta = (int) (nanosDelta / 1000);
-//            int expectedMicrosDeadline = (int) ((expectedNanosDelta + mMaxLatenessInNanos) / 1000);
-//            ALOGD("processTimestamp() - STATE_RUNNING - %7d > %7d so %4d micros LATE",
-//                  microsDelta, expectedMicrosDeadline, (microsDelta - expectedMicrosDeadline));
+        } else if (nanosDelta > (expectedNanosDelta + (2 * mBurstPeriodNanos))) {
+            // In this case we do not update mMaxMeasuredLatenessNanos because it
+            // would force it too high.
+            // mMaxMeasuredLatenessNanos should range from 1 to 2 * mBurstPeriodNanos
+            //int32_t measuredLatenessNanos = (int32_t)(nanosDelta - expectedNanosDelta);
+            //ALOGD("%s() - STATE_RUNNING - #%d, lateness %d - max %d = %4d micros VERY LATE",
+                  //__func__,
+                  //mTimestampCount,
+                  //measuredLatenessNanos / 1000,
+                  //mMaxMeasuredLatenessNanos / 1000,
+                  //(measuredLatenessNanos - mMaxMeasuredLatenessNanos) / 1000
+                  //);
 
-            // When we are late it may be because of preemption in the kernel or
-            //  we may be drifting due to a slow HW clock.
-            setPositionAndTime(framePosition,  nanoTime - mMaxLatenessInNanos);
+            // This typically happens when we are modelling a service instead of a DSP.
+            setPositionAndTime(framePosition,  nanoTime - (2 * mBurstPeriodNanos));
+        } else if (nanosDelta > (expectedNanosDelta + mMaxMeasuredLatenessNanos)) {
+            //int32_t previousLatenessNanos = mMaxMeasuredLatenessNanos;
+            mMaxMeasuredLatenessNanos = (int32_t)(nanosDelta - expectedNanosDelta);
+
+            //ALOGD("%s() - STATE_RUNNING - #%d, newmax %d - oldmax %d = %4d micros LATE",
+                  //__func__,
+                  //mTimestampCount,
+                  //mMaxMeasuredLatenessNanos / 1000,
+                  //previousLatenessNanos / 1000,
+                  //(mMaxMeasuredLatenessNanos - previousLatenessNanos) / 1000
+                  //);
+
+            // When we are late, it may be because of preemption in the kernel,
+            // or timing jitter caused by resampling in the DSP,
+            // or we may be drifting due to a slow HW clock.
+            // We add slight drift value just in case there is actual long term drift
+            // forward caused by a slower clock.
+            // If the clock is faster than the model will get pushed earlier
+            // by the code in the preceding branch.
+            // The two opposing forces should allow the model to track the real clock
+            // over a long time.
+            int64_t driftingTime = mMarkerNanoTime + expectedNanosDelta + kDriftNanos;
+            setPositionAndTime(framePosition,  driftingTime);
+            //ALOGD("%s() - #%d, max lateness = %d micros",
+                  //__func__,
+                  //mTimestampCount,
+                  //(int) (mMaxMeasuredLatenessNanos / 1000));
         }
         break;
     default:
@@ -145,9 +178,12 @@
     update();
 }
 
+// Update expected lateness based on sampleRate and framesPerBurst
 void IsochronousClockModel::update() {
-    int64_t nanosLate = convertDeltaPositionToTime(mFramesPerBurst); // uses mSampleRate
-    mMaxLatenessInNanos = (nanosLate > MIN_LATENESS_NANOS) ? nanosLate : MIN_LATENESS_NANOS;
+    mBurstPeriodNanos = convertDeltaPositionToTime(mFramesPerBurst); // uses mSampleRate
+    // Timestamps may be late by up to a burst because we are randomly sampling the time period
+    // after the DSP position is actually updated.
+    mMaxMeasuredLatenessNanos = mBurstPeriodNanos;
 }
 
 int64_t IsochronousClockModel::convertDeltaPositionToTime(int64_t framesDelta) const {
@@ -190,11 +226,25 @@
     return position;
 }
 
+int32_t IsochronousClockModel::getLateTimeOffsetNanos() const {
+    // This will never be < 0 because mMaxLatenessNanos starts at
+    // mBurstPeriodNanos and only gets bigger.
+    return (mMaxMeasuredLatenessNanos - mBurstPeriodNanos) + kExtraLatenessNanos;
+}
+
+int64_t IsochronousClockModel::convertPositionToLatestTime(int64_t framePosition) const {
+    return convertPositionToTime(framePosition) + getLateTimeOffsetNanos();
+}
+
+int64_t IsochronousClockModel::convertLatestTimeToPosition(int64_t nanoTime) const {
+    return convertTimeToPosition(nanoTime - getLateTimeOffsetNanos());
+}
+
 void IsochronousClockModel::dump() const {
     ALOGD("mMarkerFramePosition = %lld", (long long) mMarkerFramePosition);
     ALOGD("mMarkerNanoTime      = %lld", (long long) mMarkerNanoTime);
     ALOGD("mSampleRate          = %6d", mSampleRate);
     ALOGD("mFramesPerBurst      = %6d", mFramesPerBurst);
-    ALOGD("mMaxLatenessInNanos  = %6d", mMaxLatenessInNanos);
+    ALOGD("mMaxMeasuredLatenessNanos = %6d", mMaxMeasuredLatenessNanos);
     ALOGD("mState               = %6d", mState);
 }
diff --git a/media/libaaudio/src/client/IsochronousClockModel.h b/media/libaaudio/src/client/IsochronousClockModel.h
index 46ca48e..582bf4e 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.h
+++ b/media/libaaudio/src/client/IsochronousClockModel.h
@@ -18,6 +18,7 @@
 #define ANDROID_AAUDIO_ISOCHRONOUS_CLOCK_MODEL_H
 
 #include <stdint.h>
+#include "utility/AudioClock.h"
 
 namespace aaudio {
 
@@ -79,6 +80,15 @@
     int64_t convertPositionToTime(int64_t framePosition) const;
 
     /**
+     * Calculate the latest estimated time that the stream will be at that position.
+     * The more jittery the clock is then the later this will be.
+     *
+     * @param framePosition
+     * @return time in nanoseconds
+     */
+    int64_t convertPositionToLatestTime(int64_t framePosition) const;
+
+    /**
      * Calculate an estimated position where the stream will be at the specified time.
      *
      * @param nanoTime time of interest
@@ -87,6 +97,18 @@
     int64_t convertTimeToPosition(int64_t nanoTime) const;
 
     /**
+     * Calculate the corresponding estimated position based on the specified time being
+     * the latest possible time.
+     *
+     * For the same nanoTime, this may return an earlier position than
+     * convertTimeToPosition().
+     *
+     * @param nanoTime
+     * @return position in frames
+     */
+    int64_t convertLatestTimeToPosition(int64_t nanoTime) const;
+
+    /**
      * @param framesDelta difference in frames
      * @return duration in nanoseconds
      */
@@ -101,6 +123,9 @@
     void dump() const;
 
 private:
+
+    int32_t getLateTimeOffsetNanos() const;
+
     enum clock_model_state_t {
         STATE_STOPPED,
         STATE_STARTING,
@@ -108,13 +133,23 @@
         STATE_RUNNING
     };
 
+    // Amount of time to drift forward when we get a late timestamp.
+    // This value was calculated to allow tracking of a clock with 50 ppm error.
+    static constexpr int32_t   kDriftNanos         =  10 * 1000;
+    // TODO review value of kExtraLatenessNanos
+    static constexpr int32_t   kExtraLatenessNanos = 100 * 1000;
+
     int64_t             mMarkerFramePosition;
     int64_t             mMarkerNanoTime;
     int32_t             mSampleRate;
     int32_t             mFramesPerBurst;
-    int32_t             mMaxLatenessInNanos;
+    int32_t             mBurstPeriodNanos;
+    // Includes mBurstPeriodNanos because we sample randomly over time.
+    int32_t             mMaxMeasuredLatenessNanos;
     clock_model_state_t mState;
 
+    int32_t             mTimestampCount = 0;
+
     void update();
 };
 
diff --git a/media/libaudioclient/include/media/AudioParameter.h b/media/libaudioclient/include/media/AudioParameter.h
index 24837e3..7469976 100644
--- a/media/libaudioclient/include/media/AudioParameter.h
+++ b/media/libaudioclient/include/media/AudioParameter.h
@@ -67,9 +67,9 @@
     //  keyAudioLanguagePreferred: Preferred audio language
     static const char * const keyAudioLanguagePreferred;
 
-    //  keyStreamConnect / Disconnect: value is an int in audio_devices_t
-    static const char * const keyStreamConnect;
-    static const char * const keyStreamDisconnect;
+    //  keyDeviceConnect / Disconnect: value is an int in audio_devices_t
+    static const char * const keyDeviceConnect;
+    static const char * const keyDeviceDisconnect;
 
     // For querying stream capabilities. All the returned values are lists.
     //   keyStreamSupportedFormats: audio_format_t
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index c19fcf6..0a2850f 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -302,6 +302,8 @@
         for (int i = 0; i < FIVEBAND_NUMBANDS; i++) {
             pContext->pBundledContext->bandGaindB[i] = EQNB_5BandSoftPresets[i];
         }
+        pContext->pBundledContext->effectProcessCalled      = 0;
+        pContext->pBundledContext->effectInDrain            = 0;
 
         ALOGV("\tEffectCreate - Calling LvmBundle_init");
         ret = LvmBundle_init(pContext);
@@ -394,6 +396,8 @@
 
     // Clear the instantiated flag for the effect
     // protect agains the case where an effect is un-instantiated without being disabled
+
+    int &effectInDrain = pContext->pBundledContext->effectInDrain;
     if(pContext->EffectType == LVM_BASS_BOOST) {
         ALOGV("\tEffectRelease LVM_BASS_BOOST Clearing global intstantiated flag");
         pSessionContext->bBassInstantiated = LVM_FALSE;
@@ -418,12 +422,16 @@
     } else if(pContext->EffectType == LVM_VOLUME) {
         ALOGV("\tEffectRelease LVM_VOLUME Clearing global intstantiated flag");
         pSessionContext->bVolumeInstantiated = LVM_FALSE;
-        if (pContext->pBundledContext->bVolumeEnabled == LVM_TRUE){
+        // There is no samplesToExitCount for volume so we also use the drain flag to check
+        // if we should decrement the effects enabled.
+        if (pContext->pBundledContext->bVolumeEnabled == LVM_TRUE
+                || (effectInDrain & 1 << LVM_VOLUME) != 0) {
             pContext->pBundledContext->NumberEffectsEnabled--;
         }
     } else {
         ALOGV("\tLVM_ERROR : EffectRelease : Unsupported effect\n\n\n\n\n\n\n");
     }
+    effectInDrain &= ~(1 << pContext->EffectType); // no need to drain if released
 
     // Disable effect, in this case ignore errors (return codes)
     // if an effect has already been disabled
@@ -3124,8 +3132,9 @@
 
 int Effect_setEnabled(EffectContext *pContext, bool enabled)
 {
-    ALOGV("\tEffect_setEnabled() type %d, enabled %d", pContext->EffectType, enabled);
-
+    ALOGV("%s effectType %d, enabled %d, currently enabled %d", __func__,
+            pContext->EffectType, enabled, pContext->pBundledContext->NumberEffectsEnabled);
+    int &effectInDrain = pContext->pBundledContext->effectInDrain;
     if (enabled) {
         // Bass boost or Virtualizer can be temporarily disabled if playing over device speaker due
         // to their nature.
@@ -3139,6 +3148,7 @@
                 if(pContext->pBundledContext->SamplesToExitCountBb <= 0){
                     pContext->pBundledContext->NumberEffectsEnabled++;
                 }
+                effectInDrain &= ~(1 << LVM_BASS_BOOST);
                 pContext->pBundledContext->SamplesToExitCountBb =
                      (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1);
                 pContext->pBundledContext->bBassEnabled = LVM_TRUE;
@@ -3152,6 +3162,7 @@
                 if(pContext->pBundledContext->SamplesToExitCountEq <= 0){
                     pContext->pBundledContext->NumberEffectsEnabled++;
                 }
+                effectInDrain &= ~(1 << LVM_EQUALIZER);
                 pContext->pBundledContext->SamplesToExitCountEq =
                      (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1);
                 pContext->pBundledContext->bEqualizerEnabled = LVM_TRUE;
@@ -3164,6 +3175,7 @@
                 if(pContext->pBundledContext->SamplesToExitCountVirt <= 0){
                     pContext->pBundledContext->NumberEffectsEnabled++;
                 }
+                effectInDrain &= ~(1 << LVM_VIRTUALIZER);
                 pContext->pBundledContext->SamplesToExitCountVirt =
                      (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1);
                 pContext->pBundledContext->bVirtualizerEnabled = LVM_TRUE;
@@ -3174,7 +3186,10 @@
                     ALOGV("\tEffect_setEnabled() LVM_VOLUME is already enabled");
                     return -EINVAL;
                 }
-                pContext->pBundledContext->NumberEffectsEnabled++;
+                if ((effectInDrain & 1 << LVM_VOLUME) == 0) {
+                    pContext->pBundledContext->NumberEffectsEnabled++;
+                }
+                effectInDrain &= ~(1 << LVM_VOLUME);
                 pContext->pBundledContext->bVolumeEnabled = LVM_TRUE;
                 break;
             default:
@@ -3192,6 +3207,7 @@
                     return -EINVAL;
                 }
                 pContext->pBundledContext->bBassEnabled = LVM_FALSE;
+                effectInDrain |= 1 << LVM_BASS_BOOST;
                 break;
             case LVM_EQUALIZER:
                 if (pContext->pBundledContext->bEqualizerEnabled == LVM_FALSE) {
@@ -3199,6 +3215,7 @@
                     return -EINVAL;
                 }
                 pContext->pBundledContext->bEqualizerEnabled = LVM_FALSE;
+                effectInDrain |= 1 << LVM_EQUALIZER;
                 break;
             case LVM_VIRTUALIZER:
                 if (pContext->pBundledContext->bVirtualizerEnabled == LVM_FALSE) {
@@ -3206,6 +3223,7 @@
                     return -EINVAL;
                 }
                 pContext->pBundledContext->bVirtualizerEnabled = LVM_FALSE;
+                effectInDrain |= 1 << LVM_VIRTUALIZER;
                 break;
             case LVM_VOLUME:
                 if (pContext->pBundledContext->bVolumeEnabled == LVM_FALSE) {
@@ -3213,6 +3231,7 @@
                     return -EINVAL;
                 }
                 pContext->pBundledContext->bVolumeEnabled = LVM_FALSE;
+                effectInDrain |= 1 << LVM_VOLUME;
                 break;
             default:
                 ALOGV("\tEffect_setEnabled() invalid effect type");
@@ -3283,6 +3302,38 @@
         ALOGV("\tLVM_ERROR : Effect_process() ERROR NULL INPUT POINTER OR FRAME COUNT IS WRONG");
         return -EINVAL;
     }
+
+    int &effectProcessCalled = pContext->pBundledContext->effectProcessCalled;
+    int &effectInDrain = pContext->pBundledContext->effectInDrain;
+    if ((effectProcessCalled & 1 << pContext->EffectType) != 0) {
+        ALOGW("Effect %d already called", pContext->EffectType);
+        const int undrainedEffects = effectInDrain & ~effectProcessCalled;
+        if ((undrainedEffects & 1 << LVM_BASS_BOOST) != 0) {
+            ALOGW("Draining BASS_BOOST");
+            pContext->pBundledContext->SamplesToExitCountBb = 0;
+            --pContext->pBundledContext->NumberEffectsEnabled;
+            effectInDrain &= ~(1 << LVM_BASS_BOOST);
+        }
+        if ((undrainedEffects & 1 << LVM_EQUALIZER) != 0) {
+            ALOGW("Draining EQUALIZER");
+            pContext->pBundledContext->SamplesToExitCountEq = 0;
+            --pContext->pBundledContext->NumberEffectsEnabled;
+            effectInDrain &= ~(1 << LVM_EQUALIZER);
+        }
+        if ((undrainedEffects & 1 << LVM_VIRTUALIZER) != 0) {
+            ALOGW("Draining VIRTUALIZER");
+            pContext->pBundledContext->SamplesToExitCountVirt = 0;
+            --pContext->pBundledContext->NumberEffectsEnabled;
+            effectInDrain &= ~(1 << LVM_VIRTUALIZER);
+        }
+        if ((undrainedEffects & 1 << LVM_VOLUME) != 0) {
+            ALOGW("Draining VOLUME");
+            --pContext->pBundledContext->NumberEffectsEnabled;
+            effectInDrain &= ~(1 << LVM_VOLUME);
+        }
+    }
+    effectProcessCalled |= 1 << pContext->EffectType;
+
     if ((pContext->pBundledContext->bBassEnabled == LVM_FALSE)&&
         (pContext->EffectType == LVM_BASS_BOOST)){
         //ALOGV("\tEffect_process() LVM_BASS_BOOST Effect is not enabled");
@@ -3291,9 +3342,12 @@
             //ALOGV("\tEffect_process: Waiting to turn off BASS_BOOST, %d samples left",
             //    pContext->pBundledContext->SamplesToExitCountBb);
         }
-        if(pContext->pBundledContext->SamplesToExitCountBb <= 0) {
+        if (pContext->pBundledContext->SamplesToExitCountBb <= 0) {
             status = -ENODATA;
-            pContext->pBundledContext->NumberEffectsEnabled--;
+            if ((effectInDrain & 1 << LVM_BASS_BOOST) != 0) {
+                pContext->pBundledContext->NumberEffectsEnabled--;
+                effectInDrain &= ~(1 << LVM_BASS_BOOST);
+            }
             ALOGV("\tEffect_process() this is the last frame for LVM_BASS_BOOST");
         }
     }
@@ -3301,7 +3355,10 @@
         (pContext->EffectType == LVM_VOLUME)){
         //ALOGV("\tEffect_process() LVM_VOLUME Effect is not enabled");
         status = -ENODATA;
-        pContext->pBundledContext->NumberEffectsEnabled--;
+        if ((effectInDrain & 1 << LVM_VOLUME) != 0) {
+            pContext->pBundledContext->NumberEffectsEnabled--;
+            effectInDrain &= ~(1 << LVM_VOLUME);
+        }
     }
     if ((pContext->pBundledContext->bEqualizerEnabled == LVM_FALSE)&&
         (pContext->EffectType == LVM_EQUALIZER)){
@@ -3311,9 +3368,12 @@
             //ALOGV("\tEffect_process: Waiting to turn off EQUALIZER, %d samples left",
             //    pContext->pBundledContext->SamplesToExitCountEq);
         }
-        if(pContext->pBundledContext->SamplesToExitCountEq <= 0) {
+        if (pContext->pBundledContext->SamplesToExitCountEq <= 0) {
             status = -ENODATA;
-            pContext->pBundledContext->NumberEffectsEnabled--;
+            if ((effectInDrain & 1 << LVM_EQUALIZER) != 0) {
+                pContext->pBundledContext->NumberEffectsEnabled--;
+                effectInDrain &= ~(1 << LVM_EQUALIZER);
+            }
             ALOGV("\tEffect_process() this is the last frame for LVM_EQUALIZER");
         }
     }
@@ -3326,9 +3386,12 @@
             //ALOGV("\tEffect_process: Waiting for to turn off VIRTUALIZER, %d samples left",
             //    pContext->pBundledContext->SamplesToExitCountVirt);
         }
-        if(pContext->pBundledContext->SamplesToExitCountVirt <= 0) {
+        if (pContext->pBundledContext->SamplesToExitCountVirt <= 0) {
             status = -ENODATA;
-            pContext->pBundledContext->NumberEffectsEnabled--;
+            if ((effectInDrain & 1 << LVM_VIRTUALIZER) != 0) {
+                pContext->pBundledContext->NumberEffectsEnabled--;
+                effectInDrain &= ~(1 << LVM_VIRTUALIZER);
+            }
             ALOGV("\tEffect_process() this is the last frame for LVM_VIRTUALIZER");
         }
     }
@@ -3337,8 +3400,18 @@
         pContext->pBundledContext->NumberEffectsCalled++;
     }
 
-    if(pContext->pBundledContext->NumberEffectsCalled ==
-       pContext->pBundledContext->NumberEffectsEnabled){
+    if (pContext->pBundledContext->NumberEffectsCalled >=
+            pContext->pBundledContext->NumberEffectsEnabled) {
+
+        // We expect the # effects called to be equal to # effects enabled in sequence (including
+        // draining effects).  Warn if this is not the case due to inconsistent calls.
+        ALOGW_IF(pContext->pBundledContext->NumberEffectsCalled >
+                pContext->pBundledContext->NumberEffectsEnabled,
+                "%s Number of effects called %d is greater than number of effects enabled %d",
+                __func__, pContext->pBundledContext->NumberEffectsCalled,
+                pContext->pBundledContext->NumberEffectsEnabled);
+        effectProcessCalled = 0; // reset our consistency check.
+
         //ALOGV("\tEffect_process     Calling process with %d effects enabled, %d called: Effect %d",
         //pContext->pBundledContext->NumberEffectsEnabled,
         //pContext->pBundledContext->NumberEffectsCalled, pContext->EffectType);
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
index 6af4554..e4aacd0 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
@@ -110,6 +110,14 @@
 #ifdef SUPPORT_MC
     LVM_INT32                       ChMask;
 #endif
+
+    /* Bitmask whether drain is in progress due to disabling the effect.
+       The corresponding bit to an effect is set by 1 << lvm_effect_en. */
+    int                             effectInDrain;
+
+    /* Bitmask whether process() was called for a particular effect.
+       The corresponding bit to an effect is set by 1 << lvm_effect_en. */
+    int                             effectProcessCalled;
 };
 
 /* SessionContext : One session */
diff --git a/media/libmedia/AudioParameter.cpp b/media/libmedia/AudioParameter.cpp
index 1c95e27..060b92b 100644
--- a/media/libmedia/AudioParameter.cpp
+++ b/media/libmedia/AudioParameter.cpp
@@ -40,8 +40,8 @@
         AUDIO_PARAMETER_KEY_AUDIO_LANGUAGE_PREFERRED;
 const char * const AudioParameter::keyMonoOutput = AUDIO_PARAMETER_MONO_OUTPUT;
 const char * const AudioParameter::keyStreamHwAvSync = AUDIO_PARAMETER_STREAM_HW_AV_SYNC;
-const char * const AudioParameter::keyStreamConnect = AUDIO_PARAMETER_DEVICE_CONNECT;
-const char * const AudioParameter::keyStreamDisconnect = AUDIO_PARAMETER_DEVICE_DISCONNECT;
+const char * const AudioParameter::keyDeviceConnect = AUDIO_PARAMETER_DEVICE_CONNECT;
+const char * const AudioParameter::keyDeviceDisconnect = AUDIO_PARAMETER_DEVICE_DISCONNECT;
 const char * const AudioParameter::keyStreamSupportedFormats = AUDIO_PARAMETER_STREAM_SUP_FORMATS;
 const char * const AudioParameter::keyStreamSupportedChannels = AUDIO_PARAMETER_STREAM_SUP_CHANNELS;
 const char * const AudioParameter::keyStreamSupportedSamplingRates =
diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp
index 028bea1..d95bc8e 100644
--- a/media/libmedia/IMediaMetadataRetriever.cpp
+++ b/media/libmedia/IMediaMetadataRetriever.cpp
@@ -213,15 +213,14 @@
         return interface_cast<IMemory>(reply.readStrongBinder());
     }
 
-    status_t getFrameAtIndex(std::vector<sp<IMemory> > *frames,
-            int frameIndex, int numFrames, int colorFormat, bool metaOnly)
+    sp<IMemory> getFrameAtIndex(
+            int index, int colorFormat, bool metaOnly)
     {
-        ALOGV("getFrameAtIndex: frameIndex(%d), numFrames(%d), colorFormat(%d) metaOnly(%d)",
-                frameIndex, numFrames, colorFormat, metaOnly);
+        ALOGV("getFrameAtIndex: index(%d), colorFormat(%d) metaOnly(%d)",
+                index, colorFormat, metaOnly);
         Parcel data, reply;
         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
-        data.writeInt32(frameIndex);
-        data.writeInt32(numFrames);
+        data.writeInt32(index);
         data.writeInt32(colorFormat);
         data.writeInt32(metaOnly);
 #ifndef DISABLE_GROUP_SCHEDULE_HACK
@@ -230,16 +229,9 @@
         remote()->transact(GET_FRAME_AT_INDEX, data, &reply);
         status_t ret = reply.readInt32();
         if (ret != NO_ERROR) {
-            return ret;
+            return NULL;
         }
-        int retNumFrames = reply.readInt32();
-        if (retNumFrames < numFrames) {
-            numFrames = retNumFrames;
-        }
-        for (int i = 0; i < numFrames; i++) {
-            frames->push_back(interface_cast<IMemory>(reply.readStrongBinder()));
-        }
-        return OK;
+        return interface_cast<IMemory>(reply.readStrongBinder());
     }
 
     sp<IMemory> extractAlbumArt()
@@ -442,24 +434,20 @@
 
         case GET_FRAME_AT_INDEX: {
             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
-            int frameIndex = data.readInt32();
-            int numFrames = data.readInt32();
+            int index = data.readInt32();
             int colorFormat = data.readInt32();
             bool metaOnly = (data.readInt32() != 0);
-            ALOGV("getFrameAtIndex: frameIndex(%d), numFrames(%d), colorFormat(%d), metaOnly(%d)",
-                    frameIndex, numFrames, colorFormat, metaOnly);
+            ALOGV("getFrameAtIndex: index(%d), colorFormat(%d), metaOnly(%d)",
+                    index, colorFormat, metaOnly);
 #ifndef DISABLE_GROUP_SCHEDULE_HACK
             setSchedPolicy(data);
 #endif
-            std::vector<sp<IMemory> > frames;
-            status_t err = getFrameAtIndex(
-                    &frames, frameIndex, numFrames, colorFormat, metaOnly);
-            reply->writeInt32(err);
-            if (OK == err) {
-                reply->writeInt32(frames.size());
-                for (size_t i = 0; i < frames.size(); i++) {
-                    reply->writeStrongBinder(IInterface::asBinder(frames[i]));
-                }
+            sp<IMemory> frame = getFrameAtIndex(index, colorFormat, metaOnly);
+            if (frame != nullptr) {  // Don't send NULL across the binder interface
+                reply->writeInt32(NO_ERROR);
+                reply->writeStrongBinder(IInterface::asBinder(frame));
+            } else {
+                reply->writeInt32(UNKNOWN_ERROR);
             }
 #ifndef DISABLE_GROUP_SCHEDULE_HACK
             restoreSchedPolicy();
diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp
index cb8d375..2bf0802 100644
--- a/media/libmedia/Visualizer.cpp
+++ b/media/libmedia/Visualizer.cpp
@@ -77,10 +77,13 @@
     if (t != 0) {
         if (enabled) {
             if (t->exitPending()) {
+                mCaptureLock.unlock();
                 if (t->requestExitAndWait() == WOULD_BLOCK) {
+                    mCaptureLock.lock();
                     ALOGE("Visualizer::enable() called from thread");
                     return INVALID_OPERATION;
                 }
+                mCaptureLock.lock();
             }
         }
         t->mLock.lock();
diff --git a/media/libmedia/include/media/IMediaMetadataRetriever.h b/media/libmedia/include/media/IMediaMetadataRetriever.h
index c6f422d..28d2192 100644
--- a/media/libmedia/include/media/IMediaMetadataRetriever.h
+++ b/media/libmedia/include/media/IMediaMetadataRetriever.h
@@ -48,9 +48,8 @@
             int index, int colorFormat, bool metaOnly, bool thumbnail) = 0;
     virtual sp<IMemory>     getImageRectAtIndex(
             int index, int colorFormat, int left, int top, int right, int bottom) = 0;
-    virtual status_t        getFrameAtIndex(
-            std::vector<sp<IMemory> > *frames,
-            int frameIndex, int numFrames, int colorFormat, bool metaOnly) = 0;
+    virtual sp<IMemory>     getFrameAtIndex(
+            int index, int colorFormat, bool metaOnly) = 0;
     virtual sp<IMemory>     extractAlbumArt() = 0;
     virtual const char*     extractMetadata(int keyCode) = 0;
 };
diff --git a/media/libmedia/include/media/MediaMetadataRetrieverInterface.h b/media/libmedia/include/media/MediaMetadataRetrieverInterface.h
index 98d300f..37dc401 100644
--- a/media/libmedia/include/media/MediaMetadataRetrieverInterface.h
+++ b/media/libmedia/include/media/MediaMetadataRetrieverInterface.h
@@ -49,9 +49,8 @@
             int index, int colorFormat, bool metaOnly, bool thumbnail) = 0;
     virtual sp<IMemory> getImageRectAtIndex(
             int index, int colorFormat, int left, int top, int right, int bottom) = 0;
-    virtual status_t getFrameAtIndex(
-            std::vector<sp<IMemory> >* frames,
-            int frameIndex, int numFrames, int colorFormat, bool metaOnly) = 0;
+    virtual sp<IMemory> getFrameAtIndex(
+            int frameIndex, int colorFormat, bool metaOnly) = 0;
     virtual MediaAlbumArt* extractAlbumArt() = 0;
     virtual const char* extractMetadata(int keyCode) = 0;
 };
diff --git a/media/libmedia/include/media/mediametadataretriever.h b/media/libmedia/include/media/mediametadataretriever.h
index d29e97d..138a014 100644
--- a/media/libmedia/include/media/mediametadataretriever.h
+++ b/media/libmedia/include/media/mediametadataretriever.h
@@ -98,9 +98,8 @@
             int colorFormat = HAL_PIXEL_FORMAT_RGB_565, bool metaOnly = false, bool thumbnail = false);
     sp<IMemory> getImageRectAtIndex(
             int index, int colorFormat, int left, int top, int right, int bottom);
-    status_t getFrameAtIndex(
-            std::vector<sp<IMemory> > *frames, int frameIndex, int numFrames = 1,
-            int colorFormat = HAL_PIXEL_FORMAT_RGB_565, bool metaOnly = false);
+    sp<IMemory>  getFrameAtIndex(
+            int index, int colorFormat = HAL_PIXEL_FORMAT_RGB_565, bool metaOnly = false);
     sp<IMemory> extractAlbumArt();
     const char* extractMetadata(int keyCode);
 
diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp
index e61b04d..2ae76b3 100644
--- a/media/libmedia/mediametadataretriever.cpp
+++ b/media/libmedia/mediametadataretriever.cpp
@@ -179,18 +179,16 @@
             index, colorFormat, left, top, right, bottom);
 }
 
-status_t MediaMetadataRetriever::getFrameAtIndex(
-        std::vector<sp<IMemory> > *frames,
-        int frameIndex, int numFrames, int colorFormat, bool metaOnly) {
-    ALOGV("getFrameAtIndex: frameIndex(%d), numFrames(%d), colorFormat(%d) metaOnly(%d)",
-            frameIndex, numFrames, colorFormat, metaOnly);
+sp<IMemory>  MediaMetadataRetriever::getFrameAtIndex(
+        int index, int colorFormat, bool metaOnly) {
+    ALOGV("getFrameAtIndex: index(%d), colorFormat(%d) metaOnly(%d)",
+            index, colorFormat, metaOnly);
     Mutex::Autolock _l(mLock);
     if (mRetriever == 0) {
         ALOGE("retriever is not initialized");
-        return INVALID_OPERATION;
+        return NULL;
     }
-    return mRetriever->getFrameAtIndex(
-            frames, frameIndex, numFrames, colorFormat, metaOnly);
+    return mRetriever->getFrameAtIndex(index, colorFormat, metaOnly);
 }
 
 const char* MediaMetadataRetriever::extractMetadata(int keyCode)
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index 40b17bf..4a3c65e 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -242,31 +242,27 @@
     sp<IMemory> frame = mRetriever->getImageRectAtIndex(
             index, colorFormat, left, top, right, bottom);
     if (frame == NULL) {
-        ALOGE("failed to extract image");
-        return NULL;
+        ALOGE("failed to extract image at index %d", index);
     }
     return frame;
 }
 
-status_t MetadataRetrieverClient::getFrameAtIndex(
-            std::vector<sp<IMemory> > *frames,
-            int frameIndex, int numFrames, int colorFormat, bool metaOnly) {
-    ALOGV("getFrameAtIndex: frameIndex(%d), numFrames(%d), colorFormat(%d), metaOnly(%d)",
-            frameIndex, numFrames, colorFormat, metaOnly);
+sp<IMemory> MetadataRetrieverClient::getFrameAtIndex(
+            int index, int colorFormat, bool metaOnly) {
+    ALOGV("getFrameAtIndex: index(%d), colorFormat(%d), metaOnly(%d)",
+            index, colorFormat, metaOnly);
     Mutex::Autolock lock(mLock);
     Mutex::Autolock glock(sLock);
     if (mRetriever == NULL) {
         ALOGE("retriever is not initialized");
-        return INVALID_OPERATION;
+        return NULL;
     }
 
-    status_t err = mRetriever->getFrameAtIndex(
-            frames, frameIndex, numFrames, colorFormat, metaOnly);
-    if (err != OK) {
-        frames->clear();
-        return err;
+    sp<IMemory> frame = mRetriever->getFrameAtIndex(index, colorFormat, metaOnly);
+    if (frame == NULL) {
+        ALOGE("failed to extract frame at index %d", index);
     }
-    return OK;
+    return frame;
 }
 
 sp<IMemory> MetadataRetrieverClient::extractAlbumArt()
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.h b/media/libmediaplayerservice/MetadataRetrieverClient.h
index 272d093..8020441 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.h
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.h
@@ -56,9 +56,8 @@
             int index, int colorFormat, bool metaOnly, bool thumbnail);
     virtual sp<IMemory>             getImageRectAtIndex(
             int index, int colorFormat, int left, int top, int right, int bottom);
-    virtual status_t getFrameAtIndex(
-                std::vector<sp<IMemory> > *frames,
-                int frameIndex, int numFrames, int colorFormat, bool metaOnly);
+    virtual sp<IMemory>             getFrameAtIndex(
+            int index, int colorFormat, bool metaOnly);
     virtual sp<IMemory>             extractAlbumArt();
     virtual const char*             extractMetadata(int keyCode);
 
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 0f72c0d..4653711 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -404,8 +404,8 @@
             sp<DataSource> dataSource = DataSourceFactory::CreateFromURI(
                    mHTTPService, uri, &mUriHeaders, &contentType,
                    static_cast<HTTPBase *>(mHttpSource.get()));
-            mLock.lock();
             mDisconnectLock.lock();
+            mLock.lock();
             if (!mDisconnected) {
                 mDataSource = dataSource;
             }
@@ -1578,7 +1578,7 @@
         }
 
         if (mPreparing) {
-            notifyPreparedAndCleanup(finalStatus);
+            notifyPreparedAndCleanup(finalStatus == ERROR_END_OF_STREAM ? OK : finalStatus);
             mPreparing = false;
         } else if (mSentPauseOnBuffering) {
             sendCacheStats();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 2f0da2d..ee463ce 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -106,16 +106,17 @@
     releaseAndResetMediaBuffers();
 }
 
-sp<AMessage> NuPlayer::Decoder::getStats() const {
+sp<AMessage> NuPlayer::Decoder::getStats() {
 
+    Mutex::Autolock autolock(mStatsLock);
     mStats->setInt64("frames-total", mNumFramesTotal);
     mStats->setInt64("frames-dropped-input", mNumInputFramesDropped);
     mStats->setInt64("frames-dropped-output", mNumOutputFramesDropped);
     mStats->setFloat("frame-rate-total", mFrameRateTotal);
 
-    // i'm mutexed right now.
     // make our own copy, so we aren't victim to any later changes.
     sp<AMessage> copiedStats = mStats->dup();
+
     return copiedStats;
 }
 
@@ -362,13 +363,17 @@
     CHECK_EQ((status_t)OK, mCodec->getOutputFormat(&mOutputFormat));
     CHECK_EQ((status_t)OK, mCodec->getInputFormat(&mInputFormat));
 
-    mStats->setString("mime", mime.c_str());
-    mStats->setString("component-name", mComponentName.c_str());
+    {
+        Mutex::Autolock autolock(mStatsLock);
+        mStats->setString("mime", mime.c_str());
+        mStats->setString("component-name", mComponentName.c_str());
+    }
 
     if (!mIsAudio) {
         int32_t width, height;
         if (mOutputFormat->findInt32("width", &width)
                 && mOutputFormat->findInt32("height", &height)) {
+            Mutex::Autolock autolock(mStatsLock);
             mStats->setInt32("width", width);
             mStats->setInt32("height", height);
         }
@@ -799,6 +804,7 @@
         int32_t width, height;
         if (format->findInt32("width", &width)
                 && format->findInt32("height", &height)) {
+            Mutex::Autolock autolock(mStatsLock);
             mStats->setInt32("width", width);
             mStats->setInt32("height", height);
         }
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 3da2f0b..4a52b0c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -34,7 +34,7 @@
             const sp<Surface> &surface = NULL,
             const sp<CCDecoder> &ccDecoder = NULL);
 
-    virtual sp<AMessage> getStats() const;
+    virtual sp<AMessage> getStats();
 
     // sets the output surface of video decoders.
     virtual status_t setVideoSurface(const sp<Surface> &surface);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
index d44c396..a3e0046 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
@@ -47,7 +47,7 @@
     void signalResume(bool notifyComplete);
     void initiateShutdown();
 
-    virtual sp<AMessage> getStats() const {
+    virtual sp<AMessage> getStats() {
         return mStats;
     }
 
@@ -88,6 +88,7 @@
     int32_t mBufferGeneration;
     bool mPaused;
     sp<AMessage> mStats;
+    Mutex mStatsLock;
 
 private:
     enum {
diff --git a/media/libstagefright/FrameDecoder.cpp b/media/libstagefright/FrameDecoder.cpp
index 18a6bd8..7c620a0 100644
--- a/media/libstagefright/FrameDecoder.cpp
+++ b/media/libstagefright/FrameDecoder.cpp
@@ -195,14 +195,14 @@
 }
 
 status_t FrameDecoder::init(
-        int64_t frameTimeUs, size_t numFrames, int option, int colorFormat) {
+        int64_t frameTimeUs, int option, int colorFormat) {
     if (!getDstColorFormat(
             (android_pixel_format_t)colorFormat, &mDstFormat, &mDstBpp)) {
         return ERROR_UNSUPPORTED;
     }
 
     sp<AMessage> videoFormat = onGetFormatAndSeekOptions(
-            frameTimeUs, numFrames, option, &mReadOptions);
+            frameTimeUs, option, &mReadOptions);
     if (videoFormat == NULL) {
         ALOGE("video format or seek mode not supported");
         return ERROR_UNSUPPORTED;
@@ -253,19 +253,7 @@
         return NULL;
     }
 
-    return mFrames.size() > 0 ? mFrames[0] : NULL;
-}
-
-status_t FrameDecoder::extractFrames(std::vector<sp<IMemory> >* frames) {
-    status_t err = extractInternal();
-    if (err != OK) {
-        return err;
-    }
-
-    for (size_t i = 0; i < mFrames.size(); i++) {
-        frames->push_back(mFrames[i]);
-    }
-    return OK;
+    return mFrameMemory;
 }
 
 status_t FrameDecoder::extractInternal() {
@@ -404,22 +392,20 @@
         const sp<MetaData> &trackMeta,
         const sp<IMediaSource> &source)
     : FrameDecoder(componentName, trackMeta, source),
+      mFrame(NULL),
       mIsAvcOrHevc(false),
       mSeekMode(MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC),
-      mTargetTimeUs(-1LL),
-      mNumFrames(0),
-      mNumFramesDecoded(0) {
+      mTargetTimeUs(-1LL) {
 }
 
 sp<AMessage> VideoFrameDecoder::onGetFormatAndSeekOptions(
-        int64_t frameTimeUs, size_t numFrames, int seekMode, MediaSource::ReadOptions *options) {
+        int64_t frameTimeUs, int seekMode, MediaSource::ReadOptions *options) {
     mSeekMode = static_cast<MediaSource::ReadOptions::SeekMode>(seekMode);
     if (mSeekMode < MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC ||
             mSeekMode > MediaSource::ReadOptions::SEEK_FRAME_INDEX) {
         ALOGE("Unknown seek mode: %d", mSeekMode);
         return NULL;
     }
-    mNumFrames = numFrames;
 
     const char *mime;
     if (!trackMeta()->findCString(kKeyMIMEType, &mime)) {
@@ -495,7 +481,7 @@
         return OK;
     }
 
-    *done = (++mNumFramesDecoded >= mNumFrames);
+    *done = true;
 
     if (outputFormat == NULL) {
         return ERROR_MALFORMED;
@@ -518,15 +504,18 @@
         crop_bottom = height - 1;
     }
 
-    sp<IMemory> frameMem = allocVideoFrame(
-            trackMeta(),
-            (crop_right - crop_left + 1),
-            (crop_bottom - crop_top + 1),
-            0,
-            0,
-            dstBpp());
-    addFrame(frameMem);
-    VideoFrame* frame = static_cast<VideoFrame*>(frameMem->pointer());
+    if (mFrame == NULL) {
+        sp<IMemory> frameMem = allocVideoFrame(
+                trackMeta(),
+                (crop_right - crop_left + 1),
+                (crop_bottom - crop_top + 1),
+                0,
+                0,
+                dstBpp());
+        mFrame = static_cast<VideoFrame*>(frameMem->pointer());
+
+        setFrame(frameMem);
+    }
 
     ColorConverter converter((OMX_COLOR_FORMATTYPE)srcFormat, dstFormat());
 
@@ -547,8 +536,8 @@
                 (const uint8_t *)videoFrameBuffer->data(),
                 width, height, stride,
                 crop_left, crop_top, crop_right, crop_bottom,
-                frame->getFlattenedData(),
-                frame->mWidth, frame->mHeight, frame->mRowBytes,
+                mFrame->getFlattenedData(),
+                mFrame->mWidth, mFrame->mHeight, mFrame->mRowBytes,
                 crop_left, crop_top, crop_right, crop_bottom);
         return OK;
     }
@@ -577,8 +566,7 @@
 }
 
 sp<AMessage> ImageDecoder::onGetFormatAndSeekOptions(
-        int64_t frameTimeUs, size_t /*numFrames*/,
-        int /*seekMode*/, MediaSource::ReadOptions *options) {
+        int64_t frameTimeUs, int /*seekMode*/, MediaSource::ReadOptions *options) {
     sp<MetaData> overrideMeta;
     if (frameTimeUs < 0) {
         uint32_t type;
@@ -705,7 +693,7 @@
                 trackMeta(), mWidth, mHeight, mTileWidth, mTileHeight, dstBpp());
         mFrame = static_cast<VideoFrame*>(frameMem->pointer());
 
-        addFrame(frameMem);
+        setFrame(frameMem);
     }
 
     int32_t srcFormat;
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index e56750b..f579e9d 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -1110,12 +1110,10 @@
             reset();
         }
         if (!isResourceError(err)) {
-            if (err == OK) {
-                disableLegacyBufferDropPostQ(surface);
-            }
             break;
         }
     }
+
     return err;
 }
 
@@ -1178,11 +1176,7 @@
     msg->setObject("surface", surface);
 
     sp<AMessage> response;
-    status_t result = PostAndAwaitResponse(msg, &response);
-    if (result == OK) {
-        disableLegacyBufferDropPostQ(surface);
-    }
-    return result;
+    return PostAndAwaitResponse(msg, &response);
 }
 
 status_t MediaCodec::createInputSurface(
@@ -2005,6 +1999,13 @@
 
                     CHECK(msg->findMessage("input-format", &mInputFormat));
                     CHECK(msg->findMessage("output-format", &mOutputFormat));
+
+                    // limit to confirming the opt-in behavior to minimize any behavioral change
+                    if (mSurface != nullptr && !mAllowFrameDroppingBySurface) {
+                        // signal frame dropping mode in the input format as this may also be
+                        // meaningful and confusing for an encoder in a transcoder scenario
+                        mInputFormat->setInt32("allow-frame-drop", mAllowFrameDroppingBySurface);
+                    }
                     ALOGV("[%s] configured as input format: %s, output format: %s",
                             mComponentName.c_str(),
                             mInputFormat->debugString(4).c_str(),
@@ -2437,6 +2438,11 @@
             }
 
             if (obj != NULL) {
+                if (!format->findInt32("allow-frame-drop", &mAllowFrameDroppingBySurface)) {
+                    // allow frame dropping by surface by default
+                    mAllowFrameDroppingBySurface = true;
+                }
+
                 format->setObject("native-window", obj);
                 status_t err = handleSetSurface(static_cast<Surface *>(obj.get()));
                 if (err != OK) {
@@ -2444,6 +2450,9 @@
                     break;
                 }
             } else {
+                // we are not using surface so this variable is not used, but initialize sensibly anyway
+                mAllowFrameDroppingBySurface = false;
+
                 handleSetSurface(NULL);
             }
 
@@ -3436,6 +3445,10 @@
 
         if (err != OK) {
             ALOGE("nativeWindowConnect returned an error: %s (%d)", strerror(-err), err);
+        } else {
+            if (!mAllowFrameDroppingBySurface) {
+                disableLegacyBufferDropPostQ(surface);
+            }
         }
     }
     // do not return ALREADY_EXISTS unless surfaces are the same
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index 3d58d4b..a267f7e 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -170,6 +170,7 @@
 sp<IMediaCodecList> MediaCodecList::sRemoteList;
 
 sp<MediaCodecList::BinderDeathObserver> MediaCodecList::sBinderDeathObserver;
+sp<IBinder> MediaCodecList::sMediaPlayer;  // kept since linked to death
 
 void MediaCodecList::BinderDeathObserver::binderDied(const wp<IBinder> &who __unused) {
     Mutex::Autolock _l(sRemoteInitMutex);
@@ -181,15 +182,14 @@
 sp<IMediaCodecList> MediaCodecList::getInstance() {
     Mutex::Autolock _l(sRemoteInitMutex);
     if (sRemoteList == nullptr) {
-        sp<IBinder> binder =
-            defaultServiceManager()->getService(String16("media.player"));
+        sMediaPlayer = defaultServiceManager()->getService(String16("media.player"));
         sp<IMediaPlayerService> service =
-            interface_cast<IMediaPlayerService>(binder);
+            interface_cast<IMediaPlayerService>(sMediaPlayer);
         if (service.get() != nullptr) {
             sRemoteList = service->getCodecList();
             if (sRemoteList != nullptr) {
                 sBinderDeathObserver = new BinderDeathObserver();
-                binder->linkToDeath(sBinderDeathObserver.get());
+                sMediaPlayer->linkToDeath(sBinderDeathObserver.get());
             }
         }
         if (sRemoteList == nullptr) {
diff --git a/media/libstagefright/OmxInfoBuilder.cpp b/media/libstagefright/OmxInfoBuilder.cpp
index 8910463..79ffdeb 100644
--- a/media/libstagefright/OmxInfoBuilder.cpp
+++ b/media/libstagefright/OmxInfoBuilder.cpp
@@ -193,7 +193,7 @@
                 // treat OMX.google codecs as non-hardware-accelerated and non-vendor
                 if (!isSoftware) {
                     attrs |= MediaCodecInfo::kFlagIsVendor;
-                    if (std::count_if(
+                    if (!std::count_if(
                             node.attributes.begin(), node.attributes.end(),
                             [](const IOmxStore::Attribute &i) -> bool {
                                 return i.key == "attribute::software-codec";
diff --git a/media/libstagefright/SimpleDecodingSource.cpp b/media/libstagefright/SimpleDecodingSource.cpp
index babdc7a..8b6262f 100644
--- a/media/libstagefright/SimpleDecodingSource.cpp
+++ b/media/libstagefright/SimpleDecodingSource.cpp
@@ -36,7 +36,7 @@
 using namespace android;
 
 const int64_t kTimeoutWaitForOutputUs = 500000; // 0.5 seconds
-const int64_t kTimeoutWaitForInputUs = 5000; // 5 milliseconds
+const int64_t kTimeoutWaitForInputUs = 0; // don't wait
 const int kTimeoutMaxRetries = 20;
 
 //static
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index fa3d372..6f536a9 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -44,7 +44,7 @@
 StagefrightMetadataRetriever::StagefrightMetadataRetriever()
     : mParsedMetaData(false),
       mAlbumArt(NULL),
-      mLastImageIndex(-1) {
+      mLastDecodedIndex(-1) {
     ALOGV("StagefrightMetadataRetriever()");
 }
 
@@ -143,8 +143,8 @@
 
     FrameRect rect = {left, top, right, bottom};
 
-    if (mImageDecoder != NULL && index == mLastImageIndex) {
-        return mImageDecoder->extractFrame(&rect);
+    if (mDecoder != NULL && index == mLastDecodedIndex) {
+        return mDecoder->extractFrame(&rect);
     }
 
     return getImageInternal(
@@ -153,6 +153,8 @@
 
 sp<IMemory> StagefrightMetadataRetriever::getImageInternal(
         int index, int colorFormat, bool metaOnly, bool thumbnail, FrameRect* rect) {
+    mDecoder.clear();
+    mLastDecodedIndex = -1;
 
     if (mExtractor.get() == NULL) {
         ALOGE("no extractor.");
@@ -227,14 +229,14 @@
         const AString &componentName = matchingCodecs[i];
         sp<ImageDecoder> decoder = new ImageDecoder(componentName, trackMeta, source);
         int64_t frameTimeUs = thumbnail ? -1 : 0;
-        if (decoder->init(frameTimeUs, 1 /*numFrames*/, 0 /*option*/, colorFormat) == OK) {
+        if (decoder->init(frameTimeUs, 0 /*option*/, colorFormat) == OK) {
             sp<IMemory> frame = decoder->extractFrame(rect);
 
             if (frame != NULL) {
                 if (rect != NULL) {
                     // keep the decoder if slice decoding
-                    mImageDecoder = decoder;
-                    mLastImageIndex = index;
+                    mDecoder = decoder;
+                    mLastDecodedIndex = index;
                 }
                 return frame;
             }
@@ -242,6 +244,7 @@
         ALOGV("%s failed to extract thumbnail, trying next decoder.", componentName.c_str());
     }
 
+    ALOGE("all codecs failed to extract frame.");
     return NULL;
 }
 
@@ -250,36 +253,40 @@
     ALOGV("getFrameAtTime: %" PRId64 " us option: %d colorFormat: %d, metaOnly: %d",
             timeUs, option, colorFormat, metaOnly);
 
-    sp<IMemory> frame;
-    status_t err = getFrameInternal(
-            timeUs, 1, option, colorFormat, metaOnly, &frame, NULL /*outFrames*/);
-    return (err == OK) ? frame : NULL;
+    return getFrameInternal(timeUs, option, colorFormat, metaOnly);
 }
 
-status_t StagefrightMetadataRetriever::getFrameAtIndex(
-        std::vector<sp<IMemory> >* frames,
-        int frameIndex, int numFrames, int colorFormat, bool metaOnly) {
-    ALOGV("getFrameAtIndex: frameIndex %d, numFrames %d, colorFormat: %d, metaOnly: %d",
-            frameIndex, numFrames, colorFormat, metaOnly);
+sp<IMemory> StagefrightMetadataRetriever::getFrameAtIndex(
+        int frameIndex, int colorFormat, bool metaOnly) {
+    ALOGV("getFrameAtIndex: frameIndex %d, colorFormat: %d, metaOnly: %d",
+            frameIndex, colorFormat, metaOnly);
+    if (mDecoder != NULL && frameIndex == mLastDecodedIndex + 1) {
+        sp<IMemory> frame = mDecoder->extractFrame();
+        if (frame != nullptr) {
+            mLastDecodedIndex = frameIndex;
+        }
+        return frame;
+    }
 
-    return getFrameInternal(
-            frameIndex, numFrames, MediaSource::ReadOptions::SEEK_FRAME_INDEX,
-            colorFormat, metaOnly, NULL /*outFrame*/, frames);
+    return getFrameInternal(frameIndex,
+            MediaSource::ReadOptions::SEEK_FRAME_INDEX, colorFormat, metaOnly);
 }
 
-status_t StagefrightMetadataRetriever::getFrameInternal(
-        int64_t timeUs, int numFrames, int option, int colorFormat, bool metaOnly,
-        sp<IMemory>* outFrame, std::vector<sp<IMemory> >* outFrames) {
+sp<IMemory> StagefrightMetadataRetriever::getFrameInternal(
+        int64_t timeUs, int option, int colorFormat, bool metaOnly) {
+    mDecoder.clear();
+    mLastDecodedIndex = -1;
+
     if (mExtractor.get() == NULL) {
         ALOGE("no extractor.");
-        return NO_INIT;
+        return NULL;
     }
 
     sp<MetaData> fileMeta = mExtractor->getMetaData();
 
     if (fileMeta == NULL) {
         ALOGE("extractor doesn't publish metadata, failed to initialize?");
-        return NO_INIT;
+        return NULL;
     }
 
     size_t n = mExtractor->countTracks();
@@ -300,30 +307,24 @@
 
     if (i == n) {
         ALOGE("no video track found.");
-        return INVALID_OPERATION;
+        return NULL;
     }
 
     sp<MetaData> trackMeta = mExtractor->getTrackMetaData(
             i, MediaExtractor::kIncludeExtensiveMetaData);
     if (!trackMeta) {
-        return UNKNOWN_ERROR;
+        return NULL;
     }
 
     if (metaOnly) {
-        if (outFrame != NULL) {
-            *outFrame = FrameDecoder::getMetadataOnly(trackMeta, colorFormat);
-            if (*outFrame != NULL) {
-                return OK;
-            }
-        }
-        return UNKNOWN_ERROR;
+        return FrameDecoder::getMetadataOnly(trackMeta, colorFormat);
     }
 
     sp<IMediaSource> source = mExtractor->getTrack(i);
 
     if (source.get() == NULL) {
         ALOGV("unable to instantiate video track.");
-        return UNKNOWN_ERROR;
+        return NULL;
     }
 
     const void *data;
@@ -350,24 +351,22 @@
     for (size_t i = 0; i < matchingCodecs.size(); ++i) {
         const AString &componentName = matchingCodecs[i];
         sp<VideoFrameDecoder> decoder = new VideoFrameDecoder(componentName, trackMeta, source);
-        if (decoder->init(timeUs, numFrames, option, colorFormat) == OK) {
-            if (outFrame != NULL) {
-                *outFrame = decoder->extractFrame();
-                if (*outFrame != NULL) {
-                    return OK;
+        if (decoder->init(timeUs, option, colorFormat) == OK) {
+            sp<IMemory> frame = decoder->extractFrame();
+            if (frame != nullptr) {
+                // keep the decoder if seeking by frame index
+                if (option == MediaSource::ReadOptions::SEEK_FRAME_INDEX) {
+                    mDecoder = decoder;
+                    mLastDecodedIndex = timeUs;
                 }
-            } else if (outFrames != NULL) {
-                status_t err = decoder->extractFrames(outFrames);
-                if (err == OK) {
-                    return OK;
-                }
+                return frame;
             }
         }
         ALOGV("%s failed to extract frame, trying next decoder.", componentName.c_str());
     }
 
     ALOGE("all codecs failed to extract frame.");
-    return UNKNOWN_ERROR;
+    return NULL;
 }
 
 MediaAlbumArt *StagefrightMetadataRetriever::extractAlbumArt() {
diff --git a/media/libstagefright/codecs/m4v_h263/dec/src/packet_util.cpp b/media/libstagefright/codecs/m4v_h263/dec/src/packet_util.cpp
index 48414d7..5880e32 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/src/packet_util.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/src/packet_util.cpp
@@ -52,7 +52,11 @@
         PV_BitstreamByteAlign(stream);
         BitstreamReadBits32(stream, resync_marker_length);
 
-        *next_MB = (int) BitstreamReadBits16(stream, nbits);
+        int mbnum = (int) BitstreamReadBits16(stream, nbits);
+        if (mbnum < 0) {
+            return PV_FAIL;
+        }
+        *next_MB = mbnum;
 //      if (*next_MB <= video->mbnum)   /*  needs more investigation */
 //          *next_MB = video->mbnum+1;
 
diff --git a/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp b/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp
index da86758..87e8fd4 100644
--- a/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp
+++ b/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp
@@ -1426,75 +1426,90 @@
     RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DO_EXECUTE");
 
     UWORD32 ui_exec_done;
+    WORD32 i_num_preroll = 0;
     /* Checking for end of processing */
     err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_EXECUTE, IA_CMD_TYPE_DONE_QUERY,
                                 &ui_exec_done);
     RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DONE_QUERY");
 
-#ifdef ENABLE_MPEG_D_DRC
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+                              IA_ENHAACPLUS_DEC_CONFIG_GET_NUM_PRE_ROLL_FRAMES,
+                              &i_num_preroll);
+
+    RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GET_NUM_PRE_ROLL_FRAMES");
     {
-        if (ui_exec_done != 1) {
-            VOID* p_array;        // ITTIAM:buffer to handle gain payload
-            WORD32 buf_size = 0;  // ITTIAM:gain payload length
-            WORD32 bit_str_fmt = 1;
-            WORD32 gain_stream_flag = 1;
+        int32_t pi_preroll_frame_offset = 0;
+        do {
+#ifdef ENABLE_MPEG_D_DRC
+            if (ui_exec_done != 1) {
+                VOID* p_array;        // ITTIAM:buffer to handle gain payload
+                WORD32 buf_size = 0;  // ITTIAM:gain payload length
+                WORD32 bit_str_fmt = 1;
+                WORD32 gain_stream_flag = 1;
 
-            err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
-                                        IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN, &buf_size);
-            RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN");
+                err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+                                            IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN, &buf_size);
+                RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN");
 
-            err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
-                                        IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF, &p_array);
-            RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF");
+                err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+                                            IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF, &p_array);
+                RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF");
 
-            if (buf_size > 0) {
-                /*Set bitstream_split_format */
-                err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
-                                          IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT, &bit_str_fmt);
-                RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+                if (buf_size > 0) {
+                    /*Set bitstream_split_format */
+                    err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                                              IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT, &bit_str_fmt);
+                    RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
 
-                memcpy(mDrcInBuf, p_array, buf_size);
-                /* Set number of bytes to be processed */
-                err_code =
-                    ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES_BS, 0, &buf_size);
-                RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+                    memcpy(mDrcInBuf, p_array, buf_size);
+                    /* Set number of bytes to be processed */
+                    err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES_BS,
+                                              0, &buf_size);
+                    RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
 
-                err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
-                                          IA_DRC_DEC_CONFIG_GAIN_STREAM_FLAG, &gain_stream_flag);
-                RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+                    err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                                              IA_DRC_DEC_CONFIG_GAIN_STREAM_FLAG,
+                                              &gain_stream_flag);
+                    RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
 
-                /* Execute process */
-                err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
-                                          IA_CMD_TYPE_INIT_CPY_BSF_BUFF, NULL);
-                RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+                    /* Execute process */
+                    err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
+                                              IA_CMD_TYPE_INIT_CPY_BSF_BUFF, NULL);
+                    RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
 
-                mMpegDDRCPresent = 1;
+                    mMpegDDRCPresent = 1;
+                }
             }
-        }
-    }
 #endif
-    /* How much buffer is used in input buffers */
-    err_code =
-        ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CURIDX_INPUT_BUF, 0, bytesConsumed);
-    RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_CURIDX_INPUT_BUF");
+            /* How much buffer is used in input buffers */
+            err_code =
+                ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CURIDX_INPUT_BUF,
+                                 0, bytesConsumed);
+            RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_CURIDX_INPUT_BUF");
 
-    /* Get the output bytes */
-    err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_OUTPUT_BYTES, 0, outBytes);
-    RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_OUTPUT_BYTES");
+            /* Get the output bytes */
+            err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                        IA_API_CMD_GET_OUTPUT_BYTES, 0, outBytes);
+            RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_OUTPUT_BYTES");
 #ifdef ENABLE_MPEG_D_DRC
 
-    if (mMpegDDRCPresent == 1) {
-        memcpy(mDrcInBuf, mOutputBuffer, *outBytes);
-        err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES, 0, outBytes);
-        RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_INPUT_BYTES");
+            if (mMpegDDRCPresent == 1) {
+                memcpy(mDrcInBuf, mOutputBuffer + pi_preroll_frame_offset, *outBytes);
+                pi_preroll_frame_offset += *outBytes;
+                err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES,
+                                          0, outBytes);
+                RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_INPUT_BYTES");
 
-        err_code =
-            ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_EXECUTE, IA_CMD_TYPE_DO_EXECUTE, NULL);
-        RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DO_EXECUTE");
+                err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_EXECUTE,
+                                          IA_CMD_TYPE_DO_EXECUTE, NULL);
+                RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DO_EXECUTE");
 
-        memcpy(mOutputBuffer, mDrcOutBuf, *outBytes);
-    }
+                memcpy(mOutputBuffer, mDrcOutBuf, *outBytes);
+            }
 #endif
+            i_num_preroll--;
+        } while (i_num_preroll > 0);
+    }
     return IA_NO_ERROR;
 }
 
diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp
index d685321..c7dc415 100644
--- a/media/libstagefright/colorconversion/ColorConverter.cpp
+++ b/media/libstagefright/colorconversion/ColorConverter.cpp
@@ -324,8 +324,8 @@
 }
 
 #define DECLARE_YUV2RGBFUNC(func, rgb) int (*func)(     \
-        const uint8*, int, const uint8*, int,           \
-        const uint8*, int, uint8*, int, int, int)       \
+        const uint8_t*, int, const uint8_t*, int,           \
+        const uint8_t*, int, uint8_t*, int, int, int)       \
         = mSrcColorSpace.isBt709() ? libyuv::H420To##rgb \
         : mSrcColorSpace.isJpeg() ? libyuv::J420To##rgb  \
         : libyuv::I420To##rgb
@@ -350,7 +350,7 @@
     {
         DECLARE_YUV2RGBFUNC(func, RGB565);
         (*func)(src_y, src.mStride, src_u, src.mStride / 2, src_v, src.mStride / 2,
-                (uint8 *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight());
+                (uint8_t *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight());
         break;
     }
 
@@ -358,7 +358,7 @@
     {
         DECLARE_YUV2RGBFUNC(func, ABGR);
         (*func)(src_y, src.mStride, src_u, src.mStride / 2, src_v, src.mStride / 2,
-                (uint8 *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight());
+                (uint8_t *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight());
         break;
     }
 
@@ -366,7 +366,7 @@
     {
         DECLARE_YUV2RGBFUNC(func, ARGB);
         (*func)(src_y, src.mStride, src_u, src.mStride / 2, src_v, src.mStride / 2,
-                (uint8 *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight());
+                (uint8_t *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight());
         break;
     }
 
@@ -391,17 +391,17 @@
 
     switch (mDstFormat) {
     case OMX_COLOR_Format16bitRGB565:
-        libyuv::NV12ToRGB565(src_y, src.mStride, src_u, src.mStride, (uint8 *)dst_ptr,
+        libyuv::NV12ToRGB565(src_y, src.mStride, src_u, src.mStride, (uint8_t *)dst_ptr,
                 dst.mStride, src.cropWidth(), src.cropHeight());
         break;
 
     case OMX_COLOR_Format32bitBGRA8888:
-        libyuv::NV12ToARGB(src_y, src.mStride, src_u, src.mStride, (uint8 *)dst_ptr,
+        libyuv::NV12ToARGB(src_y, src.mStride, src_u, src.mStride, (uint8_t *)dst_ptr,
                 dst.mStride, src.cropWidth(), src.cropHeight());
         break;
 
     case OMX_COLOR_Format32BitRGBA8888:
-        libyuv::NV12ToABGR(src_y, src.mStride, src_u, src.mStride, (uint8 *)dst_ptr,
+        libyuv::NV12ToABGR(src_y, src.mStride, src_u, src.mStride, (uint8_t *)dst_ptr,
                 dst.mStride, src.cropWidth(), src.cropHeight());
         break;
 
diff --git a/media/libstagefright/data/media_codecs_google_c2_video.xml b/media/libstagefright/data/media_codecs_google_c2_video.xml
index 04041eb..a07eb8c 100644
--- a/media/libstagefright/data/media_codecs_google_c2_video.xml
+++ b/media/libstagefright/data/media_codecs_google_c2_video.xml
@@ -77,7 +77,7 @@
             <Limit name="bitrate" range="1-40000000" />
             <Feature name="adaptive-playback" />
         </MediaCodec>
-        <MediaCodec name="c2.android.av1.decoder" type="video/av01">
+        <MediaCodec name="c2.android.gav1.decoder" type="video/av01">
             <Limit name="size" min="96x96" max="1920x1080" />
             <Limit name="alignment" value="2x2" />
             <Limit name="block-size" value="16x16" />
diff --git a/media/libstagefright/data/media_codecs_sw.xml b/media/libstagefright/data/media_codecs_sw.xml
index 67d3f1a..9532ba6 100644
--- a/media/libstagefright/data/media_codecs_sw.xml
+++ b/media/libstagefright/data/media_codecs_sw.xml
@@ -182,7 +182,7 @@
             </Variant>
             <Feature name="adaptive-playback" />
         </MediaCodec>
-        <MediaCodec name="c2.android.av1.decoder" type="video/av01" variant="!slow-cpu">
+        <MediaCodec name="c2.android.gav1.decoder" type="video/av01" variant="!slow-cpu">
             <Limit name="size" min="2x2" max="1920x1080" />
             <Limit name="alignment" value="2x2" />
             <Limit name="block-size" value="16x16" />
diff --git a/media/libstagefright/exports.lds b/media/libstagefright/exports.lds
index 06c4f19..f5ddf1e 100644
--- a/media/libstagefright/exports.lds
+++ b/media/libstagefright/exports.lds
@@ -395,7 +395,6 @@
         ScaleFilterCols_NEON*;
         ScaleFilterReduce;
         ScaleFilterRows_NEON*;
-        ScaleOffset;
         ScalePlane;
         ScalePlane_16;
         ScalePlaneBilinearDown;
diff --git a/media/libstagefright/include/FrameDecoder.h b/media/libstagefright/include/FrameDecoder.h
index dc58c15..ce37ae8 100644
--- a/media/libstagefright/include/FrameDecoder.h
+++ b/media/libstagefright/include/FrameDecoder.h
@@ -44,13 +44,10 @@
             const sp<MetaData> &trackMeta,
             const sp<IMediaSource> &source);
 
-    status_t init(
-            int64_t frameTimeUs, size_t numFrames, int option, int colorFormat);
+    status_t init(int64_t frameTimeUs, int option, int colorFormat);
 
     sp<IMemory> extractFrame(FrameRect *rect = NULL);
 
-    status_t extractFrames(std::vector<sp<IMemory> >* frames);
-
     static sp<IMemory> getMetadataOnly(
             const sp<MetaData> &trackMeta, int colorFormat, bool thumbnail = false);
 
@@ -59,7 +56,6 @@
 
     virtual sp<AMessage> onGetFormatAndSeekOptions(
             int64_t frameTimeUs,
-            size_t numFrames,
             int seekMode,
             MediaSource::ReadOptions *options) = 0;
 
@@ -80,10 +76,7 @@
     sp<MetaData> trackMeta()     const      { return mTrackMeta; }
     OMX_COLOR_FORMATTYPE dstFormat() const  { return mDstFormat; }
     int32_t dstBpp()             const      { return mDstBpp; }
-
-    void addFrame(const sp<IMemory> &frame) {
-        mFrames.push_back(frame);
-    }
+    void setFrame(const sp<IMemory> &frameMem) { mFrameMemory = frameMem; }
 
 private:
     AString mComponentName;
@@ -91,7 +84,7 @@
     sp<IMediaSource> mSource;
     OMX_COLOR_FORMATTYPE mDstFormat;
     int32_t mDstBpp;
-    std::vector<sp<IMemory> > mFrames;
+    sp<IMemory> mFrameMemory;
     MediaSource::ReadOptions mReadOptions;
     sp<MediaCodec> mDecoder;
     sp<AMessage> mOutputFormat;
@@ -112,7 +105,6 @@
 protected:
     virtual sp<AMessage> onGetFormatAndSeekOptions(
             int64_t frameTimeUs,
-            size_t numFrames,
             int seekMode,
             MediaSource::ReadOptions *options) override;
 
@@ -134,11 +126,10 @@
             bool *done) override;
 
 private:
+    VideoFrame *mFrame;
     bool mIsAvcOrHevc;
     MediaSource::ReadOptions::SeekMode mSeekMode;
     int64_t mTargetTimeUs;
-    size_t mNumFrames;
-    size_t mNumFramesDecoded;
 };
 
 struct ImageDecoder : public FrameDecoder {
@@ -150,7 +141,6 @@
 protected:
     virtual sp<AMessage> onGetFormatAndSeekOptions(
             int64_t frameTimeUs,
-            size_t numFrames,
             int seekMode,
             MediaSource::ReadOptions *options) override;
 
diff --git a/media/libstagefright/include/StagefrightMetadataRetriever.h b/media/libstagefright/include/StagefrightMetadataRetriever.h
index c50677a..ee51290 100644
--- a/media/libstagefright/include/StagefrightMetadataRetriever.h
+++ b/media/libstagefright/include/StagefrightMetadataRetriever.h
@@ -26,7 +26,7 @@
 namespace android {
 
 class DataSource;
-struct ImageDecoder;
+struct FrameDecoder;
 struct FrameRect;
 
 struct StagefrightMetadataRetriever : public MediaMetadataRetrieverBase {
@@ -47,9 +47,8 @@
             int index, int colorFormat, bool metaOnly, bool thumbnail);
     virtual sp<IMemory> getImageRectAtIndex(
             int index, int colorFormat, int left, int top, int right, int bottom);
-    virtual status_t getFrameAtIndex(
-            std::vector<sp<IMemory> >* frames,
-            int frameIndex, int numFrames, int colorFormat, bool metaOnly);
+    virtual sp<IMemory> getFrameAtIndex(
+            int index, int colorFormat, bool metaOnly);
 
     virtual MediaAlbumArt *extractAlbumArt();
     virtual const char *extractMetadata(int keyCode);
@@ -62,17 +61,17 @@
     KeyedVector<int, String8> mMetaData;
     MediaAlbumArt *mAlbumArt;
 
-    sp<ImageDecoder> mImageDecoder;
-    int mLastImageIndex;
+    sp<FrameDecoder> mDecoder;
+    int mLastDecodedIndex;
     void parseMetaData();
     void parseColorAspects(const sp<MetaData>& meta);
     // Delete album art and clear metadata.
     void clearMetadata();
 
-    status_t getFrameInternal(
-            int64_t timeUs, int numFrames, int option, int colorFormat, bool metaOnly,
-            sp<IMemory>* outFrame, std::vector<sp<IMemory> >* outFrames);
-    virtual sp<IMemory> getImageInternal(
+    sp<IMemory> getFrameInternal(
+            int64_t timeUs, int option, int colorFormat, bool metaOnly);
+
+    sp<IMemory> getImageInternal(
             int index, int colorFormat, bool metaOnly, bool thumbnail, FrameRect* rect);
 
     StagefrightMetadataRetriever(const StagefrightMetadataRetriever &);
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 6cd4265..89cca63 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -340,6 +340,7 @@
     int32_t mVideoWidth;
     int32_t mVideoHeight;
     int32_t mRotationDegrees;
+    int32_t mAllowFrameDroppingBySurface;
 
     // initial create parameters
     AString mInitName;
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecList.h b/media/libstagefright/include/media/stagefright/MediaCodecList.h
index e44b0a4..e681d25 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecList.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecList.h
@@ -83,6 +83,7 @@
     };
 
     static sp<BinderDeathObserver> sBinderDeathObserver;
+    static sp<IBinder> sMediaPlayer;
 
     static sp<IMediaCodecList> sCodecList;
     static sp<IMediaCodecList> sRemoteList;
diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp
index e260cae..7d03d98 100644
--- a/media/libstagefright/omx/Android.bp
+++ b/media/libstagefright/omx/Android.bp
@@ -72,7 +72,6 @@
         cfi: true,
     },
 
-    compile_multilib: "32",
 }
 
 cc_library_shared {
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 6160c3c..6adf563 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -966,7 +966,7 @@
         (strcmp(name, "/") == 0) || (strcmp(basename(name), name) != 0)) {
         char errMsg[80];
 
-        sprintf(errMsg, "Invalid name: %s", (const char *) name);
+        snprintf(errMsg, sizeof(errMsg), "Invalid name: %s", (const char *) name);
         ALOGE("%s (b/130656917)", errMsg);
         android_errorWriteWithInfoLog(SN_EVENT_LOG_ID, "130656917", -1, errMsg,
                                       strlen(errMsg));
diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp
index baa4fc7..830f752 100644
--- a/media/ndk/NdkImageReader.cpp
+++ b/media/ndk/NdkImageReader.cpp
@@ -113,12 +113,12 @@
 
 void
 AImageReader::FrameListener::onFrameAvailable(const BufferItem& /*item*/) {
-    Mutex::Autolock _l(mLock);
     sp<AImageReader> reader = mReader.promote();
     if (reader == nullptr) {
         ALOGW("A frame is available after AImageReader closed!");
         return; // reader has been closed
     }
+    Mutex::Autolock _l(mLock);
     if (mListener.onImageAvailable == nullptr) {
         return; // No callback registered
     }
@@ -143,12 +143,12 @@
 
 void
 AImageReader::BufferRemovedListener::onBufferFreed(const wp<GraphicBuffer>& graphicBuffer) {
-    Mutex::Autolock _l(mLock);
     sp<AImageReader> reader = mReader.promote();
     if (reader == nullptr) {
         ALOGW("A frame is available after AImageReader closed!");
         return; // reader has been closed
     }
+    Mutex::Autolock _l(mLock);
     if (mListener.onBufferRemoved == nullptr) {
         return; // No callback registered
     }
diff --git a/media/ndk/NdkImageReaderPriv.h b/media/ndk/NdkImageReaderPriv.h
index e328cb1..19bd704 100644
--- a/media/ndk/NdkImageReaderPriv.h
+++ b/media/ndk/NdkImageReaderPriv.h
@@ -134,7 +134,7 @@
 
       private:
         AImageReader_ImageListener mListener = {nullptr, nullptr};
-        wp<AImageReader>           mReader;
+        const wp<AImageReader>     mReader;
         Mutex                      mLock;
     };
     sp<FrameListener> mFrameListener;
@@ -149,7 +149,7 @@
 
        private:
         AImageReader_BufferRemovedListener mListener = {nullptr, nullptr};
-        wp<AImageReader>           mReader;
+        const wp<AImageReader>     mReader;
         Mutex                      mLock;
     };
     sp<BufferRemovedListener> mBufferRemovedListener;
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 0b745ac..355d945 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1357,8 +1357,8 @@
         String8(AudioParameter::keyFrameCount),
         String8(AudioParameter::keyInputSource),
         String8(AudioParameter::keyMonoOutput),
-        String8(AudioParameter::keyStreamConnect),
-        String8(AudioParameter::keyStreamDisconnect),
+        String8(AudioParameter::keyDeviceConnect),
+        String8(AudioParameter::keyDeviceDisconnect),
         String8(AudioParameter::keyStreamSupportedFormats),
         String8(AudioParameter::keyStreamSupportedChannels),
         String8(AudioParameter::keyStreamSupportedSamplingRates),
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 28ad9dd..f81dacf 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3956,6 +3956,32 @@
     return INVALID_OPERATION;
 }
 
+// For dedicated VoIP outputs, let the HAL apply the stream volume. Track volume is
+// still applied by the mixer.
+// All tracks attached to a mixer with flag VOIP_RX are tied to the same
+// stream type STREAM_VOICE_CALL so this will only change the HAL volume once even
+// if more than one track are active
+status_t AudioFlinger::PlaybackThread::handleVoipVolume_l(float *volume)
+{
+    status_t result = NO_ERROR;
+    if ((mOutput->flags & AUDIO_OUTPUT_FLAG_VOIP_RX) != 0) {
+        if (*volume != mLeftVolFloat) {
+            result = mOutput->stream->setVolume(*volume, *volume);
+            ALOGE_IF(result != OK,
+                     "Error when setting output stream volume: %d", result);
+            if (result == NO_ERROR) {
+                mLeftVolFloat = *volume;
+            }
+        }
+        // if stream volume was successfully sent to the HAL, mLeftVolFloat == v here and we
+        // remove stream volume contribution from software volume.
+        if (mLeftVolFloat == *volume) {
+            *volume = 1.0f;
+        }
+    }
+    return result;
+}
+
 status_t AudioFlinger::MixerThread::createAudioPatch_l(const struct audio_patch *patch,
                                                           audio_patch_handle_t *handle)
 {
@@ -4758,22 +4784,25 @@
                     // no acknowledgement required for newly active tracks
                 }
                 sp<AudioTrackServerProxy> proxy = track->mAudioTrackServerProxy;
+                float volume;
+                if (track->isPlaybackRestricted() || mStreamTypes[track->streamType()].mute) {
+                    volume = 0.f;
+                } else {
+                    volume = masterVolume * mStreamTypes[track->streamType()].volume;
+                }
+
+                handleVoipVolume_l(&volume);
+
                 // cache the combined master volume and stream type volume for fast mixer; this
                 // lacks any synchronization or barrier so VolumeProvider may read a stale value
                 const float vh = track->getVolumeHandler()->getVolume(
-                        proxy->framesReleased()).first;
-                float volume;
-                if (track->isPlaybackRestricted()) {
-                    volume = 0.f;
-                } else {
-                    volume = masterVolume
-                        * mStreamTypes[track->streamType()].volume
-                        * vh;
-                }
+                    proxy->framesReleased()).first;
+                volume *= vh;
                 track->mCachedVolume = volume;
                 gain_minifloat_packed_t vlr = proxy->getVolumeLR();
                 float vlf = volume * float_from_gain(gain_minifloat_unpack_left(vlr));
                 float vrf = volume * float_from_gain(gain_minifloat_unpack_right(vlr));
+
                 track->setFinalVolume((vlf + vrf) / 2.f);
                 ++fastTracks;
             } else {
@@ -4916,20 +4945,22 @@
             uint32_t vl, vr;       // in U8.24 integer format
             float vlf, vrf, vaf;   // in [0.0, 1.0] float format
             // read original volumes with volume control
-            float typeVolume = mStreamTypes[track->streamType()].volume;
-            float v = masterVolume * typeVolume;
+            float v = masterVolume * mStreamTypes[track->streamType()].volume;
             // Always fetch volumeshaper volume to ensure state is updated.
             const sp<AudioTrackServerProxy> proxy = track->mAudioTrackServerProxy;
             const float vh = track->getVolumeHandler()->getVolume(
                     track->mAudioTrackServerProxy->framesReleased()).first;
 
-            if (track->isPausing() || mStreamTypes[track->streamType()].mute
-                    || track->isPlaybackRestricted()) {
+            if (mStreamTypes[track->streamType()].mute || track->isPlaybackRestricted()) {
+                v = 0;
+            }
+
+            handleVoipVolume_l(&v);
+
+            if (track->isPausing()) {
                 vl = vr = 0;
                 vlf = vrf = vaf = 0.;
-                if (track->isPausing()) {
-                    track->setPaused();
-                }
+                track->setPaused();
             } else {
                 gain_minifloat_packed_t vlr = proxy->getVolumeLR();
                 vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
@@ -4981,25 +5012,6 @@
                 track->mHasVolumeController = false;
             }
 
-            // For dedicated VoIP outputs, let the HAL apply the stream volume. Track volume is
-            // still applied by the mixer.
-            if ((mOutput->flags & AUDIO_OUTPUT_FLAG_VOIP_RX) != 0) {
-                v = mStreamTypes[track->streamType()].mute ? 0.0f : v;
-                if (v != mLeftVolFloat) {
-                    status_t result = mOutput->stream->setVolume(v, v);
-                    ALOGE_IF(result != OK, "Error when setting output stream volume: %d", result);
-                    if (result == OK) {
-                        mLeftVolFloat = v;
-                    }
-                }
-                // if stream volume was successfully sent to the HAL, mLeftVolFloat == v here and we
-                // remove stream volume contribution from software volume.
-                if (v != 0.0f && mLeftVolFloat == v) {
-                   vlf = min(1.0f, vlf / v);
-                   vrf = min(1.0f, vrf / v);
-                   vaf = min(1.0f, vaf / v);
-               }
-            }
             // XXX: these things DON'T need to be done each time
             mAudioMixer->setBufferProvider(trackId, track);
             mAudioMixer->enable(trackId);
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 336c2b4..fc8aa13 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -747,6 +747,7 @@
                 // is safe to do so. That will drop the final ref count and destroy the tracks.
     virtual     mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove) = 0;
                 void        removeTracks_l(const Vector< sp<Track> >& tracksToRemove);
+                status_t    handleVoipVolume_l(float *volume);
 
     // StreamOutHalInterfaceCallback implementation
     virtual     void        onWriteReady();
diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
index 96a8337..1f9b725 100644
--- a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
@@ -333,9 +333,10 @@
             if (encodedFormat != AUDIO_FORMAT_DEFAULT) {
                 moduleDevice->setEncodedFormat(encodedFormat);
             }
-            moduleDevice->setAddress(devAddress);
             if (allowToCreate) {
                 moduleDevice->attach(hwModule);
+                moduleDevice->setAddress(devAddress);
+                moduleDevice->setName(String8(name));
             }
             return moduleDevice;
         }
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index a9e687c..edab95a 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -94,7 +94,7 @@
 {
     AudioParameter param(device->address());
     const String8 key(state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE ?
-                AudioParameter::keyStreamConnect : AudioParameter::keyStreamDisconnect);
+                AudioParameter::keyDeviceConnect : AudioParameter::keyDeviceDisconnect);
     param.addInt(key, device->type());
     mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString());
 }
@@ -472,6 +472,10 @@
     std::unordered_set<audio_format_t> formatSet;
     sp<HwModule> primaryModule =
             mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY);
+    if (primaryModule == nullptr) {
+        ALOGE("%s() unable to get primary module", __func__);
+        return NO_INIT;
+    }
     DeviceVector declaredDevices = primaryModule->getDeclaredDevices().getDevicesFromTypeMask(
             AUDIO_DEVICE_OUT_ALL_A2DP);
     for (const auto& device : declaredDevices) {
@@ -836,7 +840,7 @@
         // if explicitly requested
         static const uint32_t kRelevantFlags =
                 (AUDIO_OUTPUT_FLAG_HW_AV_SYNC | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD |
-                 AUDIO_OUTPUT_FLAG_VOIP_RX);
+                 AUDIO_OUTPUT_FLAG_VOIP_RX | AUDIO_OUTPUT_FLAG_MMAP_NOIRQ);
         flags =
             (audio_output_flags_t)((flags & kRelevantFlags) | AUDIO_OUTPUT_FLAG_DIRECT);
     }
@@ -2236,16 +2240,22 @@
         return status;
     }
 
-  // increment activity count before calling getNewInputDevice() below as only active sessions
+    // increment activity count before calling getNewInputDevice() below as only active sessions
     // are considered for device selection
     inputDesc->setClientActive(client, true);
 
     // indicate active capture to sound trigger service if starting capture from a mic on
     // primary HW module
     sp<DeviceDescriptor> device = getNewInputDevice(inputDesc);
-    setInputDevice(input, device, true /* force */);
+    if (device != nullptr) {
+        status = setInputDevice(input, device, true /* force */);
+    } else {
+        ALOGW("%s no new input device can be found for descriptor %d",
+                __FUNCTION__, inputDesc->getId());
+        status = BAD_VALUE;
+    }
 
-    if (inputDesc->activeCount()  == 1) {
+    if (status == NO_ERROR && inputDesc->activeCount() == 1) {
         sp<AudioPolicyMix> policyMix = inputDesc->mPolicyMix.promote();
         // if input maps to a dynamic policy with an activity listener, notify of state change
         if ((policyMix != NULL)
@@ -2276,11 +2286,16 @@
                         address, "remote-submix", AUDIO_FORMAT_DEFAULT);
             }
         }
+    } else if (status != NO_ERROR) {
+        // Restore client activity state.
+        inputDesc->setClientActive(client, false);
+        inputDesc->stop();
     }
 
-    ALOGV("%s input %d source = %d exit", __FUNCTION__, input, client->source());
+    ALOGV("%s input %d source = %d status = %d exit",
+            __FUNCTION__, input, client->source(), status);
 
-    return NO_ERROR;
+    return status;
 }
 
 status_t AudioPolicyManager::stopInput(audio_port_handle_t portId)
@@ -5845,11 +5860,11 @@
 
     if (isVoiceVolSrc || isBtScoVolSrc) {
         float voiceVolume;
-        // Force voice volume to max for bluetooth SCO as volume is managed by the headset
+        // Force voice volume to max or mute for Bluetooth SCO as other attenuations are managed by the headset
         if (isVoiceVolSrc) {
             voiceVolume = (float)index/(float)curves.getVolumeIndexMax();
         } else {
-            voiceVolume = 1.0;
+            voiceVolume = index == 0 ? 0.0 : 1.0;
         }
         if (voiceVolume != mLastVoiceVolume) {
             mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 77f7997..85ea94f 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -29,7 +29,6 @@
 #include <utils/Log.h>
 #include <cutils/properties.h>
 #include <binder/IPCThreadState.h>
-#include <binder/ActivityManager.h>
 #include <binder/PermissionController.h>
 #include <binder/IResultReceiver.h>
 #include <utils/String16.h>
@@ -774,28 +773,26 @@
 // -----------  AudioPolicyService::UidPolicy implementation ----------
 
 void AudioPolicyService::UidPolicy::registerSelf() {
-    ActivityManager am;
-    am.registerUidObserver(this, ActivityManager::UID_OBSERVER_GONE
+    status_t res = mAm.linkToDeath(this);
+    mAm.registerUidObserver(this, ActivityManager::UID_OBSERVER_GONE
             | ActivityManager::UID_OBSERVER_IDLE
             | ActivityManager::UID_OBSERVER_ACTIVE
             | ActivityManager::UID_OBSERVER_PROCSTATE,
             ActivityManager::PROCESS_STATE_UNKNOWN,
             String16("audioserver"));
-    status_t res = am.linkToDeath(this);
     if (!res) {
         Mutex::Autolock _l(mLock);
         mObserverRegistered = true;
     } else {
         ALOGE("UidPolicy::registerSelf linkToDeath failed: %d", res);
 
-        am.unregisterUidObserver(this);
+        mAm.unregisterUidObserver(this);
     }
 }
 
 void AudioPolicyService::UidPolicy::unregisterSelf() {
-    ActivityManager am;
-    am.unlinkToDeath(this);
-    am.unregisterUidObserver(this);
+    mAm.unlinkToDeath(this);
+    mAm.unregisterUidObserver(this);
     Mutex::Autolock _l(mLock);
     mObserverRegistered = false;
 }
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index e467f70..74aea0d 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -23,6 +23,7 @@
 #include <utils/String8.h>
 #include <utils/Vector.h>
 #include <utils/SortedVector.h>
+#include <binder/ActivityManager.h>
 #include <binder/BinderService.h>
 #include <binder/IUidObserver.h>
 #include <system/audio.h>
@@ -387,6 +388,7 @@
 
         wp<AudioPolicyService> mService;
         Mutex mLock;
+        ActivityManager mAm;
         bool mObserverRegistered;
         std::unordered_map<uid_t, std::pair<bool, int>> mOverrideUids;
         std::unordered_map<uid_t, std::pair<bool, int>> mCachedUids;
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 3e62102..a4868bf 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -1149,6 +1149,8 @@
                 clientPid,
                 states[states.size() - 1]);
 
+        resource_policy::ClientPriority clientPriority = clientDescriptor->getPriority();
+
         // Find clients that would be evicted
         auto evicted = mActiveClientManager.wouldEvict(clientDescriptor);
 
@@ -1166,8 +1168,7 @@
             String8 msg = String8::format("%s : DENIED connect device %s client for package %s "
                     "(PID %d, score %d state %d) due to eviction policy", curTime.string(),
                     cameraId.string(), packageName.string(), clientPid,
-                    priorityScores[priorityScores.size() - 1],
-                    states[states.size() - 1]);
+                    clientPriority.getScore(), clientPriority.getState());
 
             for (auto& i : incompatibleClients) {
                 msg.appendFormat("\n   - Blocked by existing device %s client for package %s"
@@ -1212,9 +1213,8 @@
                     i->getKey().string(), String8{clientSp->getPackageName()}.string(),
                     i->getOwnerId(), i->getPriority().getScore(),
                     i->getPriority().getState(), cameraId.string(),
-                    packageName.string(), clientPid,
-                    priorityScores[priorityScores.size() - 1],
-                    states[states.size() - 1]));
+                    packageName.string(), clientPid, clientPriority.getScore(),
+                    clientPriority.getState()));
 
             // Notify the client of disconnection
             clientSp->notifyError(hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED,
@@ -1348,14 +1348,19 @@
     Status ret = Status::ok();
     String8 id = String8(cameraId);
     sp<CameraDeviceClient> client = nullptr;
-
+    String16 clientPackageNameAdj = clientPackageName;
+    if (hardware::IPCThreadState::self()->isServingCall()) {
+        std::string vendorClient =
+                StringPrintf("vendor.client.pid<%d>", CameraThreadState::getCallingPid());
+        clientPackageNameAdj = String16(vendorClient.c_str());
+    }
     ret = connectHelper<hardware::camera2::ICameraDeviceCallbacks,CameraDeviceClient>(cameraCb, id,
             /*api1CameraId*/-1,
-            CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName,
+            CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageNameAdj,
             clientUid, USE_CALLING_PID, API_2, /*shimUpdateOnly*/ false, /*out*/client);
 
     if(!ret.isOk()) {
-        logRejected(id, CameraThreadState::getCallingPid(), String8(clientPackageName),
+        logRejected(id, CameraThreadState::getCallingPid(), String8(clientPackageNameAdj),
                 ret.toString8());
         return ret;
     }
@@ -2368,11 +2373,7 @@
         }
         mClientPackageName = packages[0];
     }
-    if (hardware::IPCThreadState::self()->isServingCall()) {
-        std::string vendorClient =
-                StringPrintf("vendor.client.pid<%d>", CameraThreadState::getCallingPid());
-        mClientPackageName = String16(vendorClient.c_str());
-    } else {
+    if (!hardware::IPCThreadState::self()->isServingCall()) {
         mAppOpsManager = std::make_unique<AppOpsManager>();
     }
 }
@@ -2607,14 +2608,13 @@
 void CameraService::UidPolicy::registerSelf() {
     Mutex::Autolock _l(mUidLock);
 
-    ActivityManager am;
     if (mRegistered) return;
-    am.registerUidObserver(this, ActivityManager::UID_OBSERVER_GONE
+    status_t res = mAm.linkToDeath(this);
+    mAm.registerUidObserver(this, ActivityManager::UID_OBSERVER_GONE
             | ActivityManager::UID_OBSERVER_IDLE
             | ActivityManager::UID_OBSERVER_ACTIVE | ActivityManager::UID_OBSERVER_PROCSTATE,
             ActivityManager::PROCESS_STATE_UNKNOWN,
             String16("cameraserver"));
-    status_t res = am.linkToDeath(this);
     if (res == OK) {
         mRegistered = true;
         ALOGV("UidPolicy: Registered with ActivityManager");
@@ -2624,9 +2624,8 @@
 void CameraService::UidPolicy::unregisterSelf() {
     Mutex::Autolock _l(mUidLock);
 
-    ActivityManager am;
-    am.unregisterUidObserver(this);
-    am.unlinkToDeath(this);
+    mAm.unregisterUidObserver(this);
+    mAm.unlinkToDeath(this);
     mRegistered = false;
     mActiveUids.clear();
     ALOGV("UidPolicy: Unregistered with ActivityManager");
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 065157d..cf93a41 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -25,6 +25,7 @@
 #include <cutils/multiuser.h>
 #include <utils/Vector.h>
 #include <utils/KeyedVector.h>
+#include <binder/ActivityManager.h>
 #include <binder/AppOpsManager.h>
 #include <binder/BinderService.h>
 #include <binder/IAppOpsCallback.h>
@@ -564,6 +565,7 @@
 
         Mutex mUidLock;
         bool mRegistered;
+        ActivityManager mAm;
         wp<CameraService> mService;
         std::unordered_set<uid_t> mActiveUids;
         // Monitored uid map to cached procState and refCount pair
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
index 9525ad2..8ebaa2b 100644
--- a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
@@ -58,8 +58,8 @@
 
         entry = staticInfo.find(ANDROID_LENS_INTRINSIC_CALIBRATION);
         if (entry.count == 5) {
-            mInstrinsicCalibration.reserve(5);
-            mInstrinsicCalibration.insert(mInstrinsicCalibration.end(), entry.data.f,
+            mIntrinsicCalibration.reserve(5);
+            mIntrinsicCalibration.insert(mIntrinsicCalibration.end(), entry.data.f,
                     entry.data.f + 5);
         } else {
             ALOGW("%s: Intrinsic calibration absent from camera characteristics!", __FUNCTION__);
@@ -323,12 +323,12 @@
     depthPhoto.mMaxJpegSize = maxDepthJpegSize;
     // The camera intrinsic calibration layout is as follows:
     // [focalLengthX, focalLengthY, opticalCenterX, opticalCenterY, skew]
-    if (mInstrinsicCalibration.size() == 5) {
-        memcpy(depthPhoto.mInstrinsicCalibration, mInstrinsicCalibration.data(),
-                sizeof(depthPhoto.mInstrinsicCalibration));
-        depthPhoto.mIsInstrinsicCalibrationValid = 1;
+    if (mIntrinsicCalibration.size() == 5) {
+        memcpy(depthPhoto.mIntrinsicCalibration, mIntrinsicCalibration.data(),
+                sizeof(depthPhoto.mIntrinsicCalibration));
+        depthPhoto.mIsIntrinsicCalibrationValid = 1;
     } else {
-        depthPhoto.mIsInstrinsicCalibrationValid = 0;
+        depthPhoto.mIsIntrinsicCalibrationValid = 0;
     }
     // The camera lens distortion contains the following lens correction coefficients.
     // [kappa_1, kappa_2, kappa_3 kappa_4, kappa_5]
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.h b/services/camera/libcameraservice/api2/DepthCompositeStream.h
index 1bf31f4..975c59b 100644
--- a/services/camera/libcameraservice/api2/DepthCompositeStream.h
+++ b/services/camera/libcameraservice/api2/DepthCompositeStream.h
@@ -124,7 +124,7 @@
 
     ssize_t              mMaxJpegSize;
     std::vector<std::tuple<size_t, size_t>> mSupportedDepthSizes;
-    std::vector<float>   mInstrinsicCalibration, mLensDistortion;
+    std::vector<float>   mIntrinsicCalibration, mLensDistortion;
     bool                 mIsLogicalCamera;
     void*                mDepthPhotoLibHandle;
     process_depth_photo_frame mDepthPhotoProcess;
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index 09638d0..c72029f 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -29,6 +29,7 @@
 #include <future>
 #include <inttypes.h>
 #include <hardware/camera_common.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
 #include <hidl/ServiceManagement.h>
 #include <functional>
 #include <camera_metadata_hidden.h>
@@ -47,10 +48,6 @@
 using std::literals::chrono_literals::operator""s;
 
 namespace {
-// Hardcoded name for the passthrough HAL implementation, since it can't be discovered via the
-// service manager
-const std::string kLegacyProviderName("legacy/0");
-const std::string kExternalProviderName("external/0");
 const bool kEnableLazyHal(property_get_bool("ro.camera.enableLazyHal", false));
 } // anonymous namespace
 
@@ -62,6 +59,19 @@
 CameraProviderManager::~CameraProviderManager() {
 }
 
+hardware::hidl_vec<hardware::hidl_string>
+CameraProviderManager::HardwareServiceInteractionProxy::listServices() {
+    hardware::hidl_vec<hardware::hidl_string> ret;
+    auto manager = hardware::defaultServiceManager1_2();
+    if (manager != nullptr) {
+        manager->listManifestByInterface(provider::V2_4::ICameraProvider::descriptor,
+                [&ret](const hardware::hidl_vec<hardware::hidl_string> &registered) {
+                    ret = registered;
+                });
+    }
+    return ret;
+}
+
 status_t CameraProviderManager::initialize(wp<CameraProviderManager::StatusListener> listener,
         ServiceInteractionProxy* proxy) {
     std::lock_guard<std::mutex> lock(mInterfaceMutex);
@@ -84,9 +94,10 @@
         return INVALID_OPERATION;
     }
 
-    // See if there's a passthrough HAL, but let's not complain if there's not
-    addProviderLocked(kLegacyProviderName, /*expected*/ false);
-    addProviderLocked(kExternalProviderName, /*expected*/ false);
+
+    for (const auto& instance : mServiceProxy->listServices()) {
+        this->addProviderLocked(instance);
+    }
 
     IPCThreadState::self()->flushCommands();
 
@@ -1087,7 +1098,7 @@
     return false;
 }
 
-status_t CameraProviderManager::addProviderLocked(const std::string& newProvider, bool expected) {
+status_t CameraProviderManager::addProviderLocked(const std::string& newProvider) {
     for (const auto& providerInfo : mProviders) {
         if (providerInfo->mProviderName == newProvider) {
             ALOGW("%s: Camera provider HAL with name '%s' already registered", __FUNCTION__,
@@ -1100,13 +1111,9 @@
     interface = mServiceProxy->getService(newProvider);
 
     if (interface == nullptr) {
-        if (expected) {
-            ALOGE("%s: Camera provider HAL '%s' is not actually available", __FUNCTION__,
-                    newProvider.c_str());
-            return BAD_VALUE;
-        } else {
-            return OK;
-        }
+        ALOGE("%s: Camera provider HAL '%s' is not actually available", __FUNCTION__,
+                newProvider.c_str());
+        return BAD_VALUE;
     }
 
     sp<ProviderInfo> providerInfo = new ProviderInfo(newProvider, this);
@@ -2058,6 +2065,13 @@
     return OK;
 }
 bool CameraProviderManager::ProviderInfo::DeviceInfo3::isAPI1Compatible() const {
+    // Do not advertise NIR cameras to API1 camera app.
+    camera_metadata_ro_entry cfa = mCameraCharacteristics.find(
+            ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
+    if (cfa.count == 1 && cfa.data.u8[0] == ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR) {
+        return false;
+    }
+
     bool isBackwardCompatible = false;
     camera_metadata_ro_entry_t caps = mCameraCharacteristics.find(
             ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index a42fb4d..8cdfc24 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -78,6 +78,7 @@
                 &notification) = 0;
         virtual sp<hardware::camera::provider::V2_4::ICameraProvider> getService(
                 const std::string &serviceName) = 0;
+        virtual hardware::hidl_vec<hardware::hidl_string> listServices() = 0;
         virtual ~ServiceInteractionProxy() {}
     };
 
@@ -95,6 +96,8 @@
                 const std::string &serviceName) override {
             return hardware::camera::provider::V2_4::ICameraProvider::getService(serviceName);
         }
+
+        virtual hardware::hidl_vec<hardware::hidl_string> listServices() override;
     };
 
     /**
@@ -567,7 +570,7 @@
             hardware::hidl_version minVersion = hardware::hidl_version{0,0},
             hardware::hidl_version maxVersion = hardware::hidl_version{1000,0}) const;
 
-    status_t addProviderLocked(const std::string& newProvider, bool expected = true);
+    status_t addProviderLocked(const std::string& newProvider);
 
     status_t removeProvider(const std::string& provider);
     sp<StatusListener> getStatusListener() const;
diff --git a/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp b/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp
index fc79150..3c90de0 100644
--- a/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp
+++ b/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp
@@ -61,6 +61,13 @@
 using dynamic_depth::Profile;
 using dynamic_depth::Profiles;
 
+template<>
+struct std::default_delete<jpeg_compress_struct> {
+    inline void operator()(jpeg_compress_struct* cinfo) const {
+        jpeg_destroy_compress(cinfo);
+    }
+};
+
 namespace android {
 namespace camera3 {
 
@@ -118,16 +125,16 @@
         bool mSuccess;
     } dmgr;
 
-    jpeg_compress_struct cinfo = {};
+    std::unique_ptr<jpeg_compress_struct> cinfo = std::make_unique<jpeg_compress_struct>();
     jpeg_error_mgr jerr;
 
     // Initialize error handling with standard callbacks, but
     // then override output_message (to print to ALOG) and
     // error_exit to set a flag and print a message instead
     // of killing the whole process.
-    cinfo.err = jpeg_std_error(&jerr);
+    cinfo->err = jpeg_std_error(&jerr);
 
-    cinfo.err->output_message = [](j_common_ptr cinfo) {
+    cinfo->err->output_message = [](j_common_ptr cinfo) {
         char buffer[JMSG_LENGTH_MAX];
 
         /* Create the message */
@@ -135,7 +142,7 @@
         ALOGE("libjpeg error: %s", buffer);
     };
 
-    cinfo.err->error_exit = [](j_common_ptr cinfo) {
+    cinfo->err->error_exit = [](j_common_ptr cinfo) {
         (*cinfo->err->output_message)(cinfo);
         if(cinfo->client_data) {
             auto & dmgr = *static_cast<CustomJpegDestMgr*>(cinfo->client_data);
@@ -144,12 +151,12 @@
     };
 
     // Now that we initialized some callbacks, let's create our compressor
-    jpeg_create_compress(&cinfo);
+    jpeg_create_compress(cinfo.get());
     dmgr.mBuffer = static_cast<JOCTET*>(out);
     dmgr.mBufferSize = maxOutSize;
     dmgr.mEncodedSize = 0;
     dmgr.mSuccess = true;
-    cinfo.client_data = static_cast<void*>(&dmgr);
+    cinfo->client_data = static_cast<void*>(&dmgr);
 
     // These lambdas become C-style function pointers and as per C++11 spec
     // may not capture anything.
@@ -171,28 +178,28 @@
         dmgr.mEncodedSize = dmgr.mBufferSize - dmgr.free_in_buffer;
         ALOGV("%s:%d Done with jpeg: %zu", __FUNCTION__, __LINE__, dmgr.mEncodedSize);
     };
-    cinfo.dest = reinterpret_cast<struct jpeg_destination_mgr*>(&dmgr);
-    cinfo.image_width = width;
-    cinfo.image_height = height;
-    cinfo.input_components = 1;
-    cinfo.in_color_space = JCS_GRAYSCALE;
+    cinfo->dest = static_cast<struct jpeg_destination_mgr*>(&dmgr);
+    cinfo->image_width = width;
+    cinfo->image_height = height;
+    cinfo->input_components = 1;
+    cinfo->in_color_space = JCS_GRAYSCALE;
 
     // Initialize defaults and then override what we want
-    jpeg_set_defaults(&cinfo);
+    jpeg_set_defaults(cinfo.get());
 
-    jpeg_set_quality(&cinfo, jpegQuality, 1);
-    jpeg_set_colorspace(&cinfo, JCS_GRAYSCALE);
-    cinfo.raw_data_in = 0;
-    cinfo.dct_method = JDCT_IFAST;
+    jpeg_set_quality(cinfo.get(), jpegQuality, 1);
+    jpeg_set_colorspace(cinfo.get(), JCS_GRAYSCALE);
+    cinfo->raw_data_in = 0;
+    cinfo->dct_method = JDCT_IFAST;
 
-    cinfo.comp_info[0].h_samp_factor = 1;
-    cinfo.comp_info[1].h_samp_factor = 1;
-    cinfo.comp_info[2].h_samp_factor = 1;
-    cinfo.comp_info[0].v_samp_factor = 1;
-    cinfo.comp_info[1].v_samp_factor = 1;
-    cinfo.comp_info[2].v_samp_factor = 1;
+    cinfo->comp_info[0].h_samp_factor = 1;
+    cinfo->comp_info[1].h_samp_factor = 1;
+    cinfo->comp_info[2].h_samp_factor = 1;
+    cinfo->comp_info[0].v_samp_factor = 1;
+    cinfo->comp_info[1].v_samp_factor = 1;
+    cinfo->comp_info[2].v_samp_factor = 1;
 
-    jpeg_start_compress(&cinfo, TRUE);
+    jpeg_start_compress(cinfo.get(), TRUE);
 
     if (exifOrientation != ExifOrientation::ORIENTATION_UNDEFINED) {
         std::unique_ptr<ExifUtils> utils(ExifUtils::create());
@@ -204,19 +211,19 @@
         if (utils->generateApp1()) {
             const uint8_t* exifBuffer = utils->getApp1Buffer();
             size_t exifBufferSize = utils->getApp1Length();
-            jpeg_write_marker(&cinfo, JPEG_APP0 + 1, static_cast<const JOCTET*>(exifBuffer),
+            jpeg_write_marker(cinfo.get(), JPEG_APP0 + 1, static_cast<const JOCTET*>(exifBuffer),
                     exifBufferSize);
         } else {
             ALOGE("%s: Unable to generate App1 buffer", __FUNCTION__);
         }
     }
 
-    for (size_t i = 0; i < cinfo.image_height; i++) {
+    for (size_t i = 0; i < cinfo->image_height; i++) {
         auto currentRow  = static_cast<JSAMPROW>(in + i*width);
-        jpeg_write_scanlines(&cinfo, &currentRow, /*num_lines*/1);
+        jpeg_write_scanlines(cinfo.get(), &currentRow, /*num_lines*/1);
     }
 
-    jpeg_finish_compress(&cinfo);
+    jpeg_finish_compress(cinfo.get());
 
     actualSize = dmgr.mEncodedSize;
     if (dmgr.mSuccess) {
@@ -430,12 +437,12 @@
         return BAD_VALUE;
     }
 
-    // It is not possible to generate an imaging model without instrinsic calibration.
-    if (inputFrame.mIsInstrinsicCalibrationValid) {
+    // It is not possible to generate an imaging model without intrinsic calibration.
+    if (inputFrame.mIsIntrinsicCalibrationValid) {
         // The camera intrinsic calibration layout is as follows:
         // [focalLengthX, focalLengthY, opticalCenterX, opticalCenterY, skew]
-        const dynamic_depth::Point<double> focalLength(inputFrame.mInstrinsicCalibration[0],
-                inputFrame.mInstrinsicCalibration[1]);
+        const dynamic_depth::Point<double> focalLength(inputFrame.mIntrinsicCalibration[0],
+                inputFrame.mIntrinsicCalibration[1]);
         size_t width = inputFrame.mMainJpegWidth;
         size_t height = inputFrame.mMainJpegHeight;
         if (switchDimensions) {
@@ -444,9 +451,9 @@
         }
         const Dimension imageSize(width, height);
         ImagingModelParams imagingParams(focalLength, imageSize);
-        imagingParams.principal_point.x = inputFrame.mInstrinsicCalibration[2];
-        imagingParams.principal_point.y = inputFrame.mInstrinsicCalibration[3];
-        imagingParams.skew = inputFrame.mInstrinsicCalibration[4];
+        imagingParams.principal_point.x = inputFrame.mIntrinsicCalibration[2];
+        imagingParams.principal_point.y = inputFrame.mIntrinsicCalibration[3];
+        imagingParams.skew = inputFrame.mIntrinsicCalibration[4];
 
         // The camera lens distortion contains the following lens correction coefficients.
         // [kappa_1, kappa_2, kappa_3 kappa_4, kappa_5]
diff --git a/services/camera/libcameraservice/common/DepthPhotoProcessor.h b/services/camera/libcameraservice/common/DepthPhotoProcessor.h
index 6a2fbff..ba5ca9e 100644
--- a/services/camera/libcameraservice/common/DepthPhotoProcessor.h
+++ b/services/camera/libcameraservice/common/DepthPhotoProcessor.h
@@ -39,8 +39,8 @@
     size_t                mMaxJpegSize;
     uint8_t               mJpegQuality;
     uint8_t               mIsLogical;
-    float                 mInstrinsicCalibration[5];
-    uint8_t               mIsInstrinsicCalibrationValid;
+    float                 mIntrinsicCalibration[5];
+    uint8_t               mIsIntrinsicCalibrationValid;
     float                 mLensDistortion[5];
     uint8_t               mIsLensDistortionValid;
     DepthPhotoOrientation mOrientation;
@@ -57,8 +57,8 @@
             mMaxJpegSize(0),
             mJpegQuality(100),
             mIsLogical(0),
-            mInstrinsicCalibration{0.f},
-            mIsInstrinsicCalibrationValid(0),
+            mIntrinsicCalibration{0.f},
+            mIsIntrinsicCalibrationValid(0),
             mLensDistortion{0.f},
             mIsLensDistortionValid(0),
             mOrientation(DepthPhotoOrientation::DEPTH_ORIENTATION_0_DEGREES) {}
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 9771f9e..4227a3b 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -29,6 +29,9 @@
 #define CLOGE(fmt, ...) ALOGE("Camera %s: %s: " fmt, mId.string(), __FUNCTION__, \
             ##__VA_ARGS__)
 
+#define CLOGW(fmt, ...) ALOGW("Camera %s: %s: " fmt, mId.string(), __FUNCTION__, \
+            ##__VA_ARGS__)
+
 // Convenience macros for transitioning to the error state
 #define SET_ERR(fmt, ...) setErrorState(   \
     "%s: " fmt, __FUNCTION__,              \
@@ -3267,14 +3270,19 @@
         ALOGVV("%s: removed frame %d from InFlightMap", __FUNCTION__, frameNumber);
      }
 
-    // Sanity check - if we have too many in-flight frames, something has
-    // likely gone wrong
-    if (!mIsConstrainedHighSpeedConfiguration && mInFlightMap.size() > kInFlightWarnLimit) {
-        CLOGE("In-flight list too large: %zu", mInFlightMap.size());
-    } else if (mIsConstrainedHighSpeedConfiguration && mInFlightMap.size() >
-            kInFlightWarnLimitHighSpeed) {
-        CLOGE("In-flight list too large for high speed configuration: %zu",
-                mInFlightMap.size());
+    // Sanity check - if we have too many in-flight frames with long total inflight duration,
+    // something has likely gone wrong. This might still be legit only if application send in
+    // a long burst of long exposure requests.
+    if (mExpectedInflightDuration > kMinWarnInflightDuration) {
+        if (!mIsConstrainedHighSpeedConfiguration && mInFlightMap.size() > kInFlightWarnLimit) {
+            CLOGW("In-flight list too large: %zu, total inflight duration %" PRIu64,
+                    mInFlightMap.size(), mExpectedInflightDuration);
+        } else if (mIsConstrainedHighSpeedConfiguration && mInFlightMap.size() >
+                kInFlightWarnLimitHighSpeed) {
+            CLOGW("In-flight list too large for high speed configuration: %zu,"
+                    "total inflight duration %" PRIu64,
+                    mInFlightMap.size(), mExpectedInflightDuration);
+        }
     }
 }
 
@@ -4364,7 +4372,7 @@
         int overrideFormat = mapToFrameworkFormat(src.v3_2.overrideFormat);
         android_dataspace overrideDataSpace = mapToFrameworkDataspace(src.overrideDataSpace);
 
-        if (dst->format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
+        if (dstStream->getOriginalFormat() != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
             dstStream->setFormatOverride(false);
             dstStream->setDataSpaceOverride(false);
             if (dst->format != overrideFormat) {
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 6e8ac84..cae34ce 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -227,6 +227,7 @@
     static const size_t        kDumpLockAttempts  = 10;
     static const size_t        kDumpSleepDuration = 100000; // 0.10 sec
     static const nsecs_t       kActiveTimeout     = 500000000;  // 500 ms
+    static const nsecs_t       kMinWarnInflightDuration = 5000000000; // 5 s
     static const size_t        kInFlightWarnLimit = 30;
     static const size_t        kInFlightWarnLimitHighSpeed = 256; // batch size 32 * pipe depth 8
     static const nsecs_t       kDefaultExpectedDuration = 100000000; // 100 ms
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 2df084b..fd9b4b0 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -68,7 +68,7 @@
     mLastMaxCount(Camera3StreamInterface::ALLOCATE_PIPELINE_MAX),
     mBufferLimitLatency(kBufferLimitLatencyBinSize),
     mFormatOverridden(false),
-    mOriginalFormat(-1),
+    mOriginalFormat(format),
     mDataSpaceOverridden(false),
     mOriginalDataSpace(HAL_DATASPACE_UNKNOWN),
     mPhysicalCameraId(physicalCameraId),
@@ -125,9 +125,6 @@
 
 void Camera3Stream::setFormatOverride(bool formatOverridden) {
     mFormatOverridden = formatOverridden;
-    if (formatOverridden && mOriginalFormat == -1) {
-        mOriginalFormat = camera3_stream::format;
-    }
 }
 
 bool Camera3Stream::isFormatOverridden() const {
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 533318f..67afd0f 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -582,9 +582,9 @@
     static const int32_t kBufferLimitLatencyBinSize = 33; //in ms
     CameraLatencyHistogram mBufferLimitLatency;
 
-    //Keep track of original format in case it gets overridden
+    //Keep track of original format when the stream is created in case it gets overridden
     bool mFormatOverridden;
-    int mOriginalFormat;
+    const int mOriginalFormat;
 
     //Keep track of original dataSpace in case it gets overridden
     bool mDataSpaceOverridden;
diff --git a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
index f47e5a5..78d737d 100644
--- a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
+++ b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
@@ -205,6 +205,11 @@
         return mTestCameraProvider;
     }
 
+    virtual hardware::hidl_vec<hardware::hidl_string> listServices() override {
+        hardware::hidl_vec<hardware::hidl_string> ret = {"test/0"};
+        return ret;
+    }
+
 };
 
 struct TestStatusListener : public CameraProviderManager::StatusListener {
@@ -231,37 +236,24 @@
             vendorSection);
     serviceProxy.setProvider(provider);
 
+    int numProviders = static_cast<int>(serviceProxy.listServices().size());
+
     res = providerManager->initialize(statusListener, &serviceProxy);
     ASSERT_EQ(res, OK) << "Unable to initialize provider manager";
     // Check that both "legacy" and "external" providers (really the same object) are called
     // once for all the init methods
-    EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::SET_CALLBACK], 2) <<
+    EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::SET_CALLBACK], numProviders) <<
             "Only one call to setCallback per provider expected during init";
-    EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::GET_VENDOR_TAGS], 2) <<
+    EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::GET_VENDOR_TAGS], numProviders) <<
             "Only one call to getVendorTags per provider expected during init";
-    EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::IS_SET_TORCH_MODE_SUPPORTED], 2) <<
+    EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::IS_SET_TORCH_MODE_SUPPORTED],
+            numProviders) <<
             "Only one call to isSetTorchModeSupported per provider expected during init";
-    EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::GET_CAMERA_ID_LIST], 2) <<
+    EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::GET_CAMERA_ID_LIST], numProviders) <<
             "Only one call to getCameraIdList per provider expected during init";
-    EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::NOTIFY_DEVICE_STATE], 2) <<
+    EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::NOTIFY_DEVICE_STATE], numProviders) <<
             "Only one call to notifyDeviceState per provider expected during init";
 
-    std::string legacyInstanceName = "legacy/0";
-    std::string externalInstanceName = "external/0";
-    bool gotLegacy = false;
-    bool gotExternal = false;
-    EXPECT_EQ(2u, serviceProxy.mLastRequestedServiceNames.size()) <<
-            "Only two service queries expected to be seen by hardware service manager";
-
-    for (auto& serviceName : serviceProxy.mLastRequestedServiceNames) {
-        if (serviceName == legacyInstanceName) gotLegacy = true;
-        if (serviceName == externalInstanceName) gotExternal = true;
-    }
-    ASSERT_TRUE(gotLegacy) <<
-            "Legacy instance not requested from service manager";
-    ASSERT_TRUE(gotExternal) <<
-            "External instance not requested from service manager";
-
     hardware::hidl_string testProviderFqInterfaceName =
             "android.hardware.camera.provider@2.4::ICameraProvider";
     hardware::hidl_string testProviderInstanceName = "test/0";
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
index 3626ad1..0e7edfd 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediaanalytics/MediaAnalyticsService.cpp
@@ -82,7 +82,12 @@
 // (0 for either of these disables that threshold)
 //
 static constexpr nsecs_t kMaxRecordAgeNs =  28 * 3600 * (1000*1000*1000ll);
-static constexpr int kMaxRecords    = 0;
+// 2019/6: average daily per device is currently 375-ish;
+// setting this to 2000 is large enough to catch most devices
+// we'll lose some data on very very media-active devices, but only for
+// the gms collection; statsd will have already covered those for us.
+// This also retains enough information to help with bugreports
+static constexpr int kMaxRecords    = 2000;
 
 // max we expire in a single call, to constrain how long we hold the
 // mutex, which also constrains how long a client might wait.