Merge "C2SoftVpxEnc: process QP bounding parameters" into main
diff --git a/media/codec2/components/aac/C2SoftAacDec.cpp b/media/codec2/components/aac/C2SoftAacDec.cpp
index d1b08bd..c770d0c 100644
--- a/media/codec2/components/aac/C2SoftAacDec.cpp
+++ b/media/codec2/components/aac/C2SoftAacDec.cpp
@@ -35,14 +35,14 @@
 
 #define FILEREAD_MAX_LAYERS 2
 
-#define DRC_DEFAULT_MOBILE_REF_LEVEL -16.0  /* 64*-0.25dB = -16 dB below full scale for mobile conf */
-#define DRC_DEFAULT_MOBILE_DRC_CUT   1.0 /* maximum compression of dynamic range for mobile conf */
-#define DRC_DEFAULT_MOBILE_DRC_BOOST 1.0 /* maximum compression of dynamic range for mobile conf */
-#define DRC_DEFAULT_MOBILE_DRC_HEAVY C2Config::DRC_COMPRESSION_HEAVY   /* switch for heavy compression for mobile conf */
+#define DRC_DEFAULT_MOBILE_REF_LEVEL 64  /* 64*-0.25dB = -16 dB below full scale for mobile conf */
+#define DRC_DEFAULT_MOBILE_DRC_CUT   127 /* maximum compression of dynamic range for mobile conf */
+#define DRC_DEFAULT_MOBILE_DRC_BOOST 127 /* maximum compression of dynamic range for mobile conf */
+#define DRC_DEFAULT_MOBILE_DRC_HEAVY 1   /* switch for heavy compression for mobile conf */
 #define DRC_DEFAULT_MOBILE_DRC_EFFECT 3  /* MPEG-D DRC effect type; 3 => Limited playback range */
 #define DRC_DEFAULT_MOBILE_DRC_ALBUM  0  /* MPEG-D DRC album mode; 0 => album mode is disabled, 1 => album mode is enabled */
 #define DRC_DEFAULT_MOBILE_OUTPUT_LOUDNESS (0.25) /* decoder output loudness; -1 => the value is unknown, otherwise dB step value (e.g. 64 for -16 dB) */
-#define DRC_DEFAULT_MOBILE_ENC_LEVEL (0.25) /* encoder target level; -1 => the value is unknown, otherwise dB step value (e.g. 64 for -16 dB) */
+#define DRC_DEFAULT_MOBILE_ENC_LEVEL (-1) /* encoder target level; -1 => the value is unknown, otherwise dB step value (e.g. 64 for -16 dB) */
 #define MAX_CHANNEL_COUNT            8  /* maximum number of audio channels that can be decoded */
 // names of properties that can be used to override the default DRC settings
 #define PROP_DRC_OVERRIDE_REF_LEVEL  "aac_drc_reference_level"
@@ -145,9 +145,13 @@
                 .withSetter(ProfileLevelSetter)
                 .build());
 
