Merge "cameraserver: Stop appOps camera reporting for vendor clients." into qt-dev
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
index d7dacb8..d74bc53 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
@@ -401,20 +401,25 @@
 
         sendExpirationUpdate(sessionId, 100);
 
-        std::vector<KeyStatus> keysStatus;
-        KeyStatus keyStatus;
+        std::vector<KeyStatus_V1_2> keysStatus;
+        KeyStatus_V1_2 keyStatus;
 
         std::vector<uint8_t> keyId1 = { 0xA, 0xB, 0xC };
         keyStatus.keyId = keyId1;
-        keyStatus.type = V1_0::KeyStatusType::USABLE;
+        keyStatus.type = V1_2::KeyStatusType::USABLE;
         keysStatus.push_back(keyStatus);
 
         std::vector<uint8_t> keyId2 = { 0xD, 0xE, 0xF };
         keyStatus.keyId = keyId2;
-        keyStatus.type = V1_0::KeyStatusType::EXPIRED;
+        keyStatus.type = V1_2::KeyStatusType::EXPIRED;
         keysStatus.push_back(keyStatus);
 
-        sendKeysChange(sessionId, keysStatus, true);
+        std::vector<uint8_t> keyId3 = { 0x0, 0x1, 0x2 };
+        keyStatus.keyId = keyId3;
+        keyStatus.type = V1_2::KeyStatusType::USABLEINFUTURE;
+        keysStatus.push_back(keyStatus);
+
+        sendKeysChange_1_2(sessionId, keysStatus, true);
 
         installSecureStop(sessionId);
     } else {
diff --git a/media/codec2/components/opus/C2SoftOpusEnc.cpp b/media/codec2/components/opus/C2SoftOpusEnc.cpp
index 384d58b..70d1965 100644
--- a/media/codec2/components/opus/C2SoftOpusEnc.cpp
+++ b/media/codec2/components/opus/C2SoftOpusEnc.cpp
@@ -471,11 +471,11 @@
         uint8_t* outPtr = wView.data() + mBytesEncoded;
         int encodedBytes =
             opus_multistream_encode(mEncoder, mInputBufferPcm16,
-                                    mNumSamplesPerFrame, outPtr, kMaxPayload);
+                                    mNumSamplesPerFrame, outPtr, kMaxPayload - mBytesEncoded);
         ALOGV("encoded %i Opus bytes from %zu PCM bytes", encodedBytes,
               processSize);
 
