Merge "camera: Clarify hot pixel map coordinate system when sensor pixel mode is MAXIMUM_RESOLUTION" into main
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 34ff367..e9b757b 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -30,10 +30,8 @@
 
 # Only turn on clang-format check for the following subfolders.
 clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
-               camera/
                media/libaudioclient/tests/
                media/libaudiohal/tests/
                media/libmediatranscoding/
                services/camera/virtualcamera/
                services/mediatranscoding/
-               services/camera/libcameraservice/
diff --git a/cmds/stagefright/AudioPlayer.cpp b/cmds/stagefright/AudioPlayer.cpp
index 6cddf47..54885ef 100644
--- a/cmds/stagefright/AudioPlayer.cpp
+++ b/cmds/stagefright/AudioPlayer.cpp
@@ -101,6 +101,10 @@
 
     CHECK(mFirstBuffer == NULL);
 
+    if (!mAudioPlayerWrapper) {
+        mAudioPlayerWrapper = sp<MediaPlayerBase::WeakWrapper<AudioPlayer>>::make(this);
+    }
+
     MediaSource::ReadOptions options;
     if (mSeeking) {
         options.setSeekTo(mSeekTimeUs);
@@ -203,7 +207,7 @@
                 mSampleRate, numChannels, channelMask, audioFormat,
                 DEFAULT_AUDIOSINK_BUFFERCOUNT,
                 &AudioPlayer::AudioSinkCallback,
-                this,
+                mAudioPlayerWrapper,
                 (audio_output_flags_t)flags,
                 useOffload() ? &offloadInfo : NULL);
 