+        C2Config::drc_compression_mode_t defaultCompressionMode =
+                property_get_int32(PROP_DRC_OVERRIDE_HEAVY, DRC_DEFAULT_MOBILE_DRC_HEAVY) == 1
+                        ? C2Config::DRC_COMPRESSION_HEAVY
+                        : C2Config::DRC_COMPRESSION_LIGHT;
         addParameter(
                 DefineParam(mDrcCompressMode, C2_PARAMKEY_DRC_COMPRESSION_MODE)
-                .withDefault(new C2StreamDrcCompressionModeTuning::input(0u, C2Config::DRC_COMPRESSION_HEAVY))
+                .withDefault(new C2StreamDrcCompressionModeTuning::input(0u, defaultCompressionMode))
                 .withFields({
                     C2F(mDrcCompressMode, value).oneOf({
                             C2Config::DRC_COMPRESSION_ODM_DEFAULT,
@@ -158,37 +162,48 @@
                 .withSetter(Setter<decltype(*mDrcCompressMode)>::StrictValueWithNoDeps)
                 .build());
 
+
+        float defaultRefLevel = -0.25 * property_get_int32(PROP_DRC_OVERRIDE_REF_LEVEL,
+                                                           DRC_DEFAULT_MOBILE_REF_LEVEL);
         addParameter(
                 DefineParam(mDrcTargetRefLevel, C2_PARAMKEY_DRC_TARGET_REFERENCE_LEVEL)
-                .withDefault(new C2StreamDrcTargetReferenceLevelTuning::input(0u, DRC_DEFAULT_MOBILE_REF_LEVEL))
+                .withDefault(new C2StreamDrcTargetReferenceLevelTuning::input(0u, defaultRefLevel))
                 .withFields({C2F(mDrcTargetRefLevel, value).inRange(-31.75, 0.25)})
                 .withSetter(Setter<decltype(*mDrcTargetRefLevel)>::StrictValueWithNoDeps)
                 .build());
 
+        float defaultEncLevel = -0.25 * property_get_int32(PROP_DRC_OVERRIDE_ENC_LEVEL,
+                                                           DRC_DEFAULT_MOBILE_ENC_LEVEL);
         addParameter(
                 DefineParam(mDrcEncTargetLevel, C2_PARAMKEY_DRC_ENCODED_TARGET_LEVEL)
-                .withDefault(new C2StreamDrcEncodedTargetLevelTuning::input(0u, DRC_DEFAULT_MOBILE_ENC_LEVEL))
+                .withDefault(new C2StreamDrcEncodedTargetLevelTuning::input(0u, defaultEncLevel))
                 .withFields({C2F(mDrcEncTargetLevel, value).inRange(-31.75, 0.25)})
                 .withSetter(Setter<decltype(*mDrcEncTargetLevel)>::StrictValueWithNoDeps)
                 .build());
 
+        float defaultDrcBoost =
+                property_get_int32(PROP_DRC_OVERRIDE_BOOST, DRC_DEFAULT_MOBILE_DRC_BOOST) / 127.;
         addParameter(
                 DefineParam(mDrcBoostFactor, C2_PARAMKEY_DRC_BOOST_FACTOR)
-                .withDefault(new C2StreamDrcBoostFactorTuning::input(0u, DRC_DEFAULT_MOBILE_DRC_BOOST))
+                .withDefault(new C2StreamDrcBoostFactorTuning::input(0u, defaultDrcBoost))
                 .withFields({C2F(mDrcBoostFactor, value).inRange(0, 1.)})
                 .withSetter(Setter<decltype(*mDrcBoostFactor)>::StrictValueWithNoDeps)
                 .build());
 
+        float defaultDrcCut =
+                property_get_int32(PROP_DRC_OVERRIDE_CUT, DRC_DEFAULT_MOBILE_DRC_CUT) / 127.;
         addParameter(
                 DefineParam(mDrcAttenuationFactor, C2_PARAMKEY_DRC_ATTENUATION_FACTOR)
-                .withDefault(new C2StreamDrcAttenuationFactorTuning::input(0u, DRC_DEFAULT_MOBILE_DRC_CUT))
+                .withDefault(new C2StreamDrcAttenuationFactorTuning::input(0u, defaultDrcCut))
                 .withFields({C2F(mDrcAttenuationFactor, value).inRange(0, 1.)})
                 .withSetter(Setter<decltype(*mDrcAttenuationFactor)>::StrictValueWithNoDeps)
                 .build());
 
+        C2Config::drc_effect_type_t defaultDrcEffectType = (C2Config::drc_effect_type_t)
+                property_get_int32(PROP_DRC_OVERRIDE_EFFECT, DRC_DEFAULT_MOBILE_DRC_EFFECT);
         addParameter(
                 DefineParam(mDrcEffectType, C2_PARAMKEY_DRC_EFFECT_TYPE)
-                .withDefault(new C2StreamDrcEffectTypeTuning::input(0u, C2Config::DRC_EFFECT_LIMITED_PLAYBACK_RANGE))
+                .withDefault(new C2StreamDrcEffectTypeTuning::input(0u, defaultDrcEffectType))
                 .withFields({
                     C2F(mDrcEffectType, value).oneOf({
                             C2Config::DRC_EFFECT_ODM_DEFAULT,
diff --git a/media/codec2/hal/aidl/Component.cpp b/media/codec2/hal/aidl/Component.cpp
index 8da9861..ba5f8d4 100644
--- a/media/codec2/hal/aidl/Component.cpp
+++ b/media/codec2/hal/aidl/Component.cpp
@@ -46,6 +46,8 @@
 using ::aidl::android::hardware::common::NativeHandle;
 using ::aidl::android::hardware::media::bufferpool2::IClientManager;
 using ::ndk::ScopedAStatus;
+using ::android::MultiAccessUnitInterface;
+using ::android::MultiAccessUnitHelper;
 
 // ComponentListener wrapper
 struct Component::Listener : public C2Component::Listener {
@@ -139,6 +141,52 @@
     std::weak_ptr<IComponentListener> mListener;
 };
 
+// Component listener for handle multiple access-units
+struct MultiAccessUnitListener : public Component::Listener {
+    MultiAccessUnitListener(const std::shared_ptr<Component>& component,
+            const std::shared_ptr<MultiAccessUnitHelper> &helper):
+        Listener(component), mHelper(helper) {
+    }
+
+    virtual void onError_nb(
+            std::weak_ptr<C2Component> c2component,
+            uint32_t errorCode) override {
+        if (mHelper) {
+            std::list<std::unique_ptr<C2Work>> worklist;
+            mHelper->error(&worklist);
+            if (!worklist.empty()) {
+                Listener::onWorkDone_nb(c2component, std::move(worklist));
+            }
+        }
+        Listener::onError_nb(c2component, errorCode);
+    }
+
+    virtual void onTripped_nb(
+            std::weak_ptr<C2Component> c2component,
+            std::vector<std::shared_ptr<C2SettingResult>> c2settingResult
+            ) override {
+        Listener::onTripped_nb(c2component,
+                c2settingResult);
+    }
+
+    virtual void onWorkDone_nb(
+            std::weak_ptr<C2Component> c2component,
+            std::list<std::unique_ptr<C2Work>> c2workItems) override {
+        if (mHelper) {
+            std::list<std::unique_ptr<C2Work>> processedWork;
+            mHelper->gather(c2workItems, &processedWork);
+            if (!processedWork.empty()) {
+                Listener::onWorkDone_nb(c2component, std::move(processedWork));
+            }
+        } else {
+            Listener::onWorkDone_nb(c2component, std::move(c2workItems));
+        }
+    }
+
+    protected:
+        std::shared_ptr<MultiAccessUnitHelper> mHelper;
+};
+
 // Component::DeathContext
 struct Component::DeathContext {
     std::weak_ptr<Component> mWeakComp;
@@ -151,14 +199,38 @@
         const std::shared_ptr<ComponentStore>& store,
         const std::shared_ptr<IClientManager>& clientPoolManager)
       : mComponent{component},
-        mInterface{SharedRefBase::make<ComponentInterface>(
-                component->intf(), store->getParameterCache())},
         mListener{listener},
         mStore{store},
         mBufferPoolSender{clientPoolManager},
         mDeathContext(nullptr) {
     // Retrieve supported parameters from store
     // TODO: We could cache this per component/interface type
+    if (MultiAccessUnitHelper::isEnabledOnPlatform()) {
+        c2_status_t err = C2_OK;
+        C2ComponentDomainSetting domain;
+        std::vector<std::unique_ptr<C2Param>> heapParams;
+        err = component->intf()->query_vb({&domain}, {}, C2_MAY_BLOCK, &heapParams);
+        if (err == C2_OK && (domain.value == C2Component::DOMAIN_AUDIO)) {
+            std::vector<std::shared_ptr<C2ParamDescriptor>> params;
+            bool isComponentSupportsLargeAudioFrame = false;
+            component->intf()->querySupportedParams_nb(&params);
+            for (const auto &paramDesc : params) {
+                if (paramDesc->name().compare(C2_PARAMKEY_OUTPUT_LARGE_FRAME) == 0) {
+                    isComponentSupportsLargeAudioFrame = true;
+                    LOG(VERBOSE) << "Underlying component supports large frame audio";
+                    break;
+                }
+            }
+            if (!isComponentSupportsLargeAudioFrame) {
+                mMultiAccessUnitIntf = std::make_shared<MultiAccessUnitInterface>(
+                        component->intf(),
+                        std::static_pointer_cast<C2ReflectorHelper>(
+                                ::android::GetCodec2PlatformComponentStore()->getParamReflector()));
+            }
+        }
+    }
+    mInterface = SharedRefBase::make<ComponentInterface>(
+            component->intf(), mMultiAccessUnitIntf, store->getParameterCache());
     mInit = mInterface->status();
 }
 
@@ -181,8 +253,21 @@
                     registerFrameData(mListener, work->input);
         }
     }
+    c2_status_t err = C2_OK;
+    if (mMultiAccessUnitHelper) {
+        std::list<std::list<std::unique_ptr<C2Work>>> c2worklists;
+        mMultiAccessUnitHelper->scatter(c2works, &c2worklists);
+        for (auto &c2worklist : c2worklists) {
+            err = mComponent->queue_nb(&c2worklist);
+            if (err != C2_OK) {
+                LOG(ERROR) << "Error Queuing to component.";
+                return ScopedAStatus::fromServiceSpecificError(err);
+            }
+        }
+        return ScopedAStatus::ok();
+    }
 
-    c2_status_t err = mComponent->queue_nb(&c2works);
+    err = mComponent->queue_nb(&c2works);
     if (err == C2_OK) {
         return ScopedAStatus::ok();
     }
@@ -194,7 +279,9 @@
     c2_status_t c2res = mComponent->flush_sm(
             C2Component::FLUSH_COMPONENT,
             &c2flushedWorks);
-
+    if (mMultiAccessUnitHelper) {
+        c2res = mMultiAccessUnitHelper->flush(&c2flushedWorks);
+    }
     // Unregister input buffers.
     for (const std::unique_ptr<C2Work>& work : c2flushedWorks) {
         if (work) {
@@ -364,6 +451,9 @@
         std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
         mBlockPools.clear();
     }
+    if (mMultiAccessUnitHelper) {
+        mMultiAccessUnitHelper->reset();
+    }
     InputBufferManager::unregisterFrameData(mListener);
     if (status == C2_OK) {
         return ScopedAStatus::ok();
@@ -377,6 +467,9 @@
         std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
         mBlockPools.clear();
     }
+    if (mMultiAccessUnitHelper) {
+        mMultiAccessUnitHelper->reset();
+    }
     InputBufferManager::unregisterFrameData(mListener);
     if (status == C2_OK) {
         return ScopedAStatus::ok();
@@ -415,7 +508,12 @@
 
 void Component::initListener(const std::shared_ptr<Component>& self) {
     if (__builtin_available(android __ANDROID_API_T__, *)) {
-        std::shared_ptr<C2Component::Listener> c2listener =
+        std::shared_ptr<C2Component::Listener> c2listener;
+        if (mMultiAccessUnitIntf) {
+            mMultiAccessUnitHelper = std::make_shared<MultiAccessUnitHelper>(mMultiAccessUnitIntf);
+        }
+        c2listener = mMultiAccessUnitHelper ?
+                std::make_shared<MultiAccessUnitListener>(self, mMultiAccessUnitHelper) :
                 std::make_shared<Listener>(self);
         c2_status_t res = mComponent->setListener_vb(c2listener, C2_DONT_BLOCK);
         if (res != C2_OK) {
diff --git a/media/codec2/hal/aidl/ComponentInterface.cpp b/media/codec2/hal/aidl/ComponentInterface.cpp
index 2d812c9..1f0534d 100644
--- a/media/codec2/hal/aidl/ComponentInterface.cpp
+++ b/media/codec2/hal/aidl/ComponentInterface.cpp
@@ -24,6 +24,8 @@
 
 #include <utils/Timers.h>
 
+#include <codec2/common/MultiAccessUnitHelper.h>
+
 #include <C2Debug.h>
 #include <C2PlatformSupport.h>
 
@@ -43,9 +45,10 @@
 
 // Implementation of ConfigurableC2Intf based on C2ComponentInterface
 struct CompIntf : public ConfigurableC2Intf {
-    CompIntf(const std::shared_ptr<C2ComponentInterface>& intf) :
+    CompIntf(const std::shared_ptr<C2ComponentInterface>& intf,
+        const std::shared_ptr<MultiAccessUnitInterface>& multiAccessUnitIntf):
         ConfigurableC2Intf{intf->getName(), intf->getId()},
-        mIntf{intf} {
+        mIntf{intf}, mMultiAccessUnitIntf{multiAccessUnitIntf} {
     }
 
     virtual c2_status_t config(
@@ -53,7 +56,34 @@
             c2_blocking_t mayBlock,
             std::vector<std::unique_ptr<C2SettingResult>>* const failures
             ) override {
-        return mIntf->config_vb(params, mayBlock, failures);
+        std::vector<C2Param*> paramsToIntf;
+        std::vector<C2Param*> paramsToLargeFrameIntf;
+        c2_status_t err = C2_OK;
+        if (mMultiAccessUnitIntf == nullptr) {
+            err = mIntf->config_vb(params, mayBlock, failures);
+            return err;
+        }
+        for (auto &p : params) {
+            if (mMultiAccessUnitIntf->isParamSupported(p->index())) {
+                paramsToLargeFrameIntf.push_back(p);
+            } else {
+                paramsToIntf.push_back(p);
+            }
+        }
+        c2_status_t err1 = C2_OK;
+        if (paramsToIntf.size() > 0) {
+            err1 = mIntf->config_vb(paramsToIntf, mayBlock, failures);
+        }
+        if (err1 != C2_OK) {
+            LOG(ERROR) << "We have a failed config";
+        }
+        c2_status_t err2 = C2_OK;
+        if (paramsToLargeFrameIntf.size() > 0) {
+            err2 = mMultiAccessUnitIntf->config(
+                    paramsToLargeFrameIntf, mayBlock, failures);
+        }
+        // TODO: correct failure vector
+        return err1 != C2_OK ? err1 : err2;
     }
 
     virtual c2_status_t query(
@@ -61,23 +91,56 @@
             c2_blocking_t mayBlock,
             std::vector<std::unique_ptr<C2Param>>* const params
             ) const override {
-        return mIntf->query_vb({}, indices, mayBlock, params);
+        c2_status_t err = C2_OK;
+        if (mMultiAccessUnitIntf == nullptr) {
+            err = mIntf->query_vb({}, indices, mayBlock, params);
+            return err;
+        }
+        std::vector<C2Param::Index> paramsToIntf;
+        std::vector<C2Param::Index> paramsToLargeFrameIntf;
+        for (auto &i : indices) {
+            if (mMultiAccessUnitIntf->isParamSupported(i)) {
+                paramsToLargeFrameIntf.push_back(i);
+            } else {
+                paramsToIntf.push_back(i);
+            }
+        }
+        c2_status_t err1 = C2_OK;
+        if (paramsToIntf.size() > 0) {
+            err1 = mIntf->query_vb({}, paramsToIntf, mayBlock, params);
+        }
+        c2_status_t err2 = C2_OK;
+        if (paramsToLargeFrameIntf.size() > 0) {
+            err2 = mMultiAccessUnitIntf->query(
+                    {}, paramsToLargeFrameIntf, mayBlock, params);
+        }
+        // TODO: correct failure vector
+        return err1 != C2_OK ? err1 : err2;
     }
 
     virtual c2_status_t querySupportedParams(
             std::vector<std::shared_ptr<C2ParamDescriptor>>* const params
             ) const override {
-        return mIntf->querySupportedParams_nb(params);
+        c2_status_t err = mIntf->querySupportedParams_nb(params);
+        if (mMultiAccessUnitIntf != nullptr) {
+            err =  mMultiAccessUnitIntf->querySupportedParams(params);
+        }
+        return err;
     }
 
     virtual c2_status_t querySupportedValues(
             std::vector<C2FieldSupportedValuesQuery>& fields,
             c2_blocking_t mayBlock) const override {
-        return mIntf->querySupportedValues_vb(fields, mayBlock);
+        c2_status_t err = mIntf->querySupportedValues_vb(fields, mayBlock);
+        if (mMultiAccessUnitIntf != nullptr) {
+            err = mMultiAccessUnitIntf->querySupportedValues(fields, mayBlock);
+        }
+        return err;
     }
 
 protected:
     std::shared_ptr<C2ComponentInterface> mIntf;
+    std::shared_ptr<MultiAccessUnitInterface> mMultiAccessUnitIntf;
 };
 
 } // unnamed namespace
@@ -85,10 +148,16 @@
 // ComponentInterface
 ComponentInterface::ComponentInterface(
         const std::shared_ptr<C2ComponentInterface>& intf,
+        const std::shared_ptr<ParameterCache>& cache):ComponentInterface(intf, nullptr, cache) {
+}
+
+ComponentInterface::ComponentInterface(
+        const std::shared_ptr<C2ComponentInterface>& intf,
+        const std::shared_ptr<MultiAccessUnitInterface>& multiAccessUnitIntf,
         const std::shared_ptr<ParameterCache>& cache)
       : mInterface{intf},
         mConfigurable{SharedRefBase::make<CachedConfigurable>(
-                std::make_unique<CompIntf>(intf))} {
+                std::make_unique<CompIntf>(intf, multiAccessUnitIntf))} {
     mInit = mConfigurable->init(cache);
 }
 
diff --git a/media/codec2/hal/aidl/ComponentStore.cpp b/media/codec2/hal/aidl/ComponentStore.cpp
index 3f687b5..ef49308 100644
--- a/media/codec2/hal/aidl/ComponentStore.cpp
+++ b/media/codec2/hal/aidl/ComponentStore.cpp
@@ -206,12 +206,19 @@
         const std::shared_ptr<IClientManager>& pool,
         std::shared_ptr<IComponent> *component) {
 
+    if (!listener) {
+        ALOGE("createComponent(): listener is null");
+        return ScopedAStatus::fromServiceSpecificError(Status::BAD_VALUE);
+    }
+    if (!pool) {
+        ALOGE("createComponent(): pool is null");
+        return ScopedAStatus::fromServiceSpecificError(Status::BAD_VALUE);
+    }
+
     std::shared_ptr<C2Component> c2component;
     c2_status_t status =
             mStore->createComponent(name, &c2component);
 
-    ALOGD("createComponent(): listener(%d)", bool(listener));
-
     if (status == C2_OK) {
 #ifndef __ANDROID_APEX__
         c2component = GetFilterWrapper()->maybeWrapComponent(c2component);
diff --git a/media/codec2/hal/aidl/include/codec2/aidl/Component.h b/media/codec2/hal/aidl/include/codec2/aidl/Component.h
index 94b760f..9725bcf 100644
--- a/media/codec2/hal/aidl/include/codec2/aidl/Component.h
+++ b/media/codec2/hal/aidl/include/codec2/aidl/Component.h
@@ -31,6 +31,8 @@
 #include <aidl/android/hardware/media/c2/IInputSurface.h>
 #include <aidl/android/hardware/media/c2/IInputSurfaceConnection.h>
 
+#include <codec2/common/MultiAccessUnitHelper.h>
+
 #include <C2Component.h>
 #include <C2Buffer.h>
 #include <C2.h>
@@ -46,6 +48,8 @@
 namespace c2 {
 namespace utils {
 
+using ::android::MultiAccessUnitInterface;
+using ::android::MultiAccessUnitHelper;
 
 struct ComponentStore;
 
@@ -85,6 +89,8 @@
     std::shared_ptr<C2Component> mComponent;
     std::shared_ptr<ComponentInterface> mInterface;
     std::shared_ptr<IComponentListener> mListener;
+    std::shared_ptr<MultiAccessUnitInterface> mMultiAccessUnitIntf;
+    std::shared_ptr<MultiAccessUnitHelper> mMultiAccessUnitHelper;
     std::shared_ptr<ComponentStore> mStore;
     DefaultBufferPoolSender mBufferPoolSender;
 
@@ -102,6 +108,8 @@
 
     struct Listener;
 
+    friend struct MultiAccessUnitListener;
+
     ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
     static void OnBinderDied(void *cookie);
     static void OnBinderUnlinked(void *cookie);
diff --git a/media/codec2/hal/aidl/include/codec2/aidl/ComponentInterface.h b/media/codec2/hal/aidl/include/codec2/aidl/ComponentInterface.h
index 7723bee..bd19cd6 100644
--- a/media/codec2/hal/aidl/include/codec2/aidl/ComponentInterface.h
+++ b/media/codec2/hal/aidl/include/codec2/aidl/ComponentInterface.h
@@ -22,11 +22,14 @@
 
 #include <aidl/android/hardware/media/c2/BnComponentInterface.h>
 
+#include <codec2/common/MultiAccessUnitHelper.h>
+
 #include <C2Component.h>
 #include <C2Buffer.h>
 #include <C2.h>
 
 #include <memory>
+#include <set>
 
 namespace aidl {
 namespace android {
@@ -35,12 +38,16 @@
 namespace c2 {
 namespace utils {
 
-struct ComponentStore;
+using ::android::MultiAccessUnitInterface;
 
 struct ComponentInterface : public BnComponentInterface {
     ComponentInterface(
             const std::shared_ptr<C2ComponentInterface>& interface,
             const std::shared_ptr<ParameterCache>& cache);
+    ComponentInterface(
+        const std::shared_ptr<C2ComponentInterface>& interface,
+        const std::shared_ptr<MultiAccessUnitInterface>& largeBufferIntf,
+        const std::shared_ptr<ParameterCache>& cache);
     c2_status_t status() const;
     ::ndk::ScopedAStatus getConfigurable(
             std::shared_ptr<IConfigurable> *intf) override;
@@ -51,7 +58,6 @@
     c2_status_t mInit;
 };
 
-
 }  // namespace utils
 }  // namespace c2
 }  // namespace media
diff --git a/media/codec2/hal/common/Android.bp b/media/codec2/hal/common/Android.bp
index 2aedd8b..7d7b285 100644
--- a/media/codec2/hal/common/Android.bp
+++ b/media/codec2/hal/common/Android.bp
@@ -11,6 +11,7 @@
 
     srcs: [
         "BufferTypes.cpp",
+        "MultiAccessUnitHelper.cpp",
     ],
 
     export_include_dirs: ["include/"],
@@ -26,7 +27,10 @@
         "libcodec2_vndk",
         "liblog",
         "libstagefright_foundation",
+        "server_configurable_flags",
     ],
+
+    static_libs: ["aconfig_mediacodec_flags_c_lib"],
 }
 
 cc_library_static {
diff --git a/media/codec2/hal/common/MultiAccessUnitHelper.cpp b/media/codec2/hal/common/MultiAccessUnitHelper.cpp
new file mode 100644
index 0000000..117ce91
--- /dev/null
+++ b/media/codec2/hal/common/MultiAccessUnitHelper.cpp
@@ -0,0 +1,742 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Codec2-MultiAccessUnitHelper"
+#include <android-base/logging.h>
+
+#include <com_android_media_codec_flags.h>
+
+#include <codec2/common/MultiAccessUnitHelper.h>
+#include <android-base/properties.h>
+
+#include <C2BufferPriv.h>
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+
+namespace android {
+
+static C2R MultiAccessUnitParamsSetter(
+        bool mayBlock, C2InterfaceHelper::C2P<C2LargeFrame::output> &me) {
+    (void)mayBlock;
+    C2R res = C2R::Ok();
+    if (!me.F(me.v.maxSize).supportsAtAll(me.v.maxSize)) {
+        res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.maxSize)));
+    } else if (!me.F(me.v.thresholdSize).supportsAtAll(me.v.thresholdSize)) {
+        res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.thresholdSize)));
+    } else if (me.v.maxSize < me.v.thresholdSize) {
+        me.set().maxSize = me.v.thresholdSize;
+    } else if (me.v.thresholdSize == 0 && me.v.maxSize > 0) {
+        me.set().thresholdSize = me.v.maxSize;
+    }
+    std::vector<std::unique_ptr<C2SettingResult>> failures;
+    res.retrieveFailures(&failures);
+    if (!failures.empty()) {
+        me.set().maxSize = 0;
+        me.set().thresholdSize = 0;
+    }
+    return res;
+}
+
+MultiAccessUnitInterface::MultiAccessUnitInterface(
+        const std::shared_ptr<C2ComponentInterface>& interface,
+        std::shared_ptr<C2ReflectorHelper> helper)
+        : C2InterfaceHelper(helper), mC2ComponentIntf(interface) {
+    setDerivedInstance(this);
+    addParameter(
+            DefineParam(mLargeFrameParams, C2_PARAMKEY_OUTPUT_LARGE_FRAME)
+            .withDefault(new C2LargeFrame::output(0u, 0, 0))
+            .withFields({
+                C2F(mLargeFrameParams, maxSize).inRange(
+                        0, c2_min(UINT_MAX, 10 * 512000 * 8 * 2u)),
+                C2F(mLargeFrameParams, thresholdSize).inRange(
+                        0, c2_min(UINT_MAX, 10 * 512000 * 8 * 2u))
+            })
+            .withSetter(MultiAccessUnitParamsSetter)
+            .build());
+    std::vector<std::shared_ptr<C2ParamDescriptor>> supportedParams;
+    querySupportedParams(&supportedParams);
+    // Adding to set to do intf seperation in query/config
+    for (std::shared_ptr<C2ParamDescriptor> &desc : supportedParams) {
+        mSupportedParamIndexSet.insert(desc->index());
+    }
+
+    if (mC2ComponentIntf) {
+        c2_status_t err = mC2ComponentIntf->query_vb({&mKind}, {}, C2_MAY_BLOCK, nullptr);
+    }
+}
+
+bool MultiAccessUnitInterface::isParamSupported(C2Param::Index index) {
+    return (mSupportedParamIndexSet.count(index) != 0);
+}
+
+C2LargeFrame::output MultiAccessUnitInterface::getLargeFrameParam() const {
+    return *mLargeFrameParams;
+}
+
+C2Component::kind_t MultiAccessUnitInterface::kind() const {
+    return (C2Component::kind_t)(mKind.value);
+}
+
+void MultiAccessUnitInterface::getDecoderSampleRateAndChannelCount(
+        uint32_t &sampleRate_, uint32_t &channelCount_) const {
+    if (mC2ComponentIntf) {
+        C2StreamSampleRateInfo::output sampleRate;
+        C2StreamChannelCountInfo::output channelCount;
+        c2_status_t res = mC2ComponentIntf->query_vb(
+                {&sampleRate, &channelCount}, {}, C2_MAY_BLOCK, nullptr);
+        if (res == C2_OK) {
+            sampleRate_ = sampleRate.value;
+            channelCount_ = channelCount.value;
+        }
+    }
+}
+
+//C2MultiAccessUnitBuffer
+class C2MultiAccessUnitBuffer : public C2Buffer {
+    public:
+        explicit C2MultiAccessUnitBuffer(
+                const std::vector<C2ConstLinearBlock> &blocks):
+                C2Buffer(blocks) {
+        }
+};
+
+//MultiAccessUnitHelper
+MultiAccessUnitHelper::MultiAccessUnitHelper(
+        const std::shared_ptr<MultiAccessUnitInterface>& intf):
+        mInit(false),
+        mInterface(intf) {
+    std::shared_ptr<C2AllocatorStore> store = GetCodec2PlatformAllocatorStore();
+    if(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mLinearAllocator) == C2_OK) {
+        mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, ++mBlockPoolId);
+        mInit = true;
+    }
+}
+
+MultiAccessUnitHelper::~MultiAccessUnitHelper() {
+    std::unique_lock<std::mutex> l(mLock);
+    mFrameHolder.clear();
+}
+
+bool MultiAccessUnitHelper::isEnabledOnPlatform() {
+    bool result = com::android::media::codec::flags::provider_->large_audio_frame();
+    if (!result) {
+        false;
+    }
+    //TODO: remove this before launch
+    result = ::android::base::GetBoolProperty("debug.media.c2.large.audio.frame", true);
+    LOG(DEBUG) << "MultiAccessUnitHelper " << (result ? "enabled" : "disabled");
+    return result;
+}
+
+std::shared_ptr<MultiAccessUnitInterface> MultiAccessUnitHelper::getInterface() {
+    return mInterface;
+}
+
+bool MultiAccessUnitHelper::getStatus() {
+    return mInit;
+}
+
+void MultiAccessUnitHelper::reset() {
+    std::lock_guard<std::mutex> l(mLock);
+    mFrameHolder.clear();
+}
+
+c2_status_t MultiAccessUnitHelper::error(
+        std::list<std::unique_ptr<C2Work>> * const worklist) {
+    if (worklist == nullptr) {
+        LOG(ERROR) << "Provided null worklist for error()";
+        return C2_OK;
+    }
+    std::unique_lock<std::mutex> l(mLock);
+    for (auto frame = mFrameHolder.begin(); frame != mFrameHolder.end(); frame++) {
+        if (frame->mLargeWork) {
+            finalizeWork(*frame, 0, true);
+            worklist->push_back(std::move(frame->mLargeWork));
+            frame->reset();
+        }
+    }
+    mFrameHolder.clear();
+    return C2_OK;
+}
+
+c2_status_t MultiAccessUnitHelper::flush(
+        std::list<std::unique_ptr<C2Work>>* const c2flushedWorks) {
+    c2_status_t c2res = C2_OK;
+    std::lock_guard<std::mutex> l(mLock);
+    for (std::unique_ptr<C2Work>& w : *c2flushedWorks) {
+        bool foundFlushedFrame = false;
+        std::list<MultiAccessUnitInfo>::iterator frame =
+                mFrameHolder.begin();
+        while (frame != mFrameHolder.end() && !foundFlushedFrame) {
+            auto it = frame->mComponentFrameIds.find(
+                    w->input.ordinal.frameIndex.peekull());
+            if (it != frame->mComponentFrameIds.end()) {
+                LOG(DEBUG) << "Multi access-unit flush"
+                        << w->input.ordinal.frameIndex.peekull()
+                        << " with " << frame->inOrdinal.frameIndex.peekull();
+                w->input.ordinal.frameIndex = frame->inOrdinal.frameIndex;
+                bool removeEntry = w->worklets.empty()
+                        || !w->worklets.front()
+                        || (w->worklets.front()->output.flags
+                        & C2FrameData::FLAG_INCOMPLETE) == 0;
+                if (removeEntry) {
+                    frame->mComponentFrameIds.erase(it);
+                }
+                foundFlushedFrame = true;
+            }
+            if (frame->mComponentFrameIds.empty()) {
+                frame = mFrameHolder.erase(frame);
+            } else {
+                ++frame;
+            }
+        }
+    }
+    return c2res;
+}
+
+c2_status_t MultiAccessUnitHelper::scatter(
+        std::list<std::unique_ptr<C2Work>> &largeWork,
+        std::list<std::list<std::unique_ptr<C2Work>>>* const processedWork) {
+    LOG(DEBUG) << "Multiple access-unit: scatter";
+    if (processedWork == nullptr) {
+        LOG(ERROR) << "MultiAccessUnitHelper provided with no work list";
+        return C2_CORRUPTED;
+    }
+    for (std::unique_ptr<C2Work>& w : largeWork) {
+        std::list<std::unique_ptr<C2Work>> sliceWork;
+        C2WorkOrdinalStruct inputOrdinal = w->input.ordinal;
+        // To hold correspondence and processing bits b/w input and output
+        MultiAccessUnitInfo frameInfo(inputOrdinal);
+        std::set<uint64_t>& frameSet = frameInfo.mComponentFrameIds;
+        uint64_t newFrameIdx = mFrameIndex++;
+        // TODO: Do not split buffers if component inherantly supports MultipleFrames.
+        // if thats case, only replace frameindex.
+        auto cloneInputWork = [&newFrameIdx](std::unique_ptr<C2Work>& inWork, uint32_t flags) {
+            std::unique_ptr<C2Work> newWork(new C2Work);
+            newWork->input.flags = (C2FrameData::flags_t)flags;
+            newWork->input.ordinal = inWork->input.ordinal;
+            newWork->input.ordinal.frameIndex = newFrameIdx;
+            if (!inWork->input.configUpdate.empty()) {
+                for (std::unique_ptr<C2Param>& param : inWork->input.configUpdate) {
+                    newWork->input.configUpdate.push_back(
+                            std::move(C2Param::Copy(*(param.get()))));
+                }
+            }
+            newWork->input.infoBuffers = (inWork->input.infoBuffers);
+            if (!inWork->worklets.empty() && inWork->worklets.front() != nullptr) {
+                newWork->worklets.emplace_back(new C2Worklet);
+                newWork->worklets.front()->component = inWork->worklets.front()->component;
+                std::vector<std::unique_ptr<C2Tuning>> tunings;
+                for (std::unique_ptr<C2Tuning>& tuning : inWork->worklets.front()->tunings) {
+                    tunings.push_back(std::move(
+                            std::unique_ptr<C2Tuning>(
+                                    static_cast<C2Tuning*>(
+                                            C2Param::Copy(*(tuning.get())).release()))));
+                }
+                newWork->worklets.front()->tunings = std::move(tunings);
+            }
+            return newWork;
+        };
+        if (w->input.buffers.empty()
+                || (w->input.buffers.front() == nullptr)
+                || (!w->input.buffers.front()->hasInfo(
+                        C2AccessUnitInfos::input::PARAM_TYPE))) {
+            LOG(DEBUG) << "Empty or MultiAU info buffer scatter frames with frameIndex "
+                    << inputOrdinal.frameIndex.peekull()
+                    << ") -> newFrameIndex " << newFrameIdx
+                    <<" : input ts " << inputOrdinal.timestamp.peekull();
+            sliceWork.push_back(std::move(cloneInputWork(w, w->input.flags)));
+            if (!w->input.buffers.empty() && w->input.buffers.front() != nullptr) {
+                sliceWork.back()->input.buffers = std::move(w->input.buffers);
+            }
+            frameSet.insert(newFrameIdx);
+            processedWork->push_back(std::move(sliceWork));
+        }  else {
+            const std::vector<std::shared_ptr<C2Buffer>>& inBuffers = w->input.buffers;
+            if (inBuffers.front()->data().linearBlocks().size() == 0) {
+                LOG(ERROR) << "ERROR: Work has Large frame info but has no linear blocks.";
+                return C2_CORRUPTED;
+            }
+            const std::vector<C2ConstLinearBlock>& multiAU =
+                    inBuffers.front()->data().linearBlocks();
+            std::shared_ptr<const C2AccessUnitInfos::input> auInfo =
+                    std::static_pointer_cast<const C2AccessUnitInfos::input>(
+                    w->input.buffers.front()->getInfo(C2AccessUnitInfos::input::PARAM_TYPE));
+            uint32_t offset = 0; uint32_t multiAUSize = multiAU.front().size();
+            bool sendEos = false;
+            for (int idx = 0; idx < auInfo->flexCount(); ++idx) {
+                std::vector<C2ConstLinearBlock> au;
+                const C2AccessUnitInfosStruct &info = auInfo->m.values[idx];
+                sendEos |= (info.flags & C2FrameData::FLAG_END_OF_STREAM);
+                std::unique_ptr<C2Work> newWork = cloneInputWork(w, info.flags);
+                frameSet.insert(newFrameIdx);
+                newFrameIdx = mFrameIndex++;
+                newWork->input.ordinal.timestamp = info.timestamp;
+                au.push_back(multiAU.front().subBlock(offset, info.size));
+                if ((offset + info.size) > multiAUSize) {
+                    LOG(ERROR) << "ERROR: access-unit offset > buffer size"
+                            << " current offset " << (offset + info.size)
+                            << " buffer size " << multiAUSize;
+                    return C2_CORRUPTED;
+                }
+                newWork->input.buffers.push_back(
+                        std::shared_ptr<C2Buffer>(new C2MultiAccessUnitBuffer(au)));
+                LOG(DEBUG) << "Frame scatter queuing frames WITH info in ordinal "
+                        << inputOrdinal.frameIndex.peekull()
+                        << " total offset " << offset << " info.size " << info.size
+                        << " : TS " << newWork->input.ordinal.timestamp.peekull();
+                // add to worklist
+                sliceWork.push_back(std::move(newWork));
+                processedWork->push_back(std::move(sliceWork));
+                offset += info.size;
+            }
+            if (!sendEos && (w->input.flags & C2FrameData::FLAG_END_OF_STREAM)) {
+                if (!processedWork->empty()) {
+                    std::list<std::unique_ptr<C2Work>> &sliceWork = processedWork->back();
+                    if (!sliceWork.empty()) {
+                        std::unique_ptr<C2Work> &work = sliceWork.back();
+                        if (work) {
+                            work->input.flags = C2FrameData::FLAG_END_OF_STREAM;
+                        }
+                    }
+                }
+            }
+        }
+        if (!processedWork->empty()) {
+            {
+                C2LargeFrame::output multiAccessParams = mInterface->getLargeFrameParam();
+                if (mInterface->kind() == C2Component::KIND_DECODER) {
+                    uint32_t sampleRate = 0;
+                    uint32_t channelCount = 0;
+                    uint32_t frameSize = 0;
+                    mInterface->getDecoderSampleRateAndChannelCount(
+                            sampleRate, channelCount);
+                    if (sampleRate > 0 && channelCount > 0) {
+                        frameSize = channelCount * 2;
+                        multiAccessParams.maxSize =
+                                (multiAccessParams.maxSize / frameSize) * frameSize;
+                        multiAccessParams.thresholdSize =
+                                (multiAccessParams.thresholdSize / frameSize) * frameSize;
+                    }
+                }
+                frameInfo.mLargeFrameTuning = multiAccessParams;
+                std::lock_guard<std::mutex> l(mLock);
+                mFrameHolder.push_back(std::move(frameInfo));
+            }
+        }
+    }
+    return C2_OK;
+}
+
+c2_status_t MultiAccessUnitHelper::gather(
+        std::list<std::unique_ptr<C2Work>> &c2workItems,
+        std::list<std::unique_ptr<C2Work>>* const processedWork) {
+    LOG(DEBUG) << "Multi access-unit gather process";
+    if (processedWork == nullptr) {
+        LOG(ERROR) << "Nothing provided for processed work";
+        return C2_CORRUPTED;
+    }
+    auto addOutWork = [&processedWork](std::unique_ptr<C2Work>& work) {
+        processedWork->push_back(std::move(work));
+    };
+    {
+        std::lock_guard<std::mutex> l(mLock);
+        for (auto& work : c2workItems) {
+            LOG(DEBUG) << "FrameHolder Size: " << mFrameHolder.size();
+            uint64_t thisFrameIndex = work->input.ordinal.frameIndex.peekull();
+            bool removeEntry = work->worklets.empty()
+                    || !work->worklets.front()
+                    || (work->worklets.front()->output.flags
+                        & C2FrameData::FLAG_INCOMPLETE) == 0;
+            bool foundFrame = false;
+            std::list<MultiAccessUnitInfo>::iterator frame =
+                    mFrameHolder.begin();
+            while (!foundFrame && frame != mFrameHolder.end()) {
+                auto it = frame->mComponentFrameIds.find(thisFrameIndex);
+                if (it != frame->mComponentFrameIds.end()) {
+                    foundFrame = true;
+                    LOG(DEBUG) << "onWorkDone (frameIndex " << thisFrameIndex
+                            << " worklstsSze " << work->worklets.size()
+                            << ") -> frameIndex " << frame->inOrdinal.frameIndex.peekull();
+                    if (work->result != C2_OK
+                            || work->worklets.empty()
+                            || !work->worklets.front()
+                            || (frame->mLargeFrameTuning.thresholdSize == 0
+                            || frame->mLargeFrameTuning.maxSize == 0)) {
+                        if (removeEntry) {
+                            frame->mComponentFrameIds.erase(it);
+                            removeEntry = false;
+                        }
+                        if (frame->mLargeWork) {
+                            finalizeWork(*frame);
+                            addOutWork(frame->mLargeWork);
+                            frame->reset();
+                        }
+                        c2_status_t workResult = work->result;
+                        frame->mLargeWork = std::move(work);
+                        frame->mLargeWork->input.ordinal.frameIndex =
+                                frame->inOrdinal.frameIndex;
+                        finalizeWork(*frame);
+                        addOutWork(frame->mLargeWork);
+                        frame->reset();
+                        if (workResult != C2_OK) {
+                            frame->mAccessUnitInfos.clear();
+                        }
+                    } else if (C2_OK != processWorklets(*frame, work, addOutWork)) {
+                        LOG(DEBUG) << "Error while processing work";
+                    }
+                    if (removeEntry) {
+                        LOG(DEBUG) << "Removing entry: " << thisFrameIndex
+                                << " -> " << frame->inOrdinal.frameIndex.peekull();
+                        frame->mComponentFrameIds.erase(it);
+                    }
+                    // This is to take care of the last bytes and to decide to send with
+                    // FLAG_INCOMPLETE or not.
+                    if ((frame->mWview
+                            && (frame->mWview->offset() > frame->mLargeFrameTuning.thresholdSize))
+                            || frame->mComponentFrameIds.empty()) {
+                        if (frame->mLargeWork) {
+                            finalizeWork(*frame);
+                            addOutWork(frame->mLargeWork);
+                            frame->reset();
+                        }
+                    }
+                    if (frame->mComponentFrameIds.empty()) {
+                        LOG(DEBUG) << "This frame is finished ID " << thisFrameIndex;
+                        frame = mFrameHolder.erase(frame);
+                        continue;
+                    }
+                } else {
+                    LOG(DEBUG) << "Received an out-of-order output " << thisFrameIndex
+                            << " expected: " <<mFrameHolder.front().inOrdinal.frameIndex.peekull();
+                }
+                frame++;
+            }
+            if (!foundFrame) {
+                LOG(ERROR) <<" Error: Frame Holder reports no frame " << thisFrameIndex;
+            }
+        }
+    }
+    return C2_OK;
+}
+
+c2_status_t MultiAccessUnitHelper::createLinearBlock(MultiAccessUnitInfo &frame) {
+    if (!mInit) {
+        LOG(ERROR) << "Large buffer allocator failed";
+        return C2_NO_MEMORY;
+    }
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    uint32_t maxOutSize = frame.mLargeFrameTuning.maxSize;
+    c2_status_t err = mLinearPool->fetchLinearBlock(maxOutSize, usage, &frame.mBlock);
+    LOG(DEBUG) << "Allocated block with offset : " << frame.mBlock->offset()
+            << " size " << frame.mBlock->size() << " Capacity " << frame.mBlock->capacity();
+    if (err != C2_OK) {
+        LOG(ERROR) << "Error allocating Multi access-unit Buffer";
+        return err;
+    }
+    frame.mWview = std::make_shared<C2WriteView>(frame.mBlock->map().get());
+    LOG(DEBUG) << "Allocated buffer : requested size : " <<
+            frame.mLargeFrameTuning.maxSize
+            << " alloc size " << frame.mWview->size();
+    return C2_OK;
+}
+
+/*
+ * For every work from the component, we try to do aggregation of work here.
+*/
+c2_status_t MultiAccessUnitHelper::processWorklets(MultiAccessUnitInfo &frame,
+        std::unique_ptr<C2Work>& work,
+        const std::function <void(std::unique_ptr<C2Work>&)>& addWork) {
+    // This will allocate work, worklet, c2Block
+    auto allocateWork = [&](MultiAccessUnitInfo &frame,
+            bool allocateWorket = false,
+            bool allocateBuffer = false) {
+        c2_status_t ret = C2_OK;
+        if (frame.mLargeWork == nullptr) {
+            frame.mLargeWork.reset(new C2Work);
+            frame.mLargeWork->input.ordinal = frame.inOrdinal;
+            frame.mLargeWork->input.ordinal.frameIndex = frame.inOrdinal.frameIndex;
+        }
+        if (allocateWorket) {
+            if (frame.mLargeWork->worklets.size() == 0) {
+                frame.mLargeWork->worklets.emplace_back(new C2Worklet);
+            }
+        }
+        if (allocateBuffer) {
+            if (frame.mWview == nullptr) {
+                ret = createLinearBlock(frame);
+            }
+        }
+        return ret;
+    };
+    // we will only have one worklet.
+    bool foundEndOfStream = false;
+    for (auto worklet = work->worklets.begin();
+             worklet != work->worklets.end() && (*worklet) != nullptr; ++worklet) {
+        uint32_t flagsForNoCopy = C2FrameData::FLAG_DROP_FRAME
+                | C2FrameData::FLAG_DISCARD_FRAME
+                | C2FrameData::FLAG_CORRUPT;
+        if ((*worklet)->output.flags & flagsForNoCopy) {
+            if (frame.mLargeWork) {
+                finalizeWork(frame);
+                addWork(frame.mLargeWork);
+                frame.reset();
+            }
+            frame.mLargeWork = std::move(work);
+            frame.mLargeWork->input.ordinal.frameIndex = frame.inOrdinal.frameIndex;
+            finalizeWork(frame);
+            addWork(frame.mLargeWork);
+            frame.reset();
+            return C2_OK;
+        }
+        c2_status_t c2ret = allocateWork(frame, true);
+        if (c2ret != C2_OK) {
+            return c2ret;
+        }
+        C2FrameData& outputFramedata = frame.mLargeWork->worklets.front()->output;
+        if (!(*worklet)->output.configUpdate.empty()) {
+            for (auto& configUpdate : (*worklet)->output.configUpdate) {
+                outputFramedata.configUpdate.push_back(std::move(configUpdate));
+            }
+            (*worklet)->output.configUpdate.clear();
+        }
+        outputFramedata.infoBuffers.insert(outputFramedata.infoBuffers.begin(),
+                (*worklet)->output.infoBuffers.begin(),
+                (*worklet)->output.infoBuffers.end());
+        int64_t sampleTimeUs = 0;
+        uint32_t frameSize = 0;
+        uint32_t sampleRate = 0;
+        uint32_t channelCount = 0;
+        mInterface->getDecoderSampleRateAndChannelCount(sampleRate, channelCount);
+        if (sampleRate > 0 && channelCount > 0) {
+            sampleTimeUs = (1000000u) / (sampleRate * channelCount * 2);
+            frameSize = channelCount * 2;
+        }
+        LOG(DEBUG) << "maxOutSize " << frame.mLargeFrameTuning.maxSize
+                << " threshold " << frame.mLargeFrameTuning.thresholdSize;
+        if ((*worklet)->output.buffers.size() > 0) {
+            allocateWork(frame, true, true);
+        }
+        LOG(DEBUG) << "This worklet has " << (*worklet)->output.buffers.size() << " buffers"
+                << " ts: " << (*worklet)->output.ordinal.timestamp.peekull();
+        int64_t workletTimestamp = (*worklet)->output.ordinal.timestamp.peekull();
+        int64_t timestamp = workletTimestamp;
+        uint32_t flagsForCopy =  ((*worklet)->output.flags) & C2FrameData::FLAG_CODEC_CONFIG;
+        for (int bufIdx = 0; bufIdx < (*worklet)->output.buffers.size(); ++bufIdx) {
+            std::shared_ptr<C2Buffer>& buffer = (*worklet)->output.buffers[bufIdx];
+            if (!buffer || buffer->data().linearBlocks().empty()) {
+                continue;
+            }
+            const std::vector<C2ConstLinearBlock>& blocks = buffer->data().linearBlocks();
+            if (blocks.size() > 0) {
+                uint32_t inputOffset = 0;
+                uint32_t inputSize = blocks.front().size();
+                frame.mInfos.insert(frame.mInfos.end(),
+                        buffer->info().begin(), buffer->info().end());
+                if (frameSize != 0 && (mInterface->kind() == C2Component::KIND_DECODER)) {
+                    // For decoders we only split multiples of 16bChannelCount*2
+                    inputSize -= (inputSize % frameSize);
+                }
+                while (inputOffset < inputSize) {
+                    if (frame.mWview->offset() >= frame.mLargeFrameTuning.thresholdSize) {
+                        frame.mLargeWork->result = C2_OK;
+                        finalizeWork(frame, flagsForCopy);
+                        addWork(frame.mLargeWork);
+                        frame.reset();
+                        allocateWork(frame, true, true);
+                    }
+                    if (mInterface->kind() == C2Component::KIND_ENCODER) {
+                        if (inputSize > frame.mLargeFrameTuning.maxSize) {
+                            LOG(ERROR) << "Enc: Output buffer too small for AU, configured with "
+                                    << frame.mLargeFrameTuning.maxSize
+                                    << " block size: " << blocks.front().size()
+                                    << "alloc size " << frame.mWview->size();
+                            if (frame.mLargeWork
+                                    && frame.mWview && frame.mWview->offset() > 0) {
+                                finalizeWork(frame, flagsForCopy);
+                                addWork(frame.mLargeWork);
+                                frame.reset();
+                                allocateWork(frame, true, false);
+                            }
+                            frame.mLargeWork->result = C2_NO_MEMORY;
+                            finalizeWork(frame, 0, true);
+                            addWork(frame.mLargeWork);
+                            frame.reset();
+                            return C2_NO_MEMORY;
+                        } else if (inputSize > frame.mWview->size()) {
+                            LOG(DEBUG) << "Enc: Large frame hitting bufer limit, current size "
+                                << frame.mWview->offset();
+                            if (frame.mLargeWork
+                                    && frame.mWview && frame.mWview->offset() > 0) {
+                                finalizeWork(frame, flagsForCopy);
+                                addWork(frame.mLargeWork);
+                                frame.reset();
+                                allocateWork(frame, true, true);
+                            }
+                        }
+                    }
+                    C2ReadView rView = blocks.front().map().get();
+                    if (rView.error()) {
+                        LOG(ERROR) << "Buffer read view error";
+                        frame.mLargeWork->result = rView.error();
+                        frame.mLargeWork->worklets.clear();
+                        finalizeWork(frame, 0, true);
+                        addWork(frame.mLargeWork);
+                        frame.reset();
+                        return C2_NO_MEMORY;
+                    }
+                    uint32_t toCopy = 0;
+                    if (mInterface->kind() == C2Component::KIND_ENCODER) {
+                        toCopy = inputSize;
+                    } else {
+                        toCopy = c2_min(frame.mWview->size(), (inputSize - inputOffset));
+                        timestamp = workletTimestamp + inputOffset * sampleTimeUs;
+                        LOG(DEBUG) << "ts " << timestamp
+                                << " copiedOutput " << inputOffset
+                                << " sampleTimeUs " << sampleTimeUs;
+                    }
+                    LOG(DEBUG) << " Copy size " << toCopy
+                            << " ts " << timestamp;
+                    memcpy(frame.mWview->data(), rView.data() + inputOffset, toCopy);
+                    frame.mWview->setOffset(frame.mWview->offset() + toCopy);
+                    inputOffset += toCopy;
+                    mergeAccessUnitInfo(frame, flagsForCopy, toCopy, timestamp);
+                }
+            } else {
+                frame.mLargeWork->worklets.front()->output.buffers.push_back(std::move(buffer));
+                LOG(DEBUG) << "Copying worklets without linear buffer";
+            }
+        }
+        uint32_t flagsForCsdOrEnd = (*worklet)->output.flags
+                & (C2FrameData::FLAG_END_OF_STREAM | C2FrameData::FLAG_CODEC_CONFIG);
+        if (flagsForCsdOrEnd) {
+            LOG(DEBUG) << "Output worklet has CSD/EOS data";
+            frame.mLargeWork->result = C2_OK;
+            // we can assign timestamp as this will be evaluated in finalizeWork
+            frame.mLargeWork->worklets.front()->output.ordinal.timestamp = timestamp;
+            finalizeWork(frame, flagsForCsdOrEnd, true);
+            addWork(frame.mLargeWork);
+            frame.reset();
+        }
+    }
+    return C2_OK;
+}
+
+c2_status_t MultiAccessUnitHelper::finalizeWork(
+        MultiAccessUnitInfo& frame, uint32_t inFlags, bool forceComplete) {
+    if (frame.mLargeWork == nullptr) {
+        return C2_OK;
+    }
+    //prepare input ordinal
+    frame.mLargeWork->input.ordinal = frame.inOrdinal;
+    // remove this
+    int64_t timeStampUs = frame.inOrdinal.timestamp.peekull();
+    if (!frame.mAccessUnitInfos.empty()) {
+        timeStampUs = frame.mAccessUnitInfos.front().timestamp;
+    } else if (!frame.mLargeWork->worklets.empty()) {
+        std::unique_ptr<C2Worklet> &worklet = frame.mLargeWork->worklets.front();
+        if (worklet) {
+            timeStampUs = worklet->output.ordinal.timestamp.peekull();
+        }
+    }
+    LOG(DEBUG) << "Finalizing work with input Idx "
+            << frame.mLargeWork->input.ordinal.frameIndex.peekull()
+            << " timestamp " << timeStampUs;
+    uint32_t finalFlags = 0;
+    if ((!forceComplete)
+            && (frame.mLargeWork->result == C2_OK)
+            && (!frame.mComponentFrameIds.empty())) {
+        finalFlags |= C2FrameData::FLAG_INCOMPLETE;
+    }
+    if (frame.mLargeWork->result == C2_OK) {
+        finalFlags |= inFlags;
+    }
+    // update worklet if present
+    if (!frame.mLargeWork->worklets.empty() &&
+            frame.mLargeWork->worklets.front() != nullptr) {
+        frame.mLargeWork->workletsProcessed = 1;
+        C2FrameData& outFrameData = frame.mLargeWork->worklets.front()->output;
+        outFrameData.ordinal.frameIndex = frame.inOrdinal.frameIndex.peekull();
+        outFrameData.ordinal.timestamp = timeStampUs;
+        finalFlags |= frame.mLargeWork->worklets.front()->output.flags;
+        outFrameData.flags = (C2FrameData::flags_t)finalFlags;
+        // update buffers
+        if (frame.mBlock && (frame.mWview->offset() > 0)) {
+            size_t size = frame.mWview->offset();
+            LOG(DEBUG) << "Finalize : Block: Large frame size set as " << size
+                    << " timestamp as " << timeStampUs
+                    << "frameIndex " << outFrameData.ordinal.frameIndex.peekull();
+            frame.mWview->setOffset(0);
+            std::shared_ptr<C2Buffer> c2Buffer = C2Buffer::CreateLinearBuffer(
+                    frame.mBlock->share(0, size, ::C2Fence()));
+            if (frame.mAccessUnitInfos.size() > 0) {
+                if (finalFlags & C2FrameData::FLAG_END_OF_STREAM) {
+                    frame.mAccessUnitInfos.back().flags |=
+                            C2FrameData::FLAG_END_OF_STREAM;
+                }
+                std::shared_ptr<C2AccessUnitInfos::output> largeFrame =
+                        C2AccessUnitInfos::output::AllocShared(
+                        frame.mAccessUnitInfos.size(), 0u, frame.mAccessUnitInfos);
+                frame.mInfos.push_back(largeFrame);
+                frame.mAccessUnitInfos.clear();
+            }
+            for (auto &info : frame.mInfos) {
+                c2Buffer->setInfo(std::const_pointer_cast<C2Info>(info));
+            }
+            frame.mLargeWork->worklets.front()->output.buffers.push_back(std::move(c2Buffer));
+            frame.mInfos.clear();
+            frame.mBlock.reset();
+            frame.mWview.reset();
+        }
+    }
+    LOG(DEBUG) << "Multi access-unitflag setting as " << finalFlags;
+    return C2_OK;
+}
+
+void MultiAccessUnitHelper::mergeAccessUnitInfo(
+        MultiAccessUnitInfo &frame,
+        uint32_t flags_,
+        uint32_t size,
+        int64_t timestamp) {
+    // Remove flags that are not part of Access unit info
+    uint32_t flags = flags_ & ~(C2FrameData::FLAG_INCOMPLETE
+            | C2FrameData::FLAG_DISCARD_FRAME
+            | C2FrameData::FLAG_CORRUPT
+            | C2FrameData::FLAG_CORRECTED);
+    if (frame.mAccessUnitInfos.empty()) {
+        frame.mAccessUnitInfos.emplace_back(flags, size, timestamp);
+        return;
+    }
+    if ((mInterface->kind() == C2Component::KIND_DECODER) &&
+            (frame.mAccessUnitInfos.back().flags == flags)) {
+        // merge access units here
+        C2AccessUnitInfosStruct &s = frame.mAccessUnitInfos.back();
+        s.size += size; // don't have to update timestamp
+    } else {
+        frame.mAccessUnitInfos.emplace_back(flags, size, timestamp);
+    }
+}
+
+void MultiAccessUnitHelper::MultiAccessUnitInfo::reset() {
+    mBlock.reset();
+    mWview.reset();
+    mInfos.clear();
+    mAccessUnitInfos.clear();
+    mLargeWork.reset();
+}
+
+}  // namespace android
\ No newline at end of file
diff --git a/media/codec2/hal/common/include/codec2/common/MultiAccessUnitHelper.h b/media/codec2/hal/common/include/codec2/common/MultiAccessUnitHelper.h
new file mode 100644
index 0000000..ef5cff9
--- /dev/null
+++ b/media/codec2/hal/common/include/codec2/common/MultiAccessUnitHelper.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CODEC2_COMMON_MULTI_ACCESSUNIT_HELPER_H
+#define CODEC2_COMMON_MULTI_ACCESSUNIT_HELPER_H
+
+#include <hidl/Status.h>
+#include <hwbinder/IBinder.h>
+
+#include <C2Config.h>
+#include <util/C2InterfaceHelper.h>
+#include <C2Buffer.h>
+#include <C2.h>
+
+#include <set>
+#include <memory>
+#include <mutex>
+
+namespace android {
+
+struct MultiAccessUnitHelper;
+
+struct MultiAccessUnitInterface : public C2InterfaceHelper {
+    explicit MultiAccessUnitInterface(
+            const std::shared_ptr<C2ComponentInterface>& interface,
+            std::shared_ptr<C2ReflectorHelper> helper);
+
+    bool isParamSupported(C2Param::Index index);
+    C2LargeFrame::output getLargeFrameParam() const;
+    C2Component::kind_t kind() const;
+
+protected:
+    void getDecoderSampleRateAndChannelCount(
+            uint32_t &sampleRate_, uint32_t &channelCount_) const;
+    const std::shared_ptr<C2ComponentInterface> mC2ComponentIntf;
+    std::shared_ptr<C2LargeFrame::output> mLargeFrameParams;
+    C2ComponentKindSetting mKind;
+    std::set<C2Param::Index> mSupportedParamIndexSet;
+
+    friend struct MultiAccessUnitHelper;
+};
+
+struct MultiAccessUnitHelper {
+public:
+    MultiAccessUnitHelper(
+            const std::shared_ptr<MultiAccessUnitInterface>& intf);
+
+    virtual ~MultiAccessUnitHelper();
+
+    static bool isEnabledOnPlatform();
+
+    /*
+     * Scatters the incoming linear buffer into access-unit sized buffers
+     * based on the access-unit info.
+     */
+    c2_status_t scatter(
+            std::list<std::unique_ptr<C2Work>> &c2workItems,
+            std::list<std::list<std::unique_ptr<C2Work>>> * const processedWork);
+
+    /*
+     * Gathers different access-units into a single buffer based on the scatter list
+     * and the configured max and threshold sizes. This also generates the associated
+     * access-unit information and attach it with the final result.
+     */
+    c2_status_t gather(
+            std::list<std::unique_ptr<C2Work>> &c2workItems,
+            std::list<std::unique_ptr<C2Work>> * const processedWork);
+
+    /*
+     * Flushes the codec and generated the list of flushed buffers.
+     */
+    c2_status_t flush(
+            std::list<std::unique_ptr<C2Work>> * const c2flushedWorks);
+
+    /*
+     * Gets all the pending buffers under generation in c2workItems.
+     */
+    c2_status_t error(std::list<std::unique_ptr<C2Work>> * const c2workItems);
+
+    /*
+     * Get the interface object of this handler.
+     */
+    std::shared_ptr<MultiAccessUnitInterface> getInterface();
+
+    /*
+     * Gets the status of the object. This really is to make sure that
+     * all the allocators are configured properly within the handler.
+     */
+    bool getStatus();
+
+    /*
+     * Resets the structures inside the handler.
+     */
+    void reset();
+
+protected:
+
+    struct MultiAccessUnitInfo {
+        /*
+         * From the input
+         * Ordinal of the input frame
+         */
+        C2WorkOrdinalStruct inOrdinal;
+
+        /*
+         * Frame indexes of the scattered buffers
+         */
+        std::set<uint64_t> mComponentFrameIds;
+
+        /*
+         * For the output
+         * Current output block.
+         */
+        std::shared_ptr<C2LinearBlock> mBlock;
+
+        /*
+         * Write view of current block
+         */
+        std::shared_ptr<C2WriteView> mWview;
+
+        /*
+         * C2Info related to the current mBlock
+         */
+        std::vector<std::shared_ptr<const C2Info>> mInfos;
+
+        /*
+         * C2AccessUnitInfos for the current buffer
+         */
+        std::vector<C2AccessUnitInfosStruct> mAccessUnitInfos;
+
+        /*
+         * Current tuning used to process this input work
+         */
+        C2LargeFrame::output mLargeFrameTuning;
+
+        /*
+         * Current output C2Work being processed
+         */
+        std::unique_ptr<C2Work> mLargeWork;
+
+        MultiAccessUnitInfo(C2WorkOrdinalStruct ordinal):inOrdinal(ordinal) {
+
+        }
+
+        /*
+         * Resets this frame
+         */
+        void reset();
+    };
+
+    /*
+     * Creates a linear block to be used with work
+     */
+    c2_status_t createLinearBlock(MultiAccessUnitInfo &frame);
+
+    /*
+     * Processes worklets from the component
+     */
+    c2_status_t processWorklets(MultiAccessUnitInfo &frame,
+                std::unique_ptr<C2Work> &work,
+                const std::function <void(std::unique_ptr<C2Work>&)> &addWork);
+
+    /*
+     * Finalizes the work to be send out.
+     */
+    c2_status_t finalizeWork(MultiAccessUnitInfo &frame,
+            uint32_t flags = 0, bool forceComplete = false);
+
+    /*
+     * Merges different access unit infos if possible
+     */
+    void mergeAccessUnitInfo(MultiAccessUnitInfo &frame,
+            uint32_t flags,
+            uint32_t size,
+            int64_t timestamp);
+
+    bool mInit;
+
+    // Interface of this module
+    std::shared_ptr<MultiAccessUnitInterface> mInterface;
+    // Local pool id used for output buffer allocation
+    C2BlockPool::local_id_t mBlockPoolId;
+    // C2Blockpool for output buffer allocation
+    std::shared_ptr<C2BlockPool> mLinearPool;
+    // Allocator for output buffer allocation
+    std::shared_ptr<C2Allocator> mLinearAllocator;
+    // FrameIndex for the current outgoing work
+    std::atomic_uint64_t mFrameIndex;
+    // Mutex to protect mFrameHolder
+    std::mutex mLock;
+    // List of Infos that contains the input and
+    // output work and buffer objects
+    std::list<MultiAccessUnitInfo> mFrameHolder;
+};
+
+}  // namespace android
+
+#endif  // CODEC2_COMMON_MULTI_ACCESSUNIT_HELPER_H
diff --git a/media/codec2/hal/hidl/1.0/utils/Android.bp b/media/codec2/hal/hidl/1.0/utils/Android.bp
index 2f2ecd1..9646a0b 100644
--- a/media/codec2/hal/hidl/1.0/utils/Android.bp
+++ b/media/codec2/hal/hidl/1.0/utils/Android.bp
@@ -52,7 +52,6 @@
     ],
 }
 
-
 // DO NOT DEPEND ON THIS DIRECTLY
 // use libcodec2-hidl-defaults instead
 cc_library {
diff --git a/media/codec2/hal/hidl/1.0/utils/Component.cpp b/media/codec2/hal/hidl/1.0/utils/Component.cpp
index 0aeed08..ebbaafc 100644
--- a/media/codec2/hal/hidl/1.0/utils/Component.cpp
+++ b/media/codec2/hal/hidl/1.0/utils/Component.cpp
@@ -135,6 +135,52 @@
     wp<IComponentListener> mListener;
 };
 
+// Component listener for handle multiple access-units
+struct MultiAccessUnitListener : public Component::Listener {
+    MultiAccessUnitListener(const sp<Component> &component,
+            const std::shared_ptr<MultiAccessUnitHelper> &handler):
+        Listener(component), mHandler(handler) {
+    }
+
+    virtual void onError_nb(
+            std::weak_ptr<C2Component> c2component,
+            uint32_t errorCode) override {
+        if (mHandler) {
+            std::list<std::unique_ptr<C2Work>> worklist;
+            mHandler->error(&worklist);
+            if (!worklist.empty()) {
+                Listener::onWorkDone_nb(c2component, std::move(worklist));
+            }
+        }
+        Listener::onError_nb(c2component, errorCode);
+    }
+
+    virtual void onTripped_nb(
+            std::weak_ptr<C2Component> c2component,
+            std::vector<std::shared_ptr<C2SettingResult>> c2settingResult
+            ) override {
+        Listener::onTripped_nb(c2component,
+                c2settingResult);
+    }
+
+    virtual void onWorkDone_nb(
+            std::weak_ptr<C2Component> c2component,
+            std::list<std::unique_ptr<C2Work>> c2workItems) override {
+        if (mHandler) {
+            std::list<std::unique_ptr<C2Work>> processedWork;
+            mHandler->gather(c2workItems, &processedWork);
+            if (!processedWork.empty()) {
+                Listener::onWorkDone_nb(c2component, std::move(processedWork));
+            }
+        } else {
+            Listener::onWorkDone_nb(c2component, std::move(c2workItems));
+        }
+    }
+
+    protected:
+        std::shared_ptr<MultiAccessUnitHelper> mHandler;
+};
+
 // Component::Sink
 struct Component::Sink : public IInputSink {
     std::shared_ptr<Component> mComponent;
@@ -208,13 +254,37 @@
         const sp<::android::hardware::media::bufferpool::V2_0::
         IClientManager>& clientPoolManager)
       : mComponent{component},
-        mInterface{new ComponentInterface(component->intf(),
-                                          store->getParameterCache())},
         mListener{listener},
         mStore{store},
         mBufferPoolSender{clientPoolManager} {
     // Retrieve supported parameters from store
     // TODO: We could cache this per component/interface type
+    if (MultiAccessUnitHelper::isEnabledOnPlatform()) {
+        c2_status_t err = C2_OK;
+        C2ComponentDomainSetting domain;
+        std::vector<std::unique_ptr<C2Param>> heapParams;
+        err = component->intf()->query_vb({&domain}, {}, C2_MAY_BLOCK, &heapParams);
+        if (err == C2_OK && (domain.value == C2Component::DOMAIN_AUDIO)) {
+            std::vector<std::shared_ptr<C2ParamDescriptor>> params;
+            bool isComponentSupportsLargeAudioFrame = false;
+            component->intf()->querySupportedParams_nb(&params);
+            for (const auto &paramDesc : params) {
+                if (paramDesc->name().compare(C2_PARAMKEY_OUTPUT_LARGE_FRAME) == 0) {
+                    isComponentSupportsLargeAudioFrame = true;
+                    LOG(VERBOSE) << "Underlying component supports large frame audio";
+                    break;
+                }
+            }
+            if (!isComponentSupportsLargeAudioFrame) {
+                mMultiAccessUnitIntf = std::make_shared<MultiAccessUnitInterface>(
+                        component->intf(),
+                        std::static_pointer_cast<C2ReflectorHelper>(
+                                GetCodec2PlatformComponentStore()->getParamReflector()));
+            }
+        }
+    }
+    mInterface = new ComponentInterface(
+            component->intf(), mMultiAccessUnitIntf, store->getParameterCache());
     mInit = mInterface->status();
 }
 
@@ -240,7 +310,6 @@
 // Methods from ::android::hardware::media::c2::V1_0::IComponent
 Return<Status> Component::queue(const WorkBundle& workBundle) {
     std::list<std::unique_ptr<C2Work>> c2works;
-
     if (!objcpy(&c2works, workBundle)) {
         return Status::CORRUPTED;
     }
@@ -252,7 +321,19 @@
                     registerFrameData(mListener, work->input);
         }
     }
-
+    c2_status_t err = C2_OK;
+    if (mMultiAccessUnitHelper) {
+        std::list<std::list<std::unique_ptr<C2Work>>> c2worklists;
+        mMultiAccessUnitHelper->scatter(c2works, &c2worklists);
+        for (auto &c2worklist : c2worklists) {
+            err = mComponent->queue_nb(&c2worklist);
+            if (err != C2_OK) {
+                LOG(ERROR) << "Error Queuing to component.";
+                break;
+            }
+        }
+        return static_cast<Status>(err);
+    }
     return static_cast<Status>(mComponent->queue_nb(&c2works));
 }
 
@@ -261,6 +342,9 @@
     c2_status_t c2res = mComponent->flush_sm(
             C2Component::FLUSH_COMPONENT,
             &c2flushedWorks);
+    if (mMultiAccessUnitHelper) {
+        c2res = mMultiAccessUnitHelper->flush(&c2flushedWorks);
+    }
 
     // Unregister input buffers.
     for (const std::unique_ptr<C2Work>& work : c2flushedWorks) {
@@ -469,6 +553,9 @@
         std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
         mBlockPools.clear();
     }
+    if (mMultiAccessUnitHelper) {
+        mMultiAccessUnitHelper->reset();
+    }
     InputBufferManager::unregisterFrameData(mListener);
     return status;
 }
@@ -479,6 +566,9 @@
         std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
         mBlockPools.clear();
     }