-        if (encodedBytes < 0 || encodedBytes > kMaxPayload) {
+        if (encodedBytes < 0 || encodedBytes > (kMaxPayload - mBytesEncoded)) {
             ALOGE("opus_encode failed, encodedBytes : %d", encodedBytes);
             mSignalledError = true;
             work->result = C2_CORRUPTED;
diff --git a/media/codec2/components/opus/C2SoftOpusEnc.h b/media/codec2/components/opus/C2SoftOpusEnc.h
index 69e5240..2b4d8f2 100644
--- a/media/codec2/components/opus/C2SoftOpusEnc.h
+++ b/media/codec2/components/opus/C2SoftOpusEnc.h
@@ -47,7 +47,9 @@
 private:
     /* OPUS_FRAMESIZE_20_MS */
     const int kFrameSize = 960;
-    const int kMaxPayload = 4000;
+    const int kMaxSampleRate = 48000;
+    const int kMinSampleRate = 8000;
+    const int kMaxPayload = (4000 * kMaxSampleRate) / kMinSampleRate;
     const int kMaxNumChannels = 8;
 
     std::shared_ptr<IntfImpl> mIntf;
diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h
index 9f484a3..3820f90 100644
--- a/media/codec2/core/include/C2Config.h
+++ b/media/codec2/core/include/C2Config.h
@@ -117,7 +117,7 @@
 
     /* pipeline characteristics */
     kParamIndexMediaType,
-    kParamIndexDelayRequest,
+    __kParamIndexRESERVED_0,
     kParamIndexDelay,
     kParamIndexMaxReferenceAge,
     kParamIndexMaxReferenceCount,
@@ -151,6 +151,9 @@
     /* protected content */
     kParamIndexSecureMode,
 
+    // deprecated
+    kParamIndexDelayRequest = kParamIndexDelay | C2Param::CoreIndex::IS_REQUEST_FLAG,
+
     /* ------------------------------------ (trans/en)coders ------------------------------------ */
 
     kParamIndexBitrate = C2_PARAM_INDEX_CODER_PARAM_START,
@@ -779,22 +782,26 @@
  * outstanding input frames queued to the component, it shall produce output.
  */
 
-typedef C2PortParam<C2Tuning, C2Uint32Value, kParamIndexDelayRequest> C2PortRequestedDelayTuning;
-constexpr char C2_PARAMKEY_INPUT_DELAY_REQUEST[] = "input.delay.requested";
-constexpr char C2_PARAMKEY_OUTPUT_DELAY_REQUEST[] = "output.delay.requested";
+typedef C2PortParam<C2Tuning, C2Uint32Value, kParamIndexDelay | C2Param::CoreIndex::IS_REQUEST_FLAG>
+        C2PortRequestedDelayTuning;
+constexpr char C2_PARAMKEY_INPUT_DELAY_REQUEST[] = "input.delay"; // deprecated
+constexpr char C2_PARAMKEY_OUTPUT_DELAY_REQUEST[] = "output.delay"; // deprecated
 
-typedef C2GlobalParam<C2Tuning, C2Uint32Value, kParamIndexDelayRequest>
+typedef C2GlobalParam<C2Tuning, C2Uint32Value,
+                kParamIndexDelay | C2Param::CoreIndex::IS_REQUEST_FLAG>
         C2RequestedPipelineDelayTuning;
-constexpr char C2_PARAMKEY_PIPELINE_DELAY_REQUEST[] = "pipeline-delay.requested";
+constexpr char C2_PARAMKEY_PIPELINE_DELAY_REQUEST[] = "algo.delay"; // deprecated
 
 // read-only
-typedef C2PortParam<C2Tuning, C2Uint32Value, kParamIndexDelay> C2PortActualDelayTuning;
-constexpr char C2_PARAMKEY_INPUT_DELAY[] = "input.delay.actual";
-constexpr char C2_PARAMKEY_OUTPUT_DELAY[] = "output.delay.actual";
+typedef C2PortParam<C2Tuning, C2Uint32Value, kParamIndexDelay> C2PortDelayTuning;
+typedef C2PortDelayTuning C2PortActualDelayTuning; // deprecated
+constexpr char C2_PARAMKEY_INPUT_DELAY[] = "input.delay";
+constexpr char C2_PARAMKEY_OUTPUT_DELAY[] = "output.delay";
 
 // read-only
-typedef C2GlobalParam<C2Tuning, C2Uint32Value, kParamIndexDelay> C2ActualPipelineDelayTuning;
-constexpr char C2_PARAMKEY_PIPELINE_DELAY[] = "algo.delay.actual";
+typedef C2GlobalParam<C2Tuning, C2Uint32Value, kParamIndexDelay> C2PipelineDelayTuning;
+typedef C2PipelineDelayTuning C2ActualPipelineDelayTuning; // deprecated
+constexpr char C2_PARAMKEY_PIPELINE_DELAY[] = "algo.delay";
 
 /**
  * Reference characteristics.
diff --git a/media/codec2/core/include/C2Param.h b/media/codec2/core/include/C2Param.h
index d264bf3..cc8c17a 100644
--- a/media/codec2/core/include/C2Param.h
+++ b/media/codec2/core/include/C2Param.h
@@ -158,7 +158,8 @@
     struct CoreIndex {
     //public:
         enum : uint32_t {
-            IS_FLEX_FLAG = 0x00010000,
+            IS_FLEX_FLAG    = 0x00010000,
+            IS_REQUEST_FLAG = 0x00020000,
         };
 
     protected:
@@ -175,9 +176,9 @@
             DIR_INPUT      = 0x00000000,
             DIR_OUTPUT     = 0x10000000,
 
-            IS_STREAM_FLAG  = 0x02000000,
-            STREAM_ID_MASK  = 0x01FE0000,
-            STREAM_ID_SHIFT = 17,
+            IS_STREAM_FLAG  = 0x00100000,
+            STREAM_ID_MASK  = 0x03E00000,
+            STREAM_ID_SHIFT = 21,
             MAX_STREAM_ID   = STREAM_ID_MASK >> STREAM_ID_SHIFT,
             STREAM_MASK     = IS_STREAM_FLAG | STREAM_ID_MASK,
 
@@ -360,6 +361,10 @@
             mIndex = (mIndex & ~(DIR_MASK | IS_STREAM_FLAG)) | DIR_GLOBAL;
         }
 
+        inline void convertToRequest() {
+            mIndex = mIndex | IS_REQUEST_FLAG;
+        }
+
         /**
          * Sets the stream index.
          * \return true on success, false if could not set index (e.g. not a stream param).
@@ -476,6 +481,15 @@
         return copy;
     }
 
+    /// Returns managed clone of |orig| as a stream parameter at heap.
+    inline static std::unique_ptr<C2Param> CopyAsRequest(const C2Param &orig) {
+        std::unique_ptr<C2Param> copy = Copy(orig);
+        if (copy) {
+            copy->_mIndex.convertToRequest();
+        }
+        return copy;
+    }
+
 #if 0
     template<typename P, class=decltype(C2Param(P()))>
     P *As() { return P::From(this); }
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index f5a4d94..76f132c 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -1724,8 +1724,11 @@
                             & C2FrameData::FLAG_DISCARD_FRAME) == 0) {
 
                 // copy buffer info to config
-                std::vector<std::unique_ptr<C2Param>> updates =
-                    std::move(work->worklets.front()->output.configUpdate);
+                std::vector<std::unique_ptr<C2Param>> updates;
+                for (const std::unique_ptr<C2Param> &param
+                        : work->worklets.front()->output.configUpdate) {
+                    updates.push_back(C2Param::Copy(*param));
+                }
                 unsigned stream = 0;
                 for (const std::shared_ptr<C2Buffer> &buf : work->worklets.front()->output.buffers) {
                     for (const std::shared_ptr<const C2Info> &info : buf->info()) {
@@ -1739,7 +1742,7 @@
                         //      block.width(), block.height());
                         updates.emplace_back(new C2StreamCropRectInfo::output(stream, block.crop()));
                         updates.emplace_back(new C2StreamPictureSizeInfo::output(
-                                stream, block.width(), block.height()));
+                                stream, block.crop().width, block.crop().height));
                         break; // for now only do the first block
                     }
                     ++stream;
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 7669421..eb20b20 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -210,22 +210,36 @@
     }
 }
 
+// Input
+
+CCodecBufferChannel::Input::Input() : extraBuffers("extra") {}
+
 // CCodecBufferChannel
 
 CCodecBufferChannel::CCodecBufferChannel(
         const std::shared_ptr<CCodecCallback> &callback)
     : mHeapSeqNum(-1),
       mCCodecCallback(callback),
-      mNumInputSlots(kSmoothnessFactor),
-      mNumOutputSlots(kSmoothnessFactor),
       mDelay(0),
       mFrameIndex(0u),
       mFirstValidFrameIndex(0u),
       mMetaMode(MODE_NONE),
       mInputMetEos(false) {
     mOutputSurface.lock()->maxDequeueBuffers = kSmoothnessFactor + kRenderingDepth;
-    Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
-    buffers->reset(new DummyInputBuffers(""));
+    {
+        Mutexed<Input>::Locked input(mInput);
+        input->buffers.reset(new DummyInputBuffers(""));
+        input->extraBuffers.flush();
+        input->inputDelay = 0u;
+        input->pipelineDelay = 0u;
+        input->numSlots = kSmoothnessFactor;
+        input->numExtraSlots = 0u;
+    }
+    {
+        Mutexed<Output>::Locked output(mOutput);
+        output->outputDelay = 0u;
+        output->numSlots = kSmoothnessFactor;
+    }
 }
 
 CCodecBufferChannel::~CCodecBufferChannel() {
@@ -255,7 +269,7 @@
     return mInputSurface->signalEndOfInputStream();
 }
 
-status_t CCodecBufferChannel::queueInputBufferInternal(const sp<MediaCodecBuffer> &buffer) {
+status_t CCodecBufferChannel::queueInputBufferInternal(sp<MediaCodecBuffer> buffer) {
     int64_t timeUs;
     CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
 
@@ -287,13 +301,31 @@
 
     uint64_t queuedFrameIndex = work->input.ordinal.frameIndex.peeku();
     std::vector<std::shared_ptr<C2Buffer>> queuedBuffers;
+    sp<Codec2Buffer> copy;
 
     if (buffer->size() > 0u) {
-        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
+        Mutexed<Input>::Locked input(mInput);
         std::shared_ptr<C2Buffer> c2buffer;
-        if (!(*buffers)->releaseBuffer(buffer, &c2buffer, false)) {
+        if (!input->buffers->releaseBuffer(buffer, &c2buffer, false)) {
             return -ENOENT;
         }
+        // TODO: we want to delay copying buffers.
+        if (input->extraBuffers.numComponentBuffers() < input->numExtraSlots) {
+            copy = input->buffers->cloneAndReleaseBuffer(buffer);
+            if (copy != nullptr) {
+                (void)input->extraBuffers.assignSlot(copy);
+                if (!input->extraBuffers.releaseSlot(copy, &c2buffer, false)) {
+                    return UNKNOWN_ERROR;
+                }
+                bool released = input->buffers->releaseBuffer(buffer, nullptr, true);
+                ALOGV("[%s] queueInputBuffer: buffer copied; %sreleased",
+                      mName, released ? "" : "not ");
+                buffer.clear();
+            } else {
+                ALOGW("[%s] queueInputBuffer: failed to copy a buffer; this may cause input "
+                      "buffer starvation on component.", mName);
+            }
+        }
         work->input.buffers.push_back(c2buffer);
         queuedBuffers.push_back(c2buffer);
     } else if (eos) {
@@ -343,9 +375,15 @@
         }
     }
     if (err == C2_OK) {
-        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
-        bool released = (*buffers)->releaseBuffer(buffer, nullptr, true);
-        ALOGV("[%s] queueInputBuffer: buffer %sreleased", mName, released ? "" : "not ");
+        Mutexed<Input>::Locked input(mInput);
+        bool released = false;
+        if (buffer) {
+            released = input->buffers->releaseBuffer(buffer, nullptr, true);
+        } else if (copy) {
+            released = input->extraBuffers.releaseSlot(copy, nullptr, true);
+        }
+        ALOGV("[%s] queueInputBuffer: buffer%s %sreleased",
+              mName, (buffer == nullptr) ? "(copy)" : "", released ? "" : "not ");
     }
 
     feedInputBufferIfAvailableInternal();
@@ -492,20 +530,21 @@
            mPipelineWatcher.lock()->pipelineFull()) {
         return;
     } else {
-        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-        if ((*buffers)->numClientBuffers() >= mNumOutputSlots) {
+        Mutexed<Output>::Locked output(mOutput);
+        if (output->buffers->numClientBuffers() >= output->numSlots) {
             return;
         }
     }
-    for (size_t i = 0; i < mNumInputSlots; ++i) {
+    size_t numInputSlots = mInput.lock()->numSlots;
+    for (size_t i = 0; i < numInputSlots; ++i) {
         sp<MediaCodecBuffer> inBuffer;
         size_t index;
         {
-            Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
-            if ((*buffers)->numClientBuffers() >= mNumInputSlots) {
+            Mutexed<Input>::Locked input(mInput);
+            if (input->buffers->numClientBuffers() >= input->numSlots) {
                 return;
             }
-            if (!(*buffers)->requestNewBuffer(&index, &inBuffer)) {
+            if (!input->buffers->requestNewBuffer(&index, &inBuffer)) {
                 ALOGV("[%s] no new buffer available", mName);
                 break;
             }
@@ -521,9 +560,9 @@
     std::shared_ptr<C2Buffer> c2Buffer;
     bool released = false;
     {
-        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-        if (*buffers) {
-            released = (*buffers)->releaseBuffer(buffer, &c2Buffer);
+        Mutexed<Output>::Locked output(mOutput);
+        if (output->buffers) {
+            released = output->buffers->releaseBuffer(buffer, &c2Buffer);
         }
     }
     // NOTE: some apps try to releaseOutputBuffer() with timestamp and/or render
@@ -685,14 +724,14 @@
     ALOGV("[%s] discardBuffer: %p", mName, buffer.get());
     bool released = false;
     {
-        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
-        if (*buffers && (*buffers)->releaseBuffer(buffer, nullptr, true)) {
+        Mutexed<Input>::Locked input(mInput);
+        if (input->buffers && input->buffers->releaseBuffer(buffer, nullptr, true)) {
             released = true;
         }
     }
     {
-        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-        if (*buffers && (*buffers)->releaseBuffer(buffer, nullptr)) {
+        Mutexed<Output>::Locked output(mOutput);
+        if (output->buffers && output->buffers->releaseBuffer(buffer, nullptr)) {
             released = true;
         }
     }
@@ -707,24 +746,24 @@
 
 void CCodecBufferChannel::getInputBufferArray(Vector<sp<MediaCodecBuffer>> *array) {
     array->clear();
-    Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
+    Mutexed<Input>::Locked input(mInput);
 
-    if (!(*buffers)->isArrayMode()) {
-        *buffers = (*buffers)->toArrayMode(mNumInputSlots);
+    if (!input->buffers->isArrayMode()) {
+        input->buffers = input->buffers->toArrayMode(input->numSlots);
     }
 
-    (*buffers)->getArray(array);
+    input->buffers->getArray(array);
 }
 
 void CCodecBufferChannel::getOutputBufferArray(Vector<sp<MediaCodecBuffer>> *array) {
     array->clear();
-    Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
+    Mutexed<Output>::Locked output(mOutput);
 
-    if (!(*buffers)->isArrayMode()) {
-        *buffers = (*buffers)->toArrayMode(mNumOutputSlots);
+    if (!output->buffers->isArrayMode()) {
+        output->buffers = output->buffers->toArrayMode(output->numSlots);
     }
 
-    (*buffers)->getArray(array);
+    output->buffers->getArray(array);
 }
 
 status_t CCodecBufferChannel::start(
@@ -773,8 +812,8 @@
     uint32_t pipelineDelayValue = pipelineDelay ? pipelineDelay.value : 0;
     uint32_t outputDelayValue = outputDelay ? outputDelay.value : 0;
 
-    mNumInputSlots = inputDelayValue + pipelineDelayValue + kSmoothnessFactor;
-    mNumOutputSlots = outputDelayValue + kSmoothnessFactor;
+    size_t numInputSlots = inputDelayValue + pipelineDelayValue + kSmoothnessFactor;
+    size_t numOutputSlots = outputDelayValue + kSmoothnessFactor;
     mDelay = inputDelayValue + pipelineDelayValue + outputDelayValue;
 
     // TODO: get this from input format
@@ -848,14 +887,17 @@
         }
 
         bool forceArrayMode = false;
-        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
+        Mutexed<Input>::Locked input(mInput);
+        input->numSlots = numInputSlots;
+        input->extraBuffers.flush();
+        input->numExtraSlots = 0u;
         if (graphic) {
             if (mInputSurface) {
-                buffers->reset(new DummyInputBuffers(mName));
+                input->buffers.reset(new DummyInputBuffers(mName));
             } else if (mMetaMode == MODE_ANW) {
-                buffers->reset(new GraphicMetadataInputBuffers(mName));
+                input->buffers.reset(new GraphicMetadataInputBuffers(mName));
             } else {
-                buffers->reset(new GraphicInputBuffers(mNumInputSlots, mName));
+                input->buffers.reset(new GraphicInputBuffers(numInputSlots, mName));
             }
         } else {
             if (hasCryptoOrDescrambler()) {
@@ -868,7 +910,7 @@
                 if (mDealer == nullptr) {
                     mDealer = new MemoryDealer(
                             align(capacity, MemoryDealer::getAllocationAlignment())
-                                * (mNumInputSlots + 1),
+                                * (numInputSlots + 1),
                             "EncryptedLinearInputBuffers");
                     mDecryptDestination = mDealer->allocate((size_t)capacity);
                 }
@@ -877,24 +919,24 @@
                 } else {
                     mHeapSeqNum = -1;
                 }
-                buffers->reset(new EncryptedLinearInputBuffers(
+                input->buffers.reset(new EncryptedLinearInputBuffers(
                         secure, mDealer, mCrypto, mHeapSeqNum, (size_t)capacity,
-                        mNumInputSlots, mName));
+                        numInputSlots, mName));
                 forceArrayMode = true;
             } else {
-                buffers->reset(new LinearInputBuffers(mName));
+                input->buffers.reset(new LinearInputBuffers(mName));
             }
         }
-        (*buffers)->setFormat(inputFormat);
+        input->buffers->setFormat(inputFormat);
 
         if (err == C2_OK) {
-            (*buffers)->setPool(pool);
+            input->buffers->setPool(pool);
         } else {
             // TODO: error
         }
 
         if (forceArrayMode) {
-            *buffers = (*buffers)->toArrayMode(mNumInputSlots);
+            input->buffers = input->buffers->toArrayMode(numInputSlots);
         }
     }
 
@@ -903,7 +945,7 @@
         uint32_t outputGeneration;
         {
             Mutexed<OutputSurface>::Locked output(mOutputSurface);
-            output->maxDequeueBuffers = mNumOutputSlots + reorderDepth.value + kRenderingDepth;
+            output->maxDequeueBuffers = numOutputSlots + reorderDepth.value + kRenderingDepth;
             outputSurface = output->surface ?
                     output->surface->getIGraphicBufferProducer() : nullptr;
             if (outputSurface) {
@@ -1011,18 +1053,18 @@
             outputPoolId_ = pools->outputPoolId;
         }
 
-        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-
+        Mutexed<Output>::Locked output(mOutput);
+        output->numSlots = numOutputSlots;
         if (graphic) {
             if (outputSurface) {
-                buffers->reset(new GraphicOutputBuffers(mName));
+                output->buffers.reset(new GraphicOutputBuffers(mName));
             } else {
-                buffers->reset(new RawGraphicOutputBuffers(mNumOutputSlots, mName));
+                output->buffers.reset(new RawGraphicOutputBuffers(numOutputSlots, mName));
             }
         } else {
-            buffers->reset(new LinearOutputBuffers(mName));
+            output->buffers.reset(new LinearOutputBuffers(mName));
         }
-        (*buffers)->setFormat(outputFormat->dup());
+        output->buffers->setFormat(outputFormat->dup());
 
 
         // Try to set output surface to created block pool if given.
@@ -1038,7 +1080,7 @@
             // 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.
-            (*buffers) = (*buffers)->toArrayMode(mNumOutputSlots);
+            output->buffers = output->buffers->toArrayMode(numOutputSlots);
 
             int32_t channelCount;
             int32_t sampleRate;
@@ -1055,7 +1097,7 @@
                 if (delay || padding) {
                     // We need write access to the buffers, and we're already in
                     // array mode.
-                    (*buffers)->initSkipCutBuffer(delay, padding, sampleRate, channelCount);
+                    output->buffers->initSkipCutBuffer(delay, padding, sampleRate, channelCount);
                 }
             }
         }
@@ -1090,14 +1132,14 @@
     if (err != C2_OK) {
         return UNKNOWN_ERROR;
     }
+    size_t numInputSlots = mInput.lock()->numSlots;
     std::vector<sp<MediaCodecBuffer>> toBeQueued;
-    // TODO: use proper buffer depth instead of this random value
-    for (size_t i = 0; i < mNumInputSlots; ++i) {
+    for (size_t i = 0; i < numInputSlots; ++i) {
         size_t index;
         sp<MediaCodecBuffer> buffer;
         {
-            Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
-            if (!(*buffers)->requestNewBuffer(&index, &buffer)) {
+            Mutexed<Input>::Locked input(mInput);
+            if (!input->buffers->requestNewBuffer(&index, &buffer)) {
                 if (i == 0) {
                     ALOGW("[%s] start: cannot allocate memory at all", mName);
                     return NO_MEMORY;
@@ -1182,12 +1224,13 @@
         }
     }
     {
-        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
-        (*buffers)->flush();
+        Mutexed<Input>::Locked input(mInput);
+        input->buffers->flush();
+        input->extraBuffers.flush();
     }
     {
-        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-        (*buffers)->flush(flushedWork);
+        Mutexed<Output>::Locked output(mOutput);
+        output->buffers->flush(flushedWork);
     }
     mReorderStash.lock()->flush();
     mPipelineWatcher.lock()->flush();
@@ -1210,8 +1253,11 @@
             mPipelineWatcher.lock()->onInputBufferReleased(frameIndex, arrayIndex);
     bool newInputSlotAvailable;
     {
-        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
-        newInputSlotAvailable = (*buffers)->expireComponentBuffer(buffer);
+        Mutexed<Input>::Locked input(mInput);
+        newInputSlotAvailable = input->buffers->expireComponentBuffer(buffer);
+        if (!newInputSlotAvailable) {
+            (void)input->extraBuffers.expireComponentBuffer(buffer);
+        }
     }
     if (newInputSlotAvailable) {
         feedInputBufferIfAvailable();
@@ -1269,6 +1315,7 @@
         }
     }
 
+    std::optional<uint32_t> newInputDelay, newPipelineDelay;
     while (!worklet->output.configUpdate.empty()) {
         std::unique_ptr<C2Param> param;
         worklet->output.configUpdate.back().swap(param);
@@ -1280,8 +1327,10 @@
                     mReorderStash.lock()->setDepth(reorderDepth.value);
                     ALOGV("[%s] onWorkDone: updated reorder depth to %u",
                           mName, reorderDepth.value);
+                    size_t numOutputSlots = mOutput.lock()->numSlots;
                     Mutexed<OutputSurface>::Locked output(mOutputSurface);
-                    output->maxDequeueBuffers = mNumOutputSlots + reorderDepth.value + kRenderingDepth;
+                    output->maxDequeueBuffers =
+                        numOutputSlots + reorderDepth.value + kRenderingDepth;
                     if (output->surface) {
                         output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers);
                     }
@@ -1301,18 +1350,86 @@
                 }
                 break;
             }
+            case C2PortActualDelayTuning::CORE_INDEX: {
+                if (param->isGlobal()) {
+                    C2ActualPipelineDelayTuning pipelineDelay;
+                    if (pipelineDelay.updateFrom(*param)) {
+                        ALOGV("[%s] onWorkDone: updating pipeline delay %u",
+                              mName, pipelineDelay.value);
+                        newPipelineDelay = pipelineDelay.value;
+                        (void)mPipelineWatcher.lock()->pipelineDelay(pipelineDelay.value);
+                    }
+                }
+                if (param->forInput()) {
+                    C2PortActualDelayTuning::input inputDelay;
+                    if (inputDelay.updateFrom(*param)) {
+                        ALOGV("[%s] onWorkDone: updating input delay %u",
+                              mName, inputDelay.value);
+                        newInputDelay = inputDelay.value;
+                        (void)mPipelineWatcher.lock()->inputDelay(inputDelay.value);
+                    }
+                }
+                if (param->forOutput()) {
+                    C2PortActualDelayTuning::output outputDelay;
+                    if (outputDelay.updateFrom(*param)) {
+                        ALOGV("[%s] onWorkDone: updating output delay %u",
+                              mName, outputDelay.value);
+                        (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;
+                            }
+                        }
+                        output.unlock();
+
+                        if (outputBuffersChanged) {
+                            mCCodecCallback->onOutputBuffersChanged();
+                        }
+                    }
+                }
+                break;
+            }
             default:
                 ALOGV("[%s] onWorkDone: unrecognized config update (%08X)",
                       mName, param->index());
                 break;
         }
     }
+    if (newInputDelay || newPipelineDelay) {
+        Mutexed<Input>::Locked input(mInput);
+        size_t newNumSlots =
+            newInputDelay.value_or(input->inputDelay) +
+            newPipelineDelay.value_or(input->pipelineDelay) +
+            kSmoothnessFactor;
+        if (input->buffers->isArrayMode()) {
+            if (input->numSlots >= newNumSlots) {
+                input->numExtraSlots = 0;
+            } else {
+                input->numExtraSlots = newNumSlots - input->numSlots;
+            }
+            ALOGV("[%s] onWorkDone: updated number of extra slots to %zu (input array mode)",
+                  mName, input->numExtraSlots);
+        } else {
+            input->numSlots = newNumSlots;
+        }
+    }
 
     if (outputFormat != nullptr) {
-        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
+        Mutexed<Output>::Locked output(mOutput);
         ALOGD("[%s] onWorkDone: output format changed to %s",
                 mName, outputFormat->debugString().c_str());
-        (*buffers)->setFormat(outputFormat);
+        output->buffers->setFormat(outputFormat);
 
         AString mediaType;
         if (outputFormat->findString(KEY_MIME, &mediaType)
@@ -1321,7 +1438,7 @@
             int32_t sampleRate;
             if (outputFormat->findInt32(KEY_CHANNEL_COUNT, &channelCount)
                     && outputFormat->findInt32(KEY_SAMPLE_RATE, &sampleRate)) {
-                (*buffers)->updateSkipCutBuffer(sampleRate, channelCount);
+                output->buffers->updateSkipCutBuffer(sampleRate, channelCount);
             }
         }
     }
@@ -1356,20 +1473,18 @@
           timestamp.peekll());
 
     if (initData != nullptr) {
-        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-        if ((*buffers)->registerCsd(initData, &index, &outBuffer) == OK) {
+        Mutexed<Output>::Locked output(mOutput);
+        if (output->buffers->registerCsd(initData, &index, &outBuffer) == OK) {
             outBuffer->meta()->setInt64("timeUs", timestamp.peek());
             outBuffer->meta()->setInt32("flags", MediaCodec::BUFFER_FLAG_CODECCONFIG);
             ALOGV("[%s] onWorkDone: csd index = %zu [%p]", mName, index, outBuffer.get());
 
-            buffers.unlock();
+            output.unlock();
             mCallback->onOutputBufferAvailable(index, outBuffer);
-            buffers.lock();
         } else {
             ALOGD("[%s] onWorkDone: unable to register csd", mName);
-            buffers.unlock();
+            output.unlock();
             mCCodecCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
-            buffers.lock();
             return false;
         }
     }
@@ -1421,22 +1536,22 @@
             break;
         }
 
-        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-        status_t err = (*buffers)->registerBuffer(entry.buffer, &index, &outBuffer);
+        Mutexed<Output>::Locked output(mOutput);
+        status_t err = output->buffers->registerBuffer(entry.buffer, &index, &outBuffer);
         if (err != OK) {
             bool outputBuffersChanged = false;
             if (err != WOULD_BLOCK) {
-                if (!(*buffers)->isArrayMode()) {
-                    *buffers = (*buffers)->toArrayMode(mNumOutputSlots);
+                if (!output->buffers->isArrayMode()) {
+                    output->buffers = output->buffers->toArrayMode(output->numSlots);
                 }
-                OutputBuffersArray *array = (OutputBuffersArray *)buffers->get();
+                OutputBuffersArray *array = (OutputBuffersArray *)output->buffers.get();
                 array->realloc(entry.buffer);
                 outputBuffersChanged = true;
             }
             ALOGV("[%s] sendOutputBuffers: unable to register output buffer", mName);
             reorder->defer(entry);
 
-            buffers.unlock();
+            output.unlock();
             reorder.unlock();
 
             if (outputBuffersChanged) {
@@ -1444,7 +1559,7 @@
             }
             return;
         }
-        buffers.unlock();
+        output.unlock();
         reorder.unlock();
 
         outBuffer->meta()->setInt64("timeUs", entry.timestamp);
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index 9aec82d..ae57678 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -209,7 +209,7 @@
 
     void feedInputBufferIfAvailable();
     void feedInputBufferIfAvailableInternal();
-    status_t queueInputBufferInternal(const sp<MediaCodecBuffer> &buffer);
+    status_t queueInputBufferInternal(sp<MediaCodecBuffer> buffer);
     bool handleWork(
             std::unique_ptr<C2Work> work, const sp<AMessage> &outputFormat,
             const C2StreamInitDataInfo::output *initData);
@@ -228,13 +228,26 @@
     QueueSync mQueueSync;
     std::vector<std::unique_ptr<C2Param>> mParamsToBeSet;
 
-    size_t mNumInputSlots;
-    size_t mNumOutputSlots;
     size_t mDelay;
 
-    Mutexed<std::unique_ptr<InputBuffers>> mInputBuffers;
+    struct Input {
+        Input();
+
+        std::unique_ptr<InputBuffers> buffers;
+        size_t numSlots;
+        FlexBuffersImpl extraBuffers;
+        size_t numExtraSlots;
+        uint32_t inputDelay;
+        uint32_t pipelineDelay;
+    };
+    Mutexed<Input> mInput;
+    struct Output {
+        std::unique_ptr<OutputBuffers> buffers;
+        size_t numSlots;
+        uint32_t outputDelay;
+    };
+    Mutexed<Output> mOutput;
     Mutexed<std::list<sp<ABuffer>>> mFlushedConfigs;
-    Mutexed<std::unique_ptr<OutputBuffers>> mOutputBuffers;
 
     std::atomic_uint64_t mFrameIndex;
     std::atomic_uint64_t mFirstValidFrameIndex;
diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp
index fb0efce..5ebd5bd 100644
--- a/media/codec2/sfplugin/CCodecBuffers.cpp
+++ b/media/codec2/sfplugin/CCodecBuffers.cpp
@@ -98,6 +98,26 @@
     }
 }
 
+// InputBuffers
+
+sp<Codec2Buffer> InputBuffers::cloneAndReleaseBuffer(const sp<MediaCodecBuffer> &buffer) {
+    sp<Codec2Buffer> copy = createNewBuffer();
+    if (copy == nullptr) {
+        return nullptr;
+    }
+    std::shared_ptr<C2Buffer> c2buffer;
+    if (!releaseBuffer(buffer, &c2buffer, true)) {
+        return nullptr;
+    }
+    if (!copy->canCopy(c2buffer)) {
+        return nullptr;
+    }
+    if (!copy->copy(c2buffer)) {
+        return nullptr;
+    }
+    return copy;
+}
+
 // OutputBuffers
 
 void OutputBuffers::initSkipCutBuffer(
@@ -197,6 +217,8 @@
     mPool.push_front(std::move(vec));
 }
 
+// FlexBuffersImpl
+
 size_t FlexBuffersImpl::assignSlot(const sp<Codec2Buffer> &buffer) {
     for (size_t i = 0; i < mBuffers.size(); ++i) {
         if (mBuffers[i].clientBuffer == nullptr
@@ -209,8 +231,6 @@
     return mBuffers.size() - 1;
 }
 
-// FlexBuffersImpl
-
 bool FlexBuffersImpl::releaseSlot(
         const sp<MediaCodecBuffer> &buffer,
         std::shared_ptr<C2Buffer> *c2buffer,
@@ -270,6 +290,14 @@
             });
 }
 
+size_t FlexBuffersImpl::numComponentBuffers() const {
+    return std::count_if(
+            mBuffers.begin(), mBuffers.end(),
+            [](const Entry &entry) {
+                return !entry.compBuffer.expired();
+            });
+}
+
 // BuffersArrayImpl
 
 void BuffersArrayImpl::initialize(
@@ -395,6 +423,14 @@
     }
 }
 
+void BuffersArrayImpl::grow(
+        size_t newSize, std::function<sp<Codec2Buffer>()> alloc) {
+    CHECK_LT(mBuffers.size(), newSize);
+    while (mBuffers.size() < newSize) {
+        mBuffers.push_back({ alloc(), std::weak_ptr<C2Buffer>(), false });
+    }
+}
+
 size_t BuffersArrayImpl::numClientBuffers() const {
     return std::count_if(
             mBuffers.begin(), mBuffers.end(),
@@ -409,6 +445,7 @@
         const FlexBuffersImpl &impl,
         size_t minSize,
         std::function<sp<Codec2Buffer>()> allocate) {
+    mAllocate = allocate;
     mImpl.initialize(impl, minSize, allocate);
 }
 
@@ -448,18 +485,14 @@
     return mImpl.numClientBuffers();
 }
 
+sp<Codec2Buffer> InputBuffersArray::createNewBuffer() {
+    return mAllocate();
+}
+
 // LinearInputBuffers
 
 bool LinearInputBuffers::requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) {
-    int32_t capacity = kLinearBufferSize;
-    (void)mFormat->findInt32(KEY_MAX_INPUT_SIZE, &capacity);
-    if ((size_t)capacity > kMaxLinearBufferSize) {
-        ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize);
-        capacity = kMaxLinearBufferSize;
-    }
-    // TODO: proper max input size
-    // TODO: read usage from intf
-    sp<Codec2Buffer> newBuffer = alloc((size_t)capacity);
+    sp<Codec2Buffer> newBuffer = createNewBuffer();
     if (newBuffer == nullptr) {
         return false;
     }
@@ -486,16 +519,7 @@
     mImpl.flush();
 }
 
-std::unique_ptr<InputBuffers> LinearInputBuffers::toArrayMode(
-        size_t size) {
-    int32_t capacity = kLinearBufferSize;
-    (void)mFormat->findInt32(KEY_MAX_INPUT_SIZE, &capacity);
-    if ((size_t)capacity > kMaxLinearBufferSize) {
-        ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize);
-        capacity = kMaxLinearBufferSize;
-    }
-    // TODO: proper max input size
-    // TODO: read usage from intf
+std::unique_ptr<InputBuffers> LinearInputBuffers::toArrayMode(size_t size) {
     std::unique_ptr<InputBuffersArray> array(
             new InputBuffersArray(mComponentName.c_str(), "1D-Input[N]"));
     array->setPool(mPool);
@@ -503,7 +527,9 @@
     array->initialize(
             mImpl,
             size,
-            [this, capacity] () -> sp<Codec2Buffer> { return alloc(capacity); });
+            [pool = mPool, format = mFormat] () -> sp<Codec2Buffer> {
+                return Alloc(pool, format);
+            });
     return std::move(array);
 }
 
@@ -511,16 +537,30 @@
     return mImpl.numClientBuffers();
 }
 
-sp<Codec2Buffer> LinearInputBuffers::alloc(size_t size) {
+// static
+sp<Codec2Buffer> LinearInputBuffers::Alloc(
+        const std::shared_ptr<C2BlockPool> &pool, const sp<AMessage> &format) {
+    int32_t capacity = kLinearBufferSize;
+    (void)format->findInt32(KEY_MAX_INPUT_SIZE, &capacity);
+    if ((size_t)capacity > kMaxLinearBufferSize) {
+        ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize);
+        capacity = kMaxLinearBufferSize;
+    }
+
+    // TODO: read usage from intf
     C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
     std::shared_ptr<C2LinearBlock> block;
 
-    c2_status_t err = mPool->fetchLinearBlock(size, usage, &block);
+    c2_status_t err = pool->fetchLinearBlock(capacity, usage, &block);
     if (err != C2_OK) {
         return nullptr;
     }
 
-    return LinearBlockBuffer::Allocate(mFormat, block);
+    return LinearBlockBuffer::Allocate(format, block);
+}
+
+sp<Codec2Buffer> LinearInputBuffers::createNewBuffer() {
+    return Alloc(mPool, mFormat);
 }
 
 // EncryptedLinearInputBuffers
@@ -537,7 +577,7 @@
       mUsage({0, 0}),
       mDealer(dealer),
       mCrypto(crypto),
-      mHeapSeqNum(heapSeqNum) {
+      mMemoryVector(new std::vector<Entry>){
     if (secure) {
         mUsage = { C2MemoryUsage::READ_PROTECTED, 0 };
     } else {
@@ -550,16 +590,48 @@
                   mName, i);
             break;
         }
-        mMemoryVector.push_back({std::weak_ptr<C2LinearBlock>(), memory});
+        mMemoryVector->push_back({std::weak_ptr<C2LinearBlock>(), memory, heapSeqNum});
     }
 }
 
-sp<Codec2Buffer> EncryptedLinearInputBuffers::alloc(size_t size) {
+std::unique_ptr<InputBuffers> EncryptedLinearInputBuffers::toArrayMode(size_t size) {
+    std::unique_ptr<InputBuffersArray> array(
+            new InputBuffersArray(mComponentName.c_str(), "1D-EncryptedInput[N]"));
+    array->setPool(mPool);
+    array->setFormat(mFormat);
+    array->initialize(
+            mImpl,
+            size,
+            [pool = mPool,
+             format = mFormat,
+             usage = mUsage,
+             memoryVector = mMemoryVector] () -> sp<Codec2Buffer> {
+                return Alloc(pool, format, usage, memoryVector);
+            });
+    return std::move(array);
+}
+
+
+// static
+sp<Codec2Buffer> EncryptedLinearInputBuffers::Alloc(
+        const std::shared_ptr<C2BlockPool> &pool,
+        const sp<AMessage> &format,
+        C2MemoryUsage usage,
+        const std::shared_ptr<std::vector<EncryptedLinearInputBuffers::Entry>> &memoryVector) {
+    int32_t capacity = kLinearBufferSize;
+    (void)format->findInt32(KEY_MAX_INPUT_SIZE, &capacity);
+    if ((size_t)capacity > kMaxLinearBufferSize) {
+        ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize);
+        capacity = kMaxLinearBufferSize;
+    }
+
     sp<IMemory> memory;
     size_t slot = 0;
-    for (; slot < mMemoryVector.size(); ++slot) {
-        if (mMemoryVector[slot].block.expired()) {
-            memory = mMemoryVector[slot].memory;
+    int32_t heapSeqNum = -1;
+    for (; slot < memoryVector->size(); ++slot) {
+        if (memoryVector->at(slot).block.expired()) {
+            memory = memoryVector->at(slot).memory;
+            heapSeqNum = memoryVector->at(slot).heapSeqNum;
             break;
         }
     }
@@ -568,13 +640,18 @@
     }
 
     std::shared_ptr<C2LinearBlock> block;
-    c2_status_t err = mPool->fetchLinearBlock(size, mUsage, &block);
+    c2_status_t err = pool->fetchLinearBlock(capacity, usage, &block);
     if (err != C2_OK || block == nullptr) {
         return nullptr;
     }
 
-    mMemoryVector[slot].block = block;
-    return new EncryptedLinearBlockBuffer(mFormat, block, memory, mHeapSeqNum);
+    memoryVector->at(slot).block = block;
+    return new EncryptedLinearBlockBuffer(format, block, memory, heapSeqNum);
+}
+
+sp<Codec2Buffer> EncryptedLinearInputBuffers::createNewBuffer() {
+    // TODO: android_2020
+    return nullptr;
 }
 
 // GraphicMetadataInputBuffers
@@ -587,12 +664,7 @@
 
 bool GraphicMetadataInputBuffers::requestNewBuffer(
         size_t *index, sp<MediaCodecBuffer> *buffer) {
-    std::shared_ptr<C2Allocator> alloc;
-    c2_status_t err = mStore->fetchAllocator(mPool->getAllocatorId(), &alloc);
-    if (err != C2_OK) {
-        return false;
-    }
-    sp<GraphicMetadataBuffer> newBuffer = new GraphicMetadataBuffer(mFormat, alloc);
+    sp<Codec2Buffer> newBuffer = createNewBuffer();
     if (newBuffer == nullptr) {
         return false;
     }
@@ -642,6 +714,15 @@
     return mImpl.numClientBuffers();
 }
 
+sp<Codec2Buffer> GraphicMetadataInputBuffers::createNewBuffer() {
+    std::shared_ptr<C2Allocator> alloc;
+    c2_status_t err = mStore->fetchAllocator(mPool->getAllocatorId(), &alloc);
+    if (err != C2_OK) {
+        return nullptr;
+    }
+    return new GraphicMetadataBuffer(mFormat, alloc);
+}
+
 // GraphicInputBuffers
 
 GraphicInputBuffers::GraphicInputBuffers(
@@ -652,11 +733,7 @@
               kMaxLinearBufferSize * numInputSlots)) { }
 
 bool GraphicInputBuffers::requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) {
-    // TODO: proper max input size
-    // TODO: read usage from intf
-    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
-    sp<GraphicBlockBuffer> newBuffer = AllocateGraphicBuffer(
-            mPool, mFormat, HAL_PIXEL_FORMAT_YV12, usage, mLocalBufferPool);
+    sp<Codec2Buffer> newBuffer = createNewBuffer();
     if (newBuffer == nullptr) {
         return false;
     }
@@ -703,12 +780,20 @@
     return mImpl.numClientBuffers();
 }
 
+sp<Codec2Buffer> GraphicInputBuffers::createNewBuffer() {
+    // TODO: read usage from intf
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    return AllocateGraphicBuffer(
+            mPool, mFormat, HAL_PIXEL_FORMAT_YV12, usage, mLocalBufferPool);
+}
+
 // OutputBuffersArray
 
 void OutputBuffersArray::initialize(
         const FlexBuffersImpl &impl,
         size_t minSize,
         std::function<sp<Codec2Buffer>()> allocate) {
+    mAlloc = allocate;
     mImpl.initialize(impl, minSize, allocate);
 }
 
@@ -781,8 +866,11 @@
     mImpl.getArray(array);
 }
 
+size_t OutputBuffersArray::numClientBuffers() const {
+    return mImpl.numClientBuffers();
+}
+
 void OutputBuffersArray::realloc(const std::shared_ptr<C2Buffer> &c2buffer) {
-    std::function<sp<Codec2Buffer>()> alloc;
     switch (c2buffer->data().type()) {
         case C2BufferData::LINEAR: {
             uint32_t size = kLinearBufferSize;
@@ -792,7 +880,7 @@
             } else {
                 size = kMaxLinearBufferSize;
             }
-            alloc = [format = mFormat, size] {
+            mAlloc = [format = mFormat, size] {
                 return new LocalLinearBuffer(format, new ABuffer(size));
             };
             break;
@@ -808,11 +896,11 @@
             ALOGD("Unsupported type: %d", (int)c2buffer->data().type());
             return;
     }
-    mImpl.realloc(alloc);
+    mImpl.realloc(mAlloc);
 }
 
-size_t OutputBuffersArray::numClientBuffers() const {
-    return mImpl.numClientBuffers();
+void OutputBuffersArray::grow(size_t newSize) {
+    mImpl.grow(newSize, mAlloc);
 }
 
 // FlexOutputBuffers
@@ -861,10 +949,8 @@
     std::unique_ptr<OutputBuffersArray> array(new OutputBuffersArray(mComponentName.c_str()));
     array->setFormat(mFormat);
     array->transferSkipCutBuffer(mSkipCutBuffer);
-    array->initialize(
-            mImpl,
-            size,
-            [this]() { return allocateArrayBuffer(); });
+    std::function<sp<Codec2Buffer>()> alloc = getAlloc();
+    array->initialize(mImpl, size, alloc);
     return std::move(array);
 }
 
@@ -906,9 +992,11 @@
     return clientBuffer;
 }
 
-sp<Codec2Buffer> LinearOutputBuffers::allocateArrayBuffer() {
-    // TODO: proper max output size
-    return new LocalLinearBuffer(mFormat, new ABuffer(kLinearBufferSize));
+std::function<sp<Codec2Buffer>()> LinearOutputBuffers::getAlloc() {
+    return [format = mFormat]{
+        // TODO: proper max output size
+        return new LocalLinearBuffer(format, new ABuffer(kLinearBufferSize));
+    };
 }
 
 // GraphicOutputBuffers
@@ -917,8 +1005,10 @@
     return new DummyContainerBuffer(mFormat, buffer);
 }
 
-sp<Codec2Buffer> GraphicOutputBuffers::allocateArrayBuffer() {
-    return new DummyContainerBuffer(mFormat);
+std::function<sp<Codec2Buffer>()> GraphicOutputBuffers::getAlloc() {
+    return [format = mFormat]{
+        return new DummyContainerBuffer(format);
+    };
 }
 
 // RawGraphicOutputBuffers
@@ -952,12 +1042,14 @@
     }
 }
 
-sp<Codec2Buffer> RawGraphicOutputBuffers::allocateArrayBuffer() {
-    return ConstGraphicBlockBuffer::AllocateEmpty(
-            mFormat,
-            [lbp = mLocalBufferPool](size_t capacity) {
-                return lbp->newBuffer(capacity);
-            });
+std::function<sp<Codec2Buffer>()> RawGraphicOutputBuffers::getAlloc() {
+    return [format = mFormat, lbp = mLocalBufferPool]{
+        return ConstGraphicBlockBuffer::AllocateEmpty(
+                format,
+                [lbp](size_t capacity) {
+                    return lbp->newBuffer(capacity);
+                });
+    };
 }
 
 }  // namespace android
diff --git a/media/codec2/sfplugin/CCodecBuffers.h b/media/codec2/sfplugin/CCodecBuffers.h
index f5d9fee..e4f2809 100644
--- a/media/codec2/sfplugin/CCodecBuffers.h
+++ b/media/codec2/sfplugin/CCodecBuffers.h
@@ -31,7 +31,7 @@
 
 constexpr size_t kLinearBufferSize = 1048576;
 // This can fit 4K RGBA frame, and most likely client won't need more than this.
-constexpr size_t kMaxLinearBufferSize = 3840 * 2160 * 4;
+constexpr size_t kMaxLinearBufferSize = 4096 * 2304 * 4;
 
 /**
  * Base class for representation of buffers at one port.
@@ -134,7 +134,18 @@
      */
     virtual std::unique_ptr<InputBuffers> toArrayMode(size_t size) = 0;
 
+    /**
+     * Release the buffer obtained from requestNewBuffer(), and create a deep
+     * copy clone of the buffer.
+     *
+     * \return  the deep copy clone of the buffer; nullptr if cloning is not
+     *          possible.
+     */
+    sp<Codec2Buffer> cloneAndReleaseBuffer(const sp<MediaCodecBuffer> &buffer);
+
 protected:
+    virtual sp<Codec2Buffer> createNewBuffer() = 0;
+
     // Pool to obtain blocks for input buffers.
     std::shared_ptr<C2BlockPool> mPool;
 
@@ -346,6 +357,12 @@
      */
     size_t numClientBuffers() const;
 
+    /**
+     * Return the number of buffers that are sent to the component but not
+     * returned back yet.
+     */
+    size_t numComponentBuffers() const;
+
 private:
     friend class BuffersArrayImpl;
 
@@ -446,6 +463,16 @@
     void realloc(std::function<sp<Codec2Buffer>()> alloc);
 
     /**
+     * Grow the array to the new size. It is a programming error to supply
+     * smaller size as the new size.
+     *
+     * \param newSize[in] new size of the array.
+     * \param alloc[in]   the alllocation function for client buffers to fill
+     *                    the new empty slots.
+     */
+    void grow(size_t newSize, std::function<sp<Codec2Buffer>()> alloc);
+
+    /**
      * Return the number of buffers that are sent to the client but not released
      * yet.
      */
@@ -506,8 +533,12 @@
 
     size_t numClientBuffers() const final;
 
+protected:
+    sp<Codec2Buffer> createNewBuffer() override;
+
 private:
     BuffersArrayImpl mImpl;
+    std::function<sp<Codec2Buffer>()> mAllocate;
 };
 
 class LinearInputBuffers : public InputBuffers {
@@ -529,18 +560,18 @@
 
     void flush() override;
 
-    std::unique_ptr<InputBuffers> toArrayMode(size_t size) final;
+    std::unique_ptr<InputBuffers> toArrayMode(size_t size) override;
 
     size_t numClientBuffers() const final;
 
-    /**
-     * Allocate a client buffer with the given size. This method may be
-     * overridden to support different kind of linear buffers (e.g. encrypted).
-     */
-    virtual sp<Codec2Buffer> alloc(size_t size);
+protected:
+    sp<Codec2Buffer> createNewBuffer() override;
+
+    FlexBuffersImpl mImpl;
 
 private:
-    FlexBuffersImpl mImpl;
+    static sp<Codec2Buffer> Alloc(
+            const std::shared_ptr<C2BlockPool> &pool, const sp<AMessage> &format);
 };
 
 class EncryptedLinearInputBuffers : public LinearInputBuffers {
@@ -556,18 +587,28 @@
 
     ~EncryptedLinearInputBuffers() override = default;
 
-    sp<Codec2Buffer> alloc(size_t size) override;
+    std::unique_ptr<InputBuffers> toArrayMode(size_t size) override;
+
+protected:
+    sp<Codec2Buffer> createNewBuffer() override;
 
 private:
-    C2MemoryUsage mUsage;
-    sp<MemoryDealer> mDealer;
-    sp<ICrypto> mCrypto;
-    int32_t mHeapSeqNum;
     struct Entry {
         std::weak_ptr<C2LinearBlock> block;
         sp<IMemory> memory;
+        int32_t heapSeqNum;
     };
-    std::vector<Entry> mMemoryVector;
+
+    static sp<Codec2Buffer> Alloc(
+            const std::shared_ptr<C2BlockPool> &pool,
+            const sp<AMessage> &format,
+            C2MemoryUsage usage,
+            const std::shared_ptr<std::vector<Entry>> &memoryVector);
+
+    C2MemoryUsage mUsage;
+    sp<MemoryDealer> mDealer;
+    sp<ICrypto> mCrypto;
+    std::shared_ptr<std::vector<Entry>> mMemoryVector;
 };
 
 class GraphicMetadataInputBuffers : public InputBuffers {
@@ -591,6 +632,9 @@
 
     size_t numClientBuffers() const final;
 
+protected:
+    sp<Codec2Buffer> createNewBuffer() override;
+
 private:
     FlexBuffersImpl mImpl;
     std::shared_ptr<C2AllocatorStore> mStore;
@@ -619,6 +663,9 @@
 
     size_t numClientBuffers() const final;
 
+protected:
+    sp<Codec2Buffer> createNewBuffer() override;
+
 private:
     FlexBuffersImpl mImpl;
     std::shared_ptr<LocalBufferPool> mLocalBufferPool;
@@ -658,6 +705,11 @@
     size_t numClientBuffers() const final {
         return 0u;
     }
+
+protected:
+    sp<Codec2Buffer> createNewBuffer() override {
+        return nullptr;
+    }
 };
 
 class OutputBuffersArray : public OutputBuffers {
@@ -704,6 +756,8 @@
 
     void getArray(Vector<sp<MediaCodecBuffer>> *array) const final;
 
+    size_t numClientBuffers() const final;
+
     /**
      * Reallocate the array, filled with buffers with the same size as given
      * buffer.
@@ -712,10 +766,17 @@
      */
     void realloc(const std::shared_ptr<C2Buffer> &c2buffer);
 
-    size_t numClientBuffers() const final;
+    /**
+     * Grow the array to the new size. It is a programming error to supply
+     * smaller size as the new size.
+     *
+     * \param newSize[in] new size of the array.
+     */
+    void grow(size_t newSize);
 
 private:
     BuffersArrayImpl mImpl;
+    std::function<sp<Codec2Buffer>()> mAlloc;
 };
 
 class FlexOutputBuffers : public OutputBuffers {
@@ -755,12 +816,15 @@
     virtual sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) = 0;
 
     /**
-     * Return an appropriate Codec2Buffer object for the type of buffers, to be
-     * used as an empty array buffer.
+     * Return a function that allocates an appropriate Codec2Buffer object for
+     * the type of buffers, to be used as an empty array buffer. The function
+     * must not refer to this pointer, since it may be used after this object
+     * destructs.
      *
-     * \return  appropriate Codec2Buffer object which can copy() from C2Buffers.
+     * \return  a function that allocates appropriate Codec2Buffer object,
+     *          which can copy() from C2Buffers.
      */
-    virtual sp<Codec2Buffer> allocateArrayBuffer() = 0;
+    virtual std::function<sp<Codec2Buffer>()> getAlloc() = 0;
 
 private:
     FlexBuffersImpl mImpl;
@@ -776,7 +840,7 @@
 
     sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override;
 
-    sp<Codec2Buffer> allocateArrayBuffer() override;
+    std::function<sp<Codec2Buffer>()> getAlloc() override;
 };
 
 class GraphicOutputBuffers : public FlexOutputBuffers {
@@ -786,7 +850,7 @@
 
     sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override;
 
-    sp<Codec2Buffer> allocateArrayBuffer() override;
+    std::function<sp<Codec2Buffer>()> getAlloc() override;
 };
 
 class RawGraphicOutputBuffers : public FlexOutputBuffers {
@@ -797,7 +861,7 @@
 
     sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override;
 
-    sp<Codec2Buffer> allocateArrayBuffer() override;
+    std::function<sp<Codec2Buffer>()> getAlloc() override;
 
 private:
     std::shared_ptr<LocalBufferPool> mLocalBufferPool;
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index 4c3fff7..077a91f 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -532,7 +532,6 @@
             }
             return C2Value();
         }));