@@ -430,10 +434,11 @@
 
 // static
 size_t AudioPlayer::AudioSinkCallback(
-        MediaPlayerBase::AudioSink * /* audioSink */,
-        void *buffer, size_t size, void *cookie,
+        const sp<MediaPlayerBase::AudioSink>& /* audioSink */,
+        void *buffer, size_t size, const wp<RefBase>& cookie,
         MediaPlayerBase::AudioSink::cb_event_t event) {
-    AudioPlayer *me = (AudioPlayer *)cookie;
+    const auto me = MediaPlayerBase::WeakWrapper<AudioPlayer>::promoteFromRefBase(cookie);
+    if (!me) return 0;
 
     switch(event) {
     case MediaPlayerBase::AudioSink::CB_EVENT_FILL_BUFFER:
diff --git a/cmds/stagefright/AudioPlayer.h b/cmds/stagefright/AudioPlayer.h
index 608f54b..877ac13 100644
--- a/cmds/stagefright/AudioPlayer.h
+++ b/cmds/stagefright/AudioPlayer.h
@@ -29,7 +29,7 @@
 
 struct AwesomePlayer;
 
-class AudioPlayer : AudioTrack::IAudioTrackCallback {
+class AudioPlayer : public AudioTrack::IAudioTrackCallback {
 public:
     enum {
         REACHED_EOS,
@@ -97,14 +97,15 @@
     MediaBufferBase *mFirstBuffer;
 
     sp<MediaPlayerBase::AudioSink> mAudioSink;
+    sp<MediaPlayerBase::WeakWrapper<AudioPlayer>> mAudioPlayerWrapper;
 
     bool mPlaying;
     int64_t mStartPosUs;
     const uint32_t mCreateFlags;
 
     static size_t AudioSinkCallback(
-            MediaPlayerBase::AudioSink *audioSink,
-            void *data, size_t size, void *me,
+            const sp<MediaPlayerBase::AudioSink>& audioSink,
+            void *data, size_t size, const wp<RefBase>& me,
             MediaPlayerBase::AudioSink::cb_event_t event);
 
     size_t fillBuffer(void *data, size_t size);
diff --git a/media/aconfig/codec_fwk.aconfig b/media/aconfig/codec_fwk.aconfig
index ed1522b..84c0f48 100644
--- a/media/aconfig/codec_fwk.aconfig
+++ b/media/aconfig/codec_fwk.aconfig
@@ -103,6 +103,14 @@
 }
 
 flag {
+  name: "p210_format_support"
+  is_exported: true
+  namespace: "codec_fwk"
+  description: "Feature flag for Android support for P210 YCbCr format"
+  bug: "368395888"
+}
+
+flag {
   name: "region_of_interest"
   is_exported: true
   namespace: "codec_fwk"
@@ -118,6 +126,13 @@
 }
 
 flag {
+  name: "rendering_depth_removal"
+  namespace: "codec_fwk"
+  description: "Feature flag for removing rendering depth"
+  bug: "275527219"
+}
+
+flag {
   name: "secure_codecs_require_crypto"
   namespace: "codec_fwk"
   description: "Bugfix flag for requiring setting crypto for secure codecs"
diff --git a/media/audio/aconfig/Android.bp b/media/audio/aconfig/Android.bp
index a5aeff2..47418cf 100644
--- a/media/audio/aconfig/Android.bp
+++ b/media/audio/aconfig/Android.bp
@@ -172,6 +172,12 @@
 }
 
 cc_aconfig_library {
+    name: "android.media.audio-aconfig-cc",
+    aconfig_declarations: "android.media.audio-aconfig",
+    defaults: ["audio-aconfig-cc-defaults"],
+}
+
+cc_aconfig_library {
     name: "android.media.audiopolicy-aconfig-cc",
     aconfig_declarations: "android.media.audiopolicy-aconfig",
     defaults: ["audio-aconfig-cc-defaults"],
diff --git a/media/audio/aconfig/audio_framework.aconfig b/media/audio/aconfig/audio_framework.aconfig
index 411996e..0b434f7 100644
--- a/media/audio/aconfig/audio_framework.aconfig
+++ b/media/audio/aconfig/audio_framework.aconfig
@@ -1,5 +1,6 @@
 # Top level framework (android.media) flags
 # Only add flags here which must be included in framework.jar
+# Flags used in both framework.jar and native can be added here
 #
 # Please add flags in alphabetical order.
 
@@ -22,6 +23,15 @@
     bug: "302323921"
 }
 
+flag {
+    name: "concurrent_audio_record_bypass_permission"
+    namespace: "media_audio"
+    description:
+       "New privileged permission to allow bypassing concurrent audio"
+       "capture rules."
+    bug: "374751406"
+}
+
 flag{
     name: "enable_ringtone_haptics_customization"
     namespace: "media_audio"
@@ -144,6 +154,14 @@
     is_fixed_read_only: true
 }
 
+flag {
+    name: "routed_device_ids"
+    namespace: "media_audio"
+    description:
+        "Enable Java and native functions to get"
+        "multiple routed device ids"
+    bug: "367816690"
+}
 
 # TODO remove
 flag {
diff --git a/media/codec2/components/aom/C2SoftAomEnc.cpp b/media/codec2/components/aom/C2SoftAomEnc.cpp
index 722b13a..93009c4 100644
--- a/media/codec2/components/aom/C2SoftAomEnc.cpp
+++ b/media/codec2/components/aom/C2SoftAomEnc.cpp
@@ -466,6 +466,7 @@
 
 aom_codec_err_t C2SoftAomEnc::setupCodecParameters() {
     aom_codec_err_t codec_return = AOM_CODEC_OK;
+    const int maxIntraBitratePct = mBitrateControlMode == AOM_CBR ? 300 : 450;
 
     codec_return = aom_codec_control(mCodecContext, AV1E_SET_TARGET_SEQ_LEVEL_IDX, mAV1EncLevel);
     if (codec_return != AOM_CODEC_OK) goto BailOut;
@@ -492,6 +493,10 @@
     codec_return = aom_codec_control(mCodecContext, AV1E_SET_AQ_MODE, 3);
     if (codec_return != AOM_CODEC_OK) goto BailOut;
 
+    codec_return = aom_codec_control(mCodecContext, AOME_SET_MAX_INTRA_BITRATE_PCT,
+                                     maxIntraBitratePct);
+    if (codec_return != AOM_CODEC_OK) goto BailOut;
+
     codec_return = aom_codec_control(mCodecContext, AV1E_SET_COEFF_COST_UPD_FREQ, 3);
     if (codec_return != AOM_CODEC_OK) goto BailOut;
 
diff --git a/media/codec2/hal/common/include/codec2/common/BqPoolInvalidateHelper.h b/media/codec2/hal/common/include/codec2/common/BqPoolInvalidateHelper.h
new file mode 100644
index 0000000..859f703
--- /dev/null
+++ b/media/codec2/hal/common/include/codec2/common/BqPoolInvalidateHelper.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2024 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.
+ */
+
+#pragma once
+
+#include <C2BqBufferPriv.h>
+#include <C2PlatformSupport.h>
+
+namespace android {
+
+// filter fn from component's blockpool container to bqpool conatainer
+static inline bool BqPoolFilterFn(
+        std::pair<const uint64_t, std::shared_ptr<C2BlockPool>> pool) {
+    return (pool.second->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE);
+}
+
+// convert fn from component's blockpool container to bqpool container
+static inline std::shared_ptr<C2BufferQueueBlockPool> BqPoolConvertFn(
+        std::pair<const uint64_t, std::shared_ptr<C2BlockPool>> pool) {
+    return std::static_pointer_cast<C2BufferQueueBlockPool>(pool.second);
+}
+
+// This is similar to std::transform excpet there is \pred functor parameter.
+// The elements with \pred function value \true only will be transformed and
+// added to the dest container. (For portability std::ranges are not used.)
+template <class InputIt, class OutputIt, class Pred, class Fct>
+void transform_if(InputIt first, InputIt last, OutputIt dest, Pred pred, Fct transform)
+{
+   while (first != last) {
+      if (pred(*first)) {
+         *dest++ = transform(*first);
+      }
+      ++first;
+   }
+}
+
+}  // namespace android
diff --git a/media/codec2/hal/hidl/1.0/utils/Component.cpp b/media/codec2/hal/hidl/1.0/utils/Component.cpp
index 62f0e25..162a80e 100644
--- a/media/codec2/hal/hidl/1.0/utils/Component.cpp
+++ b/media/codec2/hal/hidl/1.0/utils/Component.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "Codec2-Component"
 #include <android-base/logging.h>
 
+#include <codec2/common/BqPoolInvalidateHelper.h>
 #include <codec2/hidl/1.0/Component.h>
 #include <codec2/hidl/1.0/ComponentStore.h>
 #include <codec2/hidl/1.0/InputBufferManager.h>
@@ -30,6 +31,7 @@
 #include <utils/Timers.h>
 
 #include <C2BqBufferPriv.h>
+#include <C2BqPoolInvalidator.h>
 #include <C2Debug.h>
 #include <C2PlatformSupport.h>
 
@@ -270,16 +272,17 @@
 }
 
 void Component::onDeathReceived() {
+    std::list<std::shared_ptr<C2BufferQueueBlockPool>> bqPools;
     {
         std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
         mClientDied = true;
-        for (auto it = mBlockPools.begin(); it != mBlockPools.end(); ++it) {
-            if (it->second->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
-                std::shared_ptr<C2BufferQueueBlockPool> bqPool =
-                        std::static_pointer_cast<C2BufferQueueBlockPool>(it->second);
-                bqPool->invalidate();
-            }
-        }
+        transform_if(mBlockPools.begin(), mBlockPools.end(), std::back_inserter(bqPools),
+                BqPoolFilterFn, BqPoolConvertFn);
+    }
+    if (!bqPools.empty()) {
+        std::shared_ptr<C2BqPoolInvalidateItem> bqInvalidateItem =
+                std::make_shared<C2BqPoolInvalidateItem>(std::move(bqPools));
+        bqInvalidateItem->invalidate();
     }
     release();
 }
@@ -549,7 +552,26 @@
 }
 
 Return<Status> Component::release() {
+    std::list<std::shared_ptr<C2BufferQueueBlockPool>> bqPools;
+    {
+        std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
+        if (!mClientDied) {
+            transform_if(mBlockPools.begin(), mBlockPools.end(), std::back_inserter(bqPools),
+                     BqPoolFilterFn, BqPoolConvertFn);
+        }
+    }
+    std::shared_ptr<C2BqPoolInvalidateItem> bqInvalidateItem;
+    if (!bqPools.empty()) {
+        // handling rare cases of process death just after release() called.
+        bqInvalidateItem = std::make_shared<C2BqPoolInvalidateItem>(std::move(bqPools));
+        C2BqPoolInvalidator::getInstance().queue(bqInvalidateItem);
+    }
     Status status = static_cast<Status>(mComponent->release());
+    if (bqInvalidateItem) {
+        // If release is not blocked,
+        // skip invalidation and finish ASAP.
+        bqInvalidateItem->skip();
+    }
     {
         std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
         mBlockPools.clear();
@@ -637,6 +659,18 @@
 }
 
 Component::~Component() {
+    std::list<std::shared_ptr<C2BufferQueueBlockPool>> bqPools;
+    {
+        std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
+        transform_if(mBlockPools.begin(), mBlockPools.end(), std::back_inserter(bqPools),
+                     BqPoolFilterFn, BqPoolConvertFn);
+    }
+    if (!bqPools.empty()) {
+        LOG(ERROR) << "blockpools are not cleared yet at dtor";
+        std::shared_ptr<C2BqPoolInvalidateItem> bqInvalidateItem =
+                std::make_shared<C2BqPoolInvalidateItem>(std::move(bqPools));
+        C2BqPoolInvalidator::getInstance().queue(bqInvalidateItem);
+    }
     InputBufferManager::unregisterFrameData(mListener);
     mStore->reportComponentDeath(this);
 }
diff --git a/media/codec2/hal/hidl/1.1/utils/Component.cpp b/media/codec2/hal/hidl/1.1/utils/Component.cpp
index 7f2c4dd..1c2a49a 100644
--- a/media/codec2/hal/hidl/1.1/utils/Component.cpp
+++ b/media/codec2/hal/hidl/1.1/utils/Component.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "Codec2-Component@1.1"
 #include <android-base/logging.h>
 
+#include <codec2/common/BqPoolInvalidateHelper.h>
 #include <codec2/hidl/1.1/Component.h>
 #include <codec2/hidl/1.1/ComponentStore.h>
 #include <codec2/hidl/1.1/InputBufferManager.h>
@@ -32,6 +33,7 @@
 #include <codec2/common/MultiAccessUnitHelper.h>
 
 #include <C2BqBufferPriv.h>
+#include <C2BqPoolInvalidator.h>
 #include <C2Debug.h>
 #include <C2PlatformSupport.h>
 
@@ -274,16 +276,17 @@
 }
 
 void Component::onDeathReceived() {
+    std::list<std::shared_ptr<C2BufferQueueBlockPool>> bqPools;
     {
         std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
         mClientDied = true;
-        for (auto it = mBlockPools.begin(); it != mBlockPools.end(); ++it) {
-            if (it->second->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
-                std::shared_ptr<C2BufferQueueBlockPool> bqPool =
-                        std::static_pointer_cast<C2BufferQueueBlockPool>(it->second);
-                bqPool->invalidate();
-            }
-        }
+        transform_if(mBlockPools.begin(), mBlockPools.end(), std::back_inserter(bqPools),
+                BqPoolFilterFn, BqPoolConvertFn);
+    }
+    if (!bqPools.empty()) {
+        std::shared_ptr<C2BqPoolInvalidateItem> bqInvalidateItem =
+                std::make_shared<C2BqPoolInvalidateItem>(std::move(bqPools));
+        bqInvalidateItem->invalidate();
     }
     release();
 }
@@ -555,7 +558,26 @@
 }
 
 Return<Status> Component::release() {
+    std::list<std::shared_ptr<C2BufferQueueBlockPool>> bqPools;
+    {
+        std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
+        if (!mClientDied) {
+            transform_if(mBlockPools.begin(), mBlockPools.end(), std::back_inserter(bqPools),
+                    BqPoolFilterFn, BqPoolConvertFn);
+        }
+    }
+    std::shared_ptr<C2BqPoolInvalidateItem> bqInvalidateItem;
+    if (!bqPools.empty()) {
+        // handling rare cases of process death just after release() called.
+        bqInvalidateItem = std::make_shared<C2BqPoolInvalidateItem>(std::move(bqPools));
+        C2BqPoolInvalidator::getInstance().queue(bqInvalidateItem);
+    }
     Status status = static_cast<Status>(mComponent->release());
+    if (bqInvalidateItem) {
+        // If release is not blocked,
+        // skip invalidation and finish ASAP.
+        bqInvalidateItem->skip();
+    }
     {
         std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
         mBlockPools.clear();
@@ -649,6 +671,18 @@
 }
 
 Component::~Component() {
+    std::list<std::shared_ptr<C2BufferQueueBlockPool>> bqPools;
+    {
+        std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
+        transform_if(mBlockPools.begin(), mBlockPools.end(), std::back_inserter(bqPools),
+                BqPoolFilterFn, BqPoolConvertFn);
+    }
+    if (!bqPools.empty()) {
+        LOG(ERROR) << "blockpools are not cleared yet at dtor";
+        std::shared_ptr<C2BqPoolInvalidateItem> bqInvalidateItem =
+                std::make_shared<C2BqPoolInvalidateItem>(std::move(bqPools));
+        C2BqPoolInvalidator::getInstance().queue(bqInvalidateItem);
+    }
     InputBufferManager::unregisterFrameData(mListener);
     mStore->reportComponentDeath(this);
 }
diff --git a/media/codec2/hal/hidl/1.2/utils/Component.cpp b/media/codec2/hal/hidl/1.2/utils/Component.cpp
index 7b0aa9b..a15febe 100644
--- a/media/codec2/hal/hidl/1.2/utils/Component.cpp
+++ b/media/codec2/hal/hidl/1.2/utils/Component.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "Codec2-Component@1.2"
 #include <android-base/logging.h>
 
+#include <codec2/common/BqPoolInvalidateHelper.h>
 #include <codec2/hidl/1.2/Component.h>
 #include <codec2/hidl/1.2/ComponentStore.h>
 #include <codec2/hidl/1.2/InputBufferManager.h>
@@ -30,6 +31,7 @@
 #include <utils/Timers.h>
 
 #include <C2BqBufferPriv.h>
+#include <C2BqPoolInvalidator.h>
 #include <C2Debug.h>
 #include <C2PlatformSupport.h>
 
@@ -272,16 +274,17 @@
 }
 
 void Component::onDeathReceived() {
+    std::list<std::shared_ptr<C2BufferQueueBlockPool>> bqPools;
     {
         std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
         mClientDied = true;
-        for (auto it = mBlockPools.begin(); it != mBlockPools.end(); ++it) {
-            if (it->second->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
-                std::shared_ptr<C2BufferQueueBlockPool> bqPool =
-                        std::static_pointer_cast<C2BufferQueueBlockPool>(it->second);
-                bqPool->invalidate();
-            }
-        }
+        transform_if(mBlockPools.begin(), mBlockPools.end(), std::back_inserter(bqPools),
+                BqPoolFilterFn, BqPoolConvertFn);
+    }
+    if (!bqPools.empty()) {
+        std::shared_ptr<C2BqPoolInvalidateItem> bqInvalidateItem =
+                std::make_shared<C2BqPoolInvalidateItem>(std::move(bqPools));
+        bqInvalidateItem->invalidate();
     }
     release();
 }
@@ -551,7 +554,26 @@
 }
 
 Return<Status> Component::release() {
+    std::list<std::shared_ptr<C2BufferQueueBlockPool>> bqPools;
+    {
+        std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
+        if (!mClientDied) {
+            transform_if(mBlockPools.begin(), mBlockPools.end(), std::back_inserter(bqPools),
+                    BqPoolFilterFn, BqPoolConvertFn);
+        }
+    }
+    std::shared_ptr<C2BqPoolInvalidateItem> bqInvalidateItem;
+    if (!bqPools.empty()) {
+        // handling rare cases of process death just after release() called.
+        bqInvalidateItem = std::make_shared<C2BqPoolInvalidateItem>(std::move(bqPools));
+        C2BqPoolInvalidator::getInstance().queue(bqInvalidateItem);
+    }
     Status status = static_cast<Status>(mComponent->release());
+    if (bqInvalidateItem) {
+        // If release is not blocked,
+        // skip invalidation and finish ASAP.
+        bqInvalidateItem->skip();
+    }
     {
         std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
         mBlockPools.clear();
@@ -676,6 +698,18 @@
 }
 
 Component::~Component() {
+    std::list<std::shared_ptr<C2BufferQueueBlockPool>> bqPools;
+    {
+        std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
+        transform_if(mBlockPools.begin(), mBlockPools.end(), std::back_inserter(bqPools),
+                BqPoolFilterFn, BqPoolConvertFn);
+    }
+    if (!bqPools.empty()) {
+        LOG(ERROR) << "blockpools are not cleared yet at dtor";
+        std::shared_ptr<C2BqPoolInvalidateItem> bqInvalidateItem =
+                std::make_shared<C2BqPoolInvalidateItem>(std::move(bqPools));
+        C2BqPoolInvalidator::getInstance().queue(bqInvalidateItem);
+    }
     InputBufferManager::unregisterFrameData(mListener);
     mStore->reportComponentDeath(this);
 }
diff --git a/media/codec2/sfplugin/Android.bp b/media/codec2/sfplugin/Android.bp
index 3c8c1b7..cc5d10c 100644
--- a/media/codec2/sfplugin/Android.bp
+++ b/media/codec2/sfplugin/Android.bp
@@ -16,6 +16,10 @@
 cc_library_shared {
     name: "libsfplugin_ccodec",
 
+    defaults: [
+        "android.hardware.graphics.common-ndk_shared",
+    ],
+
     export_include_dirs: ["include"],
 
     srcs: [
@@ -46,6 +50,7 @@
     ],
 
     static_libs: [
+        "libPlatformProperties",
         "libSurfaceFlingerProperties",
         "aconfig_mediacodec_flags_c_lib",
         "android.media.codec-aconfig-cc",
@@ -56,7 +61,6 @@
         "android.hardware.drm@1.0",
         "android.hardware.media.c2@1.0",
         "android.hardware.media.omx@1.0",
-        "android.hardware.graphics.common-V5-ndk",
         "graphicbuffersource-aidl-ndk",
         "libbase",
         "libbinder",
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 3ef2f84..5c46d99 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -38,6 +38,7 @@
 
 #include <android/hardware/cas/native/1.0/IDescrambler.h>
 #include <android/hardware/drm/1.0/types.h>
+#include <android/sysprop/MediaProperties.sysprop.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -207,8 +208,18 @@
         Mutexed<BlockPools>::Locked pools(mBlockPools);
         pools->outputPoolId = C2BlockPool::BASIC_LINEAR;
     }
-    std::string value = GetServerConfigurableFlag("media_native", "ccodec_rendering_depth", "3");
-    android::base::ParseInt(value, &mRenderingDepth);
+    if (android::media::codec::provider_->rendering_depth_removal()) {
+        constexpr int kAndroidApi202404 = 202404;
+        int vendorVersion = ::android::base::GetIntProperty("ro.vendor.api_level", -1);
+        using ::android::sysprop::MediaProperties::codec2_remove_rendering_depth;
+        if (vendorVersion > kAndroidApi202404 || codec2_remove_rendering_depth().value_or(false)) {
+            mRenderingDepth = 0;
+        }
+    } else {
+        std::string value = GetServerConfigurableFlag(
+                "media_native", "ccodec_rendering_depth", "3");
+        android::base::ParseInt(value, &mRenderingDepth);
+    }
     mOutputSurface.lock()->maxDequeueBuffers = kSmoothnessFactor + mRenderingDepth;
 }
 
@@ -2052,6 +2063,14 @@
 
 status_t CCodecBufferChannel::requestInitialInputBuffers(
         std::map<size_t, sp<MediaCodecBuffer>> &&clientInputBuffers) {
+    std::optional<QueueGuard> guard;
+    if (android::media::codec::provider_->codec_buffer_state_cleanup()) {
+        guard.emplace(mSync);
+        if (!guard->isRunning()) {
+            ALOGD("[%s] skip requestInitialInputBuffers when not running", mName);
+            return OK;
+        }
+    }
     C2StreamBufferTypeSetting::output oStreamFormat(0u);
     C2PrependHeaderModeSetting prepend(PREPEND_HEADER_TO_NONE);
     c2_status_t err = mComponent->query({ &oStreamFormat, &prepend }, {}, C2_DONT_BLOCK, nullptr);
diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp
index dc06ee6..9d1cbff 100644
--- a/media/codec2/vndk/Android.bp
+++ b/media/codec2/vndk/Android.bp
@@ -53,7 +53,7 @@
     ],
 
     defaults: [
-	"aconfig_lib_cc_static_link.defaults",
+        "aconfig_lib_cc_static_link.defaults",
         "libcodec2_hal_selection",
     ],
 
@@ -68,6 +68,7 @@
         "C2PlatformStorePluginLoader.cpp",
         "C2Store.cpp",
         "platform/C2BqBuffer.cpp",
+        "platform/C2BqPoolInvalidator.cpp",
         "platform/C2SurfaceSyncObj.cpp",
         "platform/C2IgbaBuffer.cpp",
         "types.cpp",
diff --git a/media/codec2/vndk/include/C2BqPoolInvalidator.h b/media/codec2/vndk/include/C2BqPoolInvalidator.h
new file mode 100644
index 0000000..612d023
--- /dev/null
+++ b/media/codec2/vndk/include/C2BqPoolInvalidator.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#pragma once
+
+#include <android-base/no_destructor.h>
+#include <media/stagefright/foundation/ABase.h>
+
+#include <condition_variable>
+#include <deque>
+#include <list>
+#include <memory>
+#include <thread>
+
+class C2BufferQueueBlockPool;
+
+namespace android {
+
+/**
+ * Container class in order to invalidate C2BufferQueueBlockPool(s) and their resources
+ * when the client process is dead abruptly.
+ */
+class C2BqPoolInvalidateItem {
+public:
+
+    /**
+     * invalidate contained C2BufferQueueBlockPool(s) and their resources
+     */
+    void invalidate();
+
+    /**
+     * skip invalidate(), if it is scheduled and not yet invalidated.
+     */
+    void skip();
+
+    /**
+     * returns whether invalidate() is reuqired or not.
+     */
+    bool needsInvalidate();
+
+    C2BqPoolInvalidateItem(std::list<std::shared_ptr<C2BufferQueueBlockPool>> &&pools);
+
+    ~C2BqPoolInvalidateItem() = default;
+private:
+
+    std::list<std::shared_ptr<C2BufferQueueBlockPool>>  mPools;
+    bool mNeedsInvalidate;
+    std::mutex mLock;
+
+    DISALLOW_EVIL_CONSTRUCTORS(C2BqPoolInvalidateItem);
+};
+
+/**
+ * Asynchronous C2BufferQueueBlockPool invalidator.
+ *
+ * this has C2BqPoolInvalidateItem inside. and call invalidate() from a separate
+ * thread asynchronously.
+ */
+class C2BqPoolInvalidator {
+public:
+    /**
+     * This gets the singleton instance of the class.
+     */
+    static C2BqPoolInvalidator &getInstance();
+
+    /**
+     * queue invalidation items. the item will be invalidated after certain
+     * amount of delay from a separate thread.
+     */
+    void queue(std::shared_ptr<C2BqPoolInvalidateItem> &item);
+
+    ~C2BqPoolInvalidator();
+private:
+
+    C2BqPoolInvalidator();
+
+    void run();
+
+    std::thread mThread;
+    bool mDone;
+
+    std::mutex mMutex;
+    std::condition_variable mCv;
+
+    std::deque<std::pair<int64_t, std::shared_ptr<C2BqPoolInvalidateItem>>> mItems;
+
+    friend class ::android::base::NoDestructor<C2BqPoolInvalidator>;
+
+    DISALLOW_EVIL_CONSTRUCTORS(C2BqPoolInvalidator);
+};
+
+}  // namespace android
diff --git a/media/codec2/vndk/platform/C2BqPoolInvalidator.cpp b/media/codec2/vndk/platform/C2BqPoolInvalidator.cpp
new file mode 100644
index 0000000..2666cd3
--- /dev/null
+++ b/media/codec2/vndk/platform/C2BqPoolInvalidator.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2024 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 "C2BqPoolInvalidator"
+#include <utils/Log.h>
+#include <utils/SystemClock.h>
+
+#include <C2BqBufferPriv.h>
+#include <C2BqPoolInvalidator.h>
+
+namespace android {
+
+namespace {
+    static constexpr int64_t kBqPoolInvalidateDelayMs = 1000;
+} // anonymous namespace
+
+C2BqPoolInvalidateItem::C2BqPoolInvalidateItem(
+        std::list<std::shared_ptr<C2BufferQueueBlockPool>> &&pools) : mPools(std::move(pools)) {
+    if (!mPools.empty()) {
+        mNeedsInvalidate = true;
+    } else {
+        mNeedsInvalidate = false;
+    }
+}
+
+void C2BqPoolInvalidateItem::invalidate() {
+    std::list<std::shared_ptr<C2BufferQueueBlockPool>> pools;
+    {
+        std::unique_lock<std::mutex> l(mLock);
+        if (!mNeedsInvalidate) {
+            return;
+        }
+        pools = std::move(mPools);
+        mNeedsInvalidate = false;
+    }
+    for(auto it = pools.begin(); it != pools.end(); ++it) {
+        (*it)->invalidate();
+    }
+}
+
+void C2BqPoolInvalidateItem::skip() {
+    std::unique_lock<std::mutex> l(mLock);
+    mNeedsInvalidate = false;
+    mPools.clear();
+}
+
+bool C2BqPoolInvalidateItem::needsInvalidate() {
+    std::unique_lock<std::mutex> l(mLock);
+    return mNeedsInvalidate;
+}
+
+C2BqPoolInvalidator &C2BqPoolInvalidator::getInstance() {
+    static android::base::NoDestructor<C2BqPoolInvalidator> sInvalidator;
+    return *sInvalidator;
+}
+
+C2BqPoolInvalidator::C2BqPoolInvalidator() : mDone(false) {
+    mThread = std::thread(&C2BqPoolInvalidator::run, this);
+}
+
+C2BqPoolInvalidator::~C2BqPoolInvalidator() {
+    {
+        std::unique_lock<std::mutex> l(mMutex);
+        mDone = true;
+        mCv.notify_one();
+    }
+    if (mThread.joinable()) {
+        mThread.join();
+    }
+}
+
+void C2BqPoolInvalidator::queue(std::shared_ptr<C2BqPoolInvalidateItem> &item) {
+    std::unique_lock<std::mutex> l(mMutex);
+    std::pair<int64_t, std::shared_ptr<C2BqPoolInvalidateItem>> p =
+            std::make_pair(::android::elapsedRealtime() + kBqPoolInvalidateDelayMs, item);
+    mItems.push_back(p);
+    mCv.notify_one();
+}
+
+void C2BqPoolInvalidator::run() {
+    while(true) {
+        int64_t nowMs = ::android::elapsedRealtime();
+        std::unique_lock<std::mutex> l(mMutex);
+        if (mDone) {
+            break;
+        }
+        std::list<std::shared_ptr<C2BqPoolInvalidateItem>> items;
+        while (!mItems.empty()) {
+            if (mItems.front().first <= nowMs) {
+                items.push_back(mItems.front().second);
+                mItems.pop_front();
+            } else {
+                break;
+            }
+        }
+        if (items.empty()) {
+            if (mItems.empty()) {
+                mCv.wait(l);
+            } else {
+                int64_t nextMs = mItems.front().first;
+                if (nextMs > nowMs) {
+                    mCv.wait_for(l, std::chrono::milliseconds(nextMs - nowMs));
+                }
+            }
+        } else {
+            l.unlock();
+            int invalidated = 0;
+            for (auto it = items.begin(); it != items.end(); ++it, ++invalidated) {
+                (*it)->invalidate();
+            }
+            ALOGD("invalidated %d bqpool items", invalidated);
+        }
+    }
+}
+
+} // android
diff --git a/media/libaaudio/fuzzer/Android.bp b/media/libaaudio/fuzzer/Android.bp
index a1551f8..f4637e3 100644
--- a/media/libaaudio/fuzzer/Android.bp
+++ b/media/libaaudio/fuzzer/Android.bp
@@ -66,6 +66,7 @@
         "libmedia_helper",
         "libmediametrics",
         "libprocessgroup",
+        "libprocessgroup_util",
         "mediametricsservice-aidl-cpp",
         "shared-file-region-aidl-cpp",
     ],
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index 5b954f7..b193950 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -1159,11 +1159,10 @@
             // start of lock scope
             AutoMutex lock(mLock);
 
-            uint32_t newSequence = mSequence;
             // did previous obtainBuffer() fail due to media server death or voluntary invalidation?
             if (status == DEAD_OBJECT) {
                 // re-create track, unless someone else has already done so
-                if (newSequence == oldSequence) {
+                if (mSequence == oldSequence) {
                     if (!audio_is_linear_pcm(mFormat)) {
                         // If compressed capture, don't attempt to restore the track.
                         // Return a DEAD_OBJECT error and let the caller recreate.
@@ -1179,7 +1178,7 @@
                     }
                 }
             }
-            oldSequence = newSequence;
+            oldSequence = mSequence;
 
             // Keep the extra references
             proxy = mProxy;
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index d7c0b5b..e0c5e92 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -2221,11 +2221,10 @@
         {   // start of lock scope
             AutoMutex lock(mLock);
 
-            uint32_t newSequence = mSequence;
             // did previous obtainBuffer() fail due to media server death or voluntary invalidation?
             if (status == DEAD_OBJECT) {
                 // re-create track, unless someone else has already done so
-                if (newSequence == oldSequence) {
+                if (mSequence == oldSequence) {
                     status = restoreTrack_l("obtainBuffer");
                     if (status != NO_ERROR) {
                         buffer.mFrameCount = 0;
@@ -2235,7 +2234,7 @@
                     }
                 }
             }
-            oldSequence = newSequence;
+            oldSequence = mSequence;
 
             if (status == NOT_ENOUGH_DATA) {
                 restartIfDisabled();
diff --git a/media/libaudioclient/aidl/fuzzer/Android.bp b/media/libaudioclient/aidl/fuzzer/Android.bp
index 61d5ccd..a215c0b 100644
--- a/media/libaudioclient/aidl/fuzzer/Android.bp
+++ b/media/libaudioclient/aidl/fuzzer/Android.bp
@@ -30,6 +30,7 @@
         "libjsoncpp",
         "libmediametricsservice",
         "libprocessgroup",
+        "libprocessgroup_util",
         "shared-file-region-aidl-cpp",
     ],
     shared_libs: [
diff --git a/media/libaudioclient/fuzzer/Android.bp b/media/libaudioclient/fuzzer/Android.bp
index a95c700..8bca8df 100644
--- a/media/libaudioclient/fuzzer/Android.bp
+++ b/media/libaudioclient/fuzzer/Android.bp
@@ -46,6 +46,7 @@
         "libmediametrics",
         "libmediametricsservice",
         "libprocessgroup",
+        "libprocessgroup_util",
         "shared-file-region-aidl-cpp",
     ],
     shared_libs: [
diff --git a/media/libaudioclient/include/media/AudioTimestamp.h b/media/libaudioclient/include/media/AudioTimestamp.h
index e5925dd..45d5595 100644
--- a/media/libaudioclient/include/media/AudioTimestamp.h
+++ b/media/libaudioclient/include/media/AudioTimestamp.h
@@ -154,10 +154,13 @@
     std::string toString() const {
         std::stringstream ss;
 
-        ss << "BOOTTIME offset " << mTimebaseOffset[TIMEBASE_BOOTTIME] << "\n";
+        ss << "BOOTTIME offset " << mTimebaseOffset[TIMEBASE_BOOTTIME] << ": ExtendedTimestamp: ";
         for (int i = 0; i < LOCATION_MAX; ++i) {
-            ss << "ExtendedTimestamp[" << i << "]  position: "
-                    << mPosition[i] << "  time: "  << mTimeNs[i] << "\n";
+            ss << "([" << i << "]  position: "
+                    << mPosition[i] << "  time: "  << mTimeNs[i] << ")";
+            if (i != LOCATION_MAX - 1) {
+                ss << ", ";
+            }
         }
         return ss.str();
     }
diff --git a/media/libaudiohal/impl/StreamHalAidl.cpp b/media/libaudiohal/impl/StreamHalAidl.cpp
index 9863fec..c4e4ae8 100644
--- a/media/libaudiohal/impl/StreamHalAidl.cpp
+++ b/media/libaudiohal/impl/StreamHalAidl.cpp
@@ -544,7 +544,14 @@
 }
 
 void StreamHalAidl::onAsyncTransferReady() {
-    if (auto state = getState(); state == StreamDescriptor::State::TRANSFERRING) {
+    StreamDescriptor::State state;
+    {
+        // Use 'mCommandReplyLock' to ensure that 'sendCommand' has finished updating the state
+        // after the reply from the 'burst' command.
+        std::lock_guard l(mCommandReplyLock);
+        state = getState();
+    }
+    if (state == StreamDescriptor::State::TRANSFERRING) {
         // Retrieve the current state together with position counters unconditionally
         // to ensure that the state on our side gets updated.
         sendCommand(makeHalCommand<HalCommand::Tag::getStatus>(),
@@ -555,7 +562,14 @@
 }
 
 void StreamHalAidl::onAsyncDrainReady() {
-    if (auto state = getState(); state == StreamDescriptor::State::DRAINING) {
+    StreamDescriptor::State state;
+    {
+        // Use 'mCommandReplyLock' to ensure that 'sendCommand' has finished updating the state
+        // after the reply from the 'drain' command.
+        std::lock_guard l(mCommandReplyLock);
+        state = getState();
+    }
+    if (state == StreamDescriptor::State::DRAINING) {
         // Retrieve the current state together with position counters unconditionally
         // to ensure that the state on our side gets updated.
         sendCommand(makeHalCommand<HalCommand::Tag::getStatus>(), nullptr,
diff --git a/media/libmedia/xsd/vts/Android.bp b/media/libmedia/xsd/vts/Android.bp
index 83ab977..add7b51 100644
--- a/media/libmedia/xsd/vts/Android.bp
+++ b/media/libmedia/xsd/vts/Android.bp
@@ -15,6 +15,7 @@
 //
 
 package {
+    default_team: "trendy_team_android_kernel",
     // See: http://go/android-license-faq
     // A large-scale-change added 'default_applicable_licenses' to import
     // all of the 'license_kinds' from "frameworks_av_media_libmedia_license"
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 10a1da7..b267c08 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1816,8 +1816,6 @@
         const sp<AudioSystem::AudioDeviceCallback>& deviceCallback)
     : mCachedPlayerIId(PLAYER_PIID_INVALID),
       mCallback(NULL),
-      mCallbackCookie(NULL),
-      mCallbackData(NULL),
       mStreamType(AUDIO_STREAM_MUSIC),
       mLeftVolume(1.0),
       mRightVolume(1.0),
@@ -2085,7 +2083,7 @@
 status_t MediaPlayerService::AudioOutput::open(
         uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
         audio_format_t format, int bufferCount,
-        AudioCallback cb, void *cookie,
+        AudioCallback cb, const wp<RefBase>& cookie,
         audio_output_flags_t flags,
         const audio_offload_info_t *offloadInfo,
         bool doNotReconnect,
@@ -2514,6 +2512,15 @@
     {
         Mutex::Autolock lock(mLock);
         track = mTrack;
+    }
+
+    // do not hold lock while joining.
+    if (track) {
+        track->stopAndJoinCallbacks();
+    }
+
+    {
+        Mutex::Autolock lock(mLock);
         close_l(); // clears mTrack
     }
     // destruction of the track occurs outside of mutex.
@@ -2705,7 +2712,7 @@
         return 0;
     }
     size_t actualSize = (*me->mCallback)(
-            me.get(), buffer.data(), buffer.size(), me->mCallbackCookie,
+            me, buffer.data(), buffer.size(), me->mCallbackCookie,
             CB_EVENT_FILL_BUFFER);
 
     // Log when no data is returned from the callback.
@@ -2730,7 +2737,7 @@
         return;
     }
     ALOGV("callbackwrapper: deliver EVENT_STREAM_END");
-    (*me->mCallback)(me.get(), NULL /* buffer */, 0 /* size */,
+    (*me->mCallback)(me, nullptr /* buffer */, 0 /* size */,
             me->mCallbackCookie, CB_EVENT_STREAM_END);
     unlock();
 }
@@ -2744,7 +2751,7 @@
         return;
     }
     ALOGV("callbackwrapper: deliver EVENT_TEAR_DOWN");
-    (*me->mCallback)(me.get(),  NULL /* buffer */, 0 /* size */,
+    (*me->mCallback)(me, nullptr /* buffer */, 0 /* size */,
             me->mCallbackCookie, CB_EVENT_TEAR_DOWN);
     unlock();
 }
@@ -2794,7 +2801,7 @@
 struct CallbackThread : public Thread {
     CallbackThread(const wp<MediaPlayerBase::AudioSink> &sink,
                    MediaPlayerBase::AudioSink::AudioCallback cb,
-                   void *cookie);
+                   const wp<RefBase>& cookie);
 
 protected:
     virtual ~CallbackThread();
@@ -2804,7 +2811,7 @@
 private:
     wp<MediaPlayerBase::AudioSink> mSink;
     MediaPlayerBase::AudioSink::AudioCallback mCallback;
-    void *mCookie;
+    wp<RefBase> mCookie;
     void *mBuffer;
     size_t mBufferSize;
 
@@ -2815,7 +2822,7 @@
 CallbackThread::CallbackThread(
         const wp<MediaPlayerBase::AudioSink> &sink,
         MediaPlayerBase::AudioSink::AudioCallback cb,
-        void *cookie)
+        const wp<RefBase>& cookie)
     : mSink(sink),
       mCallback(cb),
       mCookie(cookie),
@@ -2842,7 +2849,7 @@
     }
 
     size_t actualSize =
-        (*mCallback)(sink.get(), mBuffer, mBufferSize, mCookie,
+        (*mCallback)(sink, mBuffer, mBufferSize, mCookie,
                 MediaPlayerBase::AudioSink::CB_EVENT_FILL_BUFFER);
 
     if (actualSize > 0) {
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index cb544bd..76b7bcf 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -104,14 +104,14 @@
         virtual int64_t         getBufferDurationInUs() const;
         virtual audio_output_flags_t getFlags() const { return mFlags; }
 
-        virtual status_t        open(
+        status_t open(
                 uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
                 audio_format_t format, int bufferCount,
-                AudioCallback cb, void *cookie,
+                AudioCallback cb, const wp<RefBase>& cookie,
                 audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
                 const audio_offload_info_t *offloadInfo = NULL,
                 bool doNotReconnect = false,
-                uint32_t suggestedFrameCount = 0);
+                uint32_t suggestedFrameCount = 0) override;
 
         virtual void            setPlayerIId(int32_t playerIId);
 
@@ -164,7 +164,7 @@
         sp<AudioOutput>         mNextOutput;
         int                     mCachedPlayerIId;
         AudioCallback           mCallback;
-        void *                  mCallbackCookie;
+        wp<RefBase>             mCallbackCookie;
         sp<CallbackData>        mCallbackData;
         audio_stream_type_t     mStreamType;
         audio_attributes_t *    mAttributes;
diff --git a/media/libmediaplayerservice/include/MediaPlayerInterface.h b/media/libmediaplayerservice/include/MediaPlayerInterface.h
index be1aa00..495cf00 100644
--- a/media/libmediaplayerservice/include/MediaPlayerInterface.h
+++ b/media/libmediaplayerservice/include/MediaPlayerInterface.h
@@ -76,6 +76,33 @@
         virtual ~Listener() {}
     };
 
+    // For the AudioCallback, we provide a WeakWrapper class
+    // to wrap a virtual RefBase derived object to pass into the AudioCallback.
+    // This is not used for NuPlayer::Renderer, only for legacy AudioPlayer implementation.
+    template <typename T>
+    class WeakWrapper : public RefBase {
+    public:
+        explicit WeakWrapper(const sp<T>& object)
+                : mObject(object) {}
+
+        sp<T> promote() const {
+            if (mObject == nullptr) return {};
+            return mObject.promote();
+        }
+
+        static sp<T> promoteFromRefBase(const wp<RefBase>& weakWrapper) {
+            if (weakWrapper == nullptr) return {};
+            const auto refBase = weakWrapper.promote();
+            if (!refBase) return {};
+            const auto wrapper = sp<WeakWrapper<T>>::fromExisting(
+                    static_cast<WeakWrapper<T>*>(refBase.get()));
+            return wrapper->promote();
+        }
+
+    private:
+        const wp<T> mObject;
+    };
+
     // AudioSink: abstraction layer for audio output
     class AudioSink : public RefBase {
     public:
@@ -89,8 +116,8 @@
 
         // Callback returns the number of bytes actually written to the buffer.
         typedef size_t (*AudioCallback)(
-                AudioSink *audioSink, void *buffer, size_t size, void *cookie,
-                        cb_event_t event);
+                const sp<AudioSink>& audioSink, void *buffer, size_t size,
+                const wp<RefBase>& cookie, cb_event_t event);
 
         virtual             ~AudioSink() {}
         virtual bool        ready() const = 0; // audio output is open and ready
@@ -117,7 +144,7 @@
                 audio_format_t format=AUDIO_FORMAT_PCM_16_BIT,
                 int bufferCount=DEFAULT_AUDIOSINK_BUFFERCOUNT,
                 AudioCallback cb = NULL,
-                void *cookie = NULL,
+                const wp<RefBase>& cookie = {},
                 audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
                 const audio_offload_info_t *offloadInfo = NULL,
                 bool doNotReconnect = false,
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 899d50e..f6aff83 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -912,12 +912,15 @@
 
 // static
 size_t NuPlayer::Renderer::AudioSinkCallback(
-        MediaPlayerBase::AudioSink * /* audioSink */,
+        const sp<MediaPlayerBase::AudioSink>& /* audioSink */,
         void *buffer,
         size_t size,
-        void *cookie,
+        const wp<RefBase>& cookie,
         MediaPlayerBase::AudioSink::cb_event_t event) {
-    NuPlayer::Renderer *me = (NuPlayer::Renderer *)cookie;
+    if (cookie == nullptr) return 0;
+    const auto ref = cookie.promote();
+    if (!ref) return 0;
+    const auto me = static_cast<NuPlayer::Renderer*>(ref.get()); // we already hold a sp.
 
     switch (event) {
         case MediaPlayerBase::AudioSink::CB_EVENT_FILL_BUFFER:
diff --git a/media/libmediaplayerservice/nuplayer/include/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/include/nuplayer/NuPlayerRenderer.h
index 574ad3d..cfa742e 100644
--- a/media/libmediaplayerservice/nuplayer/include/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/include/nuplayer/NuPlayerRenderer.h
@@ -43,8 +43,8 @@
              uint32_t flags = 0);
 
     static size_t AudioSinkCallback(
-            MediaPlayerBase::AudioSink *audioSink,
-            void *data, size_t size, void *me,
+            const sp<MediaPlayerBase::AudioSink>& audioSink,
+            void *data, size_t size, const wp<RefBase>& me,
             MediaPlayerBase::AudioSink::cb_event_t event);
 
     void queueBuffer(
diff --git a/media/libstagefright/xmlparser/vts/Android.bp b/media/libstagefright/xmlparser/vts/Android.bp
index 1e36c8f..527230c 100644
--- a/media/libstagefright/xmlparser/vts/Android.bp
+++ b/media/libstagefright/xmlparser/vts/Android.bp
@@ -15,6 +15,7 @@
 //
 
 package {
+    default_team: "trendy_team_android_media_codec_framework",
     // See: http://go/android-license-faq
     // A large-scale-change added 'default_applicable_licenses' to import
     // all of the 'license_kinds' from "frameworks_av_media_libstagefright_license"
diff --git a/media/module/aidlpersistentsurface/Android.bp b/media/module/aidlpersistentsurface/Android.bp
index 5c1a010..8b273f3 100644
--- a/media/module/aidlpersistentsurface/Android.bp
+++ b/media/module/aidlpersistentsurface/Android.bp
@@ -3,6 +3,9 @@
     unstable: true,
     local_include_dir: "aidl",
     min_sdk_version: "29",
+    defaults: [
+        "android.hardware.graphics.common-latest"
+    ],
     srcs: [
         "aidl/android/media/AidlColorAspects.aidl",
         "aidl/android/media/IAidlGraphicBufferSource.aidl",
@@ -12,9 +15,6 @@
     headers: [
         "HardwareBuffer_aidl",
     ],
-    imports: [
-        "android.hardware.graphics.common-V5",
-    ],
     include_dirs: [
         "frameworks/native/aidl/gui",
     ],
@@ -41,6 +41,9 @@
 cc_library_shared {
     name: "libstagefright_graphicbuffersource_aidl",
     min_sdk_version: "29",
+    defaults: [
+        "android.hardware.graphics.common-ndk_shared",
+    ],
     srcs: [
         "AidlGraphicBufferSource.cpp",
         "wrapper/WAidlGraphicBufferSource.cpp",
@@ -56,7 +59,6 @@
         "media_plugin_headers",
     ],
     shared_libs: [
-        "android.hardware.graphics.common-V5-ndk",
         "graphicbuffersource-aidl-ndk",
         "libbinder_ndk",
         "libcutils",
diff --git a/media/module/codecs/amrwb/enc/Android.bp b/media/module/codecs/amrwb/enc/Android.bp
index 04f36b5..6ca3b6e 100644
--- a/media/module/codecs/amrwb/enc/Android.bp
+++ b/media/module/codecs/amrwb/enc/Android.bp
@@ -96,8 +96,6 @@
                 "-DARM",
                 "-DARMV7",
                 "-DASM_OPT",
-                // don't actually generate neon instructions, see bug 26932980
-                "-mfpu=vfpv3",
             ],
             local_include_dirs: [
                 "src/asm/ARMV7",
diff --git a/media/module/extractors/extractor.aconfig b/media/module/extractors/extractor.aconfig
index c61efa4..c9bf694 100644
--- a/media/module/extractors/extractor.aconfig
+++ b/media/module/extractors/extractor.aconfig
@@ -7,6 +7,7 @@
 flag {
     name: "extractor_sniff_midi_optimizations"
     is_exported: true
+    is_fixed_read_only: true
     namespace: "media_extractor"
     description: "Enable SniffMidi optimizations."
     bug: "359920208"
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/BlockModelDecoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/BlockModelDecoder.java
new file mode 100644
index 0000000..3b3640e
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/BlockModelDecoder.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+package com.android.media.benchmark.library;
+
+import android.media.MediaCodec;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.os.Build;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+import com.android.media.benchmark.library.Decoder;
+
+public class BlockModelDecoder extends Decoder {
+    private static final String TAG = BlockModelDecoder.class.getSimpleName();
+    private final boolean DEBUG = false;
+    protected final LinearBlockWrapper mLinearInputBlock = new LinearBlockWrapper();
+
+    /**
+     * Wrapper class for {@link MediaCodec.LinearBlock}
+     */
+    public static class LinearBlockWrapper {
+        private MediaCodec.LinearBlock mBlock;
+        private ByteBuffer mBuffer;
+        private int mOffset;
+
+        public MediaCodec.LinearBlock getBlock() {
+            return mBlock;
+        }
+
+        public ByteBuffer getBuffer() {
+            return mBuffer;
+        }
+
+        public int getBufferCapacity() {
+            return mBuffer == null ? 0 : mBuffer.capacity();
+        }
+
+        public int getOffset() {
+            return mOffset;
+        }
+
+        public void setOffset(int size) {
+            mOffset = size;
+        }
+
+        public boolean allocateBlock(String codec, int size) throws RuntimeException{
+            recycle();
+            mBlock = MediaCodec.LinearBlock.obtain(size, new String[]{codec});
+            if (mBlock == null || !mBlock.isMappable()) {
+                throw new RuntimeException("Linear Block not allocated/mapped");
+            }
+            mBuffer = mBlock.map();
+            mOffset = 0;
+            return true;
+        }
+
+        public void recycle() {
+            if (mBlock != null) {
+                mBlock.recycle();
+                mBlock = null;
+            }
+            mBuffer = null;
+            mOffset = 0;
+        }
+    }
+
+    public BlockModelDecoder() {
+        // empty
+    }
+
+    public void tearDown() {
+        mLinearInputBlock.recycle();
+
+    }
+
+    /**
+     * Decodes the given input buffer,
+     * provided valid list of buffer info and format are passed as inputs.
+     *
+     * @param inputBuffer     Decode the provided list of ByteBuffers
+     * @param inputBufferInfo List of buffer info corresponding to provided input buffers
+     * @param asyncMode       Will run on async implementation if true
+     * @param format          For creating the decoder if codec name is empty and configuring it
+     * @param codecName       Will create the decoder with codecName
+     * @return DECODE_SUCCESS if decode was successful, DECODE_DECODER_ERROR for fail,
+     *         DECODE_CREATE_ERROR for decoder not created
+     * @throws IOException if the codec cannot be created.
+     */
+    @Override
+    public int decode(@NonNull List<ByteBuffer> inputBuffer,
+        @NonNull List<MediaCodec.BufferInfo> inputBufferInfo, final boolean asyncMode,
+        @NonNull MediaFormat format, String codecName)
+        throws IOException, InterruptedException {
+        setExtraConfigureFlags(MediaCodec.CONFIGURE_FLAG_USE_BLOCK_MODEL);
+        return super.decode(inputBuffer, inputBufferInfo, asyncMode, format, codecName);
+    }
+
+    @Override
+    protected void onInputAvailable(int inputBufferId, MediaCodec mediaCodec) {
+        if (mNumInFramesProvided >= mNumInFramesRequired) {
+            mIndex = mInputBufferInfo.size() - 1;
+        }
+        MediaCodec.BufferInfo bufInfo = mInputBufferInfo.get(mIndex);
+        mSawInputEOS = (bufInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+        if (mLinearInputBlock.getOffset() + bufInfo.size > mLinearInputBlock.getBufferCapacity()) {
+            int requestSize = 8192;
+            requestSize = Math.max(bufInfo.size, requestSize);
+            mLinearInputBlock.allocateBlock(mediaCodec.getCanonicalName(), requestSize);
+        }
+        int codecFlags = 0;
+        if ((bufInfo.flags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
+            codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
+        }
+        if ((bufInfo.flags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
+            codecFlags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
+        }
+        codecFlags |= mSawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0;
+        if (DEBUG) {
+            Log.v(TAG, "input: id: " + inputBufferId
+                    + " size: " + bufInfo.size
+                    + " pts: " + bufInfo.presentationTimeUs
+                    + " flags: " + codecFlags);
+        }
+        mLinearInputBlock.getBuffer().put(mInputBuffer.get(mIndex).array());
+        mNumInFramesProvided++;
+        mIndex = mNumInFramesProvided % (mInputBufferInfo.size() - 1);
+        if (mSawInputEOS) {
+            Log.i(TAG, "Saw Input EOS");
+        }
+        mStats.addFrameSize(bufInfo.size);
+        MediaCodec.QueueRequest request = mCodec.getQueueRequest(inputBufferId);
+        request.setLinearBlock(mLinearInputBlock.getBlock(), mLinearInputBlock.getOffset(),
+                bufInfo.size);
+        request.setPresentationTimeUs(bufInfo.presentationTimeUs);
+        request.setFlags(codecFlags);
+        request.queue();
+        if (bufInfo.size > 0 && (codecFlags & (MediaCodec.BUFFER_FLAG_CODEC_CONFIG
+                | MediaCodec.BUFFER_FLAG_PARTIAL_FRAME)) == 0) {
+            mLinearInputBlock.setOffset(mLinearInputBlock.getOffset() + bufInfo.size);
+        }
+    }
+
+    @Override
+    protected void onOutputAvailable(
+            MediaCodec mediaCodec, int outputBufferId, MediaCodec.BufferInfo outputBufferInfo) {
+        if (mSawOutputEOS || outputBufferId < 0) {
+            return;
+        }
+        mNumOutputFrame++;
+        if (DEBUG) {
+            Log.d(TAG,
+                    "In OutputBufferAvailable ,"
+                            + " output frame number = " + mNumOutputFrame
+                            + " timestamp = " + outputBufferInfo.presentationTimeUs
+                            + " size = " + outputBufferInfo.size);
+        }
+        MediaCodec.OutputFrame outFrame = mediaCodec.getOutputFrame(outputBufferId);
+        ByteBuffer outputBuffer = null;
+        try {
+            if (outFrame.getLinearBlock() != null) {
+                outputBuffer = outFrame.getLinearBlock().map();
+            }
+        } catch(IllegalStateException e) {
+            // buffer may not be linear, this is ok
+            // as we are handling non-linear buffers below.
+        }
+        if (mOutputStream != null) {
+            try {
+                if (outputBuffer != null) {
+                    byte[] bytesOutput = new byte[outputBuffer.remaining()];
+                    outputBuffer.get(bytesOutput);
+                    mOutputStream.write(bytesOutput);
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+                Log.d(TAG, "Error Dumping File: Exception " + e.toString());
+            }
+        }
+        ByteBuffer copiedBuffer = null;
+        int bytesRemaining = 0;
+        if (outputBuffer != null) {
+            bytesRemaining = outputBuffer.remaining();
+            if (mIBufferSend != null) {
+                copiedBuffer = ByteBuffer.allocate(outputBuffer.remaining());
+                copiedBuffer.put(outputBuffer);
+            }
+            outFrame.getLinearBlock().recycle();
+            outputBuffer = null;
+        }
+        if (mFrameReleaseQueue != null) {
+            if (mMime.startsWith("audio/")) {
+                try {
+                    mFrameReleaseQueue.pushFrame(outputBufferId, bytesRemaining);
+                } catch (Exception e) {
+                    Log.d(TAG, "Error in getting MediaCodec buffer" + e.toString());
+                }
+            } else {
+                mFrameReleaseQueue.pushFrame(mNumOutputFrame, outputBufferId,
+                                                outputBufferInfo.presentationTimeUs);
+            }
+
+        } else if (mIBufferSend != null) {
+            IBufferXfer.BufferXferInfo info = new IBufferXfer.BufferXferInfo();
+            // TODO: may be inefficient;
+            info.buf = copiedBuffer;
+            info.idx = outputBufferId;
+            info.obj = mediaCodec;
+            info.bytesRead = outputBufferInfo.size;
+            info.presentationTimeUs = outputBufferInfo.presentationTimeUs;
+            info.flag = outputBufferInfo.flags;
+            mIBufferSend.sendBuffer(this, info);
+        } else {
+            mediaCodec.releaseOutputBuffer(outputBufferId, mRender);
+        }
+        mSawOutputEOS = (outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+        if (DEBUG && mSawOutputEOS) {
+            Log.i(TAG, "Saw output EOS");
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java
index f223242..031817b 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java
@@ -78,4 +78,21 @@
         }
         return null;
     }
+    /**
+     * Returns compression ratio for a given mediaType.
+     * @param mediaType mime type for which compression ratio is to be returned.
+     */
+    public static float getCompressionRatio(String mediaType) {
+        switch (mediaType) {
+            case MediaFormat.MIMETYPE_AUDIO_FLAC:
+                return 0.7f;
+            case MediaFormat.MIMETYPE_AUDIO_G711_MLAW:
+            case MediaFormat.MIMETYPE_AUDIO_G711_ALAW:
+            case MediaFormat.MIMETYPE_AUDIO_MSGSM:
+                return 0.5f;
+            case MediaFormat.MIMETYPE_AUDIO_RAW:
+                return 1.0f;
+        }
+        return 0.1f;
+    }
 }
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
index e9b337d..2ea0ed2 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
@@ -18,6 +18,7 @@
 
 import android.view.Surface;
 
+import android.media.AudioFormat;
 import android.media.MediaCodec;
 import android.media.MediaCodec.BufferInfo;
 import android.media.MediaFormat;
@@ -42,6 +43,7 @@
 
     protected final Object mLock = new Object();
     protected MediaCodec mCodec;
+    protected int mExtraFlags = 0;
     protected Surface mSurface = null;
     protected boolean mRender = false;
     protected ArrayList<BufferInfo> mInputBufferInfo;
@@ -58,6 +60,8 @@
     protected int mNumOutputFrame;
     protected int mIndex;
 
+    protected boolean mUseFrameReleaseQueue = false;
+
     protected ArrayList<ByteBuffer> mInputBuffer;
     protected FileOutputStream mOutputStream;
     protected FrameReleaseQueue mFrameReleaseQueue = null;
@@ -85,6 +89,11 @@
         mIBufferSend = receiver;
         return true;
     }
+
+    public void setExtraConfigureFlags(int flags) {
+        this.mExtraFlags = flags;
+    }
+
     /**
      * Setup of decoder
      *
@@ -94,17 +103,32 @@
         mSignalledError = false;
         mOutputStream = outputStream;
     }
+
+    /*
+     * This can be used to setup audio decoding, simulating audio playback.
+     */
+    public void setupDecoder(
+            boolean render, boolean useFrameReleaseQueue, int numInFramesRequired) {
+        mRender = render;
+        mUseFrameReleaseQueue = useFrameReleaseQueue;
+        mNumInFramesRequired = numInFramesRequired;
+        mSignalledError = false;
+        setupDecoder(null);
+    }
+
     public void setupDecoder(Surface surface, boolean render,
             boolean useFrameReleaseQueue, int frameRate) {
         setupDecoder(surface, render, useFrameReleaseQueue, frameRate, -1);
     }
+
     public void setupDecoder(Surface surface, boolean render,
             boolean useFrameReleaseQueue, int frameRate, int numInFramesRequired) {
         mSignalledError = false;
         mOutputStream = null;
         mSurface = surface;
         mRender = render;
-        if (useFrameReleaseQueue) {
+        mUseFrameReleaseQueue = useFrameReleaseQueue;
+        if (mUseFrameReleaseQueue) {
             Log.i(TAG, "Using FrameReleaseQueue with frameRate " + frameRate);
             mFrameReleaseQueue = new FrameReleaseQueue(mRender, frameRate);
         }
@@ -166,6 +190,18 @@
         public void onOutputFormatChanged(
                 @NonNull MediaCodec mediaCodec, @NonNull MediaFormat format) {
             Log.i(TAG, "Output format changed. Format: " + format.toString());
+            if (mUseFrameReleaseQueue
+                    && mFrameReleaseQueue == null && mMime.startsWith("audio/")) {
+                // start a frame release thread for this configuration.
+                int bytesPerSample = AudioFormat.getBytesPerSample(
+                        format.getInteger(MediaFormat.KEY_PCM_ENCODING,
+                                AudioFormat.ENCODING_PCM_16BIT));
+                int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
+                int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
+                mFrameReleaseQueue = new FrameReleaseQueue(
+                        mRender, sampleRate, channelCount, bytesPerSample);
+                mFrameReleaseQueue.setMediaCodec(mCodec);
+            }
         }
 
         @Override
@@ -223,11 +259,10 @@
         if (asyncMode) {
             setCallback(mCodec);
         }
-        int isEncoder = 0;
         if (DEBUG) {
             Log.d(TAG, "Media Format : " + format.toString());
         }
-        mCodec.configure(format, mSurface, null, isEncoder);
+        mCodec.configure(format, mSurface, null, mExtraFlags);
 
         mCodec.start();
         Log.i(TAG, "Codec started async mode ?  " + asyncMode);
@@ -395,8 +430,17 @@
             }
         }
         if (mFrameReleaseQueue != null) {
-            mFrameReleaseQueue.pushFrame(mNumOutputFrame, outputBufferId,
-                                            outputBufferInfo.presentationTimeUs);
+            if (mMime.startsWith("audio/")) {
+                try {
+                    ByteBuffer outputBuffer = mediaCodec.getOutputBuffer(outputBufferId);
+                    mFrameReleaseQueue.pushFrame(outputBufferId, outputBuffer.remaining());
+                } catch (Exception e) {
+                    Log.d(TAG, "Error in getting MediaCodec buffer" + e.toString());
+                }
+            } else {
+                mFrameReleaseQueue.pushFrame(mNumOutputFrame, outputBufferId,
+                                                outputBufferInfo.presentationTimeUs);
+            }
         } else if (mIBufferSend != null) {
             IBufferXfer.BufferXferInfo info = new IBufferXfer.BufferXferInfo();
             info.buf = mediaCodec.getOutputBuffer(outputBufferId);
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/FrameReleaseQueue.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/FrameReleaseQueue.java
index 20a2573..0861c2c 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/FrameReleaseQueue.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/FrameReleaseQueue.java
@@ -29,45 +29,89 @@
 
 public class FrameReleaseQueue {
     private static final String TAG = "FrameReleaseQueue";
+    private static final boolean DEBUG = false;
     private final String MIME_AV1 = "video/av01";
     private final int AV1_SUPERFRAME_DELAY = 6;
     private final int THRESHOLD_TIME = 5;
 
+    private final long HOUR_IN_MS = (60 * 60 * 1000L);
+    private final long MINUTE_IN_MS = (60 * 1000L);
+
     private MediaCodec mCodec;
     private LinkedBlockingQueue<FrameInfo> mFrameInfoQueue;
     private ReleaseThread mReleaseThread;
     private AtomicBoolean doFrameRelease = new AtomicBoolean(false);
-    private boolean mReleaseJobStarted = false;
+    private AtomicBoolean mReleaseJobStarted = new AtomicBoolean(false);
     private boolean mRender = false;
-    private int mWaitTime = 40; // milliseconds per frame
+    private long mWaitTime = 40; // milliseconds per frame
     private int mWaitTimeCorrection = 0;
     private int mCorrectionLoopCount;
-    private int firstReleaseTime = -1;
-    private int mAllowedDelayTime = THRESHOLD_TIME;
+    protected long firstReleaseTime = -1;
+    private long mAllowedDelayTime = THRESHOLD_TIME;
     private int mFrameDelay = 0;
     private final ScheduledExecutorService mScheduler = Executors.newScheduledThreadPool(1);
 
+    public FrameReleaseQueue(boolean render, int frameRate) {
+        this.mFrameInfoQueue = new LinkedBlockingQueue();
+        this.mReleaseThread = new ReleaseThread();
+        this.doFrameRelease.set(true);
+        this.mRender = render;
+        this.mWaitTime = 1000 / frameRate; // wait time in milliseconds per frame
+        int waitTimeRemainder = 1000 % frameRate;
+        int gcd = gcd(frameRate, waitTimeRemainder);
+        this.mCorrectionLoopCount = frameRate / gcd;
+        this.mWaitTimeCorrection = waitTimeRemainder / gcd;
+        Log.i(TAG, "Constructed FrameReleaseQueue with wait time " + this.mWaitTime + " ms");
+    }
+
+    public FrameReleaseQueue(boolean render, int sampleRate, int nChannels, int bytesPerChannel) {
+        this.mFrameInfoQueue = new LinkedBlockingQueue();
+        this.doFrameRelease.set(true);
+        this.mRender = render;
+        this.mReleaseThread = new AudioRendererThread(sampleRate, nChannels, bytesPerChannel);
+    }
 
     private static class FrameInfo {
         private int number;
         private int bufferId;
         private int displayTime;
+        private int bytes;
         public FrameInfo(int frameNumber, int frameBufferId, int frameDisplayTime) {
             this.number = frameNumber;
             this.bufferId = frameBufferId;
             this.displayTime = frameDisplayTime;
         }
+        public FrameInfo(int frameBufferId, int bytes) {
+            this.bufferId = frameBufferId;
+            this.bytes = bytes;
+        }
     }
 
     private class ReleaseThread extends Thread {
         private int mLoopCount = 0;
-        private int mNextReleaseTime = 0;
+        private long mNextReleaseTime = 0;
+
+        protected void printPlaybackTime() {
+            if (firstReleaseTime == -1) {
+                Log.d(TAG, "Playback Time not initialized");
+                return;
+            }
+            long curTime = getCurSysTime() - firstReleaseTime;
+            long hours = curTime / (HOUR_IN_MS);
+            curTime -= (hours * HOUR_IN_MS);
+            long min = curTime / MINUTE_IN_MS;
+            curTime -= (min * MINUTE_IN_MS);
+            Log.d(TAG, "Playback time: "
+                    + hours + "h "
+                    + min + "m "
+                    + (double)(curTime / (double)1000) +"s");
+        }
 
         @SuppressWarnings("FutureReturnValueIgnored")
         public void run() {
             /* Check if the release thread wakes up too late */
             if (mLoopCount != 0) {
-                int delta = getCurSysTime() - mNextReleaseTime;
+                long delta = getCurSysTime() - mNextReleaseTime;
                 if (delta >= THRESHOLD_TIME) {
                     Log.d(TAG, "Release thread wake up late by " + delta);
                     /* For accidental late wake up, we should relax the timestamp
@@ -93,8 +137,8 @@
                         popAndRelease(false);
                     } else {
                         mNextReleaseTime += mWaitTime;
-                        int curSysTime = getCurSysTime();
-                        int curMediaTime = curSysTime - firstReleaseTime;
+                        long curSysTime = getCurSysTime();
+                        long curMediaTime = curSysTime - firstReleaseTime;
                         while (curFrameInfo != null && curFrameInfo.displayTime > 0 &&
                                 curFrameInfo.displayTime <= curMediaTime) {
                             if (!((curMediaTime - curFrameInfo.displayTime) <= mAllowedDelayTime)) {
@@ -123,21 +167,86 @@
                     mNextReleaseTime += mWaitTimeCorrection;
                 }
                 mLoopCount += 1;
+            } else {
+                mReleaseJobStarted.set(false);
             }
         }
     }
 
-    public FrameReleaseQueue(boolean render, int frameRate) {
-        this.mFrameInfoQueue = new LinkedBlockingQueue();
-        this.mReleaseThread = new ReleaseThread();
-        this.doFrameRelease.set(true);
-        this.mRender = render;
-        this.mWaitTime = 1000 / frameRate; // wait time in milliseconds per frame
-        int waitTimeRemainder = 1000 % frameRate;
-        int gcd = gcd(frameRate, waitTimeRemainder);
-        this.mCorrectionLoopCount = frameRate / gcd;
-        this.mWaitTimeCorrection = waitTimeRemainder / gcd;
-        Log.i(TAG, "Constructed FrameReleaseQueue with wait time " + this.mWaitTime + " ms");
+    private class AudioRendererThread extends ReleaseThread {
+        private final int WAIT_FOR_BUFFER_IN_SEC = 2;
+        private double mTimeAdjustMs = 0;
+        private double mMsForByte = 0;
+        private double mExpectedWakeUpTime = 0;
+        private FrameInfo mCurrentFrameInfo;
+
+        AudioRendererThread(int sampleRate, int nChannels, int bytesPerChannel) {
+            if (DEBUG) {
+                Log.d(TAG, "sampleRate " + sampleRate
+                        + " nChannels " + nChannels
+                        + " bytesPerChannel " + bytesPerChannel);
+            }
+            this.mMsForByte = 1000 / (double)(sampleRate * nChannels * bytesPerChannel);
+        }
+
+        @Override
+        @SuppressWarnings("FutureReturnValueIgnored")
+        public void run() {
+            long curTime = getCurSysTime();
+            if (DEBUG) {
+                if (firstReleaseTime == -1) {
+                    firstReleaseTime = curTime;
+                }
+                printPlaybackTime();
+            }
+            if (mMsForByte == 0) {
+                Log.e(TAG, "Audio rendering not possible, no valid params");
+                return;
+            }
+            if (mCurrentFrameInfo != null) {
+                try {
+                    mCodec.releaseOutputBuffer(mCurrentFrameInfo.bufferId, mRender);
+                } catch (IllegalStateException e) {
+                    doFrameRelease.set(false);
+                    Log.e(TAG, "Threw InterruptedException on releaseOutputBuffer");
+                } finally {
+                    mCurrentFrameInfo = null;
+                }
+            }
+            boolean requestedSchedule = false;
+            try {
+                while (doFrameRelease.get() || mFrameInfoQueue.size() > 0) {
+                    mCurrentFrameInfo = mFrameInfoQueue.poll(
+                            WAIT_FOR_BUFFER_IN_SEC, TimeUnit.SECONDS);
+                    if (mCurrentFrameInfo != null) {
+                        mTimeAdjustMs = 0;
+                        if (mExpectedWakeUpTime != 0) {
+                            mTimeAdjustMs = mExpectedWakeUpTime - getCurSysTime();
+                        }
+                        double sleepTimeUs =
+                                (mMsForByte * mCurrentFrameInfo.bytes + mTimeAdjustMs) * 1000;
+                        mExpectedWakeUpTime = getCurSysTime() + (sleepTimeUs / 1000);
+                        if (DEBUG) {
+                            Log.d(TAG, " mExpectedWakeUpTime " + mExpectedWakeUpTime
+                                + " Waiting for " + (long)(sleepTimeUs) + "us"
+                                + " Now " + getCurSysTime()
+                                + " bytes " + mCurrentFrameInfo.bytes
+                                + " bufferID " + mCurrentFrameInfo.bufferId);
+                        }
+                        mScheduler.schedule(
+                                mReleaseThread,(long)(sleepTimeUs),TimeUnit.MICROSECONDS);
+                        requestedSchedule = true;
+                        break;
+                    }
+                }
+            } catch(InterruptedException e) {
+                Log.d(TAG, "Interrupted during poll wait");
+                doFrameRelease.set(false);
+            }
+            if (!requestedSchedule) {
+                mReleaseJobStarted.set(false);
+            }
+        }
     }
 
     private static int gcd(int a, int b) {
@@ -154,6 +263,19 @@
         }
     }
 
+    public boolean pushFrame(int frameBufferId, int bytes) {
+        FrameInfo info = new FrameInfo(frameBufferId, bytes);
+        boolean pushSuccess = mFrameInfoQueue.offer(info);
+        if (!pushSuccess) {
+            Log.e(TAG, "Failed to push frame with buffer id " + info.bufferId);
+            return false;
+        }
+        if (!mReleaseJobStarted.get()) {
+            mScheduler.execute(mReleaseThread);
+            mReleaseJobStarted.set(true);
+        }
+        return true;
+    }
     public boolean pushFrame(int frameNumber, int frameBufferId, long frameDisplayTime) {
         int frameDisplayTimeMs = (int)(frameDisplayTime/1000);
         FrameInfo curFrameInfo = new FrameInfo(frameNumber, frameBufferId, frameDisplayTimeMs);
@@ -163,16 +285,16 @@
             return false;
         }
 
-        if (!mReleaseJobStarted && frameNumber >= mFrameDelay) {
+        if (!mReleaseJobStarted.get() && frameNumber >= mFrameDelay) {
             mScheduler.execute(mReleaseThread);
-            mReleaseJobStarted = true;
+            mReleaseJobStarted.set(true);
             Log.i(TAG, "Started frame release thread");
         }
         return true;
     }
 
-    private int getCurSysTime() {
-        return (int)(System.nanoTime()/1000000);
+    private long getCurSysTime() {
+        return (long)(System.nanoTime() / 1000000L);
     }
 
     @SuppressWarnings("FutureReturnValueIgnored")
@@ -196,7 +318,7 @@
 
     public void stopFrameRelease() {
         doFrameRelease.set(false);
-        while (mFrameInfoQueue.size() > 0) {
+        while (mReleaseJobStarted.get()) {
             try {
                 TimeUnit.SECONDS.sleep(1);
             } catch (InterruptedException e) {
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXfer.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXfer.java
index c97a35c..bbc3d48 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXfer.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXfer.java
@@ -16,6 +16,8 @@
 
 package com.android.media.benchmark.library;
 import android.media.MediaCodec;
+
+import java.util.ArrayDeque;
 import java.nio.ByteBuffer;
 /**
  * interfaces that can be used to implement
@@ -26,10 +28,11 @@
       public ByteBuffer buf;
       public int idx;
       public Object obj;
-      int flag;
-      int bytesRead;
-      boolean isComplete = true;
-      long presentationTimeUs;
+      public ArrayDeque<MediaCodec.BufferInfo> infos;
+      public int flag;
+      public int bytesRead;
+      public boolean isComplete = true;
+      public long presentationTimeUs;
   }
 
   public interface IReceiveBuffer {
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXferImpl.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXferImpl.java
index 3e6cee1..c68ac8a 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXferImpl.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXferImpl.java
@@ -96,10 +96,10 @@
                       pBuf.info.buf.remaining() +" C:" + cBuf.info.buf.remaining());
               }
           }
+          cBuf.info.infos = pBuf.info.infos;
           cBuf.info.bytesRead = bytesRead;
           cBuf.info.presentationTimeUs = pBuf.info.presentationTimeUs;
           cBuf.info.flag = pBuf.info.flag;
-
           if (pBuf.rIface != null) {
               pBuf.rIface.receiveBuffer(pBuf.info);
           }
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/MultiAccessUnitBlockModelDecoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/MultiAccessUnitBlockModelDecoder.java
new file mode 100644
index 0000000..ed2defe
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/MultiAccessUnitBlockModelDecoder.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+package com.android.media.benchmark.library;
+
+import android.media.AudioFormat;
+import android.media.MediaCodec;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.os.Build;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import java.util.ArrayDeque;
+import java.util.Iterator;
+import java.util.List;
+
+import com.android.media.benchmark.library.CodecUtils;
+import com.android.media.benchmark.library.BlockModelDecoder;
+
+public class MultiAccessUnitBlockModelDecoder extends BlockModelDecoder {
+	private static final String TAG = MultiAccessUnitBlockModelDecoder.class.getSimpleName();
+    private final ArrayDeque<MediaCodec.BufferInfo> mInputInfos = new ArrayDeque<>();
+    private final boolean DEBUG = false;
+    protected int mMaxInputSize = 0;
+
+    public MultiAccessUnitBlockModelDecoder() {
+    	// empty
+    }
+
+    /**
+     * Decodes the given input buffer,
+     * provided valid list of buffer info and format are passed as inputs.
+     *
+     * @param inputBuffer     Decode the provided list of ByteBuffers
+     * @param inputBufferInfo List of buffer info corresponding to provided input buffers
+     * @param asyncMode       Will run on async implementation if true
+     * @param format          For creating the decoder if codec name is empty and configuring it
+     * @param codecName       Will create the decoder with codecName
+     * @return DECODE_SUCCESS if decode was successful, DECODE_DECODER_ERROR for fail,
+     *         DECODE_CREATE_ERROR for decoder not created
+     * @throws IOException if the codec cannot be created.
+     */
+    @Override
+    public int decode(@NonNull List<ByteBuffer> inputBuffer,
+        @NonNull List<MediaCodec.BufferInfo> inputBufferInfo, final boolean asyncMode,
+        @NonNull MediaFormat format, String codecName)
+        throws IOException, InterruptedException {
+        setExtraConfigureFlags(MediaCodec.CONFIGURE_FLAG_USE_BLOCK_MODEL);
+        configureMaxInputSize(format);
+        return super.decode(inputBuffer, inputBufferInfo, asyncMode, format, codecName);
+    }
+
+    protected void configureMaxInputSize(MediaFormat format) {
+        final String mime = format.getString(MediaFormat.KEY_MIME);
+        final int maxOutputSize = format.getNumber(
+            MediaFormat.KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE, 0).intValue();
+        final int maxInputSizeInBytes = format.getInteger(
+                MediaFormat.KEY_MAX_INPUT_SIZE);
+        mMaxInputSize = Math.max(maxInputSizeInBytes,
+                (int) (maxOutputSize * CodecUtils.getCompressionRatio(mime)));
+    }
+
+    @Override
+    public void setCallback(MediaCodec codec) {
+        mCodec.setCallback(new MediaCodec.Callback() {
+            boolean isUsingLargeFrameMode = false;
+
+            @Override
+            public void onInputBufferAvailable(
+                    @NonNull MediaCodec mediaCodec, int inputBufferId) {
+                try {
+                    mStats.addInputTime();
+                    if (isUsingLargeFrameMode) {
+                        onInputsAvailable(inputBufferId, mediaCodec);
+                    } else {
+                        onInputAvailable(inputBufferId, mediaCodec);
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    Log.e(TAG, e.toString());
+                }
+            }
+
+            @Override
+            public void onOutputBufferAvailable(@NonNull MediaCodec mediaCodec,
+                    int outputBufferId, @NonNull MediaCodec.BufferInfo bufferInfo) {
+                mStats.addOutputTime();
+                onOutputAvailable(mediaCodec, outputBufferId, bufferInfo);
+                if (mSawOutputEOS) {
+                    synchronized (mLock) { mLock.notify(); }
+                }
+            }
+
+            @Override
+            public void onOutputBuffersAvailable(
+                    @NonNull MediaCodec mediaCodec,
+                            int outputBufferId, @NonNull ArrayDeque<MediaCodec.BufferInfo> infos) {
+                int i = 0;
+                while(i++ < infos.size()) {
+                    mStats.addOutputTime();
+                }
+                onOutputsAvailable(mediaCodec, outputBufferId, infos);
+                if (mSawOutputEOS) {
+                    synchronized (mLock) { mLock.notify(); }
+                }
+            }
+
+            @Override
+            public void onOutputFormatChanged(
+                    @NonNull MediaCodec mediaCodec, @NonNull MediaFormat format) {
+                Log.i(TAG, "Output format changed. Format: " + format.toString());
+                final int maxOutputSize = format.getNumber(
+                            MediaFormat.KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE, 0).intValue();
+                isUsingLargeFrameMode = (maxOutputSize > 0);
+                configureMaxInputSize(format);
+                if (mUseFrameReleaseQueue && mFrameReleaseQueue == null) {
+                    int bytesPerSample = AudioFormat.getBytesPerSample(
+                            format.getInteger(MediaFormat.KEY_PCM_ENCODING,
+                                    AudioFormat.ENCODING_PCM_16BIT));
+                    int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
+                    int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
+                    mFrameReleaseQueue = new FrameReleaseQueue(
+                            mRender, sampleRate, channelCount, bytesPerSample);
+                    mFrameReleaseQueue.setMediaCodec(mCodec);
+                }
+            }
+
+            @Override
+            public void onError(
+                    @NonNull MediaCodec mediaCodec, @NonNull MediaCodec.CodecException e) {
+                mSignalledError = true;
+                Log.e(TAG, "Codec Error: " + e.toString());
+                e.printStackTrace();
+                synchronized (mLock) { mLock.notify(); }
+            }
+        });
+
+    }
+
+    protected void onInputsAvailable(int inputBufferId, MediaCodec mediaCodec) {
+        if (inputBufferId >= 0) {
+            mLinearInputBlock.allocateBlock(mediaCodec.getCanonicalName(), mMaxInputSize);
+            MediaCodec.BufferInfo bufInfo;
+            mInputInfos.clear();
+            int offset = 0;
+            while (mNumInFramesProvided < mNumInFramesRequired) {
+                bufInfo = mInputBufferInfo.get(mIndex);
+                mSawInputEOS = (bufInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+                int bufferSizeNeeded = mLinearInputBlock.getOffset() + bufInfo.size;
+                if (bufferSizeNeeded > mLinearInputBlock.getBufferCapacity()) {
+                    break;
+                }
+                mLinearInputBlock.getBuffer().put(mInputBuffer.get(mIndex).array());
+                mLinearInputBlock.setOffset(mLinearInputBlock.getOffset() + bufInfo.size);
+                bufInfo.offset = offset; offset += bufInfo.size;
+                mInputInfos.add(bufInfo);
+                mNumInFramesProvided++;
+                mIndex = mNumInFramesProvided % (mInputBufferInfo.size() - 1);
+
+            }
+            if (DEBUG) {
+                Log.d(TAG, "inputsAvailable ID : " + inputBufferId
+                        + " queued info size: " + mInputInfos.size()
+                        + " Total queued size: " + offset);
+            }
+            if (mNumInFramesProvided >= mNumInFramesRequired) {
+                mIndex = mInputBufferInfo.size() - 1;
+                bufInfo = mInputBufferInfo.get(mIndex);
+                int bufferSizeNeeded = mLinearInputBlock.getOffset() + bufInfo.size;
+                if (bufferSizeNeeded <= mLinearInputBlock.getBufferCapacity()) {
+                    if ((bufInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) == 0) {
+                        Log.e(TAG, "Error in EOS flag for Decoder");
+                    }
+                    mSawInputEOS = (bufInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+                    mLinearInputBlock.getBuffer().put(mInputBuffer.get(mIndex).array());
+                    mLinearInputBlock.setOffset(mLinearInputBlock.getOffset() + bufInfo.size);
+                    bufInfo.offset = offset; offset += bufInfo.size;
+                    //bufInfo.flags = codecFlags;
+                    mInputInfos.add(bufInfo);
+                    mNumInFramesProvided++;
+                }
+            }
+            if (mInputInfos.size() == 0) {
+                Log.d(TAG, " No inputs to queue");
+            } else {
+                mStats.addFrameSize(offset);
+                MediaCodec.QueueRequest request = mediaCodec.getQueueRequest(inputBufferId);
+                request.setMultiFrameLinearBlock(mLinearInputBlock.getBlock(), mInputInfos);
+                request.queue();
+            }
+        }
+    }
+
+    protected void onOutputsAvailable(MediaCodec mediaCodec, int outputBufferId,
+            ArrayDeque<MediaCodec.BufferInfo> infos) {
+        if (mSawOutputEOS || outputBufferId < 0) {
+            return;
+        }
+        MediaCodec.OutputFrame outFrame = mediaCodec.getOutputFrame(outputBufferId);
+        ByteBuffer outputBuffer = null;
+        try {
+            if (outFrame.getLinearBlock() != null) {
+                outputBuffer = outFrame.getLinearBlock().map();
+            }
+        } catch(IllegalStateException e) {
+            // buffer may not be linear, this is ok
+            // as we are handling non-linear buffers below.
+        }
+        if (mOutputStream != null) {
+            try {
+                if (outputBuffer != null) {
+                    byte[] bytesOutput = new byte[outputBuffer.remaining()];
+                    outputBuffer.get(bytesOutput);
+                    mOutputStream.write(bytesOutput);
+                    if (DEBUG) {
+                        Log.d(TAG, "Received outputs buffer size : " + outputBuffer.remaining()
+                                + " infos size " + infos.size());
+                    }
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+                Log.d(TAG, "Error Dumping File: Exception " + e.toString());
+            }
+        }
+        mNumOutputFrame += infos.size();
+        MediaCodec.BufferInfo last = infos.peekLast();
+        if (last != null) {
+            mSawOutputEOS |= ((last.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0);
+        }
+        int bytesRemaining = 0;
+        if (outputBuffer != null) {
+            bytesRemaining = outputBuffer.remaining();
+            outFrame.getLinearBlock().recycle();
+            outputBuffer = null;
+        }
+        if (mFrameReleaseQueue != null) {
+            mFrameReleaseQueue.pushFrame(outputBufferId, bytesRemaining);
+        } else if (mIBufferSend == null) {
+            mediaCodec.releaseOutputBuffer(outputBufferId, mRender);
+        }
+        if (mSawOutputEOS) {
+            Log.i(TAG, "Large frame - saw output EOS");
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/MultiAccessUnitDecoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/MultiAccessUnitDecoder.java
index cb92f06..fd8859b 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/MultiAccessUnitDecoder.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/MultiAccessUnitDecoder.java
@@ -18,6 +18,7 @@
 
 import android.view.Surface;
 
+import android.media.AudioFormat;
 import android.media.MediaCodec;
 import android.media.MediaCodec.BufferInfo;
 import android.media.MediaFormat;
@@ -91,8 +92,18 @@
                     @NonNull MediaCodec mediaCodec, @NonNull MediaFormat format) {
                 Log.i(TAG, "Output format changed. Format: " + format.toString());
                 final int maxOutputSize = format.getNumber(
-                        MediaFormat.KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE, 0).intValue();
+                            MediaFormat.KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE, 0).intValue();
                 isUsingLargeFrameMode = (maxOutputSize > 0);
+                if (mUseFrameReleaseQueue && mFrameReleaseQueue == null) {
+                    int bytesPerSample = AudioFormat.getBytesPerSample(
+                            format.getInteger(MediaFormat.KEY_PCM_ENCODING,
+                                    AudioFormat.ENCODING_PCM_16BIT));
+                    int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
+                    int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
+                    mFrameReleaseQueue = new FrameReleaseQueue(
+                            mRender, sampleRate, channelCount, bytesPerSample);
+                    mFrameReleaseQueue.setMediaCodec(mCodec);
+                }
             }
 
             @Override
@@ -177,30 +188,6 @@
         if (mSawOutputEOS || outputBufferId < 0) {
             return;
         }
-        Iterator<BufferInfo> iter = infos.iterator();
-        while (iter.hasNext()) {
-            BufferInfo bufferInfo = iter.next();
-            mNumOutputFrame++;
-            if (DEBUG) {
-                Log.d(TAG,
-                        "In OutputBufferAvailable ,"
-                                + " output frame number = " + mNumOutputFrame
-                                + " timestamp = " + bufferInfo.presentationTimeUs
-                                + " size = " + bufferInfo.size);
-            }
-            if (mIBufferSend != null) {
-                IBufferXfer.BufferXferInfo info = new IBufferXfer.BufferXferInfo();
-                info.buf = mc.getOutputBuffer(outputBufferId);
-                info.idx = outputBufferId;
-                info.obj = mc;
-                info.bytesRead = bufferInfo.size;
-                info.presentationTimeUs = bufferInfo.presentationTimeUs;
-                info.flag = bufferInfo.flags;
-                info.isComplete = iter.hasNext() ? false : true;
-                mIBufferSend.sendBuffer(this, info);
-            }
-            mSawOutputEOS |= (bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
-        }
         if (mOutputStream != null) {
             try {
                 ByteBuffer outputBuffer = mc.getOutputBuffer(outputBufferId);
@@ -212,12 +199,27 @@
                 Log.d(TAG, "Error Dumping File: Exception " + e.toString());
             }
         }
-        if (mIBufferSend == null) {
+        mNumOutputFrame += infos.size();
+        MediaCodec.BufferInfo last = infos.peekLast();
+        if (last != null) {
+            mSawOutputEOS |= ((last.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0);
+        }
+        if (mIBufferSend != null) {
+            IBufferXfer.BufferXferInfo info = new IBufferXfer.BufferXferInfo();
+            info.buf = mc.getOutputBuffer(outputBufferId);
+            info.idx = outputBufferId;
+            info.obj = mc;
+            info.infos = infos;
+            mIBufferSend.sendBuffer(this, info);
+        } else if (mFrameReleaseQueue != null) {
+            ByteBuffer outputBuffer = mc.getOutputBuffer(outputBufferId);
+            mFrameReleaseQueue.pushFrame(
+                    outputBufferId, outputBuffer.remaining());
+        } else {
             mc.releaseOutputBuffer(outputBufferId, mRender);
         }
         if (mSawOutputEOS) {
             Log.i(TAG, "Large frame - saw output EOS");
         }
-        // we don't support frame release queue for large audio frame
     }
 }
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 70fa2ca..a1a0634 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -4121,7 +4121,7 @@
                                                        frameCountToBeReady,
                                                        track->getSpeed(),
                                                        1.f /* volume */,
-                                                       track->getPortMute() /* muted */);
+                                                       false /* muted */);
         status = patchTrack->initCheck();
         if (status != NO_ERROR) {
             ALOGE("Secondary output patchTrack init failed: %d", status);
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 13db5b3..62cb6c7 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -3556,6 +3556,9 @@
                                                             bool enabled,
                                                             audio_stream_type_t streamToDriveAbs)
 {
+    ALOGI("%s: deviceType 0x%X, enabled %d, streamToDriveAbs %d", __func__, deviceType, enabled,
+          streamToDriveAbs);
+
     if (!enabled) {
         mAbsoluteVolumeDrivingStreams.erase(deviceType);
         return NO_ERROR;
@@ -8312,9 +8315,9 @@
     float volumeDb = curves.volIndexToDb(deviceCategory, index);
 
     if (com_android_media_audio_abs_volume_index_fix()) {
-        if (mAbsoluteVolumeDrivingStreams.find(volumeDevice) !=
-            mAbsoluteVolumeDrivingStreams.end()) {
-            audio_attributes_t attributesToDriveAbs = mAbsoluteVolumeDrivingStreams[volumeDevice];
+        const auto it = mAbsoluteVolumeDrivingStreams.find(volumeDevice);
+        if (it != mAbsoluteVolumeDrivingStreams.end()) {
+            audio_attributes_t attributesToDriveAbs = it->second;
             auto groupToDriveAbs = mEngine->getVolumeGroupForAttributes(attributesToDriveAbs);
             if (groupToDriveAbs == VOLUME_GROUP_NONE) {
                 ALOGD("%s: no group matching with %s", __FUNCTION__,
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 8fbe1cc..0442cae 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -1121,8 +1121,15 @@
 Status AudioPolicyService::setDeviceAbsoluteVolumeEnabled(const AudioDevice& deviceAidl,
                                                           bool enabled,
                                                           AudioStreamType streamToDriveAbsAidl) {
-    audio_stream_type_t streamToDriveAbs = VALUE_OR_RETURN_BINDER_STATUS(
-            aidl2legacy_AudioStreamType_audio_stream_type_t(streamToDriveAbsAidl));
+    ALOGI("%s: deviceAidl %s, enabled %d, streamToDriveAbsAidl %d", __func__,
+          deviceAidl.toString().c_str(), enabled, streamToDriveAbsAidl);
+
+    audio_stream_type_t streamToDriveAbs = AUDIO_STREAM_DEFAULT;
+    if (enabled) {
+        streamToDriveAbs = VALUE_OR_RETURN_BINDER_STATUS(
+                aidl2legacy_AudioStreamType_audio_stream_type_t(streamToDriveAbsAidl));
+    }
+
     audio_devices_t deviceType;
     std::string address;
     RETURN_BINDER_STATUS_IF_ERROR(
@@ -1136,9 +1143,7 @@
             : settingsAllowed())) {
         return binderStatusFromStatusT(PERMISSION_DENIED);
     }
-    if (uint32_t(streamToDriveAbs) >= AUDIO_STREAM_PUBLIC_CNT) {
-        return binderStatusFromStatusT(BAD_VALUE);
-    }
+
     audio_utils::lock_guard _l(mMutex);
     AutoCallerClear acc;
     return binderStatusFromStatusT(
diff --git a/services/camera/virtualcamera/VirtualCameraRenderThread.h b/services/camera/virtualcamera/VirtualCameraRenderThread.h
index a35eea1..1fb4e84 100644
--- a/services/camera/virtualcamera/VirtualCameraRenderThread.h
+++ b/services/camera/virtualcamera/VirtualCameraRenderThread.h
@@ -221,8 +221,8 @@
   std::mutex mLock;
   std::deque<std::unique_ptr<ProcessCaptureRequestTask>> mQueue GUARDED_BY(mLock);
   std::condition_variable mCondVar;
-  volatile bool mTextureUpdateRequested GUARDED_BY(mLock);
-  volatile bool mPendingExit GUARDED_BY(mLock);
+  volatile bool GUARDED_BY(mLock) mTextureUpdateRequested = false;
+  volatile bool GUARDED_BY(mLock) mPendingExit = false;
 
   // Acquisition timestamp of last frame.
   std::atomic<uint64_t> mLastAcquisitionTimestampNanoseconds;