+    if (mMultiAccessUnitHelper) {
+        mMultiAccessUnitHelper->reset();
+    }
     InputBufferManager::unregisterFrameData(mListener);
     return status;
 }
@@ -501,8 +591,14 @@
 }
 
 void Component::initListener(const sp<Component>& self) {
-    std::shared_ptr<C2Component::Listener> c2listener =
+    std::shared_ptr<C2Component::Listener> c2listener;
+    if (mMultiAccessUnitIntf) {
+        mMultiAccessUnitHelper = std::make_shared<MultiAccessUnitHelper>(mMultiAccessUnitIntf);
+    }
+    c2listener = mMultiAccessUnitHelper ?
+            std::make_shared<MultiAccessUnitListener>(self, mMultiAccessUnitHelper) :
             std::make_shared<Listener>(self);
+
     c2_status_t res = mComponent->setListener_vb(c2listener, C2_DONT_BLOCK);
     if (res != C2_OK) {
         mInit = res;
diff --git a/media/codec2/hal/hidl/1.0/utils/ComponentInterface.cpp b/media/codec2/hal/hidl/1.0/utils/ComponentInterface.cpp
index 12078e0..5a5e780 100644
--- a/media/codec2/hal/hidl/1.0/utils/ComponentInterface.cpp
+++ b/media/codec2/hal/hidl/1.0/utils/ComponentInterface.cpp
@@ -25,7 +25,6 @@
 #include <hidl/HidlBinderSupport.h>
 #include <utils/Timers.h>
 
-#include <C2BqBufferPriv.h>
 #include <C2Debug.h>
 #include <C2PlatformSupport.h>
 
@@ -45,9 +44,10 @@
 
 // Implementation of ConfigurableC2Intf based on C2ComponentInterface
 struct CompIntf : public ConfigurableC2Intf {
-    CompIntf(const std::shared_ptr<C2ComponentInterface>& intf) :
+    CompIntf(const std::shared_ptr<C2ComponentInterface>& intf,
+        const std::shared_ptr<MultiAccessUnitInterface>& multiAccessUnitIntf):
         ConfigurableC2Intf{intf->getName(), intf->getId()},
-        mIntf{intf} {
+        mIntf{intf}, mMultiAccessUnitIntf{multiAccessUnitIntf} {
     }
 
     virtual c2_status_t config(
@@ -55,7 +55,34 @@
             c2_blocking_t mayBlock,
             std::vector<std::unique_ptr<C2SettingResult>>* const failures
             ) override {
-        return mIntf->config_vb(params, mayBlock, failures);
+        std::vector<C2Param*> paramsToIntf;
+        std::vector<C2Param*> paramsToLargeFrameIntf;
+        c2_status_t err = C2_OK;
+        if (mMultiAccessUnitIntf == nullptr) {
+            err = mIntf->config_vb(params, mayBlock, failures);
+            return err;
+        }
+        for (auto &p : params) {
+            if (mMultiAccessUnitIntf->isParamSupported(p->index())) {
+                paramsToLargeFrameIntf.push_back(p);
+            } else {
+                paramsToIntf.push_back(p);
+            }
+        }
+        c2_status_t err1 = C2_OK;
+        if (paramsToIntf.size() > 0) {
+            err1 = mIntf->config_vb(paramsToIntf, mayBlock, failures);
+        }
+        if (err1 != C2_OK) {
+            LOG(ERROR) << "We have a failed config";
+        }
+        c2_status_t err2 = C2_OK;
+        if (paramsToLargeFrameIntf.size() > 0) {
+            err2 = mMultiAccessUnitIntf->config(
+                    paramsToLargeFrameIntf, mayBlock, failures);
+        }
+        // TODO: correct failure vector
+        return err1 != C2_OK ? err1 : err2;
     }
 
     virtual c2_status_t query(
@@ -63,33 +90,74 @@
             c2_blocking_t mayBlock,
             std::vector<std::unique_ptr<C2Param>>* const params
             ) const override {
-        return mIntf->query_vb({}, indices, mayBlock, params);
+        c2_status_t err = C2_OK;
+        if (mMultiAccessUnitIntf == nullptr) {
+            err = mIntf->query_vb({}, indices, mayBlock, params);
+            return err;
+        }
+        std::vector<C2Param::Index> paramsToIntf;
+        std::vector<C2Param::Index> paramsToLargeFrameIntf;
+        for (auto &i : indices) {
+            if (mMultiAccessUnitIntf->isParamSupported(i)) {
+                paramsToLargeFrameIntf.push_back(i);
+            } else {
+                paramsToIntf.push_back(i);
+            }
+        }
+        c2_status_t err1 = C2_OK;
+        if (paramsToIntf.size() > 0) {
+            err1 = mIntf->query_vb({}, paramsToIntf, mayBlock, params);
+        }
+        c2_status_t err2 = C2_OK;
+        if (paramsToLargeFrameIntf.size() > 0) {
+            err2 = mMultiAccessUnitIntf->query(
+                    {}, paramsToLargeFrameIntf, mayBlock, params);
+        }
+        // TODO: correct failure vector
+        return err1 != C2_OK ? err1 : err2;
     }
 
     virtual c2_status_t querySupportedParams(
             std::vector<std::shared_ptr<C2ParamDescriptor>>* const params
             ) const override {
-        return mIntf->querySupportedParams_nb(params);
+        c2_status_t err = mIntf->querySupportedParams_nb(params);
+        if (mMultiAccessUnitIntf != nullptr) {
+            err =  mMultiAccessUnitIntf->querySupportedParams(params);
+        }
+        return err;
     }
 
     virtual c2_status_t querySupportedValues(
             std::vector<C2FieldSupportedValuesQuery>& fields,
             c2_blocking_t mayBlock) const override {
-        return mIntf->querySupportedValues_vb(fields, mayBlock);
+        c2_status_t err = mIntf->querySupportedValues_vb(fields, mayBlock);
+        if (mMultiAccessUnitIntf != nullptr) {
+            err = mMultiAccessUnitIntf->querySupportedValues(fields, mayBlock);
+        }
+        return err;
     }
 
 protected:
     std::shared_ptr<C2ComponentInterface> mIntf;
+    std::shared_ptr<MultiAccessUnitInterface> mMultiAccessUnitIntf;
 };
 
 } // unnamed namespace
 
+
 // ComponentInterface
 ComponentInterface::ComponentInterface(
         const std::shared_ptr<C2ComponentInterface>& intf,
+        const std::shared_ptr<ParameterCache>& cache):ComponentInterface(intf, nullptr, cache) {
+}
+
+ComponentInterface::ComponentInterface(
+        const std::shared_ptr<C2ComponentInterface>& intf,
+        const std::shared_ptr<MultiAccessUnitInterface>& multiAccessUnitIntf,
         const std::shared_ptr<ParameterCache>& cache)
       : mInterface{intf},
-        mConfigurable{new CachedConfigurable(std::make_unique<CompIntf>(intf))} {
+        mConfigurable{new CachedConfigurable(
+                std::make_unique<CompIntf>(intf, multiAccessUnitIntf))} {
     mInit = mConfigurable->init(cache);
 }
 
diff --git a/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h b/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h
index 3f55618..aed94ec 100644
--- a/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h
+++ b/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h
@@ -30,6 +30,8 @@
 #include <hidl/Status.h>
 #include <hwbinder/IBinder.h>
 
+#include <codec2/common/MultiAccessUnitHelper.h>
+
 #include <C2Component.h>
 #include <C2Buffer.h>
 #include <C2.h>
@@ -54,6 +56,8 @@
 using ::android::hardware::IBinder;
 using ::android::sp;
 using ::android::wp;
+using ::android::MultiAccessUnitInterface;
+using ::android::MultiAccessUnitHelper;
 
 struct ComponentStore;
 
@@ -113,6 +117,8 @@
     std::shared_ptr<C2Component> mComponent;
     sp<ComponentInterface> mInterface;
     sp<IComponentListener> mListener;
+    std::shared_ptr<MultiAccessUnitInterface> mMultiAccessUnitIntf;
+    std::shared_ptr<MultiAccessUnitHelper> mMultiAccessUnitHelper;
     sp<ComponentStore> mStore;
     ::android::hardware::media::c2::V1_0::utils::DefaultBufferPoolSender
             mBufferPoolSender;
@@ -135,6 +141,8 @@
 
     struct Listener;
 
+    friend struct MultiAccessUnitListener;
+
     using HwDeathRecipient = ::android::hardware::hidl_death_recipient;
     sp<HwDeathRecipient> mDeathRecipient;
     bool mClientDied{false};
diff --git a/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h b/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h
index 9102f92..2995faf 100644
--- a/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h
+++ b/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h
@@ -23,10 +23,15 @@
 #include <android/hardware/media/c2/1.0/IComponentInterface.h>
 #include <hidl/Status.h>
 
+#include <codec2/common/MultiAccessUnitHelper.h>
+
 #include <C2Component.h>
 #include <C2Buffer.h>
+#include <C2Config.h>
+#include <util/C2InterfaceHelper.h>
 #include <C2.h>
 
+#include <set>
 #include <memory>
 
 namespace android {
@@ -39,6 +44,7 @@
 using ::android::hardware::Return;
 using ::android::hardware::Void;
 using ::android::sp;
+using ::android::MultiAccessUnitInterface;
 
 struct ComponentStore;
 
@@ -46,6 +52,11 @@
     ComponentInterface(
             const std::shared_ptr<C2ComponentInterface>& interface,
             const std::shared_ptr<ParameterCache>& cache);
+
+    ComponentInterface(
+            const std::shared_ptr<C2ComponentInterface>& interface,
+            const std::shared_ptr<MultiAccessUnitInterface>& largeBufferIntf,
+            const std::shared_ptr<ParameterCache>& cache);
     c2_status_t status() const;
     virtual Return<sp<IConfigurable>> getConfigurable() override;
 
diff --git a/media/codec2/hal/hidl/1.1/utils/Android.bp b/media/codec2/hal/hidl/1.1/utils/Android.bp
index 4f86511..d8b5db5 100644
--- a/media/codec2/hal/hidl/1.1/utils/Android.bp
+++ b/media/codec2/hal/hidl/1.1/utils/Android.bp
@@ -54,7 +54,6 @@
     ],
 }
 
-
 // DO NOT DEPEND ON THIS DIRECTLY
 // use libcodec2-hidl-defaults instead
 cc_library {
@@ -66,7 +65,6 @@
         "com.android.media.swcodec",
     ],
 
-
     defaults: ["hidl_defaults"],
 
     srcs: [
@@ -97,6 +95,7 @@
         "android.hardware.media.omx@1.0",
         "libbase",
         "libcodec2",
+        "libcodec2_hal_common",
         "libcodec2_hidl@1.0",
         "libcodec2_hidl_plugin_stub",
         "libcodec2_vndk",
@@ -173,4 +172,3 @@
         "libhidlbase",
     ],
 }
-
diff --git a/media/codec2/hal/hidl/1.1/utils/Component.cpp b/media/codec2/hal/hidl/1.1/utils/Component.cpp
index d0f4f19..5073983 100644
--- a/media/codec2/hal/hidl/1.1/utils/Component.cpp
+++ b/media/codec2/hal/hidl/1.1/utils/Component.cpp
@@ -29,6 +29,8 @@
 #include <hidl/HidlBinderSupport.h>
 #include <utils/Timers.h>
 
+#include <codec2/common/MultiAccessUnitHelper.h>
+
 #include <C2BqBufferPriv.h>
 #include <C2Debug.h>
 #include <C2PlatformSupport.h>
@@ -44,6 +46,8 @@
 namespace utils {
 
 using namespace ::android;
+using ::android::MultiAccessUnitInterface;
+using ::android::MultiAccessUnitHelper;
 
 // ComponentListener wrapper
 struct Component::Listener : public C2Component::Listener {
@@ -135,6 +139,52 @@
     wp<IComponentListener> mListener;
 };
 
+// Component listener for handle multiple access-units
+struct MultiAccessUnitListener : public Component::Listener {
+    MultiAccessUnitListener(const sp<Component> &component,
+            const std::shared_ptr<MultiAccessUnitHelper> &helper):
+        Listener(component), mHelper(helper) {
+    }
+
+    virtual void onError_nb(
+            std::weak_ptr<C2Component> c2component,
+            uint32_t errorCode) override {
+        if (mHelper) {
+            std::list<std::unique_ptr<C2Work>> worklist;
+            mHelper->error(&worklist);
+            if (!worklist.empty()) {
+                Listener::onWorkDone_nb(c2component, std::move(worklist));
+            }
+        }
+        Listener::onError_nb(c2component, errorCode);
+    }
+
+    virtual void onTripped_nb(
+            std::weak_ptr<C2Component> c2component,
+            std::vector<std::shared_ptr<C2SettingResult>> c2settingResult
+            ) override {
+        Listener::onTripped_nb(c2component,
+                c2settingResult);
+    }
+
+    virtual void onWorkDone_nb(
+            std::weak_ptr<C2Component> c2component,
+            std::list<std::unique_ptr<C2Work>> c2workItems) override {
+        if (mHelper) {
+            std::list<std::unique_ptr<C2Work>> processedWork;
+            mHelper->gather(c2workItems, &processedWork);
+            if (!processedWork.empty()) {
+                Listener::onWorkDone_nb(c2component, std::move(processedWork));
+            }
+        } else {
+            Listener::onWorkDone_nb(c2component, std::move(c2workItems));
+        }
+    }
+
+    protected:
+        std::shared_ptr<MultiAccessUnitHelper> mHelper;
+};
+
 // Component::Sink
 struct Component::Sink : public IInputSink {
     std::shared_ptr<Component> mComponent;
@@ -208,13 +258,37 @@
         const sp<::android::hardware::media::bufferpool::V2_0::
         IClientManager>& clientPoolManager)
       : mComponent{component},
-        mInterface{new ComponentInterface(component->intf(),
-                                          store->getParameterCache())},
         mListener{listener},
         mStore{store},
         mBufferPoolSender{clientPoolManager} {
     // Retrieve supported parameters from store
     // TODO: We could cache this per component/interface type
+    if (MultiAccessUnitHelper::isEnabledOnPlatform()) {
+        c2_status_t err = C2_OK;
+        C2ComponentDomainSetting domain;
+        std::vector<std::unique_ptr<C2Param>> heapParams;
+        err = component->intf()->query_vb({&domain}, {}, C2_MAY_BLOCK, &heapParams);
+        if (err == C2_OK && (domain.value == C2Component::DOMAIN_AUDIO)) {
+            std::vector<std::shared_ptr<C2ParamDescriptor>> params;
+            bool isComponentSupportsLargeAudioFrame = false;
+            component->intf()->querySupportedParams_nb(&params);
+            for (const auto &paramDesc : params) {
+                if (paramDesc->name().compare(C2_PARAMKEY_OUTPUT_LARGE_FRAME) == 0) {
+                    isComponentSupportsLargeAudioFrame = true;
+                    LOG(VERBOSE) << "Underlying component supports large frame audio";
+                    break;
+                }
+            }
+            if (!isComponentSupportsLargeAudioFrame) {
+                mMultiAccessUnitIntf = std::make_shared<MultiAccessUnitInterface>(
+                        component->intf(),
+                        std::static_pointer_cast<C2ReflectorHelper>(
+                                GetCodec2PlatformComponentStore()->getParamReflector()));
+            }
+        }
+    }
+    mInterface = new ComponentInterface(
+            component->intf(), mMultiAccessUnitIntf, store->getParameterCache());
     mInit = mInterface->status();
 }
 
@@ -252,6 +326,19 @@
                     registerFrameData(mListener, work->input);
         }
     }