-    add(ConfigMapper(KEY_QUALITY, C2_PARAMKEY_QUALITY, "value"));
     deprecated(ConfigMapper(PARAMETER_KEY_REQUEST_SYNC_FRAME,
                      "coding.request-sync", "value")
         .limitTo(D::PARAM & D::ENCODER)
@@ -798,11 +797,12 @@
             }
         }));
 
-    add(ConfigMapper(KEY_QUALITY, C2_PARAMKEY_QUALITY, "value"));
+    add(ConfigMapper(KEY_QUALITY, C2_PARAMKEY_QUALITY, "value")
+        .limitTo(D::ENCODER & (D::CONFIG | D::PARAM)));
     add(ConfigMapper(KEY_FLAC_COMPRESSION_LEVEL, C2_PARAMKEY_COMPLEXITY, "value")
         .limitTo(D::AUDIO & D::ENCODER));
     add(ConfigMapper("complexity", C2_PARAMKEY_COMPLEXITY, "value")
-        .limitTo(D::ENCODER));
+        .limitTo(D::ENCODER & (D::CONFIG | D::PARAM)));
 
     add(ConfigMapper(KEY_GRID_COLUMNS, C2_PARAMKEY_TILE_LAYOUT, "columns")
         .limitTo(D::IMAGE));
diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp
index c6cbad3..702ad6f 100644
--- a/media/codec2/sfplugin/Codec2Buffer.cpp
+++ b/media/codec2/sfplugin/Codec2Buffer.cpp
@@ -201,9 +201,10 @@
      * \param colorFormat desired SDK color format for the MediaImage (if this is a flexible format,
      *        an attempt is made to simply represent the graphic view as a flexible SDK format
      *        without a memcpy)
+     * \param copy whether the converter is used for copy or not
      */
     GraphicView2MediaImageConverter(
-            const C2GraphicView &view, int32_t colorFormat)
+            const C2GraphicView &view, int32_t colorFormat, bool copy)
         : mInitCheck(NO_INIT),
           mView(view),
           mWidth(view.width()),
@@ -229,8 +230,8 @@
         uint32_t bitDepth = layout.planes[0].bitDepth;
 
         // align width and height to support subsampling cleanly
-        uint32_t mStride = align(mWidth, 2) * divUp(layout.planes[0].allocatedDepth, 8u);
-        uint32_t mVStride = align(mHeight, 2);
+        uint32_t stride = align(view.crop().width, 2) * divUp(layout.planes[0].allocatedDepth, 8u);
+        uint32_t vStride = align(view.crop().height, 2);
 
         switch (layout.type) {
             case C2PlanarLayout::TYPE_YUV:
@@ -255,59 +256,62 @@
                 }
                 switch (mColorFormat) {
                     case COLOR_FormatYUV420Flexible:
-                    {  // try to map directly. check if the planes are near one another
-                        const uint8_t *minPtr = mView.data()[0];
-                        const uint8_t *maxPtr = mView.data()[0];
-                        int32_t planeSize = 0;
-                        for (uint32_t i = 0; i < layout.numPlanes; ++i) {
-                            const C2PlaneInfo &plane = layout.planes[i];
-                            ssize_t minOffset = plane.minOffset(mWidth, mHeight);
-                            ssize_t maxOffset = plane.maxOffset(mWidth, mHeight);
-                            if (minPtr > mView.data()[i] + minOffset) {
-                                minPtr = mView.data()[i] + minOffset;
-                            }
-                            if (maxPtr < mView.data()[i] + maxOffset) {
-                                maxPtr = mView.data()[i] + maxOffset;
-                            }
-                            planeSize += std::abs(plane.rowInc) * align(mHeight, 64)
-                                    / plane.rowSampling / plane.colSampling * divUp(mAllocatedDepth, 8u);
-                        }
-
-                        if ((maxPtr - minPtr + 1) <= planeSize) {
-                            // FIXME: this is risky as reading/writing data out of bound results in
-                            //        an undefined behavior, but gralloc does assume a contiguous
-                            //        mapping
+                        if (!copy) {
+                            // try to map directly. check if the planes are near one another
+                            const uint8_t *minPtr = mView.data()[0];
+                            const uint8_t *maxPtr = mView.data()[0];
+                            int32_t planeSize = 0;
                             for (uint32_t i = 0; i < layout.numPlanes; ++i) {
                                 const C2PlaneInfo &plane = layout.planes[i];
-                                mediaImage->mPlane[i].mOffset = mView.data()[i] - minPtr;
-                                mediaImage->mPlane[i].mColInc = plane.colInc;
-                                mediaImage->mPlane[i].mRowInc = plane.rowInc;
-                                mediaImage->mPlane[i].mHorizSubsampling = plane.colSampling;
-                                mediaImage->mPlane[i].mVertSubsampling = plane.rowSampling;
+                                ssize_t minOffset = plane.minOffset(mWidth, mHeight);
+                                ssize_t maxOffset = plane.maxOffset(mWidth, mHeight);
+                                if (minPtr > mView.data()[i] + minOffset) {
+                                    minPtr = mView.data()[i] + minOffset;
+                                }
+                                if (maxPtr < mView.data()[i] + maxOffset) {
+                                    maxPtr = mView.data()[i] + maxOffset;
+                                }
+                                planeSize += std::abs(plane.rowInc) * align(mHeight, 64)
+                                        / plane.rowSampling / plane.colSampling
+                                        * divUp(mAllocatedDepth, 8u);
                             }
-                            mWrapped = new ABuffer(const_cast<uint8_t *>(minPtr), maxPtr - minPtr + 1);
-                            break;
+
+                            if ((maxPtr - minPtr + 1) <= planeSize) {
+                                // FIXME: this is risky as reading/writing data out of bound results
+                                //        in an undefined behavior, but gralloc does assume a
+                                //        contiguous mapping
+                                for (uint32_t i = 0; i < layout.numPlanes; ++i) {
+                                    const C2PlaneInfo &plane = layout.planes[i];
+                                    mediaImage->mPlane[i].mOffset = mView.data()[i] - minPtr;
+                                    mediaImage->mPlane[i].mColInc = plane.colInc;
+                                    mediaImage->mPlane[i].mRowInc = plane.rowInc;
+                                    mediaImage->mPlane[i].mHorizSubsampling = plane.colSampling;
+                                    mediaImage->mPlane[i].mVertSubsampling = plane.rowSampling;
+                                }
+                                mWrapped = new ABuffer(const_cast<uint8_t *>(minPtr),
+                                                       maxPtr - minPtr + 1);
+                                break;
+                            }
                         }
-                    }
-                    [[fallthrough]];
+                        [[fallthrough]];
 
                     case COLOR_FormatYUV420Planar:
                     case COLOR_FormatYUV420PackedPlanar:
                         mediaImage->mPlane[mediaImage->Y].mOffset = 0;
                         mediaImage->mPlane[mediaImage->Y].mColInc = 1;
-                        mediaImage->mPlane[mediaImage->Y].mRowInc = mStride;
+                        mediaImage->mPlane[mediaImage->Y].mRowInc = stride;
                         mediaImage->mPlane[mediaImage->Y].mHorizSubsampling = 1;
                         mediaImage->mPlane[mediaImage->Y].mVertSubsampling = 1;
 
-                        mediaImage->mPlane[mediaImage->U].mOffset = mStride * mVStride;
+                        mediaImage->mPlane[mediaImage->U].mOffset = stride * vStride;
                         mediaImage->mPlane[mediaImage->U].mColInc = 1;
-                        mediaImage->mPlane[mediaImage->U].mRowInc = mStride / 2;
+                        mediaImage->mPlane[mediaImage->U].mRowInc = stride / 2;
                         mediaImage->mPlane[mediaImage->U].mHorizSubsampling = 2;
                         mediaImage->mPlane[mediaImage->U].mVertSubsampling = 2;
 
-                        mediaImage->mPlane[mediaImage->V].mOffset = mStride * mVStride * 5 / 4;
+                        mediaImage->mPlane[mediaImage->V].mOffset = stride * vStride * 5 / 4;
                         mediaImage->mPlane[mediaImage->V].mColInc = 1;
-                        mediaImage->mPlane[mediaImage->V].mRowInc = mStride / 2;
+                        mediaImage->mPlane[mediaImage->V].mRowInc = stride / 2;
                         mediaImage->mPlane[mediaImage->V].mHorizSubsampling = 2;
                         mediaImage->mPlane[mediaImage->V].mVertSubsampling = 2;
                         break;
@@ -316,19 +320,19 @@
                     case COLOR_FormatYUV420PackedSemiPlanar:
                         mediaImage->mPlane[mediaImage->Y].mOffset = 0;
                         mediaImage->mPlane[mediaImage->Y].mColInc = 1;
-                        mediaImage->mPlane[mediaImage->Y].mRowInc = mStride;
+                        mediaImage->mPlane[mediaImage->Y].mRowInc = stride;
                         mediaImage->mPlane[mediaImage->Y].mHorizSubsampling = 1;
                         mediaImage->mPlane[mediaImage->Y].mVertSubsampling = 1;
 
-                        mediaImage->mPlane[mediaImage->U].mOffset = mStride * mVStride;
+                        mediaImage->mPlane[mediaImage->U].mOffset = stride * vStride;
                         mediaImage->mPlane[mediaImage->U].mColInc = 2;
-                        mediaImage->mPlane[mediaImage->U].mRowInc = mStride;
+                        mediaImage->mPlane[mediaImage->U].mRowInc = stride;
                         mediaImage->mPlane[mediaImage->U].mHorizSubsampling = 2;
                         mediaImage->mPlane[mediaImage->U].mVertSubsampling = 2;
 
-                        mediaImage->mPlane[mediaImage->V].mOffset = mStride * mVStride + 1;
+                        mediaImage->mPlane[mediaImage->V].mOffset = stride * vStride + 1;
                         mediaImage->mPlane[mediaImage->V].mColInc = 2;
-                        mediaImage->mPlane[mediaImage->V].mRowInc = mStride;
+                        mediaImage->mPlane[mediaImage->V].mRowInc = stride;
                         mediaImage->mPlane[mediaImage->V].mHorizSubsampling = 2;
                         mediaImage->mPlane[mediaImage->V].mVertSubsampling = 2;
                         break;
@@ -391,8 +395,8 @@
                 return;
         }
         mediaImage->mNumPlanes = layout.numPlanes;
-        mediaImage->mWidth = mWidth;
-        mediaImage->mHeight = mHeight;
+        mediaImage->mWidth = view.crop().width;
+        mediaImage->mHeight = view.crop().height;
         mediaImage->mBitDepth = bitDepth;
         mediaImage->mBitDepthAllocated = mAllocatedDepth;
 
@@ -415,7 +419,7 @@
                 mInitCheck = BAD_VALUE;
                 return;
             }