+    c2_status_t err = C2_OK;
+    if (mMultiAccessUnitHelper) {
+        std::list<std::list<std::unique_ptr<C2Work>>> c2worklists;
+        mMultiAccessUnitHelper->scatter(c2works, &c2worklists);
+        for (auto &c2worklist : c2worklists) {
+            err = mComponent->queue_nb(&c2worklist);
+            if (err != C2_OK) {
+                LOG(ERROR) << "Error Queuing to component.";
+                break;
+            }
+        }
+        return static_cast<Status>(err);
+    }
 
     return static_cast<Status>(mComponent->queue_nb(&c2works));
 }
@@ -261,6 +348,9 @@
     c2_status_t c2res = mComponent->flush_sm(
             C2Component::FLUSH_COMPONENT,
             &c2flushedWorks);
+    if (mMultiAccessUnitHelper) {
+        c2res = mMultiAccessUnitHelper->flush(&c2flushedWorks);
+    }
 
     // Unregister input buffers.
     for (const std::unique_ptr<C2Work>& work : c2flushedWorks) {
@@ -469,6 +559,9 @@
         std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
         mBlockPools.clear();
     }
+    if (mMultiAccessUnitHelper) {
+        mMultiAccessUnitHelper->reset();
+    }
     InputBufferManager::unregisterFrameData(mListener);
     return status;
 }
@@ -479,6 +572,9 @@
         std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
         mBlockPools.clear();
     }
+    if (mMultiAccessUnitHelper) {
+        mMultiAccessUnitHelper->reset();
+    }
     InputBufferManager::unregisterFrameData(mListener);
     return status;
 }
@@ -508,7 +604,12 @@
 }
 
 void Component::initListener(const sp<Component>& self) {
-    std::shared_ptr<C2Component::Listener> c2listener =
+    std::shared_ptr<C2Component::Listener> c2listener;
+    if (mMultiAccessUnitIntf) {
+        mMultiAccessUnitHelper = std::make_shared<MultiAccessUnitHelper>(mMultiAccessUnitIntf);
+    }
+    c2listener = mMultiAccessUnitHelper ?
+            std::make_shared<MultiAccessUnitListener>(self, mMultiAccessUnitHelper) :
             std::make_shared<Listener>(self);
     c2_status_t res = mComponent->setListener_vb(c2listener, C2_DONT_BLOCK);
     if (res != C2_OK) {
diff --git a/media/codec2/hal/hidl/1.1/utils/include/codec2/hidl/1.1/Component.h b/media/codec2/hal/hidl/1.1/utils/include/codec2/hidl/1.1/Component.h
index f16de24..8f0478f 100644
--- a/media/codec2/hal/hidl/1.1/utils/include/codec2/hidl/1.1/Component.h
+++ b/media/codec2/hal/hidl/1.1/utils/include/codec2/hidl/1.1/Component.h
@@ -29,6 +29,8 @@
 #include <hidl/Status.h>
 #include <hwbinder/IBinder.h>
 
+#include <codec2/common/MultiAccessUnitHelper.h>
+
 #include <C2Component.h>
 #include <C2Buffer.h>
 #include <C2.h>
@@ -57,6 +59,8 @@
 using ::android::hardware::IBinder;
 using ::android::sp;
 using ::android::wp;
+using ::android::MultiAccessUnitInterface;
+using ::android::MultiAccessUnitHelper;
 
 struct ComponentStore;
 
@@ -118,6 +122,8 @@
     std::shared_ptr<C2Component> mComponent;
     sp<ComponentInterface> mInterface;
     sp<IComponentListener> mListener;
+    std::shared_ptr<MultiAccessUnitInterface> mMultiAccessUnitIntf;
+    std::shared_ptr<MultiAccessUnitHelper> mMultiAccessUnitHelper;
     sp<ComponentStore> mStore;
     ::android::hardware::media::c2::V1_1::utils::DefaultBufferPoolSender
             mBufferPoolSender;
@@ -140,6 +146,8 @@
 
     struct Listener;
 
+    friend struct MultiAccessUnitListener;
+
     using HwDeathRecipient = ::android::hardware::hidl_death_recipient;
     sp<HwDeathRecipient> mDeathRecipient;
     bool mClientDied{false};
diff --git a/media/codec2/hal/hidl/1.2/utils/Android.bp b/media/codec2/hal/hidl/1.2/utils/Android.bp
index b92dc07..a339946 100644
--- a/media/codec2/hal/hidl/1.2/utils/Android.bp
+++ b/media/codec2/hal/hidl/1.2/utils/Android.bp
@@ -58,7 +58,6 @@
     ],
 }
 
-
 // DO NOT DEPEND ON THIS DIRECTLY
 // use libcodec2-hidl-defaults instead
 cc_library {
@@ -102,6 +101,7 @@
         "android.hardware.media.omx@1.0",
         "libbase",
         "libcodec2",
+        "libcodec2_hal_common",
         "libcodec2_hidl@1.0",
         "libcodec2_hidl@1.1",
         "libcodec2_hidl_plugin_stub",
@@ -197,4 +197,3 @@
     name: "libcodec2-hidl-client-defaults",
     defaults: ["libcodec2-hidl-client-defaults@1.2"],
 }
-
diff --git a/media/codec2/hal/hidl/1.2/utils/Component.cpp b/media/codec2/hal/hidl/1.2/utils/Component.cpp
index 036c900..bbdbef5 100644
--- a/media/codec2/hal/hidl/1.2/utils/Component.cpp
+++ b/media/codec2/hal/hidl/1.2/utils/Component.cpp
@@ -44,6 +44,8 @@
 namespace utils {
 
 using namespace ::android;
+using ::android::MultiAccessUnitInterface;
+using ::android::MultiAccessUnitHelper;
 
 // ComponentListener wrapper
 struct Component::Listener : public C2Component::Listener {
@@ -135,6 +137,52 @@
     wp<IComponentListener> mListener;
 };
 
+// Component listener for handle multiple access-units
+struct MultiAccessUnitListener : public Component::Listener {
+    MultiAccessUnitListener(const sp<Component> &component,
+            const std::shared_ptr<MultiAccessUnitHelper> &helper):
+        Listener(component), mHelper(helper) {
+    }
+
+    virtual void onError_nb(
+            std::weak_ptr<C2Component> c2component,
+            uint32_t errorCode) override {
+        if (mHelper) {
+            std::list<std::unique_ptr<C2Work>> worklist;
+            mHelper->error(&worklist);
+            if (!worklist.empty()) {
+                Listener::onWorkDone_nb(c2component, std::move(worklist));
+            }
+        }
+        Listener::onError_nb(c2component, errorCode);
+    }
+
+    virtual void onTripped_nb(
+            std::weak_ptr<C2Component> c2component,
+            std::vector<std::shared_ptr<C2SettingResult>> c2settingResult
+            ) override {
+        Listener::onTripped_nb(c2component,
+                c2settingResult);
+    }
+
+    virtual void onWorkDone_nb(
+            std::weak_ptr<C2Component> c2component,
+            std::list<std::unique_ptr<C2Work>> c2workItems) override {
+        if (mHelper) {
+            std::list<std::unique_ptr<C2Work>> processedWork;
+            mHelper->gather(c2workItems, &processedWork);
+            if (!processedWork.empty()) {
+                Listener::onWorkDone_nb(c2component, std::move(processedWork));
+            }
+        } else {
+            Listener::onWorkDone_nb(c2component, std::move(c2workItems));
+        }
+    }
+
+    protected:
+        std::shared_ptr<MultiAccessUnitHelper> mHelper;
+};
+
 // Component::Sink
 struct Component::Sink : public IInputSink {
     std::shared_ptr<Component> mComponent;
@@ -208,13 +256,37 @@
         const sp<::android::hardware::media::bufferpool::V2_0::
         IClientManager>& clientPoolManager)
       : mComponent{component},
-        mInterface{new ComponentInterface(component->intf(),
-                                          store->getParameterCache())},
         mListener{listener},
         mStore{store},
         mBufferPoolSender{clientPoolManager} {
     // Retrieve supported parameters from store
     // TODO: We could cache this per component/interface type
+    if (MultiAccessUnitHelper::isEnabledOnPlatform()) {
+        c2_status_t err = C2_OK;
+        C2ComponentDomainSetting domain;
+        std::vector<std::unique_ptr<C2Param>> heapParams;
+        err = component->intf()->query_vb({&domain}, {}, C2_MAY_BLOCK, &heapParams);
+        if (err == C2_OK && (domain.value == C2Component::DOMAIN_AUDIO)) {
+            std::vector<std::shared_ptr<C2ParamDescriptor>> params;
+            bool isComponentSupportsLargeAudioFrame = false;
+            component->intf()->querySupportedParams_nb(&params);
+            for (const auto &paramDesc : params) {
+                if (paramDesc->name().compare(C2_PARAMKEY_OUTPUT_LARGE_FRAME) == 0) {
+                    isComponentSupportsLargeAudioFrame = true;
+                    LOG(VERBOSE) << "Underlying component supports large frame audio";
+                    break;
+                }
+            }
+            if (!isComponentSupportsLargeAudioFrame) {
+                mMultiAccessUnitIntf = std::make_shared<MultiAccessUnitInterface>(
+                        component->intf(),
+                        std::static_pointer_cast<C2ReflectorHelper>(
+                                GetCodec2PlatformComponentStore()->getParamReflector()));
+            }
+        }
+    }
+    mInterface = new ComponentInterface(
+            component->intf(), mMultiAccessUnitIntf, store->getParameterCache());
     mInit = mInterface->status();
 }
 
@@ -252,7 +324,19 @@
                     registerFrameData(mListener, work->input);
         }
     }
-
+    c2_status_t err = C2_OK;
+    if (mMultiAccessUnitHelper) {
+        std::list<std::list<std::unique_ptr<C2Work>>> c2worklists;
+        mMultiAccessUnitHelper->scatter(c2works, &c2worklists);
+        for (auto &c2worklist : c2worklists) {
+            err = mComponent->queue_nb(&c2worklist);
+            if (err != C2_OK) {
+                LOG(ERROR) << "Error Queuing to component.";
+                break;
+            }
+        }
+        return static_cast<Status>(err);
+    }
     return static_cast<Status>(mComponent->queue_nb(&c2works));
 }
 
@@ -261,7 +345,9 @@
     c2_status_t c2res = mComponent->flush_sm(
             C2Component::FLUSH_COMPONENT,
             &c2flushedWorks);
-
+    if (mMultiAccessUnitHelper) {
+        c2res = mMultiAccessUnitHelper->flush(&c2flushedWorks);
+    }
     // Unregister input buffers.
     for (const std::unique_ptr<C2Work>& work : c2flushedWorks) {
         if (work) {
@@ -469,6 +555,9 @@
         std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
         mBlockPools.clear();
     }
+    if (mMultiAccessUnitHelper) {
+        mMultiAccessUnitHelper->reset();
+    }
     InputBufferManager::unregisterFrameData(mListener);
     return status;
 }
@@ -479,6 +568,9 @@
         std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
         mBlockPools.clear();
     }
+    if (mMultiAccessUnitHelper) {
+        mMultiAccessUnitHelper->reset();
+    }
     InputBufferManager::unregisterFrameData(mListener);
     return status;
 }