-            bufferSize += mStride * mVStride
+            bufferSize += stride * vStride
                     / plane.rowSampling / plane.colSampling;
         }
 
@@ -503,7 +507,7 @@
     int32_t colorFormat = COLOR_FormatYUV420Flexible;
     (void)format->findInt32("color-format", &colorFormat);
 
-    GraphicView2MediaImageConverter converter(view, colorFormat);
+    GraphicView2MediaImageConverter converter(view, colorFormat, false /* copy */);
     if (converter.initCheck() != OK) {
         ALOGD("Converter init failed: %d", converter.initCheck());
         return nullptr;
@@ -615,7 +619,7 @@
     int32_t colorFormat = COLOR_FormatYUV420Flexible;
     (void)format->findInt32("color-format", &colorFormat);
 
-    GraphicView2MediaImageConverter converter(*view, colorFormat);
+    GraphicView2MediaImageConverter converter(*view, colorFormat, false /* copy */);
     if (converter.initCheck() != OK) {
         ALOGD("Converter init failed: %d", converter.initCheck());
         return nullptr;
@@ -652,7 +656,8 @@
         ALOGD("format had no width / height");
         return nullptr;
     }
-    sp<ABuffer> aBuffer(alloc(width * height * 4));
+    // NOTE: we currently only support YUV420 formats for byte-buffer mode.
+    sp<ABuffer> aBuffer(alloc(align(width, 16) * align(height, 16) * 3 / 2));
     return new ConstGraphicBlockBuffer(
             format,
             aBuffer,
@@ -707,7 +712,7 @@
     const_cast<ConstGraphicBlockBuffer *>(this)->format()->findInt32("color-format", &colorFormat);
 
     GraphicView2MediaImageConverter converter(
-            buffer->data().graphicBlocks()[0].map().get(), colorFormat);
+            buffer->data().graphicBlocks()[0].map().get(), colorFormat, true /* copy */);
     if (converter.initCheck() != OK) {
         ALOGD("ConstGraphicBlockBuffer::canCopy: converter init failed: %d", converter.initCheck());
         return false;
@@ -729,7 +734,7 @@
     format()->findInt32("color-format", &colorFormat);
 
     GraphicView2MediaImageConverter converter(
-            buffer->data().graphicBlocks()[0].map().get(), colorFormat);
+            buffer->data().graphicBlocks()[0].map().get(), colorFormat, true /* copy */);
     if (converter.initCheck() != OK) {
         ALOGD("ConstGraphicBlockBuffer::copy: converter init failed: %d", converter.initCheck());
         return false;
diff --git a/media/codec2/sfplugin/Codec2Buffer.h b/media/codec2/sfplugin/Codec2Buffer.h
index dd618aa..36dcab9 100644
--- a/media/codec2/sfplugin/Codec2Buffer.h
+++ b/media/codec2/sfplugin/Codec2Buffer.h
@@ -226,10 +226,10 @@
             const std::shared_ptr<C2GraphicBlock> &block,
             std::function<sp<ABuffer>(size_t)> alloc);
 
-    std::shared_ptr<C2Buffer> asC2Buffer() override;
-
     virtual ~GraphicBlockBuffer() = default;
 
+    std::shared_ptr<C2Buffer> asC2Buffer() override;
+
 private:
     GraphicBlockBuffer(
             const sp<AMessage> &format,
@@ -260,11 +260,10 @@
      */
     GraphicMetadataBuffer(
             const sp<AMessage> &format, const std::shared_ptr<C2Allocator> &alloc);
+    virtual ~GraphicMetadataBuffer() = default;
 
     std::shared_ptr<C2Buffer> asC2Buffer() override;
 
-    virtual ~GraphicMetadataBuffer() = default;
-
 private:
     GraphicMetadataBuffer() = delete;
 
@@ -307,12 +306,12 @@
             const sp<AMessage> &format,
             std::function<sp<ABuffer>(size_t)> alloc);
 
+    virtual ~ConstGraphicBlockBuffer() = default;
+
     std::shared_ptr<C2Buffer> asC2Buffer() override;
     bool canCopy(const std::shared_ptr<C2Buffer> &buffer) const override;
     bool copy(const std::shared_ptr<C2Buffer> &buffer) override;
 
-    virtual ~ConstGraphicBlockBuffer() = default;
-
 private:
     ConstGraphicBlockBuffer(
             const sp<AMessage> &format,
diff --git a/media/codec2/sfplugin/ReflectedParamUpdater.cpp b/media/codec2/sfplugin/ReflectedParamUpdater.cpp
index 880d4a5..55b0ec9 100644
--- a/media/codec2/sfplugin/ReflectedParamUpdater.cpp
+++ b/media/codec2/sfplugin/ReflectedParamUpdater.cpp
@@ -205,6 +205,13 @@
         const std::shared_ptr<C2ParamReflector> &reflector, bool markVendor) {
     C2String paramName = desc->name();
 
+    // Do not reflect requested parameters
+    // TODO: split these once aliases are introduced into '.actual' and '.requested' and alias
+    // the name to '.actual'.
+    if (desc->index() & C2Param::CoreIndex::IS_REQUEST_FLAG) {
+        return;
+    }
+
     // prefix vendor parameters
     if (desc->index().isVendor() && markVendor) {
         paramName = "vendor." + paramName;
diff --git a/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp b/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp
index 6b8663f..bf2a07e 100644
--- a/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp
+++ b/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp
@@ -118,7 +118,7 @@
 }  // namespace
 
 status_t ImageCopy(uint8_t *imgBase, const MediaImage2 *img, const C2GraphicView &view) {
-    if (view.width() != img->mWidth || view.height() != img->mHeight) {
+    if (view.crop().width != img->mWidth || view.crop().height != img->mHeight) {
         return BAD_VALUE;
     }
     if ((IsNV12(view) && IsI420(img)) || (IsI420(view) && IsNV12(img))) {
@@ -137,14 +137,14 @@
         int32_t dst_stride_v = img->mPlane[2].mRowInc;
         if (IsNV12(view) && IsI420(img)) {
             if (!libyuv::NV12ToI420(src_y, src_stride_y, src_u, src_stride_u, dst_y, dst_stride_y,
-                                    dst_u, dst_stride_u, dst_v, dst_stride_v, view.width(),
-                                    view.height())) {
+                                    dst_u, dst_stride_u, dst_v, dst_stride_v, view.crop().width,
+                                    view.crop().height)) {
                 return OK;
             }
         } else {
             if (!libyuv::I420ToNV12(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v,
-                                    dst_y, dst_stride_y, dst_u, dst_stride_u, view.width(),
-                                    view.height())) {
+                                    dst_y, dst_stride_y, dst_u, dst_stride_u, view.crop().width,
+                                    view.crop().height)) {
                 return OK;
             }
         }
@@ -153,7 +153,7 @@
 }
 
 status_t ImageCopy(C2GraphicView &view, const uint8_t *imgBase, const MediaImage2 *img) {
-    if (view.width() != img->mWidth || view.height() != img->mHeight) {
+    if (view.crop().width != img->mWidth || view.crop().height != img->mHeight) {
         return BAD_VALUE;
     }
     if ((IsNV12(img) && IsI420(view)) || (IsI420(img) && IsNV12(view))) {
diff --git a/media/codec2/vndk/C2AllocatorIon.cpp b/media/codec2/vndk/C2AllocatorIon.cpp
index d22153d..752bc46 100644
--- a/media/codec2/vndk/C2AllocatorIon.cpp
+++ b/media/codec2/vndk/C2AllocatorIon.cpp
@@ -554,7 +554,11 @@
         } else {
             *align = 0; // TODO make this 1
             *heapMask = ~0; // default mask
-            *flags = 0; // default flags
+            if (usage.expected & (C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE)) {
+                *flags = ION_FLAG_CACHED; // cache CPU accessed buffers
+            } else {
+                *flags = 0;  // default flags
+            }
             res = C2_NO_INIT;
         }
         // add usage to cache
diff --git a/media/codec2/vndk/util/C2InterfaceHelper.cpp b/media/codec2/vndk/util/C2InterfaceHelper.cpp
index e447fbe..9eb52d2 100644
--- a/media/codec2/vndk/util/C2InterfaceHelper.cpp
+++ b/media/codec2/vndk/util/C2InterfaceHelper.cpp
@@ -609,6 +609,9 @@
     // { depIx, paramIx } may be a suitable key
     std::map<size_t, std::pair<C2Param::Index, bool>> dependencies;
 
+    std::vector<std::unique_ptr<C2Param>> paramRequests;
+    std::vector<C2Param*> lateReadParams;
+
     // we cannot determine the last valid parameter, so add an extra
     // loop iteration after the last parameter
     for (size_t p_ix = 0; p_ix <= params.size(); ++p_ix) {
@@ -625,7 +628,27 @@
             }
 
             paramIx = p->index();
-            paramDepIx = getDependencyIndex_l(paramIx);
+
+            // convert parameter to request in case this is a split parameter
+            C2Param::Index requestParamIx = paramIx | C2Param::CoreIndex::IS_REQUEST_FLAG;
+
+            // setting a request directly is handled as normal
+            if (paramIx != requestParamIx) {
+                paramDepIx = getDependencyIndex_l(requestParamIx);
+                if (paramDepIx == SIZE_MAX) {
+                    // not a split parameter, handle it normally
+                    paramDepIx = getDependencyIndex_l(paramIx);
+                } else {
+                    // split parameter - replace with setting for the request - and queue to
+                    // read back actual value
+                    // TODO: read late params at the right time
+                    lateReadParams.emplace_back(p);
+                    std::unique_ptr<C2Param> request(C2Param::CopyAsRequest(*p));
+                    p = request.get();
+                    paramRequests.emplace_back(std::move(request));
+                }
+            }
+
             if (paramDepIx == SIZE_MAX) {
                 // unsupported parameter
                 paramNotFound = true;
@@ -715,6 +738,16 @@
         }
     }
 
+    // get late read parameters
+    for (C2Param *p : lateReadParams) {
+        std::shared_ptr<C2Param> value = _mFactory->getParamValue(p->index());
+        if (value) {
+            p->updateFrom(*value);
+        } else {
+            p->invalidate();
+        }
+    }
+
     return (paramCorrupted ? C2_CORRUPTED :
             paramBlocking ? C2_BLOCKING :
             paramTimedOut ? C2_TIMED_OUT :
@@ -761,9 +794,11 @@
             if (p != nullptr) {
                 heapParams->push_back(std::move(p));
             } else {
+                heapParams->push_back(nullptr);
                 paramNoMemory = true;
             }
         } else {
+            heapParams->push_back(nullptr);
             paramNotFound = true;
         }
     }
diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp
index b1eb301..23022e4 100644
--- a/media/extractors/mkv/MatroskaExtractor.cpp
+++ b/media/extractors/mkv/MatroskaExtractor.cpp
@@ -2109,8 +2109,10 @@
 
                 if (!strcmp("A_AAC", codecID)) {
                     AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_AAC);
-                    CHECK(codecPrivateSize >= 2);
-
+                    if (codecPrivateSize < 2) {
+                        ALOGW("Incomplete AAC Codec Info %zu byte", codecPrivateSize);
+                        continue;
+                    }
                     addESDSFromCodecPrivate(
                             meta, true, codecPrivate, codecPrivateSize);
                 } else if (!strcmp("A_VORBIS", codecID)) {
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 527bb77..9d5890c 100755
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -1197,45 +1197,16 @@
                         AMEDIAFORMAT_KEY_SAMPLE_RATE, sample_rate);
             }
 