@@ -539,7 +631,12 @@
 }
 
 void Component::initListener(const sp<Component>& self) {
-    std::shared_ptr<C2Component::Listener> c2listener =
+    std::shared_ptr<C2Component::Listener> c2listener;
+    if (mMultiAccessUnitIntf) {
+        mMultiAccessUnitHelper = std::make_shared<MultiAccessUnitHelper>(mMultiAccessUnitIntf);
+    }
+    c2listener = mMultiAccessUnitHelper ?
+            std::make_shared<MultiAccessUnitListener>(self, mMultiAccessUnitHelper) :
             std::make_shared<Listener>(self);
     c2_status_t res = mComponent->setListener_vb(c2listener, C2_DONT_BLOCK);
     if (res != C2_OK) {
diff --git a/media/codec2/hal/hidl/1.2/utils/include/codec2/hidl/1.2/Component.h b/media/codec2/hal/hidl/1.2/utils/include/codec2/hidl/1.2/Component.h
index 6a73392..f2b77bc 100644
--- a/media/codec2/hal/hidl/1.2/utils/include/codec2/hidl/1.2/Component.h
+++ b/media/codec2/hal/hidl/1.2/utils/include/codec2/hidl/1.2/Component.h
@@ -29,6 +29,8 @@
 #include <hidl/Status.h>
 #include <hwbinder/IBinder.h>
 
+#include <codec2/common/MultiAccessUnitHelper.h>
+
 #include <C2Component.h>
 #include <C2Buffer.h>
 #include <C2.h>
@@ -57,6 +59,8 @@
 using ::android::hardware::IBinder;
 using ::android::sp;
 using ::android::wp;
+using ::android::MultiAccessUnitInterface;
+using ::android::MultiAccessUnitHelper;
 
 struct ComponentStore;
 
@@ -123,6 +127,8 @@
     std::shared_ptr<C2Component> mComponent;
     sp<ComponentInterface> mInterface;
     sp<IComponentListener> mListener;
+    std::shared_ptr<MultiAccessUnitInterface> mMultiAccessUnitIntf;
+    std::shared_ptr<MultiAccessUnitHelper> mMultiAccessUnitHelper;
     sp<ComponentStore> mStore;
     ::android::hardware::media::c2::V1_2::utils::DefaultBufferPoolSender
             mBufferPoolSender;
@@ -145,6 +151,8 @@
 
     struct Listener;
 
+    friend struct MultiAccessUnitListener;
+
     using HwDeathRecipient = ::android::hardware::hidl_death_recipient;
     sp<HwDeathRecipient> mDeathRecipient;
     bool mClientDied{false};
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 6e6d3f7..7b1721e 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -90,6 +90,28 @@
     return v == "true";
 }
 
+// Flags can come with individual BufferInfos
+// when used with large frame audio
+constexpr static std::initializer_list<std::pair<uint32_t, uint32_t>> flagList = {
+        {BUFFER_FLAG_CODEC_CONFIG, C2FrameData::FLAG_CODEC_CONFIG},
+        {BUFFER_FLAG_END_OF_STREAM, C2FrameData::FLAG_END_OF_STREAM},
+        {BUFFER_FLAG_DECODE_ONLY, C2FrameData::FLAG_DROP_FRAME}
+};
+
+static uint32_t convertFlags(uint32_t flags, bool toC2) {
+    return std::transform_reduce(
+            flagList.begin(), flagList.end(),
+            0u,
+            std::bit_or{},
+            [flags, toC2](const std::pair<uint32_t, uint32_t> &entry) {
+                if (toC2) {
+                    return (flags & entry.first) ? entry.second : 0;
+                } else {
+                    return (flags & entry.second) ? entry.first : 0;
+                }
+            });
+}
+
 }  // namespace
 
 CCodecBufferChannel::QueueGuard::QueueGuard(
@@ -245,7 +267,8 @@
     if (buffer->meta()->findInt32("decode-only", &tmp) && tmp) {
         flags |= C2FrameData::FLAG_DROP_FRAME;
     }
-    ALOGV("[%s] queueInputBuffer: buffer->size() = %zu", mName, buffer->size());
+    ALOGV("[%s] queueInputBuffer: buffer->size() = %zu time: %lld",
+            mName, buffer->size(), (long long)timeUs);
     std::list<std::unique_ptr<C2Work>> items;
     std::unique_ptr<C2Work> work(new C2Work);
     work->input.ordinal.timestamp = timeUs;
@@ -296,6 +319,34 @@
                 uint64_t frameIndex = work->input.ordinal.frameIndex.peeku();
                 output->rotation[frameIndex] = rotation;
             }
+            sp<RefBase> obj;
+            if (buffer->meta()->findObject("accessUnitInfo", &obj)) {
+                ALOGV("Filling C2Info from multiple access units");
+                sp<WrapperObject<std::vector<AccessUnitInfo>>> infos{
+                        (decltype(infos.get()))obj.get()};
+                std::vector<AccessUnitInfo> &accessUnitInfoVec = infos->value;
+                std::vector<C2AccessUnitInfosStruct> multipleAccessUnitInfos;
+                uint32_t outFlags = 0;
+                for (int i = 0; i < accessUnitInfoVec.size(); i++) {
+                    outFlags = 0;
+                    outFlags = convertFlags(accessUnitInfoVec[i].mFlags, true);
+                    if (eos && (outFlags & C2FrameData::FLAG_END_OF_STREAM)) {
+                        outFlags &= (~C2FrameData::FLAG_END_OF_STREAM);
+                    }
+                    multipleAccessUnitInfos.emplace_back(
+                            outFlags,
+                            accessUnitInfoVec[i].mSize,
+                            accessUnitInfoVec[i].mTimestamp);
+                    ALOGV("%d) flags: %d, size: %d, time: %llu",
+                            i, outFlags, accessUnitInfoVec[i].mSize,
+                            (long long)accessUnitInfoVec[i].mTimestamp);
+
+                }
+                const std::shared_ptr<C2AccessUnitInfos::input> c2AccessUnitInfos =
+                        C2AccessUnitInfos::input::AllocShared(
+                                multipleAccessUnitInfos.size(), 0u, multipleAccessUnitInfos);
+                c2buffer->setInfo(c2AccessUnitInfos);
+            }
             work->input.buffers.push_back(c2buffer);
             if (encryptedBlock) {
                 work->input.infoBuffers.emplace_back(C2InfoBuffer::CreateLinearBuffer(
@@ -2265,12 +2316,34 @@
         case OutputBuffers::DISCARD:
             break;
         case OutputBuffers::NOTIFY_CLIENT:
+        {
             // TRICKY: we want popped buffers reported in order, so sending
             // the callback while holding the lock here. This assumes that
             // onOutputBufferAvailable() does not block. onOutputBufferAvailable()
             // callbacks are always sent with the Output lock held.
+            if (c2Buffer) {
+                std::shared_ptr<const C2AccessUnitInfos::output> bufferMetadata =
+                        std::static_pointer_cast<const C2AccessUnitInfos::output>(
+                        c2Buffer->getInfo(C2AccessUnitInfos::output::PARAM_TYPE));
+                if (bufferMetadata && bufferMetadata->flexCount() > 0) {
+                    uint32_t flag = 0;
+                    std::vector<AccessUnitInfo> accessUnitInfos;
+                    for (int nMeta = 0; nMeta < bufferMetadata->flexCount(); nMeta++) {
+                        const C2AccessUnitInfosStruct &bufferMetadataStruct =
+                                bufferMetadata->m.values[nMeta];
+                        flag = convertFlags(bufferMetadataStruct.flags, false);
+                        accessUnitInfos.emplace_back(flag,
+                                static_cast<size_t>(bufferMetadataStruct.size),
+                                static_cast<size_t>(bufferMetadataStruct.timestamp));
+                    }
+                    sp<WrapperObject<std::vector<AccessUnitInfo>>> obj{
+                        new WrapperObject<std::vector<AccessUnitInfo>>{accessUnitInfos}};
+                    outBuffer->meta()->setObject("accessUnitInfo", obj);
+                }
+            }
             mCallback->onOutputBufferAvailable(index, outBuffer);
             break;
+        }
         case OutputBuffers::REALLOCATE:
             if (++reallocTryNum > kMaxReallocTry) {
                 output.unlock();
diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp
index 670923b..8a48777 100644
--- a/media/codec2/sfplugin/CCodecBuffers.cpp
+++ b/media/codec2/sfplugin/CCodecBuffers.cpp
@@ -18,11 +18,14 @@
 #define LOG_TAG "CCodecBuffers"
 #include <utils/Log.h>
 
+#include <numeric>
+
 #include <C2AllocatorGralloc.h>
 #include <C2PlatformSupport.h>
 
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/MediaDefs.h>
+#include <media/stagefright/CodecBase.h>
 #include <media/stagefright/MediaCodecConstants.h>
 #include <media/stagefright/SkipCutBuffer.h>
 #include <mediadrm/ICrypto.h>
@@ -147,6 +150,165 @@
     return copy;
 }
 
+// MultiAccessUnitSkipCutBuffer for buffer and bufferInfos
+
+class MultiAccessUnitSkipCutBuffer : public SkipCutBuffer {
+
+public:
+    explicit MultiAccessUnitSkipCutBuffer(
+            int32_t skip, int32_t cut, size_t num16BitChannels):
+        SkipCutBuffer(skip, cut, num16BitChannels),
+        mFrontPaddingDelay(0), mSize(0) {
+    }
+
+    virtual ~MultiAccessUnitSkipCutBuffer() {
+
+    }
+
+    void submitMultiAccessUnits(
+            const sp<MediaCodecBuffer>& buffer,
+            int32_t sampleRate, size_t num16BitChannels,
+            std::shared_ptr<const C2AccessUnitInfos::output> &infos) {
+        if (infos == nullptr) {
+            // there is nothing to do more.
+            SkipCutBuffer::submit(buffer);
+            return;
+        }
+        typedef WrapperObject<std::vector<AccessUnitInfo>> BufferInfosWrapper;
+        CHECK_EQ(mSize, SkipCutBuffer::size());
+        sp<BufferInfosWrapper> bufferInfos{new BufferInfosWrapper(decltype(bufferInfos->value)())};
+        uint32_t availableSize = buffer->size() + SkipCutBuffer::size();
+        uint32_t frontPadding = mFrontPadding;
+        int32_t lastEmptyAccessUnitIndex = -1;
+        int64_t byteInUs = 0;
+        if (sampleRate > 0 && num16BitChannels > 0) {
+            byteInUs = (1000000u / (sampleRate * num16BitChannels * 2));
+        }
+        if (frontPadding > 0) {
+            mInfos.clear();
+            mSize = 0;
+        }
+        for (int i = 0 ; i < infos->flexCount() && frontPadding > 0; i++) {
+            uint32_t flagsInPadding = 0;
+            int64_t timeInPadding = 0;
+            if (infos->m.values[i].size <= frontPadding) {
+                // we have more front padding so this buffer is not going to be used.
+                int32_t consumed = infos->m.values[i].size;
+                frontPadding -= consumed;
+                mFrontPaddingDelay += byteInUs * (consumed);
+                availableSize -= consumed;
+                flagsInPadding |= toMediaCodecFlags(infos->m.values[i].flags);
+                timeInPadding = infos->m.values[i].timestamp;
+            } else {
+                C2AccessUnitInfosStruct info = infos->m.values[i];
+                mFrontPaddingDelay +=  byteInUs * (frontPadding);
+                info.size -= frontPadding;
+                info.timestamp -= mFrontPaddingDelay;
+                availableSize -= frontPadding;
+                flagsInPadding |= toMediaCodecFlags(infos->m.values[i].flags);
+                timeInPadding = infos->m.values[i].timestamp;
+                frontPadding = 0;
+                mInfos.push_back(info);
+                mSize += info.size;
+            }
+            if (flagsInPadding != 0) {
+                bufferInfos->value.emplace_back(
+                        flagsInPadding, 0, timeInPadding);
+            }
+            lastEmptyAccessUnitIndex = i;
+        }
+        if (frontPadding <= 0) {
+            // process what's already in the buffer first
+            auto it = mInfos.begin();
+            while (it != mInfos.end() && availableSize > mBackPadding) {
+                // we have samples to send out.
+                if ((availableSize - it->size) >= mBackPadding) {
+                    // this is totally used here.
+                    int32_t consumed = it->size;
+                    bufferInfos->value.emplace_back(
+                            toMediaCodecFlags(it->flags), consumed, it->timestamp);
+                    availableSize -= consumed;
+                    mSize -= consumed;
+                    it = mInfos.erase(it);
+                } else {
+                    int32_t consumed = availableSize - mBackPadding;
+                    bufferInfos->value.emplace_back(
+                            toMediaCodecFlags(it->flags),
+                            consumed,
+                            it->timestamp);
+                    it->size -= consumed;
+                    it->timestamp += consumed * byteInUs;
+                    availableSize -= consumed;
+                    mSize -= consumed;
+                    it++;
+                }
+            }
+            // if buffer has more process all of it and keep the remaining info.
+            for (int i = (lastEmptyAccessUnitIndex + 1) ; i < infos->flexCount() ; i++) {
+                // upddate updatedInfo and mInfos
+                if (availableSize > mBackPadding) {
+                    // we have to take data from the new buffer.
+                    if (availableSize - infos->m.values[i].size >= mBackPadding) {
+                        // we are using this info
+                        int32_t consumed = infos->m.values[i].size;
+                        bufferInfos->value.emplace_back(
+                                toMediaCodecFlags(infos->m.values[i].flags),
+                                consumed,
+                                infos->m.values[i].timestamp - mFrontPaddingDelay);
+                        availableSize -= consumed;
+                    } else {
+                        // if we need to update the size
+                        C2AccessUnitInfosStruct info = infos->m.values[i];
+                        int32_t consumed = availableSize - mBackPadding;
+                        bufferInfos->value.emplace_back(
+                                toMediaCodecFlags(infos->m.values[i].flags),
+                                consumed,
+                                infos->m.values[i].timestamp - mFrontPaddingDelay);
+                        info.size -= consumed;
+                        info.timestamp = info.timestamp - mFrontPaddingDelay +
+                                consumed * byteInUs;
+                        mInfos.push_back(info);
+                        availableSize -= consumed;
+                        mSize += info.size;
+                    }
+                } else {
+                    // we have to maintain infos
+                    C2AccessUnitInfosStruct info = infos->m.values[i];
+                    info.timestamp -= mFrontPaddingDelay;
+                    mInfos.push_back(info);
+                    mSize += info.size;
+                }
+            }
+        }
+        SkipCutBuffer::submit(buffer);
+        infos = nullptr;
+        if (!bufferInfos->value.empty()) {
+            buffer->meta()->setObject("accessUnitInfo", bufferInfos);
+        }
+    }
+protected:
+    // Flags can come with individual BufferInfos
+    // when used with large frame audio
+    constexpr static std::initializer_list<std::pair<uint32_t, uint32_t>> flagList = {
+            {BUFFER_FLAG_CODEC_CONFIG, C2FrameData::FLAG_CODEC_CONFIG},
+            {BUFFER_FLAG_END_OF_STREAM, C2FrameData::FLAG_END_OF_STREAM},
+            {BUFFER_FLAG_DECODE_ONLY, C2FrameData::FLAG_DROP_FRAME}
+    };
+
+    static uint32_t toMediaCodecFlags(uint32_t flags) {
+        return std::transform_reduce(
+                flagList.begin(), flagList.end(),
+                0u,
+                std::bit_or{},
+                [flags](const std::pair<uint32_t, uint32_t> &entry) {
+                    return (flags & entry.second) ? entry.first : 0;
+                });
+    }
+    std::list<C2AccessUnitInfosStruct> mInfos;
+    int64_t mFrontPaddingDelay;
+    size_t mSize;
+};
+
 // OutputBuffers
 
 OutputBuffers::OutputBuffers(const char *componentName, const char *name)
@@ -201,6 +363,15 @@
     }
 }
 
+bool OutputBuffers::submit(const sp<MediaCodecBuffer> &buffer, int32_t sampleRate,
+            int32_t channelCount, std::shared_ptr<const C2AccessUnitInfos::output> &infos) {
+    if (mSkipCutBuffer == nullptr) {
+        return false;
+    }
+    mSkipCutBuffer->submitMultiAccessUnits(buffer, sampleRate, channelCount, infos);
+    return true;
+}
+
 void OutputBuffers::setSkipCutBuffer(int32_t skip, int32_t cut) {
     if (mSkipCutBuffer != nullptr) {
         size_t prevSize = mSkipCutBuffer->size();
@@ -208,7 +379,7 @@
             ALOGD("[%s] Replacing SkipCutBuffer holding %zu bytes", mName, prevSize);
         }
     }
-    mSkipCutBuffer = new SkipCutBuffer(skip, cut, mChannelCount);
+    mSkipCutBuffer = new MultiAccessUnitSkipCutBuffer(skip, cut, mChannelCount);
 }
 
 bool OutputBuffers::convert(
@@ -1160,7 +1331,16 @@
         ALOGD("[%s] copy buffer failed", mName);
         return WOULD_BLOCK;
     }
-    submit(c2Buffer);
+    if (buffer && buffer->hasInfo(C2AccessUnitInfos::output::PARAM_TYPE)) {
+        std::shared_ptr<const C2AccessUnitInfos::output> bufferMetadata =
+                        std::static_pointer_cast<const C2AccessUnitInfos::output>(
+                        buffer->getInfo(C2AccessUnitInfos::output::PARAM_TYPE));
+        if (submit(c2Buffer, mSampleRate, mChannelCount, bufferMetadata)) {
+            buffer->removeInfo(C2AccessUnitInfos::output::PARAM_TYPE);
+        }
+    } else {
+        submit(c2Buffer);
+    }
     handleImageData(c2Buffer);
     *clientBuffer = c2Buffer;
     ALOGV("[%s] grabbed buffer %zu", mName, *index);
diff --git a/media/codec2/sfplugin/CCodecBuffers.h b/media/codec2/sfplugin/CCodecBuffers.h
index cbef644..f0936bc 100644
--- a/media/codec2/sfplugin/CCodecBuffers.h
+++ b/media/codec2/sfplugin/CCodecBuffers.h
@@ -20,6 +20,7 @@
 
 #include <optional>
 #include <string>
+#include <vector>
 
 #include <C2Config.h>
 #include <DataConverter.h>
@@ -33,6 +34,8 @@
 struct ICrypto;
 class MemoryDealer;
 class SkipCutBuffer;
+class MultiAccessUnitSkipCutBuffer;
+struct AccessUnitInfo;
 
 constexpr size_t kLinearBufferSize = 1048576;
 // This can fit an 8K frame.
@@ -382,13 +385,17 @@
             sp<MediaCodecBuffer>* outBuffer);
 
 protected:
-    sp<SkipCutBuffer> mSkipCutBuffer;
+
+    sp<MultiAccessUnitSkipCutBuffer> mSkipCutBuffer;
 
     /**
      * Update the SkipCutBuffer object. No-op if it's never initialized.
      */
     void updateSkipCutBuffer(int32_t sampleRate, int32_t channelCount);
 
+    bool submit(const sp<MediaCodecBuffer> &buffer, int32_t sampleRate,
+            int32_t channelCount, std::shared_ptr<const C2AccessUnitInfos::output> &infos);
+
     /**
      * Submit buffer to SkipCutBuffer object, if initialized.
      */
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index 6d49fa8..c22deca 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -402,10 +402,19 @@
 
     add(ConfigMapper(KEY_MAX_INPUT_SIZE, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE, "value")
         .limitTo(D::INPUT));
+
     // remove when codecs switch to PARAMKEY
     deprecated(ConfigMapper(KEY_MAX_INPUT_SIZE, "coded.max-frame-size", "value")
                .limitTo(D::INPUT));
 
+    // large frame params
+    add(ConfigMapper(KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE,
+            C2_PARAMKEY_OUTPUT_LARGE_FRAME, "max-size")
+        .limitTo(D::AUDIO & D::OUTPUT));
+    add(ConfigMapper(KEY_BUFFER_BATCH_THRESHOLD_OUTPUT_SIZE,
+            C2_PARAMKEY_OUTPUT_LARGE_FRAME, "threshold-size")
+        .limitTo(D::AUDIO & D::OUTPUT));
+
     // Rotation
     // Note: SDK rotation is clock-wise, while C2 rotation is counter-clock-wise
     add(ConfigMapper(KEY_ROTATION, C2_PARAMKEY_VUI_ROTATION, "value")
diff --git a/media/codec2/vndk/platform/C2IgbaBuffer.cpp b/media/codec2/vndk/platform/C2IgbaBuffer.cpp
index eafdb22..3622d5e 100644
--- a/media/codec2/vndk/platform/C2IgbaBuffer.cpp
+++ b/media/codec2/vndk/platform/C2IgbaBuffer.cpp
@@ -192,28 +192,25 @@
     c2_status_t res = _fetchGraphicBlock(
             width, height, format, usage, kBlockingFetchTimeoutNs, &origId, block, &fence);
 
-    if (res == C2_BLOCKING) {
+    if (res == C2_TIMED_OUT) {
+        // SyncFence waiting timeout.
+        // Usually HAL treats C2_TIMED_OUT as an irrecoverable error.
+        // We want HAL to re-try.
         return C2_BLOCKING;
     }
-    if (res != C2_OK) {
-        return res;
-    }
-    // TODO: bundle the fence to the block. Are API changes required?
-    res = fence.wait(kSyncFenceWaitNs);
-    if (res != C2_OK) {
-        bool aidlRet = true;
-        ::ndk::ScopedAStatus status = mIgba->deallocate(origId, &aidlRet);
-        ALOGE("Waiting a sync fence failed %d aidl(%d: %d)",
-              res, status.isOk(), aidlRet);
-    }
-    return C2_OK;
+    return res;
 }
 
 c2_status_t C2IgbaBlockPool::fetchGraphicBlock(
         uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage,
         std::shared_ptr<C2GraphicBlock> *block, C2Fence *fence) {
     uint64_t origId;
-    return _fetchGraphicBlock(width, height, format, usage, 0LL, &origId, block, fence);
+    c2_status_t res = _fetchGraphicBlock(width, height, format, usage, 0LL, &origId, block, fence);
+    if (res == C2_TIMED_OUT) {
+        *fence = C2Fence();
+        return C2_BLOCKING;
+    }
+    return res;
 }
 
 c2_status_t C2IgbaBlockPool::_fetchGraphicBlock(
@@ -263,10 +260,27 @@
             }
         }
 
-        *fence = _C2FenceFactory::CreateSyncFence(allocation.fence.release());
+        C2Fence syncFence  = _C2FenceFactory::CreateSyncFence(allocation.fence.release());
         AHardwareBuffer *ahwb = allocation.buffer.release(); // This is acquired.
         CHECK(AHardwareBuffer_getId(ahwb, origId) == ::android::OK);
-        c2_status_t res = CreateGraphicBlockFromAhwb(ahwb, mAllocator, mIgba, block);
+
+        // We are waiting for SyncFence here for backward compatibility.
+        // H/W based Sync Fence could be returned to improve pipeline latency.
+        //
+        // TODO: Add a component configuration for returning sync fence
+        // from fetchGraphicBlock() as the C2Fence output param(b/322283520).
+        // In the case C2_OK along with GraphicBlock must be returned together.
+        c2_status_t res = syncFence.wait(kSyncFenceWaitNs);
+        if (res != C2_OK) {
+            AHardwareBuffer_release(ahwb);
+            bool aidlRet = true;
+            ::ndk::ScopedAStatus status = mIgba->deallocate(*origId, &aidlRet);
+            ALOGE("Waiting a sync fence failed %d aidl(%d: %d)",
+                  res, status.isOk(), aidlRet);
+            return C2_TIMED_OUT;
+        }
+
+        res = CreateGraphicBlockFromAhwb(ahwb, mAllocator, mIgba, block);
         AHardwareBuffer_release(ahwb);
         if (res != C2_OK) {
             bool aidlRet = true;
diff --git a/media/libaudioclient/TEST_MAPPING b/media/libaudioclient/TEST_MAPPING
index 234e858..68dba34 100644
--- a/media/libaudioclient/TEST_MAPPING
+++ b/media/libaudioclient/TEST_MAPPING
@@ -43,10 +43,9 @@
     }
   ],
   "postsubmit": [
-  // TODO(b/302036943): Enable once we make it pass with AIDL HAL on CF.
-  //   {
-  //      "name": "audioeffect_analysis"
-  //   },
+    {
+      "name": "audioeffect_analysis"
+    },
     {
       "name": "CtsVirtualDevicesTestCases",
       "options" : [
diff --git a/media/libaudiohal/impl/EffectConversionHelperAidl.cpp b/media/libaudiohal/impl/EffectConversionHelperAidl.cpp
index 39999a5..e1a82f8 100644
--- a/media/libaudiohal/impl/EffectConversionHelperAidl.cpp
+++ b/media/libaudiohal/impl/EffectConversionHelperAidl.cpp
@@ -187,13 +187,7 @@
         IEffect::OpenEffectReturn openReturn;
         RETURN_STATUS_IF_ERROR(
                 statusTFromBinderStatus(mEffect->open(common, std::nullopt, &openReturn)));
-        updateMqs(openReturn);
-
-        if (status_t status = updateEventFlags(); status != OK) {
-            ALOGV("%s closing at status %d", __func__, status);
-            mEffect->close();
-            return status;
-        }
+        updateMqsAndEventFlags(openReturn);
     } else if (mCommon != common) {
         ALOGI("%s at state %s, setParameter", __func__, android::internal::ToString(state).c_str());
         Parameter aidlParam = UNION_MAKE(Parameter, common, common);
@@ -204,13 +198,21 @@
     return *static_cast<int32_t*>(pReplyData) = OK;
 }
 
-void EffectConversionHelperAidl::updateMqs(const IEffect::OpenEffectReturn& ret) {
+void EffectConversionHelperAidl::updateMqsAndEventFlags(const IEffect::OpenEffectReturn& ret) {
     if (mIsProxyEffect) {
         mStatusQ = std::static_pointer_cast<EffectProxy>(mEffect)->getStatusMQ();
+    } else {
+        mStatusQ = std::make_shared<StatusMQ>(ret.statusMQ);
+    }
+    updateEventFlags();
+    updateDataMqs(ret);
+}
+
+void EffectConversionHelperAidl::updateDataMqs(const IEffect::OpenEffectReturn& ret) {
+    if (mIsProxyEffect) {
         mInputQ = std::static_pointer_cast<EffectProxy>(mEffect)->getInputMQ();
         mOutputQ = std::static_pointer_cast<EffectProxy>(mEffect)->getOutputMQ();
     } else {
-        mStatusQ = std::make_shared<StatusMQ>(ret.statusMQ);
         mInputQ = std::make_shared<DataMQ>(ret.inputDataMQ);
         mOutputQ = std::make_shared<DataMQ>(ret.outputDataMQ);
     }
@@ -407,10 +409,8 @@
         }
         // update FMQs if the effect instance already open
         if (State state; effectProxy->getState(&state).isOk() && state != State::INIT) {
-            mStatusQ = effectProxy->getStatusMQ();
-            mInputQ = effectProxy->getInputMQ();
-            mOutputQ = effectProxy->getOutputMQ();
-            updateEventFlags();
+            IEffect::OpenEffectReturn openReturn;
+            updateMqsAndEventFlags(openReturn);
         }
     }
     return *static_cast<int32_t*>(pReplyData) = OK;
@@ -512,7 +512,8 @@
     IEffect::OpenEffectReturn openReturn;
     RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mEffect->reopen(&openReturn)));
 
-    updateMqs(openReturn);
+    // status MQ won't be changed after open
+    updateDataMqs(openReturn);
     return OK;
 }
 
diff --git a/media/libaudiohal/impl/EffectConversionHelperAidl.h b/media/libaudiohal/impl/EffectConversionHelperAidl.h
index 8b9efb3..c4841c5 100644
--- a/media/libaudiohal/impl/EffectConversionHelperAidl.h
+++ b/media/libaudiohal/impl/EffectConversionHelperAidl.h
@@ -98,7 +98,6 @@
     std::shared_ptr<StatusMQ> mStatusQ = nullptr;
     std::shared_ptr<DataMQ> mInputQ = nullptr, mOutputQ = nullptr;
 
-
     struct EventFlagDeleter {
         void operator()(::android::hardware::EventFlag* flag) const {
             if (flag) {
@@ -108,8 +107,10 @@
     };
     std::shared_ptr<android::hardware::EventFlag> mEfGroup = nullptr;
     status_t updateEventFlags();
-
-    void updateMqs(const ::aidl::android::hardware::audio::effect::IEffect::OpenEffectReturn& ret);
+    void updateDataMqs(
+            const ::aidl::android::hardware::audio::effect::IEffect::OpenEffectReturn& ret);
+    void updateMqsAndEventFlags(
+            const ::aidl::android::hardware::audio::effect::IEffect::OpenEffectReturn& ret);
 
     status_t handleInit(uint32_t cmdSize, const void* pCmdData, uint32_t* replySize,
                         void* pReplyData);
diff --git a/media/libeffects/downmix/aidl/EffectDownmix.cpp b/media/libeffects/downmix/aidl/EffectDownmix.cpp
index c82c23b..46156ce 100644
--- a/media/libeffects/downmix/aidl/EffectDownmix.cpp
+++ b/media/libeffects/downmix/aidl/EffectDownmix.cpp
@@ -195,6 +195,8 @@
         const auto availableToWrite = outputMQ->availableToWrite() *
                                       mImplContext->getInputFrameSize() /
                                       mImplContext->getOutputFrameSize();
+        assert(mImplContext->getWorkBufferSize() >=
+               std::max(availableToRead(), availableToWrite));
         auto processSamples = std::min(availableToRead, availableToWrite);
         if (processSamples) {
             inputMQ->read(buffer, processSamples);
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 712b405..896e021 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -315,6 +315,7 @@
         "libaudioclient_aidl_conversion",
         "packagemanager_aidl-cpp",
         "server_configurable_flags",
+        "aconfig_mediacodec_flags_c_lib",
     ],
 
     static_libs: [
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index a0a2891..bc1a97c 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -172,6 +172,7 @@
     const char *getTrackType() const;
     void resetInternal();
     int64_t trackMetaDataSize();
+    bool isTimestampValid(int64_t timeUs);
 
 private:
     // A helper class to handle faster write box with table entries
@@ -2430,6 +2431,42 @@
     return OK;
 }
 
+bool MPEG4Writer::isSampleMetadataValid(size_t trackIndex, int64_t timeUs) {
+    // Track Index starts from zero, so it should be at least 1 less than size.
+    if (trackIndex >= mTracks.size()) {
+        ALOGE("Incorrect trackIndex %zu, mTracks->size() %zu", trackIndex, mTracks.size());
+        return false;
+    }
+
+    List<Track *>::iterator it = mTracks.begin();
+
+    // (*it) is already pointing to trackIndex 0.
+    for (int i = 1; i <= trackIndex; i++) {
+        it++;
+    }
+
+    return (*it)->isTimestampValid(timeUs);
+}
+
+bool MPEG4Writer::Track::isTimestampValid(int64_t timeUs) {
+    // No timescale if HEIF
+    if (mIsHeif) {
+        return true;
+    }
+
+    // Ensure that the timeUs value does not have extremely low or high values
+    // that would cause an underflow or overflow, like in the calculation -
+    // mdhdDuration = (trakDurationUs * mTimeScale + 5E5) / 1E6
+    if (abs(timeUs) >= (INT64_MAX - 5E5) / mTimeScale) {
+        return false;
+    }
+    // Limit check for calculations in ctts box
+    if (abs(timeUs) + kMaxCttsOffsetTimeUs >= INT64_MAX / mTimeScale) {
+        return false;
+    }
+    return true;
+}
+
 bool MPEG4Writer::Track::isExifData(
         MediaBufferBase *buffer, uint32_t *tiffHdrOffset) const {
     if (!mIsHeif) {
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 6aac0e5..006eb90 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -273,6 +273,38 @@
 // XXX suppress until we get our representation right
 static bool kEmitHistogram = false;
 
+typedef WrapperObject<std::vector<AccessUnitInfo>> BufferInfosWrapper;
+
+// Multi access unit helpers
+static status_t generateFlagsFromAccessUnitInfo(
+        sp<AMessage> &msg, const sp<BufferInfosWrapper> &bufferInfos) {
+    msg->setInt64("timeUs", bufferInfos->value[0].mTimestamp);
+    msg->setInt32("flags", bufferInfos->value[0].mFlags);
+    // will prevent any access-unit info copy.
+    if (bufferInfos->value.size() > 1) {
+        uint32_t bufferFlags = 0;
+        uint32_t flagsInAllAU = BUFFER_FLAG_DECODE_ONLY | BUFFER_FLAG_CODEC_CONFIG;
+        uint32_t andFlags = flagsInAllAU;
+        int infoIdx = 0;
+        bool foundEndOfStream = false;
+        for ( ; infoIdx < bufferInfos->value.size() && !foundEndOfStream; ++infoIdx) {
+            bufferFlags |= bufferInfos->value[infoIdx].mFlags;
+            andFlags &= bufferInfos->value[infoIdx].mFlags;
+            if (bufferFlags & BUFFER_FLAG_END_OF_STREAM) {
+                foundEndOfStream = true;
+            }
+        }
+        bufferFlags = bufferFlags & (andFlags | (~flagsInAllAU));
+        if (infoIdx != bufferInfos->value.size()) {
+            ALOGE("Error: incorrect access-units");
+            return -EINVAL;
+        }
+        msg->setInt32("flags", bufferFlags);
+        msg->setObject("accessUnitInfo", bufferInfos);
+    }
+    return OK;
+}
+
 static int64_t getId(IResourceManagerClient const * client) {
     return (int64_t) client;
 }
@@ -3176,7 +3208,49 @@
     msg->setInt64("timeUs", presentationTimeUs);
     msg->setInt32("flags", flags);
     msg->setPointer("errorDetailMsg", errorDetailMsg);
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
 
+status_t MediaCodec::queueInputBuffers(
+        size_t index,
+        size_t offset,
+        size_t size,
+        const sp<BufferInfosWrapper> &infos,
+        AString *errorDetailMsg) {
+    sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
+    uint32_t bufferFlags = 0;
+    uint32_t flagsinAllAU = BUFFER_FLAG_DECODE_ONLY | BUFFER_FLAG_CODECCONFIG;
+    uint32_t andFlags = flagsinAllAU;
+    if (infos == nullptr || infos->value.empty()) {
+        ALOGE("ERROR: Large Audio frame with no BufferInfo");
+        return BAD_VALUE;
+    }
+    int infoIdx = 0;
+    std::vector<AccessUnitInfo> &accessUnitInfo = infos->value;
+    int64_t minTimeUs = accessUnitInfo.front().mTimestamp;
+    bool foundEndOfStream = false;
+    for ( ; infoIdx < accessUnitInfo.size() && !foundEndOfStream; ++infoIdx) {
+        bufferFlags |= accessUnitInfo[infoIdx].mFlags;
+        andFlags &= accessUnitInfo[infoIdx].mFlags;
+        if (bufferFlags & BUFFER_FLAG_END_OF_STREAM) {
+            foundEndOfStream = true;
+        }
+    }
+    bufferFlags = bufferFlags & (andFlags | (~flagsinAllAU));
+    if (infoIdx != accessUnitInfo.size()) {
+        ALOGE("queueInputBuffers has incorrect access-units");
+        return -EINVAL;
+    }
+    msg->setSize("index", index);
+    msg->setSize("offset", offset);
+    msg->setSize("size", size);
+    msg->setInt64("timeUs", minTimeUs);
+    // Make this represent flags for the entire buffer
+    // decodeOnly Flag is set only when all buffers are decodeOnly
+    msg->setInt32("flags", bufferFlags);
+    msg->setObject("accessUnitInfo", infos);
+    msg->setPointer("errorDetailMsg", errorDetailMsg);
     sp<AMessage> response;
     return PostAndAwaitResponse(msg, &response);
 }
@@ -3220,28 +3294,30 @@
 status_t MediaCodec::queueBuffer(
         size_t index,
         const std::shared_ptr<C2Buffer> &buffer,
-        int64_t presentationTimeUs,
-        uint32_t flags,
+        const sp<BufferInfosWrapper> &bufferInfos,
         const sp<AMessage> &tunings,
         AString *errorDetailMsg) {
     if (errorDetailMsg != NULL) {
         errorDetailMsg->clear();
     }
-
+    if (bufferInfos == nullptr || bufferInfos->value.empty()) {
+        return BAD_VALUE;
+    }
+    status_t err = OK;
     sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
     msg->setSize("index", index);
     sp<WrapperObject<std::shared_ptr<C2Buffer>>> obj{
         new WrapperObject<std::shared_ptr<C2Buffer>>{buffer}};
     msg->setObject("c2buffer", obj);
-    msg->setInt64("timeUs", presentationTimeUs);
-    msg->setInt32("flags", flags);
+    if (OK != (err = generateFlagsFromAccessUnitInfo(msg, bufferInfos))) {
+        return err;
+    }
     if (tunings && tunings->countEntries() > 0) {
         msg->setMessage("tunings", tunings);
     }
     msg->setPointer("errorDetailMsg", errorDetailMsg);
-
     sp<AMessage> response;
-    status_t err = PostAndAwaitResponse(msg, &response);
+    err = PostAndAwaitResponse(msg, &response);
 
     return err;
 }
@@ -3256,14 +3332,16 @@
         const uint8_t iv[16],
         CryptoPlugin::Mode mode,
         const CryptoPlugin::Pattern &pattern,
-        int64_t presentationTimeUs,
-        uint32_t flags,
+        const sp<BufferInfosWrapper> &bufferInfos,
         const sp<AMessage> &tunings,
         AString *errorDetailMsg) {
     if (errorDetailMsg != NULL) {
         errorDetailMsg->clear();
     }
-
+    if (bufferInfos == nullptr || bufferInfos->value.empty()) {
+        return BAD_VALUE;
+    }
+    status_t err = OK;
     sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
     msg->setSize("index", index);
     sp<WrapperObject<sp<hardware::HidlMemory>>> memory{
@@ -3277,15 +3355,16 @@
     msg->setInt32("mode", mode);
     msg->setInt32("encryptBlocks", pattern.mEncryptBlocks);
     msg->setInt32("skipBlocks", pattern.mSkipBlocks);
-    msg->setInt64("timeUs", presentationTimeUs);
-    msg->setInt32("flags", flags);
+    if (OK != (err = generateFlagsFromAccessUnitInfo(msg, bufferInfos))) {
+        return err;
+    }
     if (tunings && tunings->countEntries() > 0) {
         msg->setMessage("tunings", tunings);
     }
     msg->setPointer("errorDetailMsg", errorDetailMsg);
 
     sp<AMessage> response;
-    status_t err = PostAndAwaitResponse(msg, &response);
+    err = PostAndAwaitResponse(msg, &response);
 
     return err;
 }
@@ -4760,6 +4839,33 @@
                     }
                 }
             }
+            int32_t largeFrameParam;
+            if (format->findInt32(KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE, &largeFrameParam) ||
+                    format->findInt32(KEY_BUFFER_BATCH_THRESHOLD_OUTPUT_SIZE,
+                    &largeFrameParam)) {
+                if(mComponentName.startsWith("OMX")) {
+                    mErrorLog.log(LOG_TAG,
+                            "Large Frame params are not supported on OMX codecs."
+                            "Currently only supported on C2 audio codec.");
+                    PostReplyWithError(replyID, INVALID_OPERATION);
+                    break;
+                }
+                AString mime;
+                CHECK(format->findString("mime", &mime));
+                if (!mime.startsWith("audio")) {
+                    mErrorLog.log(LOG_TAG,
+                            "Large Frame params only works with audio codec");
+                    PostReplyWithError(replyID, INVALID_OPERATION);
+                    break;
+                }
+                if (!(mFlags & kFlagIsAsync)) {
+                        mErrorLog.log(LOG_TAG, "Large Frame audio" \
+                                "config works only with async mode");
+                    PostReplyWithError(replyID, INVALID_OPERATION);
+                    break;
+                }
+            }
+
             mReplyID = replyID;
             setState(CONFIGURING);
 
@@ -5908,10 +6014,10 @@
 
 status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) {
     size_t index;
-    size_t offset;
-    size_t size;
-    int64_t timeUs;
-    uint32_t flags;
+    size_t offset = 0;
+    size_t size = 0;
+    int64_t timeUs = 0;
+    uint32_t flags = 0;
     CHECK(msg->findSize("index", &index));
     CHECK(msg->findInt64("timeUs", &timeUs));
     CHECK(msg->findInt32("flags", (int32_t *)&flags));
@@ -5993,9 +6099,13 @@
                 "client does not own the buffer #%zu", index));
         return -EACCES;
     }