-            // If format type is 'alac', it is necessary to get the parameters
-            // from a alac atom spreading behind the frma atom.
-            // See 'external/alac/ALACMagicCookieDescription.txt'.
-            if (original_fourcc == FOURCC("alac")) {
-                // Store ALAC magic cookie (decoder needs it).
-                uint8_t alacInfo[12];
-                data_offset = *offset;
-                if (mDataSource->readAt(
-                        data_offset, alacInfo, sizeof(alacInfo)) < (ssize_t)sizeof(alacInfo)) {
-                    return ERROR_IO;
+            if (!mIsQT && original_fourcc == FOURCC("alac")) {
+                off64_t tmpOffset = *offset;
+                status_t err = parseALACSampleEntry(&tmpOffset);
+                if (err != OK) {
+                    ALOGE("parseALACSampleEntry err:%d Line:%d", err, __LINE__);
+                    return err;
                 }
-                uint32_t size = U32_AT(&alacInfo[0]);
-                if ((size != ALAC_SPECIFIC_INFO_SIZE) ||
-                        (U32_AT(&alacInfo[4]) != FOURCC("alac")) ||
-                        (U32_AT(&alacInfo[8]) != 0)) {
-                    return ERROR_MALFORMED;
-                }
-
-                data_offset += sizeof(alacInfo);
-                uint8_t cookie[size - sizeof(alacInfo)];
-                if (mDataSource->readAt(
-                        data_offset, cookie, sizeof(cookie)) < (ssize_t)sizeof(cookie)) {
-                    return ERROR_IO;
-                }
-
-                uint8_t bitsPerSample = cookie[5];
-                AMediaFormat_setInt32(mLastTrack->meta,
-                        AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, bitsPerSample);
-                AMediaFormat_setInt32(mLastTrack->meta,
-                        AMEDIAFORMAT_KEY_CHANNEL_COUNT, cookie[9]);
-                AMediaFormat_setInt32(mLastTrack->meta,
-                        AMEDIAFORMAT_KEY_SAMPLE_RATE, U32_AT(&cookie[20]));
-                AMediaFormat_setBuffer(mLastTrack->meta,
-                        AMEDIAFORMAT_KEY_CSD_0, cookie, sizeof(cookie));
-
-                // Add the size of ALAC Specific Info (36 bytes) and terminator
-                // atom (8 bytes).
-                *offset += (size + 8);
+                *offset = tmpOffset + 8;
             }
+
             break;
         }
 
@@ -1653,7 +1624,18 @@
         case 0x6D730055: // "ms U" mp3 audio
         {
             if (mIsQT && depth >= 1 && mPath[depth - 1] == FOURCC("wave")) {
+
+                if (chunk_type == FOURCC("alac")) {
+                    off64_t offsetTmp = *offset;
+                    status_t err = parseALACSampleEntry(&offsetTmp);
+                    if (err != OK) {
+                        ALOGE("parseALACSampleEntry err:%d Line:%d", err, __LINE__);
+                        return err;
+                    }
+                }
+
                 // Ignore all atoms embedded in QT wave atom
+                ALOGV("Ignore all atoms embedded in QT wave atom");
                 *offset += chunk_size;
                 break;
             }
@@ -1792,39 +1774,14 @@
                 CHECK_EQ(*offset, stop_offset);
             }
 
-            if (chunk_type == FOURCC("alac")) {
-
-                // See 'external/alac/ALACMagicCookieDescription.txt for the detail'.
-                // Store ALAC magic cookie (decoder needs it).
-                uint8_t alacInfo[12];
+            if (!mIsQT && chunk_type == FOURCC("alac")) {
                 data_offset += sizeof(buffer);
-                if (mDataSource->readAt(
-                        data_offset, alacInfo, sizeof(alacInfo)) < (ssize_t)sizeof(alacInfo)) {
-                    return ERROR_IO;
-                }
-                uint32_t size = U32_AT(&alacInfo[0]);
-                if ((size != ALAC_SPECIFIC_INFO_SIZE) ||
-                        (U32_AT(&alacInfo[4]) != FOURCC("alac")) ||
-                        (U32_AT(&alacInfo[8]) != 0)) {
-                    return ERROR_MALFORMED;
-                }
-                data_offset += sizeof(alacInfo);
-                uint8_t cookie[size - sizeof(alacInfo)];
-                if (mDataSource->readAt(
-                        data_offset, cookie, sizeof(cookie)) < (ssize_t)sizeof(cookie)) {
-                    return ERROR_IO;
-                }
 
-                uint8_t bitsPerSample = cookie[5];
-                AMediaFormat_setInt32(mLastTrack->meta,
-                        AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, bitsPerSample);
-                AMediaFormat_setInt32(mLastTrack->meta,
-                        AMEDIAFORMAT_KEY_CHANNEL_COUNT, cookie[9]);
-                AMediaFormat_setInt32(mLastTrack->meta,
-                        AMEDIAFORMAT_KEY_SAMPLE_RATE, U32_AT(&cookie[20]));
-                AMediaFormat_setBuffer(mLastTrack->meta,
-                        AMEDIAFORMAT_KEY_CSD_0, cookie, sizeof(cookie));
-                data_offset += sizeof(cookie);
+                status_t err = parseALACSampleEntry(&data_offset);
+                if (err != OK) {
+                    ALOGE("parseALACSampleEntry err:%d Line:%d", err, __LINE__);
+                    return err;
+                }
                 *offset = data_offset;
                 CHECK_EQ(*offset, stop_offset);
             }
@@ -3289,6 +3246,45 @@
     return OK;
 }
 
+status_t MPEG4Extractor::parseALACSampleEntry(off64_t *offset) {
+    // See 'external/alac/ALACMagicCookieDescription.txt for the detail'.
+    // Store ALAC magic cookie (decoder needs it).
+    uint8_t alacInfo[12];
+    off64_t data_offset = *offset;
+
+    if (mDataSource->readAt(
+            data_offset, alacInfo, sizeof(alacInfo)) < (ssize_t)sizeof(alacInfo)) {
+        return ERROR_IO;
+    }
+    uint32_t size = U32_AT(&alacInfo[0]);
+    if ((size != ALAC_SPECIFIC_INFO_SIZE) ||
+            (U32_AT(&alacInfo[4]) != FOURCC("alac")) ||
+            (U32_AT(&alacInfo[8]) != 0)) {
+        ALOGV("Size:%u, U32_AT(&alacInfo[4]):%u, U32_AT(&alacInfo[8]):%u",
+            size, U32_AT(&alacInfo[4]), U32_AT(&alacInfo[8]));
+        return ERROR_MALFORMED;
+    }
+    data_offset += sizeof(alacInfo);
+    uint8_t cookie[size - sizeof(alacInfo)];
+    if (mDataSource->readAt(
+            data_offset, cookie, sizeof(cookie)) < (ssize_t)sizeof(cookie)) {
+        return ERROR_IO;
+    }
+
+    uint8_t bitsPerSample = cookie[5];
+    AMediaFormat_setInt32(mLastTrack->meta,
+            AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, bitsPerSample);
+    AMediaFormat_setInt32(mLastTrack->meta,
+            AMEDIAFORMAT_KEY_CHANNEL_COUNT, cookie[9]);
+    AMediaFormat_setInt32(mLastTrack->meta,
+            AMEDIAFORMAT_KEY_SAMPLE_RATE, U32_AT(&cookie[20]));
+    AMediaFormat_setBuffer(mLastTrack->meta,
+            AMEDIAFORMAT_KEY_CSD_0, cookie, sizeof(cookie));
+    data_offset += sizeof(cookie);
+    *offset = data_offset;
+    return OK;
+}
+
 status_t MPEG4Extractor::parseSegmentIndex(off64_t offset, size_t size) {
   ALOGV("MPEG4Extractor::parseSegmentIndex");
 
diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h
index e10bf8a..fcddbb8 100644
--- a/media/extractors/mp4/MPEG4Extractor.h
+++ b/media/extractors/mp4/MPEG4Extractor.h
@@ -179,6 +179,7 @@
     status_t parseAC3SpecificBox(off64_t offset);
     status_t parseEAC3SpecificBox(off64_t offset);
     status_t parseAC4SpecificBox(off64_t offset);
+    status_t parseALACSampleEntry(off64_t *offset);
     void adjustRawDefaultFrameSize();
 
     MPEG4Extractor(const MPEG4Extractor &);
diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp
index 8b539c2ff..4fa7f27 100644
--- a/media/extractors/wav/WAVExtractor.cpp
+++ b/media/extractors/wav/WAVExtractor.cpp
@@ -505,45 +505,40 @@
 
     // TODO: add capability to return data as float PCM instead of 16 bit PCM.
     if (mWaveFormat == WAVE_FORMAT_PCM) {
+        const size_t bytesPerFrame = (mBitsPerSample >> 3) * mNumChannels;
+        const size_t numFrames = n / bytesPerFrame;
+        const size_t numSamples = numFrames * mNumChannels;
         if (mOutputFloat) {
             float *fdest = (float *)buffer->data();
+            buffer->set_range(0, 4 * numSamples);
             switch (mBitsPerSample) {
             case 8: {
-                buffer->set_range(0, 4 * n);
-                memcpy_to_float_from_u8(fdest, (const uint8_t *)buffer->data(), n);
+                memcpy_to_float_from_u8(fdest, (const uint8_t *)buffer->data(), numSamples);
             } break;
             case 16: {
-                const size_t numSamples = n / 2;
-                buffer->set_range(0, 4 * numSamples);
                 memcpy_to_float_from_i16(fdest, (const int16_t *)buffer->data(), numSamples);
             } break;
             case 24: {
-                const size_t numSamples = n / 3;
-                buffer->set_range(0, 4 * numSamples);
                 memcpy_to_float_from_p24(fdest, (const uint8_t *)buffer->data(), numSamples);
             } break;
             case 32: { // buffer range is correct
-                const size_t numSamples = n / 4;
                 memcpy_to_float_from_i32(fdest, (const int32_t *)buffer->data(), numSamples);
             } break;
             }
         } else {
             int16_t *idest = (int16_t *)buffer->data();
+            buffer->set_range(0, 2 * numSamples);
             switch (mBitsPerSample) {
             case 8: {
-                buffer->set_range(0, 2 * n);
-                memcpy_to_i16_from_u8(idest, (const uint8_t *)buffer->data(), n);
+                memcpy_to_i16_from_u8(idest, (const uint8_t *)buffer->data(), numSamples);
             } break;
             case 16:
-                break; // no translation needed
+                // no conversion needed
+                break;
             case 24: {
-                const size_t numSamples = n / 3;
-                buffer->set_range(0, 2 * numSamples);
                 memcpy_to_i16_from_p24(idest, (const uint8_t *)buffer->data(), numSamples);
             } break;
             case 32: {
-                const size_t numSamples = n / 4;
-                buffer->set_range(0, 2 * numSamples);
                 memcpy_to_i16_from_i32(idest, (const int32_t *)buffer->data(), numSamples);
             } break;
             }
diff --git a/media/libstagefright/httplive/HTTPDownloader.cpp b/media/libstagefright/httplive/HTTPDownloader.cpp
index 59265fe..c7e92cd 100644
--- a/media/libstagefright/httplive/HTTPDownloader.cpp
+++ b/media/libstagefright/httplive/HTTPDownloader.cpp
@@ -157,6 +157,12 @@
                  buffer->size() + bufferRemaining);
 
             sp<ABuffer> copy = new ABuffer(buffer->size() + bufferRemaining);
+            if (copy->data() == NULL) {
+                android_errorWriteLog(0x534e4554, "68399439");
+                ALOGE("not enough memory to download: requesting %zu + %zu",
+                        buffer->size(), bufferRemaining);
+                return NO_MEMORY;
+            }
             memcpy(copy->data(), buffer->data(), buffer->size());
             copy->setRange(0, buffer->size());
 
diff --git a/media/libstagefright/timedtext/TextDescriptions.cpp b/media/libstagefright/timedtext/TextDescriptions.cpp
index 088eaae..0dc7722 100644
--- a/media/libstagefright/timedtext/TextDescriptions.cpp
+++ b/media/libstagefright/timedtext/TextDescriptions.cpp
@@ -383,7 +383,7 @@
         tmpData += 8;
         size_t remaining = size - 8;
 
-        if (size < chunkSize) {
+        if (chunkSize <= 8 || size < chunkSize) {
             return OK;
         }
         switch(chunkType) {
diff --git a/media/libstagefright/timedtext/TextDescriptions2.cpp b/media/libstagefright/timedtext/TextDescriptions2.cpp
index f48eacc..fd42d3a 100644
--- a/media/libstagefright/timedtext/TextDescriptions2.cpp
+++ b/media/libstagefright/timedtext/TextDescriptions2.cpp
@@ -145,7 +145,7 @@
         tmpData += 8;
         size_t remaining = size - 8;
 
-        if (size < chunkSize) {
+        if (chunkSize <= 8 || size < chunkSize) {
             return OK;
         }
         switch(chunkType) {