-    auto setInputBufferParams = [this, &buffer]
+    auto setInputBufferParams = [this, &msg, &buffer]
         (int64_t timeUs, uint32_t flags = 0) -> status_t {
         status_t err = OK;
+        sp<RefBase> obj;
+        if (msg->findObject("accessUnitInfo", &obj)) {
+                buffer->meta()->setObject("accessUnitInfo", obj);
+        }
         buffer->meta()->setInt64("timeUs", timeUs);
         if (flags & BUFFER_FLAG_EOS) {
             buffer->meta()->setInt32("eos", true);
@@ -6505,10 +6615,11 @@
         if (discardDecodeOnlyOutputBuffer(index)) {
             continue;
         }
+        sp<AMessage> msg = mCallback->dup();
         const sp<MediaCodecBuffer> &buffer =
             mPortBuffers[kPortIndexOutput][index].mData;
-        sp<AMessage> msg = mCallback->dup();
-        msg->setInt32("callbackID", CB_OUTPUT_AVAILABLE);
+        int32_t outputCallbackID = CB_OUTPUT_AVAILABLE;
+        sp<RefBase> accessUnitInfoObj;
         msg->setInt32("index", index);
         msg->setSize("offset", buffer->offset());
         msg->setSize("size", buffer->size());
@@ -6522,6 +6633,15 @@
         CHECK(buffer->meta()->findInt32("flags", &flags));
 
         msg->setInt32("flags", flags);
+        buffer->meta()->findObject("accessUnitInfo", &accessUnitInfoObj);
+        if (accessUnitInfoObj) {
+            outputCallbackID = CB_LARGE_FRAME_OUTPUT_AVAILABLE;
+            msg->setObject("accessUnitInfo", accessUnitInfoObj);
+             sp<BufferInfosWrapper> auInfo(
+                    (decltype(auInfo.get()))accessUnitInfoObj.get());
+             auInfo->value.back().mFlags |= flags & BUFFER_FLAG_END_OF_STREAM;
+        }
+        msg->setInt32("callbackID", outputCallbackID);
 
         statsBufferReceived(timeUs, buffer);
 
diff --git a/media/libstagefright/TEST_MAPPING b/media/libstagefright/TEST_MAPPING
index 22885c9..5dd8423 100644
--- a/media/libstagefright/TEST_MAPPING
+++ b/media/libstagefright/TEST_MAPPING
@@ -1,8 +1,5 @@
 {
-  // tests which require dynamic content
-  // invoke with: atest -- --enable-module-dynamic-download=true
-  // TODO(b/148094059): unit tests not allowed to download content
-  "dynamic-presubmit": [
+  "postsubmit": [
     // writerTest fails about 5 out of 66
     // { "name": "writerTest" },
 
diff --git a/media/libstagefright/include/media/stagefright/CodecBase.h b/media/libstagefright/include/media/stagefright/CodecBase.h
index 0927653..8741daa 100644
--- a/media/libstagefright/include/media/stagefright/CodecBase.h
+++ b/media/libstagefright/include/media/stagefright/CodecBase.h
@@ -61,6 +61,16 @@
 
 using hardware::cas::native::V1_0::IDescrambler;
 
+struct AccessUnitInfo {
+    uint32_t mFlags;
+    uint32_t mSize;
+    int64_t mTimestamp;
+    AccessUnitInfo(uint32_t flags, uint32_t size, int64_t ptsUs)
+            :mFlags(flags), mSize(size), mTimestamp(ptsUs) {
+    }
+    ~AccessUnitInfo() {}
+};
+
 struct CodecParameterDescriptor {
     std::string name;
     AMessage::Type type;
diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
index 054a4b8..1ff8acf 100644
--- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h
+++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
@@ -77,6 +77,9 @@
     virtual void setStartTimeOffsetMs(int ms) { mStartTimeOffsetMs = ms; }
     virtual int32_t getStartTimeOffsetMs() const { return mStartTimeOffsetMs; }
     virtual status_t setNextFd(int fd);
+    // Returns true if the timestamp is valid which is compatible with the Mpeg4.
+    // Note that this overloads that method in the base class.
+    bool isSampleMetadataValid(size_t trackIndex, int64_t timeUs) override;
 
 protected:
     virtual ~MPEG4Writer();
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 2f94e5e..6622f4f 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -28,6 +28,7 @@
 #include <media/MediaMetrics.h>
 #include <media/MediaProfiles.h>
 #include <media/stagefright/foundation/AHandler.h>
+#include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/CodecErrorLog.h>
 #include <media/stagefright/FrameRenderTracker.h>
 #include <media/stagefright/MediaHistogram.h>
@@ -56,6 +57,7 @@
 struct AString;
 struct BatteryChecker;
 class BufferChannelBase;
+struct AccessUnitInfo;
 struct CodecBase;
 struct CodecParameterDescriptor;
 class IBatteryStats;
@@ -78,6 +80,8 @@
 using aidl::android::media::MediaResourceParcel;
 using aidl::android::media::ClientConfigParcel;
 
+typedef WrapperObject<std::vector<AccessUnitInfo>> BufferInfosWrapper;
+
 struct MediaCodec : public AHandler {
     enum Domain {
         DOMAIN_UNKNOWN = 0,
@@ -115,6 +119,7 @@
         CB_OUTPUT_FORMAT_CHANGED = 4,
         CB_RESOURCE_RECLAIMED = 5,
         CB_CRYPTO_ERROR = 6,
+        CB_LARGE_FRAME_OUTPUT_AVAILABLE = 7,
     };
 
     static const pid_t kNoPid = -1;
@@ -185,6 +190,13 @@
             uint32_t flags,
             AString *errorDetailMsg = NULL);
 
+    status_t queueInputBuffers(
+            size_t index,
+            size_t offset,
+            size_t size,
+            const sp<BufferInfosWrapper> &accessUnitInfo,
+            AString *errorDetailMsg = NULL);
+
     status_t queueSecureInputBuffer(
             size_t index,
             size_t offset,
@@ -201,8 +213,7 @@
     status_t queueBuffer(
             size_t index,
             const std::shared_ptr<C2Buffer> &buffer,
-            int64_t presentationTimeUs,
-            uint32_t flags,
+            const sp<BufferInfosWrapper> &bufferInfos,
             const sp<AMessage> &tunings,
             AString *errorDetailMsg = NULL);
 
@@ -216,8 +227,7 @@
             const uint8_t iv[16],
             CryptoPlugin::Mode mode,
             const CryptoPlugin::Pattern &pattern,
-            int64_t presentationTimeUs,
-            uint32_t flags,
+            const sp<BufferInfosWrapper> &bufferInfos,
             const sp<AMessage> &tunings,
             AString *errorDetailMsg = NULL);
 
diff --git a/media/libstagefright/include/media/stagefright/SkipCutBuffer.h b/media/libstagefright/include/media/stagefright/SkipCutBuffer.h
index 0fb5690..66f0bb1 100644
--- a/media/libstagefright/include/media/stagefright/SkipCutBuffer.h
+++ b/media/libstagefright/include/media/stagefright/SkipCutBuffer.h
@@ -59,6 +59,12 @@
     int32_t mReadHead;
     int32_t mCapacity;
     char* mCutBuffer;
+
+    /*
+     * Added to use Access unit skip cut in Codec2 framework
+     */
+    friend class MultiAccessUnitSkipCutBuffer;
+
     DISALLOW_EVIL_CONSTRUCTORS(SkipCutBuffer);
 };
 
diff --git a/media/libstagefright/timedtext/TEST_MAPPING b/media/libstagefright/timedtext/TEST_MAPPING
index 35a5b11..f011d04 100644
--- a/media/libstagefright/timedtext/TEST_MAPPING
+++ b/media/libstagefright/timedtext/TEST_MAPPING
@@ -1,9 +1,5 @@
-// mappings for frameworks/av/media/libstagefright/timedtext
 {
-  // tests which require dynamic content
-  // invoke with: atest -- --enable-module-dynamic-download=true
-  // TODO(b/148094059): unit tests not allowed to download content
-  "dynamic-presubmit": [
+  "postsubmit": [
     { "name": "TimedTextUnitTest" }
   ]
 }
diff --git a/media/module/codecs/amrnb/TEST_MAPPING b/media/module/codecs/amrnb/TEST_MAPPING
index 343d08a..306921f 100644
--- a/media/module/codecs/amrnb/TEST_MAPPING
+++ b/media/module/codecs/amrnb/TEST_MAPPING
@@ -1,9 +1,5 @@
-// mappings for frameworks/av/media/libstagefright/codecs/amrnb
 {
-  // tests which require dynamic content
-  // invoke with: atest -- --enable-module-dynamic-download=true
-  // TODO(b/148094059): unit tests not allowed to download content
-  "dynamic-presubmit": [
+  "postsubmit": [
     { "name": "AmrnbDecoderTest"},
     { "name": "AmrnbEncoderTest"}
   ]
diff --git a/media/module/codecs/amrwb/TEST_MAPPING b/media/module/codecs/amrwb/TEST_MAPPING
new file mode 100644
index 0000000..3f05c90
--- /dev/null
+++ b/media/module/codecs/amrwb/TEST_MAPPING
@@ -0,0 +1,6 @@
+{
+  "postsubmit": [
+    { "name": "AmrwbDecoderTest"},
+    { "name": "AmrwbEncoderTest"}
+  ]
+}
diff --git a/media/module/codecs/amrwb/dec/TEST_MAPPING b/media/module/codecs/amrwb/dec/TEST_MAPPING
deleted file mode 100644
index 0278d26..0000000
--- a/media/module/codecs/amrwb/dec/TEST_MAPPING
+++ /dev/null
@@ -1,10 +0,0 @@
-// mappings for frameworks/av/media/libstagefright/codecs/amrwb
-{
-  // tests which require dynamic content
-  // invoke with: atest -- --enable-module-dynamic-download=true
-  // TODO(b/148094059): unit tests not allowed to download content
-  "dynamic-presubmit": [
-    { "name": "AmrwbDecoderTest"}
-
-  ]
-}
diff --git a/media/module/codecs/amrwb/enc/TEST_MAPPING b/media/module/codecs/amrwb/enc/TEST_MAPPING
deleted file mode 100644
index 045e8b3..0000000
--- a/media/module/codecs/amrwb/enc/TEST_MAPPING
+++ /dev/null
@@ -1,10 +0,0 @@
-// mappings for frameworks/av/media/libstagefright/codecs/amrwbenc
-{
-  // tests which require dynamic content
-  // invoke with: atest -- --enable-module-dynamic-download=true
-  // TODO(b/148094059): unit tests not allowed to download content
-  "dynamic-presubmit": [
-    { "name": "AmrwbEncoderTest"}
-
-  ]
-}
diff --git a/media/module/codecs/flac/TEST_MAPPING b/media/module/codecs/flac/TEST_MAPPING
new file mode 100644
index 0000000..725ea90
--- /dev/null
+++ b/media/module/codecs/flac/TEST_MAPPING
@@ -0,0 +1,5 @@
+{
+  "postsubmit": [
+    { "name": "FlacDecoderTest"}
+  ]
+}
diff --git a/media/module/codecs/flac/dec/test/Android.bp b/media/module/codecs/flac/dec/test/Android.bp
index a4c2735..8004c4a 100644
--- a/media/module/codecs/flac/dec/test/Android.bp
+++ b/media/module/codecs/flac/dec/test/Android.bp
@@ -28,6 +28,7 @@
 cc_test {
     name: "FlacDecoderTest",
     gtest: true,
+    test_suites: ["device-tests"],
 
     srcs: [
         "FlacDecoderTest.cpp",
diff --git a/media/module/codecs/m4v_h263/TEST_MAPPING b/media/module/codecs/m4v_h263/TEST_MAPPING
index ba3ff1c..8599fa5 100644
--- a/media/module/codecs/m4v_h263/TEST_MAPPING
+++ b/media/module/codecs/m4v_h263/TEST_MAPPING
@@ -1,18 +1,6 @@
-// mappings for frameworks/av/media/libstagefright/codecs/m4v_h263
 {
-  // tests which require dynamic content
-  // invoke with: atest -- --enable-module-dynamic-download=true
-  // TODO(b/148094059): unit tests not allowed to download content
-  "dynamic-presubmit": [
-
-    // the decoder reports something bad about an unexpected newline in the *config file
-    // and the config file looks like the AndroidTest.xml file that we put in there.
-    // I don't get this from the Encoder -- and I don't see any substantive difference
-    // between decode and encode AndroidTest.xml files -- except that encode does NOT
-    // finish with a newline.
-    // strange.
+  "postsubmit": [
     { "name": "Mpeg4H263DecoderTest"},
     { "name": "Mpeg4H263EncoderTest"}
-
   ]
 }
diff --git a/media/module/codecs/mp3dec/TEST_MAPPING b/media/module/codecs/mp3dec/TEST_MAPPING
index 4ef4317..5faece6 100644
--- a/media/module/codecs/mp3dec/TEST_MAPPING
+++ b/media/module/codecs/mp3dec/TEST_MAPPING
@@ -1,9 +1,5 @@
-// mappings for frameworks/av/media/libstagefright/codecs/mp3dec
 {
-  // tests which require dynamic content
-  // invoke with: atest -- --enable-module-dynamic-download=true
-  // TODO(b/148094059): unit tests not allowed to download content
-  "dynamic-presubmit": [
+  "postsubmit": [
     { "name": "Mp3DecoderTest"}
   ]
 }
diff --git a/media/module/esds/TEST_MAPPING b/media/module/esds/TEST_MAPPING
index 9368b6d..0337743 100644
--- a/media/module/esds/TEST_MAPPING
+++ b/media/module/esds/TEST_MAPPING
@@ -1,9 +1,5 @@
-// mappings for frameworks/av/media/module/esds
 {
-  // tests which require dynamic content
-  // invoke with: atest -- --enable-module-dynamic-download=true
-  // TODO(b/148094059): unit tests not allowed to download content
-  "dynamic-presubmit": [
+  "postsubmit": [
     { "name": "ESDSTest" }
   ]
 }
diff --git a/media/module/esds/tests/Android.bp b/media/module/esds/tests/Android.bp
index aea611e..427b275 100644
--- a/media/module/esds/tests/Android.bp
+++ b/media/module/esds/tests/Android.bp
@@ -25,6 +25,7 @@
 cc_test {
     name: "ESDSTest",
     gtest: true,
+    test_suites: ["device-tests"],
 
     srcs: [
         "ESDSTest.cpp",
diff --git a/media/module/foundation/TEST_MAPPING b/media/module/foundation/TEST_MAPPING
index a70c352..ea4e4fd 100644
--- a/media/module/foundation/TEST_MAPPING
+++ b/media/module/foundation/TEST_MAPPING
@@ -1,9 +1,6 @@
-// mappings for frameworks/av/media/libstagefright/foundation
 {
-  // tests which require dynamic content
-  // invoke with: atest -- --enable-module-dynamic-download=true
-  // TODO(b/148094059): unit tests not allowed to download content
-  "dynamic-presubmit": [
+  "postsubmit": [
+    { "name": "AVCUtilsUnitTest" },
     { "name": "OpusHeaderTest" }
   ],
 
diff --git a/media/module/foundation/include/media/stagefright/foundation/AMessage.h b/media/module/foundation/include/media/stagefright/foundation/AMessage.h
index 7594565..b301c53 100644
--- a/media/module/foundation/include/media/stagefright/foundation/AMessage.h
+++ b/media/module/foundation/include/media/stagefright/foundation/AMessage.h
@@ -356,6 +356,16 @@
     DISALLOW_EVIL_CONSTRUCTORS(AMessage);
 };
 
+/*
+ * Helper struct for wrapping any object with RefBase.
+ */
+template <typename T>
+struct WrapperObject : public RefBase {
+    WrapperObject(const T& v) : value(v) {}
+    WrapperObject(T&& v) : value(std::move(v)) {}
+    T value;
+};
+
 }  // namespace android
 
 #endif  // A_MESSAGE_H_
diff --git a/media/module/foundation/tests/AVCUtils/Android.bp b/media/module/foundation/tests/AVCUtils/Android.bp
index ee7db21..c306c73 100644
--- a/media/module/foundation/tests/AVCUtils/Android.bp
+++ b/media/module/foundation/tests/AVCUtils/Android.bp
@@ -28,6 +28,7 @@
 cc_test {
     name: "AVCUtilsUnitTest",
     gtest: true,
+    test_suites: ["device-tests"],
 
     srcs: [
         "AVCUtilsUnitTest.cpp",
diff --git a/media/module/foundation/tests/AVCUtils/AndroidTest.xml b/media/module/foundation/tests/AVCUtils/AndroidTest.xml
index e30bfbf..315373f 100644
--- a/media/module/foundation/tests/AVCUtils/AndroidTest.xml
+++ b/media/module/foundation/tests/AVCUtils/AndroidTest.xml
@@ -16,7 +16,7 @@
 <configuration description="Test module config for AVC Utils unit tests">
     <option name="test-suite-tag" value="AVCUtilsUnitTest" />
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
-        <option name="cleanup" value="false" />
+        <option name="cleanup" value="true" />
         <option name="push" value="AVCUtilsUnitTest->/data/local/tmp/AVCUtilsUnitTest" />
     </target_preparer>
 
diff --git a/media/module/id3/TEST_MAPPING b/media/module/id3/TEST_MAPPING
index 6106908..497d984 100644
--- a/media/module/id3/TEST_MAPPING
+++ b/media/module/id3/TEST_MAPPING
@@ -1,9 +1,5 @@
-// frameworks/av/media/libstagefright/id3
 {
-  // tests which require dynamic content
-  // invoke with: atest -- --enable-module-dynamic-download=true
-  // TODO(b/148094059): unit tests not allowed to download content
-  "dynamic-presubmit": [
+  "postsubmit": [
     { "name": "ID3Test" }
   ],
 
diff --git a/media/module/mpeg2ts/TEST_MAPPING b/media/module/mpeg2ts/TEST_MAPPING
index 9f4bbdf..536450f 100644
--- a/media/module/mpeg2ts/TEST_MAPPING
+++ b/media/module/mpeg2ts/TEST_MAPPING
@@ -1,9 +1,5 @@
-// frameworks/av/media/libstagefright/mpeg2ts
 {
-  // tests which require dynamic content
-  // invoke with: atest -- --enable-module-dynamic-download=true
-  // TODO(b/148094059): unit tests not allowed to download content
-  "dynamic-presubmit": [
+  "postsubmit": [
     { "name": "Mpeg2tsUnitTest" }
   ]
 }
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 2fd908f..0057c9b 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -3265,6 +3265,7 @@
     // requested device or one of the devices selected by the engine for this stream
     // - For default requested device (AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME), apply volume only if
     // no specific device volume value exists for currently selected device.
+    // - Only apply the volume if the requested device is the desired device for volume control.
     for (size_t i = 0; i < mOutputs.size(); i++) {
         sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
         DeviceTypeSet curDevices = desc->devices().types();
@@ -3284,7 +3285,8 @@
         if (device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) {
             curSrcDevices.insert(device);
             applyVolume = (curSrcDevices.find(
-                    Volume::getDeviceForVolume(curDevices)) != curSrcDevices.end());
+                    Volume::getDeviceForVolume(curDevices)) != curSrcDevices.end())
+                    && Volume::getDeviceForVolume(curSrcDevices) == device;
         } else {
             applyVolume = !curves.hasVolumeIndexForDevice(curSrcDevice);
         }