Merge "stagefright: notify async release complete" into rvc-dev
diff --git a/MainlineFiles.cfg b/MainlineFiles.cfg
new file mode 100644
index 0000000..37d714c
--- /dev/null
+++ b/MainlineFiles.cfg
@@ -0,0 +1,34 @@
+# 
+# mainline files for frameworks/av
+#
+# ignore comment (#) lines and blank lines
+# rest are path prefixes starting at root of the project
+# (so OWNERS, not frameworks/av/OWNERS)
+# 
+# path
+# INCLUDE path
+# EXCLUDE path
+#
+# 'path' and 'INCLUDE path' are identical -- they both indicate that this path
+# is part of mainline
+# EXCLUDE indicates that this is not part of mainline,
+# so 'foo/' and 'EXCLUDE foo/nope'
+# means everything under foo/ is part of mainline EXCEPT foo/nope.
+# INCLUDE/EXCLUDE/INCLUDE nested structuring is not supported
+#
+# matching is purely prefix
+# so 'foo' will match 'foo', 'foo.c', 'foo/bar/baz'
+# if you want to exclude a directory, best to use a pattern like "foo/"
+#
+
+media/codec2/components/
+media/codecs/
+media/extractors/
+media/libstagefright/codecs/amrnb/
+media/libstagefright/codecs/amrwb/
+media/libstagefright/codecs/amrwbenc/
+media/libstagefright/codecs/common/
+media/libstagefright/codecs/flac/
+media/libstagefright/codecs/m4v_h263/
+media/libstagefright/codecs/mp3dec/
+media/libstagefright/mpeg2ts
diff --git a/apex/testing/Android.bp b/apex/testing/Android.bp
index 376d3e4..a04ab3f 100644
--- a/apex/testing/Android.bp
+++ b/apex/testing/Android.bp
@@ -17,6 +17,7 @@
     manifest: "test_manifest.json",
     file_contexts: ":com.android.media-file_contexts",
     defaults: ["com.android.media-defaults"],
+    prebuilts: ["sdkinfo_45"],
     installable: false,
 }
 
diff --git a/camera/CaptureResult.cpp b/camera/CaptureResult.cpp
index 9cbfdb0..755051c 100644
--- a/camera/CaptureResult.cpp
+++ b/camera/CaptureResult.cpp
@@ -49,6 +49,9 @@
         }
         errorPhysicalCameraId = cameraId;
     }
+    parcel->readInt64(&lastCompletedRegularFrameNumber);
+    parcel->readInt64(&lastCompletedReprocessFrameNumber);
+    parcel->readInt64(&lastCompletedZslFrameNumber);
 
     return OK;
 }
@@ -76,6 +79,9 @@
     } else {
         parcel->writeBool(false);
     }
+    parcel->writeInt64(lastCompletedRegularFrameNumber);
+    parcel->writeInt64(lastCompletedReprocessFrameNumber);
+    parcel->writeInt64(lastCompletedZslFrameNumber);
 
     return OK;
 }
diff --git a/camera/include/camera/CaptureResult.h b/camera/include/camera/CaptureResult.h
index dc3d282..f163c1e 100644
--- a/camera/include/camera/CaptureResult.h
+++ b/camera/include/camera/CaptureResult.h
@@ -76,6 +76,34 @@
      */
     String16  errorPhysicalCameraId;
 
+    // The last completed frame numbers shouldn't be checked in onResultReceived() and notifyError()
+    // because the output buffers could be arriving after onResultReceived() and
+    // notifyError(). Given this constraint, we check it for each
+    // onCaptureStarted, and if there is no further onCaptureStarted(),
+    // check for onDeviceIdle() to clear out all pending frame numbers.
+
+    /**
+     * The latest regular request frameNumber for which all buffers and capture result have been
+     * returned or notified as an BUFFER_ERROR/RESULT_ERROR/REQUEST_ERROR. -1 if
+     * none has completed.
+     */
+    int64_t lastCompletedRegularFrameNumber;
+
+    /**
+     * The latest reprocess request frameNumber for which all buffers and capture result have been
+     * returned or notified as an BUFFER_ERROR/RESULT_ERROR/REQUEST_ERROR. -1 if
+     * none has completed.
+     */
+    int64_t lastCompletedReprocessFrameNumber;
+
+    /**
+     * The latest Zsl request frameNumber for which all buffers and capture result have been
+     * returned or notified as an BUFFER_ERROR/RESULT_ERROR/REQUEST_ERROR. -1 if
+     * none has completed.
+     */
+    int64_t lastCompletedZslFrameNumber;
+
+
     /**
      * Constructor initializes object as invalid by setting requestId to be -1.
      */
@@ -87,7 +115,10 @@
           frameNumber(0),
           partialResultCount(0),
           errorStreamId(-1),
-          errorPhysicalCameraId() {
+          errorPhysicalCameraId(),
+          lastCompletedRegularFrameNumber(-1),
+          lastCompletedReprocessFrameNumber(-1),
+          lastCompletedZslFrameNumber(-1) {
     }
 
     /**
diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp
index 0d7180a..c15c5a5 100644
--- a/camera/ndk/impl/ACameraDevice.cpp
+++ b/camera/ndk/impl/ACameraDevice.cpp
@@ -1336,56 +1336,97 @@
 void
 CameraDevice::checkAndFireSequenceCompleteLocked() {
     int64_t completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
-    //std::map<int, int64_t> mSequenceLastFrameNumberMap;
     auto it = mSequenceLastFrameNumberMap.begin();
     while (it != mSequenceLastFrameNumberMap.end()) {
         int sequenceId = it->first;
-        int64_t lastFrameNumber = it->second;
-        bool seqCompleted = false;
-        bool hasCallback  = true;
+        int64_t lastFrameNumber = it->second.lastFrameNumber;
+        bool hasCallback = true;
+
+        if (mRemote == nullptr) {
+            ALOGW("Camera %s closed while checking sequence complete", getId());
+            return;
+        }
+        ALOGV("%s: seq %d's last frame number %" PRId64 ", completed %" PRId64,
+                __FUNCTION__, sequenceId, lastFrameNumber, completedFrameNumber);
+        if (!it->second.isSequenceCompleted) {
+            // Check if there is callback for this sequence
+            // This should not happen because we always register callback (with nullptr inside)
+            if (mSequenceCallbackMap.count(sequenceId) == 0) {
+                ALOGW("No callback found for sequenceId %d", sequenceId);
+                hasCallback = false;
+            }
+
+            if (lastFrameNumber <= completedFrameNumber) {
+                ALOGV("Mark sequenceId %d as sequence completed", sequenceId);
+                it->second.isSequenceCompleted = true;
+            }
+
+            if (it->second.isSequenceCompleted && hasCallback) {
+                auto cbIt = mSequenceCallbackMap.find(sequenceId);
+                CallbackHolder cbh = cbIt->second;
+
+                // send seq complete callback
+                sp<AMessage> msg = new AMessage(kWhatCaptureSeqEnd, mHandler);
+                msg->setPointer(kContextKey, cbh.mContext);
+                msg->setObject(kSessionSpKey, cbh.mSession);
+                msg->setPointer(kCallbackFpKey, (void*) cbh.mOnCaptureSequenceCompleted);
+                msg->setInt32(kSequenceIdKey, sequenceId);
+                msg->setInt64(kFrameNumberKey, lastFrameNumber);
+
+                // Clear the session sp before we send out the message
+                // This will guarantee the rare case where the message is processed
+                // before cbh goes out of scope and causing we call the session
+                // destructor while holding device lock
+                cbh.mSession.clear();
+                postSessionMsgAndCleanup(msg);
+            }
+        }
+
+        if (it->second.isSequenceCompleted && it->second.isInflightCompleted) {
+            if (mSequenceCallbackMap.find(sequenceId) != mSequenceCallbackMap.end()) {
+                mSequenceCallbackMap.erase(sequenceId);
+            }
+            it = mSequenceLastFrameNumberMap.erase(it);
+            ALOGV("%s: Remove holder for sequenceId %d", __FUNCTION__, sequenceId);
+        } else {
+            ++it;
+        }
+    }
+}
+
+void
+CameraDevice::removeCompletedCallbackHolderLocked(int64_t lastCompletedRegularFrameNumber) {
+    auto it = mSequenceLastFrameNumberMap.begin();
+    while (it != mSequenceLastFrameNumberMap.end()) {
+        int sequenceId = it->first;
+        int64_t lastFrameNumber = it->second.lastFrameNumber;
 
         if (mRemote == nullptr) {
             ALOGW("Camera %s closed while checking sequence complete", getId());
             return;
         }
 
-        // Check if there is callback for this sequence
-        // This should not happen because we always register callback (with nullptr inside)
-        if (mSequenceCallbackMap.count(sequenceId) == 0) {
-            ALOGW("No callback found for sequenceId %d", sequenceId);
-            hasCallback = false;
-        }
+        ALOGV("%s: seq %d's last frame number %" PRId64
+                ", completed inflight frame number %" PRId64,
+                __FUNCTION__, sequenceId, lastFrameNumber,
+                lastCompletedRegularFrameNumber);
+        if (lastFrameNumber <= lastCompletedRegularFrameNumber) {
+            if (it->second.isSequenceCompleted) {
+                // Check if there is callback for this sequence
+                // This should not happen because we always register callback (with nullptr inside)
+                if (mSequenceCallbackMap.count(sequenceId) == 0) {
+                    ALOGW("No callback found for sequenceId %d", sequenceId);
+                } else {
+                    mSequenceCallbackMap.erase(sequenceId);
+                }
 
-        if (lastFrameNumber <= completedFrameNumber) {
-            ALOGV("seq %d reached last frame %" PRId64 ", completed %" PRId64,
-                    sequenceId, lastFrameNumber, completedFrameNumber);
-            seqCompleted = true;
-        }
-
-        if (seqCompleted && hasCallback) {
-            // remove callback holder from callback map
-            auto cbIt = mSequenceCallbackMap.find(sequenceId);
-            CallbackHolder cbh = cbIt->second;
-            mSequenceCallbackMap.erase(cbIt);
-            // send seq complete callback
-            sp<AMessage> msg = new AMessage(kWhatCaptureSeqEnd, mHandler);
-            msg->setPointer(kContextKey, cbh.mContext);
-            msg->setObject(kSessionSpKey, cbh.mSession);
-            msg->setPointer(kCallbackFpKey, (void*) cbh.mOnCaptureSequenceCompleted);
-            msg->setInt32(kSequenceIdKey, sequenceId);
-            msg->setInt64(kFrameNumberKey, lastFrameNumber);
-
-            // Clear the session sp before we send out the message
-            // This will guarantee the rare case where the message is processed
-            // before cbh goes out of scope and causing we call the session
-            // destructor while holding device lock
-            cbh.mSession.clear();
-            postSessionMsgAndCleanup(msg);
-        }
-
-        // No need to track sequence complete if there is no callback registered
-        if (seqCompleted || !hasCallback) {
-            it = mSequenceLastFrameNumberMap.erase(it);
+                it = mSequenceLastFrameNumberMap.erase(it);
+                ALOGV("%s: Remove holder for sequenceId %d", __FUNCTION__, sequenceId);
+            } else {
+                ALOGV("Mark sequenceId %d as inflight completed", sequenceId);
+                it->second.isInflightCompleted = true;
+                ++it;
+            }
         } else {
             ++it;
         }
@@ -1480,6 +1521,9 @@
         return ret;
     }
 
+    dev->removeCompletedCallbackHolderLocked(
+             std::numeric_limits<int64_t>::max()/*lastCompletedRegularFrameNumber*/);
+
     if (dev->mIdle) {
         // Already in idle state. Possibly other thread did waitUntilIdle
         return ret;
@@ -1522,6 +1566,9 @@
         return ret;
     }
 
+    dev->removeCompletedCallbackHolderLocked(
+            resultExtras.lastCompletedRegularFrameNumber);
+
     int sequenceId = resultExtras.requestId;
     int32_t burstId = resultExtras.burstId;
 
diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h
index 6c2ceb3..2f2299f 100644
--- a/camera/ndk/impl/ACameraDevice.h
+++ b/camera/ndk/impl/ACameraDevice.h
@@ -267,8 +267,15 @@
     static const int REQUEST_ID_NONE = -1;
     int mRepeatingSequenceId = REQUEST_ID_NONE;
 
-    // sequence id -> last frame number map
-    std::map<int, int64_t> mSequenceLastFrameNumberMap;
+    // sequence id -> last frame number holder map
+    struct RequestLastFrameNumberHolder {
+        int64_t lastFrameNumber;
+        bool isSequenceCompleted = false;
+        bool isInflightCompleted = false;
+        RequestLastFrameNumberHolder(int64_t lastFN) :
+                lastFrameNumber(lastFN) {}
+    };
+    std::map<int, RequestLastFrameNumberHolder> mSequenceLastFrameNumberMap;
 
     struct CallbackHolder {
         CallbackHolder(sp<ACameraCaptureSession>          session,
@@ -338,6 +345,7 @@
 
     void checkRepeatingSequenceCompleteLocked(const int sequenceId, const int64_t lastFrameNumber);
     void checkAndFireSequenceCompleteLocked();
+    void removeCompletedCallbackHolderLocked(int64_t lastCompletedRegularFrameNumber);
 
     // Misc variables
     int32_t mShadingMapSize[2];   // const after constructor
diff --git a/media/bufferpool/2.0/Android.bp b/media/bufferpool/2.0/Android.bp
index 557b7ef..536f75e 100644
--- a/media/bufferpool/2.0/Android.bp
+++ b/media/bufferpool/2.0/Android.bp
@@ -30,6 +30,7 @@
     name: "libstagefright_bufferpool@2.0.1",
     defaults: ["libstagefright_bufferpool@2.0-default"],
     vendor_available: true,
+    min_sdk_version: "29",
     // TODO: b/147147992
     double_loadable: true,
     cflags: [
diff --git a/media/codec2/core/Android.bp b/media/codec2/core/Android.bp
index 1f9d7ab..ce1c9ac 100644
--- a/media/codec2/core/Android.bp
+++ b/media/codec2/core/Android.bp
@@ -1,12 +1,14 @@
 cc_library_headers {
     name: "libcodec2_headers",
     vendor_available: true,
+    min_sdk_version: "29",
     export_include_dirs: ["include"],
 }
 
 cc_library_shared {
     name: "libcodec2",
     vendor_available: true,
+    min_sdk_version: "29",
     vndk: {
         enabled: true,
     },
diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h
index 9fc0e17..29bccd5 100644
--- a/media/codec2/core/include/C2Config.h
+++ b/media/codec2/core/include/C2Config.h
@@ -278,16 +278,19 @@
         C2ApiLevelSetting;
 constexpr char C2_PARAMKEY_API_LEVEL[] = "api.level";
 
-enum C2Config::api_feature_t : uint64_t {
+C2ENUM(C2Config::api_feature_t, uint64_t,
     API_REFLECTION       = (1U << 0),  ///< ability to list supported parameters
     API_VALUES           = (1U << 1),  ///< ability to list supported values for each parameter
     API_CURRENT_VALUES   = (1U << 2),  ///< ability to list currently supported values for each parameter
     API_DEPENDENCY       = (1U << 3),  ///< have a defined parameter dependency
 
+    API_SAME_INPUT_BUFFER = (1U << 16),   ///< supporting multiple input buffers
+                                          ///< backed by the same allocation
+
     API_STREAMS          = (1ULL << 32),  ///< supporting variable number of streams
 
-    API_TUNNELING        = (1ULL << 48),  ///< tunneling API
-};
+    API_TUNNELING        = (1ULL << 48)   ///< tunneling API
+)
 
 // read-only
 typedef C2GlobalParam<C2Setting, C2SimpleValueStruct<C2Config::api_feature_t>, kParamIndexApiFeatures>
diff --git a/media/codec2/hidl/1.0/utils/Android.bp b/media/codec2/hidl/1.0/utils/Android.bp
index 75c9424..3b73350 100644
--- a/media/codec2/hidl/1.0/utils/Android.bp
+++ b/media/codec2/hidl/1.0/utils/Android.bp
@@ -48,6 +48,7 @@
 cc_library {
     name: "libcodec2_hidl@1.0",
     vendor_available: true,
+    min_sdk_version: "29",
 
     defaults: ["hidl_defaults"],
 
diff --git a/media/codec2/hidl/1.0/vts/functional/Android.bp b/media/codec2/hidl/1.0/vts/functional/Android.bp
index cd3be81..5ea4825 100644
--- a/media/codec2/hidl/1.0/vts/functional/Android.bp
+++ b/media/codec2/hidl/1.0/vts/functional/Android.bp
@@ -91,6 +91,14 @@
         "res/bbb_av1_176_144.info",
         "res/bbb_vp9_704x480_280kbps_24fps_altref_2.vp9",
         "res/bbb_vp9_704x480_280kbps_24fps_altref_2.info",
+        "res/bbb_avc_176x144_300kbps_60fps_chksum.md5",
+        "res/bbb_avc_640x360_768kbps_30fps_chksum.md5",
+        "res/bbb_hevc_176x144_176kbps_60fps_chksum.md5",
+        "res/bbb_hevc_640x360_1600kbps_30fps_chksum.md5",
+        "res/bbb_vp8_640x360_2mbps_30fps_chksm.md5",
+        "res/bbb_vp9_640x360_1600kbps_30fps_chksm.md5",
+        "res/bbb_av1_640_360_chksum.md5",
+        "res/bbb_av1_176_144_chksm.md5",
     ],
 }
 
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
index 264abba..3a47ae9 100644
--- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
@@ -22,7 +22,6 @@
 #include <hidl/GtestPrinter.h>
 #include <stdio.h>
 #include <algorithm>
-#include <fstream>
 
 #include <C2AllocatorIon.h>
 #include <C2Buffer.h>
@@ -35,15 +34,11 @@
 
 #include "media_c2_hidl_test_common.h"
 
-struct FrameInfo {
-    int bytesCount;
-    uint32_t flags;
-    int64_t timestamp;
-};
-
 static std::vector<std::tuple<std::string, std::string, std::string, std::string>>
         kDecodeTestParameters;
 
+static std::vector<std::tuple<std::string, std::string, std::string>> kCsdFlushTestParameters;
+
 // Resource directory
 static std::string sResourceDir = "";
 
@@ -105,6 +100,7 @@
         mEos = false;
         mFramesReceived = 0;
         mTimestampUs = 0u;
+        mWorkResult = C2_OK;
         mTimestampDevTest = false;
         if (mCompName == unknown_comp) mDisableTest = true;
         if (mDisableTest) std::cout << "[   WARN   ] Test Disabled \n";
@@ -121,6 +117,8 @@
     // Get the test parameters from GetParam call.
     virtual void getParams() {}
 
+    virtual void validateTimestampList(int32_t* bitStreamInfo);
+
     struct outputMetaData {
         uint64_t timestampUs;
         uint32_t rangeLength;
@@ -131,6 +129,7 @@
             if (!work->worklets.empty()) {
                 // For decoder components current timestamp always exceeds
                 // previous timestamp
+                mWorkResult |= work->result;
                 bool codecConfig = ((work->worklets.front()->output.flags &
                                      C2FrameData::FLAG_CODEC_CONFIG) != 0);
                 if (!codecConfig && !work->worklets.front()->output.buffers.empty()) {
@@ -182,6 +181,8 @@
     bool mDisableTest;
     bool mTimestampDevTest;
     standardComp mCompName;
+
+    int32_t mWorkResult;
     uint64_t mTimestampUs;
     uint32_t mFramesReceived;
     std::list<uint64_t> mFlushedIndices;
@@ -457,6 +458,31 @@
     }
 }
 
+void Codec2AudioDecHidlTestBase::validateTimestampList(int32_t* bitStreamInfo) {
+    uint32_t samplesReceived = 0;
+    // Update SampleRate and ChannelCount
+    ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+    int32_t nSampleRate = bitStreamInfo[0];
+    int32_t nChannels = bitStreamInfo[1];
+    std::list<uint64_t>::iterator itIn = mTimestampUslist.begin();
+    auto itOut = oBufferMetaData.begin();
+    EXPECT_EQ(*itIn, itOut->timestampUs);
+    uint64_t expectedTimeStamp = *itIn;
+    while (itOut != oBufferMetaData.end()) {
+        EXPECT_EQ(expectedTimeStamp, itOut->timestampUs);
+        if (expectedTimeStamp != itOut->timestampUs) break;
+        // buffer samples = ((total bytes) / (ac * (bits per sample / 8))
+        samplesReceived += ((itOut->rangeLength) / (nChannels * 2));
+        expectedTimeStamp = samplesReceived * 1000000ll / nSampleRate;
+        itOut++;
+    }
+    itIn = mTimestampUslist.end();
+    --itIn;
+    EXPECT_GT(expectedTimeStamp, *itIn);
+    oBufferMetaData.clear();
+    mTimestampUslist.clear();
+}
+
 TEST_P(Codec2AudioDecHidlTest, validateCompName) {
     if (mDisableTest) GTEST_SKIP() << "Test is disabled";
     ALOGV("Checks if the given component is a valid audio component");
@@ -493,7 +519,7 @@
     bool signalEOS = !std::get<3>(GetParam()).compare("true");
     mTimestampDevTest = true;
     char mURL[512], info[512];
-    std::ifstream eleStream, eleInfo;
+    android::Vector<FrameInfo> Info;
 
     strcpy(mURL, sResourceDir.c_str());
     strcpy(info, sResourceDir.c_str());
@@ -503,21 +529,9 @@
         return;
     }
 
-    eleInfo.open(info);
-    ASSERT_EQ(eleInfo.is_open(), true);
-    android::Vector<FrameInfo> Info;
-    int bytesCount = 0;
-    uint32_t flags = 0;
-    uint32_t timestamp = 0;
-    while (1) {
-        if (!(eleInfo >> bytesCount)) break;
-        eleInfo >> flags;
-        eleInfo >> timestamp;
-        bool codecConfig = ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0;
-        if (mTimestampDevTest && !codecConfig) mTimestampUslist.push_back(timestamp);
-        Info.push_back({bytesCount, flags, timestamp});
-    }
-    eleInfo.close();
+    int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
+    ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << info;
+
     // Reset total no of frames received
     mFramesReceived = 0;
     mTimestampUs = 0;
@@ -534,6 +548,7 @@
     }
     ASSERT_EQ(mComponent->start(), C2_OK);
     ALOGV("mURL : %s", mURL);
+    std::ifstream eleStream;
     eleStream.open(mURL, std::ifstream::binary);
     ASSERT_EQ(eleStream.is_open(), true);
     ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
@@ -550,7 +565,7 @@
     }
     // blocking call to ensures application to Wait till all the inputs are
     // consumed
-    ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+    waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
     eleStream.close();
     if (mFramesReceived != infoSize) {
         ALOGE("Input buffer count and Output buffer count mismatch");
@@ -558,32 +573,12 @@
         ASSERT_TRUE(false);
     }
     ASSERT_EQ(mEos, true);
+
     if (mTimestampDevTest) {
-        uint64_t expTs;
-        uint32_t samplesReceived = 0;
-        // Update SampleRate and ChannelCount
-        ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
-        int nSampleRate = bitStreamInfo[0];
-        int nChannels = bitStreamInfo[1];
-        std::list<uint64_t>::iterator itIn = mTimestampUslist.begin();
-        auto itOut = oBufferMetaData.begin();
-        EXPECT_EQ(*itIn, itOut->timestampUs);
-        expTs = *itIn;
-        while (itOut != oBufferMetaData.end()) {
-            EXPECT_EQ(expTs, itOut->timestampUs);
-            if (expTs != itOut->timestampUs) break;
-            // buffer samples = ((total bytes) / (ac * (bits per sample / 8))
-            samplesReceived += ((itOut->rangeLength) / (nChannels * 2));
-            expTs = samplesReceived * 1000000ll / nSampleRate;
-            itOut++;
-        }
-        itIn = mTimestampUslist.end();
-        --itIn;
-        EXPECT_GT(expTs, *itIn);
-        oBufferMetaData.clear();
-        mTimestampUslist.clear();
+        validateTimestampList(bitStreamInfo);
     }
     ASSERT_EQ(mComponent->stop(), C2_OK);
+    ASSERT_EQ(mWorkResult, C2_OK);
 }
 
 // thumbnail test
@@ -592,25 +587,15 @@
     if (mDisableTest) GTEST_SKIP() << "Test is disabled";
 
     char mURL[512], info[512];
-    std::ifstream eleStream, eleInfo;
+    android::Vector<FrameInfo> Info;
 
     strcpy(mURL, sResourceDir.c_str());
     strcpy(info, sResourceDir.c_str());
     GetURLForComponent(mCompName, mURL, info);
 
-    eleInfo.open(info);
-    ASSERT_EQ(eleInfo.is_open(), true);
-    android::Vector<FrameInfo> Info;
-    int bytesCount = 0;
-    uint32_t flags = 0;
-    uint32_t timestamp = 0;
-    while (1) {
-        if (!(eleInfo >> bytesCount)) break;
-        eleInfo >> flags;
-        eleInfo >> timestamp;
-        Info.push_back({bytesCount, flags, timestamp});
-    }
-    eleInfo.close();
+    int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
+    ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << info;
+
     int32_t bitStreamInfo[2] = {0};
     if (mCompName == raw) {
         bitStreamInfo[0] = 8000;
@@ -628,22 +613,25 @@
     // request EOS for thumbnail
     // signal EOS flag with last frame
     size_t i = -1;
+    uint32_t flags;
     do {
         i++;
         flags = 0;
         if (Info[i].flags) flags = 1u << (Info[i].flags - 1);
 
     } while (!(flags & SYNC_FRAME));
+    std::ifstream eleStream;
     eleStream.open(mURL, std::ifstream::binary);
     ASSERT_EQ(eleStream.is_open(), true);
     ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
                                           mFlushedIndices, mLinearPool, eleStream, &Info, 0,
                                           i + 1));
-    ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+    waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
     eleStream.close();
     EXPECT_GE(mFramesReceived, 1U);
     ASSERT_EQ(mEos, true);
     ASSERT_EQ(mComponent->stop(), C2_OK);
+    ASSERT_EQ(mWorkResult, C2_OK);
 }
 
 TEST_P(Codec2AudioDecHidlTest, EOSTest) {
@@ -684,33 +672,22 @@
     ASSERT_EQ(mEos, true);
     ASSERT_EQ(mWorkQueue.size(), (size_t)MAX_INPUT_BUFFERS);
     ASSERT_EQ(mComponent->stop(), C2_OK);
+    ASSERT_EQ(mWorkResult, C2_OK);
 }
 
 TEST_P(Codec2AudioDecHidlTest, FlushTest) {
     description("Tests Flush calls");
     if (mDisableTest) GTEST_SKIP() << "Test is disabled";
-    typedef std::unique_lock<std::mutex> ULock;
     char mURL[512], info[512];
-    std::ifstream eleStream, eleInfo;
+    android::Vector<FrameInfo> Info;
 
     strcpy(mURL, sResourceDir.c_str());
     strcpy(info, sResourceDir.c_str());
     GetURLForComponent(mCompName, mURL, info);
 
-    eleInfo.open(info);
-    ASSERT_EQ(eleInfo.is_open(), true);
-    android::Vector<FrameInfo> Info;
-    int bytesCount = 0;
-    uint32_t flags = 0;
-    uint32_t timestamp = 0;
-    mFlushedIndices.clear();
-    while (1) {
-        if (!(eleInfo >> bytesCount)) break;
-        eleInfo >> flags;
-        eleInfo >> timestamp;
-        Info.push_back({bytesCount, flags, timestamp});
-    }
-    eleInfo.close();
+    int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
+    ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << info;
+
     int32_t bitStreamInfo[2] = {0};
     if (mCompName == raw) {
         bitStreamInfo[0] = 8000;
@@ -723,44 +700,37 @@
         return;
     }
     ASSERT_EQ(mComponent->start(), C2_OK);
-    ALOGV("mURL : %s", mURL);
-    eleStream.open(mURL, std::ifstream::binary);
-    ASSERT_EQ(eleStream.is_open(), true);
-    // Decode 128 frames and flush. here 128 is chosen to ensure there is a key
-    // frame after this so that the below section can be covered for all
-    // components
-    uint32_t numFramesFlushed = 128;
-    ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
-                                          mFlushedIndices, mLinearPool, eleStream, &Info, 0,
-                                          numFramesFlushed, false));
     // flush
     std::list<std::unique_ptr<C2Work>> flushedWork;
     c2_status_t err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
     ASSERT_EQ(err, C2_OK);
-    ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
-                                                   (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
-    uint64_t frameIndex;
-    {
-        // Update mFlushedIndices based on the index received from flush()
-        ULock l(mQueueLock);
-        for (std::unique_ptr<C2Work>& work : flushedWork) {
-            ASSERT_NE(work, nullptr);
-            frameIndex = work->input.ordinal.frameIndex.peeku();
-            std::list<uint64_t>::iterator frameIndexIt =
-                    std::find(mFlushedIndices.begin(), mFlushedIndices.end(), frameIndex);
-            if (!mFlushedIndices.empty() && (frameIndexIt != mFlushedIndices.end())) {
-                mFlushedIndices.erase(frameIndexIt);
-                work->input.buffers.clear();
-                work->worklets.clear();
-                mWorkQueue.push_back(std::move(work));
-            }
-        }
-    }
+    ASSERT_NO_FATAL_FAILURE(
+            verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
+    ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
+
+    ALOGV("mURL : %s", mURL);
+    std::ifstream eleStream;
+    eleStream.open(mURL, std::ifstream::binary);
+    ASSERT_EQ(eleStream.is_open(), true);
+    // Decode 30 frames and flush.
+    uint32_t numFramesFlushed = FLUSH_INTERVAL;
+    ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+                                          mFlushedIndices, mLinearPool, eleStream, &Info, 0,
+                                          numFramesFlushed, false));
+    // flush
+    err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
+    ASSERT_EQ(err, C2_OK);
+    waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+                           (size_t)MAX_INPUT_BUFFERS - flushedWork.size());
+    ASSERT_NO_FATAL_FAILURE(
+            verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
+    ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
+
     // Seek to next key frame and start decoding till the end
     mFlushedIndices.clear();
     int index = numFramesFlushed;
     bool keyFrame = false;
-    flags = 0;
+    uint32_t flags = 0;
     while (index < (int)Info.size()) {
         if (Info[index].flags) flags = 1u << (Info[index].flags - 1);
         if ((flags & SYNC_FRAME) == SYNC_FRAME) {
@@ -779,25 +749,13 @@
     eleStream.close();
     err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
     ASSERT_EQ(err, C2_OK);
-    ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
-                                                   (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
-    {
-        // Update mFlushedIndices based on the index received from flush()
-        ULock l(mQueueLock);
-        for (std::unique_ptr<C2Work>& work : flushedWork) {
-            ASSERT_NE(work, nullptr);
-            frameIndex = work->input.ordinal.frameIndex.peeku();
-            std::list<uint64_t>::iterator frameIndexIt =
-                    std::find(mFlushedIndices.begin(), mFlushedIndices.end(), frameIndex);
-            if (!mFlushedIndices.empty() && (frameIndexIt != mFlushedIndices.end())) {
-                mFlushedIndices.erase(frameIndexIt);
-                work->input.buffers.clear();
-                work->worklets.clear();
-                mWorkQueue.push_back(std::move(work));
-            }
-        }
-    }
-    ASSERT_EQ(mFlushedIndices.empty(), true);
+    waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+                           (size_t)MAX_INPUT_BUFFERS - flushedWork.size());
+    ASSERT_NO_FATAL_FAILURE(
+            verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
+    ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
+    // TODO: (b/154671521)
+    // Add assert for mWorkResult
     ASSERT_EQ(mComponent->stop(), C2_OK);
 }
 
@@ -862,7 +820,7 @@
     // consumed
     if (!mEos) {
         ALOGV("Waiting for input consumption");
-        ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+        waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
     }
 
     eleStream.close();
@@ -875,6 +833,109 @@
     ASSERT_EQ(mComponent->stop(), C2_OK);
 }
 
+class Codec2AudioDecCsdInputTests
+    : public Codec2AudioDecHidlTestBase,
+      public ::testing::WithParamInterface<std::tuple<std::string, std::string, std::string>> {
+    void getParams() {
+        mInstanceName = std::get<0>(GetParam());
+        mComponentName = std::get<1>(GetParam());
+    }
+};
+
+// Test the codecs for the following
+// start - csd - data… - (with/without)flush - data… - flush - data…
+TEST_P(Codec2AudioDecCsdInputTests, CSDFlushTest) {
+    description("Tests codecs for flush at different states");
+    if (mDisableTest) GTEST_SKIP() << "Test is disabled";
+
+    char mURL[512], info[512];
+    android::Vector<FrameInfo> Info;
+
+    strcpy(mURL, sResourceDir.c_str());
+    strcpy(info, sResourceDir.c_str());
+    GetURLForComponent(mCompName, mURL, info);
+    if (!strcmp(mURL, sResourceDir.c_str())) {
+        ALOGV("EMPTY INPUT sResourceDir.c_str() %s mURL  %s ", sResourceDir.c_str(), mURL);
+        return;
+    }
+    ALOGV("mURL : %s", mURL);
+
+    int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
+    ASSERT_GE(numCsds, 0) << "Error in parsing input info file";
+
+    int32_t bitStreamInfo[2] = {0};
+    if (mCompName == raw) {
+        bitStreamInfo[0] = 8000;
+        bitStreamInfo[1] = 1;
+    } else {
+        ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+    }
+    if (!setupConfigParam(mComponent, bitStreamInfo)) {
+        std::cout << "[   WARN   ] Test Skipped \n";
+        return;
+    }
+
+    ASSERT_EQ(mComponent->start(), C2_OK);
+    std::ifstream eleStream;
+    eleStream.open(mURL, std::ifstream::binary);
+    ASSERT_EQ(eleStream.is_open(), true);
+
+    bool signalEOS = false;
+    bool flushCsd = !std::get<2>(GetParam()).compare("true");
+    ALOGV("sending %d csd data ", numCsds);
+    int framesToDecode = numCsds;
+    ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+                                          mFlushedIndices, mLinearPool, eleStream, &Info, 0,
+                                          framesToDecode, false));
+
+    c2_status_t err = C2_OK;
+    std::list<std::unique_ptr<C2Work>> flushedWork;
+    if (numCsds && flushCsd) {
+        // We wait for all the CSD buffers to get consumed.
+        // Once we have received all CSD work back, we call flush
+        waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
+
+        err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
+        ASSERT_EQ(err, C2_OK);
+        waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+                               MAX_INPUT_BUFFERS - flushedWork.size());
+        ASSERT_NO_FATAL_FAILURE(
+                verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
+        ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
+        oBufferMetaData.clear();
+    }
+
+    int offset = framesToDecode;
+    while (1) {
+        framesToDecode = c2_min(FLUSH_INTERVAL, (int)Info.size() - offset);
+        if (framesToDecode < FLUSH_INTERVAL) signalEOS = true;
+        ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+                                              mFlushedIndices, mLinearPool, eleStream, &Info,
+                                              offset, framesToDecode, signalEOS));
+        offset += framesToDecode;
+        err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
+        ASSERT_EQ(err, C2_OK);
+        // blocking call to ensures application to Wait till remaining
+        // 'non-flushed' inputs are consumed
+        waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+                               MAX_INPUT_BUFFERS - flushedWork.size());
+        ASSERT_NO_FATAL_FAILURE(
+                verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
+        ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
+        if (signalEOS || offset >= (int)Info.size()) {
+            break;
+        }
+    }
+    if (!signalEOS) {
+        ASSERT_NO_FATAL_FAILURE(testInputBuffer(mComponent, mQueueLock, mWorkQueue,
+                                                C2FrameData::FLAG_END_OF_STREAM, false));
+        waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
+    }
+    eleStream.close();
+    ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
+    ASSERT_EQ(mComponent->stop(), C2_OK);
+}
+
 INSTANTIATE_TEST_SUITE_P(PerInstance, Codec2AudioDecHidlTest, testing::ValuesIn(kTestParameters),
                          android::hardware::PrintInstanceTupleNameToString<>);
 
@@ -883,6 +944,10 @@
                          testing::ValuesIn(kDecodeTestParameters),
                          android::hardware::PrintInstanceTupleNameToString<>);
 
+INSTANTIATE_TEST_SUITE_P(CsdInputs, Codec2AudioDecCsdInputTests,
+                         testing::ValuesIn(kCsdFlushTestParameters),
+                         android::hardware::PrintInstanceTupleNameToString<>);
+
 }  // anonymous namespace
 
 int main(int argc, char** argv) {
@@ -896,6 +961,11 @@
                 std::make_tuple(std::get<0>(params), std::get<1>(params), "1", "false"));
         kDecodeTestParameters.push_back(
                 std::make_tuple(std::get<0>(params), std::get<1>(params), "1", "true"));
+
+        kCsdFlushTestParameters.push_back(
+                std::make_tuple(std::get<0>(params), std::get<1>(params), "true"));
+        kCsdFlushTestParameters.push_back(
+                std::make_tuple(std::get<0>(params), std::get<1>(params), "false"));
     }
 
     // Set the resource directory based on command line args.
@@ -909,4 +979,4 @@
 
     ::testing::InitGoogleTest(&argc, argv);
     return RUN_ALL_TESTS();
-}
\ No newline at end of file
+}
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
index 5f3ae41..e3a4f68 100644
--- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
@@ -95,6 +95,8 @@
         mEos = false;
         mCsd = false;
         mFramesReceived = 0;
+        mWorkResult = C2_OK;
+        mOutputSize = 0u;
         if (mCompName == unknown_comp) mDisableTest = true;
         if (mDisableTest) std::cout << "[   WARN   ] Test Disabled \n";
         getInputMaxBufSize();
@@ -115,6 +117,17 @@
     void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
         for (std::unique_ptr<C2Work>& work : workItems) {
             if (!work->worklets.empty()) {
+                mWorkResult |= work->result;
+                if (!work->worklets.front()->output.buffers.empty()) {
+                    mOutputSize += work->worklets.front()
+                                           ->output.buffers[0]
+                                           ->data()
+                                           .linearBlocks()
+                                           .front()
+                                           .map()
+                                           .get()
+                                           .capacity();
+                }
                 workDone(mComponent, work, mFlushedIndices, mQueueLock, mQueueCondition, mWorkQueue,
                          mEos, mCsd, mFramesReceived);
             }
@@ -135,8 +148,11 @@
     bool mCsd;
     bool mDisableTest;
     standardComp mCompName;
+
+    int32_t mWorkResult;
     uint32_t mFramesReceived;
     int32_t mInputMaxBufSize;
+    uint64_t mOutputSize;
     std::list<uint64_t> mFlushedIndices;
 
     C2BlockPool::local_id_t mBlockPoolId;
@@ -236,6 +252,41 @@
     return false;
 }
 
+// Get config params for a component
+bool getConfigParams(Codec2AudioEncHidlTest::standardComp compName, int32_t* nChannels,
+                     int32_t* nSampleRate, int32_t* samplesPerFrame) {
+    switch (compName) {
+        case Codec2AudioEncHidlTest::aac:
+            *nChannels = 2;
+            *nSampleRate = 48000;
+            *samplesPerFrame = 1024;
+            break;
+        case Codec2AudioEncHidlTest::flac:
+            *nChannels = 2;
+            *nSampleRate = 48000;
+            *samplesPerFrame = 1152;
+            break;
+        case Codec2AudioEncHidlTest::opus:
+            *nChannels = 2;
+            *nSampleRate = 48000;
+            *samplesPerFrame = 960;
+            break;
+        case Codec2AudioEncHidlTest::amrnb:
+            *nChannels = 1;
+            *nSampleRate = 8000;
+            *samplesPerFrame = 160;
+            break;
+        case Codec2AudioEncHidlTest::amrwb:
+            *nChannels = 1;
+            *nSampleRate = 16000;
+            *samplesPerFrame = 160;
+            break;
+        default:
+            return false;
+    }
+    return true;
+}
+
 // LookUpTable of clips and metadata for component testing
 void GetURLForComponent(Codec2AudioEncHidlTest::standardComp comp, char* mURL) {
     struct CompToURL {
@@ -367,36 +418,18 @@
     bool signalEOS = !std::get<2>(GetParam()).compare("true");
     // Ratio w.r.t to mInputMaxBufSize
     int32_t inputMaxBufRatio = std::stoi(std::get<3>(GetParam()));
-    ;
 
-    // Setting default sampleRate
-    int32_t nChannels = 2;
-    int32_t nSampleRate = 44100;
-    switch (mCompName) {
-        case aac:
-            nChannels = 2;
-            nSampleRate = 48000;
-            break;
-        case flac:
-            nChannels = 2;
-            nSampleRate = 48000;
-            break;
-        case opus:
-            nChannels = 2;
-            nSampleRate = 48000;
-            break;
-        case amrnb:
-            nChannels = 1;
-            nSampleRate = 8000;
-            break;
-        case amrwb:
-            nChannels = 1;
-            nSampleRate = 16000;
-            break;
-        default:
-            ASSERT_TRUE(false);
+    int32_t nChannels;
+    int32_t nSampleRate;
+    int32_t samplesPerFrame;
+
+    if (!getConfigParams(mCompName, &nChannels, &nSampleRate, &samplesPerFrame)) {
+        std::cout << "Failed to get the config params for " << mCompName << " component\n";
+        std::cout << "[   WARN   ] Test Skipped \n";
+        return;
     }
-    int32_t samplesPerFrame = ((mInputMaxBufSize / inputMaxBufRatio) / (nChannels * 2));
+
+    samplesPerFrame = ((mInputMaxBufSize / inputMaxBufRatio) / (nChannels * 2));
     ALOGV("signalEOS %d mInputMaxBufSize %d samplesPerFrame %d", signalEOS, mInputMaxBufSize,
           samplesPerFrame);
 
@@ -416,7 +449,7 @@
 
     // If EOS is not sent, sending empty input with EOS flag
     if (!signalEOS) {
-        ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1));
+        waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1);
         ASSERT_NO_FATAL_FAILURE(testInputBuffer(mComponent, mQueueLock, mWorkQueue,
                                                 C2FrameData::FLAG_END_OF_STREAM, false));
         numFrames += 1;
@@ -424,7 +457,7 @@
 
     // blocking call to ensures application to Wait till all the inputs are
     // consumed
-    ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+    waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
     eleStream.close();
     if (mFramesReceived != numFrames) {
         ALOGE("Input buffer count and Output buffer count mismatch");
@@ -439,6 +472,7 @@
     }
     ASSERT_EQ(mEos, true);
     ASSERT_EQ(mComponent->stop(), C2_OK);
+    ASSERT_EQ(mWorkResult, C2_OK);
 }
 
 TEST_P(Codec2AudioEncHidlTest, EOSTest) {
@@ -479,50 +513,26 @@
     }
     ASSERT_EQ(mEos, true);
     ASSERT_EQ(mComponent->stop(), C2_OK);
+    ASSERT_EQ(mWorkResult, C2_OK);
 }
 
 TEST_P(Codec2AudioEncHidlTest, FlushTest) {
     description("Test Request for flush");
     if (mDisableTest) GTEST_SKIP() << "Test is disabled";
 
-    typedef std::unique_lock<std::mutex> ULock;
     char mURL[512];
     strcpy(mURL, sResourceDir.c_str());
     GetURLForComponent(mCompName, mURL);
 
-    // Setting default configuration
     mFlushedIndices.clear();
-    int32_t nChannels = 2;
-    int32_t nSampleRate = 44100;
-    int32_t samplesPerFrame = 1024;
-    switch (mCompName) {
-        case aac:
-            nChannels = 2;
-            nSampleRate = 48000;
-            samplesPerFrame = 1024;
-            break;
-        case flac:
-            nChannels = 2;
-            nSampleRate = 48000;
-            samplesPerFrame = 1152;
-            break;
-        case opus:
-            nChannels = 2;
-            nSampleRate = 48000;
-            samplesPerFrame = 960;
-            break;
-        case amrnb:
-            nChannels = 1;
-            nSampleRate = 8000;
-            samplesPerFrame = 160;
-            break;
-        case amrwb:
-            nChannels = 1;
-            nSampleRate = 16000;
-            samplesPerFrame = 160;
-            break;
-        default:
-            ASSERT_TRUE(false);
+    int32_t nChannels;
+    int32_t nSampleRate;
+    int32_t samplesPerFrame;
+
+    if (!getConfigParams(mCompName, &nChannels, &nSampleRate, &samplesPerFrame)) {
+        std::cout << "Failed to get the config params for " << mCompName << " component\n";
+        std::cout << "[   WARN   ] Test Skipped \n";
+        return;
     }
 
     if (!setupConfigParam(mComponent, nChannels, nSampleRate)) {
@@ -536,33 +546,24 @@
     uint32_t numFrames = 128;
     eleStream.open(mURL, std::ifstream::binary);
     ASSERT_EQ(eleStream.is_open(), true);
+    // flush
+    std::list<std::unique_ptr<C2Work>> flushedWork;
+    c2_status_t err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
+    ASSERT_EQ(err, C2_OK);
+    ASSERT_NO_FATAL_FAILURE(
+            verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
+    ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
     ALOGV("mURL : %s", mURL);
     ASSERT_NO_FATAL_FAILURE(encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
                                           mFlushedIndices, mLinearPool, eleStream, numFramesFlushed,
                                           samplesPerFrame, nChannels, nSampleRate));
-    std::list<std::unique_ptr<C2Work>> flushedWork;
-    c2_status_t err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
+    err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
     ASSERT_EQ(err, C2_OK);
-    ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
-                                                   (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
-    uint64_t frameIndex;
-    {
-        // Update mFlushedIndices based on the index received from flush()
-        ULock l(mQueueLock);
-        for (std::unique_ptr<C2Work>& work : flushedWork) {
-            ASSERT_NE(work, nullptr);
-            frameIndex = work->input.ordinal.frameIndex.peeku();
-            std::list<uint64_t>::iterator frameIndexIt =
-                    std::find(mFlushedIndices.begin(), mFlushedIndices.end(), frameIndex);
-            if (!mFlushedIndices.empty() && (frameIndexIt != mFlushedIndices.end())) {
-                mFlushedIndices.erase(frameIndexIt);
-                work->input.buffers.clear();
-                work->worklets.clear();
-                mWorkQueue.push_back(std::move(work));
-            }
-        }
-    }
-    mFlushedIndices.clear();
+    waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+                           (size_t)MAX_INPUT_BUFFERS - flushedWork.size());
+    ASSERT_NO_FATAL_FAILURE(
+            verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
+    ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
     ASSERT_NO_FATAL_FAILURE(encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
                                           mFlushedIndices, mLinearPool, eleStream,
                                           numFrames - numFramesFlushed, samplesPerFrame, nChannels,
@@ -570,28 +571,220 @@
     eleStream.close();
     err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
     ASSERT_EQ(err, C2_OK);
-    ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
-                                                   (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
-    {
-        // Update mFlushedIndices based on the index received from flush()
-        ULock l(mQueueLock);
-        for (std::unique_ptr<C2Work>& work : flushedWork) {
-            ASSERT_NE(work, nullptr);
-            frameIndex = work->input.ordinal.frameIndex.peeku();
-            std::list<uint64_t>::iterator frameIndexIt =
-                    std::find(mFlushedIndices.begin(), mFlushedIndices.end(), frameIndex);
-            if (!mFlushedIndices.empty() && (frameIndexIt != mFlushedIndices.end())) {
-                mFlushedIndices.erase(frameIndexIt);
-                work->input.buffers.clear();
-                work->worklets.clear();
-                mWorkQueue.push_back(std::move(work));
-            }
-        }
-    }
-    ASSERT_EQ(mFlushedIndices.empty(), true);
+    waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+                           (size_t)MAX_INPUT_BUFFERS - flushedWork.size());
+    ASSERT_NO_FATAL_FAILURE(
+            verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
+    ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
+    // TODO: (b/154671521)
+    // Add assert for mWorkResult
     ASSERT_EQ(mComponent->stop(), C2_OK);
 }
 
+TEST_P(Codec2AudioEncHidlTest, MultiChannelCountTest) {
+    description("Encodes input file for different channel count");
+    if (mDisableTest) GTEST_SKIP() << "Test is disabled";
+
+    char mURL[512];
+    strcpy(mURL, sResourceDir.c_str());
+    GetURLForComponent(mCompName, mURL);
+
+    std::ifstream eleStream;
+    eleStream.open(mURL, std::ifstream::binary);
+    ASSERT_EQ(eleStream.is_open(), true) << mURL << " file not found";
+    ALOGV("mURL : %s", mURL);
+
+    int32_t nSampleRate;
+    int32_t samplesPerFrame;
+    int32_t nChannels;
+    int32_t numFrames = 16;
+    int32_t maxChannelCount = 8;
+
+    if (!getConfigParams(mCompName, &nChannels, &nSampleRate, &samplesPerFrame)) {
+        std::cout << "Failed to get the config params for " << mCompName << " component\n";
+        std::cout << "[   WARN   ] Test Skipped \n";
+        return;
+    }
+
+    uint64_t prevOutputSize = 0u;
+    uint32_t prevChannelCount = 0u;
+
+    // Looping through the maximum number of channel count supported by encoder
+    for (nChannels = 1; nChannels < maxChannelCount; nChannels++) {
+        ALOGV("Configuring %u encoder for channel count = %d", mCompName, nChannels);
+        if (!setupConfigParam(mComponent, nChannels, nSampleRate)) {
+            std::cout << "[   WARN   ] Test Skipped \n";
+            return;
+        }
+
+        std::vector<std::unique_ptr<C2Param>> inParams;
+        c2_status_t c2_status = mComponent->query({}, {C2StreamChannelCountInfo::input::PARAM_TYPE},
+                                                  C2_DONT_BLOCK, &inParams);
+        ASSERT_TRUE(!c2_status && inParams.size())
+                << "Query configured channelCount failed => %d" << c2_status;
+
+        size_t offset = sizeof(C2Param);
+        C2Param* param = inParams[0].get();
+        int32_t channelCount = *(int32_t*)((uint8_t*)param + offset);
+        if (channelCount != nChannels) {
+            std::cout << "[   WARN   ] Test Skipped for ChannelCount " << nChannels << "\n";
+            continue;
+        }
+
+        // To check if the input stream is sufficient to encode for the higher channel count
+        int32_t bytesCount = (samplesPerFrame * nChannels * 2) * numFrames;
+        if (eleStream.gcount() < bytesCount) {
+            std::cout << "[   WARN   ] Test Skipped for ChannelCount " << nChannels
+                      << " because of insufficient input data\n";
+            continue;
+        }
+
+        ASSERT_EQ(mComponent->start(), C2_OK);
+
+        ASSERT_NO_FATAL_FAILURE(encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+                                              mFlushedIndices, mLinearPool, eleStream, numFrames,
+                                              samplesPerFrame, nChannels, nSampleRate));
+
+        // mDisableTest will be set if buffer was not fetched properly.
+        // This may happen when config params is not proper but config succeeded
+        // In this cases, we skip encoding the input stream
+        if (mDisableTest) {
+            std::cout << "[   WARN   ] Test Disabled for ChannelCount " << nChannels << "\n";
+            ASSERT_EQ(mComponent->stop(), C2_OK);
+            return;
+        }
+
+        // blocking call to ensures application to Wait till all the inputs are consumed
+        waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
+
+        // Validate output size based on chosen ChannelCount
+        EXPECT_GE(mOutputSize, prevOutputSize);
+
+        prevChannelCount = nChannels;
+        prevOutputSize = mOutputSize;
+
+        if (mFramesReceived != numFrames) {
+            ALOGE("Input buffer count and Output buffer count mismatch");
+            ALOGE("framesReceived : %d inputFrames : %u", mFramesReceived, numFrames);
+            ASSERT_TRUE(false);
+        }
+        if ((mCompName == flac || mCompName == opus || mCompName == aac)) {
+            ASSERT_TRUE(mCsd) << "CSD buffer missing";
+        }
+        ASSERT_TRUE(mEos);
+        ASSERT_EQ(mComponent->stop(), C2_OK);
+        mFramesReceived = 0;
+        mOutputSize = 0;
+        mEos = false;
+        mCsd = false;
+        eleStream.seekg(0, eleStream.beg);
+    }
+}
+
+TEST_P(Codec2AudioEncHidlTest, MultiSampleRateTest) {
+    description("Encodes input file for different SampleRate");
+    if (mDisableTest) GTEST_SKIP() << "Test is disabled";
+
+    char mURL[512];
+    strcpy(mURL, sResourceDir.c_str());
+    GetURLForComponent(mCompName, mURL);
+
+    std::ifstream eleStream;
+    eleStream.open(mURL, std::ifstream::binary);
+    ASSERT_EQ(eleStream.is_open(), true) << mURL << " file not found";
+    ALOGV("mURL : %s", mURL);
+
+    int32_t nSampleRate;
+    int32_t samplesPerFrame;
+    int32_t nChannels;
+    int32_t numFrames = 16;
+
+    if (!getConfigParams(mCompName, &nChannels, &nSampleRate, &samplesPerFrame)) {
+        std::cout << "Failed to get the config params for " << mCompName << " component\n";
+        std::cout << "[   WARN   ] Test Skipped \n";
+        return;
+    }
+
+    int32_t sampleRateValues[] = {1000, 8000, 16000, 24000, 48000, 96000, 192000};
+
+    uint64_t prevOutputSize = 0u;
+    uint32_t prevSampleRate = 0u;
+
+    for (int32_t nSampleRate : sampleRateValues) {
+        ALOGV("Configuring %u encoder for SampleRate = %d", mCompName, nSampleRate);
+        if (!setupConfigParam(mComponent, nChannels, nSampleRate)) {
+            std::cout << "[   WARN   ] Test Skipped \n";
+            return;
+        }
+
+        std::vector<std::unique_ptr<C2Param>> inParams;
+        c2_status_t c2_status = mComponent->query({}, {C2StreamSampleRateInfo::input::PARAM_TYPE},
+                                                  C2_DONT_BLOCK, &inParams);
+
+        ASSERT_TRUE(!c2_status && inParams.size())
+                << "Query configured SampleRate failed => %d" << c2_status;
+        size_t offset = sizeof(C2Param);
+        C2Param* param = inParams[0].get();
+        int32_t configuredSampleRate = *(int32_t*)((uint8_t*)param + offset);
+
+        if (configuredSampleRate != nSampleRate) {
+            std::cout << "[   WARN   ] Test Skipped for SampleRate " << nSampleRate << "\n";
+            continue;
+        }
+
+        // To check if the input stream is sufficient to encode for the higher SampleRate
+        int32_t bytesCount = (samplesPerFrame * nChannels * 2) * numFrames;
+        if (eleStream.gcount() < bytesCount) {
+            std::cout << "[   WARN   ] Test Skipped for SampleRate " << nSampleRate
+                      << " because of insufficient input data\n";
+            continue;
+        }
+
+        ASSERT_EQ(mComponent->start(), C2_OK);
+
+        ASSERT_NO_FATAL_FAILURE(encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+                                              mFlushedIndices, mLinearPool, eleStream, numFrames,
+                                              samplesPerFrame, nChannels, nSampleRate));
+
+        // mDisableTest will be set if buffer was not fetched properly.
+        // This may happen when config params is not proper but config succeeded
+        // In this case, we skip encoding the input stream
+        if (mDisableTest) {
+            std::cout << "[   WARN   ] Test Disabled for SampleRate" << nSampleRate << "\n";
+            ASSERT_EQ(mComponent->stop(), C2_OK);
+            return;
+        }
+
+        // blocking call to ensures application to Wait till all the inputs are consumed
+        waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
+
+        // Validate output size based on chosen samplerate
+        if (prevSampleRate >= nSampleRate) {
+            EXPECT_LE(mOutputSize, prevOutputSize);
+        } else {
+            EXPECT_GT(mOutputSize, prevOutputSize);
+        }
+        prevSampleRate = nSampleRate;
+        prevOutputSize = mOutputSize;
+
+        if (mFramesReceived != numFrames) {
+            ALOGE("Input buffer count and Output buffer count mismatch");
+            ALOGE("framesReceived : %d inputFrames : %u", mFramesReceived, numFrames);
+            ASSERT_TRUE(false);
+        }
+        if ((mCompName == flac || mCompName == opus || mCompName == aac)) {
+            ASSERT_TRUE(mCsd) << "CSD buffer missing";
+        }
+        ASSERT_TRUE(mEos);
+        ASSERT_EQ(mComponent->stop(), C2_OK);
+        mFramesReceived = 0;
+        mOutputSize = 0;
+        mEos = false;
+        mCsd = false;
+        eleStream.seekg(0, eleStream.beg);
+    }
+}
+
 INSTANTIATE_TEST_SUITE_P(PerInstance, Codec2AudioEncHidlTest, testing::ValuesIn(kTestParameters),
                          android::hardware::PrintInstanceTupleNameToString<>);
 
diff --git a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
index da8225c..a41c2dc 100644
--- a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
@@ -160,4 +160,57 @@
     }
 
     return parameters;
-}
\ No newline at end of file
+}
+
+// Populate Info vector and return number of CSDs
+int32_t populateInfoVector(std::string info, android::Vector<FrameInfo>* frameInfo,
+                           bool timestampDevTest, std::list<uint64_t>* timestampUslist) {
+    std::ifstream eleInfo;
+    eleInfo.open(info);
+    if (!eleInfo.is_open()) {
+        ALOGE("Can't open info file");
+        return -1;
+    }
+    int32_t numCsds = 0;
+    int32_t bytesCount = 0;
+    uint32_t flags = 0;
+    uint32_t timestamp = 0;
+    while (1) {
+        if (!(eleInfo >> bytesCount)) break;
+        eleInfo >> flags;
+        eleInfo >> timestamp;
+        bool codecConfig = flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
+        if (codecConfig) numCsds++;
+        bool nonDisplayFrame = ((flags & FLAG_NON_DISPLAY_FRAME) != 0);
+        if (timestampDevTest && !codecConfig && !nonDisplayFrame) {
+            timestampUslist->push_back(timestamp);
+        }
+        frameInfo->push_back({bytesCount, flags, timestamp});
+    }
+    ALOGV("numCsds : %d", numCsds);
+    eleInfo.close();
+    return numCsds;
+}
+
+void verifyFlushOutput(std::list<std::unique_ptr<C2Work>>& flushedWork,
+                       std::list<std::unique_ptr<C2Work>>& workQueue,
+                       std::list<uint64_t>& flushedIndices, std::mutex& queueLock) {
+    // Update mFlushedIndices based on the index received from flush()
+    typedef std::unique_lock<std::mutex> ULock;
+    uint64_t frameIndex;
+    ULock l(queueLock);
+    for (std::unique_ptr<C2Work>& work : flushedWork) {
+        ASSERT_NE(work, nullptr);
+        frameIndex = work->input.ordinal.frameIndex.peeku();
+        std::list<uint64_t>::iterator frameIndexIt =
+                std::find(flushedIndices.begin(), flushedIndices.end(), frameIndex);
+        if (!flushedIndices.empty() && (frameIndexIt != flushedIndices.end())) {
+            flushedIndices.erase(frameIndexIt);
+            work->input.buffers.clear();
+            work->worklets.clear();
+            workQueue.push_back(std::move(work));
+        }
+    }
+    ASSERT_EQ(flushedIndices.empty(), true);
+    flushedWork.clear();
+}
diff --git a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
index 4b5e0a6..50e3ac5 100644
--- a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
+++ b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
@@ -25,10 +25,13 @@
 #include <gtest/gtest.h>
 #include <hidl/HidlSupport.h>
 #include <chrono>
+#include <fstream>
 
+#define FLAG_NON_DISPLAY_FRAME (1 << 4)
 #define MAX_RETRY 20
 #define TIME_OUT 400ms
 #define MAX_INPUT_BUFFERS 8
+#define FLUSH_INTERVAL 30
 
 using ::android::hardware::hidl_string;
 using ::android::hardware::hidl_vec;
@@ -39,6 +42,12 @@
 
 static std::vector<std::tuple<std::string, std::string>> kTestParameters;
 
+struct FrameInfo {
+    int bytesCount;
+    uint32_t flags;
+    int64_t timestamp;
+};
+
 /*
  * Handle Callback functions onWorkDone(), onTripped(),
  * onError(), onDeath(), onFramesRendered()
@@ -123,4 +132,10 @@
 
 int64_t getNowUs();
 
+int32_t populateInfoVector(std::string info, android::Vector<FrameInfo>* frameInfo,
+                           bool timestampDevTest, std::list<uint64_t>* timestampUslist);
+
+void verifyFlushOutput(std::list<std::unique_ptr<C2Work>>& flushedWork,
+                       std::list<std::unique_ptr<C2Work>>& workQueue,
+                       std::list<uint64_t>& flushedIndices, std::mutex& queueLock);
 #endif  // MEDIA_C2_HIDL_TEST_COMMON_H
diff --git a/media/codec2/hidl/1.0/vts/functional/res/bbb_av1_176_144_chksm.md5 b/media/codec2/hidl/1.0/vts/functional/res/bbb_av1_176_144_chksm.md5
new file mode 100644
index 0000000..cb69709
--- /dev/null
+++ b/media/codec2/hidl/1.0/vts/functional/res/bbb_av1_176_144_chksm.md5
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/functional/res/bbb_av1_640_360_chksum.md5 b/media/codec2/hidl/1.0/vts/functional/res/bbb_av1_640_360_chksum.md5
new file mode 100644
index 0000000..2693071
--- /dev/null
+++ b/media/codec2/hidl/1.0/vts/functional/res/bbb_av1_640_360_chksum.md5
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/functional/res/bbb_avc_176x144_300kbps_60fps_chksum.md5 b/media/codec2/hidl/1.0/vts/functional/res/bbb_avc_176x144_300kbps_60fps_chksum.md5
new file mode 100644
index 0000000..5c802d9
--- /dev/null
+++ b/media/codec2/hidl/1.0/vts/functional/res/bbb_avc_176x144_300kbps_60fps_chksum.md5
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/functional/res/bbb_avc_640x360_768kbps_30fps_chksum.md5 b/media/codec2/hidl/1.0/vts/functional/res/bbb_avc_640x360_768kbps_30fps_chksum.md5
new file mode 100644
index 0000000..073f8eb
--- /dev/null
+++ b/media/codec2/hidl/1.0/vts/functional/res/bbb_avc_640x360_768kbps_30fps_chksum.md5
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/functional/res/bbb_hevc_176x144_176kbps_60fps_chksum.md5 b/media/codec2/hidl/1.0/vts/functional/res/bbb_hevc_176x144_176kbps_60fps_chksum.md5
new file mode 100644
index 0000000..83f11c0
--- /dev/null
+++ b/media/codec2/hidl/1.0/vts/functional/res/bbb_hevc_176x144_176kbps_60fps_chksum.md5
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/functional/res/bbb_hevc_640x360_1600kbps_30fps_chksum.md5 b/media/codec2/hidl/1.0/vts/functional/res/bbb_hevc_640x360_1600kbps_30fps_chksum.md5
new file mode 100644
index 0000000..3344881
--- /dev/null
+++ b/media/codec2/hidl/1.0/vts/functional/res/bbb_hevc_640x360_1600kbps_30fps_chksum.md5
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/functional/res/bbb_vp8_640x360_2mbps_30fps_chksm.md5 b/media/codec2/hidl/1.0/vts/functional/res/bbb_vp8_640x360_2mbps_30fps_chksm.md5
new file mode 100644
index 0000000..738b1da
--- /dev/null
+++ b/media/codec2/hidl/1.0/vts/functional/res/bbb_vp8_640x360_2mbps_30fps_chksm.md5
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/functional/res/bbb_vp9_640x360_1600kbps_30fps_chksm.md5 b/media/codec2/hidl/1.0/vts/functional/res/bbb_vp9_640x360_1600kbps_30fps_chksm.md5
new file mode 100644
index 0000000..a52faf2
--- /dev/null
+++ b/media/codec2/hidl/1.0/vts/functional/res/bbb_vp9_640x360_1600kbps_30fps_chksm.md5
Binary files differ
diff --git a/media/codec2/hidl/1.0/vts/functional/video/Android.bp b/media/codec2/hidl/1.0/vts/functional/video/Android.bp
index 760f4da..c7b0c12 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/Android.bp
+++ b/media/codec2/hidl/1.0/vts/functional/video/Android.bp
@@ -26,6 +26,7 @@
         "libbinder",
         "libgui",
         "libutils",
+        "libcrypto",
     ],
     data: [":media_c2_v1_video_decode_res"],
     test_config: "VtsHalMediaC2V1_0TargetVideoDecTest.xml",
diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
index f216429..74088dd 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
@@ -21,7 +21,8 @@
 #include <gtest/gtest.h>
 #include <hidl/GtestPrinter.h>
 #include <stdio.h>
-#include <fstream>
+
+#include <openssl/md5.h>
 
 #include <C2AllocatorIon.h>
 #include <C2Buffer.h>
@@ -39,15 +40,11 @@
 #include "media_c2_hidl_test_common.h"
 #include "media_c2_video_hidl_test_common.h"
 
-struct FrameInfo {
-    int bytesCount;
-    uint32_t flags;
-    int64_t timestamp;
-};
-
 static std::vector<std::tuple<std::string, std::string, std::string, std::string>>
         kDecodeTestParameters;
 
+static std::vector<std::tuple<std::string, std::string, std::string>> kCsdFlushTestParameters;
+
 // Resource directory
 static std::string sResourceDir = "";
 
@@ -111,7 +108,11 @@
         mEos = false;
         mFramesReceived = 0;
         mTimestampUs = 0u;
+        mWorkResult = C2_OK;
         mTimestampDevTest = false;
+        mMd5Offset = 0;
+        mMd5Enable = false;
+        mRefMd5 = nullptr;
         if (mCompName == unknown_comp) mDisableTest = true;
 
         C2SecureModeTuning secureModeTuning{};
@@ -134,6 +135,77 @@
     // Get the test parameters from GetParam call.
     virtual void getParams() {}
 
+    /* Calculate the CKSUM for the data in inbuf */
+    void calc_md5_cksum(uint8_t* pu1_inbuf, uint32_t u4_stride, uint32_t u4_width,
+                        uint32_t u4_height, uint8_t* pu1_cksum_p) {
+        int32_t row;
+        MD5_CTX s_md5_context;
+        MD5_Init(&s_md5_context);
+        for (row = 0; row < u4_height; row++) {
+            MD5_Update(&s_md5_context, pu1_inbuf, u4_width);
+            pu1_inbuf += u4_stride;
+        }
+        MD5_Final(pu1_cksum_p, &s_md5_context);
+    }
+
+    void compareMd5Chksm(std::unique_ptr<C2Work>& work) {
+        uint8_t chksum[48];
+        uint8_t* au1_y_chksum = chksum;
+        uint8_t* au1_u_chksum = chksum + 16;
+        uint8_t* au1_v_chksum = chksum + 32;
+        const C2GraphicView output = work->worklets.front()
+                                             ->output.buffers[0]
+                                             ->data()
+                                             .graphicBlocks()
+                                             .front()
+                                             .map()
+                                             .get();
+        uint8_t* yPlane = const_cast<uint8_t*>(output.data()[C2PlanarLayout::PLANE_Y]);
+        uint8_t* uPlane = const_cast<uint8_t*>(output.data()[C2PlanarLayout::PLANE_U]);
+        uint8_t* vPlane = const_cast<uint8_t*>(output.data()[C2PlanarLayout::PLANE_V]);
+        C2PlanarLayout layout = output.layout();
+
+        size_t yStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+        size_t uvStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc;
+        size_t colInc = layout.planes[C2PlanarLayout::PLANE_U].colInc;
+        size_t bitDepth = layout.planes[C2PlanarLayout::PLANE_Y].bitDepth;
+        uint32_t layoutType = layout.type;
+        size_t cropWidth = output.crop().width;
+        size_t cropHeight = output.crop().height;
+
+        if (bitDepth == 8 && layoutType == C2PlanarLayout::TYPE_YUV && colInc == 1) {
+            calc_md5_cksum(yPlane, yStride, cropWidth, cropHeight, au1_y_chksum);
+            calc_md5_cksum(uPlane, uvStride, cropWidth / 2, cropHeight / 2, au1_u_chksum);
+            calc_md5_cksum(vPlane, uvStride, cropWidth / 2, cropHeight / 2, au1_v_chksum);
+        } else if (bitDepth == 8 && layoutType == C2PlanarLayout::TYPE_YUV && colInc == 2) {
+            uint8_t* cbPlane = (uint8_t*)malloc(cropWidth * cropHeight / 4);
+            uint8_t* crPlane = (uint8_t*)malloc(cropWidth * cropHeight / 4);
+            ASSERT_NE(cbPlane, nullptr);
+            ASSERT_NE(crPlane, nullptr);
+            size_t count = 0;
+            for (size_t k = 0; k < (cropHeight / 2); k++) {
+                for (size_t l = 0; l < (cropWidth); l = l + 2) {
+                    cbPlane[count] = uPlane[k * uvStride + l];
+                    crPlane[count] = vPlane[k * uvStride + l];
+                    count++;
+                }
+            }
+            calc_md5_cksum(yPlane, yStride, cropWidth, cropHeight, au1_y_chksum);
+            calc_md5_cksum(cbPlane, cropWidth / 2, cropWidth / 2, cropHeight / 2, au1_u_chksum);
+            calc_md5_cksum(crPlane, cropWidth / 2, cropWidth / 2, cropHeight / 2, au1_v_chksum);
+            free(cbPlane);
+            free(crPlane);
+        } else {
+            mMd5Enable = false;
+            ALOGV("Disabling MD5 chksm flag");
+            return;
+        }
+        if (memcmp(mRefMd5 + mMd5Offset, chksum, 48)) ASSERT_TRUE(false);
+        mMd5Offset += 48;
+        return;
+    }
+    bool configPixelFormat(uint32_t format);
+
     // callback function to process onWorkDone received by Listener
     void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
         for (std::unique_ptr<C2Work>& work : workItems) {
@@ -141,6 +213,7 @@
                 // For decoder components current timestamp always exceeds
                 // previous timestamp
                 typedef std::unique_lock<std::mutex> ULock;
+                mWorkResult |= work->result;
                 bool codecConfig = ((work->worklets.front()->output.flags &
                                      C2FrameData::FLAG_CODEC_CONFIG) != 0);
                 if (!codecConfig && !work->worklets.front()->output.buffers.empty()) {
@@ -169,8 +242,11 @@
                             }
                         }
                     }
+                    if (mMd5Enable) {
+                        compareMd5Chksm(work);
+                    }
                 }
-                bool mCsd;
+                bool mCsd = false;
                 workDone(mComponent, work, mFlushedIndices, mQueueLock, mQueueCondition, mWorkQueue,
                          mEos, mCsd, mFramesReceived);
                 (void)mCsd;
@@ -195,11 +271,16 @@
 
     bool mEos;
     bool mDisableTest;
+    bool mMd5Enable;
     bool mTimestampDevTest;
     uint64_t mTimestampUs;
+    uint64_t mMd5Offset;
+    char* mRefMd5;
     std::list<uint64_t> mTimestampUslist;
     std::list<uint64_t> mFlushedIndices;
     standardComp mCompName;
+
+    int32_t mWorkResult;
     uint32_t mFramesReceived;
     C2BlockPool::local_id_t mBlockPoolId;
     std::shared_ptr<C2BlockPool> mLinearPool;
@@ -268,54 +349,73 @@
 
 // number of elementary streams per component
 #define STREAM_COUNT 3
-// LookUpTable of clips and metadata for component testing
-void GetURLForComponent(Codec2VideoDecHidlTest::standardComp comp, char* mURL, char* info,
-                        size_t streamIndex = 1) {
+// LookUpTable of clips, metadata and chksum for component testing
+void GetURLChksmForComponent(Codec2VideoDecHidlTest::standardComp comp, char* mURL, char* info,
+                             char* chksum, size_t streamIndex = 1) {
     struct CompToURL {
         Codec2VideoDecHidlTest::standardComp comp;
         const char mURL[STREAM_COUNT][512];
         const char info[STREAM_COUNT][512];
+        const char chksum[STREAM_COUNT][512];
     };
     ASSERT_TRUE(streamIndex < STREAM_COUNT);
 
     static const CompToURL kCompToURL[] = {
             {Codec2VideoDecHidlTest::standardComp::avc,
              {"bbb_avc_176x144_300kbps_60fps.h264", "bbb_avc_640x360_768kbps_30fps.h264", ""},
-             {"bbb_avc_176x144_300kbps_60fps.info", "bbb_avc_640x360_768kbps_30fps.info", ""}},
+             {"bbb_avc_176x144_300kbps_60fps.info", "bbb_avc_640x360_768kbps_30fps.info", ""},
+             {"bbb_avc_176x144_300kbps_60fps_chksum.md5",
+              "bbb_avc_640x360_768kbps_30fps_chksum.md5", ""}},
             {Codec2VideoDecHidlTest::standardComp::hevc,
              {"bbb_hevc_176x144_176kbps_60fps.hevc", "bbb_hevc_640x360_1600kbps_30fps.hevc", ""},
-             {"bbb_hevc_176x144_176kbps_60fps.info", "bbb_hevc_640x360_1600kbps_30fps.info", ""}},
+             {"bbb_hevc_176x144_176kbps_60fps.info", "bbb_hevc_640x360_1600kbps_30fps.info", ""},
+             {"bbb_hevc_176x144_176kbps_60fps_chksum.md5",
+              "bbb_hevc_640x360_1600kbps_30fps_chksum.md5", ""}},
             {Codec2VideoDecHidlTest::standardComp::mpeg2,
              {"bbb_mpeg2_176x144_105kbps_25fps.m2v", "bbb_mpeg2_352x288_1mbps_60fps.m2v", ""},
-             {"bbb_mpeg2_176x144_105kbps_25fps.info", "bbb_mpeg2_352x288_1mbps_60fps.info", ""}},
+             {"bbb_mpeg2_176x144_105kbps_25fps.info", "bbb_mpeg2_352x288_1mbps_60fps.info", ""},
+             {"", "", ""}},
             {Codec2VideoDecHidlTest::standardComp::h263,
              {"", "bbb_h263_352x288_300kbps_12fps.h263", ""},
-             {"", "bbb_h263_352x288_300kbps_12fps.info", ""}},
+             {"", "bbb_h263_352x288_300kbps_12fps.info", ""},
+             {"", "", ""}},
             {Codec2VideoDecHidlTest::standardComp::mpeg4,
              {"", "bbb_mpeg4_352x288_512kbps_30fps.m4v", ""},
-             {"", "bbb_mpeg4_352x288_512kbps_30fps.info", ""}},
+             {"", "bbb_mpeg4_352x288_512kbps_30fps.info", ""},
+             {"", "", ""}},
             {Codec2VideoDecHidlTest::standardComp::vp8,
              {"bbb_vp8_176x144_240kbps_60fps.vp8", "bbb_vp8_640x360_2mbps_30fps.vp8", ""},
-             {"bbb_vp8_176x144_240kbps_60fps.info", "bbb_vp8_640x360_2mbps_30fps.info", ""}},
+             {"bbb_vp8_176x144_240kbps_60fps.info", "bbb_vp8_640x360_2mbps_30fps.info", ""},
+             {"", "bbb_vp8_640x360_2mbps_30fps_chksm.md5", ""}},
             {Codec2VideoDecHidlTest::standardComp::vp9,
              {"bbb_vp9_176x144_285kbps_60fps.vp9", "bbb_vp9_640x360_1600kbps_30fps.vp9",
               "bbb_vp9_704x480_280kbps_24fps_altref_2.vp9"},
              {"bbb_vp9_176x144_285kbps_60fps.info", "bbb_vp9_640x360_1600kbps_30fps.info",
-              "bbb_vp9_704x480_280kbps_24fps_altref_2.info"}},
+              "bbb_vp9_704x480_280kbps_24fps_altref_2.info"},
+             {"", "bbb_vp9_640x360_1600kbps_30fps_chksm.md5", ""}},
             {Codec2VideoDecHidlTest::standardComp::av1,
              {"bbb_av1_640_360.av1", "bbb_av1_176_144.av1", ""},
-             {"bbb_av1_640_360.info", "bbb_av1_176_144.info", ""}},
+             {"bbb_av1_640_360.info", "bbb_av1_176_144.info", ""},
+             {"bbb_av1_640_360_chksum.md5", "bbb_av1_176_144_chksm.md5", ""}},
     };
 
     for (size_t i = 0; i < sizeof(kCompToURL) / sizeof(kCompToURL[0]); ++i) {
         if (kCompToURL[i].comp == comp) {
             strcat(mURL, kCompToURL[i].mURL[streamIndex]);
             strcat(info, kCompToURL[i].info[streamIndex]);
+            strcat(chksum, kCompToURL[i].chksum[streamIndex]);
             return;
         }
     }
 }
 
+void GetURLForComponent(Codec2VideoDecHidlTest::standardComp comp, char* mURL, char* info,
+                        size_t streamIndex = 1) {
+    char chksum[512];
+    strcpy(chksum, sResourceDir.c_str());
+    GetURLChksmForComponent(comp, mURL, info, chksum, streamIndex);
+}
+
 void decodeNFrames(const std::shared_ptr<android::Codec2Client::Component>& component,
                    std::mutex& queueLock, std::condition_variable& queueCondition,
                    std::list<std::unique_ptr<C2Work>>& workQueue,
@@ -446,6 +546,19 @@
     ASSERT_EQ(producer->setSidebandStream(nativeHandle), NO_ERROR);
 }
 
+// Config output pixel format
+bool Codec2VideoDecHidlTestBase::configPixelFormat(uint32_t format) {
+    std::vector<std::unique_ptr<C2SettingResult>> failures;
+    C2StreamPixelFormatInfo::output pixelformat(0u, format);
+
+    std::vector<C2Param*> configParam{&pixelformat};
+    c2_status_t status = mComponent->config(configParam, C2_DONT_BLOCK, &failures);
+    if (status == C2_OK && failures.size() == 0u) {
+        return true;
+    }
+    return false;
+}
+
 class Codec2VideoDecDecodeTest
     : public Codec2VideoDecHidlTestBase,
       public ::testing::WithParamInterface<
@@ -463,40 +576,61 @@
 
     uint32_t streamIndex = std::stoi(std::get<2>(GetParam()));
     bool signalEOS = !std::get<2>(GetParam()).compare("true");
-    char mURL[512], info[512];
-    std::ifstream eleStream, eleInfo;
+    mTimestampDevTest = true;
+
+    char mURL[512], info[512], chksum[512];
+    android::Vector<FrameInfo> Info;
+
     strcpy(mURL, sResourceDir.c_str());
     strcpy(info, sResourceDir.c_str());
-    GetURLForComponent(mCompName, mURL, info, streamIndex);
+    strcpy(chksum, sResourceDir.c_str());
 
-    eleInfo.open(info);
-    ASSERT_EQ(eleInfo.is_open(), true) << mURL << " - file not found";
-    android::Vector<FrameInfo> Info;
-    int bytesCount = 0;
-    uint32_t flags = 0;
-    uint32_t timestamp = 0;
-    mTimestampDevTest = true;
+    GetURLChksmForComponent(mCompName, mURL, info, chksum, streamIndex);
+    if (!(strcmp(mURL, sResourceDir.c_str())) || !(strcmp(info, sResourceDir.c_str()))) {
+        ALOGV("Skipping Test, Stream not available");
+        return;
+    }
+    mMd5Enable = true;
+    if (!strcmp(chksum, sResourceDir.c_str())) mMd5Enable = false;
+
+    uint32_t format = HAL_PIXEL_FORMAT_YCBCR_420_888;
+    if (!configPixelFormat(format)) {
+        std::cout << "[   WARN   ] Test Skipped PixelFormat not configured\n";
+        return;
+    }
+
     mFlushedIndices.clear();
     mTimestampUslist.clear();
-    while (1) {
-        if (!(eleInfo >> bytesCount)) break;
-        eleInfo >> flags;
-        eleInfo >> timestamp;
-        bool codecConfig = flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
-        bool nonDisplayFrame = ((flags & FLAG_NON_DISPLAY_FRAME) != 0);
-        if (mTimestampDevTest && !codecConfig && !nonDisplayFrame)
-            mTimestampUslist.push_back(timestamp);
-        Info.push_back({bytesCount, flags, timestamp});
-    }
-    eleInfo.close();
+
+    int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
+    ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << info;
 
     ASSERT_EQ(mComponent->start(), C2_OK);
     // Reset total no of frames received
     mFramesReceived = 0;
     mTimestampUs = 0;
     ALOGV("mURL : %s", mURL);
+    std::ifstream eleStream;
     eleStream.open(mURL, std::ifstream::binary);
     ASSERT_EQ(eleStream.is_open(), true);
+
+    size_t refChksmSize = 0;
+    std::ifstream refChksum;
+    if (mMd5Enable) {
+        ALOGV("chksum file name: %s", chksum);
+        refChksum.open(chksum, std::ifstream::binary | std::ifstream::ate);
+        ASSERT_EQ(refChksum.is_open(), true);
+        refChksmSize = refChksum.tellg();
+        refChksum.seekg(0, std::ifstream::beg);
+
+        ALOGV("chksum Size %zu ", refChksmSize);
+        mRefMd5 = (char*)malloc(refChksmSize);
+        ASSERT_NE(mRefMd5, nullptr);
+        refChksum.read(mRefMd5, refChksmSize);
+        ASSERT_EQ(refChksum.gcount(), refChksmSize);
+        refChksum.close();
+    }
+
     ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
                                           mFlushedIndices, mLinearPool, eleStream, &Info, 0,
                                           (int)Info.size(), signalEOS));
@@ -504,7 +638,7 @@
     // If EOS is not sent, sending empty input with EOS flag
     size_t infoSize = Info.size();
     if (!signalEOS) {
-        ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1));
+        waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1);
         ASSERT_NO_FATAL_FAILURE(testInputBuffer(mComponent, mQueueLock, mWorkQueue,
                                                 C2FrameData::FLAG_END_OF_STREAM, false));
         infoSize += 1;
@@ -513,7 +647,7 @@
     // consumed
     if (!mEos) {
         ALOGV("Waiting for input consumption");
-        ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+        waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
     }
 
     eleStream.close();
@@ -523,8 +657,17 @@
         ASSERT_TRUE(false);
     }
 
+    if (mRefMd5 != nullptr) free(mRefMd5);
+    if (mMd5Enable && refChksmSize != mMd5Offset) {
+        ALOGE("refChksum size and generated chksum size mismatch refChksum size %zu generated "
+              "chksum size %" PRId64 "",
+              refChksmSize, mMd5Offset);
+        ASSERT_TRUE(false);
+    }
+
     if (mTimestampDevTest) EXPECT_EQ(mTimestampUslist.empty(), true);
     ASSERT_EQ(mComponent->stop(), C2_OK);
+    ASSERT_EQ(mWorkResult, C2_OK);
 }
 
 // Adaptive Test
@@ -622,7 +765,7 @@
     // blocking call to ensures application to Wait till all the inputs are
     // consumed
     ALOGV("Waiting for input consumption");
-    ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+    waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
 
     if (mFramesReceived != ((Info.size()) + 1)) {
         ALOGE("Input buffer count and Output buffer count mismatch");
@@ -631,6 +774,7 @@
     }
 
     if (mTimestampDevTest) EXPECT_EQ(mTimestampUslist.empty(), true);
+    ASSERT_EQ(mWorkResult, C2_OK);
 }
 
 // thumbnail test
@@ -639,26 +783,16 @@
     if (mDisableTest) GTEST_SKIP() << "Test is disabled";
 
     char mURL[512], info[512];
-    std::ifstream eleStream, eleInfo;
+    android::Vector<FrameInfo> Info;
 
     strcpy(mURL, sResourceDir.c_str());
     strcpy(info, sResourceDir.c_str());
     GetURLForComponent(mCompName, mURL, info);
 
-    eleInfo.open(info);
-    ASSERT_EQ(eleInfo.is_open(), true);
-    android::Vector<FrameInfo> Info;
-    int bytesCount = 0;
-    uint32_t flags = 0;
-    uint32_t timestamp = 0;
-    while (1) {
-        if (!(eleInfo >> bytesCount)) break;
-        eleInfo >> flags;
-        eleInfo >> timestamp;
-        Info.push_back({bytesCount, flags, timestamp});
-    }
-    eleInfo.close();
+    int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
+    ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << info;
 
+    uint32_t flags = 0;
     for (size_t i = 0; i < MAX_ITERATIONS; i++) {
         ASSERT_EQ(mComponent->start(), C2_OK);
 
@@ -671,18 +805,21 @@
             if (Info[j].flags) flags = 1u << (Info[j].flags - 1);
 
         } while (!(flags & SYNC_FRAME));
+
+        std::ifstream eleStream;
         eleStream.open(mURL, std::ifstream::binary);
         ASSERT_EQ(eleStream.is_open(), true);
         ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
                                               mFlushedIndices, mLinearPool, eleStream, &Info, 0,
                                               j + 1));
-        ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+        waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
         eleStream.close();
         EXPECT_GE(mFramesReceived, 1U);
         ASSERT_EQ(mEos, true);
         ASSERT_EQ(mComponent->stop(), C2_OK);
     }
     ASSERT_EQ(mComponent->release(), C2_OK);
+    ASSERT_EQ(mWorkResult, C2_OK);
 }
 
 TEST_P(Codec2VideoDecHidlTest, EOSTest) {
@@ -723,72 +860,59 @@
     ASSERT_EQ(mEos, true);
     ASSERT_EQ(mWorkQueue.size(), (size_t)MAX_INPUT_BUFFERS);
     ASSERT_EQ(mComponent->stop(), C2_OK);
+    ASSERT_EQ(mWorkResult, C2_OK);
 }
 
 TEST_P(Codec2VideoDecHidlTest, FlushTest) {
     description("Tests Flush calls");
     if (mDisableTest) GTEST_SKIP() << "Test is disabled";
-    typedef std::unique_lock<std::mutex> ULock;
+
     ASSERT_EQ(mComponent->start(), C2_OK);
+
     char mURL[512], info[512];
-    std::ifstream eleStream, eleInfo;
+    android::Vector<FrameInfo> Info;
 
     strcpy(mURL, sResourceDir.c_str());
     strcpy(info, sResourceDir.c_str());
     GetURLForComponent(mCompName, mURL, info);
 
-    eleInfo.open(info);
-    ASSERT_EQ(eleInfo.is_open(), true);
-    android::Vector<FrameInfo> Info;
-    int bytesCount = 0;
-    uint32_t flags = 0;
-    uint32_t timestamp = 0;
     mFlushedIndices.clear();
-    while (1) {
-        if (!(eleInfo >> bytesCount)) break;
-        eleInfo >> flags;
-        eleInfo >> timestamp;
-        Info.push_back({bytesCount, flags, timestamp});
-    }
-    eleInfo.close();
+
+    int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
+    ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << info;
 
     ALOGV("mURL : %s", mURL);
-    eleStream.open(mURL, std::ifstream::binary);
-    ASSERT_EQ(eleStream.is_open(), true);
-    // Decode 128 frames and flush. here 128 is chosen to ensure there is a key
-    // frame after this so that the below section can be covered for all
-    // components
-    uint32_t numFramesFlushed = 128;
-    ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
-                                          mFlushedIndices, mLinearPool, eleStream, &Info, 0,
-                                          numFramesFlushed, false));
+
     // flush
     std::list<std::unique_ptr<C2Work>> flushedWork;
     c2_status_t err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
     ASSERT_EQ(err, C2_OK);
-    ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
-                                                   (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
+    ASSERT_NO_FATAL_FAILURE(
+            verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
+    ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
 
-    {
-        // Update mFlushedIndices based on the index received from flush()
-        ULock l(mQueueLock);
-        for (std::unique_ptr<C2Work>& work : flushedWork) {
-            ASSERT_NE(work, nullptr);
-            auto frameIndexIt = std::find(mFlushedIndices.begin(), mFlushedIndices.end(),
-                                          work->input.ordinal.frameIndex.peeku());
-            if (!mFlushedIndices.empty() && (frameIndexIt != mFlushedIndices.end())) {
-                mFlushedIndices.erase(frameIndexIt);
-                work->input.buffers.clear();
-                work->worklets.clear();
-                mWorkQueue.push_back(std::move(work));
-            }
-        }
-    }
+    std::ifstream eleStream;
+    eleStream.open(mURL, std::ifstream::binary);
+    ASSERT_EQ(eleStream.is_open(), true);
+    // Decode 30 frames and flush. here 30 is chosen to ensure there is a key
+    // frame after this so that the below section can be covered for all
+    // components
+    uint32_t numFramesFlushed = FLUSH_INTERVAL;
+    ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+                                          mFlushedIndices, mLinearPool, eleStream, &Info, 0,
+                                          numFramesFlushed, false));
+    // flush
+    err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
+    ASSERT_EQ(err, C2_OK);
+    waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+                           (size_t)MAX_INPUT_BUFFERS - flushedWork.size());
+    ASSERT_NO_FATAL_FAILURE(
+            verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
+    ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
     // Seek to next key frame and start decoding till the end
-    mFlushedIndices.clear();
     int index = numFramesFlushed;
     bool keyFrame = false;
-    flags = 0;
+    uint32_t flags = 0;
     while (index < (int)Info.size()) {
         if (Info[index].flags) flags = 1u << (Info[index].flags - 1);
         if ((flags & SYNC_FRAME) == SYNC_FRAME) {
@@ -807,25 +931,13 @@
     eleStream.close();
     err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
     ASSERT_EQ(err, C2_OK);
-    ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
-                                                   (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
-    {
-        // Update mFlushedIndices based on the index received from flush()
-        ULock l(mQueueLock);
-        for (std::unique_ptr<C2Work>& work : flushedWork) {
-            ASSERT_NE(work, nullptr);
-            uint64_t frameIndex = work->input.ordinal.frameIndex.peeku();
-            std::list<uint64_t>::iterator frameIndexIt =
-                    std::find(mFlushedIndices.begin(), mFlushedIndices.end(), frameIndex);
-            if (!mFlushedIndices.empty() && (frameIndexIt != mFlushedIndices.end())) {
-                mFlushedIndices.erase(frameIndexIt);
-                work->input.buffers.clear();
-                work->worklets.clear();
-                mWorkQueue.push_back(std::move(work));
-            }
-        }
-    }
-    ASSERT_EQ(mFlushedIndices.empty(), true);
+    waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+                           (size_t)MAX_INPUT_BUFFERS - flushedWork.size());
+    ASSERT_NO_FATAL_FAILURE(
+            verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
+    ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
+    // TODO: (b/154671521)
+    // Add assert for mWorkResult
     ASSERT_EQ(mComponent->stop(), C2_OK);
 }
 
@@ -880,7 +992,7 @@
     // consumed
     if (!mEos) {
         ALOGV("Waiting for input consumption");
-        ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+        waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
     }
 
     eleStream.close();
@@ -891,6 +1003,110 @@
     }
 }
 
+class Codec2VideoDecCsdInputTests
+    : public Codec2VideoDecHidlTestBase,
+      public ::testing::WithParamInterface<std::tuple<std::string, std::string, std::string>> {
+    void getParams() {
+        mInstanceName = std::get<0>(GetParam());
+        mComponentName = std::get<1>(GetParam());
+    }
+};
+
+// Test the codecs for the following
+// start - csd - data… - (with/without)flush - data… - flush - data…
+TEST_P(Codec2VideoDecCsdInputTests, CSDFlushTest) {
+    description("Tests codecs for flush at different states");
+    if (mDisableTest) GTEST_SKIP() << "Test is disabled";
+
+    char mURL[512], info[512];
+
+    android::Vector<FrameInfo> Info;
+
+    strcpy(mURL, sResourceDir.c_str());
+    strcpy(info, sResourceDir.c_str());
+    GetURLForComponent(mCompName, mURL, info);
+
+    int32_t numCsds = populateInfoVector(info, &Info, mTimestampDevTest, &mTimestampUslist);
+    ASSERT_GE(numCsds, 0) << "Error in parsing input info file";
+
+    ASSERT_EQ(mComponent->start(), C2_OK);
+
+    ALOGV("mURL : %s", mURL);
+    std::ifstream eleStream;
+    eleStream.open(mURL, std::ifstream::binary);
+    ASSERT_EQ(eleStream.is_open(), true);
+    bool flushedDecoder = false;
+    bool signalEOS = false;
+    bool keyFrame = false;
+    bool flushCsd = !std::get<2>(GetParam()).compare("true");
+
+    ALOGV("sending %d csd data ", numCsds);
+    int framesToDecode = numCsds;
+    ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+                                          mFlushedIndices, mLinearPool, eleStream, &Info, 0,
+                                          framesToDecode, false));
+    c2_status_t err = C2_OK;
+    std::list<std::unique_ptr<C2Work>> flushedWork;
+    if (numCsds && flushCsd) {
+        // We wait for all the CSD buffers to get consumed.
+        // Once we have received all CSD work back, we call flush
+        waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
+
+        err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
+        ASSERT_EQ(err, C2_OK);
+        flushedDecoder = true;
+        waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+                               MAX_INPUT_BUFFERS - flushedWork.size());
+        ASSERT_NO_FATAL_FAILURE(
+                verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
+        ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
+    }
+
+    int offset = framesToDecode;
+    uint32_t flags = 0;
+    while (1) {
+        while (offset < (int)Info.size()) {
+            flags = 0;
+            if (Info[offset].flags) flags = 1u << (Info[offset].flags - 1);
+            if (flags & SYNC_FRAME) {
+                keyFrame = true;
+                break;
+            }
+            eleStream.ignore(Info[offset].bytesCount);
+            offset++;
+        }
+        if (keyFrame) {
+            framesToDecode = c2_min(FLUSH_INTERVAL, (int)Info.size() - offset);
+            if (framesToDecode < FLUSH_INTERVAL) signalEOS = true;
+            ASSERT_NO_FATAL_FAILURE(decodeNFrames(
+                    mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices,
+                    mLinearPool, eleStream, &Info, offset, framesToDecode, signalEOS));
+            offset += framesToDecode;
+        }
+        err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
+        ASSERT_EQ(err, C2_OK);
+        keyFrame = false;
+        // blocking call to ensures application to Wait till remaining
+        // 'non-flushed' inputs are consumed
+        waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+                               MAX_INPUT_BUFFERS - flushedWork.size());
+        ASSERT_NO_FATAL_FAILURE(
+                verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
+        ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
+        if (signalEOS || offset >= (int)Info.size()) {
+            break;
+        }
+    }
+    if (!signalEOS) {
+        ASSERT_NO_FATAL_FAILURE(testInputBuffer(mComponent, mQueueLock, mWorkQueue,
+                                                C2FrameData::FLAG_END_OF_STREAM, false));
+        waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
+    }
+    eleStream.close();
+    ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
+    ASSERT_EQ(mComponent->stop(), C2_OK);
+}
+
 INSTANTIATE_TEST_SUITE_P(PerInstance, Codec2VideoDecHidlTest, testing::ValuesIn(kTestParameters),
                          android::hardware::PrintInstanceTupleNameToString<>);
 
@@ -899,6 +1115,10 @@
                          testing::ValuesIn(kDecodeTestParameters),
                          android::hardware::PrintInstanceTupleNameToString<>);
 
+INSTANTIATE_TEST_SUITE_P(CsdInputs, Codec2VideoDecCsdInputTests,
+                         testing::ValuesIn(kCsdFlushTestParameters),
+                         android::hardware::PrintInstanceTupleNameToString<>);
+
 }  // anonymous namespace
 
 // TODO : Video specific configuration Test
@@ -917,6 +1137,11 @@
                 std::make_tuple(std::get<0>(params), std::get<1>(params), "2", "false"));
         kDecodeTestParameters.push_back(
                 std::make_tuple(std::get<0>(params), std::get<1>(params), "2", "true"));
+
+        kCsdFlushTestParameters.push_back(
+                std::make_tuple(std::get<0>(params), std::get<1>(params), "true"));
+        kCsdFlushTestParameters.push_back(
+                std::make_tuple(std::get<0>(params), std::get<1>(params), "false"));
     }
 
     // Set the resource directory based on command line args.
@@ -930,4 +1155,4 @@
 
     ::testing::InitGoogleTest(&argc, argv);
     return RUN_ALL_TESTS();
-}
\ No newline at end of file
+}
diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.xml b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.xml
index 63e7a69..a1049df 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.xml
+++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.xml
@@ -51,6 +51,14 @@
         <option name="push-file" key="bbb_av1_176_144.info" value="/data/local/tmp/media/bbb_av1_176_144.info" />
         <option name="push-file" key="bbb_vp9_704x480_280kbps_24fps_altref_2.vp9" value="/data/local/tmp/media/bbb_vp9_704x480_280kbps_24fps_altref_2.vp9" />
         <option name="push-file" key="bbb_vp9_704x480_280kbps_24fps_altref_2.info" value="/data/local/tmp/media/bbb_vp9_704x480_280kbps_24fps_altref_2.info" />
+        <option name="push-file" key="bbb_avc_176x144_300kbps_60fps_chksum.md5" value="/data/local/tmp/media/bbb_avc_176x144_300kbps_60fps_chksum.md5" />
+        <option name="push-file" key="bbb_avc_640x360_768kbps_30fps_chksum.md5" value="/data/local/tmp/media/bbb_avc_640x360_768kbps_30fps_chksum.md5" />
+        <option name="push-file" key="bbb_hevc_176x144_176kbps_60fps_chksum.md5" value="/data/local/tmp/media/bbb_hevc_176x144_176kbps_60fps_chksum.md5" />
+        <option name="push-file" key="bbb_hevc_640x360_1600kbps_30fps_chksum.md5" value="/data/local/tmp/media/bbb_hevc_640x360_1600kbps_30fps_chksum.md5" />
+        <option name="push-file" key="bbb_vp8_640x360_2mbps_30fps_chksm.md5" value="/data/local/tmp/media/bbb_vp8_640x360_2mbps_30fps_chksm.md5" />
+        <option name="push-file" key="bbb_vp9_640x360_1600kbps_30fps_chksm.md5" value="/data/local/tmp/media/bbb_vp9_640x360_1600kbps_30fps_chksm.md5" />
+        <option name="push-file" key="bbb_av1_640_360_chksum.md5" value="/data/local/tmp/media/bbb_av1_640_360_chksum.md5" />
+        <option name="push-file" key="bbb_av1_176_144_chksm.md5" value="/data/local/tmp/media/bbb_av1_176_144_chksm.md5" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.GTest" >
diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
index 823e11b..9e425d2 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
@@ -492,7 +492,7 @@
     // If EOS is not sent, sending empty input with EOS flag
     inputFrames += ENC_NUM_FRAMES;
     if (!signalEOS) {
-        ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1));
+        waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1);
         ASSERT_NO_FATAL_FAILURE(testInputBuffer(mComponent, mQueueLock, mWorkQueue,
                                                 C2FrameData::FLAG_END_OF_STREAM, false));
         inputFrames += 1;
@@ -501,7 +501,7 @@
     // blocking call to ensures application to Wait till all the inputs are
     // consumed
     ALOGD("Waiting for input consumption");
-    ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+    waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
 
     eleStream.close();
     if (mFramesReceived != inputFrames) {
@@ -520,6 +520,9 @@
 
     if (mTimestampDevTest) EXPECT_EQ(mTimestampUslist.empty(), true);
     ASSERT_EQ(mComponent->stop(), C2_OK);
+
+    // TODO: (b/155534991)
+    // Add assert for mFailedWorkReceived
 }
 
 TEST_P(Codec2VideoEncHidlTest, EOSTest) {
@@ -560,13 +563,13 @@
     }
     ASSERT_EQ(mEos, true);
     ASSERT_EQ(mComponent->stop(), C2_OK);
+    ASSERT_EQ(mFailedWorkReceived, 0);
 }
 
 TEST_P(Codec2VideoEncHidlTest, FlushTest) {
     description("Test Request for flush");
     if (mDisableTest) GTEST_SKIP() << "Test is disabled";
 
-    typedef std::unique_lock<std::mutex> ULock;
     char mURL[512];
     int32_t nWidth = ENC_DEFAULT_FRAME_WIDTH;
     int32_t nHeight = ENC_DEFAULT_FRAME_HEIGHT;
@@ -587,9 +590,17 @@
     eleStream.open(mURL, std::ifstream::binary);
     ASSERT_EQ(eleStream.is_open(), true);
     ALOGV("mURL : %s", mURL);
+    // flush
+    std::list<std::unique_ptr<C2Work>> flushedWork;
+    c2_status_t err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
+    ASSERT_EQ(err, C2_OK);
+    ASSERT_NO_FATAL_FAILURE(
+            verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
+    ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
+
     ASSERT_NO_FATAL_FAILURE(encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
                                           mFlushedIndices, mGraphicPool, eleStream, mDisableTest, 0,
-                                          numFramesFlushed, nWidth, nHeight));
+                                          numFramesFlushed, nWidth, nHeight, false, false));
     // mDisableTest will be set if buffer was not fetched properly.
     // This may happen when resolution is not proper but config succeeded
     // In this cases, we skip encoding the input stream
@@ -599,29 +610,14 @@
         return;
     }
 
-    std::list<std::unique_ptr<C2Work>> flushedWork;
-    c2_status_t err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
+    // flush
+    err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
     ASSERT_EQ(err, C2_OK);
-    ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
-                                                   (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
-    uint64_t frameIndex;
-    {
-        // Update mFlushedIndices based on the index received from flush()
-        ULock l(mQueueLock);
-        for (std::unique_ptr<C2Work>& work : flushedWork) {
-            ASSERT_NE(work, nullptr);
-            frameIndex = work->input.ordinal.frameIndex.peeku();
-            std::list<uint64_t>::iterator frameIndexIt =
-                    std::find(mFlushedIndices.begin(), mFlushedIndices.end(), frameIndex);
-            if (!mFlushedIndices.empty() && (frameIndexIt != mFlushedIndices.end())) {
-                mFlushedIndices.erase(frameIndexIt);
-                work->input.buffers.clear();
-                work->worklets.clear();
-                mWorkQueue.push_back(std::move(work));
-            }
-        }
-    }
-    mFlushedIndices.clear();
+    waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+                           (size_t)MAX_INPUT_BUFFERS - flushedWork.size());
+    ASSERT_NO_FATAL_FAILURE(
+            verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
+    ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
     ASSERT_NO_FATAL_FAILURE(encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
                                           mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
                                           numFramesFlushed, numFrames - numFramesFlushed, nWidth,
@@ -638,25 +634,13 @@
 
     err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
     ASSERT_EQ(err, C2_OK);
-    ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
-                                                   (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
-    {
-        // Update mFlushedIndices based on the index received from flush()
-        ULock l(mQueueLock);
-        for (std::unique_ptr<C2Work>& work : flushedWork) {
-            ASSERT_NE(work, nullptr);
-            frameIndex = work->input.ordinal.frameIndex.peeku();
-            std::list<uint64_t>::iterator frameIndexIt =
-                    std::find(mFlushedIndices.begin(), mFlushedIndices.end(), frameIndex);
-            if (!mFlushedIndices.empty() && (frameIndexIt != mFlushedIndices.end())) {
-                mFlushedIndices.erase(frameIndexIt);
-                work->input.buffers.clear();
-                work->worklets.clear();
-                mWorkQueue.push_back(std::move(work));
-            }
-        }
-    }
-    ASSERT_EQ(mFlushedIndices.empty(), true);
+    waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+                           (size_t)MAX_INPUT_BUFFERS - flushedWork.size());
+    ASSERT_NO_FATAL_FAILURE(
+            verifyFlushOutput(flushedWork, mWorkQueue, mFlushedIndices, mQueueLock));
+    ASSERT_EQ(mWorkQueue.size(), MAX_INPUT_BUFFERS);
+    // TODO: (b/154671521)
+    // Add assert for mFailedWorkReceived
     ASSERT_EQ(mComponent->stop(), C2_OK);
 }
 
@@ -691,7 +675,7 @@
     // blocking call to ensures application to Wait till all the inputs are
     // consumed
     ALOGD("Waiting for input consumption");
-    ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+    waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
 
     if (mFramesReceived != 3) {
         std::cout << "[   WARN   ] Component didn't receive all buffers back \n";
@@ -746,7 +730,7 @@
     }
 
     ALOGD("Waiting for input consumption");
-    ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+    waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue);
 
     ASSERT_EQ(mEos, true);
     ASSERT_EQ(mComponent->stop(), C2_OK);
diff --git a/media/codec2/hidl/1.0/vts/functional/video/media_c2_video_hidl_test_common.h b/media/codec2/hidl/1.0/vts/functional/video/media_c2_video_hidl_test_common.h
index 9c1a5cb..d3a693b 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/media_c2_video_hidl_test_common.h
+++ b/media/codec2/hidl/1.0/vts/functional/video/media_c2_video_hidl_test_common.h
@@ -22,7 +22,6 @@
 #define ENC_DEFAULT_FRAME_WIDTH 352
 #define ENC_DEFAULT_FRAME_HEIGHT 288
 #define MAX_ITERATIONS 128
-#define FLAG_NON_DISPLAY_FRAME (1 << 4)
 
 #define ALIGN(_sz, _align) ((_sz + (_align - 1)) & ~(_align - 1))
 
diff --git a/media/codec2/hidl/1.1/utils/Android.bp b/media/codec2/hidl/1.1/utils/Android.bp
index 8fddf98..386f6e2 100644
--- a/media/codec2/hidl/1.1/utils/Android.bp
+++ b/media/codec2/hidl/1.1/utils/Android.bp
@@ -52,6 +52,7 @@
 cc_library {
     name: "libcodec2_hidl@1.1",
     vendor_available: true,
+    min_sdk_version: "29",
 
     defaults: ["hidl_defaults"],
 
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 83d3ada..4b158bd 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -18,6 +18,8 @@
 #define LOG_TAG "CCodecBufferChannel"
 #include <utils/Log.h>
 
+#include <algorithm>
+#include <list>
 #include <numeric>
 
 #include <C2AllocatorGralloc.h>
@@ -128,97 +130,6 @@
     count->value = -1;
 }
 
-// CCodecBufferChannel::ReorderStash
-
-CCodecBufferChannel::ReorderStash::ReorderStash() {
-    clear();
-}
-
-void CCodecBufferChannel::ReorderStash::clear() {
-    mPending.clear();
-    mStash.clear();
-    mDepth = 0;
-    mKey = C2Config::ORDINAL;
-}
-
-void CCodecBufferChannel::ReorderStash::flush() {
-    mPending.clear();
-    mStash.clear();
-}
-
-void CCodecBufferChannel::ReorderStash::setDepth(uint32_t depth) {
-    mPending.splice(mPending.end(), mStash);
-    mDepth = depth;
-}
-
-void CCodecBufferChannel::ReorderStash::setKey(C2Config::ordinal_key_t key) {
-    mPending.splice(mPending.end(), mStash);
-    mKey = key;
-}
-
-bool CCodecBufferChannel::ReorderStash::pop(Entry *entry) {
-    if (mPending.empty()) {
-        return false;
-    }
-    entry->buffer     = mPending.front().buffer;
-    entry->timestamp  = mPending.front().timestamp;
-    entry->flags      = mPending.front().flags;
-    entry->ordinal    = mPending.front().ordinal;
-    mPending.pop_front();
-    return true;
-}
-
-void CCodecBufferChannel::ReorderStash::emplace(
-        const std::shared_ptr<C2Buffer> &buffer,
-        int64_t timestamp,
-        int32_t flags,
-        const C2WorkOrdinalStruct &ordinal) {
-    bool eos = flags & MediaCodec::BUFFER_FLAG_EOS;
-    if (!buffer && eos) {
-        // TRICKY: we may be violating ordering of the stash here. Because we
-        // don't expect any more emplace() calls after this, the ordering should
-        // not matter.
-        mStash.emplace_back(buffer, timestamp, flags, ordinal);
-    } else {
-        flags = flags & ~MediaCodec::BUFFER_FLAG_EOS;
-        auto it = mStash.begin();
-        for (; it != mStash.end(); ++it) {
-            if (less(ordinal, it->ordinal)) {
-                break;
-            }
-        }
-        mStash.emplace(it, buffer, timestamp, flags, ordinal);
-        if (eos) {
-            mStash.back().flags = mStash.back().flags | MediaCodec::BUFFER_FLAG_EOS;
-        }
-    }
-    while (!mStash.empty() && mStash.size() > mDepth) {
-        mPending.push_back(mStash.front());
-        mStash.pop_front();
-    }
-}
-
-void CCodecBufferChannel::ReorderStash::defer(
-        const CCodecBufferChannel::ReorderStash::Entry &entry) {
-    mPending.push_front(entry);
-}
-
-bool CCodecBufferChannel::ReorderStash::hasPending() const {
-    return !mPending.empty();
-}
-
-bool CCodecBufferChannel::ReorderStash::less(
-        const C2WorkOrdinalStruct &o1, const C2WorkOrdinalStruct &o2) {
-    switch (mKey) {
-        case C2Config::ORDINAL:   return o1.frameIndex < o2.frameIndex;
-        case C2Config::TIMESTAMP: return o1.timestamp < o2.timestamp;
-        case C2Config::CUSTOM:    return o1.customOrdinal < o2.customOrdinal;
-        default:
-            ALOGD("Unrecognized key; default to timestamp");
-            return o1.frameIndex < o2.frameIndex;
-    }
-}
-
 // Input
 
 CCodecBufferChannel::Input::Input() : extraBuffers("extra") {}
@@ -708,7 +619,7 @@
 
 void CCodecBufferChannel::feedInputBufferIfAvailableInternal() {
     if (mInputMetEos ||
-           mReorderStash.lock()->hasPending() ||
+           mOutput.lock()->buffers->hasPending() ||
            mPipelineWatcher.lock()->pipelineFull()) {
         return;
     } else {
@@ -989,17 +900,6 @@
         return UNKNOWN_ERROR;
     }
 
-    {
-        Mutexed<ReorderStash>::Locked reorder(mReorderStash);
-        reorder->clear();
-        if (reorderDepth) {
-            reorder->setDepth(reorderDepth.value);
-        }
-        if (reorderKey) {
-            reorder->setKey(reorderKey.value);
-        }
-    }
-
     uint32_t inputDelayValue = inputDelay ? inputDelay.value : 0;
     uint32_t pipelineDelayValue = pipelineDelay ? pipelineDelay.value : 0;
     uint32_t outputDelayValue = outputDelay ? outputDelay.value : 0;
@@ -1268,6 +1168,13 @@
         }
         output->buffers->setFormat(outputFormat);
 
+        output->buffers->clearStash();
+        if (reorderDepth) {
+            output->buffers->setReorderDepth(reorderDepth.value);
+        }
+        if (reorderKey) {
+            output->buffers->setReorderKey(reorderKey.value);
+        }
 
         // Try to set output surface to created block pool if given.
         if (outputSurface) {
@@ -1337,62 +1244,98 @@
         return UNKNOWN_ERROR;
     }
     size_t numInputSlots = mInput.lock()->numSlots;
-    std::vector<sp<MediaCodecBuffer>> toBeQueued;
-    for (size_t i = 0; i < numInputSlots; ++i) {
+
+    struct ClientInputBuffer {
         size_t index;
         sp<MediaCodecBuffer> buffer;
-        {
-            Mutexed<Input>::Locked input(mInput);
-            if (!input->buffers->requestNewBuffer(&index, &buffer)) {
-                if (i == 0) {
-                    ALOGW("[%s] start: cannot allocate memory at all", mName);
-                    return NO_MEMORY;
-                } else {
-                    ALOGV("[%s] start: cannot allocate memory, only %zu buffers allocated",
-                            mName, i);
-                }
+        size_t capacity;
+    };
+    std::list<ClientInputBuffer> clientInputBuffers;
+
+    {
+        Mutexed<Input>::Locked input(mInput);
+        while (clientInputBuffers.size() < numInputSlots) {
+            ClientInputBuffer clientInputBuffer;
+            if (!input->buffers->requestNewBuffer(&clientInputBuffer.index,
+                                                  &clientInputBuffer.buffer)) {
                 break;
             }
+            clientInputBuffer.capacity = clientInputBuffer.buffer->capacity();
+            clientInputBuffers.emplace_back(std::move(clientInputBuffer));
         }
-        if (buffer) {
-            Mutexed<std::list<sp<ABuffer>>>::Locked configs(mFlushedConfigs);
-            ALOGV("[%s] input buffer %zu available", mName, index);
-            bool post = true;
-            if (!configs->empty()) {
+    }
+    if (clientInputBuffers.empty()) {
+        ALOGW("[%s] start: cannot allocate memory at all", mName);
+        return NO_MEMORY;
+    } else if (clientInputBuffers.size() < numInputSlots) {
+        ALOGD("[%s] start: cannot allocate memory for all slots, "
+              "only %zu buffers allocated",
+              mName, clientInputBuffers.size());
+    } else {
+        ALOGV("[%s] %zu initial input buffers available",
+              mName, clientInputBuffers.size());
+    }
+    // Sort input buffers by their capacities in increasing order.
+    clientInputBuffers.sort(
+            [](const ClientInputBuffer& a, const ClientInputBuffer& b) {
+                return a.capacity < b.capacity;
+            });
+
+    {
+        Mutexed<std::list<sp<ABuffer>>>::Locked configs(mFlushedConfigs);
+        if (!configs->empty()) {
+            while (!configs->empty()) {
                 sp<ABuffer> config = configs->front();
                 configs->pop_front();
-                if (buffer->capacity() >= config->size()) {
-                    memcpy(buffer->base(), config->data(), config->size());
-                    buffer->setRange(0, config->size());
-                    buffer->meta()->clear();
-                    buffer->meta()->setInt64("timeUs", 0);
-                    buffer->meta()->setInt32("csd", 1);
-                    post = false;
-                } else {
-                    ALOGD("[%s] buffer capacity too small for the config (%zu < %zu)",
-                            mName, buffer->capacity(), config->size());
+                // Find the smallest input buffer that can fit the config.
+                auto i = std::find_if(
+                        clientInputBuffers.begin(),
+                        clientInputBuffers.end(),
+                        [cfgSize = config->size()](const ClientInputBuffer& b) {
+                            return b.capacity >= cfgSize;
+                        });
+                if (i == clientInputBuffers.end()) {
+                    ALOGW("[%s] no input buffer large enough for the config "
+                          "(%zu bytes)",
+                          mName, config->size());
+                    return NO_MEMORY;
                 }
-            } else if (oStreamFormat.value == C2BufferData::LINEAR && i == 0
-                        && (!prepend || prepend.value == PREPEND_HEADER_TO_NONE)) {
-                // WORKAROUND: Some apps expect CSD available without queueing
-                //             any input. Queue an empty buffer to get the CSD.
-                buffer->setRange(0, 0);
+                sp<MediaCodecBuffer> buffer = i->buffer;
+                memcpy(buffer->base(), config->data(), config->size());
+                buffer->setRange(0, config->size());
                 buffer->meta()->clear();
                 buffer->meta()->setInt64("timeUs", 0);
-                post = false;
+                buffer->meta()->setInt32("csd", 1);
+                if (queueInputBufferInternal(buffer) != OK) {
+                    ALOGW("[%s] Error while queueing a flushed config",
+                          mName);
+                    return UNKNOWN_ERROR;
+                }
+                clientInputBuffers.erase(i);
             }
-            if (post) {
-                mCallback->onInputBufferAvailable(index, buffer);
-            } else {
-                toBeQueued.emplace_back(buffer);
+        } else if (oStreamFormat.value == C2BufferData::LINEAR &&
+                   (!prepend || prepend.value == PREPEND_HEADER_TO_NONE)) {
+            sp<MediaCodecBuffer> buffer = clientInputBuffers.front().buffer;
+            // WORKAROUND: Some apps expect CSD available without queueing
+            //             any input. Queue an empty buffer to get the CSD.
+            buffer->setRange(0, 0);
+            buffer->meta()->clear();
+            buffer->meta()->setInt64("timeUs", 0);
+            if (queueInputBufferInternal(buffer) != OK) {
+                ALOGW("[%s] Error while queueing an empty buffer to get CSD",
+                      mName);
+                return UNKNOWN_ERROR;
             }
+            clientInputBuffers.pop_front();
         }
     }
-    for (const sp<MediaCodecBuffer> &buffer : toBeQueued) {
-        if (queueInputBufferInternal(buffer) != OK) {
-            ALOGV("[%s] Error while queueing initial buffers", mName);
-        }
+
+    for (const ClientInputBuffer& clientInputBuffer: clientInputBuffers) {
+        mCallback->onInputBufferAvailable(
+                clientInputBuffer.index,
+                clientInputBuffer.buffer);
     }
+
     return OK;
 }
 
@@ -1464,9 +1407,9 @@
         Mutexed<Output>::Locked output(mOutput);
         if (output->buffers) {
             output->buffers->flush(flushedWork);
+            output->buffers->flushStash();
         }
     }
-    mReorderStash.lock()->flush();
     mPipelineWatcher.lock()->flush();
 }
 
@@ -1507,45 +1450,36 @@
         if (!output->buffers) {
             return false;
         }
-        if (outputFormat != nullptr) {
-            ALOGD("[%s] onWorkDone: output format changed to %s",
-                    mName, outputFormat->debugString().c_str());
-            output->buffers->setFormat(outputFormat);
-
-            AString mediaType;
-            if (outputFormat->findString(KEY_MIME, &mediaType)
-                    && mediaType == MIMETYPE_AUDIO_RAW) {
-                int32_t channelCount;
-                int32_t sampleRate;
-                if (outputFormat->findInt32(KEY_CHANNEL_COUNT, &channelCount)
-                        && outputFormat->findInt32(KEY_SAMPLE_RATE, &sampleRate)) {
-                    output->buffers->updateSkipCutBuffer(sampleRate, channelCount);
-                }
-            }
-        }
     }
 
-    if ((work->input.ordinal.frameIndex - mFirstValidFrameIndex.load()).peek() < 0) {
+    // Whether the output buffer should be reported to the client or not.
+    bool notifyClient = false;
+
+    if (work->result == C2_OK){
+        notifyClient = true;
+    } else if (work->result == C2_NOT_FOUND) {
+        ALOGD("[%s] flushed work; ignored.", mName);
+    } else {
+        // C2_OK and C2_NOT_FOUND are the only results that we accept for processing
+        // the config update.
+        ALOGD("[%s] work failed to complete: %d", mName, work->result);
+        mCCodecCallback->onError(work->result, ACTION_CODE_FATAL);
+        return false;
+    }
+
+    if ((work->input.ordinal.frameIndex -
+            mFirstValidFrameIndex.load()).peek() < 0) {
         // Discard frames from previous generation.
         ALOGD("[%s] Discard frames from previous generation.", mName);
-        return false;
+        notifyClient = false;
     }
 
     if (mInputSurface == nullptr && (work->worklets.size() != 1u
             || !work->worklets.front()
-            || !(work->worklets.front()->output.flags & C2FrameData::FLAG_INCOMPLETE))) {
-        mPipelineWatcher.lock()->onWorkDone(work->input.ordinal.frameIndex.peeku());
-    }
-
-    if (work->result == C2_NOT_FOUND) {
-        ALOGD("[%s] flushed work; ignored.", mName);
-        return true;
-    }
-
-    if (work->result != C2_OK) {
-        ALOGD("[%s] work failed to complete: %d", mName, work->result);
-        mCCodecCallback->onError(work->result, ACTION_CODE_FATAL);
-        return false;
+            || !(work->worklets.front()->output.flags &
+                 C2FrameData::FLAG_INCOMPLETE))) {
+        mPipelineWatcher.lock()->onWorkDone(
+                work->input.ordinal.frameIndex.peeku());
     }
 
     // NOTE: MediaCodec usage supposedly have only one worklet
@@ -1581,8 +1515,10 @@
             case C2PortReorderBufferDepthTuning::CORE_INDEX: {
                 C2PortReorderBufferDepthTuning::output reorderDepth;
                 if (reorderDepth.updateFrom(*param)) {
-                    bool secure = mComponent->getName().find(".secure") != std::string::npos;
-                    mReorderStash.lock()->setDepth(reorderDepth.value);
+                    bool secure = mComponent->getName().find(".secure") !=
+                                  std::string::npos;
+                    mOutput.lock()->buffers->setReorderDepth(
+                            reorderDepth.value);
                     ALOGV("[%s] onWorkDone: updated reorder depth to %u",
                           mName, reorderDepth.value);
                     size_t numOutputSlots = mOutput.lock()->numSlots;
@@ -1594,17 +1530,19 @@
                         output->maxDequeueBuffers += numInputSlots;
                     }
                     if (output->surface) {
-                        output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers);
+                        output->surface->setMaxDequeuedBufferCount(
+                                output->maxDequeueBuffers);
                     }
                 } else {
-                    ALOGD("[%s] onWorkDone: failed to read reorder depth", mName);
+                    ALOGD("[%s] onWorkDone: failed to read reorder depth",
+                          mName);
                 }
                 break;
             }
             case C2PortReorderKeySetting::CORE_INDEX: {
                 C2PortReorderKeySetting::output reorderKey;
                 if (reorderKey.updateFrom(*param)) {
-                    mReorderStash.lock()->setKey(reorderKey.value);
+                    mOutput.lock()->buffers->setReorderKey(reorderKey.value);
                     ALOGV("[%s] onWorkDone: updated reorder key to %u",
                           mName, reorderKey.value);
                 } else {
@@ -1619,7 +1557,8 @@
                         ALOGV("[%s] onWorkDone: updating pipeline delay %u",
                               mName, pipelineDelay.value);
                         newPipelineDelay = pipelineDelay.value;
-                        (void)mPipelineWatcher.lock()->pipelineDelay(pipelineDelay.value);
+                        (void)mPipelineWatcher.lock()->pipelineDelay(
+                                pipelineDelay.value);
                     }
                 }
                 if (param->forInput()) {
@@ -1628,7 +1567,8 @@
                         ALOGV("[%s] onWorkDone: updating input delay %u",
                               mName, inputDelay.value);
                         newInputDelay = inputDelay.value;
-                        (void)mPipelineWatcher.lock()->inputDelay(inputDelay.value);
+                        (void)mPipelineWatcher.lock()->inputDelay(
+                                inputDelay.value);
                     }
                 }
                 if (param->forOutput()) {
@@ -1636,8 +1576,10 @@
                     if (outputDelay.updateFrom(*param)) {
                         ALOGV("[%s] onWorkDone: updating output delay %u",
                               mName, outputDelay.value);
-                        bool secure = mComponent->getName().find(".secure") != std::string::npos;
-                        (void)mPipelineWatcher.lock()->outputDelay(outputDelay.value);
+                        bool secure = mComponent->getName().find(".secure") !=
+                                      std::string::npos;
+                        (void)mPipelineWatcher.lock()->outputDelay(
+                                outputDelay.value);
 
                         bool outputBuffersChanged = false;
                         size_t numOutputSlots = 0;
@@ -1648,7 +1590,8 @@
                                 return false;
                             }
                             output->outputDelay = outputDelay.value;
-                            numOutputSlots = outputDelay.value + kSmoothnessFactor;
+                            numOutputSlots = outputDelay.value +
+                                             kSmoothnessFactor;
                             if (output->numSlots < numOutputSlots) {
                                 output->numSlots = numOutputSlots;
                                 if (output->buffers->isArrayMode()) {
@@ -1667,7 +1610,7 @@
                             mCCodecCallback->onOutputBuffersChanged();
                         }
 
-                        uint32_t depth = mReorderStash.lock()->depth();
+                        uint32_t depth = mOutput.lock()->buffers->getReorderDepth();
                         Mutexed<OutputSurface>::Locked output(mOutputSurface);
                         output->maxDequeueBuffers = numOutputSlots + depth + kRenderingDepth;
                         if (!secure) {
@@ -1711,9 +1654,6 @@
         ALOGV("[%s] onWorkDone: output EOS", mName);
     }
 
-    sp<MediaCodecBuffer> outBuffer;
-    size_t index;
-
     // WORKAROUND: adjust output timestamp based on client input timestamp and codec
     // input timestamp. Codec output timestamp (in the timestamp field) shall correspond to
     // the codec input timestamp, but client output timestamp should (reported in timeUs)
@@ -1734,8 +1674,18 @@
           worklet->output.ordinal.timestamp.peekll(),
           timestamp.peekll());
 
+    // csd cannot be re-ordered and will always arrive first.
     if (initData != nullptr) {
         Mutexed<Output>::Locked output(mOutput);
+        if (output->buffers && outputFormat) {
+            output->buffers->updateSkipCutBuffer(outputFormat);
+            output->buffers->setFormat(outputFormat);
+        }
+        if (!notifyClient) {
+            return false;
+        }
+        size_t index;
+        sp<MediaCodecBuffer> outBuffer;
         if (output->buffers && output->buffers->registerCsd(initData, &index, &outBuffer) == OK) {
             outBuffer->meta()->setInt64("timeUs", timestamp.peek());
             outBuffer->meta()->setInt32("flags", MediaCodec::BUFFER_FLAG_CODECCONFIG);
@@ -1751,10 +1701,10 @@
         }
     }
 
-    if (!buffer && !flags) {
+    if (notifyClient && !buffer && !flags) {
         ALOGV("[%s] onWorkDone: Not reporting output buffer (%lld)",
               mName, work->input.ordinal.frameIndex.peekull());
-        return true;
+        notifyClient = false;
     }
 
     if (buffer) {
@@ -1773,66 +1723,65 @@
     }
 
     {
-        Mutexed<ReorderStash>::Locked reorder(mReorderStash);
-        reorder->emplace(buffer, timestamp.peek(), flags, worklet->output.ordinal);
-        if (flags & MediaCodec::BUFFER_FLAG_EOS) {
-            // Flush reorder stash
-            reorder->setDepth(0);
+        Mutexed<Output>::Locked output(mOutput);
+        if (!output->buffers) {
+            return false;
         }
+        output->buffers->pushToStash(
+                buffer,
+                notifyClient,
+                timestamp.peek(),
+                flags,
+                outputFormat,
+                worklet->output.ordinal);
     }
     sendOutputBuffers();
     return true;
 }
 
 void CCodecBufferChannel::sendOutputBuffers() {
-    ReorderStash::Entry entry;
-    sp<MediaCodecBuffer> outBuffer;
+    OutputBuffers::BufferAction action;
     size_t index;
+    sp<MediaCodecBuffer> outBuffer;
+    std::shared_ptr<C2Buffer> c2Buffer;
 
     while (true) {
-        Mutexed<ReorderStash>::Locked reorder(mReorderStash);
-        if (!reorder->hasPending()) {
-            break;
-        }
-        if (!reorder->pop(&entry)) {
-            break;
-        }
-
         Mutexed<Output>::Locked output(mOutput);
         if (!output->buffers) {
             return;
         }
-        status_t err = output->buffers->registerBuffer(entry.buffer, &index, &outBuffer);
-        if (err != OK) {
-            bool outputBuffersChanged = false;
-            if (err != WOULD_BLOCK) {
-                if (!output->buffers->isArrayMode()) {
-                    output->buffers = output->buffers->toArrayMode(output->numSlots);
-                }
-                OutputBuffersArray *array = (OutputBuffersArray *)output->buffers.get();
-                array->realloc(entry.buffer);
-                outputBuffersChanged = true;
-            }
-            ALOGV("[%s] sendOutputBuffers: unable to register output buffer", mName);
-            reorder->defer(entry);
-
+        action = output->buffers->popFromStashAndRegister(
+                &c2Buffer, &index, &outBuffer);
+        switch (action) {
+        case OutputBuffers::SKIP:
+            return;
+        case OutputBuffers::DISCARD:
+            break;
+        case OutputBuffers::NOTIFY_CLIENT:
             output.unlock();
-            reorder.unlock();
-
-            if (outputBuffersChanged) {
-                mCCodecCallback->onOutputBuffersChanged();
+            mCallback->onOutputBufferAvailable(index, outBuffer);
+            break;
+        case OutputBuffers::REALLOCATE:
+            if (!output->buffers->isArrayMode()) {
+                output->buffers =
+                    output->buffers->toArrayMode(output->numSlots);
             }
+            static_cast<OutputBuffersArray*>(output->buffers.get())->
+                    realloc(c2Buffer);
+            output.unlock();
+            mCCodecCallback->onOutputBuffersChanged();
+            return;
+        case OutputBuffers::RETRY:
+            ALOGV("[%s] sendOutputBuffers: unable to register output buffer",
+                  mName);
+            return;
+        default:
+            LOG_ALWAYS_FATAL("[%s] sendOutputBuffers: "
+                    "corrupted BufferAction value (%d) "
+                    "returned from popFromStashAndRegister.",
+                    mName, int(action));
             return;
         }
-        output.unlock();
-        reorder.unlock();
-
-        outBuffer->meta()->setInt64("timeUs", entry.timestamp);
-        outBuffer->meta()->setInt32("flags", entry.flags);
-        ALOGV("[%s] sendOutputBuffers: out buffer index = %zu [%p] => %p + %zu (%lld)",
-                mName, index, outBuffer.get(), outBuffer->data(), outBuffer->size(),
-                (long long)entry.timestamp);
-        mCallback->onOutputBufferAvailable(index, outBuffer);
     }
 }
 
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index f6e7024..046c5c3 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -306,48 +306,6 @@
 
     Mutexed<PipelineWatcher> mPipelineWatcher;
 
-    class ReorderStash {
-    public:
-        struct Entry {
-            inline Entry() : buffer(nullptr), timestamp(0), flags(0), ordinal({0, 0, 0}) {}
-            inline Entry(
-                    const std::shared_ptr<C2Buffer> &b,
-                    int64_t t,
-                    int32_t f,
-                    const C2WorkOrdinalStruct &o)
-                : buffer(b), timestamp(t), flags(f), ordinal(o) {}
-            std::shared_ptr<C2Buffer> buffer;
-            int64_t timestamp;
-            int32_t flags;
-            C2WorkOrdinalStruct ordinal;
-        };
-
-        ReorderStash();
-
-        void clear();
-        void flush();
-        void setDepth(uint32_t depth);
-        void setKey(C2Config::ordinal_key_t key);
-        bool pop(Entry *entry);
-        void emplace(
-                const std::shared_ptr<C2Buffer> &buffer,
-                int64_t timestamp,
-                int32_t flags,
-                const C2WorkOrdinalStruct &ordinal);
-        void defer(const Entry &entry);
-        bool hasPending() const;
-        uint32_t depth() const { return mDepth; }
-
-    private:
-        std::list<Entry> mPending;
-        std::list<Entry> mStash;
-        uint32_t mDepth;
-        C2Config::ordinal_key_t mKey;
-
-        bool less(const C2WorkOrdinalStruct &o1, const C2WorkOrdinalStruct &o2);
-    };
-    Mutexed<ReorderStash> mReorderStash;
-
     std::atomic_bool mInputMetEos;
     std::once_flag mRenderWarningFlag;
 
diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp
index a9120c4..e58a1e4 100644
--- a/media/codec2/sfplugin/CCodecBuffers.cpp
+++ b/media/codec2/sfplugin/CCodecBuffers.cpp
@@ -21,6 +21,7 @@
 #include <C2PlatformSupport.h>
 
 #include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaCodec.h>
 #include <media/stagefright/MediaCodecConstants.h>
 #include <media/stagefright/SkipCutBuffer.h>
 #include <mediadrm/ICrypto.h>
@@ -155,16 +156,29 @@
     setSkipCutBuffer(delay, padding);
 }
 
+void OutputBuffers::updateSkipCutBuffer(
+        const sp<AMessage> &format, bool notify) {
+    AString mediaType;
+    if (format->findString(KEY_MIME, &mediaType)
+            && mediaType == MIMETYPE_AUDIO_RAW) {
+        int32_t channelCount;
+        int32_t sampleRate;
+        if (format->findInt32(KEY_CHANNEL_COUNT, &channelCount)
+                && format->findInt32(KEY_SAMPLE_RATE, &sampleRate)) {
+            updateSkipCutBuffer(sampleRate, channelCount);
+        }
+    }
+    if (notify) {
+        mUnreportedFormat = nullptr;
+    }
+}
+
 void OutputBuffers::submit(const sp<MediaCodecBuffer> &buffer) {
     if (mSkipCutBuffer != nullptr) {
         mSkipCutBuffer->submit(buffer);
     }
 }
 
-void OutputBuffers::transferSkipCutBuffer(const sp<SkipCutBuffer> &scb) {
-    mSkipCutBuffer = scb;
-}
-
 void OutputBuffers::setSkipCutBuffer(int32_t skip, int32_t cut) {
     if (mSkipCutBuffer != nullptr) {
         size_t prevSize = mSkipCutBuffer->size();
@@ -175,6 +189,175 @@
     mSkipCutBuffer = new SkipCutBuffer(skip, cut, mChannelCount);
 }
 
+void OutputBuffers::clearStash() {
+    mPending.clear();
+    mReorderStash.clear();
+    mDepth = 0;
+    mKey = C2Config::ORDINAL;
+    mUnreportedFormat = nullptr;
+}
+
+void OutputBuffers::flushStash() {
+    for (StashEntry& e : mPending) {
+        e.notify = false;
+    }
+    for (StashEntry& e : mReorderStash) {
+        e.notify = false;
+    }
+}
+
+uint32_t OutputBuffers::getReorderDepth() const {
+    return mDepth;
+}
+
+void OutputBuffers::setReorderDepth(uint32_t depth) {
+    mPending.splice(mPending.end(), mReorderStash);
+    mDepth = depth;
+}
+
+void OutputBuffers::setReorderKey(C2Config::ordinal_key_t key) {
+    mPending.splice(mPending.end(), mReorderStash);
+    mKey = key;
+}
+
+void OutputBuffers::pushToStash(
+        const std::shared_ptr<C2Buffer>& buffer,
+        bool notify,
+        int64_t timestamp,
+        int32_t flags,
+        const sp<AMessage>& format,
+        const C2WorkOrdinalStruct& ordinal) {
+    bool eos = flags & MediaCodec::BUFFER_FLAG_EOS;
+    if (!buffer && eos) {
+        // TRICKY: we may be violating ordering of the stash here. Because we
+        // don't expect any more emplace() calls after this, the ordering should
+        // not matter.
+        mReorderStash.emplace_back(
+                buffer, notify, timestamp, flags, format, ordinal);
+    } else {
+        flags = flags & ~MediaCodec::BUFFER_FLAG_EOS;
+        auto it = mReorderStash.begin();
+        for (; it != mReorderStash.end(); ++it) {
+            if (less(ordinal, it->ordinal)) {
+                break;
+            }
+        }
+        mReorderStash.emplace(it,
+                buffer, notify, timestamp, flags, format, ordinal);
+        if (eos) {
+            mReorderStash.back().flags =
+                mReorderStash.back().flags | MediaCodec::BUFFER_FLAG_EOS;
+        }
+    }
+    while (!mReorderStash.empty() && mReorderStash.size() > mDepth) {
+        mPending.push_back(mReorderStash.front());
+        mReorderStash.pop_front();
+    }
+    ALOGV("[%s] %s: pushToStash -- pending size = %zu", mName, __func__, mPending.size());
+}
+
+OutputBuffers::BufferAction OutputBuffers::popFromStashAndRegister(
+        std::shared_ptr<C2Buffer>* c2Buffer,
+        size_t* index,
+        sp<MediaCodecBuffer>* outBuffer) {
+    if (mPending.empty()) {
+        return SKIP;
+    }
+
+    // Retrieve the first entry.
+    StashEntry &entry = mPending.front();
+
+    *c2Buffer = entry.buffer;
+    sp<AMessage> outputFormat = entry.format;
+
+    // The output format can be processed without a registered slot.
+    if (outputFormat) {
+        ALOGD("[%s] popFromStashAndRegister: output format changed to %s",
+                mName, outputFormat->debugString().c_str());
+        updateSkipCutBuffer(outputFormat, entry.notify);
+    }
+
+    if (entry.notify) {
+        if (outputFormat) {
+            setFormat(outputFormat);
+        } else if (mUnreportedFormat) {
+            outputFormat = mUnreportedFormat;
+            setFormat(outputFormat);
+        }
+        mUnreportedFormat = nullptr;
+    } else {
+        if (outputFormat) {
+            mUnreportedFormat = outputFormat;
+        } else if (!mUnreportedFormat) {
+            mUnreportedFormat = mFormat;
+        }
+    }
+
+    // Flushing mReorderStash because no other buffers should come after output
+    // EOS.
+    if (entry.flags & MediaCodec::BUFFER_FLAG_EOS) {
+        // Flush reorder stash
+        setReorderDepth(0);
+    }
+
+    if (!entry.notify) {
+        mPending.pop_front();
+        return DISCARD;
+    }
+
+    // Try to register the buffer.
+    status_t err = registerBuffer(*c2Buffer, index, outBuffer);
+    if (err != OK) {
+        if (err != WOULD_BLOCK) {
+            return REALLOCATE;
+        }
+        return RETRY;
+    }
+
+    // Append information from the front stash entry to outBuffer.
+    (*outBuffer)->meta()->setInt64("timeUs", entry.timestamp);
+    (*outBuffer)->meta()->setInt32("flags", entry.flags);
+    ALOGV("[%s] popFromStashAndRegister: "
+          "out buffer index = %zu [%p] => %p + %zu (%lld)",
+          mName, *index, outBuffer->get(),
+          (*outBuffer)->data(), (*outBuffer)->size(),
+          (long long)entry.timestamp);
+
+    // The front entry of mPending will be removed now that the registration
+    // succeeded.
+    mPending.pop_front();
+    return NOTIFY_CLIENT;
+}
+
+bool OutputBuffers::popPending(StashEntry *entry) {
+    if (mPending.empty()) {
+        return false;
+    }
+    *entry = mPending.front();
+    mPending.pop_front();
+    return true;
+}
+
+void OutputBuffers::deferPending(const OutputBuffers::StashEntry &entry) {
+    mPending.push_front(entry);
+}
+
+bool OutputBuffers::hasPending() const {
+    return !mPending.empty();
+}
+
+bool OutputBuffers::less(
+        const C2WorkOrdinalStruct &o1, const C2WorkOrdinalStruct &o2) const {
+    switch (mKey) {
+        case C2Config::ORDINAL:   return o1.frameIndex < o2.frameIndex;
+        case C2Config::TIMESTAMP: return o1.timestamp < o2.timestamp;
+        case C2Config::CUSTOM:    return o1.customOrdinal < o2.customOrdinal;
+        default:
+            ALOGD("Unrecognized key; default to timestamp");
+            return o1.frameIndex < o2.frameIndex;
+    }
+}
+
 // LocalBufferPool
 
 constexpr size_t kInitialPoolCapacity = kMaxLinearBufferSize;
@@ -981,6 +1164,16 @@
     mImpl.grow(newSize, mAlloc);
 }
 
+void OutputBuffersArray::transferFrom(OutputBuffers* source) {
+    mFormat = source->mFormat;
+    mSkipCutBuffer = source->mSkipCutBuffer;
+    mUnreportedFormat = source->mUnreportedFormat;
+    mPending = std::move(source->mPending);
+    mReorderStash = std::move(source->mReorderStash);
+    mDepth = source->mDepth;
+    mKey = source->mKey;
+}
+
 // FlexOutputBuffers
 
 status_t FlexOutputBuffers::registerBuffer(
@@ -1023,13 +1216,12 @@
     // track of the flushed work.
 }
 
-std::unique_ptr<OutputBuffers> FlexOutputBuffers::toArrayMode(size_t size) {
+std::unique_ptr<OutputBuffersArray> FlexOutputBuffers::toArrayMode(size_t size) {
     std::unique_ptr<OutputBuffersArray> array(new OutputBuffersArray(mComponentName.c_str()));
-    array->setFormat(mFormat);
-    array->transferSkipCutBuffer(mSkipCutBuffer);
+    array->transferFrom(this);
     std::function<sp<Codec2Buffer>()> alloc = getAlloc();
     array->initialize(mImpl, size, alloc);
-    return std::move(array);
+    return array;
 }
 
 size_t FlexOutputBuffers::numClientBuffers() const {
diff --git a/media/codec2/sfplugin/CCodecBuffers.h b/media/codec2/sfplugin/CCodecBuffers.h
index eec79f1..0d4fa81 100644
--- a/media/codec2/sfplugin/CCodecBuffers.h
+++ b/media/codec2/sfplugin/CCodecBuffers.h
@@ -156,6 +156,8 @@
     DISALLOW_EVIL_CONSTRUCTORS(InputBuffers);
 };
 
+class OutputBuffersArray;
+
 class OutputBuffers : public CCodecBuffers {
 public:
     OutputBuffers(const char *componentName, const char *name = "Output");
@@ -163,8 +165,12 @@
 
     /**
      * Register output C2Buffer from the component and obtain corresponding
-     * index and MediaCodecBuffer object. Returns false if registration
-     * fails.
+     * index and MediaCodecBuffer object.
+     *
+     * Returns:
+     *   OK if registration succeeds.
+     *   NO_MEMORY if all buffers are available but not compatible.
+     *   WOULD_BLOCK if there are compatible buffers, but they are all in use.
      */
     virtual status_t registerBuffer(
             const std::shared_ptr<C2Buffer> &buffer,
@@ -199,7 +205,7 @@
      * shall retain the internal state so that it will honor index and
      * buffer from previous calls of registerBuffer().
      */
-    virtual std::unique_ptr<OutputBuffers> toArrayMode(size_t size) = 0;
+    virtual std::unique_ptr<OutputBuffersArray> toArrayMode(size_t size) = 0;
 
     /**
      * Initialize SkipCutBuffer object.
@@ -208,6 +214,164 @@
             int32_t delay, int32_t padding, int32_t sampleRate, int32_t channelCount);
 
     /**
+     * Update SkipCutBuffer from format. The @p format must not be null.
+     * @p notify determines whether the format comes with a buffer that should
+     * be reported to the client or not.
+     */
+    void updateSkipCutBuffer(const sp<AMessage> &format, bool notify = true);
+
+    /**
+     * Output Stash
+     * ============
+     *
+     * The output stash is a place to hold output buffers temporarily before
+     * they are registered to output slots. It has 2 main functions:
+     * 1. Allow reordering of output frames as the codec may produce frames in a
+     *    different order.
+     * 2. Act as a "buffer" between the codec and the client because the codec
+     *    may produce more buffers than available slots. This excess of codec's
+     *    output buffers should be registered to slots later, after the client
+     *    has released some slots.
+     *
+     * The stash consists of 2 lists of buffers: mPending and mReorderStash.
+     * mPending is a normal FIFO queue with not size limit, while mReorderStash
+     * is a sorted list with size limit mDepth.
+     *
+     * The normal flow of a non-csd output buffer is as follows:
+     *
+     *           |----------------OutputBuffers---------------|
+     *           |----------Output stash----------|           |
+     *   Codec --|-> mReorderStash --> mPending --|-> slots --|-> client
+     *           |                                |           |
+     *     pushToStash()                    popFromStashAndRegister()
+     *
+     * The buffer that comes from the codec first enters mReorderStash. The
+     * first buffer in mReorderStash gets moved to mPending when mReorderStash
+     * overflows. Buffers in mPending are registered to slots and given to the
+     * client as soon as slots are available.
+     *
+     * Every output buffer that is not a csd buffer should be put on the stash
+     * by calling pushToStash(), then later registered to a slot by calling
+     * popFromStashAndRegister() before notifying the client with
+     * onOutputBufferAvailable().
+     *
+     * Reordering
+     * ==========
+     *
+     * mReorderStash is a sorted list with a specified size limit. The size
+     * limit can be set by calling setReorderDepth().
+     *
+     * Every buffer in mReorderStash has a C2WorkOrdinalStruct, which contains 3
+     * members, all of which are comparable. Which member of C2WorkOrdinalStruct
+     * should be used for reordering can be chosen by calling setReorderKey().
+     */
+
+    /**
+     * Return the reorder depth---the size of mReorderStash.
+     */
+    uint32_t getReorderDepth() const;
+
+    /**
+     * Set the reorder depth.
+     */
+    void setReorderDepth(uint32_t depth);
+
+    /**
+     * Set the type of "key" to use in comparisons.
+     */
+    void setReorderKey(C2Config::ordinal_key_t key);
+
+    /**
+     * Return whether the output stash has any pending buffers.
+     */
+    bool hasPending() const;
+
+    /**
+     * Flush the stash and reset the depth and the key to their default values.
+     */
+    void clearStash();
+
+    /**
+     * Flush the stash.
+     */
+    void flushStash();
+
+    /**
+     * Push a buffer to the reorder stash.
+     *
+     * @param buffer    C2Buffer object from the returned work.
+     * @param notify    Whether the returned work contains a buffer that should
+     *                  be reported to the client. This may be false if the
+     *                  caller wants to process the buffer without notifying the
+     *                  client.
+     * @param timestamp Buffer timestamp to report to the client.
+     * @param flags     Buffer flags to report to the client.
+     * @param format    Buffer format to report to the client.
+     * @param ordinal   Ordinal used in reordering. This determines when the
+     *                  buffer will be popped from the output stash by
+     *                  `popFromStashAndRegister()`.
+     */
+    void pushToStash(
+            const std::shared_ptr<C2Buffer>& buffer,
+            bool notify,
+            int64_t timestamp,
+            int32_t flags,
+            const sp<AMessage>& format,
+            const C2WorkOrdinalStruct& ordinal);
+
+    enum BufferAction : int {
+        SKIP,
+        DISCARD,
+        NOTIFY_CLIENT,
+        REALLOCATE,
+        RETRY,
+    };
+
+    /**
+     * Try to atomically pop the first buffer from the reorder stash and
+     * register it to an output slot. The function returns a value that
+     * indicates a recommended course of action for the caller.
+     *
+     * If the stash is empty, the function will return `SKIP`.
+     *
+     * If the stash is not empty, the function will peek at the first (oldest)
+     * entry in mPending process the buffer in the entry as follows:
+     * - If the buffer should not be sent to the client, the function will
+     *   return `DISCARD`. The stash entry will be removed.
+     * - If the buffer should be sent to the client, the function will attempt
+     *   to register the buffer to a slot. The registration may have 3 outcomes
+     *   corresponding to the following return values:
+     *   - `NOTIFY_CLIENT`: The buffer is successfully registered to a slot. The
+     *     output arguments @p index and @p outBuffer will contain valid values
+     *     that the caller can use to call onOutputBufferAvailable(). The stash
+     *     entry will be removed.
+     *   - `REALLOCATE`: The buffer is not registered because it is not
+     *     compatible with the current slots (which are available). The caller
+     *     should reallocate the OutputBuffers with slots that can fit the
+     *     returned @p c2Buffer. The stash entry will not be removed
+     *   - `RETRY`: All slots are currently occupied by the client. The caller
+     *     should try to call this function again after the client has released
+     *     some slots.
+     *
+     * @return What the caller should do afterwards.
+     *
+     * @param[out] c2Buffer   Underlying C2Buffer associated to the first buffer
+     *                        on the stash. This value is guaranteed to be valid
+     *                        unless the return value is `SKIP`.
+     * @param[out] index      Slot index. This value is valid only if the return
+     *                        value is `NOTIFY_CLIENT`.
+     * @param[out] outBuffer  Registered buffer. This value is valid only if the
+     *                        return valu is `NOTIFY_CLIENT`.
+     */
+    BufferAction popFromStashAndRegister(
+            std::shared_ptr<C2Buffer>* c2Buffer,
+            size_t* index,
+            sp<MediaCodecBuffer>* outBuffer);
+
+protected:
+    sp<SkipCutBuffer> mSkipCutBuffer;
+
+    /**
      * Update the SkipCutBuffer object. No-op if it's never initialized.
      */
     void updateSkipCutBuffer(int32_t sampleRate, int32_t channelCount);
@@ -217,15 +381,8 @@
      */
     void submit(const sp<MediaCodecBuffer> &buffer);
 
-    /**
-     * Transfer SkipCutBuffer object to the other Buffers object.
-     */
-    void transferSkipCutBuffer(const sp<SkipCutBuffer> &scb);
-
-protected:
-    sp<SkipCutBuffer> mSkipCutBuffer;
-
 private:
+    // SkipCutBuffer
     int32_t mDelay;
     int32_t mPadding;
     int32_t mSampleRate;
@@ -233,7 +390,78 @@
 
     void setSkipCutBuffer(int32_t skip, int32_t cut);
 
+    // Output stash
+
+    // Output format that has not been made available to the client.
+    sp<AMessage> mUnreportedFormat;
+
+    // Struct for an entry in the output stash (mPending and mReorderStash)
+    struct StashEntry {
+        inline StashEntry()
+            : buffer(nullptr),
+              notify(false),
+              timestamp(0),
+              flags(0),
+              format(),
+              ordinal({0, 0, 0}) {}
+        inline StashEntry(
+                const std::shared_ptr<C2Buffer> &b,
+                bool n,
+                int64_t t,
+                int32_t f,
+                const sp<AMessage> &fmt,
+                const C2WorkOrdinalStruct &o)
+            : buffer(b),
+              notify(n),
+              timestamp(t),
+              flags(f),
+              format(fmt),
+              ordinal(o) {}
+        std::shared_ptr<C2Buffer> buffer;
+        bool notify;
+        int64_t timestamp;
+        int32_t flags;
+        sp<AMessage> format;
+        C2WorkOrdinalStruct ordinal;
+    };
+
+    /**
+     * FIFO queue of stash entries.
+     */
+    std::list<StashEntry> mPending;
+    /**
+     * Sorted list of stash entries.
+     */
+    std::list<StashEntry> mReorderStash;
+    /**
+     * Size limit of mReorderStash.
+     */
+    uint32_t mDepth{0};
+    /**
+     * Choice of key to use in ordering of stash entries in mReorderStash.
+     */
+    C2Config::ordinal_key_t mKey{C2Config::ORDINAL};
+
+    /**
+     * Return false if mPending is empty; otherwise, pop the first entry from
+     * mPending and return true.
+     */
+    bool popPending(StashEntry *entry);
+
+    /**
+     * Push an entry as the first entry of mPending.
+     */
+    void deferPending(const StashEntry &entry);
+
+    /**
+     * Comparison of C2WorkOrdinalStruct based on mKey.
+     */
+    bool less(const C2WorkOrdinalStruct &o1,
+              const C2WorkOrdinalStruct &o2) const;
+
     DISALLOW_EVIL_CONSTRUCTORS(OutputBuffers);
+
+    friend OutputBuffersArray;
 };
 
 /**
@@ -770,7 +998,7 @@
 
     bool isArrayMode() const final { return true; }
 
-    std::unique_ptr<OutputBuffers> toArrayMode(size_t) final {
+    std::unique_ptr<OutputBuffersArray> toArrayMode(size_t) final {
         return nullptr;
     }
 
@@ -809,6 +1037,12 @@
      */
     void grow(size_t newSize);
 
+    /**
+     * Transfer the SkipCutBuffer and the output stash from another
+     * OutputBuffers.
+     */
+    void transferFrom(OutputBuffers* source);
+
 private:
     BuffersArrayImpl mImpl;
     std::function<sp<Codec2Buffer>()> mAlloc;
@@ -837,7 +1071,7 @@
     void flush(
             const std::list<std::unique_ptr<C2Work>> &flushedWork) override;
 
-    std::unique_ptr<OutputBuffers> toArrayMode(size_t size) override;
+    std::unique_ptr<OutputBuffersArray> toArrayMode(size_t size) override;
 
     size_t numClientBuffers() const final;
 
diff --git a/media/codec2/sfplugin/utils/Android.bp b/media/codec2/sfplugin/utils/Android.bp
index 205abdc..6287221 100644
--- a/media/codec2/sfplugin/utils/Android.bp
+++ b/media/codec2/sfplugin/utils/Android.bp
@@ -1,6 +1,7 @@
 cc_library_shared {
     name: "libsfplugin_ccodec_utils",
     vendor_available: true,
+    min_sdk_version: "29",
     double_loadable: true,
 
     srcs: [
diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp
index f3e37e0..6f7acce 100644
--- a/media/codec2/vndk/Android.bp
+++ b/media/codec2/vndk/Android.bp
@@ -7,6 +7,8 @@
 
     // TODO: Remove this when this module is moved back to frameworks/av.
     vendor_available: true,
+
+    min_sdk_version: "29",
 }
 
 // !!!DO NOT DEPEND ON THIS SHARED LIBRARY DIRECTLY!!!
@@ -14,6 +16,7 @@
 cc_library_shared {
     name: "libcodec2_vndk",
     vendor_available: true,
+    min_sdk_version: "29",
     // TODO: b/147147883
     double_loadable: true,
 
@@ -87,6 +90,8 @@
         "libcodec2_vndk",
         "libutils",
     ],
+
+    min_sdk_version: "29",
 }
 
 // public dependency for implementing Codec 2 framework utilities
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 54f1fa2..d06c8ba 100755
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -20,6 +20,7 @@
 #include <ctype.h>
 #include <inttypes.h>
 #include <algorithm>
+#include <map>
 #include <memory>
 #include <stdint.h>
 #include <stdlib.h>
@@ -204,6 +205,7 @@
         Vector<size_t> encryptedsizes;
     };
     Vector<Sample> mCurrentSamples;
+    std::map<off64_t, uint32_t> mDrmOffsets;
 
     MPEG4Source(const MPEG4Source &);
     MPEG4Source &operator=(const MPEG4Source &);
@@ -5153,6 +5155,9 @@
                     if (chunk_type == FOURCC("moof")) {
                         mNextMoofOffset = *offset;
                         break;
+                    } else if (chunk_type == FOURCC("mdat")) {
+                        parseChunk(offset);
+                        continue;
                     } else if (chunk_size == 0) {
                         break;
                     }
@@ -5214,6 +5219,22 @@
             // parse DRM info if present
             ALOGV("MPEG4Source::parseChunk mdat");
             // if saiz/saoi was previously observed, do something with the sampleinfos
+            status_t err = OK;
+            auto kv = mDrmOffsets.lower_bound(*offset);
+            if (kv != mDrmOffsets.end()) {
+                auto drmoffset = kv->first;
+                auto flags = kv->second;
+                mDrmOffsets.erase(kv);
+                ALOGV("mdat chunk_size %" PRIu64 " drmoffset %" PRId64 " offset %" PRId64,
+                        chunk_size, drmoffset, *offset);
+                if (chunk_size >= drmoffset - *offset) {
+                    err = parseClearEncryptedSizes(drmoffset, false, flags,
+                        chunk_size - (drmoffset - *offset));
+                }
+            }
+            if (err != OK) {
+                return err;
+            }
             *offset += chunk_size;
             break;
         }
@@ -5395,8 +5416,10 @@
     off64_t drmoffset = mCurrentSampleInfoOffsets[0]; // from moof
 
     drmoffset += mCurrentMoofOffset;
+    mDrmOffsets[drmoffset] = flags;
+    ALOGV("saio drmoffset %" PRId64 " flags %u", drmoffset, flags);
 
-    return parseClearEncryptedSizes(drmoffset, false, 0, mCurrentMoofSize);
+    return OK;
 }
 
 status_t MPEG4Source::parseClearEncryptedSizes(
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 79fa5ed..4520823 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -288,7 +288,7 @@
             requestStop();
         }
 
-        logBufferState();
+        logReleaseBufferState();
 
         setState(AAUDIO_STREAM_STATE_CLOSING);
         aaudio_handle_t serviceStreamHandle = mServiceStreamHandle;
@@ -783,6 +783,14 @@
         adjustedFrames = std::min(actualFrames, adjustedFrames);
     }
 
+    if (adjustedFrames != mBufferSizeInFrames) {
+        android::mediametrics::LogItem(mMetricsId)
+                .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETBUFFERSIZE)
+                .set(AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, adjustedFrames)
+                .set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t) getXRunCount())
+                .record();
+    }
+
     mBufferSizeInFrames = adjustedFrames;
     ALOGV("%s(%d) returns %d", __func__, requestedFrames, adjustedFrames);
     return (aaudio_result_t) adjustedFrames;
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 0644368..bc973bd 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -116,9 +116,10 @@
     }
 }
 
-void AudioStream::logBufferState() {
+void AudioStream::logReleaseBufferState() {
     if (mMetricsId.size() > 0) {
         android::mediametrics::LogItem(mMetricsId)
+                .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_RELEASE)
                 .set(AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, (int32_t) getBufferSize())
                 .set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t) getXRunCount())
                 .record();
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index 613a092..fb71c36 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -115,7 +115,7 @@
 
     // log to MediaMetrics
     virtual void logOpen();
-    void logBufferState();
+    void logReleaseBufferState();
 
     /**
      * Free any hardware or system resources from the open() call.
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 853c0db..6e5110f 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -292,7 +292,7 @@
     //  Then call it from here
     if (getState() != AAUDIO_STREAM_STATE_CLOSING) {
         mAudioRecord->removeAudioDeviceCallback(mDeviceCallback);
-        logBufferState();
+        logReleaseBufferState();
         mAudioRecord.clear();
         mFixedBlockWriter.close();
         return AudioStream::release_l();
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 1120f05..ea08361 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -254,7 +254,7 @@
 aaudio_result_t AudioStreamTrack::release_l() {
     if (getState() != AAUDIO_STREAM_STATE_CLOSING) {
         mAudioTrack->removeAudioDeviceCallback(mDeviceCallback);
-        logBufferState();
+        logReleaseBufferState();
         // TODO Investigate why clear() causes a hang in test_various.cpp
         // if I call close() from a data callback.
         // But the same thing in AudioRecord is OK!
diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp
index a6e5f70..8935d57 100644
--- a/media/libaaudio/tests/Android.bp
+++ b/media/libaaudio/tests/Android.bp
@@ -233,6 +233,7 @@
     srcs: ["test_steal_exclusive.cpp"],
     shared_libs: [
         "libaaudio",
+        "liblog",
         "libbinder",
         "libcutils",
         "libutils",
diff --git a/media/libaaudio/tests/test_steal_exclusive.cpp b/media/libaaudio/tests/test_steal_exclusive.cpp
index 2a05910..05c560d 100644
--- a/media/libaaudio/tests/test_steal_exclusive.cpp
+++ b/media/libaaudio/tests/test_steal_exclusive.cpp
@@ -47,137 +47,271 @@
  */
 
 #include <atomic>
+#include <mutex>
 #include <stdio.h>
 #include <thread>
 #include <unistd.h>
 
+#include <android/log.h>
+
 #include <aaudio/AAudio.h>
+#include <aaudio/AAudioTesting.h>
 
 #define DEFAULT_TIMEOUT_NANOS  ((int64_t)1000000000)
 #define SOLO_DURATION_MSEC    2000
 #define DUET_DURATION_MSEC    8000
 #define SLEEP_DURATION_MSEC    500
 
+#define MODULE_NAME  "stealAudio"
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, MODULE_NAME, __VA_ARGS__)
+
 static const char * s_sharingModeToText(aaudio_sharing_mode_t mode) {
     return (mode == AAUDIO_SHARING_MODE_EXCLUSIVE) ? "EXCLUSIVE"
         : ((mode == AAUDIO_SHARING_MODE_SHARED)  ? "SHARED"
             : AAudio_convertResultToText(mode));
 }
 
+static const char * s_performanceModeToText(aaudio_performance_mode_t mode) {
+    return (mode == AAUDIO_PERFORMANCE_MODE_LOW_LATENCY) ? "LOWLAT"
+        : ((mode == AAUDIO_PERFORMANCE_MODE_NONE)  ? "NONE"
+            : AAudio_convertResultToText(mode));
+}
+
+static aaudio_data_callback_result_t s_myDataCallbackProc(
+        AAudioStream * /* stream */,
+        void *userData,
+        void *audioData,
+        int32_t numFrames);
+
 static void s_myErrorCallbackProc(
         AAudioStream *stream,
         void *userData,
         aaudio_result_t error);
 
-struct AudioEngine {
-    AAudioStream        *stream = nullptr;
-    std::thread         *thread = nullptr;
-    aaudio_direction_t   direction = AAUDIO_DIRECTION_OUTPUT;
+class AudioEngine {
+public:
+
+    AudioEngine(const char *name) {
+        mName = name;
+    }
 
     // These counters are read and written by the callback and the main thread.
-    std::atomic<int32_t> framesRead{};
     std::atomic<int32_t> framesCalled{};
     std::atomic<int32_t> callbackCount{};
+    std::atomic<aaudio_sharing_mode_t> sharingMode{};
+    std::atomic<aaudio_performance_mode_t> performanceMode{};
+    std::atomic<bool> isMMap{false};
 
+    void setMaxRetries(int maxRetries) {
+        mMaxRetries = maxRetries;
+    }
+
+    void setOpenDelayMillis(int openDelayMillis) {
+        mOpenDelayMillis = openDelayMillis;
+    }
+
+    void restartStream() {
+        int retriesLeft = mMaxRetries;
+        aaudio_result_t result;
+        do {
+            closeAudioStream();
+            if (mOpenDelayMillis) usleep(mOpenDelayMillis * 1000);
+            openAudioStream(mDirection, mRequestedSharingMode);
+            // It is possible for the stream to be disconnected, or stolen between the time
+            // it is opened and when it is started. If that happens then try again.
+            // If it was stolen then it should succeed the second time because there will already be
+            // a SHARED stream, which will not get stolen.
+            result = AAudioStream_requestStart(mStream);
+            printf("%s: AAudioStream_requestStart() returns %s\n",
+                    mName.c_str(),
+                    AAudio_convertResultToText(result));
+        } while (retriesLeft-- > 0 && result != AAUDIO_OK);
+    }
+
+    aaudio_data_callback_result_t onAudioReady(
+            void * /*audioData */,
+            int32_t numFrames) {
+        callbackCount++;
+        framesCalled += numFrames;
+        return AAUDIO_CALLBACK_RESULT_CONTINUE;
+    }
+
+    aaudio_result_t openAudioStream(aaudio_direction_t direction,
+            aaudio_sharing_mode_t requestedSharingMode) {
+        std::lock_guard<std::mutex> lock(mLock);
+
+        AAudioStreamBuilder *builder = nullptr;
+        mDirection = direction;
+        mRequestedSharingMode = requestedSharingMode;
+
+        // Use an AAudioStreamBuilder to contain requested parameters.
+        aaudio_result_t result = AAudio_createStreamBuilder(&builder);
+        if (result != AAUDIO_OK) {
+            printf("AAudio_createStreamBuilder returned %s",
+                   AAudio_convertResultToText(result));
+            return result;
+        }
+
+        // Request stream properties.
+        AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
+        AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+        AAudioStreamBuilder_setSharingMode(builder, mRequestedSharingMode);
+        AAudioStreamBuilder_setDirection(builder, direction);
+        AAudioStreamBuilder_setDataCallback(builder, s_myDataCallbackProc, this);
+        AAudioStreamBuilder_setErrorCallback(builder, s_myErrorCallbackProc, this);
+
+        // Create an AAudioStream using the Builder.
+        result = AAudioStreamBuilder_openStream(builder, &mStream);
+        AAudioStreamBuilder_delete(builder);
+        builder = nullptr;
+        if (result != AAUDIO_OK) {
+            printf("AAudioStreamBuilder_openStream returned %s",
+                   AAudio_convertResultToText(result));
+        }
+
+        // See what kind of stream we actually opened.
+        int32_t deviceId = AAudioStream_getDeviceId(mStream);
+        sharingMode = AAudioStream_getSharingMode(mStream);
+        performanceMode = AAudioStream_getPerformanceMode(mStream);
+        isMMap = AAudioStream_isMMapUsed(mStream);
+        printf("%s: opened: deviceId = %3d, sharingMode = %s, perf = %s, %s --------\n",
+               mName.c_str(),
+               deviceId,
+               s_sharingModeToText(sharingMode),
+               s_performanceModeToText(performanceMode),
+               (isMMap ? "MMAP" : "Legacy")
+               );
+
+        return result;
+    }
+
+    aaudio_result_t closeAudioStream() {
+        std::lock_guard<std::mutex> lock(mLock);
+        aaudio_result_t result = AAUDIO_OK;
+        if (mStream != nullptr) {
+            result = AAudioStream_close(mStream);
+            if (result != AAUDIO_OK) {
+                printf("AAudioStream_close returned %s\n",
+                       AAudio_convertResultToText(result));
+            }
+            mStream = nullptr;
+        }
+        return result;
+    }
+
+    /**
+     * @return 0 is OK, -1 for error
+     */
+    int checkEnginePositions() {
+        std::lock_guard<std::mutex> lock(mLock);
+        if (mStream == nullptr) return 0;
+
+        const int64_t framesRead = AAudioStream_getFramesRead(mStream);
+        const int64_t framesWritten = AAudioStream_getFramesWritten(mStream);
+        const int32_t delta = (int32_t)(framesWritten - framesRead);
+        printf("%s: playing framesRead = %7d, framesWritten = %7d"
+               ", delta = %4d, framesCalled = %6d, callbackCount = %4d\n",
+               mName.c_str(),
+               (int32_t) framesRead,
+               (int32_t) framesWritten,
+               delta,
+               framesCalled.load(),
+               callbackCount.load()
+        );
+        if (delta > AAudioStream_getBufferCapacityInFrames(mStream)) {
+            printf("ERROR - delta > capacity\n");
+            return -1;
+        }
+        return 0;
+    }
+
+    aaudio_result_t start() {
+        std::lock_guard<std::mutex> lock(mLock);
+        reset();
+        if (mStream == nullptr) return 0;
+        return AAudioStream_requestStart(mStream);
+    }
+
+    aaudio_result_t stop() {
+        std::lock_guard<std::mutex> lock(mLock);
+        if (mStream == nullptr) return 0;
+        return AAudioStream_requestStop(mStream);
+    }
+
+    bool hasAdvanced() {
+        std::lock_guard<std::mutex> lock(mLock);
+        if (mStream == nullptr) return 0;
+        if (mDirection == AAUDIO_DIRECTION_OUTPUT) {
+            return AAudioStream_getFramesRead(mStream) > 0;
+        } else {
+            return AAudioStream_getFramesWritten(mStream) > 0;
+        }
+    }
+
+    aaudio_result_t verify() {
+        int errorCount = 0;
+        if (hasAdvanced()) {
+            printf("%s: stream is running => PASS\n", mName.c_str());
+        } else {
+            errorCount++;
+            printf("%s: stream should be running => FAIL!!\n", mName.c_str());
+        }
+
+        if (isMMap) {
+            printf("%s: data path is MMAP => PASS\n", mName.c_str());
+        } else {
+            errorCount++;
+            printf("%s: data path is Legacy! => FAIL\n", mName.c_str());
+        }
+
+        // Check for PASS/FAIL
+        if (sharingMode == AAUDIO_SHARING_MODE_SHARED) {
+            printf("%s: mode is SHARED => PASS\n", mName.c_str());
+        } else {
+            errorCount++;
+            printf("%s: modes is EXCLUSIVE => FAIL!!\n", mName.c_str());
+        }
+        return errorCount ? AAUDIO_ERROR_INVALID_FORMAT : AAUDIO_OK;
+    }
+
+private:
     void reset() {
-        framesRead.store(0);
         framesCalled.store(0);
         callbackCount.store(0);
     }
+
+    AAudioStream       *mStream = nullptr;
+    aaudio_direction_t  mDirection = AAUDIO_DIRECTION_OUTPUT;
+    aaudio_sharing_mode_t mRequestedSharingMode = AAUDIO_UNSPECIFIED;
+    std::mutex          mLock;
+    std::string         mName;
+    int                 mMaxRetries = 1;
+    int                 mOpenDelayMillis = 0;
 };
 
 // Callback function that fills the audio output buffer.
 static aaudio_data_callback_result_t s_myDataCallbackProc(
-        AAudioStream *stream,
+        AAudioStream * /* stream */,
         void *userData,
         void *audioData,
         int32_t numFrames
 ) {
-    (void) audioData;
-    (void) numFrames;
-    AudioEngine *engine = (struct AudioEngine *)userData;
-    engine->callbackCount++;
-
-    engine->framesRead = (int32_t)AAudioStream_getFramesRead(stream);
-    engine->framesCalled += numFrames;
-    return AAUDIO_CALLBACK_RESULT_CONTINUE;
-}
-
-static aaudio_result_t s_OpenAudioStream(struct AudioEngine *engine,
-                                         aaudio_direction_t direction) {
-    AAudioStreamBuilder *builder = nullptr;
-    engine->direction = direction;
-
-    // Use an AAudioStreamBuilder to contain requested parameters.
-    aaudio_result_t result = AAudio_createStreamBuilder(&builder);
-    if (result != AAUDIO_OK) {
-        printf("AAudio_createStreamBuilder returned %s",
-               AAudio_convertResultToText(result));
-        return result;
-    }
-
-    // Request stream properties.
-    AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
-    AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
-    AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_EXCLUSIVE);
-    AAudioStreamBuilder_setDirection(builder, direction);
-    AAudioStreamBuilder_setDataCallback(builder, s_myDataCallbackProc, engine);
-    AAudioStreamBuilder_setErrorCallback(builder, s_myErrorCallbackProc, engine);
-
-    // Create an AAudioStream using the Builder.
-    result = AAudioStreamBuilder_openStream(builder, &engine->stream);
-    AAudioStreamBuilder_delete(builder);
-    builder = nullptr;
-    if (result != AAUDIO_OK) {
-        printf("AAudioStreamBuilder_openStream returned %s",
-               AAudio_convertResultToText(result));
-    }
-
-    // See see what kind of stream we actually opened.
-    int32_t deviceId = AAudioStream_getDeviceId(engine->stream);
-    aaudio_sharing_mode_t actualSharingMode = AAudioStream_getSharingMode(engine->stream);
-    printf("-------- opened: deviceId = %3d, actualSharingMode = %s\n",
-           deviceId,
-           s_sharingModeToText(actualSharingMode));
-
-    return result;
-}
-
-static aaudio_result_t s_CloseAudioStream(struct AudioEngine *engine) {
-    aaudio_result_t result = AAUDIO_OK;
-    if (engine->stream != nullptr) {
-        result = AAudioStream_close(engine->stream);
-        if (result != AAUDIO_OK) {
-            printf("AAudioStream_close returned %s\n",
-                   AAudio_convertResultToText(result));
-        }
-        engine->stream = nullptr;
-    }
-    return result;
+    AudioEngine *engine = (AudioEngine *)userData;
+    return engine->onAudioReady(audioData, numFrames);
 }
 
 static void s_myRestartStreamProc(void *userData) {
+    LOGI("%s() called", __func__);
     printf("%s() - restart in separate thread\n", __func__);
     AudioEngine *engine = (AudioEngine *) userData;
-    int retriesLeft = 1;
-    aaudio_result_t result;
-    do {
-        s_CloseAudioStream(engine);
-        s_OpenAudioStream(engine, engine->direction);
-        // It is possible for the stream to be disconnected, or stolen between the time
-        // it is opened and when it is started. If that happens then try again.
-        // If it was stolen then it should succeed the second time because there will already be
-        // a SHARED stream, which will not get stolen.
-        result = AAudioStream_requestStart(engine->stream);
-        printf("%s() - AAudioStream_requestStart() returns %s\n", __func__,
-                AAudio_convertResultToText(result));
-    } while (retriesLeft-- > 0 && result != AAUDIO_OK);
+    engine->restartStream();
 }
 
 static void s_myErrorCallbackProc(
         AAudioStream * /* stream */,
         void *userData,
         aaudio_result_t error) {
+    LOGI("%s() called", __func__);
     printf("%s() - error = %s\n", __func__, AAudio_convertResultToText(error));
     // Handle error on a separate thread.
     std::thread t(s_myRestartStreamProc, userData);
@@ -185,48 +319,28 @@
 }
 
 static void s_usage() {
-    printf("test_steal_exclusive [-i]\n");
+    printf("test_steal_exclusive [-i] [-r{maxRetries}] [-d{delay}] -s\n");
     printf("     -i direction INPUT, otherwise OUTPUT\n");
+    printf("     -d delay open by milliseconds, default = 0\n");
+    printf("     -r max retries in the error callback, default = 1\n");
+    printf("     -s try to open in SHARED mode\n");
 }
 
-/**
- * @return 0 is OK, -1 for error
- */
-static int s_checkEnginePositions(AudioEngine *engine) {
-    if (engine->stream == nullptr) return 0; // race condition with onError procs!
-
-    const int64_t framesRead = AAudioStream_getFramesRead(engine->stream);
-    const int64_t framesWritten = AAudioStream_getFramesWritten(engine->stream);
-    const int32_t delta = (int32_t)(framesWritten - framesRead);
-    printf("playing framesRead = %7d, framesWritten = %7d"
-           ", delta = %4d, framesCalled = %6d, callbackCount = %4d\n",
-           (int32_t) framesRead,
-           (int32_t) framesWritten,
-           delta,
-           engine->framesCalled.load(),
-           engine->callbackCount.load()
-    );
-    if (delta > AAudioStream_getBufferCapacityInFrames(engine->stream)) {
-        printf("ERROR - delta > capacity\n");
-        return -1;
-    }
-    return 0;
-}
-
-int main(int argc, char **argv) {
-    (void) argc;
-    (void) argv;
-    struct AudioEngine victim;
-    struct AudioEngine thief;
+int main(int argc, char ** argv) {
+    AudioEngine victim("victim");
+    AudioEngine thief("thief");
     aaudio_direction_t direction = AAUDIO_DIRECTION_OUTPUT;
     aaudio_result_t result = AAUDIO_OK;
     int errorCount = 0;
+    int maxRetries = 1;
+    int openDelayMillis = 0;
+    aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE;
 
     // Make printf print immediately so that debug info is not stuck
     // in a buffer if we hang or crash.
     setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
 
-    printf("Test Stealing an EXCLUSIVE stream V1.0\n");
+    printf("Test interaction between streams V1.1\n");
     printf("\n");
 
     for (int i = 1; i < argc; i++) {
@@ -234,9 +348,18 @@
         if (arg[0] == '-') {
             char option = arg[1];
             switch (option) {
+                case 'd':
+                    openDelayMillis = atoi(&arg[2]);
+                    break;
                 case 'i':
                     direction = AAUDIO_DIRECTION_INPUT;
                     break;
+                case 'r':
+                    maxRetries = atoi(&arg[2]);
+                    break;
+                case 's':
+                    requestedSharingMode = AAUDIO_SHARING_MODE_SHARED;
+                    break;
                 default:
                     s_usage();
                     exit(EXIT_FAILURE);
@@ -249,16 +372,35 @@
         }
     }
 
-    result = s_OpenAudioStream(&victim, direction);
+    victim.setOpenDelayMillis(openDelayMillis);
+    thief.setOpenDelayMillis(openDelayMillis);
+    victim.setMaxRetries(maxRetries);
+    thief.setMaxRetries(maxRetries);
+
+    result = victim.openAudioStream(direction, requestedSharingMode);
     if (result != AAUDIO_OK) {
         printf("s_OpenAudioStream victim returned %s\n",
                AAudio_convertResultToText(result));
         errorCount++;
     }
-    victim.reset();
+
+    if (victim.sharingMode == requestedSharingMode) {
+        printf("Victim modes is %s => OK\n", s_sharingModeToText(requestedSharingMode));
+    } else {
+        printf("Victim modes should be %s => test not valid!\n",
+                s_sharingModeToText(requestedSharingMode));
+        goto onerror;
+    }
+
+    if (victim.isMMap) {
+        printf("Victim data path is MMAP => OK\n");
+    } else {
+        printf("Victim data path is Legacy! => test not valid\n");
+        goto onerror;
+    }
 
     // Start stream.
-    result = AAudioStream_requestStart(victim.stream);
+    result = victim.start();
     printf("AAudioStream_requestStart(VICTIM) returned %d >>>>>>>>>>>>>>>>>>>>>>\n", result);
     if (result != AAUDIO_OK) {
         errorCount++;
@@ -267,77 +409,69 @@
     if (result == AAUDIO_OK) {
         const int watchLoops = SOLO_DURATION_MSEC / SLEEP_DURATION_MSEC;
         for (int i = watchLoops; i > 0; i--) {
-            errorCount += s_checkEnginePositions(&victim) ? 1 : 0;
+            errorCount += victim.checkEnginePositions() ? 1 : 0;
             usleep(SLEEP_DURATION_MSEC * 1000);
         }
     }
 
-    printf("Try to start the THIEF stream that may steal the VICTIM MMAP resource -----\n");
-    result = s_OpenAudioStream(&thief, direction);
+    printf("Trying to start the THIEF stream, which may steal the VICTIM MMAP resource -----\n");
+    result = thief.openAudioStream(direction, requestedSharingMode);
     if (result != AAUDIO_OK) {
         printf("s_OpenAudioStream victim returned %s\n",
                AAudio_convertResultToText(result));
         errorCount++;
     }
-    thief.reset();
 
     // Start stream.
-    result = AAudioStream_requestStart(thief.stream);
+    result = thief.start();
     printf("AAudioStream_requestStart(THIEF) returned %d >>>>>>>>>>>>>>>>>>>>>>\n", result);
     if (result != AAUDIO_OK) {
         errorCount++;
     }
-    printf("You might enjoy plugging in a headset now to see what happens...\n");
+
+    // Give stream time to advance.
+    usleep(SLEEP_DURATION_MSEC * 1000);
+
+    if (victim.verify()) {
+        errorCount++;
+        goto onerror;
+    }
+    if (thief.verify()) {
+        errorCount++;
+        goto onerror;
+    }
+
+    LOGI("Both streams running. Ask user to plug in headset. ====");
+    printf("\n====\nPlease PLUG IN A HEADSET now!\n====\n\n");
 
     if (result == AAUDIO_OK) {
         const int watchLoops = DUET_DURATION_MSEC / SLEEP_DURATION_MSEC;
         for (int i = watchLoops; i > 0; i--) {
-            printf("victim: ");
-            errorCount += s_checkEnginePositions(&victim) ? 1 : 0;
-            printf(" thief: ");
-            errorCount += s_checkEnginePositions(&thief) ? 1 : 0;
+            errorCount += victim.checkEnginePositions() ? 1 : 0;
+            errorCount += thief.checkEnginePositions() ? 1 : 0;
             usleep(SLEEP_DURATION_MSEC * 1000);
         }
     }
 
-    // Check for PASS/FAIL
-    aaudio_sharing_mode_t victimSharingMode = AAudioStream_getSharingMode(victim.stream);
-    aaudio_sharing_mode_t thiefSharingMode = AAudioStream_getSharingMode(thief.stream);
-    printf("victimSharingMode = %s, thiefSharingMode = %s, - ",
-           s_sharingModeToText(victimSharingMode),
-           s_sharingModeToText(thiefSharingMode));
-    if ((victimSharingMode == AAUDIO_SHARING_MODE_SHARED)
-            && (thiefSharingMode == AAUDIO_SHARING_MODE_SHARED)) {
-        printf("Both modes are SHARED => PASS\n");
-    } else {
-        errorCount++;
-        printf("Both modes should be SHARED => FAIL!!\n");
-    }
+    errorCount += victim.verify() ? 1 : 0;
+    errorCount += thief.verify() ? 1 : 0;
 
-    const int64_t victimFramesRead = AAudioStream_getFramesRead(victim.stream);
-    const int64_t thiefFramesRead = AAudioStream_getFramesRead(thief.stream);
-    printf("victimFramesRead = %d, thiefFramesRead = %d, - ",
-           (int)victimFramesRead, (int)thiefFramesRead);
-    if (victimFramesRead > 0 && thiefFramesRead > 0) {
-        printf("Both streams are running => PASS\n");
-    } else {
-        errorCount++;
-        printf("Both streams should be running => FAIL!!\n");
-    }
-
-    result = AAudioStream_requestStop(victim.stream);
+    result = victim.stop();
     printf("AAudioStream_requestStop() returned %d <<<<<<<<<<<<<<<<<<<<<\n", result);
     if (result != AAUDIO_OK) {
+        printf("stop result = %d = %s\n", result, AAudio_convertResultToText(result));
         errorCount++;
     }
-    result = AAudioStream_requestStop(thief.stream);
+    result = thief.stop();
     printf("AAudioStream_requestStop() returned %d <<<<<<<<<<<<<<<<<<<<<\n", result);
     if (result != AAUDIO_OK) {
+        printf("stop result = %d = %s\n", result, AAudio_convertResultToText(result));
         errorCount++;
     }
 
-    s_CloseAudioStream(&victim);
-    s_CloseAudioStream(&thief);
+onerror:
+    victim.closeAudioStream();
+    thief.closeAudioStream();
 
     printf("aaudio result = %d = %s\n", result, AAudio_convertResultToText(result));
     printf("test %s\n", errorCount ? "FAILED" : "PASSED");
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index 0d20f20..0c40cbb 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -1,6 +1,8 @@
 cc_library_headers {
     name: "libaudioclient_headers",
     vendor_available: true,
+    min_sdk_version: "29",
+
     header_libs: [
         "libaudiofoundation_headers",
     ],
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index d4c421a..7efa67c 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -773,7 +773,7 @@
             // use case 3: obtain/release mode
             (mTransfer == TRANSFER_OBTAIN);
         if (!useCaseAllowed) {
-            ALOGW("%s(%d): AUDIO_INPUT_FLAG_FAST denied, incompatible transfer = %s",
+            ALOGD("%s(%d): AUDIO_INPUT_FLAG_FAST denied, incompatible transfer = %s",
                   __func__, mPortId,
                   convertTransferToText(mTransfer));
             mFlags = (audio_input_flags_t) (mFlags & ~(AUDIO_INPUT_FLAG_FAST |
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 011b0fa..005d358 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -639,9 +639,17 @@
 
 status_t AudioTrack::start()
 {
-    const int64_t beginNs = systemTime();
     AutoMutex lock(mLock);
 
+    if (mState == STATE_ACTIVE) {
+        return INVALID_OPERATION;
+    }
+
+    ALOGV("%s(%d): prior state:%s", __func__, mPortId, stateToString(mState));
+
+    // Defer logging here due to OpenSL ES repeated start calls.
+    // TODO(b/154868033) after fix, restore this logging back to the beginning of start().
+    const int64_t beginNs = systemTime();
     status_t status = NO_ERROR; // logged: make sure to set this before returning.
     mediametrics::Defer defer([&] {
         mediametrics::LogItem(mMetricsId)
@@ -655,12 +663,6 @@
             .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)status)
             .record(); });
 
-    ALOGV("%s(%d): prior state:%s", __func__, mPortId, stateToString(mState));
-
-    if (mState == STATE_ACTIVE) {
-        status = INVALID_OPERATION;
-        return status;
-    }
 
     mInUnderrun = true;
 
@@ -785,8 +787,9 @@
             .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_STOP)
             .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(systemTime() - beginNs))
             .set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
+            .set(AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, (int32_t)mProxy->getBufferSizeInFrames())
+            .set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t) getUnderrunCount_l())
             .record();
-        logBufferSizeUnderruns();
     });
 
     ALOGV("%s(%d): prior state:%s", __func__, mPortId, stateToString(mState));
@@ -1139,16 +1142,6 @@
     return NO_ERROR;
 }
 
-void AudioTrack::logBufferSizeUnderruns() {
-    LOG_ALWAYS_FATAL_IF(mMetricsId.size() == 0, "mMetricsId is empty!");
-    ALOGD("%s(), mMetricsId = %s", __func__, mMetricsId.c_str());
-    // FIXME THis hangs! Why?
-//    android::mediametrics::LogItem(mMetricsId)
-//            .set(AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, (int32_t) getBufferSizeInFrames())
-//            .set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t) getUnderrunCount())
-//            .record();
-}
-
 ssize_t AudioTrack::setBufferSizeInFrames(size_t bufferSizeInFrames)
 {
     AutoMutex lock(mLock);
@@ -1163,7 +1156,11 @@
     ssize_t originalBufferSize = mProxy->getBufferSizeInFrames();
     ssize_t finalBufferSize  = mProxy->setBufferSizeInFrames((uint32_t) bufferSizeInFrames);
     if (originalBufferSize != finalBufferSize) {
-        logBufferSizeUnderruns();
+        android::mediametrics::LogItem(mMetricsId)
+                .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETBUFFERSIZE)
+                .set(AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, (int32_t)mProxy->getBufferSizeInFrames())
+                .set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t)getUnderrunCount_l())
+                .record();
     }
     return finalBufferSize;
 }
@@ -1632,7 +1629,7 @@
                 mAwaitBoost = true;
             }
         } else {
-            ALOGW("%s(%d): AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %zu -> %zu",
+            ALOGD("%s(%d): AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %zu -> %zu",
                   __func__, mPortId, mReqFrameCount, mFrameCount);
         }
     }
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index 17af7d4..0dbd842 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -1274,8 +1274,6 @@
     std::string mMetricsId;  // GUARDED_BY(mLock), could change in createTrack_l().
     std::string mCallerName; // for example "aaudio"
 
-    void logBufferSizeUnderruns();
-
 private:
     class AudioTrackCallback : public media::BnAudioTrackCallback {
     public:
diff --git a/media/libaudiofoundation/Android.bp b/media/libaudiofoundation/Android.bp
index 93bc4d9..548b080 100644
--- a/media/libaudiofoundation/Android.bp
+++ b/media/libaudiofoundation/Android.bp
@@ -1,6 +1,8 @@
 cc_library_headers {
     name: "libaudiofoundation_headers",
     vendor_available: true,
+    min_sdk_version: "29",
+
     export_include_dirs: ["include"],
     header_libs: [
         "libaudio_system_headers",
diff --git a/media/libaudioprocessing/AudioMixerBase.cpp b/media/libaudioprocessing/AudioMixerBase.cpp
index 64f91fe..a54e22f 100644
--- a/media/libaudioprocessing/AudioMixerBase.cpp
+++ b/media/libaudioprocessing/AudioMixerBase.cpp
@@ -1500,7 +1500,7 @@
     ALOGVV("track__Resample\n");
     mResampler->setSampleRate(sampleRate);
     const bool ramp = needsRamp();
-    if (MIXTYPE == MIXTYPE_MONOEXPAND || MIXTYPE == MIXTYPE_STEREOEXPAND
+    if (MIXTYPE == MIXTYPE_MONOEXPAND || MIXTYPE == MIXTYPE_STEREOEXPAND // custom volume handling
             || ramp || aux != NULL) {
         // if ramp:        resample with unity gain to temp buffer and scale/mix in 2nd step.
         // if aux != NULL: resample with unity gain to temp buffer then apply send level.
diff --git a/media/libaudioprocessing/AudioMixerOps.h b/media/libaudioprocessing/AudioMixerOps.h
index 2748182..80bd093 100644
--- a/media/libaudioprocessing/AudioMixerOps.h
+++ b/media/libaudioprocessing/AudioMixerOps.h
@@ -236,7 +236,8 @@
             || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
             || MIXTYPE == MIXTYPE_STEREOEXPAND);
     auto proc = [](auto& a, const auto& b) {
-        if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL) {
+        if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
+                || MIXTYPE == MIXTYPE_STEREOEXPAND) {
             a += b;
         } else {
             a = b;
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index be3f995..4925ea4 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -1,6 +1,8 @@
 cc_library_headers {
     name: "libmedia_headers",
     vendor_available: true,
+    min_sdk_version: "29",
+
     export_include_dirs: ["include"],
     header_libs: [
         "libbase_headers",
@@ -184,6 +186,8 @@
 cc_library_static {
     name: "libmedia_midiiowrapper",
 
+    min_sdk_version: "29",
+
     srcs: ["MidiIoWrapper.cpp"],
 
     static_libs: [
diff --git a/media/libmediahelper/Android.bp b/media/libmediahelper/Android.bp
index 72edeec..6fcbc7b 100644
--- a/media/libmediahelper/Android.bp
+++ b/media/libmediahelper/Android.bp
@@ -1,6 +1,7 @@
 cc_library_headers {
     name: "libmedia_helper_headers",
     vendor_available: true,
+    min_sdk_version: "29",
     export_include_dirs: ["include"],
 }
 
diff --git a/media/libmediametrics/include/MediaMetricsConstants.h b/media/libmediametrics/include/MediaMetricsConstants.h
index b916a78..0906411 100644
--- a/media/libmediametrics/include/MediaMetricsConstants.h
+++ b/media/libmediametrics/include/MediaMetricsConstants.h
@@ -175,10 +175,12 @@
 #define AMEDIAMETRICS_PROP_EVENT_VALUE_OPEN       "open"
 #define AMEDIAMETRICS_PROP_EVENT_VALUE_PAUSE      "pause"  // AudioTrack
 #define AMEDIAMETRICS_PROP_EVENT_VALUE_READPARAMETERS "readParameters" // Thread
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_RELEASE    "release"
 #define AMEDIAMETRICS_PROP_EVENT_VALUE_RESTORE    "restore"
 #define AMEDIAMETRICS_PROP_EVENT_VALUE_SETMODE    "setMode" // AudioFlinger
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETBUFFERSIZE    "setBufferSize" // AudioTrack
 #define AMEDIAMETRICS_PROP_EVENT_VALUE_SETPLAYBACKPARAM "setPlaybackParam" // AudioTrack
-#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOICEVOLUME "setVoiceVolume" // AudioFlinger
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOICEVOLUME   "setVoiceVolume" // AudioFlinger
 #define AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOLUME  "setVolume"  // AudioTrack
 #define AMEDIAMETRICS_PROP_EVENT_VALUE_START      "start"  // AudioTrack, AudioRecord
 #define AMEDIAMETRICS_PROP_EVENT_VALUE_STOP       "stop"   // AudioTrack, AudioRecord
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index cf7f423..7897959 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -2011,6 +2011,7 @@
     }
     if (mOutputFormat == OUTPUT_FORMAT_MPEG_4 || mOutputFormat == OUTPUT_FORMAT_THREE_GPP) {
         (*meta)->setInt32(kKeyEmptyTrackMalFormed, true);
+        (*meta)->setInt32(kKey4BitTrackIds, true);
     }
 }
 
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 0eaa503..e6bb2e1 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -1159,7 +1159,7 @@
         readBuffer(MEDIA_TRACK_TYPE_VIDEO, seekTimeUs, mode, &actualTimeUs);
 
         if (mode != MediaPlayerSeekMode::SEEK_CLOSEST) {
-            seekTimeUs = actualTimeUs;
+            seekTimeUs = std::max<int64_t>(0, actualTimeUs);
         }
         mVideoLastDequeueTimeUs = actualTimeUs;
     }
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
index b5142ed..a532603 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
@@ -34,8 +34,6 @@
       mTargetHandler(targetHandler),
       mEOS(false),
       mSendDataNotification(true) {
-    mSource->setListener(this);
-
     mMemoryDealer = new MemoryDealer(kNumBuffers * kBufferSize);
     for (size_t i = 0; i < kNumBuffers; ++i) {
         sp<IMemory> mem = mMemoryDealer->allocate(kBufferSize);
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
index 14f1323..bec27d3 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
@@ -79,6 +79,7 @@
 
 void NuPlayer::StreamingSource::start() {
     mStreamListener = new NuPlayerStreamListener(mSource, NULL);
+    mSource->setListener(mStreamListener);
 
     uint32_t sourceFlags = mSource->flags();
 
diff --git a/media/libstagefright/ACodecBufferChannel.cpp b/media/libstagefright/ACodecBufferChannel.cpp
index fa13f32..88b15ae 100644
--- a/media/libstagefright/ACodecBufferChannel.cpp
+++ b/media/libstagefright/ACodecBufferChannel.cpp
@@ -22,11 +22,14 @@
 
 #include <C2Buffer.h>
 
+#include <Codec2BufferUtils.h>
+
 #include <android/hardware/cas/native/1.0/IDescrambler.h>
 #include <android/hardware/drm/1.0/types.h>
 #include <binder/MemoryDealer.h>
 #include <hidlmemory/FrameworkUtils.h>
 #include <media/openmax/OMX_Core.h>
+#include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/foundation/AUtils.h>
 #include <media/stagefright/MediaCodec.h>
@@ -91,15 +94,27 @@
 }
 
 status_t ACodecBufferChannel::queueInputBuffer(const sp<MediaCodecBuffer> &buffer) {
-    if (mDealer != nullptr) {
-        return -ENOSYS;
-    }
     std::shared_ptr<const std::vector<const BufferInfo>> array(
             std::atomic_load(&mInputBuffers));
     BufferInfoIterator it = findClientBuffer(array, buffer);
     if (it == array->end()) {
         return -ENOENT;
     }
+    if (it->mClientBuffer != it->mCodecBuffer) {
+        // Copy metadata from client to codec buffer.
+        it->mCodecBuffer->meta()->clear();
+        int64_t timeUs;
+        CHECK(it->mClientBuffer->meta()->findInt64("timeUs", &timeUs));
+        it->mCodecBuffer->meta()->setInt64("timeUs", timeUs);
+        int32_t eos;
+        if (it->mClientBuffer->meta()->findInt32("eos", &eos)) {
+            it->mCodecBuffer->meta()->setInt32("eos", eos);
+        }
+        int32_t csd;
+        if (it->mClientBuffer->meta()->findInt32("csd", &csd)) {
+            it->mCodecBuffer->meta()->setInt32("csd", csd);
+        }
+    }
     ALOGV("queueInputBuffer #%d", it->mBufferId);
     sp<AMessage> msg = mInputBufferFilled->dup();
     msg->setObject("buffer", it->mCodecBuffer);
@@ -267,16 +282,30 @@
             }
             C2ConstLinearBlock block{c2Buffer->data().linearBlocks().front()};
             C2ReadView view{block.map().get()};
-            if (view.capacity() > buffer->capacity()) {
-                return -ENOSYS;
-            }
-            memcpy(buffer->base(), view.data(), view.capacity());
-            buffer->setRange(0, view.capacity());
+            size_t copyLength = std::min(size_t(view.capacity()), buffer->capacity());
+            ALOGV_IF(view.capacity() > buffer->capacity(),
+                    "view.capacity() = %zu, buffer->capacity() = %zu",
+                    view.capacity(), buffer->capacity());
+            memcpy(buffer->base(), view.data(), copyLength);
+            buffer->setRange(0, copyLength);
             break;
         }
         case C2BufferData::GRAPHIC: {
-            // TODO
-            return -ENOSYS;
+            sp<ABuffer> imageData;
+            if (!buffer->format()->findBuffer("image-data", &imageData)) {
+                return -ENOSYS;
+            }
+            if (c2Buffer->data().graphicBlocks().size() != 1u) {
+                return -ENOSYS;
+            }
+            C2ConstGraphicBlock block{c2Buffer->data().graphicBlocks().front()};
+            const C2GraphicView view{block.map().get()};
+            status_t err = ImageCopy(
+                    buffer->base(), (const MediaImage2 *)(imageData->base()), view);
+            if (err != OK) {
+                return err;
+            }
+            break;
         }
         case C2BufferData::LINEAR_CHUNKS:  [[fallthrough]];
         case C2BufferData::GRAPHIC_CHUNKS: [[fallthrough]];
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index a8fea90..3bccb7b 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -2,10 +2,21 @@
     name: "libstagefright_headers",
     export_include_dirs: ["include"],
     vendor_available: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media",
+        "com.android.media.swcodec",
+    ],
+    min_sdk_version: "29",
 }
 
 cc_library_static {
     name: "libstagefright_esds",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media",
+    ],
+    min_sdk_version: "29",
 
     srcs: ["ESDS.cpp"],
 
@@ -27,6 +38,11 @@
 
 cc_library_static {
     name: "libstagefright_metadatautils",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media",
+    ],
+    min_sdk_version: "29",
 
     srcs: ["MetaDataUtils.cpp"],
 
@@ -94,6 +110,11 @@
 
 cc_library_static {
     name: "libstagefright_mpeg2extractor",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media",
+    ],
+    min_sdk_version: "29",
 
     srcs: [
         "Utils.cpp",
@@ -252,6 +273,7 @@
         "libutils",
         "libmedia_helper",
         "libsfplugin_ccodec",
+        "libsfplugin_ccodec_utils",
         "libstagefright_codecbase",
         "libstagefright_foundation",
         "libstagefright_omx_utils",
diff --git a/media/libstagefright/HevcUtils.cpp b/media/libstagefright/HevcUtils.cpp
index 482a1a7..b347453 100644
--- a/media/libstagefright/HevcUtils.cpp
+++ b/media/libstagefright/HevcUtils.cpp
@@ -83,6 +83,7 @@
     }
 
     if (err != OK) {
+        ALOGE("error parsing VPS or SPS or PPS");
         return err;
     }
 
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index af8096d..39423c7 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -103,8 +103,40 @@
 //#define SHOW_MODEL_BUILD 1
 
 class MPEG4Writer::Track {
+    struct TrackId {
+        TrackId(uint32_t aId)
+            :mId(aId),
+             mTrackIdValid(false) {
+        }
+        bool isValid(bool akKey4BitTrackIds) {
+            // trackId cannot be zero, ISO/IEC 14496-12 8.3.2.3
+            if (mId == 0) {
+                return false;
+            }
+            /* MediaRecorder uses only 4 bit to represent track ids during notifying clients.
+             * MediaMuxer's track ids are restricted by container allowed size only.
+             * MPEG4 Container defines unsigned int (32), ISO/IEC 14496-12 8.3.2.2
+             */
+            if (akKey4BitTrackIds && mId > 15) {
+                return false;
+            }
+            mTrackIdValid = true;
+            return true;
+        }
+        uint32_t getId() const {
+            CHECK(mTrackIdValid);
+            return mId;
+        }
+        TrackId() = delete;
+        DISALLOW_EVIL_CONSTRUCTORS(TrackId);
+    private:
+        // unsigned int (32), ISO/IEC 14496-12 8.3.2.2
+        uint32_t mId;
+        bool mTrackIdValid;
+    };
+
 public:
-    Track(MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId);
+    Track(MPEG4Writer *owner, const sp<MediaSource> &source, uint32_t aTrackId);
 
     ~Track();
 
@@ -129,7 +161,7 @@
     void addChunkOffset(off64_t offset);
     void addItemOffsetAndSize(off64_t offset, size_t size, bool isExif);
     void flushItemRefs();
-    int32_t getTrackId() const { return mTrackId; }
+    TrackId& getTrackId() { return mTrackId; }
     status_t dump(int fd, const Vector<String16>& args) const;
     static const char *getFourCCForMime(const char *mime);
     const char *getTrackType() const;
@@ -290,7 +322,7 @@
     bool mIsMPEG4;
     bool mGotStartKeyFrame;
     bool mIsMalformed;
-    int32_t mTrackId;
+    TrackId mTrackId;
     int64_t mTrackDurationUs;
     int64_t mMaxChunkDurationUs;
     int64_t mLastDecodingTimeUs;
@@ -413,7 +445,7 @@
     void addOneElstTableEntry(uint32_t segmentDuration, int32_t mediaTime,
         int16_t mediaRate, int16_t mediaRateFraction);
 
-    bool isTrackMalFormed() const;
+    bool isTrackMalFormed();
     void sendTrackSummary(bool hasMultipleTracks);
 
     // Write the boxes
@@ -534,7 +566,7 @@
         release();
     }
 
-    if (fallocate(mFd, 0, 0, 1) == 0) {
+    if (fallocate(mFd, FALLOC_FL_KEEP_SIZE, 0, 1) == 0) {
         ALOGD("PreAllocation enabled");
         mPreAllocationEnabled = true;
     } else {
@@ -744,8 +776,7 @@
     // where 1MB is the common file size limit for MMS application.
     // The default MAX _MOOV_BOX_SIZE value is based on about 3
     // minute video recording with a bit rate about 3 Mbps, because
-    // statistics also show that most of the video captured are going
-    // to be less than 3 minutes.
+    // statistics show that most captured videos are less than 3 minutes.
 
     // If the estimation is wrong, we will pay the price of wasting
     // some reserved space. This should not happen so often statistically.
@@ -796,6 +827,15 @@
     return size;
 }
 
+status_t MPEG4Writer::validateAllTracksId(bool akKey4BitTrackIds) {
+    for (List<Track *>::iterator it = mTracks.begin(); it != mTracks.end(); ++it) {
+        if (!(*it)->getTrackId().isValid(akKey4BitTrackIds)) {
+            return BAD_VALUE;
+        }
+    }
+    return OK;
+}
+
 status_t MPEG4Writer::start(MetaData *param) {
     if (mInitCheck != OK) {
         return UNKNOWN_ERROR;
@@ -810,6 +850,9 @@
         mIsFileSizeLimitExplicitlyRequested = true;
     }
 
+    /* mMaxFileSizeLimitBytes has to be set everytime fd is switched, hence the following code is
+     * appropriate in start() method.
+     */
     int32_t fileSizeBits = fpathconf(mFd, _PC_FILESIZEBITS);
     ALOGD("fpathconf _PC_FILESIZEBITS:%" PRId32, fileSizeBits);
     fileSizeBits = std::min(fileSizeBits, 52 /* cap it below 4 peta bytes */);
@@ -902,10 +945,30 @@
     mInMemoryCache = NULL;
     mInMemoryCacheOffset = 0;
 
+    status_t err = OK;
+    int32_t is4bitTrackId = false;
+    if (param && param->findInt32(kKey4BitTrackIds, &is4bitTrackId) && is4bitTrackId) {
+        err = validateAllTracksId(true);
+    } else {
+        err = validateAllTracksId(false);
+    }
+    if (err != OK) {
+        return err;
+    }
 
     ALOGV("muxer starting: mHasMoovBox %d, mHasFileLevelMeta %d",
             mHasMoovBox, mHasFileLevelMeta);
 
+    err = startWriterThread();
+    if (err != OK) {
+        return err;
+    }
+
+    err = setupAndStartLooper();
+    if (err != OK) {
+        return err;
+    }
+
     writeFtypBox(param);
 
     mFreeBoxOffset = mOffset;
@@ -937,13 +1000,24 @@
     seekOrPostError(mFd, mMdatOffset, SEEK_SET);
     write("\x00\x00\x00\x01mdat????????", 16);
 
-    status_t err = startWriterThread();
-    if (err != OK) {
-        return err;
+    /* Confirm whether the writing of the initial file atoms, ftyp and free,
+     * are written to the file properly by posting kWhatNoIOErrorSoFar to the
+     * MP4WtrCtrlHlpLooper that's handling write and seek errors also. If there
+     * was kWhatIOError, the following two scenarios should be handled.
+     * 1) If kWhatIOError was delivered and processed, MP4WtrCtrlHlpLooper
+     * would have stopped all threads gracefully already and posting
+     * kWhatNoIOErrorSoFar would fail.
+     * 2) If kWhatIOError wasn't delivered or getting processed,
+     * kWhatNoIOErrorSoFar should get posted successfully.  Wait for
+     * response from MP4WtrCtrlHlpLooper.
+     */
+    sp<AMessage> msg = new AMessage(kWhatNoIOErrorSoFar, mReflector);
+    sp<AMessage> response;
+    err = msg->postAndAwaitResponse(&response);
+    if (err != OK || !response->findInt32("err", &err) || err != OK) {
+        return ERROR_IO;
     }
 
-    setupAndStartLooper();
-
     err = startTracks(param);
     if (err != OK) {
         return err;
@@ -961,6 +1035,7 @@
 status_t MPEG4Writer::stopWriterThread() {
     ALOGV("Stopping writer thread");
     if (!mWriterThreadStarted) {
+        ALOGD("Writer thread not started");
         return OK;
     }
     {
@@ -970,12 +1045,16 @@
     }
 
     void *dummy;
-    status_t err = pthread_join(mThread, &dummy);
-    WARN_UNLESS(err == 0, "stopWriterThread pthread_join err: %d", err);
-
-    err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
+    status_t err = OK;
+    int retVal = pthread_join(mThread, &dummy);
+    if (retVal == 0) {
+        err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
+        ALOGD("WriterThread stopped. Status:%d", err);
+    } else {
+        ALOGE("stopWriterThread pthread_join status:%d", retVal);
+        err = UNKNOWN_ERROR;
+    }
     mWriterThreadStarted = false;
-    WARN_UNLESS(err == 0, "stopWriterThread pthread_join retVal: %d, writer thread stopped", err);
     return err;
 }
 
@@ -1031,26 +1110,35 @@
     writeInt32(0x40000000);  // w
 }
 
-void MPEG4Writer::release() {
+status_t MPEG4Writer::release() {
     ALOGD("release()");
-    if (mPreAllocationEnabled) {
-        truncatePreAllocation();
+    status_t err = OK;
+    if (!truncatePreAllocation()) {
+        if (err == OK) { err = ERROR_IO; }
     }
-    int retVal = fsync(mFd);
-    WARN_UNLESS(retVal == 0, "fsync retVal:%d", retVal);
-    retVal = close(mFd);
-    WARN_UNLESS(retVal == 0, "close mFd retVal :%d", retVal);
+    if (fsync(mFd) != 0) {
+        ALOGW("(ignored)fsync err:%s(%d)", std::strerror(errno), errno);
+        // Don't bubble up fsync error, b/157291505.
+        // if (err == OK) { err = ERROR_IO; }
+    }
+    if (close(mFd) != 0) {
+        ALOGE("close err:%s(%d)", std::strerror(errno), errno);
+        if (err == OK) { err = ERROR_IO; }
+    }
     mFd = -1;
     if (mNextFd != -1) {
-        retVal = close(mNextFd);
+        if (close(mNextFd) != 0) {
+            ALOGE("close(mNextFd) error:%s(%d)", std::strerror(errno), errno);
+        }
+        if (err == OK) { err = ERROR_IO; }
         mNextFd = -1;
-        WARN_UNLESS(retVal == 0, "close mNextFd retVal :%d", retVal);
     }
     stopAndReleaseLooper();
     mInitCheck = NO_INIT;
     mStarted = false;
     free(mInMemoryCache);
     mInMemoryCache = NULL;
+    return err;
 }
 
 void MPEG4Writer::finishCurrentSession() {
@@ -1065,7 +1153,7 @@
     }
 
     if (mNextFd == -1) {
-        ALOGW("No FileDescripter for next recording");
+        ALOGW("No FileDescriptor for next recording");
         return INVALID_OPERATION;
     }
 
@@ -1084,12 +1172,15 @@
     } else {
         if (!mWriterThreadStarted ||
             !mStarted) {
-            status_t err = OK;
+            status_t writerErr = OK;
             if (mWriterThreadStarted) {
-                err = stopWriterThread();
+                writerErr = stopWriterThread();
             }
-            release();
-            return err;
+            status_t retErr = release();
+            if (writerErr != OK) {
+                retErr = writerErr;
+            }
+            return retErr;
         }
     }
 
@@ -1100,8 +1191,9 @@
     for (List<Track *>::iterator it = mTracks.begin();
         it != mTracks.end(); ++it) {
         status_t trackErr = (*it)->stop(stopSource);
+        WARN_UNLESS(trackErr == OK, "%s track stopped with an error",
+                    (*it)->getTrackType());
         if (err == OK && trackErr != OK) {
-            ALOGW("%s track stopped with an error", (*it)->getTrackType());
             err = trackErr;
         }
 
@@ -1118,7 +1210,6 @@
         }
     }
 
-
     if (nonImageTrackCount > 1) {
         ALOGD("Duration from tracks range is [%" PRId64 ", %" PRId64 "] us",
             minDurationUs, maxDurationUs);
@@ -1126,13 +1217,15 @@
 
     status_t writerErr = stopWriterThread();
 
-    // TODO: which error to propagage, writerErr or trackErr?
+    // Propagating writer error
     if (err == OK && writerErr != OK) {
         err = writerErr;
     }
 
     // Do not write out movie header on error except malformed track.
+    // TODO: Remove samples of malformed tracks added in mdat.
     if (err != OK && err != ERROR_MALFORMED) {
+        // Ignoring release() return value as there was an "err" already.
         release();
         return err;
     }
@@ -1174,6 +1267,7 @@
         } else {
             ALOGI("The mp4 file will not be streamable.");
         }
+        ALOGI("MOOV atom was written to the file");
     }
     mWriteBoxToMemory = false;
 
@@ -1186,7 +1280,11 @@
 
     CHECK(mBoxes.empty());
 
-    release();
+    status_t errRelease = release();
+    // Prioritize the error that occurred before release().
+    if (err == OK) {
+        err = errRelease;
+    }
     return err;
 }
 
@@ -1354,7 +1452,7 @@
 
     for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
          it != mChunkInfos.end(); ++it) {
-        int trackNum = it->mTrack->getTrackId() << 28;
+        uint32_t trackNum = (it->mTrack->getTrackId().getId() << 28);
         notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
                 trackNum | MEDIA_RECORDER_TRACK_INTER_CHUNK_TIME_MS,
                 it->mMaxInterChunkDurUs);
@@ -1438,25 +1536,21 @@
 void MPEG4Writer::addLengthPrefixedSample_l(MediaBuffer *buffer) {
     size_t length = buffer->range_length();
     if (mUse4ByteNalLength) {
-        uint8_t x = length >> 24;
-        writeOrPostError(mFd, &x, 1);
-        x = (length >> 16) & 0xff;
-        writeOrPostError(mFd, &x, 1);
-        x = (length >> 8) & 0xff;
-        writeOrPostError(mFd, &x, 1);
-        x = length & 0xff;
-        writeOrPostError(mFd, &x, 1);
-
+        uint8_t x[4];
+        x[0] = length >> 24;
+        x[1] = (length >> 16) & 0xff;
+        x[2] = (length >> 8) & 0xff;
+        x[3] = length & 0xff;
+        writeOrPostError(mFd, &x, 4);
         writeOrPostError(mFd, (const uint8_t*)buffer->data() + buffer->range_offset(), length);
-
         mOffset += length + 4;
     } else {
         CHECK_LT(length, 65536u);
 
-        uint8_t x = length >> 8;
-        writeOrPostError(mFd, &x, 1);
-        x = length & 0xff;
-        writeOrPostError(mFd, &x, 1);
+        uint8_t x[2];
+        x[0] = length >> 8;
+        x[1] = length & 0xff;
+        writeOrPostError(mFd, &x, 2);
         writeOrPostError(mFd, (const uint8_t*)buffer->data() + buffer->range_offset(), length);
         mOffset += length + 2;
     }
@@ -1512,9 +1606,9 @@
           std::strerror(errno), errno);
 
     // Can't guarantee that file is usable or write would succeed anymore, hence signal to stop.
-    sp<AMessage> msg = new AMessage(kWhatHandleIOError, mReflector);
-    status_t err = msg->post();
-    ALOGE("writeOrPostError post:%d", err);
+    sp<AMessage> msg = new AMessage(kWhatIOError, mReflector);
+    msg->setInt32("err", ERROR_IO);
+    WARN_UNLESS(msg->post() == OK, "writeOrPostError:error posting ERROR_IO");
 }
 
 void MPEG4Writer::seekOrPostError(int fd, off64_t offset, int whence) {
@@ -1531,9 +1625,9 @@
           offset, std::strerror(errno), errno);
 
     // Can't guarantee that file is usable or seek would succeed anymore, hence signal to stop.
-    sp<AMessage> msg = new AMessage(kWhatHandleIOError, mReflector);
-    status_t err = msg->post();
-    ALOGE("seekOrPostError post:%d", err);
+    sp<AMessage> msg = new AMessage(kWhatIOError, mReflector);
+    msg->setInt32("err", ERROR_IO);
+    WARN_UNLESS(msg->post() == OK, "seekOrPostError:error posting ERROR_IO");
 }
 
 void MPEG4Writer::beginBox(uint32_t id) {
@@ -1768,10 +1862,11 @@
     ALOGV("preAllocateSize :%" PRIu64 " lastFileEndOffset:%" PRIu64, preAllocateSize,
           lastFileEndOffset);
 
-    int res = fallocate(mFd, 0, lastFileEndOffset, preAllocateSize);
+    int res = fallocate(mFd, FALLOC_FL_KEEP_SIZE, lastFileEndOffset, preAllocateSize);
     if (res == -1) {
         ALOGE("fallocate err:%s, %d, fd:%d", strerror(errno), errno, mFd);
-        sp<AMessage> msg = new AMessage(kWhatHandleFallocateError, mReflector);
+        sp<AMessage> msg = new AMessage(kWhatFallocateError, mReflector);
+        msg->setInt32("err", ERROR_IO);
         status_t err = msg->post();
         mFallocateErr = true;
         ALOGD("preAllocation post:%d", err);
@@ -1783,6 +1878,9 @@
 }
 
 bool MPEG4Writer::truncatePreAllocation() {
+    if (!mPreAllocationEnabled)
+        return true;
+
     bool status = true;
     off64_t endOffset = std::max(mMdatEndOffset, mOffset);
     /* if mPreAllocateFileEndOffset >= endOffset, then preallocation logic works good. (diff >= 0).
@@ -1794,6 +1892,10 @@
     if(ftruncate(mFd, endOffset) == -1) {
         ALOGE("ftruncate err:%s, %d, fd:%d", strerror(errno), errno, mFd);
         status = false;
+        /* No need to post and handle(stop & notify client) error like it's done in preAllocate(),
+         * because ftruncate() is called during release() only and the error here would be
+         * reported from there as this function is returning false on any error in ftruncate().
+         */
     }
     return status;
 }
@@ -1899,7 +2001,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 MPEG4Writer::Track::Track(
-        MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId)
+        MPEG4Writer *owner, const sp<MediaSource> &source, uint32_t aTrackId)
     : mOwner(owner),
       mMeta(source->getFormat()),
       mSource(source),
@@ -1909,7 +2011,7 @@
       mStarted(false),
       mGotStartKeyFrame(false),
       mIsMalformed(false),
-      mTrackId(trackId),
+      mTrackId(aTrackId),
       mTrackDurationUs(0),
       mEstimatedTrackSizeBytes(0),
       mSamplesHaveSameSize(true),
@@ -2086,25 +2188,28 @@
     mElstTableEntries->add(htonl((((uint32_t)mediaRate) << 16) | (uint32_t)mediaRateFraction));
 }
 
-void MPEG4Writer::setupAndStartLooper() {
+status_t MPEG4Writer::setupAndStartLooper() {
+    status_t err = OK;
     if (mLooper == nullptr) {
         mLooper = new ALooper;
-        mLooper->setName("MP4WriterLooper");
-        mLooper->start();
+        mLooper->setName("MP4WtrCtrlHlpLooper");
+        err = mLooper->start();
         mReflector = new AHandlerReflector<MPEG4Writer>(this);
         mLooper->registerHandler(mReflector);
     }
+    ALOGD("MP4WtrCtrlHlpLooper Started");
+    return err;
 }
 
 void MPEG4Writer::stopAndReleaseLooper() {
     if (mLooper != nullptr) {
         if (mReflector != nullptr) {
-            ALOGD("unregisterHandler");
             mLooper->unregisterHandler(mReflector->id());
             mReflector.clear();
         }
         mLooper->stop();
         mLooper.clear();
+        ALOGD("MP4WtrCtrlHlpLooper stopped");
     }
 }
 
@@ -2329,19 +2434,36 @@
             break;
         }
         // ::write() or lseek64() wasn't a success, file could be malformed
-        case kWhatHandleIOError: {
-            ALOGE("kWhatHandleIOError");
+        case kWhatIOError: {
+            ALOGE("kWhatIOError");
+            int32_t err;
+            CHECK(msg->findInt32("err", &err));
             // Stop tracks' threads and main writer thread.
-            notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, ERROR_MALFORMED);
             stop();
+            notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, err);
             break;
         }
-        // fallocate() failed, hence notify app about it and stop().
-        case kWhatHandleFallocateError: {
-            ALOGE("kWhatHandleFallocateError");
-            //TODO: introduce new MEDIA_RECORDER_INFO_STOPPED instead MEDIA_RECORDER_INFO_UNKNOWN?
-            notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_UNKNOWN, ERROR_IO);
+        // fallocate() failed, hence stop() and notify app.
+        case kWhatFallocateError: {
+            ALOGE("kWhatFallocateError");
+            int32_t err;
+            CHECK(msg->findInt32("err", &err));
+            // Stop tracks' threads and main writer thread.
             stop();
+            //TODO: introduce a suitable MEDIA_RECORDER_ERROR_* instead MEDIA_RECORDER_ERROR_UNKNOWN?
+            notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, err);
+            break;
+        }
+        /* Response to kWhatNoIOErrorSoFar would be OK always as of now.
+         * Responding with other options could be added later if required.
+         */
+        case kWhatNoIOErrorSoFar: {
+            ALOGD("kWhatNoIOErrorSoFar");
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", OK);
+            sp<AReplyToken> replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+            response->postReply(replyID);
             break;
         }
         default:
@@ -2706,11 +2828,16 @@
     mDone = true;
 
     void *dummy;
-    status_t err = pthread_join(mThread, &dummy);
-    WARN_UNLESS(err == 0, "track::stop: pthread_join status:%d", err);
-    err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
-    WARN_UNLESS(err == 0, "%s track stopped. Status :%d. %s source", getTrackType(), err,
-                stopSource ? "Stop" : "Not Stop");
+    status_t err = OK;
+    int retVal = pthread_join(mThread, &dummy);
+    if (retVal == 0) {
+        err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
+        ALOGD("%s track stopped. Status:%d. %s source",
+            getTrackType(), err, stopSource ? "Stop" : "Not Stop");
+    } else {
+        ALOGE("track::stop: pthread_join retVal:%d", retVal);
+        err = UNKNOWN_ERROR;
+    }
     mStarted = false;
     return err;
 }
@@ -2849,6 +2976,7 @@
         }
 
         if (nextStartCode == NULL) {
+            ALOGE("nextStartCode is null");
             return ERROR_MALFORMED;
         }
 
@@ -3126,11 +3254,11 @@
     int64_t lastSampleDurationTicks = -1;   // Timescale based ticks
 
     if (mIsAudio) {
-        prctl(PR_SET_NAME, (unsigned long)"AudioTrackWriterThread", 0, 0, 0);
+        prctl(PR_SET_NAME, (unsigned long)"MP4WtrAudTrkThread", 0, 0, 0);
     } else if (mIsVideo) {
-        prctl(PR_SET_NAME, (unsigned long)"VideoTrackWriterThread", 0, 0, 0);
+        prctl(PR_SET_NAME, (unsigned long)"MP4WtrVidTrkThread", 0, 0, 0);
     } else {
-        prctl(PR_SET_NAME, (unsigned long)"MetadataTrackWriterThread", 0, 0, 0);
+        prctl(PR_SET_NAME, (unsigned long)"MP4WtrMetaTrkThread", 0, 0, 0);
     }
 
     if (mOwner->isRealTimeRecording()) {
@@ -3181,6 +3309,7 @@
         }
 
         ++count;
+
         int32_t isCodecConfig;
         if (buffer->meta_data().findInt32(kKeyIsCodecConfig, &isCodecConfig)
                 && isCodecConfig) {
@@ -3204,7 +3333,7 @@
                                 + buffer->range_offset(),
                             buffer->range_length());
                 } else if (mIsMPEG4) {
-                    copyCodecSpecificData((const uint8_t *)buffer->data() + buffer->range_offset(),
+                    err = copyCodecSpecificData((const uint8_t *)buffer->data() + buffer->range_offset(),
                             buffer->range_length());
                 }
             }
@@ -3213,8 +3342,10 @@
             buffer = NULL;
             if (OK != err) {
                 mSource->stop();
+                mIsMalformed = true;
+                uint32_t trackNum = (mTrackId.getId() << 28);
                 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_ERROR,
-                       mTrackId | MEDIA_RECORDER_TRACK_ERROR_GENERAL, err);
+                       trackNum | MEDIA_RECORDER_TRACK_ERROR_GENERAL, err);
                 break;
             }
 
@@ -3251,7 +3382,7 @@
          * Reserve space in the file for the current sample + to be written MOOV box. If reservation
          * for a new sample fails, preAllocate(...) stops muxing session completely. Stop() could
          * write MOOV box successfully as space for the same was reserved in the prior call.
-         * Release the current buffer/sample only here.
+         * Release the current buffer/sample here.
          */
         if (!mOwner->preAllocate(buffer->range_length())) {
             buffer->release();
@@ -3291,6 +3422,7 @@
         updateTrackSizeEstimate();
 
         if (mOwner->exceedsFileSizeLimit()) {
+            copy->release();
             if (mOwner->switchFd() != OK) {
                 ALOGW("Recorded file size exceeds limit %" PRId64 "bytes",
                         mOwner->mMaxFileSizeLimitBytes);
@@ -3301,16 +3433,15 @@
                 ALOGV("%s Current recorded file size exceeds limit %" PRId64 "bytes. Switching output",
                         getTrackType(), mOwner->mMaxFileSizeLimitBytes);
             }
-            copy->release();
             break;
         }
 
         if (mOwner->exceedsFileDurationLimit()) {
             ALOGW("Recorded file duration exceeds limit %" PRId64 "microseconds",
                     mOwner->mMaxFileDurationLimitUs);
-            mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);
             copy->release();
             mSource->stop();
+            mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);
             break;
         }
 
@@ -3592,13 +3723,13 @@
             }
         }
     }
+
     if (isTrackMalFormed()) {
-        mIsMalformed = true;
         dumpTimeStamps();
         err = ERROR_MALFORMED;
     }
 
-    mOwner->trackProgressStatus(mTrackId, -1, err);
+    mOwner->trackProgressStatus(mTrackId.getId(), -1, err);
 
     // Add final entries only for non-empty tracks.
     if (mStszTableEntries->count() > 0) {
@@ -3665,7 +3796,7 @@
     return err;
 }
 
-bool MPEG4Writer::Track::isTrackMalFormed() const {
+bool MPEG4Writer::Track::isTrackMalFormed() {
     if (mIsMalformed) {
         return true;
     }
@@ -3674,23 +3805,29 @@
     if (mOwner->mStartMeta &&
         mOwner->mStartMeta->findInt32(kKeyEmptyTrackMalFormed, &emptyTrackMalformed) &&
         emptyTrackMalformed) {
+        // MediaRecorder(sets kKeyEmptyTrackMalFormed by default) report empty tracks as malformed.
         if (!mIsHeic && mStszTableEntries->count() == 0) {  // no samples written
             ALOGE("The number of recorded samples is 0");
+            mIsMalformed = true;
             return true;
         }
         if (mIsVideo && mStssTableEntries->count() == 0) {  // no sync frames for video
             ALOGE("There are no sync frames for video track");
+            mIsMalformed = true;
             return true;
         }
     } else {
-        // No sync frames for video.
+        // Through MediaMuxer, empty tracks can be added. No sync frames for video.
         if (mIsVideo && mStszTableEntries->count() > 0 && mStssTableEntries->count() == 0) {
             ALOGE("There are no sync frames for video track");
+            mIsMalformed = true;
             return true;
         }
     }
-
-    if (OK != checkCodecSpecificData()) {         // no codec specific data
+    // Don't check for CodecSpecificData when track is empty.
+    if (mStszTableEntries->count() > 0 && OK != checkCodecSpecificData()) {
+        // No codec specific data.
+        mIsMalformed = true;
         return true;
     }
 
@@ -3704,7 +3841,7 @@
         return;
     }
 
-    int trackNum = (mTrackId << 28);
+    uint32_t trackNum = (mTrackId.getId() << 28);
 
     mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
                     trackNum | MEDIA_RECORDER_TRACK_INFO_TYPE,
@@ -3758,15 +3895,15 @@
     if (mTrackEveryTimeDurationUs > 0 &&
         timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) {
         ALOGV("Fire time tracking progress status at %" PRId64 " us", timeUs);
-        mOwner->trackProgressStatus(mTrackId, timeUs - mPreviousTrackTimeUs, err);
+        mOwner->trackProgressStatus(mTrackId.getId(), timeUs - mPreviousTrackTimeUs, err);
         mPreviousTrackTimeUs = timeUs;
     }
 }
 
 void MPEG4Writer::trackProgressStatus(
-        size_t trackId, int64_t timeUs, status_t err) {
+        uint32_t trackId, int64_t timeUs, status_t err) {
     Mutex::Autolock lock(mLock);
-    int32_t trackNum = (trackId << 28);
+    uint32_t trackNum = (trackId << 28);
 
     // Error notification
     // Do not consider ERROR_END_OF_STREAM an error
@@ -3936,8 +4073,8 @@
 
 void MPEG4Writer::Track::writeStblBox() {
     mOwner->beginBox("stbl");
-    // Add subboxes only for non-empty tracks.
-    if (mStszTableEntries->count() > 0) {
+    // Add subboxes for only non-empty and well-formed tracks.
+    if (mStszTableEntries->count() > 0 && !isTrackMalFormed()) {
         mOwner->beginBox("stsd");
         mOwner->writeInt32(0);               // version=0, flags=0
         mOwner->writeInt32(1);               // entry count
@@ -4242,7 +4379,7 @@
     mOwner->writeInt32(0x07);          // version=0, flags=7
     mOwner->writeInt32(now);           // creation time
     mOwner->writeInt32(now);           // modification time
-    mOwner->writeInt32(mTrackId);      // track id starts with 1
+    mOwner->writeInt32(mTrackId.getId()); // track id starts with 1
     mOwner->writeInt32(0);             // reserved
     int64_t trakDurationUs = getDurationUs();
     int32_t mvhdTimeScale = mOwner->getTimeScale();
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 288b551..5d17f97 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -57,6 +57,7 @@
 #include <media/stagefright/BufferProducerWrapper.h>
 #include <media/stagefright/CCodec.h>
 #include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MediaCodecConstants.h>
 #include <media/stagefright/MediaCodecList.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
@@ -95,9 +96,12 @@
 static const char *kCodecCrypto = "android.media.mediacodec.crypto";   /* 0,1 */
 static const char *kCodecProfile = "android.media.mediacodec.profile";  /* 0..n */
 static const char *kCodecLevel = "android.media.mediacodec.level";  /* 0..n */
+static const char *kCodecBitrateMode = "android.media.mediacodec.bitrate_mode";  /* CQ/VBR/CBR */
+static const char *kCodecBitrate = "android.media.mediacodec.bitrate";  /* 0..n */
 static const char *kCodecMaxWidth = "android.media.mediacodec.maxwidth";  /* 0..n */
 static const char *kCodecMaxHeight = "android.media.mediacodec.maxheight";  /* 0..n */
 static const char *kCodecError = "android.media.mediacodec.errcode";
+static const char *kCodecLifetimeMs = "android.media.mediacodec.lifetimeMs";   /* 0..n ms*/
 static const char *kCodecErrorState = "android.media.mediacodec.errstate";
 static const char *kCodecLatencyMax = "android.media.mediacodec.latency.max";   /* in us */
 static const char *kCodecLatencyMin = "android.media.mediacodec.latency.min";   /* in us */
@@ -619,7 +623,6 @@
       mFlags(0),
       mStickyError(OK),
       mSoftRenderer(NULL),
-      mMetricsHandle(0),
       mIsVideo(false),
       mVideoWidth(0),
       mVideoHeight(0),
@@ -679,6 +682,8 @@
         mIndexOfFirstFrameWhenLowLatencyOn = -1;
         mInputBufferCounter = 0;
     }
+
+    mLifetimeStartNs = systemTime(SYSTEM_TIME_MONOTONIC);
 }
 
 void MediaCodec::updateMediametrics() {
@@ -687,7 +692,6 @@
         return;
     }
 
-
     if (mLatencyHist.getCount() != 0 ) {
         mediametrics_setInt64(mMetricsHandle, kCodecLatencyMax, mLatencyHist.getMax());
         mediametrics_setInt64(mMetricsHandle, kCodecLatencyMin, mLatencyHist.getMin());
@@ -703,6 +707,11 @@
     if (mLatencyUnknown > 0) {
         mediametrics_setInt64(mMetricsHandle, kCodecLatencyUnknown, mLatencyUnknown);
     }
+    if (mLifetimeStartNs > 0) {
+        nsecs_t lifetime = systemTime(SYSTEM_TIME_MONOTONIC) - mLifetimeStartNs;
+        lifetime = lifetime / (1000 * 1000);    // emitted in ms, truncated not rounded
+        mediametrics_setInt64(mMetricsHandle, kCodecLifetimeMs, lifetime);
+    }
 
     {
         Mutex::Autolock al(mLatencyLock);
@@ -740,7 +749,6 @@
         }
     }
 
-
     // spit the data (if any) into the supplied analytics record
     if (recentHist.getCount()!= 0 ) {
         mediametrics_setInt64(item, kCodecRecentLatencyMax, recentHist.getMax());
@@ -2309,6 +2317,8 @@
                         // meaningful and confusing for an encoder in a transcoder scenario
                         mInputFormat->setInt32("allow-frame-drop", mAllowFrameDroppingBySurface);
                     }
+                    sp<AMessage> interestingFormat =
+                            (mFlags & kFlagIsEncoder) ? mOutputFormat : mInputFormat;
                     ALOGV("[%s] configured as input format: %s, output format: %s",
                             mComponentName.c_str(),
                             mInputFormat->debugString(4).c_str(),
@@ -2322,6 +2332,7 @@
                     (new AMessage)->postReply(mReplyID);
 
                     // augment our media metrics info, now that we know more things
+                    // such as what the codec extracted from any CSD passed in.
                     if (mMetricsHandle != 0) {
                         sp<AMessage> format;
                         if (mConfigureMsg != NULL &&
@@ -2333,6 +2344,30 @@
                                                             mime.c_str());
                                 }
                             }
+                        // perhaps video only?
+                        int32_t profile = 0;
+                        if (interestingFormat->findInt32("profile", &profile)) {
+                            mediametrics_setInt32(mMetricsHandle, kCodecProfile, profile);
+                        }
+                        int32_t level = 0;
+                        if (interestingFormat->findInt32("level", &level)) {
+                            mediametrics_setInt32(mMetricsHandle, kCodecLevel, level);
+                        }
+                        // bitrate and bitrate mode, encoder only
+                        if (mFlags & kFlagIsEncoder) {
+                            // encoder specific values
+                            int32_t bitrate_mode = -1;
+                            if (mOutputFormat->findInt32(KEY_BITRATE_MODE, &bitrate_mode)) {
+                                    mediametrics_setCString(mMetricsHandle, kCodecBitrateMode,
+                                          asString_BitrateMode(bitrate_mode));
+                            }
+                            int32_t bitrate = -1;
+                            if (mOutputFormat->findInt32(KEY_BIT_RATE, &bitrate)) {
+                                    mediametrics_setInt32(mMetricsHandle, kCodecBitrate, bitrate);
+                            }
+                        } else {
+                            // decoder specific values
+                        }
                     }
                     break;
                 }
@@ -2461,6 +2496,18 @@
                         }
                         break;
                     }
+                    if (!mLeftover.empty()) {
+                        ssize_t index = dequeuePortBuffer(kPortIndexInput);
+                        CHECK_GE(index, 0);
+
+                        status_t err = handleLeftover(index);
+                        if (err != OK) {
+                            setStickyError(err);
+                            postActivityNotificationIfPossible();
+                            cancelPendingDequeueOperations();
+                        }
+                        break;
+                    }
 
                     if (mFlags & kFlagIsAsync) {
                         if (!mHaveInputSurface) {
@@ -3156,7 +3203,15 @@
                 break;
             }
 
-            status_t err = onQueueInputBuffer(msg);
+            status_t err = UNKNOWN_ERROR;
+            if (!mLeftover.empty()) {
+                mLeftover.push_back(msg);
+                size_t index;
+                msg->findSize("index", &index);
+                err = handleLeftover(index);
+            } else {
+                err = onQueueInputBuffer(msg);
+            }
 
             PostReplyWithError(replyID, err);
             break;
@@ -3443,8 +3498,8 @@
     sp<hardware::HidlMemory> memory;
     size_t offset = 0;
 
-    if ((mFlags & kFlagUseBlockModel) && mOwnerName.startsWith("codec2::")) {
-        if (mCrypto) {
+    if (mFlags & kFlagUseBlockModel) {
+        if (hasCryptoOrDescrambler()) {
             constexpr size_t kInitialDealerCapacity = 1048576;  // 1MB
             thread_local sp<MemoryDealer> sDealer = new MemoryDealer(
                     kInitialDealerCapacity, "CSD(1MB)");
@@ -3569,6 +3624,9 @@
     CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
     Mutex::Autolock al(mBufferLock);
 
+    if (portIndex == kPortIndexInput) {
+        mLeftover.clear();
+    }
     for (size_t i = 0; i < mPortBuffers[portIndex].size(); ++i) {
         BufferInfo *info = &mPortBuffers[portIndex][i];
 
@@ -3699,7 +3757,26 @@
             err = mBufferChannel->attachEncryptedBuffer(
                     memory, (mFlags & kFlagIsSecure), key, iv, mode, pattern,
                     offset, subSamples, numSubSamples, buffer);
+        } else {
+            err = UNKNOWN_ERROR;
         }
+
+        if (err == OK && !buffer->asC2Buffer()
+                && c2Buffer && c2Buffer->data().type() == C2BufferData::LINEAR) {
+            C2ConstLinearBlock block{c2Buffer->data().linearBlocks().front()};
+            if (block.size() > buffer->size()) {
+                C2ConstLinearBlock leftover = block.subBlock(
+                        block.offset() + buffer->size(), block.size() - buffer->size());
+                sp<WrapperObject<std::shared_ptr<C2Buffer>>> obj{
+                    new WrapperObject<std::shared_ptr<C2Buffer>>{
+                        C2Buffer::CreateLinearBuffer(leftover)}};
+                msg->setObject("c2buffer", obj);
+                mLeftover.push_front(msg);
+                // Not sending EOS if we have leftovers
+                flags &= ~BUFFER_FLAG_EOS;
+            }
+        }
+
         offset = buffer->offset();
         size = buffer->size();
         if (err != OK) {
@@ -3764,6 +3841,16 @@
     return err;
 }
 
+status_t MediaCodec::handleLeftover(size_t index) {
+    if (mLeftover.empty()) {
+        return OK;
+    }
+    sp<AMessage> msg = mLeftover.front();
+    mLeftover.pop_front();
+    msg->setSize("index", index);
+    return onQueueInputBuffer(msg);
+}
+
 //static
 size_t MediaCodec::CreateFramesRenderedMessage(
         const std::list<FrameRenderTracker::Info> &done, sp<AMessage> &msg) {
diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp
index 1cb45ac..cab4ebd 100644
--- a/media/libstagefright/MediaMuxer.cpp
+++ b/media/libstagefright/MediaMuxer.cpp
@@ -48,8 +48,7 @@
 
 MediaMuxer::MediaMuxer(int fd, OutputFormat format)
     : mFormat(format),
-      mState(UNINITIALIZED),
-      mError(OK) {
+      mState(UNINITIALIZED) {
     if (isMp4Format(format)) {
         mWriter = new MPEG4Writer(fd);
     } else if (format == OUTPUT_FORMAT_WEBM) {
@@ -59,7 +58,6 @@
     }
 
     if (mWriter != NULL) {
-        mWriter->setMuxerListener(this);
         mFileMeta = new MetaData;
         if (format == OUTPUT_FORMAT_HEIF) {
             // Note that the key uses recorder file types.
@@ -157,23 +155,16 @@
 
 status_t MediaMuxer::stop() {
     Mutex::Autolock autoLock(mMuxerLock);
-    if (mState == STARTED || mState == ERROR) {
+    if (mState == STARTED) {
         mState = STOPPED;
         for (size_t i = 0; i < mTrackList.size(); i++) {
             if (mTrackList[i]->stop() != OK) {
                 return INVALID_OPERATION;
             }
         }
-        // Unlock this mutex to allow notify to be called during stop process.
-        mMuxerLock.unlock();
         status_t err = mWriter->stop();
-        mMuxerLock.lock();
-        if (err != OK || mError != OK) {
-            ALOGE("stop err: %d, mError:%d", err, mError);
-        }
-        // Prioritize mError over err.
-        if (mError != OK) {
-            err = mError;
+        if (err != OK) {
+            ALOGE("stop() err: %d", err);
         }
         return err;
     } else {
@@ -229,29 +220,4 @@
     return currentTrack->pushBuffer(mediaBuffer);
 }
 
-void MediaMuxer::notify(int msg, int ext1, int ext2) {
-    switch (msg) {
-        case MEDIA_RECORDER_EVENT_ERROR:
-        case MEDIA_RECORDER_TRACK_EVENT_ERROR: {
-            Mutex::Autolock autoLock(mMuxerLock);
-            mState = ERROR;
-            mError = ext2;
-            ALOGW("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
-            break;
-        }
-        case MEDIA_RECORDER_EVENT_INFO: {
-            if (ext1 == MEDIA_RECORDER_INFO_UNKNOWN) {
-                Mutex::Autolock autoLock(mMuxerLock);
-                mState = ERROR;
-                mError = ext2;
-                ALOGW("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
-            }
-            break;
-        }
-        default:
-            // Ignore INFO and other notifications for now.
-            break;
-    }
-}
-
 }  // namespace android
diff --git a/media/libstagefright/bqhelper/Android.bp b/media/libstagefright/bqhelper/Android.bp
index 37e842a..8698d33 100644
--- a/media/libstagefright/bqhelper/Android.bp
+++ b/media/libstagefright/bqhelper/Android.bp
@@ -63,6 +63,8 @@
     vndk: {
         enabled: true,
     },
+    min_sdk_version: "29",
+
     shared_libs: [ "libgui" ],
     target: {
         vendor: {
diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.bp b/media/libstagefright/codecs/m4v_h263/dec/Android.bp
index c67dc2b..4303565 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/Android.bp
+++ b/media/libstagefright/codecs/m4v_h263/dec/Android.bp
@@ -1,6 +1,11 @@
 cc_library_static {
     name: "libstagefright_m4vh263dec",
     vendor_available: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media.swcodec",
+    ],
+    min_sdk_version: "29",
     shared_libs: ["liblog"],
 
     srcs: [
diff --git a/media/libstagefright/codecs/m4v_h263/enc/Android.bp b/media/libstagefright/codecs/m4v_h263/enc/Android.bp
index 846f614..b8bc24e 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/Android.bp
+++ b/media/libstagefright/codecs/m4v_h263/enc/Android.bp
@@ -1,6 +1,11 @@
 cc_library_static {
     name: "libstagefright_m4vh263enc",
     vendor_available: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media.swcodec",
+    ],
+    min_sdk_version: "29",
 
     srcs: [
         "src/bitstream_io.cpp",
diff --git a/media/libstagefright/foundation/Android.bp b/media/libstagefright/foundation/Android.bp
index 9fe879e..f440e00 100644
--- a/media/libstagefright/foundation/Android.bp
+++ b/media/libstagefright/foundation/Android.bp
@@ -3,6 +3,7 @@
     export_include_dirs: ["include"],
     vendor_available: true,
     host_supported: true,
+    min_sdk_version: "29",
 }
 
 cc_defaults {
@@ -101,11 +102,13 @@
 cc_library {
     name: "libstagefright_foundation",
     defaults: ["libstagefright_foundation_defaults"],
+    min_sdk_version: "29",
 }
 
 cc_library_static {
     name: "libstagefright_foundation_without_imemory",
     defaults: ["libstagefright_foundation_defaults"],
+    min_sdk_version: "29",
 
     cflags: [
         "-Wno-multichar",
diff --git a/media/libstagefright/id3/Android.bp b/media/libstagefright/id3/Android.bp
index c8173cf..db37fe9 100644
--- a/media/libstagefright/id3/Android.bp
+++ b/media/libstagefright/id3/Android.bp
@@ -1,5 +1,6 @@
 cc_library_static {
     name: "libstagefright_id3",
+    min_sdk_version: "29",
 
     srcs: ["ID3.cpp"],
 
diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
index 34a7d55..a1fe57c 100644
--- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h
+++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
@@ -85,9 +85,10 @@
     friend struct AHandlerReflector<MPEG4Writer>;
 
     enum {
-        kWhatSwitch                          = 'swch',
-        kWhatHandleIOError                   = 'ioer',
-        kWhatHandleFallocateError            = 'faer'
+        kWhatSwitch                  = 'swch',
+        kWhatIOError                 = 'ioer',
+        kWhatFallocateError          = 'faer',
+        kWhatNoIOErrorSoFar          = 'noie'
     };
 
     int  mFd;
@@ -231,7 +232,7 @@
     status_t stopWriterThread();
     static void *ThreadWrapper(void *me);
     void threadFunc();
-    void setupAndStartLooper();
+    status_t setupAndStartLooper();
     void stopAndReleaseLooper();
 
     // Buffer a single chunk to be written out later.
@@ -287,7 +288,8 @@
     bool exceedsFileDurationLimit();
     bool approachingFileSizeLimit();
     bool isFileStreamable() const;
-    void trackProgressStatus(size_t trackId, int64_t timeUs, status_t err = OK);
+    void trackProgressStatus(uint32_t trackId, int64_t timeUs, status_t err = OK);
+    status_t validateAllTracksId(bool akKey4BitTrackIds);
     void writeCompositionMatrix(int32_t degrees);
     void writeMvhdBox(int64_t durationUs);
     void writeMoovBox(int64_t durationUs);
@@ -310,7 +312,7 @@
      */
     bool preAllocate(uint64_t wantSize);
     /*
-     * Truncate file as per the size used for meta data and actual data in a session.
+     * Truncate file as per the size used for metadata and actual data in a session.
      */
     bool truncatePreAllocation();
 
@@ -327,7 +329,7 @@
     void writeFileLevelMetaBox();
 
     void sendSessionSummary();
-    void release();
+    status_t release();
     status_t switchFd();
     status_t reset(bool stopSource = true);
 
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 8765a33..f7e6c27 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -371,7 +371,8 @@
     sp<Surface> mSurface;
     SoftwareRenderer *mSoftRenderer;
 
-    mediametrics_handle_t mMetricsHandle;
+    mediametrics_handle_t mMetricsHandle = 0;
+    nsecs_t mLifetimeStartNs = 0;
     void initMediametrics();
     void updateMediametrics();
     void flushMediametrics();
@@ -515,6 +516,9 @@
     class ReleaseSurface;
     std::unique_ptr<ReleaseSurface> mReleaseSurface;
 
+    std::list<sp<AMessage>> mLeftover;
+    status_t handleLeftover(size_t index);
+
     sp<BatteryChecker> mBatteryChecker;
 
     void statsBufferSent(int64_t presentationUs);
diff --git a/media/libstagefright/include/media/stagefright/MediaMuxer.h b/media/libstagefright/include/media/stagefright/MediaMuxer.h
index 7c75f74..a1b9465 100644
--- a/media/libstagefright/include/media/stagefright/MediaMuxer.h
+++ b/media/libstagefright/include/media/stagefright/MediaMuxer.h
@@ -117,8 +117,6 @@
     status_t writeSampleData(const sp<ABuffer> &buffer, size_t trackIndex,
                              int64_t timeUs, uint32_t flags) ;
 
-    void notify(int msg, int ext1, int ext2);
-
 private:
     const OutputFormat mFormat;
     sp<MediaWriter> mWriter;
@@ -130,11 +128,9 @@
         UNINITIALIZED,
         INITIALIZED,
         STARTED,
-        STOPPED,
-        ERROR
+        STOPPED
     };
     State mState;
-    status_t mError;
 
     DISALLOW_EVIL_CONSTRUCTORS(MediaMuxer);
 };
diff --git a/media/libstagefright/include/media/stagefright/MediaWriter.h b/media/libstagefright/include/media/stagefright/MediaWriter.h
index 08e54b3..1f4fbcb 100644
--- a/media/libstagefright/include/media/stagefright/MediaWriter.h
+++ b/media/libstagefright/include/media/stagefright/MediaWriter.h
@@ -21,7 +21,7 @@
 #include <utils/RefBase.h>
 #include <media/stagefright/MediaSource.h>
 #include <media/IMediaRecorderClient.h>
-#include <media/stagefright/MediaMuxer.h>
+#include <media/mediarecorder.h>
 
 namespace android {
 
@@ -46,7 +46,6 @@
     virtual void setListener(const sp<IMediaRecorderClient>& listener) {
         mListener = listener;
     }
-    virtual void setMuxerListener(const wp<MediaMuxer>& muxer) { mMuxer = muxer; }
 
     virtual status_t dump(int /*fd*/, const Vector<String16>& /*args*/) {
         return OK;
@@ -61,17 +60,20 @@
     int64_t mMaxFileSizeLimitBytes;
     int64_t mMaxFileDurationLimitUs;
     sp<IMediaRecorderClient> mListener;
-    wp<MediaMuxer> mMuxer;
 
     void notify(int msg, int ext1, int ext2) {
-        ALOG(LOG_VERBOSE, "MediaWriter", "notify msg:%d, ext1:%d, ext2:%d", msg, ext1, ext2);
+        if (msg == MEDIA_RECORDER_TRACK_EVENT_INFO || msg == MEDIA_RECORDER_TRACK_EVENT_ERROR) {
+            uint32_t trackId = (ext1 >> 28) & 0xf;
+            int type = ext1 & 0xfffffff;
+            ALOG(LOG_VERBOSE, "MediaWriter", "Track event err/info msg:%d, trackId:%u, type:%d,"
+                                             "val:%d", msg, trackId, type, ext2);
+        } else {
+            ALOG(LOG_VERBOSE, "MediaWriter", "Recorder event msg:%d, ext1:%d, ext2:%d",
+                                              msg, ext1, ext2);
+        }
         if (mListener != nullptr) {
             mListener->notify(msg, ext1, ext2);
         }
-        sp<MediaMuxer> muxer = mMuxer.promote();
-        if (muxer != nullptr) {
-            muxer->notify(msg, ext1, ext2);
-        }
     }
 private:
     MediaWriter(const MediaWriter &);
diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h
index 3b701f6..64eb8b4 100644
--- a/media/libstagefright/include/media/stagefright/MetaDataBase.h
+++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h
@@ -239,6 +239,12 @@
 
     kKeyHapticChannelCount = 'hapC',
 
+    /* MediaRecorder.h, error notifications can represent track ids with 4 bits only.
+     * | track id | reserved |     error or info type     |
+     * 31         28         16                           0
+     */
+    kKey4BitTrackIds = '4bid',
+
     // Treat empty track as malformed for MediaRecorder.
     kKeyEmptyTrackMalFormed = 'nemt', // bool (int32_t)
 };
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 5b2f6de..ac42373 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -214,6 +214,80 @@
     }
 }
 
+template <typename T>
+inline static T *asSetting(void *setting /* nonnull */, size_t size) {
+    // no need to check internally stored size as that is outside of sanitizing
+    // the underlying buffer's size is the one passed into this method.
+    if (size < sizeof(T)) {
+        return nullptr;
+    }
+
+    return (T *)setting;
+}
+
+inline static void sanitize(OMX_CONFIG_CONTAINERNODEIDTYPE *s) {
+    s->cNodeName = 0;
+}
+
+inline static void sanitize(OMX_CONFIG_METADATAITEMTYPE *s) {
+    s->sLanguageCountry = 0;
+}
+
+inline static void sanitize(OMX_PARAM_PORTDEFINITIONTYPE *s) {
+    switch (s->eDomain) {
+    case OMX_PortDomainAudio:
+        s->format.audio.cMIMEType = 0;
+        break;
+    case OMX_PortDomainVideo:
+        s->format.video.cMIMEType = 0;
+        break;
+    case OMX_PortDomainImage:
+        s->format.image.cMIMEType = 0;
+        break;
+    default:
+        break;
+    }
+}
+
+template <typename T>
+static bool sanitizeAs(void *setting, size_t size) {
+    T *s = asSetting<T>(setting, size);
+    if (s) {
+        sanitize(s);
+        return true;
+    }
+    return false;
+}
+
+static void sanitizeSetting(OMX_INDEXTYPE index, void *setting, size_t size) {
+    if (size < 8 || setting == nullptr) {
+        return;
+    }
+
+    bool ok = true;
+
+    // there are 3 standard OMX settings that contain pointer members
+    switch ((OMX_U32)index) {
+    case OMX_IndexConfigCounterNodeID:
+        ok = sanitizeAs<OMX_CONFIG_CONTAINERNODEIDTYPE>(setting, size);
+        break;
+    case OMX_IndexConfigMetadataItem:
+        ok = sanitizeAs<OMX_CONFIG_METADATAITEMTYPE>(setting, size);
+        break;
+    case OMX_IndexParamPortDefinition:
+        ok = sanitizeAs<OMX_PARAM_PORTDEFINITIONTYPE>(setting, size);
+        break;
+    }
+
+    if (!ok) {
+        // cannot effectively sanitize - we should not be here as IOMX.cpp
+        // should guard against size being too small. Nonetheless, log and
+        // clear result.
+        android_errorWriteLog(0x534e4554, "120781925");
+        memset(setting, 0, size);
+    }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 // This provides the underlying Thread used by CallbackDispatcher.
@@ -608,7 +682,7 @@
 }
 
 status_t OMXNodeInstance::getParameter(
-        OMX_INDEXTYPE index, void *params, size_t /* size */) {
+        OMX_INDEXTYPE index, void *params, size_t size) {
     Mutex::Autolock autoLock(mLock);
     if (mHandle == NULL) {
         return DEAD_OBJECT;
@@ -625,6 +699,7 @@
     if (err != OMX_ErrorNoMore) {
         CLOG_IF_ERROR(getParameter, err, "%s(%#x)", asString(extIndex), index);
     }
+    sanitizeSetting(index, params, size);
     return StatusFromOMXError(err);
 }
 
@@ -650,11 +725,12 @@
     OMX_ERRORTYPE err = OMX_SetParameter(
             mHandle, index, const_cast<void *>(params));
     CLOG_IF_ERROR(setParameter, err, "%s(%#x)", asString(extIndex), index);
+    sanitizeSetting(index, const_cast<void *>(params), size);
     return StatusFromOMXError(err);
 }
 
 status_t OMXNodeInstance::getConfig(
-        OMX_INDEXTYPE index, void *params, size_t /* size */) {
+        OMX_INDEXTYPE index, void *params, size_t size) {
     Mutex::Autolock autoLock(mLock);
     if (mHandle == NULL) {
         return DEAD_OBJECT;
@@ -671,6 +747,8 @@
     if (err != OMX_ErrorNoMore) {
         CLOG_IF_ERROR(getConfig, err, "%s(%#x)", asString(extIndex), index);
     }
+
+    sanitizeSetting(index, params, size);
     return StatusFromOMXError(err);
 }
 
@@ -692,6 +770,7 @@
     OMX_ERRORTYPE err = OMX_SetConfig(
             mHandle, index, const_cast<void *>(params));
     CLOG_IF_ERROR(setConfig, err, "%s(%#x)", asString(extIndex), index);
+    sanitizeSetting(index, const_cast<void *>(params), size);
     return StatusFromOMXError(err);
 }
 
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index 7893148..039991c 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -211,6 +211,17 @@
     status_t err = getPortDefinition(portIndex, &def);
     EXPECT_SUCCESS(err, "getPortDefinition");
 
+    switch (def.eDomain) {
+        case OMX_PortDomainVideo:
+            EXPECT(def.format.video.cMIMEType == 0, "portDefinition video MIME");
+            break;
+        case OMX_PortDomainAudio:
+            EXPECT(def.format.audio.cMIMEType == 0, "portDefinition audio MIME");
+            break;
+        default:
+            break;
+    }
+
     for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
         Buffer buffer;
         buffer.mFlags = 0;
diff --git a/media/libwatchdog/Android.bp b/media/libwatchdog/Android.bp
index 2aefa7d..1a87824 100644
--- a/media/libwatchdog/Android.bp
+++ b/media/libwatchdog/Android.bp
@@ -31,4 +31,5 @@
         },
     },
     apex_available: ["com.android.media"],
+    min_sdk_version: "29",
 }
diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp
index a04a962..a2f8230 100644
--- a/media/ndk/Android.bp
+++ b/media/ndk/Android.bp
@@ -35,6 +35,12 @@
 cc_library_headers {
     name: "media_ndk_headers",
     vendor_available: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media",
+        "com.android.media.swcodec",
+    ],
+    min_sdk_version: "29",
     export_include_dirs: ["include"]
 }
 
diff --git a/services/audioflinger/FastCapture.cpp b/services/audioflinger/FastCapture.cpp
index dd84bf2..d6d6e25 100644
--- a/services/audioflinger/FastCapture.cpp
+++ b/services/audioflinger/FastCapture.cpp
@@ -154,7 +154,7 @@
         mReadBufferState = -1;
         dumpState->mFrameCount = frameCount;
     }
-
+    dumpState->mSilenced = current->mSilenceCapture;
 }
 
 void FastCapture::onWork()
@@ -208,6 +208,9 @@
             mReadBufferState = frameCount;
         }
         if (mReadBufferState > 0) {
+            if (current->mSilenceCapture) {
+                memset(mReadBuffer, 0, mReadBufferState * Format_frameSize(mFormat));
+            }
             ssize_t framesWritten = mPipeSink->write(mReadBuffer, mReadBufferState);
             audio_track_cblk_t* cblk = current->mCblk;
             if (fastPatchRecordBufferProvider != 0) {
diff --git a/services/audioflinger/FastCaptureDumpState.cpp b/services/audioflinger/FastCaptureDumpState.cpp
index 53eeba5..b8b3866 100644
--- a/services/audioflinger/FastCaptureDumpState.cpp
+++ b/services/audioflinger/FastCaptureDumpState.cpp
@@ -44,10 +44,11 @@
     double periodSec = (double) mFrameCount / mSampleRate;
     dprintf(fd, "  FastCapture command=%s readSequence=%u framesRead=%u\n"
                 "              readErrors=%u sampleRate=%u frameCount=%zu\n"
-                "              measuredWarmup=%.3g ms, warmupCycles=%u period=%.2f ms\n",
+                "              measuredWarmup=%.3g ms, warmupCycles=%u period=%.2f ms\n"
+                "              silenced: %s\n",
                 FastCaptureState::commandToString(mCommand), mReadSequence, mFramesRead,
                 mReadErrors, mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles,
-                periodSec * 1e3);
+                periodSec * 1e3, mSilenced ? "true" : "false");
 }
 
 }   // android
diff --git a/services/audioflinger/FastCaptureDumpState.h b/services/audioflinger/FastCaptureDumpState.h
index 6f9c4c3..a1b8706 100644
--- a/services/audioflinger/FastCaptureDumpState.h
+++ b/services/audioflinger/FastCaptureDumpState.h
@@ -35,6 +35,7 @@
     uint32_t mReadErrors;       // total number of read() errors
     uint32_t mSampleRate;
     size_t   mFrameCount;
+    bool     mSilenced = false; // capture is silenced
 };
 
 }   // android
diff --git a/services/audioflinger/FastCaptureState.h b/services/audioflinger/FastCaptureState.h
index d287232..f949275 100644
--- a/services/audioflinger/FastCaptureState.h
+++ b/services/audioflinger/FastCaptureState.h
@@ -41,6 +41,8 @@
     audio_format_t  mFastPatchRecordFormat = AUDIO_FORMAT_INVALID;
     AudioBufferProvider* mFastPatchRecordBufferProvider = nullptr;   // a reference to a patch
                                                                      // record in fast mode
+    bool            mSilenceCapture = false;    // request to silence capture for fast track.
+                                                // note: this also silences the normal mixer pipe
 
     // Extends FastThreadState::Command
     static const Command
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 4a4899f..024168b 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -7089,6 +7089,8 @@
         // reference to a fast track which is about to be removed
         sp<RecordTrack> fastTrackToRemove;
 
+        bool silenceFastCapture = false;
+
         { // scope for mLock
             Mutex::Autolock _l(mLock);
 
@@ -7176,14 +7178,33 @@
                             __func__, activeTrackState, activeTrack->id(), size);
                 }
 
-                activeTracks.add(activeTrack);
-                i++;
-
                 if (activeTrack->isFastTrack()) {
                     ALOG_ASSERT(!mFastTrackAvail);
                     ALOG_ASSERT(fastTrack == 0);
+                    // if the active fast track is silenced either:
+                    // 1) silence the whole capture from fast capture buffer if this is
+                    //    the only active track
+                    // 2) invalidate this track: this will cause the client to reconnect and possibly
+                    //    be invalidated again until unsilenced
+                    if (activeTrack->isSilenced()) {
+                        if (size > 1) {
+                            activeTrack->invalidate();
+                            ALOG_ASSERT(fastTrackToRemove == 0);
+                            fastTrackToRemove = activeTrack;
+                            removeTrack_l(activeTrack);
+                            mActiveTracks.remove(activeTrack);
+                            size--;
+                            continue;
+                        } else {
+                            silenceFastCapture = true;
+                        }
+                    }
                     fastTrack = activeTrack;
                 }
+
+                activeTracks.add(activeTrack);
+                i++;
+
             }
 
             mActiveTracks.updatePowerState(this);
@@ -7257,6 +7278,10 @@
                         AUDIO_FORMAT_INVALID : fastTrack->format();
                 didModify = true;
             }
+            if (state->mSilenceCapture != silenceFastCapture) {
+                state->mSilenceCapture = silenceFastCapture;
+                didModify = true;
+            }
             sq->end(didModify);
             if (didModify) {
                 sq->push(block);
@@ -7341,8 +7366,10 @@
 
         // Update server timestamp with server stats
         // systemTime() is optional if the hardware supports timestamps.
-        mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER] += framesRead;
-        mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = lastIoEndNs;
+        if (framesRead >= 0) {
+            mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER] += framesRead;
+            mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = lastIoEndNs;
+        }
 
         // Update server timestamp with kernel stats
         if (mPipeSource.get() == nullptr /* don't obtain for FastCapture, could block */) {
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 15c66fb..01d5345 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -373,10 +373,15 @@
     bool                mIsInvalid; // non-resettable latch, set by invalidate()
 
     // It typically takes 5 threadloop mix iterations for latency to stabilize.
-    static inline constexpr int32_t LOG_START_COUNTDOWN = 8;
-    int32_t             mLogStartCountdown = 0;
-    int64_t             mLogStartTimeNs = 0;
-    int64_t             mLogStartFrames = 0;
+    // However, this can be 12+ iterations for BT.
+    // To be sure, we wait for latency to dip (it usually increases at the start)
+    // to assess stability and then log to MediaMetrics.
+    // Rapid start / pause calls may cause inaccurate numbers.
+    static inline constexpr int32_t LOG_START_COUNTDOWN = 12;
+    int32_t             mLogStartCountdown = 0; // Mixer period countdown
+    int64_t             mLogStartTimeNs = 0;    // Monotonic time at start()
+    int64_t             mLogStartFrames = 0;    // Timestamp frames at start()
+    double              mLogLatencyMs = 0.;     // Track the last log latency
 
     TrackMetrics        mTrackMetrics;
 
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 73a40d3..9386a42 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -978,7 +978,8 @@
             mLogStartCountdown = LOG_START_COUNTDOWN;
             mLogStartTimeNs = systemTime();
             mLogStartFrames = mAudioTrackServerProxy->getTimestamp()
-                    .mPosition[ExtendedTimestamp::LOCATION_SERVER];
+                    .mPosition[ExtendedTimestamp::LOCATION_KERNEL];
+            mLogLatencyMs = 0.;
         }
 
         if (status == NO_ERROR || status == ALREADY_EXISTS) {
@@ -1514,23 +1515,31 @@
     mServerLatencyFromTrack.store(useTrackTimestamp);
     mServerLatencyMs.store(latencyMs);
 
-    if (mLogStartCountdown > 0) {
-        if (--mLogStartCountdown == 0) {
+    if (mLogStartCountdown > 0
+            && local.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] > 0
+            && local.mPosition[ExtendedTimestamp::LOCATION_KERNEL] > 0)
+    {
+        if (mLogStartCountdown > 1) {
+            --mLogStartCountdown;
+        } else if (latencyMs < mLogLatencyMs) { // wait for latency to stabilize (dip)
+            mLogStartCountdown = 0;
             // startup is the difference in times for the current timestamp and our start
             double startUpMs =
-                    (local.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] - mLogStartTimeNs) * 1e-6;
+                    (local.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] - mLogStartTimeNs) * 1e-6;
             // adjust for frames played.
-            startUpMs -= (local.mPosition[ExtendedTimestamp::LOCATION_SERVER] - mLogStartFrames)
-                      * 1e3 / mSampleRate;
-            ALOGV("%s: logging localTime:%lld, startTime:%lld"
-                  "  localPosition:%lld, startPosition:%lld",
-                    __func__,
-                    (long long)local.mTimeNs[ExtendedTimestamp::LOCATION_SERVER],
+            startUpMs -= (local.mPosition[ExtendedTimestamp::LOCATION_KERNEL] - mLogStartFrames)
+                    * 1e3 / mSampleRate;
+            ALOGV("%s: latencyMs:%lf startUpMs:%lf"
+                    " localTime:%lld startTime:%lld"
+                    " localPosition:%lld startPosition:%lld",
+                    __func__, latencyMs, startUpMs,
+                    (long long)local.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL],
                     (long long)mLogStartTimeNs,
-                    (long long)local.mPosition[ExtendedTimestamp::LOCATION_SERVER],
+                    (long long)local.mPosition[ExtendedTimestamp::LOCATION_KERNEL],
                     (long long)mLogStartFrames);
             mTrackMetrics.logLatencyAndStartup(latencyMs, startUpMs);
         }
+        mLogLatencyMs = latencyMs;
     }
 }
 
diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
index 0c5d1d0..923310c 100644
--- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
@@ -35,6 +35,7 @@
 
 namespace android {
 
+class AudioPolicyMix;
 class DeviceDescriptor;
 class HwAudioOutputDescriptor;
 class SwAudioOutputDescriptor;
@@ -90,11 +91,12 @@
                           product_strategy_t strategy, VolumeSource volumeSource,
                           audio_output_flags_t flags,
                           bool isPreferredDeviceForExclusiveUse,
-                          std::vector<wp<SwAudioOutputDescriptor>> secondaryOutputs) :
+                          std::vector<wp<SwAudioOutputDescriptor>> secondaryOutputs,
+                          wp<AudioPolicyMix> primaryMix) :
         ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId,
                          isPreferredDeviceForExclusiveUse),
         mStream(stream), mStrategy(strategy), mVolumeSource(volumeSource), mFlags(flags),
-        mSecondaryOutputs(std::move(secondaryOutputs)) {}
+        mSecondaryOutputs(std::move(secondaryOutputs)), mPrimaryMix(primaryMix) {}
     ~TrackClientDescriptor() override = default;
 
     using ClientDescriptor::dump;
@@ -108,6 +110,9 @@
         return mSecondaryOutputs;
     };
     VolumeSource volumeSource() const { return mVolumeSource; }
+    const sp<AudioPolicyMix> getPrimaryMix() const {
+        return mPrimaryMix.promote();
+    };
 
     void setActive(bool active) override
     {
@@ -136,7 +141,7 @@
     const VolumeSource mVolumeSource;
     const audio_output_flags_t mFlags;
     const std::vector<wp<SwAudioOutputDescriptor>> mSecondaryOutputs;
-
+    const wp<AudioPolicyMix> mPrimaryMix;
     /**
      * required for duplicating thread, prevent from removing active client from an output
      * involved in a duplication.
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index fc1a59f..b6de4be 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -28,7 +28,7 @@
 
 void AudioPolicyMix::dump(String8 *dst, int spaces, int index) const
 {
-    dst->appendFormat("%*sAudio Policy Mix %d:\n", spaces, "", index + 1);
+    dst->appendFormat("%*sAudio Policy Mix %d (%p):\n", spaces, "", index + 1, this);
     std::string mixTypeLiteral;
     if (!MixTypeConverter::toString(mMixType, mixTypeLiteral)) {
         ALOGE("%s: failed to convert mix type %d", __FUNCTION__, mMixType);
@@ -44,6 +44,9 @@
 
     dst->appendFormat("%*s- device address: %s\n", spaces, "", mDeviceAddress.string());
 
+    dst->appendFormat("%*s- output: %d\n", spaces, "",
+            mOutput == nullptr ? 0 : mOutput->mIoHandle);
+
     int indexCriterion = 0;
     for (const auto &criterion : mCriteria) {
         dst->appendFormat("%*s- Criterion %d: ", spaces + 2, "", indexCriterion++);
diff --git a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
index 95822b9..afc4d01 100644
--- a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
@@ -23,6 +23,7 @@
 #include <TypeConverter.h>
 #include "AudioOutputDescriptor.h"
 #include "AudioPatch.h"
+#include "AudioPolicyMix.h"
 #include "ClientDescriptor.h"
 #include "DeviceDescriptor.h"
 #include "HwModule.h"
@@ -55,6 +56,12 @@
     ClientDescriptor::dump(dst, spaces, index);
     dst->appendFormat("%*s- Stream: %d flags: %08x\n", spaces, "", mStream, mFlags);
     dst->appendFormat("%*s- Refcount: %d\n", spaces, "", mActivityCount);
+    dst->appendFormat("%*s- DAP Primary Mix: %p\n", spaces, "", mPrimaryMix.promote().get());
+    dst->appendFormat("%*s- DAP Secondary Outputs:\n", spaces, "");
+    for (auto desc : mSecondaryOutputs) {
+        dst->appendFormat("%*s  - %d\n", spaces, "",
+                desc.promote() == nullptr ? 0 : desc.promote()->mIoHandle);
+    }
 }
 
 std::string TrackClientDescriptor::toShortString() const
@@ -88,7 +95,7 @@
     TrackClientDescriptor::TrackClientDescriptor(portId, uid, AUDIO_SESSION_NONE, attributes,
         {config.sample_rate, config.channel_mask, config.format}, AUDIO_PORT_HANDLE_NONE,
         stream, strategy, volumeSource, AUDIO_OUTPUT_FLAG_NONE, false,
-        {} /* Sources do not support secondary outputs*/), mSrcDevice(srcDevice)
+        {} /* Sources do not support secondary outputs*/, nullptr), mSrcDevice(srcDevice)
 {
 }
 
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 2a9a4c4..3730c34 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1102,14 +1102,15 @@
     };
     *portId = PolicyAudioPort::getNextUniqueId();
 
+    sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(*output);
     sp<TrackClientDescriptor> clientDesc =
         new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig,
                                   sanitizedRequestedPortId, *stream,
                                   mEngine->getProductStrategyForAttributes(resultAttr),
                                   toVolumeSource(resultAttr),
                                   *flags, isRequestedDeviceForExclusiveUse,
-                                  std::move(weakSecondaryOutputDescs));
-    sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(*output);
+                                  std::move(weakSecondaryOutputDescs),
+                                  outputDesc->mPolicyMix);
     outputDesc->addClient(clientDesc);
 
     ALOGV("%s() returns output %d requestedPortId %d selectedDeviceId %d for port ID %d", __func__,
@@ -2877,7 +2878,7 @@
 {
     ALOGV("registerPolicyMixes() %zu mix(es)", mixes.size());
     status_t res = NO_ERROR;
-
+    bool checkOutputs = false;
     sp<HwModule> rSubmixModule;
     // examine each mix's route type
     for (size_t i = 0; i < mixes.size(); i++) {
@@ -2996,11 +2997,16 @@
                         i, type, address.string());
                 res = INVALID_OPERATION;
                 break;
+            } else {
+                checkOutputs = true;
             }
         }
     }
     if (res != NO_ERROR) {
         unregisterPolicyMixes(mixes);
+    } else if (checkOutputs) {
+        checkForDeviceAndOutputChanges();
+        updateCallAndOutputRouting();
     }
     return res;
 }
@@ -3009,6 +3015,7 @@
 {
     ALOGV("unregisterPolicyMixes() num mixes %zu", mixes.size());
     status_t res = NO_ERROR;
+    bool checkOutputs = false;
     sp<HwModule> rSubmixModule;
     // examine each mix's route type
     for (const auto& mix : mixes) {
@@ -3049,9 +3056,15 @@
             if (mPolicyMixes.unregisterMix(mix) != NO_ERROR) {
                 res = INVALID_OPERATION;
                 continue;
+            } else {
+                checkOutputs = true;
             }
         }
     }
+    if (res == NO_ERROR && checkOutputs) {
+        checkForDeviceAndOutputChanges();
+        updateCallAndOutputRouting();
+    }
     return res;
 }
 
@@ -3820,7 +3833,11 @@
                     ALOGE("%s output not found for id %d", __func__, patch->sources[0].id);
                     return BAD_VALUE;
                 }
-                // Reset handle so that setOutputDevice will force new AF patch to reach the sink
+                if (patchDesc->getHandle() != outputDesc->getPatchHandle()) {
+                    // force SwOutput patch removal as AF counter part patch has already gone.
+                    ALOGV("%s reset patch handle on Output as different from SWBridge", __func__);
+                    removeAudioPatch(outputDesc->getPatchHandle());
+                }
                 outputDesc->setPatchHandle(AUDIO_PATCH_HANDLE_NONE);
                 setOutputDevices(outputDesc,
                                  getNewOutputDevices(outputDesc, true /*fromCache*/),
@@ -5222,32 +5239,38 @@
     SortedVector<audio_io_handle_t> srcOutputs = getOutputsForDevices(oldDevices, mPreviousOutputs);
     SortedVector<audio_io_handle_t> dstOutputs = getOutputsForDevices(newDevices, mOutputs);
 
-    // also take into account external policy-related changes: add all outputs which are
-    // associated with policies in the "before" and "after" output vectors
-    ALOGVV("%s(): policy related outputs", __func__);
-    bool hasDynamicPolicy = false;
-    for (size_t i = 0 ; i < mPreviousOutputs.size() ; i++) {
-        const sp<SwAudioOutputDescriptor> desc = mPreviousOutputs.valueAt(i);
-        if (desc != 0 && desc->mPolicyMix != NULL) {
-            srcOutputs.add(desc->mIoHandle);
-            hasDynamicPolicy = true;
-            ALOGVV(" previous outputs: adding %d", desc->mIoHandle);
+    uint32_t maxLatency = 0;
+    bool invalidate = false;
+    // take into account dynamic audio policies related changes: if a client is now associated
+    // to a different policy mix than at creation time, invalidate corresponding stream
+    for (size_t i = 0; i < mPreviousOutputs.size() && !invalidate; i++) {
+        const sp<SwAudioOutputDescriptor>& desc = mPreviousOutputs.valueAt(i);
+        if (desc->isDuplicated()) {
+            continue;
         }
-    }
-    for (size_t i = 0 ; i < mOutputs.size() ; i++) {
-        const sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
-        if (desc != 0 && desc->mPolicyMix != NULL) {
-            dstOutputs.add(desc->mIoHandle);
-            hasDynamicPolicy = true;
-            ALOGVV(" new outputs: adding %d", desc->mIoHandle);
+        for (const sp<TrackClientDescriptor>& client : desc->getClientIterable()) {
+            if (mEngine->getProductStrategyForAttributes(client->attributes()) != psId) {
+                continue;
+            }
+            sp<AudioPolicyMix> primaryMix;
+            status_t status = mPolicyMixes.getOutputForAttr(client->attributes(), client->uid(),
+                    client->flags(), primaryMix, nullptr);
+            if (status != OK) {
+                continue;
+            }
+            if (client->getPrimaryMix() != primaryMix) {
+                invalidate = true;
+                if (desc->isStrategyActive(psId)) {
+                    maxLatency = desc->latency();
+                }
+                break;
+            }
         }
     }
 
-    if (srcOutputs != dstOutputs) {
+    if (srcOutputs != dstOutputs || invalidate) {
         // get maximum latency of all source outputs to determine the minimum mute time guaranteeing
         // audio from invalidated tracks will be rendered when unmuting
-        uint32_t maxLatency = 0;
-        bool invalidate = hasDynamicPolicy;
         for (audio_io_handle_t srcOut : srcOutputs) {
             sp<SwAudioOutputDescriptor> desc = mPreviousOutputs.valueFor(srcOut);
             if (desc == nullptr) continue;
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index c9d2c68..bac9430 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -467,10 +467,21 @@
             logDeviceRemoved(idCombo,
                     String8::format("Device status changed to %d", newStatus));
         }
-
+        // Avoid calling getSystemCameraKind() with mStatusListenerLock held (b/141756275)
+        SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
+        if (getSystemCameraKind(id, &deviceKind) != OK) {
+            ALOGE("%s: Invalid camera id %s, skipping", __FUNCTION__, id.string());
+            return;
+        }
         String16 id16(id), physicalId16(physicalId);
         Mutex::Autolock lock(mStatusListenerLock);
         for (auto& listener : mListenerList) {
+            if (shouldSkipStatusUpdates(deviceKind, listener->isVendorListener(),
+                    listener->getListenerPid(), listener->getListenerUid())) {
+                ALOGV("Skipping discovery callback for system-only camera device %s",
+                        id.c_str());
+                continue;
+            }
             listener->getListener()->onPhysicalCameraStatusChanged(mapToInterface(newStatus),
                     id16, physicalId16);
         }
@@ -3757,13 +3768,13 @@
 
             Mutex::Autolock lock(mStatusListenerLock);
 
-            notifyPhysicalCameraStatusLocked(mapToInterface(status), cameraId);
+            notifyPhysicalCameraStatusLocked(mapToInterface(status), cameraId, deviceKind);
 
             for (auto& listener : mListenerList) {
                 bool isVendorListener = listener->isVendorListener();
                 if (shouldSkipStatusUpdates(deviceKind, isVendorListener,
                         listener->getListenerPid(), listener->getListenerUid()) ||
-                    (isVendorListener && !supportsHAL3)) {
+                        (isVendorListener && !supportsHAL3)) {
                     ALOGV("Skipping discovery callback for system-only camera/HAL1 device %s",
                             cameraId.c_str());
                     continue;
@@ -3875,7 +3886,8 @@
     return OK;
 }
 
-void CameraService::notifyPhysicalCameraStatusLocked(int32_t status, const String8& cameraId) {
+void CameraService::notifyPhysicalCameraStatusLocked(int32_t status, const String8& cameraId,
+        SystemCameraKind deviceKind) {
     Mutex::Autolock lock(mCameraStatesLock);
     for (const auto& state : mCameraStates) {
         std::vector<std::string> physicalCameraIds;
@@ -3891,6 +3903,12 @@
 
         String16 id16(state.first), physicalId16(cameraId);
         for (auto& listener : mListenerList) {
+            if (shouldSkipStatusUpdates(deviceKind, listener->isVendorListener(),
+                    listener->getListenerPid(), listener->getListenerUid())) {
+                ALOGV("Skipping discovery callback for system-only camera device %s",
+                        cameraId.c_str());
+                continue;
+            }
             listener->getListener()->onPhysicalCameraStatusChanged(status,
                     id16, physicalId16);
         }
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 18cf77a..4321201 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -1005,7 +1005,8 @@
             hardware::camera::common::V1_0::TorchModeStatus status);
 
     // notify physical camera status when the physical camera is public.
-    void notifyPhysicalCameraStatusLocked(int32_t status, const String8& cameraId);
+    void notifyPhysicalCameraStatusLocked(int32_t status, const String8& cameraId,
+            SystemCameraKind deviceKind);
 
     // IBinder::DeathRecipient implementation
     virtual void        binderDied(const wp<IBinder> &who);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index b00a2d9..01e70d8 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -756,9 +756,10 @@
         for (size_t i = 0; i < mInFlightMap.size(); i++) {
             InFlightRequest r = mInFlightMap.valueAt(i);
             lines.appendFormat("      Frame %d |  Timestamp: %" PRId64 ", metadata"
-                    " arrived: %s, buffers left: %d\n", mInFlightMap.keyAt(i),
+                    " arrived: %s, buffers left: %d, buffers returned with STATUS_ERROR: %d, "
+                    " buffers notified with error: %d\n", mInFlightMap.keyAt(i),
                     r.shutterTimestamp, r.haveResultMetadata ? "true" : "false",
-                    r.numBuffersLeft);
+                    r.numBuffersLeft, r.numErrorBuffersReturned, r.numErrorBuffersNotified);
         }
     }
     write(fd, lines.string(), lines.size());
@@ -1043,8 +1044,9 @@
     }
     CaptureOutputStates states {
         mId,
-        mInFlightLock, mInFlightMap,
-        mOutputLock,  mResultQueue, mResultSignal,
+        mInFlightLock, mLastCompletedRegularFrameNumber,
+        mLastCompletedReprocessFrameNumber, mLastCompletedZslFrameNumber,
+        mInFlightMap, mOutputLock,  mResultQueue, mResultSignal,
         mNextShutterFrameNumber,
         mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
         mNextResultFrameNumber,
@@ -1100,8 +1102,9 @@
 
     CaptureOutputStates states {
         mId,
-        mInFlightLock, mInFlightMap,
-        mOutputLock,  mResultQueue, mResultSignal,
+        mInFlightLock, mLastCompletedRegularFrameNumber,
+        mLastCompletedReprocessFrameNumber, mLastCompletedZslFrameNumber,
+        mInFlightMap, mOutputLock,  mResultQueue, mResultSignal,
         mNextShutterFrameNumber,
         mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
         mNextResultFrameNumber,
@@ -1139,8 +1142,9 @@
 
     CaptureOutputStates states {
         mId,
-        mInFlightLock, mInFlightMap,
-        mOutputLock,  mResultQueue, mResultSignal,
+        mInFlightLock, mLastCompletedRegularFrameNumber,
+        mLastCompletedReprocessFrameNumber, mLastCompletedZslFrameNumber,
+        mInFlightMap, mOutputLock,  mResultQueue, mResultSignal,
         mNextShutterFrameNumber,
         mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
         mNextResultFrameNumber,
@@ -1777,13 +1781,6 @@
     mStatusChanged.broadcast();
 }
 
-void Camera3Device::pauseStateNotify(bool enable) {
-    Mutex::Autolock il(mInterfaceLock);
-    Mutex::Autolock l(mLock);
-
-    mPauseStateNotify = enable;
-}
-
 // Pause to reconfigure
 status_t Camera3Device::internalPauseAndWaitLocked(nsecs_t maxExpectedDuration) {
     if (mRequestThread.get() != nullptr) {
@@ -2359,7 +2356,7 @@
     return false;
 }
 
-bool Camera3Device::reconfigureCamera(const CameraMetadata& sessionParams) {
+bool Camera3Device::reconfigureCamera(const CameraMetadata& sessionParams, int clientStatusId) {
     ATRACE_CALL();
     bool ret = false;
 
@@ -2373,7 +2370,16 @@
         return true;
     }
 
-    auto rc = internalPauseAndWaitLocked(maxExpectedDuration);
+    status_t rc = NO_ERROR;
+    bool markClientActive = false;
+    if (mStatus == STATUS_ACTIVE) {
+        markClientActive = true;
+        mPauseStateNotify = true;
+        mStatusTracker->markComponentIdle(clientStatusId, Fence::NO_FENCE);
+
+        rc = internalPauseAndWaitLocked(maxExpectedDuration);
+    }
+
     if (rc == NO_ERROR) {
         mNeedConfig = true;
         rc = configureStreamsLocked(mOperatingMode, sessionParams, /*notifyRequestThread*/ false);
@@ -2401,6 +2407,10 @@
         ALOGE("%s: Failed to pause streaming: %d", __FUNCTION__, rc);
     }
 
+    if (markClientActive) {
+        mStatusTracker->markComponentActive(clientStatusId);
+    }
+
     return ret;
 }
 
@@ -4277,22 +4287,11 @@
         }
 
         if (res == OK) {
-            sp<StatusTracker> statusTracker = mStatusTracker.promote();
-            if (statusTracker != 0) {
-                sp<Camera3Device> parent = mParent.promote();
-                if (parent != nullptr) {
-                    parent->pauseStateNotify(true);
-                }
-
-                statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);
-
-                if (parent != nullptr) {
-                    mReconfigured |= parent->reconfigureCamera(mLatestSessionParams);
-                }
-
-                statusTracker->markComponentActive(mStatusId);
-                setPaused(false);
+            sp<Camera3Device> parent = mParent.promote();
+            if (parent != nullptr) {
+                mReconfigured |= parent->reconfigureCamera(mLatestSessionParams, mStatusId);
             }
+            setPaused(false);
 
             if (mNextRequests[0].captureRequest->mInputStream != nullptr) {
                 mNextRequests[0].captureRequest->mInputStream->restoreConfiguredState();
@@ -5906,11 +5905,13 @@
     //       though technically no other thread should be talking to Camera3Device at this point
     Camera3OfflineStates offlineStates(
             mTagMonitor, mVendorTagId, mUseHalBufManager, mNeedFixupMonochromeTags,
-            mUsePartialResult, mNumPartialResults, mNextResultFrameNumber,
-            mNextReprocessResultFrameNumber, mNextZslStillResultFrameNumber,
-            mNextShutterFrameNumber, mNextReprocessShutterFrameNumber,
-            mNextZslStillShutterFrameNumber, mDeviceInfo, mPhysicalDeviceInfoMap,
-            mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers);
+            mUsePartialResult, mNumPartialResults, mLastCompletedRegularFrameNumber,
+            mLastCompletedReprocessFrameNumber, mLastCompletedZslFrameNumber,
+            mNextResultFrameNumber, mNextReprocessResultFrameNumber,
+            mNextZslStillResultFrameNumber, mNextShutterFrameNumber,
+            mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
+            mDeviceInfo, mPhysicalDeviceInfoMap, mDistortionMappers,
+            mZoomRatioMappers, mRotateAndCropMappers);
 
     *session = new Camera3OfflineSession(mId, inputStream, offlineStreamSet,
             std::move(bufferRecords), offlineReqs, offlineStates, offlineSession);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index c059f55..408f1f9 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -638,17 +638,10 @@
                                             const SurfaceMap &surfaceMap);
 
     /**
-     * Pause state updates to the client application.  Needed to mask out idle/active
-     * transitions during internal reconfigure
-     */
-    void pauseStateNotify(bool enable);
-
-    /**
      * Internally re-configure camera device using new session parameters.
-     * This will get triggered by the request thread. Be sure to call
-     * pauseStateNotify(true) before going idle in the requesting location.
+     * This will get triggered by the request thread.
      */
-    bool reconfigureCamera(const CameraMetadata& sessionParams);
+    bool reconfigureCamera(const CameraMetadata& sessionParams, int clientStatusId);
 
     /**
      * Return true in case of any output or input abandoned streams,
@@ -1017,6 +1010,9 @@
     std::mutex                    mInFlightLock;
     camera3::InFlightRequestMap   mInFlightMap;
     nsecs_t                       mExpectedInflightDuration = 0;
+    int64_t                       mLastCompletedRegularFrameNumber = -1;
+    int64_t                       mLastCompletedReprocessFrameNumber = -1;
+    int64_t                       mLastCompletedZslFrameNumber = -1;
     // End of mInFlightLock protection scope
 
     int mInFlightStatusId; // const after initialize
diff --git a/services/camera/libcameraservice/device3/Camera3OfflineSession.cpp b/services/camera/libcameraservice/device3/Camera3OfflineSession.cpp
index 5942868..95f9633 100644
--- a/services/camera/libcameraservice/device3/Camera3OfflineSession.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OfflineSession.cpp
@@ -61,6 +61,9 @@
         mNeedFixupMonochromeTags(offlineStates.mNeedFixupMonochromeTags),
         mUsePartialResult(offlineStates.mUsePartialResult),
         mNumPartialResults(offlineStates.mNumPartialResults),
+        mLastCompletedRegularFrameNumber(offlineStates.mLastCompletedRegularFrameNumber),
+        mLastCompletedReprocessFrameNumber(offlineStates.mLastCompletedReprocessFrameNumber),
+        mLastCompletedZslFrameNumber(offlineStates.mLastCompletedZslFrameNumber),
         mNextResultFrameNumber(offlineStates.mNextResultFrameNumber),
         mNextReprocessResultFrameNumber(offlineStates.mNextReprocessResultFrameNumber),
         mNextZslStillResultFrameNumber(offlineStates.mNextZslStillResultFrameNumber),
@@ -247,8 +250,9 @@
 
     CaptureOutputStates states {
         mId,
-        mOfflineReqsLock, mOfflineReqs,
-        mOutputLock, mResultQueue, mResultSignal,
+        mOfflineReqsLock, mLastCompletedRegularFrameNumber,
+        mLastCompletedReprocessFrameNumber, mLastCompletedZslFrameNumber,
+        mOfflineReqs, mOutputLock, mResultQueue, mResultSignal,
         mNextShutterFrameNumber,
         mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
         mNextResultFrameNumber,
@@ -285,8 +289,9 @@
 
     CaptureOutputStates states {
         mId,
-        mOfflineReqsLock, mOfflineReqs,
-        mOutputLock, mResultQueue, mResultSignal,
+        mOfflineReqsLock, mLastCompletedRegularFrameNumber,
+        mLastCompletedReprocessFrameNumber, mLastCompletedZslFrameNumber,
+        mOfflineReqs, mOutputLock, mResultQueue, mResultSignal,
         mNextShutterFrameNumber,
         mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
         mNextResultFrameNumber,
@@ -318,8 +323,9 @@
 
     CaptureOutputStates states {
         mId,
-        mOfflineReqsLock, mOfflineReqs,
-        mOutputLock, mResultQueue, mResultSignal,
+        mOfflineReqsLock, mLastCompletedRegularFrameNumber,
+        mLastCompletedReprocessFrameNumber, mLastCompletedZslFrameNumber,
+        mOfflineReqs, mOutputLock, mResultQueue, mResultSignal,
         mNextShutterFrameNumber,
         mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
         mNextResultFrameNumber,
diff --git a/services/camera/libcameraservice/device3/Camera3OfflineSession.h b/services/camera/libcameraservice/device3/Camera3OfflineSession.h
index 208f70d..c4c7a85 100644
--- a/services/camera/libcameraservice/device3/Camera3OfflineSession.h
+++ b/services/camera/libcameraservice/device3/Camera3OfflineSession.h
@@ -57,10 +57,11 @@
             const TagMonitor& tagMonitor, const metadata_vendor_id_t vendorTagId,
             const bool useHalBufManager, const bool needFixupMonochromeTags,
             const bool usePartialResult, const uint32_t numPartialResults,
-            const uint32_t nextResultFN, const uint32_t nextReprocResultFN,
-            const uint32_t nextZslResultFN,  const uint32_t nextShutterFN,
-            const uint32_t nextReprocShutterFN, const uint32_t nextZslShutterFN,
-            const CameraMetadata& deviceInfo,
+            const int64_t lastCompletedRegularFN, const int64_t lastCompletedReprocessFN,
+            const int64_t lastCompletedZslFN, const uint32_t nextResultFN,
+            const uint32_t nextReprocResultFN, const uint32_t nextZslResultFN,
+            const uint32_t nextShutterFN, const uint32_t nextReprocShutterFN,
+            const uint32_t nextZslShutterFN, const CameraMetadata& deviceInfo,
             const std::unordered_map<std::string, CameraMetadata>& physicalDeviceInfoMap,
             const std::unordered_map<std::string, camera3::DistortionMapper>& distortionMappers,
             const std::unordered_map<std::string, camera3::ZoomRatioMapper>& zoomRatioMappers,
@@ -69,6 +70,9 @@
             mTagMonitor(tagMonitor), mVendorTagId(vendorTagId),
             mUseHalBufManager(useHalBufManager), mNeedFixupMonochromeTags(needFixupMonochromeTags),
             mUsePartialResult(usePartialResult), mNumPartialResults(numPartialResults),
+            mLastCompletedRegularFrameNumber(lastCompletedRegularFN),
+            mLastCompletedReprocessFrameNumber(lastCompletedReprocessFN),
+            mLastCompletedZslFrameNumber(lastCompletedZslFN),
             mNextResultFrameNumber(nextResultFN),
             mNextReprocessResultFrameNumber(nextReprocResultFN),
             mNextZslStillResultFrameNumber(nextZslResultFN),
@@ -90,6 +94,15 @@
     const bool mUsePartialResult;
     const uint32_t mNumPartialResults;
 
+    // The last completed (buffers, result metadata, and error notify) regular
+    // request frame number
+    const int64_t mLastCompletedRegularFrameNumber;
+    // The last completed (buffers, result metadata, and error notify) reprocess
+    // request frame number
+    const int64_t mLastCompletedReprocessFrameNumber;
+    // The last completed (buffers, result metadata, and error notify) zsl
+    // request frame number
+    const int64_t mLastCompletedZslFrameNumber;
     // the minimal frame number of the next non-reprocess result
     const uint32_t mNextResultFrameNumber;
     // the minimal frame number of the next reprocess result
@@ -214,6 +227,12 @@
     std::mutex mOutputLock;
     std::list<CaptureResult> mResultQueue;
     std::condition_variable mResultSignal;
+    // the last completed frame number of regular requests
+    int64_t mLastCompletedRegularFrameNumber;
+    // the last completed frame number of reprocess requests
+    int64_t mLastCompletedReprocessFrameNumber;
+    // the last completed frame number of ZSL still capture requests
+    int64_t mLastCompletedZslFrameNumber;
     // the minimal frame number of the next non-reprocess result
     uint32_t mNextResultFrameNumber;
     // the minimal frame number of the next reprocess result
diff --git a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
index 603f516..4994393 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
@@ -395,7 +395,7 @@
 
 void removeInFlightRequestIfReadyLocked(CaptureOutputStates& states, int idx) {
     InFlightRequestMap& inflightMap = states.inflightMap;
-    const InFlightRequest &request = inflightMap.valueAt(idx);
+    InFlightRequest &request = inflightMap.editValueAt(idx);
     const uint32_t frameNumber = inflightMap.keyAt(idx);
 
     nsecs_t sensorTimestamp = request.sensorTimestamp;
@@ -405,8 +405,8 @@
     // In the case of a successful request:
     //      all input and output buffers, all result metadata, shutter callback
     //      arrived.
-    // In the case of a unsuccessful request:
-    //      all input and output buffers arrived.
+    // In the case of an unsuccessful request:
+    //      all input and output buffers, as well as error notifications, arrived.
     if (request.numBuffersLeft == 0 &&
             (request.skipResultMetadata ||
             (request.haveResultMetadata && shutterTimestamp != 0))) {
@@ -430,14 +430,26 @@
         assert(request.requestStatus != OK ||
                request.pendingOutputBuffers.size() == 0);
 
-        returnOutputBuffers(
+        size_t bufferErrorCnt = returnOutputBuffers(
             states.useHalBufManager, states.listener,
             request.pendingOutputBuffers.array(),
             request.pendingOutputBuffers.size(), 0, /*timestampIncreasing*/true,
             request.outputSurfaces, request.resultExtras);
 
-        removeInFlightMapEntryLocked(states, idx);
-        ALOGVV("%s: removed frame %d from InFlightMap", __FUNCTION__, frameNumber);
+        request.numErrorBuffersReturned += bufferErrorCnt;
+        if (request.numErrorBuffersReturned == request.numErrorBuffersNotified) {
+            removeInFlightMapEntryLocked(states, idx);
+            ALOGVV("%s: removed frame %d from InFlightMap", __FUNCTION__, frameNumber);
+
+            // Note down the just completed frame number
+            if (request.hasInputBuffer) {
+                states.lastCompletedReprocessFrameNumber = frameNumber;
+            } else if (request.zslCapture) {
+                states.lastCompletedZslFrameNumber = frameNumber;
+            } else {
+                states.lastCompletedRegularFrameNumber = frameNumber;
+            }
+        }
     }
 
     states.inflightIntf.checkInflightMapLengthLocked();
@@ -487,10 +499,10 @@
         InFlightRequest &request = states.inflightMap.editValueAt(idx);
         ALOGVV("%s: got InFlightRequest requestId = %" PRId32
                 ", frameNumber = %" PRId64 ", burstId = %" PRId32
-                ", partialResultCount = %d, hasCallback = %d",
+                ", partialResultCount = %d, hasCallback = %d, num_output_buffers %d",
                 __FUNCTION__, request.resultExtras.requestId,
                 request.resultExtras.frameNumber, request.resultExtras.burstId,
-                result->partial_result, request.hasCallback);
+                result->partial_result, request.hasCallback, result->num_output_buffers);
         // Always update the partial count to the latest one if it's not 0
         // (buffers only). When framework aggregates adjacent partial results
         // into one, the latest partial count will be used.
@@ -589,10 +601,11 @@
                 result->num_output_buffers);
         } else {
             bool timestampIncreasing = !(request.zslCapture || request.hasInputBuffer);
-            returnOutputBuffers(states.useHalBufManager, states.listener,
+            auto numErrorBuffers = returnOutputBuffers(states.useHalBufManager, states.listener,
                 result->output_buffers, result->num_output_buffers,
                 shutterTimestamp, timestampIncreasing,
                 request.outputSurfaces, request.resultExtras);
+            request.numErrorBuffersReturned += numErrorBuffers;
         }
 
         if (result->result != NULL && !isPartialResult) {
@@ -630,6 +643,7 @@
                       "  its stream:%s (%d)",  __FUNCTION__,
                       frameNumber, strerror(-res), res);
             }
+
         } else {
             ALOGW("%s: Input buffer should be NULL if there is no input"
                     " buffer sent in the request, skipping input buffer return.",
@@ -785,7 +799,7 @@
     processCaptureResult(states, &r);
 }
 
-void returnOutputBuffers(
+size_t returnOutputBuffers(
         bool useHalBufManager,
         sp<NotificationListener> listener,
         const camera3_stream_buffer_t *outputBuffers, size_t numBuffers,
@@ -793,6 +807,7 @@
         const SurfaceMap& outputSurfaces,
         const CaptureResultExtras &inResultExtras) {
 
+    size_t numErrorBuffers = 0;
     for (size_t i = 0; i < numBuffers; i++)
     {
         if (outputBuffers[i].buffer == nullptr) {
@@ -801,6 +816,10 @@
                 // has not got a output buffer handle filled yet. This is though illegal if HAL
                 // buffer management API is not being used.
                 ALOGE("%s: cannot return a null buffer!", __FUNCTION__);
+            } else {
+                if (outputBuffers[i].status == CAMERA3_BUFFER_STATUS_ERROR) {
+                    numErrorBuffers++;
+                }
             }
             continue;
         }
@@ -844,8 +863,13 @@
                         hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER,
                         extras);
             }
+        } else {
+            if (outputBuffers[i].status == CAMERA3_BUFFER_STATUS_ERROR) {
+               numErrorBuffers++;
+            }
         }
     }
+    return numErrorBuffers;
 }
 
 void notifyShutter(CaptureOutputStates& states, const camera3_shutter_msg_t &msg) {
@@ -899,6 +923,12 @@
                     msg.frame_number, r.resultExtras.requestId, msg.timestamp);
                 // Call listener, if any
                 if (states.listener != nullptr) {
+                    r.resultExtras.lastCompletedRegularFrameNumber =
+                            states.lastCompletedRegularFrameNumber;
+                    r.resultExtras.lastCompletedReprocessFrameNumber =
+                            states.lastCompletedReprocessFrameNumber;
+                    r.resultExtras.lastCompletedZslFrameNumber =
+                            states.lastCompletedZslFrameNumber;
                     states.listener->notifyShutter(r.resultExtras, msg.timestamp);
                 }
                 // send pending result and buffers
@@ -909,11 +939,12 @@
                     r.rotateAndCropAuto, r.cameraIdsWithZoom, r.physicalMetadatas);
             }
             bool timestampIncreasing = !(r.zslCapture || r.hasInputBuffer);
-            returnOutputBuffers(
+            size_t bufferErrorCnt = returnOutputBuffers(
                     states.useHalBufManager, states.listener,
                     r.pendingOutputBuffers.array(),
                     r.pendingOutputBuffers.size(), r.shutterTimestamp, timestampIncreasing,
                     r.outputSurfaces, r.resultExtras);
+            r.numErrorBuffersReturned += bufferErrorCnt;
             r.pendingOutputBuffers.clear();
 
             removeInFlightRequestIfReadyLocked(states, idx);
@@ -976,7 +1007,7 @@
                     InFlightRequest &r = states.inflightMap.editValueAt(idx);
                     r.requestStatus = msg.error_code;
                     resultExtras = r.resultExtras;
-                    bool logicalDeviceResultError = false;
+                    bool physicalDeviceResultError = false;
                     if (hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT ==
                             errorCode) {
                         if (physicalCameraId.size() > 0) {
@@ -990,23 +1021,39 @@
                             }
                             r.physicalCameraIds.erase(iter);
                             resultExtras.errorPhysicalCameraId = physicalCameraId;
-                        } else {
-                            logicalDeviceResultError = true;
+                            physicalDeviceResultError = true;
                         }
                     }
 
-                    if (logicalDeviceResultError
-                            ||  hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST ==
-                            errorCode) {
-                        r.skipResultMetadata = true;
-                    }
-                    if (logicalDeviceResultError) {
-                        // In case of missing result check whether the buffers
-                        // returned. If they returned, then remove inflight
-                        // request.
-                        // TODO: should we call this for ERROR_CAMERA_REQUEST as well?
-                        //       otherwise we are depending on HAL to send the buffers back after
-                        //       calling notifyError. Not sure if that's in the spec.
+                    if (!physicalDeviceResultError) {
+                        if (hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT
+                                == errorCode) {
+                            r.skipResultMetadata = true;
+                        } else if (hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER
+                                == errorCode) {
+                            r.numErrorBuffersNotified ++;
+                        } else {
+                            // errorCode is ERROR_CAMERA_REQUEST
+                            if (!r.skipResultMetadata) {
+                                // In case HAL calls multiples ERROR_REQUEST
+                                // callback, only count the pending buffer
+                                // notify error counter once. And also handle
+                                // the case where ERROR_BUFFERs are sent before
+                                // ERROR_REQUEST, even though it's not allowed
+                                // by the HAL API.
+                                if (r.numErrorBuffersNotified != 0) {
+                                    ALOGW("Camera %s: %s: HAL should not notify ERROR_REQUEST"
+                                            " and ERROR_BUFFER for the same request",
+                                            states.cameraId.string(), __FUNCTION__);
+                                }
+                                r.numErrorBuffersNotified =
+                                        r.numOutputBuffers - r.numErrorBuffersNotified;
+                                r.skipResultMetadata = true;
+                            }
+                        }
+
+                        // Check whether the buffers returned. If they returned,
+                        // remove inflight request.
                         removeInFlightRequestIfReadyLocked(states, idx);
                     }
                 } else {
@@ -1332,14 +1379,23 @@
     { // First return buffers cached in mInFlightMap
         std::lock_guard<std::mutex> l(states.inflightLock);
         for (size_t idx = 0; idx < states.inflightMap.size(); idx++) {
-            const InFlightRequest &request = states.inflightMap.valueAt(idx);
-            returnOutputBuffers(
+            InFlightRequest &request = states.inflightMap.editValueAt(idx);
+            size_t bufferErrorCnt = returnOutputBuffers(
                 states.useHalBufManager, states.listener,
                 request.pendingOutputBuffers.array(),
                 request.pendingOutputBuffers.size(), 0,
                 /*timestampIncreasing*/true, request.outputSurfaces,
                 request.resultExtras);
+            request.numErrorBuffersReturned += bufferErrorCnt;
+            ALOGW("%s: Frame %d |  Timestamp: %" PRId64 ", metadata"
+                    " arrived: %s, buffers left: %d, buffers returned with STATUS_ERROR: %d, "
+                    " buffers notified with error: %d\n", __FUNCTION__,
+                    states.inflightMap.keyAt(idx), request.shutterTimestamp,
+                    request.haveResultMetadata ? "true" : "false",
+                    request.numBuffersLeft, request.numErrorBuffersReturned,
+                    request.numErrorBuffersNotified);
         }
+
         states.inflightMap.clear();
         states.inflightIntf.onInflightMapFlushedLocked();
     }
diff --git a/services/camera/libcameraservice/device3/Camera3OutputUtils.h b/services/camera/libcameraservice/device3/Camera3OutputUtils.h
index fbb47f8..53f78f2 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputUtils.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputUtils.h
@@ -45,7 +45,8 @@
      * Helper methods shared between Camera3Device/Camera3OfflineSession for HAL callbacks
      */
     // helper function to return the output buffers to output streams.
-    void returnOutputBuffers(
+    // returns the number of STATUS_ERROR buffers
+    size_t returnOutputBuffers(
             bool useHalBufManager,
             sp<NotificationListener> listener, // Only needed when outputSurfaces is not empty
             const camera3_stream_buffer_t *outputBuffers,
@@ -60,6 +61,9 @@
     struct CaptureOutputStates {
         const String8& cameraId;
         std::mutex& inflightLock;
+        int64_t& lastCompletedRegularFrameNumber;
+        int64_t& lastCompletedZslFrameNumber;
+        int64_t& lastCompletedReprocessFrameNumber;
         InFlightRequestMap& inflightMap; // end of inflightLock scope
         std::mutex& outputLock;
         std::list<CaptureResult>& resultQueue;
diff --git a/services/camera/libcameraservice/device3/InFlightRequest.h b/services/camera/libcameraservice/device3/InFlightRequest.h
index 424043b..3cb8324 100644
--- a/services/camera/libcameraservice/device3/InFlightRequest.h
+++ b/services/camera/libcameraservice/device3/InFlightRequest.h
@@ -43,6 +43,19 @@
     // Decremented by calls to process_capture_result with valid output
     // and input buffers
     int     numBuffersLeft;
+    // Total number of output buffers for this request
+    int     numOutputBuffers;
+
+    // The inflight request is considered complete if all buffers are returned
+    // and numErrorBuffersReturned == numErrorBuffersNotified.
+
+    // The number of buffers returned with STATUS_ERROR;
+    int numErrorBuffersReturned;
+    // The number of buffers that are notified as error:
+    //   +1 for each notifyError(ERROR_BUFFER), and
+    //   +numOutputBuffers for notifyError(ERROR_REQUEST)
+    int numErrorBuffersNotified;
+
     CaptureResultExtras resultExtras;
     // If this request has any input buffer
     bool hasInputBuffer;
@@ -110,6 +123,9 @@
             requestStatus(OK),
             haveResultMetadata(false),
             numBuffersLeft(0),
+            numOutputBuffers(0),
+            numErrorBuffersReturned(0),
+            numErrorBuffersNotified(0),
             hasInputBuffer(false),
             hasCallback(true),
             maxExpectedDuration(kDefaultExpectedDuration),
@@ -129,6 +145,9 @@
             requestStatus(OK),
             haveResultMetadata(false),
             numBuffersLeft(numBuffers),
+            numOutputBuffers(hasInput ? numBuffers-1 : numBuffers),
+            numErrorBuffersReturned(0),
+            numErrorBuffersNotified(0),
             resultExtras(extras),
             hasInputBuffer(hasInput),
             hasCallback(hasAppCallback),
diff --git a/services/mediacodec/Android.bp b/services/mediacodec/Android.bp
index 4bf103c..f4c1924 100644
--- a/services/mediacodec/Android.bp
+++ b/services/mediacodec/Android.bp
@@ -1,6 +1,7 @@
 cc_binary {
     name: "mediaswcodec",
     vendor_available: true,
+    min_sdk_version: "29",
 
     srcs: [
         "main_swcodecservice.cpp",
diff --git a/services/mediametrics/Android.bp b/services/mediametrics/Android.bp
index c87fbd9..f819f1b 100644
--- a/services/mediametrics/Android.bp
+++ b/services/mediametrics/Android.bp
@@ -1,8 +1,94 @@
 // Media Statistics service
 //
 
+tidy_errors = [
+    // https://clang.llvm.org/extra/clang-tidy/checks/list.html
+    // For many categories, the checks are too many to specify individually.
+    // Feel free to disable as needed - as warnings are generally ignored,
+    // we treat warnings as errors.
+    "android-*",
+    "bugprone-*",
+    "cert-*",
+    "clang-analyzer-security*",
+    "google-*",
+    "misc-*",
+    //"modernize-*",  // explicitly list the modernize as they can be subjective.
+    "modernize-avoid-bind",
+    //"modernize-avoid-c-arrays", // std::array<> can be verbose
+    "modernize-concat-nested-namespaces",
+    //"modernize-deprecated-headers", // C headers still ok even if there is C++ equivalent.
+    "modernize-deprecated-ios-base-aliases",
+    "modernize-loop-convert",
+    "modernize-make-shared",
+    "modernize-make-unique",
+    "modernize-pass-by-value",
+    "modernize-raw-string-literal",
+    "modernize-redundant-void-arg",
+    "modernize-replace-auto-ptr",
+    "modernize-replace-random-shuffle",
+    "modernize-return-braced-init-list",
+    "modernize-shrink-to-fit",
+    "modernize-unary-static-assert",
+    "modernize-use-auto",  // debatable - auto can obscure type
+    "modernize-use-bool-literals",
+    "modernize-use-default-member-init",
+    "modernize-use-emplace",
+    "modernize-use-equals-default",
+    "modernize-use-equals-delete",
+    "modernize-use-nodiscard",
+    "modernize-use-noexcept",
+    "modernize-use-nullptr",
+    "modernize-use-override",
+    //"modernize-use-trailing-return-type", // not necessarily more readable
+    "modernize-use-transparent-functors",
+    "modernize-use-uncaught-exceptions",
+    "modernize-use-using",
+    "performance-*",
+
+    // Remove some pedantic stylistic requirements.
+    "-google-readability-casting", // C++ casts not always necessary and may be verbose
+    "-google-readability-todo",    // do not require TODO(info)
+]
+
+cc_defaults {
+    name: "mediametrics_flags_defaults",
+    // https://clang.llvm.org/docs/UsersManual.html#command-line-options
+    // https://clang.llvm.org/docs/DiagnosticsReference.html
+    cflags: [
+        "-Wall",
+        "-Wdeprecated",
+        "-Werror",
+        "-Werror=implicit-fallthrough",
+        "-Werror=sometimes-uninitialized",
+        "-Werror=conditional-uninitialized",
+        "-Wextra",
+        "-Wredundant-decls",
+        "-Wshadow",
+        "-Wstrict-aliasing",
+        "-fstrict-aliasing",
+        "-Wthread-safety",
+        //"-Wthread-safety-negative", // experimental - looks broken in R.
+        "-Wunreachable-code",
+        "-Wunreachable-code-break",
+        "-Wunreachable-code-return",
+        "-Wunused",
+        "-Wused-but-marked-unused",
+    ],
+    // https://clang.llvm.org/extra/clang-tidy/
+    tidy: true,
+    tidy_checks: tidy_errors,
+    tidy_checks_as_errors: tidy_errors,
+    tidy_flags: [
+      "-format-style='file'",
+      "--header-filter='frameworks/av/services/mediametrics/'",
+    ],
+}
+
 cc_binary {
     name: "mediametrics",
+    defaults: [
+        "mediametrics_flags_defaults",
+    ],
 
     srcs: [
         "main_mediametrics.cpp",
@@ -22,21 +108,18 @@
     init_rc: [
         "mediametrics.rc",
     ],
-
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wextra",
-        "-Wthread-safety",
-    ],
 }
 
 cc_library_shared {
     name: "libmediametricsservice",
+    defaults: [
+        "mediametrics_flags_defaults",
+    ],
 
     srcs: [
         "AudioAnalytics.cpp",
         "AudioPowerUsage.cpp",
+        "AudioTypes.cpp",
         "iface_statsd.cpp",
         "MediaMetricsService.cpp",
         "statsd_audiopolicy.cpp",
@@ -48,6 +131,7 @@
         "statsd_extractor.cpp",
         "statsd_nuplayer.cpp",
         "statsd_recorder.cpp",
+        "StringUtils.cpp"
     ],
 
     proto: {
@@ -55,9 +139,11 @@
     },
 
     shared_libs: [
+        "libbase", // android logging
         "libbinder",
         "libcutils",
         "liblog",
+        "libmedia_helper",
         "libmediametrics",
         "libmediautils",
         "libmemunreachable",
@@ -73,11 +159,4 @@
     include_dirs: [
         "system/media/audio_utils/include",
     ],
-
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wextra",
-        "-Wthread-safety",
-    ],
 }
diff --git a/services/mediametrics/AudioAnalytics.cpp b/services/mediametrics/AudioAnalytics.cpp
index 6138d32..800f099 100644
--- a/services/mediametrics/AudioAnalytics.cpp
+++ b/services/mediametrics/AudioAnalytics.cpp
@@ -16,20 +16,45 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "AudioAnalytics"
+#include <android-base/logging.h>
 #include <utils/Log.h>
 
 #include "AudioAnalytics.h"
-#include "MediaMetricsService.h"  // package info
+
 #include <audio_utils/clock.h>    // clock conversions
+#include <cutils/properties.h>
 #include <statslog.h>             // statsd
 
+#include "AudioTypes.h"           // string to int conversions
+#include "MediaMetricsService.h"  // package info
+#include "StringUtils.h"
+
+#define PROP_AUDIO_ANALYTICS_CLOUD_ENABLED "persist.audio.analytics.cloud.enabled"
+
 // Enable for testing of delivery to statsd
-// #define STATSD
+//#define STATSD
+
+// Transmit to statsd in integer or strings
+//#define USE_INT
+
+#ifdef USE_INT
+using short_enum_type_t = int32_t;
+using long_enum_type_t = int64_t;
+#define ENUM_EXTRACT(x) (x)
+#else
+using short_enum_type_t = std::string;
+using long_enum_type_t = std::string;
+#define ENUM_EXTRACT(x) (x).c_str()
+#endif
+
+using android::base::DEBUG;
 
 namespace android::mediametrics {
 
 AudioAnalytics::AudioAnalytics()
+    : mDeliverStatistics(property_get_bool(PROP_AUDIO_ANALYTICS_CLOUD_ENABLED, true))
 {
+    SetMinimumLogSeverity(DEBUG); // for LOG().
     ALOGD("%s", __func__);
 
     // Add action to save AnalyticsState if audioserver is restarted.
@@ -243,33 +268,47 @@
     int32_t frameCount = 0;
     mAudioAnalytics.mAnalyticsState->timeMachine().get(
             key, AMEDIAMETRICS_PROP_FRAMECOUNT, &frameCount);
-    std::string inputDevices;
+    std::string inputDevicePairs;
     mAudioAnalytics.mAnalyticsState->timeMachine().get(
-            key, AMEDIAMETRICS_PROP_INPUTDEVICES, &inputDevices);
+            key, AMEDIAMETRICS_PROP_INPUTDEVICES, &inputDevicePairs);
     int32_t intervalCount = 0;
     mAudioAnalytics.mAnalyticsState->timeMachine().get(
             key, AMEDIAMETRICS_PROP_INTERVALCOUNT, &intervalCount);
-    std::string outputDevices;
+    std::string outputDevicePairs;
     mAudioAnalytics.mAnalyticsState->timeMachine().get(
-            key, AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices);
+            key, AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevicePairs);
     int32_t sampleRate = 0;
     mAudioAnalytics.mAnalyticsState->timeMachine().get(
             key, AMEDIAMETRICS_PROP_SAMPLERATE, &sampleRate);
     std::string flags;
     mAudioAnalytics.mAnalyticsState->timeMachine().get(
             key, AMEDIAMETRICS_PROP_FLAGS, &flags);
+
     // We may have several devices.
-    // Strings allow us to mix input and output devices together.
-    // TODO: review if we want to separate them.
-    std::stringstream ss;
-    for (const auto& devicePairs : { outputDevices, inputDevices }) {
-        const auto devaddrvec = MediaMetricsService::getDeviceAddressPairs(devicePairs);
+    // Accumulate the bit flags for input and output devices.
+    std::stringstream oss;
+    long_enum_type_t outputDeviceBits{};
+    {   // compute outputDevices
+        const auto devaddrvec = stringutils::getDeviceAddressPairs(outputDevicePairs);
         for (const auto& [device, addr] : devaddrvec) {
-            if (ss.tellp() > 0) ss << "|";  // delimit devices with '|'.
-            ss << device;
+            if (oss.tellp() > 0) oss << "|";  // delimit devices with '|'.
+            oss << device;
+            outputDeviceBits += types::lookup<types::OUTPUT_DEVICE, long_enum_type_t>(device);
         }
     }
-    std::string devices = ss.str();
+    const std::string outputDevices = oss.str();
+
+    std::stringstream iss;
+    long_enum_type_t inputDeviceBits{};
+    {   // compute inputDevices
+        const auto devaddrvec = stringutils::getDeviceAddressPairs(inputDevicePairs);
+        for (const auto& [device, addr] : devaddrvec) {
+            if (iss.tellp() > 0) iss << "|";  // delimit devices with '|'.
+            iss << device;
+            inputDeviceBits += types::lookup<types::INPUT_DEVICE, long_enum_type_t>(device);
+        }
+    }
+    const std::string inputDevices = iss.str();
 
     // Get connected device name if from bluetooth.
     bool isBluetooth = false;
@@ -278,8 +317,8 @@
         isBluetooth = true;
         mAudioAnalytics.mAnalyticsState->timeMachine().get(
             "audio.device.bt_a2dp", AMEDIAMETRICS_PROP_NAME, &deviceNames);
-        // We don't check if deviceName is sanitized.
-        // TODO: remove reserved chars such as '|' and replace with a char like '_'.
+        // Remove | if present
+        stringutils::replace(deviceNames, "|", '?');
     }
 
     switch (itemType) {
@@ -305,37 +344,43 @@
         mAudioAnalytics.mAnalyticsState->timeMachine().get(
                 key, AMEDIAMETRICS_PROP_SOURCE, &source);
 
-        ALOGD("(key=%s) id:%s endAudioIntervalGroup devices:%s deviceNames:%s "
-                 "deviceTimeNs:%lld encoding:%s frameCount:%d intervalCount:%d "
-                 "sampleRate:%d "
-                 "packageName:%s "
-                 "selectedDeviceId:%d "
-                 "callerName:%s source:%s",
-                key.c_str(), id.c_str(), devices.c_str(), deviceNames.c_str(),
-                (long long)deviceTimeNs, encoding.c_str(), frameCount, intervalCount,
-                sampleRate,
-                packageName.c_str(), selectedDeviceId,
-                callerName.c_str(), source.c_str());
+        const auto callerNameForStats =
+                types::lookup<types::CALLER_NAME, short_enum_type_t>(callerName);
+        const auto encodingForStats = types::lookup<types::ENCODING, short_enum_type_t>(encoding);
+        const auto flagsForStats = types::lookup<types::INPUT_FLAG, short_enum_type_t>(flags);
+        const auto sourceForStats = types::lookup<types::SOURCE_TYPE, short_enum_type_t>(source);
 
+        LOG(DEBUG) << "key:" << key
+              << " id:" << id
+              << " inputDevices:" << inputDevices << "(" << inputDeviceBits
+              << ") deviceNames:" << deviceNames
+              << " deviceTimeNs:" << deviceTimeNs
+              << " encoding:" << encoding << "(" << encodingForStats
+              << ") frameCount:" << frameCount
+              << " intervalCount:" << intervalCount
+              << " sampleRate:" << sampleRate
+              << " flags:" << flags << "(" << flagsForStats
+              << ") packageName:" << packageName
+              << " selectedDeviceId:" << selectedDeviceId
+              << " callerName:" << callerName << "(" << callerNameForStats
+              << ") source:" << source << "(" << sourceForStats << ")";
 #ifdef STATSD
         if (mAudioAnalytics.mDeliverStatistics) {
             (void)android::util::stats_write(
                     android::util::MEDIAMETRICS_AUDIORECORDDEVICEUSAGE_REPORTED
-                    /* timestamp, */
-                    /* mediaApexVersion, */
-                    , devices.c_str()
+                    , ENUM_EXTRACT(inputDeviceBits)
                     , deviceNames.c_str()
                     , deviceTimeNs
-                    , encoding.c_str()
+                    , ENUM_EXTRACT(encodingForStats)
                     , frameCount
                     , intervalCount
                     , sampleRate
-                    , flags.c_str()
+                    , ENUM_EXTRACT(flagsForStats)
 
                     , packageName.c_str()
                     , selectedDeviceId
-                    , callerName.c_str()
-                    , source.c_str()
+                    , ENUM_EXTRACT(callerNameForStats)
+                    , ENUM_EXTRACT(sourceForStats)
                     );
         }
 #endif
@@ -347,31 +392,43 @@
         int32_t underrun = 0; // zero for record types
         mAudioAnalytics.mAnalyticsState->timeMachine().get(
                 key, AMEDIAMETRICS_PROP_UNDERRUN, &underrun);
-        ALOGD("(key=%s) id:%s endAudioIntervalGroup devices:%s deviceNames:%s "
-                 "deviceTimeNs:%lld encoding:%s frameCount:%d intervalCount:%d "
-                 "sampleRate:%d underrun:%d "
-                 "flags:%s type:%s",
-                key.c_str(), id.c_str(), devices.c_str(), deviceNames.c_str(),
-                (long long)deviceTimeNs, encoding.c_str(), frameCount, intervalCount,
-                sampleRate, underrun,
-                flags.c_str(), type.c_str());
+
+        const bool isInput = types::isInputThreadType(type);
+        const auto encodingForStats = types::lookup<types::ENCODING, short_enum_type_t>(encoding);
+        const auto flagsForStats =
+                (isInput ? types::lookup<types::INPUT_FLAG, short_enum_type_t>(flags)
+                        : types::lookup<types::OUTPUT_FLAG, short_enum_type_t>(flags));
+        const auto typeForStats = types::lookup<types::THREAD_TYPE, short_enum_type_t>(type);
+
+        LOG(DEBUG) << "key:" << key
+              << " id:" << id
+              << " inputDevices:" << inputDevices << "(" << inputDeviceBits
+              << ") outputDevices:" << outputDevices << "(" << outputDeviceBits
+              << ") deviceNames:" << deviceNames
+              << " deviceTimeNs:" << deviceTimeNs
+              << " encoding:" << encoding << "(" << encodingForStats
+              << ") frameCount:" << frameCount
+              << " intervalCount:" << intervalCount
+              << " sampleRate:" << sampleRate
+              << " underrun:" << underrun
+              << " flags:" << flags << "(" << flagsForStats
+              << ") type:" << type << "(" << typeForStats
+              << ")";
 #ifdef STATSD
         if (mAudioAnalytics.mDeliverStatistics) {
             (void)android::util::stats_write(
                 android::util::MEDIAMETRICS_AUDIOTHREADDEVICEUSAGE_REPORTED
-                /* timestamp, */
-                /* mediaApexVersion, */
-                , devices.c_str()
+                , ENUM_EXTRACT(inputDeviceBits)
+                , ENUM_EXTRACT(outputDeviceBits)
                 , deviceNames.c_str()
                 , deviceTimeNs
-                , encoding.c_str()
+                , ENUM_EXTRACT(encodingForStats)
                 , frameCount
                 , intervalCount
                 , sampleRate
-                , flags.c_str()
-
+                , ENUM_EXTRACT(flagsForStats)
                 , underrun
-                , type.c_str()
+                , ENUM_EXTRACT(typeForStats)
             );
         }
 #endif
@@ -420,34 +477,51 @@
         mAudioAnalytics.mAnalyticsState->timeMachine().get(
                 key, AMEDIAMETRICS_PROP_USAGE, &usage);
 
-        ALOGD("(key=%s) id:%s endAudioIntervalGroup devices:%s deviceNames:%s "
-                 "deviceTimeNs:%lld encoding:%s frameCount:%d intervalCount:%d "
-                 "sampleRate:%d underrun:%d "
-                 "callerName:%s contentType:%s "
-                 "deviceLatencyMs:%lf deviceStartupMs:%lf deviceVolume:%lf "
-                 "packageName:%s playbackPitch:%lf playbackSpeed:%lf "
-                 "selectedDeviceId:%d streamType:%s usage:%s",
-                key.c_str(), id.c_str(), devices.c_str(), deviceNames.c_str(),
-                (long long)deviceTimeNs, encoding.c_str(), frameCount, intervalCount,
-                sampleRate, underrun,
-                callerName.c_str(), contentType.c_str(),
-                deviceLatencyMs, deviceStartupMs, deviceVolume,
-                packageName.c_str(), playbackPitch, playbackSpeed,
-                selectedDeviceId, streamType.c_str(), usage.c_str());
+        const auto callerNameForStats =
+                types::lookup<types::CALLER_NAME, short_enum_type_t>(callerName);
+        const auto contentTypeForStats =
+                types::lookup<types::CONTENT_TYPE, short_enum_type_t>(contentType);
+        const auto encodingForStats = types::lookup<types::ENCODING, short_enum_type_t>(encoding);
+        const auto flagsForStats = types::lookup<types::OUTPUT_FLAG, short_enum_type_t>(flags);
+        const auto streamTypeForStats =
+                types::lookup<types::STREAM_TYPE, short_enum_type_t>(streamType);
+        const auto usageForStats = types::lookup<types::USAGE, short_enum_type_t>(usage);
+
+        LOG(DEBUG) << "key:" << key
+              << " id:" << id
+              << " outputDevices:" << outputDevices << "(" << outputDeviceBits
+              << ") deviceNames:" << deviceNames
+              << " deviceTimeNs:" << deviceTimeNs
+              << " encoding:" << encoding << "(" << encodingForStats
+              << ") frameCount:" << frameCount
+              << " intervalCount:" << intervalCount
+              << " sampleRate:" << sampleRate
+              << " underrun:" << underrun
+              << " flags:" << flags << "(" << flagsForStats
+              << ") callerName:" << callerName << "(" << callerNameForStats
+              << ") contentType:" << contentType << "(" << contentTypeForStats
+              << ") deviceLatencyMs:" << deviceLatencyMs
+              << " deviceStartupMs:" << deviceStartupMs
+              << " deviceVolume:" << deviceVolume
+              << " packageName:" << packageName
+              << " playbackPitch:" << playbackPitch
+              << " playbackSpeed:" << playbackSpeed
+              << " selectedDeviceId:" << selectedDeviceId
+              << " streamType:" << streamType << "(" << streamTypeForStats
+              << ") usage:" << usage << "(" << usageForStats
+              << ")";
 #ifdef STATSD
         if (mAudioAnalytics.mDeliverStatistics) {
             (void)android::util::stats_write(
                     android::util::MEDIAMETRICS_AUDIOTRACKDEVICEUSAGE_REPORTED
-                    /* timestamp, */
-                    /* mediaApexVersion, */
-                    , devices.c_str()
+                    , ENUM_EXTRACT(outputDeviceBits)
                     , deviceNames.c_str()
                     , deviceTimeNs
-                    , encoding.c_str()
+                    , ENUM_EXTRACT(encodingForStats)
                     , frameCount
                     , intervalCount
                     , sampleRate
-                    , flags.c_str()
+                    , ENUM_EXTRACT(flagsForStats)
                     , underrun
 
                     , packageName.c_str()
@@ -455,10 +529,10 @@
                     , (float)deviceStartupMs
                     , (float)deviceVolume
                     , selectedDeviceId
-                    , streamType.c_str()
-                    , usage.c_str()
-                    , contentType.c_str()
-                    , callerName.c_str()
+                    , ENUM_EXTRACT(streamTypeForStats)
+                    , ENUM_EXTRACT(usageForStats)
+                    , ENUM_EXTRACT(contentTypeForStats)
+                    , ENUM_EXTRACT(callerNameForStats)
                     );
         }
 #endif
@@ -490,7 +564,6 @@
     item->get(AMEDIAMETRICS_PROP_NAME, &name);
     ALOGD("(key=%s) a2dp connected device:%s atNs:%lld",
             key.c_str(), name.c_str(), (long long)atNs);
-
 }
 
 void AudioAnalytics::DeviceConnection::createPatch(
@@ -502,27 +575,36 @@
     item->get(AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices);
     if (outputDevices.find("AUDIO_DEVICE_OUT_BLUETOOTH_A2DP") != std::string::npos) {
         // TODO compare address
-        int64_t timeDiff = item->getTimestamp();
+        int64_t timeDiffNs = item->getTimestamp();
         if (mA2dpConnectionRequestNs == 0) {
             ALOGD("%s: A2DP create patch didn't see a connection request", __func__);
-            timeDiff -= mA2dpConnectionServiceNs;
+            timeDiffNs -= mA2dpConnectionServiceNs;
         } else {
-            timeDiff -= mA2dpConnectionRequestNs;
+            timeDiffNs -= mA2dpConnectionRequestNs;
         }
-        ALOGD("(key=%s) A2DP device connection time: %lld", key.c_str(), (long long)timeDiff);
+
         mA2dpConnectionRequestNs = 0;
         mA2dpConnectionServiceNs = 0;
         ++mA2dpConnectionSuccesses;
 
+        const auto connectionTimeMs = float(timeDiffNs * 1e-6);
+
+        const auto outputDeviceBits = types::lookup<types::OUTPUT_DEVICE, long_enum_type_t>(
+                "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP");
+
+        LOG(DEBUG) << "key:" << key
+                << " A2DP SUCCESS"
+                << " outputDevices:" << outputDeviceBits
+                << " connectionTimeMs:" <<  connectionTimeMs;
 #ifdef STATSD
         if (mAudioAnalytics.mDeliverStatistics) {
+            const long_enum_type_t inputDeviceBits{};
             (void)android::util::stats_write(
                     android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED
-                    /* timestamp, */
-                    /* mediaApexVersion, */
-                    , "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP"
-                    , android::util::MEDIAMETRICS_AUDIO_DEVICE_CONNECTION_REPORTED__RESULT__SUCCESS
-                    , /* connection_time_ms */ timeDiff * 1e-6 /* NS to MS */
+                    , ENUM_EXTRACT(inputDeviceBits)
+                    , ENUM_EXTRACT(outputDeviceBits)
+                    , types::DEVICE_CONNECTION_RESULT_SUCCESS
+                    , connectionTimeMs
                     , /* connection_count */ 1
                     );
         }
@@ -552,18 +634,25 @@
 void AudioAnalytics::DeviceConnection::expire() {
     std::lock_guard l(mLock);
     if (mA2dpConnectionRequestNs == 0) return; // ignore (this was an internal connection).
+
+#ifdef STATSD
+    const long_enum_type_t inputDeviceBits{};
+#endif
+    const auto outputDeviceBits = types::lookup<types::OUTPUT_DEVICE, long_enum_type_t>(
+            "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP");
+
     if (mA2dpConnectionServiceNs == 0) {
-        ALOGD("A2DP device connection service cancels");
         ++mA2dpConnectionJavaServiceCancels;  // service did not connect to A2DP
 
+        LOG(DEBUG) << "A2DP CANCEL"
+                << " outputDevices:" << outputDeviceBits;
 #ifdef STATSD
         if (mAudioAnalytics.mDeliverStatistics) {
             (void)android::util::stats_write(
                     android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED
-                    /* timestamp, */
-                    /* mediaApexVersion, */
-                    , "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP"
-                    , android::util::MEDIAMETRICS_AUDIO_DEVICE_CONNECTION_REPORTED__RESULT__JAVA_SERVICE_CANCEL
+                    , ENUM_EXTRACT(inputDeviceBits)
+                    , ENUM_EXTRACT(outputDeviceBits)
+                    , types::DEVICE_CONNECTION_RESULT_JAVA_SERVICE_CANCEL
                     , /* connection_time_ms */ 0.f
                     , /* connection_count */ 1
                     );
@@ -575,18 +664,19 @@
     // AudioFlinger didn't play - an expiration may occur because there is no audio playing.
     // Should we check elsewhere?
     // TODO: disambiguate this case.
-    ALOGD("A2DP device connection expired, state unknown");
     mA2dpConnectionRequestNs = 0;
     mA2dpConnectionServiceNs = 0;
     ++mA2dpConnectionUnknowns;  // connection result unknown
+
+    LOG(DEBUG) << "A2DP UNKNOWN"
+            << " outputDevices:" << outputDeviceBits;
 #ifdef STATSD
     if (mAudioAnalytics.mDeliverStatistics) {
         (void)android::util::stats_write(
                 android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED
-                /* timestamp, */
-                /* mediaApexVersion, */
-                , "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP"
-                , android::util::MEDIAMETRICS_AUDIO_DEVICE_CONNECTION_REPORTED__RESULT__UNKNOWN
+                , ENUM_EXTRACT(inputDeviceBits)
+                , ENUM_EXTRACT(outputDeviceBits)
+                , types::DEVICE_CONNECTION_RESULT_UNKNOWN
                 , /* connection_time_ms */ 0.f
                 , /* connection_count */ 1
                 );
@@ -594,4 +684,4 @@
 #endif
 }
 
-} // namespace android
+} // namespace android::mediametrics
diff --git a/services/mediametrics/AudioAnalytics.h b/services/mediametrics/AudioAnalytics.h
index 9089d6f..138ddcc 100644
--- a/services/mediametrics/AudioAnalytics.h
+++ b/services/mediametrics/AudioAnalytics.h
@@ -109,7 +109,7 @@
      */
     std::string getThreadFromTrack(const std::string& track) const;
 
-    const bool mDeliverStatistics __unused = true;
+    const bool mDeliverStatistics;
 
     // Actions is individually locked
     AnalyticsActions mActions;
diff --git a/services/mediametrics/AudioPowerUsage.cpp b/services/mediametrics/AudioPowerUsage.cpp
index b1615bd..c441110 100644
--- a/services/mediametrics/AudioPowerUsage.cpp
+++ b/services/mediametrics/AudioPowerUsage.cpp
@@ -20,6 +20,7 @@
 
 #include "AudioAnalytics.h"
 #include "MediaMetricsService.h"
+#include "StringUtils.h"
 #include <map>
 #include <sstream>
 #include <string>
@@ -117,7 +118,7 @@
 
 int32_t AudioPowerUsage::deviceFromStringPairs(const std::string& device_strings) {
     int32_t deviceMask = 0;
-    const auto devaddrvec = MediaMetricsService::getDeviceAddressPairs(device_strings);
+    const auto devaddrvec = stringutils::getDeviceAddressPairs(device_strings);
     for (const auto &[device, addr] : devaddrvec) {
         int32_t combo_device = 0;
         deviceFromString(device, combo_device);
@@ -160,7 +161,7 @@
         return true; //ignore unknown device
     }
 
-    for (auto item : mItems) {
+    for (const auto& item : mItems) {
         int32_t item_type = 0, item_device = 0;
         double item_volume = 0.;
         int64_t item_duration_ns = 0;
@@ -259,8 +260,8 @@
         const int64_t endCallNs = item->getTimestamp();
         const int64_t durationNs = endCallNs - mDeviceTimeNs;
         if (durationNs > 0) {
-            mDeviceVolume = (mDeviceVolume * (mVolumeTimeNs - mDeviceTimeNs) +
-                    mVoiceVolume * (endCallNs - mVolumeTimeNs)) / durationNs;
+            mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
+                    mVoiceVolume * double(endCallNs - mVolumeTimeNs)) / durationNs;
             saveAsItem_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
         }
     } else if (mode == "AUDIO_MODE_IN_CALL") { // entering call mode
@@ -287,8 +288,8 @@
         const int64_t timeNs = item->getTimestamp();
         const int64_t durationNs = timeNs - mDeviceTimeNs;
         if (durationNs > 0) {
-            mDeviceVolume = (mDeviceVolume * (mVolumeTimeNs - mDeviceTimeNs) +
-                    mVoiceVolume * (timeNs - mVolumeTimeNs)) / durationNs;
+            mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
+                    mVoiceVolume * double(timeNs - mVolumeTimeNs)) / durationNs;
             mVolumeTimeNs = timeNs;
         }
     }
@@ -318,8 +319,8 @@
         const int64_t endDeviceNs = item->getTimestamp();
         const int64_t durationNs = endDeviceNs - mDeviceTimeNs;
         if (durationNs > 0) {
-            mDeviceVolume = (mDeviceVolume * (mVolumeTimeNs - mDeviceTimeNs) +
-                    mVoiceVolume * (endDeviceNs - mVolumeTimeNs)) / durationNs;
+            mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
+                    mVoiceVolume * double(endDeviceNs - mVolumeTimeNs)) / durationNs;
             saveAsItem_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
         }
         // reset statistics
@@ -391,4 +392,4 @@
     return { ss.str(), slot };
 }
 
-} // namespace android
+} // namespace android::mediametrics
diff --git a/services/mediametrics/AudioTypes.cpp b/services/mediametrics/AudioTypes.cpp
new file mode 100644
index 0000000..2a2dbaf
--- /dev/null
+++ b/services/mediametrics/AudioTypes.cpp
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AudioTypes.h"
+#include "StringUtils.h"
+#include <media/TypeConverter.h> // requires libmedia_helper to get the Audio code.
+
+namespace android::mediametrics::types {
+
+std::unordered_map<std::string, int64_t>& getAudioDeviceInMap() {
+    // DO NOT MODIFY VALUES (OK to add new ones).  This does NOT match audio_device_t.
+    static std::unordered_map<std::string, int64_t> map{
+        {"AUDIO_DEVICE_IN_COMMUNICATION",          1LL << 0},
+        {"AUDIO_DEVICE_IN_AMBIENT",                1LL << 1},
+        {"AUDIO_DEVICE_IN_BUILTIN_MIC",            1LL << 2},
+        {"AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET",  1LL << 3},
+        {"AUDIO_DEVICE_IN_WIRED_HEADSET",          1LL << 4},
+        {"AUDIO_DEVICE_IN_AUX_DIGITAL",            1LL << 5},
+        {"AUDIO_DEVICE_IN_HDMI",                   1LL << 5}, // HDMI == AUX_DIGITAL (6 reserved)
+        {"AUDIO_DEVICE_IN_VOICE_CALL",             1LL << 7},
+        {"AUDIO_DEVICE_IN_TELEPHONY_RX",           1LL << 7}, // TELEPHONY_RX == VOICE_CALL (8 reserved)
+        {"AUDIO_DEVICE_IN_BACK_MIC",               1LL << 9},
+        {"AUDIO_DEVICE_IN_REMOTE_SUBMIX",          1LL << 10},
+        {"AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET",      1LL << 11},
+        {"AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET",      1LL << 12},
+        {"AUDIO_DEVICE_IN_USB_ACCESSORY",          1LL << 13},
+        {"AUDIO_DEVICE_IN_USB_DEVICE",             1LL << 14},
+        {"AUDIO_DEVICE_IN_FM_TUNER",               1LL << 15},
+        {"AUDIO_DEVICE_IN_TV_TUNER",               1LL << 16},
+        {"AUDIO_DEVICE_IN_LINE",                   1LL << 17},
+        {"AUDIO_DEVICE_IN_SPDIF",                  1LL << 18},
+        {"AUDIO_DEVICE_IN_BLUETOOTH_A2DP",         1LL << 19},
+        {"AUDIO_DEVICE_IN_LOOPBACK",               1LL << 20},
+        {"AUDIO_DEVICE_IN_IP",                     1LL << 21},
+        {"AUDIO_DEVICE_IN_BUS",                    1LL << 22},
+        {"AUDIO_DEVICE_IN_PROXY",                  1LL << 23},
+        {"AUDIO_DEVICE_IN_USB_HEADSET",            1LL << 24},
+        {"AUDIO_DEVICE_IN_BLUETOOTH_BLE",          1LL << 25},
+        {"AUDIO_DEVICE_IN_HDMI_ARC",               1LL << 26},
+        {"AUDIO_DEVICE_IN_ECHO_REFERENCE",         1LL << 27},
+        {"AUDIO_DEVICE_IN_DEFAULT",                1LL << 28},
+        // R values above.
+    };
+    return map;
+}
+
+std::unordered_map<std::string, int64_t>& getAudioDeviceOutMap() {
+    // DO NOT MODIFY VALUES (OK to add new ones).  This does NOT match audio_device_t.
+    static std::unordered_map<std::string, int64_t> map{
+        {"AUDIO_DEVICE_OUT_EARPIECE",                  1LL << 0},
+        {"AUDIO_DEVICE_OUT_SPEAKER",                   1LL << 1},
+        {"AUDIO_DEVICE_OUT_WIRED_HEADSET",             1LL << 2},
+        {"AUDIO_DEVICE_OUT_WIRED_HEADPHONE",           1LL << 3},
+        {"AUDIO_DEVICE_OUT_BLUETOOTH_SCO",             1LL << 4},
+        {"AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET",     1LL << 5},
+        {"AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT",      1LL << 6},
+        {"AUDIO_DEVICE_OUT_BLUETOOTH_A2DP",            1LL << 7},
+        {"AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES", 1LL << 8},
+        {"AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER",    1LL << 9},
+        {"AUDIO_DEVICE_OUT_AUX_DIGITAL",               1LL << 10},
+        {"AUDIO_DEVICE_OUT_HDMI",                      1LL << 10}, // HDMI == AUX_DIGITAL (11 reserved)
+        {"AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET",         1LL << 12},
+        {"AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET",         1LL << 13},
+        {"AUDIO_DEVICE_OUT_USB_ACCESSORY",             1LL << 14},
+        {"AUDIO_DEVICE_OUT_USB_DEVICE",                1LL << 15},
+        {"AUDIO_DEVICE_OUT_REMOTE_SUBMIX",             1LL << 16},
+        {"AUDIO_DEVICE_OUT_TELEPHONY_TX",              1LL << 17},
+        {"AUDIO_DEVICE_OUT_LINE",                      1LL << 18},
+        {"AUDIO_DEVICE_OUT_HDMI_ARC",                  1LL << 19},
+        {"AUDIO_DEVICE_OUT_SPDIF",                     1LL << 20},
+        {"AUDIO_DEVICE_OUT_FM",                        1LL << 21},
+        {"AUDIO_DEVICE_OUT_AUX_LINE",                  1LL << 22},
+        {"AUDIO_DEVICE_OUT_SPEAKER_SAFE",              1LL << 23},
+        {"AUDIO_DEVICE_OUT_IP",                        1LL << 24},
+        {"AUDIO_DEVICE_OUT_BUS",                       1LL << 25},
+        {"AUDIO_DEVICE_OUT_PROXY",                     1LL << 26},
+        {"AUDIO_DEVICE_OUT_USB_HEADSET",               1LL << 27},
+        {"AUDIO_DEVICE_OUT_HEARING_AID",               1LL << 28},
+        {"AUDIO_DEVICE_OUT_ECHO_CANCELLER",            1LL << 29},
+        {"AUDIO_DEVICE_OUT_DEFAULT",                   1LL << 30},
+        // R values above.
+    };
+    return map;
+}
+
+std::unordered_map<std::string, int32_t>& getCallerNameMap() {
+    // DO NOT MODIFY VALUES (OK to add new ones).
+    // This may be found in frameworks/av/media/libmediametrics/include/MediaMetricsConstants.h
+    static std::unordered_map<std::string, int32_t> map{
+        {"aaudio",        0},           // Native AAudio
+        {"java",          1},           // Java API layer
+        {"media",         2},           // libmedia (mediaplayer)
+        {"opensles",      3},           // Open SLES
+        {"rtp",           4},           // RTP communication
+        {"soundpool",     5},           // SoundPool
+        {"tonegenerator", 6},           // dial tones
+        {"unknown",       7},           // callerName not set
+        // R values above.
+    };
+    return map;
+}
+
+std::unordered_map<std::string, int32_t>& getThreadTypeMap() {
+    // DO NOT MODIFY VALUES (OK to add new ones).
+    // This may be found in frameworks/av/services/audioflinger/Threads.h
+    static std::unordered_map<std::string, int32_t> map{
+        // UNKNOWN is -1
+        {"MIXER",         0},          // Thread class is MixerThread
+        {"DIRECT",        1},          // Thread class is DirectOutputThread
+        {"DUPLICATING",   2},          // Thread class is DuplicatingThread
+        {"RECORD",        3},          // Thread class is RecordThread
+        {"OFFLOAD",       4},          // Thread class is OffloadThread
+        {"MMAP_PLAYBACK", 5},          // Thread class for MMAP playback stream
+        {"MMAP_CAPTURE",  6},          // Thread class for MMAP capture stream
+        // R values above.
+    };
+    return map;
+}
+
+// Helper: Create the corresponding int32 from string flags split with '|'.
+template <typename Traits>
+int32_t int32FromFlags(const std::string &flags)
+{
+    const auto result = stringutils::split(flags, "|");
+    int32_t intFlags = 0;
+    for (const auto& flag : result) {
+        typename Traits::Type value;
+        if (!TypeConverter<Traits>::fromString(flag, value)) {
+            break;
+        }
+        intFlags |= value;
+    }
+    return intFlags;
+}
+
+template <typename Traits>
+std::string stringFromFlags(const std::string &flags, size_t len)
+{
+    const auto result = stringutils::split(flags, "|");
+    std::string sFlags;
+    for (const auto& flag : result) {
+        typename Traits::Type value;
+        if (!TypeConverter<Traits>::fromString(flag, value)) {
+            break;
+        }
+        if (len >= flag.size()) continue;
+        if (!sFlags.empty()) sFlags += "|";
+        sFlags += flag.c_str() + len;
+    }
+    return sFlags;
+}
+
+template <>
+int32_t lookup<CONTENT_TYPE>(const std::string &contentType)
+{
+    AudioContentTraits::Type value;
+    if (!TypeConverter<AudioContentTraits>::fromString(contentType, value)) {
+        value = AUDIO_CONTENT_TYPE_UNKNOWN;
+    }
+    return (int32_t)value;
+}
+
+template <>
+std::string lookup<CONTENT_TYPE>(const std::string &contentType)
+{
+    AudioContentTraits::Type value;
+    if (!TypeConverter<AudioContentTraits>::fromString(contentType, value)) {
+        return "UNKNOWN";
+    }
+    return contentType.c_str() + sizeof("AUDIO_CONTENT_TYPE");
+}
+
+template <>
+int32_t lookup<ENCODING>(const std::string &encoding)
+{
+    FormatTraits::Type value;
+    if (!TypeConverter<FormatTraits>::fromString(encoding, value)) {
+        value = AUDIO_FORMAT_INVALID;
+    }
+    return (int32_t)value;
+}
+
+template <>
+std::string lookup<ENCODING>(const std::string &encoding)
+{
+    FormatTraits::Type value;
+    if (!TypeConverter<FormatTraits>::fromString(encoding, value)) {
+        return "INVALID";
+    }
+    return encoding.c_str() + sizeof("AUDIO_FORMAT");
+}
+
+template <>
+int32_t lookup<INPUT_FLAG>(const std::string &inputFlag)
+{
+    return int32FromFlags<InputFlagTraits>(inputFlag);
+}
+
+template <>
+std::string lookup<INPUT_FLAG>(const std::string &inputFlag)
+{
+    return stringFromFlags<InputFlagTraits>(inputFlag, sizeof("AUDIO_INPUT_FLAG"));
+}
+
+template <>
+int32_t lookup<OUTPUT_FLAG>(const std::string &outputFlag)
+{
+    return int32FromFlags<OutputFlagTraits>(outputFlag);
+}
+
+template <>
+std::string lookup<OUTPUT_FLAG>(const std::string &outputFlag)
+{
+    return stringFromFlags<OutputFlagTraits>(outputFlag, sizeof("AUDIO_OUTPUT_FLAG"));
+}
+
+template <>
+int32_t lookup<SOURCE_TYPE>(const std::string &sourceType)
+{
+    SourceTraits::Type value;
+    if (!TypeConverter<SourceTraits>::fromString(sourceType, value)) {
+        value = AUDIO_SOURCE_DEFAULT;
+    }
+    return (int32_t)value;
+}
+
+template <>
+std::string lookup<SOURCE_TYPE>(const std::string &sourceType)
+{
+    SourceTraits::Type value;
+    if (!TypeConverter<SourceTraits>::fromString(sourceType, value)) {
+        return "DEFAULT";
+    }
+    return sourceType.c_str() + sizeof("AUDIO_SOURCE");
+}
+
+template <>
+int32_t lookup<STREAM_TYPE>(const std::string &streamType)
+{
+    StreamTraits::Type value;
+    if (!TypeConverter<StreamTraits>::fromString(streamType, value)) {
+        value = AUDIO_STREAM_DEFAULT;
+    }
+    return (int32_t)value;
+}
+
+template <>
+std::string lookup<STREAM_TYPE>(const std::string &streamType)
+{
+    StreamTraits::Type value;
+    if (!TypeConverter<StreamTraits>::fromString(streamType, value)) {
+        return "DEFAULT";
+    }
+    return streamType.c_str() + sizeof("AUDIO_STREAM");
+}
+
+template <>
+int32_t lookup<USAGE>(const std::string &usage)
+{
+    UsageTraits::Type value;
+    if (!TypeConverter<UsageTraits>::fromString(usage, value)) {
+        value = AUDIO_USAGE_UNKNOWN;
+    }
+    return (int32_t)value;
+}
+
+template <>
+std::string lookup<USAGE>(const std::string &usage)
+{
+    UsageTraits::Type value;
+    if (!TypeConverter<UsageTraits>::fromString(usage, value)) {
+        return "UNKNOWN";
+    }
+    return usage.c_str() + sizeof("AUDIO_USAGE");
+}
+
+template <>
+int64_t lookup<INPUT_DEVICE>(const std::string &inputDevice)
+{
+    auto& map = getAudioDeviceInMap();
+    auto it = map.find(inputDevice);
+    if (it == map.end()) {
+        return 0;
+    }
+    return it->second;
+}
+
+template <>
+std::string lookup<INPUT_DEVICE>(const std::string &inputDevice)
+{
+    auto& map = getAudioDeviceInMap();
+    auto it = map.find(inputDevice);
+    if (it == map.end()) {
+        return "NONE";
+    }
+    return inputDevice.c_str() + sizeof("AUDIO_DEVICE_IN");
+}
+
+template <>
+int64_t lookup<OUTPUT_DEVICE>(const std::string &outputDevice)
+{
+    auto& map = getAudioDeviceOutMap();
+    auto it = map.find(outputDevice);
+    if (it == map.end()) {
+        return 0; // nothing
+    }
+    return it->second;
+}
+
+template <>
+std::string lookup<OUTPUT_DEVICE>(const std::string &outputDevice)
+{
+    auto& map = getAudioDeviceOutMap();
+    auto it = map.find(outputDevice);
+    if (it == map.end()) {
+        return "NONE";
+    }
+    return outputDevice.c_str() + sizeof("AUDIO_DEVICE_OUT");
+}
+
+template <>
+int32_t lookup<CALLER_NAME>(const std::string &callerName)
+{
+    auto& map = getCallerNameMap();
+    auto it = map.find(callerName);
+    if (it == map.end()) {
+        return 7;      // return unknown
+    }
+    return it->second;
+}
+
+template <>
+std::string lookup<CALLER_NAME>(const std::string &callerName)
+{
+    auto& map = getCallerNameMap();
+    auto it = map.find(callerName);
+    if (it == map.end()) {
+        return "unknown";
+    }
+    return callerName;
+}
+
+template <>
+int32_t lookup<THREAD_TYPE>(const std::string &threadType)
+{
+    auto& map = getThreadTypeMap();
+    auto it = map.find(threadType);
+    if (it == map.end()) {
+        return -1; // note this as an illegal thread value as we don't have unknown here.
+    }
+    return it->second;
+}
+
+template <>
+std::string lookup<THREAD_TYPE>(const std::string &threadType)
+{
+    auto& map = getThreadTypeMap();
+    auto it = map.find(threadType);
+    if (it == map.end()) {
+        return "UNKNOWN";
+    }
+    return threadType;
+}
+
+bool isInputThreadType(const std::string &threadType)
+{
+    return threadType == "RECORD" || threadType == "MMAP_CAPTURE";
+}
+
+} // namespace android::mediametrics::types
diff --git a/services/mediametrics/AudioTypes.h b/services/mediametrics/AudioTypes.h
new file mode 100644
index 0000000..a094e6e
--- /dev/null
+++ b/services/mediametrics/AudioTypes.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 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 <string>
+#include <unordered_map>
+
+namespace android::mediametrics::types {
+
+// Helper methods that map mediametrics logged strings to
+// integer codes.
+std::unordered_map<std::string, int64_t>& getAudioDeviceInMap();
+std::unordered_map<std::string, int64_t>& getAudioDeviceOutMap();
+std::unordered_map<std::string, int32_t>& getCallerNameMap();
+std::unordered_map<std::string, int32_t>& getThreadTypeMap();
+
+// Enumeration for the device connection results.
+enum DeviceConnectionResult : int32_t {
+    DEVICE_CONNECTION_RESULT_UNKNOWN = 0,              // Success is unknown.
+    DEVICE_CONNECTION_RESULT_SUCCESS = 1,              // Audio delivered
+    DEVICE_CONNECTION_RESULT_JAVA_SERVICE_CANCEL = 2,  // Canceled in Java service
+    // Do not modify the constants above after R.  Adding new constants is fine.
+};
+
+// Enumeration for all the string translations to integers (generally int32_t) unless noted.
+enum AudioEnumCategory {
+    CALLER_NAME,
+    CONTENT_TYPE,
+    ENCODING,
+    INPUT_DEVICE,  // int64_t
+    INPUT_FLAG,
+    OUTPUT_DEVICE, // int64_t
+    OUTPUT_FLAG,
+    SOURCE_TYPE,
+    STREAM_TYPE,
+    THREAD_TYPE,
+    USAGE,
+};
+
+// Convert a string (or arbitrary S) from an AudioEnumCategory to a particular type.
+// This is used to convert log std::strings back to the original type (int32_t or int64_t).
+//
+// For a string, generally there is a prefix "AUDIO_INPUT_FLAG" or some such that could
+// actually indicate the category so the AudioEnumCategory could be superfluous, but
+// we use it to find the proper default value in case of an unknown string.
+//
+// lookup<ENCODING, int32_t>("AUDIO_FORMAT_PCM_16_BIT") -> 1
+//
+template <AudioEnumCategory C, typename T, typename S>
+T lookup(const S &str);
+
+// Helper: Allow using a const char * in lieu of std::string.
+template <AudioEnumCategory C, typename T>
+T lookup(const char *str) {
+    return lookup<C, T, std::string>(str);
+}
+
+bool isInputThreadType(const std::string &threadType);
+
+} // namespace android::mediametrics::types
diff --git a/services/mediametrics/MediaMetricsService.cpp b/services/mediametrics/MediaMetricsService.cpp
index 3b3dc3e..d682fed 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -32,7 +32,8 @@
 
 namespace android {
 
-using namespace mediametrics;
+using mediametrics::Item;
+using mediametrics::startsWith;
 
 // individual records kept in memory: age or count
 // age: <= 28 hours (1 1/6 days)
@@ -63,7 +64,7 @@
 bool MediaMetricsService::useUidForPackage(
         const std::string& package, const std::string& installer)
 {
-    if (strchr(package.c_str(), '.') == NULL) {
+    if (strchr(package.c_str(), '.') == nullptr) {
         return false;  // not of form 'com.whatever...'; assume internal and ok
     } else if (strncmp(package.c_str(), "android.", 8) == 0) {
         return false;  // android.* packages are assumed fine
@@ -94,58 +95,6 @@
     }
 }
 
-/* static */
-std::string MediaMetricsService::tokenizer(std::string::const_iterator& it,
-        const std::string::const_iterator& end, const char *reserved) {
-    // consume leading white space
-    for (; it != end && std::isspace(*it); ++it);
-    if (it == end) return {};
-
-    auto start = it;
-    // parse until we hit a reserved keyword or space
-    if (strchr(reserved, *it)) return {start, ++it};
-    for (;;) {
-        ++it;
-        if (it == end || std::isspace(*it) || strchr(reserved, *it)) return {start, it};
-    }
-}
-
-/* static */
-std::vector<std::pair<std::string, std::string>>
-MediaMetricsService::getDeviceAddressPairs(const std::string& devices) {
-    std::vector<std::pair<std::string, std::string>> result;
-
-    // Currently, the device format is EXACTLY
-    // (device1, addr1)|(device2, addr2)|...
-
-    static constexpr char delim[] = "()|,";
-    for (auto it = devices.begin(); ; ) {
-        auto token = tokenizer(it, devices.end(), delim);
-        if (token != "(") return result;
-
-        auto device = tokenizer(it, devices.end(), delim);
-        if (device.empty() || !std::isalnum(device[0])) return result;
-
-        token = tokenizer(it, devices.end(), delim);
-        if (token != ",") return result;
-
-        // special handling here for empty addresses
-        auto address = tokenizer(it, devices.end(), delim);
-        if (address.empty() || !std::isalnum(device[0])) return result;
-        if (address == ")") {  // no address, just the ")"
-            address.clear();
-        } else {
-            token = tokenizer(it, devices.end(), delim);
-            if (token != ")") return result;
-        }
-
-        result.emplace_back(std::move(device), std::move(address));
-
-        token = tokenizer(it, devices.end(), delim);
-        if (token != "|") return result;  // this includes end of string detection
-    }
-}
-
 MediaMetricsService::MediaMetricsService()
         : mMaxRecords(kMaxRecords),
           mMaxRecordAgeNs(kMaxRecordAgeNs),
@@ -203,9 +152,9 @@
 
     // Overwrite package name and version if the caller was untrusted or empty
     if (!isTrusted || item->getPkgName().empty()) {
-        const uid_t uid = item->getUid();
+        const uid_t uidItem = item->getUid();
         const auto [ pkgName, version ] =
-                MediaMetricsService::getSanitizedPackageNameAndVersionCode(uid);
+                MediaMetricsService::getSanitizedPackageNameAndVersionCode(uidItem);
         item->setPkgName(pkgName);
         item->setPkgVersionCode(version);
     }
@@ -320,7 +269,7 @@
                 String8 value(args[i]);
                 char *endp;
                 const char *p = value.string();
-                long long sec = strtoll(p, &endp, 10);
+                const auto sec = (int64_t)strtoll(p, &endp, 10);
                 if (endp == p || *endp != '\0' || sec == 0) {
                     sinceNs = 0;
                 } else if (sec < 0) {
diff --git a/services/mediametrics/MediaMetricsService.h b/services/mediametrics/MediaMetricsService.h
index b8eb267..d152264 100644
--- a/services/mediametrics/MediaMetricsService.h
+++ b/services/mediametrics/MediaMetricsService.h
@@ -77,20 +77,6 @@
      */
     static std::pair<std::string, int64_t> getSanitizedPackageNameAndVersionCode(uid_t uid);
 
-    /**
-     * Return string tokens from iterator, separated by spaces and reserved chars.
-     */
-    static std::string tokenizer(std::string::const_iterator& it,
-            const std::string::const_iterator& end, const char *reserved);
-
-    /**
-     * Parse the devices string and return a vector of device address pairs.
-     *
-     * A failure to parse returns early with the contents that were able to be parsed.
-     */
-    static std::vector<std::pair<std::string, std::string>>
-    getDeviceAddressPairs(const std::string &devices);
-
 protected:
 
     // Internal call where release is true if ownership of item is transferred
diff --git a/services/mediametrics/StringUtils.cpp b/services/mediametrics/StringUtils.cpp
new file mode 100644
index 0000000..50525bc
--- /dev/null
+++ b/services/mediametrics/StringUtils.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 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 "MediaMetricsService::stringutils"
+#include <utils/Log.h>
+
+#include "StringUtils.h"
+
+namespace android::mediametrics::stringutils {
+
+std::string tokenizer(std::string::const_iterator& it,
+        const std::string::const_iterator& end, const char *reserved)
+{
+    // consume leading white space
+    for (; it != end && std::isspace(*it); ++it);
+    if (it == end) return {};
+
+    auto start = it;
+    // parse until we hit a reserved keyword or space
+    if (strchr(reserved, *it)) return {start, ++it};
+    for (;;) {
+        ++it;
+        if (it == end || std::isspace(*it) || strchr(reserved, *it)) return {start, it};
+    }
+}
+
+std::vector<std::string> split(const std::string& flags, const char *delim)
+{
+    std::vector<std::string> result;
+    for (auto it = flags.begin(); ; ) {
+        auto flag = tokenizer(it, flags.end(), delim);
+        if (flag.empty() || !std::isalnum(flag[0])) return result;
+        result.emplace_back(std::move(flag));
+
+        // look for the delimeter and discard
+        auto token = tokenizer(it, flags.end(), delim);
+        if (token.size() != 1 || strchr(delim, token[0]) == nullptr) return result;
+    }
+}
+
+std::vector<std::pair<std::string, std::string>> getDeviceAddressPairs(const std::string& devices)
+{
+    std::vector<std::pair<std::string, std::string>> result;
+
+    // Currently, the device format is EXACTLY
+    // (device1, addr1)|(device2, addr2)|...
+
+    static constexpr char delim[] = "()|,";
+    for (auto it = devices.begin(); ; ) {
+        auto token = tokenizer(it, devices.end(), delim);
+        if (token != "(") return result;
+
+        auto device = tokenizer(it, devices.end(), delim);
+        if (device.empty() || !std::isalnum(device[0])) return result;
+
+        token = tokenizer(it, devices.end(), delim);
+        if (token != ",") return result;
+
+        // special handling here for empty addresses
+        auto address = tokenizer(it, devices.end(), delim);
+        if (address.empty() || !std::isalnum(device[0])) return result;
+        if (address == ")") {  // no address, just the ")"
+            address.clear();
+        } else {
+            token = tokenizer(it, devices.end(), delim);
+            if (token != ")") return result;
+        }
+
+        result.emplace_back(std::move(device), std::move(address));
+
+        token = tokenizer(it, devices.end(), delim);
+        if (token != "|") return result;  // this includes end of string detection
+    }
+}
+
+size_t replace(std::string &str, const char *targetChars, const char replaceChar)
+{
+    size_t replaced = 0;
+    for (char &c : str) {
+        if (strchr(targetChars, c) != nullptr) {
+            c = replaceChar;
+            ++replaced;
+        }
+    }
+    return replaced;
+}
+
+} // namespace android::mediametrics::stringutils
diff --git a/services/mediametrics/StringUtils.h b/services/mediametrics/StringUtils.h
new file mode 100644
index 0000000..d878720
--- /dev/null
+++ b/services/mediametrics/StringUtils.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 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 <string>
+#include <vector>
+
+namespace android::mediametrics::stringutils {
+
+/**
+ * Return string tokens from iterator, separated by spaces and reserved chars.
+ */
+std::string tokenizer(std::string::const_iterator& it,
+        const std::string::const_iterator& end, const char *reserved);
+
+/**
+ * Splits flags string based on delimeters (or, whitespace which is removed).
+ */
+std::vector<std::string> split(const std::string& flags, const char *delim);
+
+/**
+ * Parse the devices string and return a vector of device address pairs.
+ *
+ * A failure to parse returns early with the contents that were able to be parsed.
+ */
+std::vector<std::pair<std::string, std::string>> getDeviceAddressPairs(const std::string &devices);
+
+/**
+ * Replaces targetChars with replaceChar in string, returns number of chars replaced.
+ */
+size_t replace(std::string &str, const char *targetChars, const char replaceChar);
+
+} // namespace android::mediametrics::stringutils
diff --git a/services/mediametrics/TimeMachine.h b/services/mediametrics/TimeMachine.h
index c82778b..00a44a4 100644
--- a/services/mediametrics/TimeMachine.h
+++ b/services/mediametrics/TimeMachine.h
@@ -18,6 +18,7 @@
 
 #include <any>
 #include <map>
+#include <mutex>
 #include <sstream>
 #include <string>
 #include <variant>
@@ -81,6 +82,8 @@
             , mCreationTime(time)
             , mLastModificationTime(time)
         {
+            (void)mCreationTime; // suppress unused warning.
+
             // allowUid allows an untrusted client with a matching uid to set properties
             // in this key.
             // If allowUid == (uid_t)-1, no untrusted client may set properties in the key.
@@ -209,7 +212,7 @@
 
         const std::string mKey;
         const uid_t mAllowUid;
-        const int64_t mCreationTime __unused;
+        const int64_t mCreationTime;
 
         int64_t mLastModificationTime;
         std::map<std::string /* property */, PropertyHistory> mPropertyMap;
@@ -442,7 +445,7 @@
                 ++it) {
             if (ll <= 0) break;
             if (prefix != nullptr && !startsWith(it->first, prefix)) break;
-            std::lock_guard lock(getLockForKey(it->first));
+            std::lock_guard lock2(getLockForKey(it->first));
             auto [s, l] = it->second->dump(ll, sinceNs);
             ss << s;
             ll -= l;
diff --git a/services/mediametrics/iface_statsd.cpp b/services/mediametrics/iface_statsd.cpp
index 3a1eea7..6e51f72 100644
--- a/services/mediametrics/iface_statsd.cpp
+++ b/services/mediametrics/iface_statsd.cpp
@@ -71,7 +71,7 @@
 
 // give me a record, i'll look at the type and upload appropriately
 bool dump2Statsd(const std::shared_ptr<const mediametrics::Item>& item) {
-    if (item == NULL) return false;
+    if (item == nullptr) return false;
 
     // get the key
     std::string key = item->getKey();
diff --git a/services/mediametrics/main_mediametrics.cpp b/services/mediametrics/main_mediametrics.cpp
index 6992c32..3a66538 100644
--- a/services/mediametrics/main_mediametrics.cpp
+++ b/services/mediametrics/main_mediametrics.cpp
@@ -25,9 +25,9 @@
 #include <binder/ProcessState.h>
 #include <mediautils/LimitProcessMemory.h>
 
-int main(int argc __unused, char **argv __unused)
+int main(int argc __unused, char **argv)
 {
-    using namespace android;
+    using namespace android; // NOLINT (clang-tidy)
 
     limitProcessMemory(
         "media.metrics.maxmem", /* property that defines limit */
@@ -39,7 +39,8 @@
     // to match the service name
     // we're replacing "/system/bin/mediametrics" with "media.metrics"
     // we add a ".", but discard the path components: we finish with a shorter string
-    strcpy(argv[0], MediaMetricsService::kServiceName);
+    const size_t origSize = strlen(argv[0]) + 1; // include null termination.
+    strlcpy(argv[0], MediaMetricsService::kServiceName, origSize);
 
     defaultServiceManager()->addService(
             String16(MediaMetricsService::kServiceName), new MediaMetricsService());
diff --git a/services/mediametrics/statsd_audiopolicy.cpp b/services/mediametrics/statsd_audiopolicy.cpp
index 634c801..393c6ae 100644
--- a/services/mediametrics/statsd_audiopolicy.cpp
+++ b/services/mediametrics/statsd_audiopolicy.cpp
@@ -39,7 +39,7 @@
 
 bool statsd_audiopolicy(const mediametrics::Item *item)
 {
-    if (item == NULL) return false;
+    if (item == nullptr) return false;
 
     // these go into the statsd wrapper
     const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
@@ -122,4 +122,4 @@
     return true;
 }
 
-};
+} // namespace android
diff --git a/services/mediametrics/statsd_audiorecord.cpp b/services/mediametrics/statsd_audiorecord.cpp
index 69d1661..43feda1 100644
--- a/services/mediametrics/statsd_audiorecord.cpp
+++ b/services/mediametrics/statsd_audiorecord.cpp
@@ -39,7 +39,7 @@
 
 bool statsd_audiorecord(const mediametrics::Item *item)
 {
-    if (item == NULL) return false;
+    if (item == nullptr) return false;
 
     // these go into the statsd wrapper
     const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
@@ -155,4 +155,4 @@
     return true;
 }
 
-};
+} // namespace android
diff --git a/services/mediametrics/statsd_audiothread.cpp b/services/mediametrics/statsd_audiothread.cpp
index 300151b..e867f5b 100644
--- a/services/mediametrics/statsd_audiothread.cpp
+++ b/services/mediametrics/statsd_audiothread.cpp
@@ -39,7 +39,7 @@
 
 bool statsd_audiothread(const mediametrics::Item *item)
 {
-    if (item == NULL) return false;
+    if (item == nullptr) return false;
 
     // these go into the statsd wrapper
     const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
@@ -204,4 +204,4 @@
     return true;
 }
 
-};
+} // namespace android
diff --git a/services/mediametrics/statsd_audiotrack.cpp b/services/mediametrics/statsd_audiotrack.cpp
index 397cdf3..ee5b9b2 100644
--- a/services/mediametrics/statsd_audiotrack.cpp
+++ b/services/mediametrics/statsd_audiotrack.cpp
@@ -39,7 +39,7 @@
 
 bool statsd_audiotrack(const mediametrics::Item *item)
 {
-    if (item == NULL) return false;
+    if (item == nullptr) return false;
 
     // these go into the statsd wrapper
     const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
@@ -146,4 +146,4 @@
     return true;
 }
 
-};
+} // namespace android
diff --git a/services/mediametrics/statsd_codec.cpp b/services/mediametrics/statsd_codec.cpp
index f5fa57e..26eda79 100644
--- a/services/mediametrics/statsd_codec.cpp
+++ b/services/mediametrics/statsd_codec.cpp
@@ -39,7 +39,7 @@
 
 bool statsd_codec(const mediametrics::Item *item)
 {
-    if (item == NULL) return false;
+    if (item == nullptr) return false;
 
     // these go into the statsd wrapper
     const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
@@ -168,6 +168,28 @@
     }
     // android.media.mediacodec.latency.hist    NOT EMITTED
 
+#if 0
+    // TODO(b/139143194)
+    // can't send them to statsd until statsd proto updates merge
+    // but in the meantime, they can appear in local 'dumpsys media.metrics' output
+    //
+    // android.media.mediacodec.bitrate_mode string
+    std::string bitrate_mode;
+    if (item->getString("android.media.mediacodec.bitrate_mode", &bitrate_mode)) {
+        metrics_proto.set_bitrate_mode(std::move(bitrate_mode));
+    }
+    // android.media.mediacodec.bitrate int32
+    int32_t bitrate = -1;
+    if (item->getInt32("android.media.mediacodec.bitrate", &bitrate)) {
+        metrics_proto.set_bitrate(bitrate);
+    }
+    // android.media.mediacodec.lifetimeMs int64
+    int64_t lifetimeMs = -1;
+    if ( item->getInt64("android.media.mediacodec.lifetimeMs", &lifetimeMs)) {
+        metrics_proto.set_lifetime_millis(lifetimeMs);
+    }
+#endif
+
     std::string serialized;
     if (!metrics_proto.SerializeToString(&serialized)) {
         ALOGE("Failed to serialize codec metrics");
@@ -188,4 +210,4 @@
     return true;
 }
 
-};
+} // namespace android
diff --git a/services/mediametrics/statsd_drm.cpp b/services/mediametrics/statsd_drm.cpp
index 4f2e861..ac58929 100644
--- a/services/mediametrics/statsd_drm.cpp
+++ b/services/mediametrics/statsd_drm.cpp
@@ -43,67 +43,60 @@
 // mediadrm
 bool statsd_mediadrm(const mediametrics::Item *item)
 {
-    if (item == NULL) return false;
+    if (item == nullptr) return false;
 
     const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
     std::string pkgName = item->getPkgName();
     int64_t pkgVersionCode = item->getPkgVersionCode();
     int64_t mediaApexVersion = 0;
 
-    char *vendor = NULL;
-    (void) item->getCString("vendor", &vendor);
-    char *description = NULL;
-    (void) item->getCString("description", &description);
-    char *serialized_metrics = NULL;
-    (void) item->getCString("serialized_metrics", &serialized_metrics);
+    std::string vendor;
+    (void) item->getString("vendor", &vendor);
+    std::string description;
+    (void) item->getString("description", &description);
+    std::string serialized_metrics;
+    (void) item->getString("serialized_metrics", &serialized_metrics);
 
     if (enabled_statsd) {
-        android::util::BytesField bf_serialized(serialized_metrics ? serialized_metrics : NULL,
-                                                serialized_metrics ? strlen(serialized_metrics)
-                                                                   : 0);
+        android::util::BytesField bf_serialized(serialized_metrics.c_str(),
+                                                serialized_metrics.size());
         android::util::stats_write(android::util::MEDIAMETRICS_MEDIADRM_REPORTED,
                                    timestamp, pkgName.c_str(), pkgVersionCode,
                                    mediaApexVersion,
-                                   vendor, description,
+                                   vendor.c_str(),
+                                   description.c_str(),
                                    bf_serialized);
     } else {
-        ALOGV("NOT sending: mediadrm private data (len=%zu)",
-              serialized_metrics ? strlen(serialized_metrics) : 0);
+        ALOGV("NOT sending: mediadrm private data (len=%zu)", serialized_metrics.size());
     }
 
-    free(vendor);
-    free(description);
-    free(serialized_metrics);
     return true;
 }
 
 // widevineCDM
 bool statsd_widevineCDM(const mediametrics::Item *item)
 {
-    if (item == NULL) return false;
+    if (item == nullptr) return false;
 
     const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
     std::string pkgName = item->getPkgName();
     int64_t pkgVersionCode = item->getPkgVersionCode();
     int64_t mediaApexVersion = 0;
 
-    char *serialized_metrics = NULL;
-    (void) item->getCString("serialized_metrics", &serialized_metrics);
+    std::string serialized_metrics;
+    (void) item->getString("serialized_metrics", &serialized_metrics);
 
     if (enabled_statsd) {
-        android::util::BytesField bf_serialized(serialized_metrics ? serialized_metrics : NULL,
-                                                serialized_metrics ? strlen(serialized_metrics)
-                                                                   : 0);
+        android::util::BytesField bf_serialized(serialized_metrics.c_str(),
+                                                serialized_metrics.size());
         android::util::stats_write(android::util::MEDIAMETRICS_DRM_WIDEVINE_REPORTED,
                                    timestamp, pkgName.c_str(), pkgVersionCode,
                                    mediaApexVersion,
                                    bf_serialized);
     } else {
-        ALOGV("NOT sending: widevine private data (len=%zu)",
-              serialized_metrics ? strlen(serialized_metrics) : 0);
+        ALOGV("NOT sending: widevine private data (len=%zu)", serialized_metrics.size());
     }
 
-    free(serialized_metrics);
     return true;
 }
 
@@ -111,7 +104,7 @@
 bool statsd_drmmanager(const mediametrics::Item *item)
 {
     using namespace std::string_literals;
-    if (item == NULL) return false;
+    if (item == nullptr) return false;
 
     if (!enabled_statsd) {
         ALOGV("NOT sending: drmmanager data");
@@ -123,14 +116,14 @@
     int64_t pkgVersionCode = item->getPkgVersionCode();
     int64_t mediaApexVersion = 0;
 
-    char *plugin_id = NULL;
-    (void) item->getCString("plugin_id", &plugin_id);
-    char *description = NULL;
-    (void) item->getCString("description", &description);
+    std::string plugin_id;
+    (void) item->getString("plugin_id", &plugin_id);
+    std::string description;
+    (void) item->getString("description", &description);
     int32_t method_id = -1;
     (void) item->getInt32("method_id", &method_id);
-    char *mime_types = NULL;
-    (void) item->getCString("mime_types", &mime_types);
+    std::string mime_types;
+    (void) item->getString("mime_types", &mime_types);
 
     // Corresponds to the 13 APIs tracked in the MediametricsDrmManagerReported statsd proto
     // Please see also DrmManager::kMethodIdMap
@@ -141,16 +134,15 @@
 
     android::util::stats_write(android::util::MEDIAMETRICS_DRMMANAGER_REPORTED,
                                timestamp, pkgName.c_str(), pkgVersionCode, mediaApexVersion,
-                               plugin_id, description, method_id, mime_types,
+                               plugin_id.c_str(), description.c_str(),
+                               method_id, mime_types.c_str(),
                                methodCounts[0], methodCounts[1], methodCounts[2],
                                methodCounts[3], methodCounts[4], methodCounts[5],
                                methodCounts[6], methodCounts[7], methodCounts[8],
                                methodCounts[9], methodCounts[10], methodCounts[11],
                                methodCounts[12]);
 
-    free(plugin_id);
-    free(description);
-    free(mime_types);
     return true;
 }
+
 } // namespace android
diff --git a/services/mediametrics/statsd_extractor.cpp b/services/mediametrics/statsd_extractor.cpp
index 8574358..3d5739f 100644
--- a/services/mediametrics/statsd_extractor.cpp
+++ b/services/mediametrics/statsd_extractor.cpp
@@ -39,7 +39,7 @@
 
 bool statsd_extractor(const mediametrics::Item *item)
 {
-    if (item == NULL) return false;
+    if (item == nullptr) return false;
 
     // these go into the statsd wrapper
     const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
@@ -91,4 +91,4 @@
     return true;
 }
 
-};
+} // namespace android
diff --git a/services/mediametrics/statsd_nuplayer.cpp b/services/mediametrics/statsd_nuplayer.cpp
index df7e59f..488bdcb 100644
--- a/services/mediametrics/statsd_nuplayer.cpp
+++ b/services/mediametrics/statsd_nuplayer.cpp
@@ -43,7 +43,7 @@
  */
 bool statsd_nuplayer(const mediametrics::Item *item)
 {
-    if (item == NULL) return false;
+    if (item == nullptr) return false;
 
     // these go into the statsd wrapper
     const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
@@ -167,4 +167,4 @@
     return true;
 }
 
-};
+} // namespace android
diff --git a/services/mediametrics/statsd_recorder.cpp b/services/mediametrics/statsd_recorder.cpp
index 4de1746..6d5fca0 100644
--- a/services/mediametrics/statsd_recorder.cpp
+++ b/services/mediametrics/statsd_recorder.cpp
@@ -39,7 +39,7 @@
 
 bool statsd_recorder(const mediametrics::Item *item)
 {
-    if (item == NULL) return false;
+    if (item == nullptr) return false;
 
     // these go into the statsd wrapper
     const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
@@ -186,4 +186,4 @@
     return true;
 }
 
-};
+} // namespace android
diff --git a/services/mediametrics/tests/mediametrics_tests.cpp b/services/mediametrics/tests/mediametrics_tests.cpp
index f7988f1..7da6306 100644
--- a/services/mediametrics/tests/mediametrics_tests.cpp
+++ b/services/mediametrics/tests/mediametrics_tests.cpp
@@ -18,6 +18,7 @@
 #include <utils/Log.h>
 
 #include "MediaMetricsService.h"
+#include "StringUtils.h"
 
 #include <stdio.h>
 
@@ -884,12 +885,12 @@
 }
 
 TEST(mediametrics_tests, device_parsing) {
-    auto devaddr = android::MediaMetricsService::getDeviceAddressPairs("(DEVICE, )");
+    auto devaddr = android::mediametrics::stringutils::getDeviceAddressPairs("(DEVICE, )");
     ASSERT_EQ((size_t)1, devaddr.size());
     ASSERT_EQ("DEVICE", devaddr[0].first);
     ASSERT_EQ("", devaddr[0].second);
 
-    devaddr = android::MediaMetricsService::getDeviceAddressPairs(
+    devaddr = android::mediametrics::stringutils::getDeviceAddressPairs(
             "(DEVICE1, A)|(D, ADDRB)");
     ASSERT_EQ((size_t)2, devaddr.size());
     ASSERT_EQ("DEVICE1", devaddr[0].first);
@@ -897,7 +898,7 @@
     ASSERT_EQ("D", devaddr[1].first);
     ASSERT_EQ("ADDRB", devaddr[1].second);
 
-    devaddr = android::MediaMetricsService::getDeviceAddressPairs(
+    devaddr = android::mediametrics::stringutils::getDeviceAddressPairs(
             "(A,B)|(C,D)");
     ASSERT_EQ((size_t)2, devaddr.size());
     ASSERT_EQ("A", devaddr[0].first);
@@ -905,7 +906,7 @@
     ASSERT_EQ("C", devaddr[1].first);
     ASSERT_EQ("D", devaddr[1].second);
 
-    devaddr = android::MediaMetricsService::getDeviceAddressPairs(
+    devaddr = android::mediametrics::stringutils::getDeviceAddressPairs(
             "  ( A1 , B )  | ( C , D2 )  ");
     ASSERT_EQ((size_t)2, devaddr.size());
     ASSERT_EQ("A1", devaddr[0].first);
diff --git a/services/minijail/Android.bp b/services/minijail/Android.bp
index 5ea6d1e..b057968 100644
--- a/services/minijail/Android.bp
+++ b/services/minijail/Android.bp
@@ -18,6 +18,7 @@
     name: "libavservices_minijail",
     defaults: ["libavservices_minijail_defaults"],
     vendor_available: true,
+    min_sdk_version: "29",
     export_include_dirs: ["."],
 }
 
diff --git a/services/oboeservice/AAudioClientTracker.cpp b/services/oboeservice/AAudioClientTracker.cpp
index 6e14434..9d9ca63 100644
--- a/services/oboeservice/AAudioClientTracker.cpp
+++ b/services/oboeservice/AAudioClientTracker.cpp
@@ -106,18 +106,9 @@
 
 aaudio_result_t
 AAudioClientTracker::registerClientStream(pid_t pid, sp<AAudioServiceStreamBase> serviceStream) {
-    aaudio_result_t result = AAUDIO_OK;
     ALOGV("registerClientStream(%d,)\n", pid);
     std::lock_guard<std::mutex> lock(mLock);
-    sp<NotificationClient> notificationClient = mNotificationClients[pid];
-    if (notificationClient == 0) {
-        // This will get called the first time the audio server registers an internal stream.
-        ALOGV("registerClientStream(%d,) unrecognized pid\n", pid);
-        notificationClient = new NotificationClient(pid, nullptr);
-        mNotificationClients[pid] = notificationClient;
-    }
-    notificationClient->registerClientStream(serviceStream);
-    return result;
+    return getNotificationClient_l(pid)->registerClientStream(serviceStream);
 }
 
 // Find the tracker for this process and remove it.
@@ -136,6 +127,33 @@
     return AAUDIO_OK;
 }
 
+void AAudioClientTracker::setExclusiveEnabled(pid_t pid, bool enabled) {
+    ALOGD("%s(%d, %d)\n", __func__, pid, enabled);
+    std::lock_guard<std::mutex> lock(mLock);
+    getNotificationClient_l(pid)->setExclusiveEnabled(enabled);
+}
+
+bool AAudioClientTracker::isExclusiveEnabled(pid_t pid) {
+    std::lock_guard<std::mutex> lock(mLock);
+    return getNotificationClient_l(pid)->isExclusiveEnabled();
+}
+
+sp<AAudioClientTracker::NotificationClient>
+        AAudioClientTracker::getNotificationClient_l(pid_t pid) {
+    sp<NotificationClient> notificationClient = mNotificationClients[pid];
+    if (notificationClient == nullptr) {
+        // This will get called the first time the audio server uses this PID.
+        ALOGV("%s(%d,) unrecognized PID\n", __func__, pid);
+        notificationClient = new AAudioClientTracker::NotificationClient(pid, nullptr);
+        mNotificationClients[pid] = notificationClient;
+    }
+    return notificationClient;
+}
+
+// =======================================
+// AAudioClientTracker::NotificationClient
+// =======================================
+
 AAudioClientTracker::NotificationClient::NotificationClient(pid_t pid, const sp<IBinder>& binder)
         : mProcessId(pid), mBinder(binder) {
 }
diff --git a/services/oboeservice/AAudioClientTracker.h b/services/oboeservice/AAudioClientTracker.h
index 00ff467..943b809 100644
--- a/services/oboeservice/AAudioClientTracker.h
+++ b/services/oboeservice/AAudioClientTracker.h
@@ -58,6 +58,15 @@
     aaudio_result_t unregisterClientStream(pid_t pid,
                                            android::sp<AAudioServiceStreamBase> serviceStream);
 
+    /**
+     * Specify whether a process is allowed to create an EXCLUSIVE MMAP stream.
+     * @param pid
+     * @param enabled
+     */
+    void setExclusiveEnabled(pid_t pid, bool enabled);
+
+    bool isExclusiveEnabled(pid_t pid);
+
     android::AAudioService *getAAudioService() const {
         return mAAudioService;
     }
@@ -84,17 +93,29 @@
 
         aaudio_result_t unregisterClientStream(android::sp<AAudioServiceStreamBase> serviceStream);
 
+        void setExclusiveEnabled(bool enabled) {
+            mExclusiveEnabled = enabled;
+        }
+
+        bool isExclusiveEnabled() {
+            return mExclusiveEnabled;
+        }
+
         // IBinder::DeathRecipient
         virtual     void    binderDied(const android::wp<IBinder>& who);
 
-    protected:
+    private:
         mutable std::mutex                              mLock;
         const pid_t                                     mProcessId;
         std::set<android::sp<AAudioServiceStreamBase>>  mStreams;
         // hold onto binder to receive death notifications
         android::sp<IBinder>                            mBinder;
+        bool                                            mExclusiveEnabled = true;
     };
 
+    // This must be called under mLock
+    android::sp<NotificationClient> getNotificationClient_l(pid_t pid);
+
     mutable std::mutex                               mLock;
     std::map<pid_t, android::sp<NotificationClient>> mNotificationClients;
     android::AAudioService                          *mAAudioService = nullptr;
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index c9bf72f..9f34153 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -25,6 +25,7 @@
 #include <sstream>
 #include <utility/AAudioUtilities.h>
 
+#include "AAudioClientTracker.h"
 #include "AAudioEndpointManager.h"
 #include "AAudioServiceEndpointShared.h"
 #include "AAudioServiceEndpointMMAP.h"
@@ -174,7 +175,15 @@
                 && !request.isSharingModeMatchRequired()) { // app did not request a shared stream
             ALOGD("%s() endpoint in EXCLUSIVE use. Steal it!", __func__);
             mExclusiveStolenCount++;
-            endpointToSteal = endpoint;
+            // Prevent this process from getting another EXCLUSIVE stream.
+            // This will prevent two clients from colliding after a DISCONNECTION
+            // when they both try to open an exclusive stream at the same time.
+            // That can result in a stream getting disconnected between the OPEN
+            // and START calls. This will help preserve app compatibility.
+            // An app can avoid having this happen by closing their streams when
+            // the app is paused.
+            AAudioClientTracker::getInstance().setExclusiveEnabled(request.getProcessId(), false);
+            endpointToSteal = endpoint; // return it to caller
         }
         return nullptr;
     } else {
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index ecbcb7e..22cdb35 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -23,7 +23,6 @@
 #include <sstream>
 
 #include <aaudio/AAudio.h>
-#include <mediautils/SchedulingPolicyService.h>
 #include <mediautils/ServiceUtilities.h>
 #include <utils/String16.h>
 
@@ -118,7 +117,8 @@
         return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
     }
 
-    if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) {
+    if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE
+        && AAudioClientTracker::getInstance().isExclusiveEnabled(request.getProcessId())) {
         // only trust audioserver for in service indication
         bool inService = false;
         if (isCallerInService()) {
@@ -162,28 +162,6 @@
     }
 }
 
-// If a close request is pending then close the stream
-bool AAudioService::releaseStream(const sp<AAudioServiceStreamBase> &serviceStream) {
-    bool closed = false;
-    // decrementAndRemoveStreamByHandle() uses a lock so that if there are two simultaneous closes
-    // then only one will get the pointer and do the close.
-    sp<AAudioServiceStreamBase> foundStream = mStreamTracker.decrementAndRemoveStreamByHandle(
-            serviceStream->getHandle());
-    if (foundStream.get() != nullptr) {
-        foundStream->close();
-        pid_t pid = foundStream->getOwnerProcessId();
-        AAudioClientTracker::getInstance().unregisterClientStream(pid, foundStream);
-        closed = true;
-    }
-    return closed;
-}
-
-aaudio_result_t AAudioService::checkForPendingClose(
-        const sp<AAudioServiceStreamBase> &serviceStream,
-        aaudio_result_t defaultResult) {
-    return releaseStream(serviceStream) ? AAUDIO_ERROR_INVALID_STATE : defaultResult;
-}
-
 aaudio_result_t AAudioService::closeStream(aaudio_handle_t streamHandle) {
     // Check permission and ownership first.
     sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
@@ -195,17 +173,20 @@
 }
 
 aaudio_result_t AAudioService::closeStream(sp<AAudioServiceStreamBase> serviceStream) {
+    // This is protected by a lock in AAudioClientTracker.
+    // It is safe to unregister the same stream twice.
     pid_t pid = serviceStream->getOwnerProcessId();
     AAudioClientTracker::getInstance().unregisterClientStream(pid, serviceStream);
+    // This is protected by a lock in mStreamTracker.
+    // It is safe to remove the same stream twice.
+    mStreamTracker.removeStreamByHandle(serviceStream->getHandle());
 
-    serviceStream->markCloseNeeded();
-    (void) releaseStream(serviceStream);
-    return AAUDIO_OK;
+    return serviceStream->close();
 }
 
 sp<AAudioServiceStreamBase> AAudioService::convertHandleToServiceStream(
         aaudio_handle_t streamHandle) {
-    sp<AAudioServiceStreamBase> serviceStream = mStreamTracker.getStreamByHandleAndIncrement(
+    sp<AAudioServiceStreamBase> serviceStream = mStreamTracker.getStreamByHandle(
             streamHandle);
     if (serviceStream.get() != nullptr) {
         // Only allow owner or the aaudio service to access the stream.
@@ -218,8 +199,6 @@
         if (!allowed) {
             ALOGE("AAudioService: calling uid %d cannot access stream 0x%08X owned by %d",
                   callingUserId, streamHandle, ownerUserId);
-            // We incremented the reference count so we must check if it needs to be closed.
-            checkForPendingClose(serviceStream, AAUDIO_OK);
             serviceStream.clear();
         }
     }
@@ -234,96 +213,66 @@
         ALOGE("getStreamDescription(), illegal stream handle = 0x%0x", streamHandle);
         return AAUDIO_ERROR_INVALID_HANDLE;
     }
-
-    aaudio_result_t result = serviceStream->getDescription(parcelable);
-    // parcelable.dump();
-    return checkForPendingClose(serviceStream, result);
+    return serviceStream->getDescription(parcelable);
 }
 
 aaudio_result_t AAudioService::startStream(aaudio_handle_t streamHandle) {
     sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
     if (serviceStream.get() == nullptr) {
-        ALOGE("startStream(), illegal stream handle = 0x%0x", streamHandle);
+        ALOGW("%s(), invalid streamHandle = 0x%0x", __func__, streamHandle);
         return AAUDIO_ERROR_INVALID_HANDLE;
     }
-
-    aaudio_result_t result = serviceStream->start();
-    return checkForPendingClose(serviceStream, result);
+    return serviceStream->start();
 }
 
 aaudio_result_t AAudioService::pauseStream(aaudio_handle_t streamHandle) {
     sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
     if (serviceStream.get() == nullptr) {
-        ALOGE("pauseStream(), illegal stream handle = 0x%0x", streamHandle);
+        ALOGW("%s(), invalid streamHandle = 0x%0x", __func__, streamHandle);
         return AAUDIO_ERROR_INVALID_HANDLE;
     }
-    aaudio_result_t result = serviceStream->pause();
-    return checkForPendingClose(serviceStream, result);
+    return serviceStream->pause();
 }
 
 aaudio_result_t AAudioService::stopStream(aaudio_handle_t streamHandle) {
     sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
     if (serviceStream.get() == nullptr) {
-        ALOGE("stopStream(), illegal stream handle = 0x%0x", streamHandle);
+        ALOGW("%s(), invalid streamHandle = 0x%0x", __func__, streamHandle);
         return AAUDIO_ERROR_INVALID_HANDLE;
     }
-    aaudio_result_t result = serviceStream->stop();
-    return checkForPendingClose(serviceStream, result);
+    return serviceStream->stop();
 }
 
 aaudio_result_t AAudioService::flushStream(aaudio_handle_t streamHandle) {
     sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
     if (serviceStream.get() == nullptr) {
-        ALOGE("flushStream(), illegal stream handle = 0x%0x", streamHandle);
+        ALOGW("%s(), invalid streamHandle = 0x%0x", __func__, streamHandle);
         return AAUDIO_ERROR_INVALID_HANDLE;
     }
-    aaudio_result_t result = serviceStream->flush();
-    return checkForPendingClose(serviceStream, result);
+    return serviceStream->flush();
 }
 
 aaudio_result_t AAudioService::registerAudioThread(aaudio_handle_t streamHandle,
                                                    pid_t clientThreadId,
-                                                   int64_t periodNanoseconds) {
-    aaudio_result_t result = AAUDIO_OK;
+                                                   int64_t /* periodNanoseconds */) {
     sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
     if (serviceStream.get() == nullptr) {
-        ALOGE("registerAudioThread(), illegal stream handle = 0x%0x", streamHandle);
+        ALOGW("%s(), invalid streamHandle = 0x%0x", __func__, streamHandle);
         return AAUDIO_ERROR_INVALID_HANDLE;
     }
-    if (serviceStream->getRegisteredThread() != AAudioServiceStreamBase::ILLEGAL_THREAD_ID) {
-        ALOGE("AAudioService::registerAudioThread(), thread already registered");
-        result = AAUDIO_ERROR_INVALID_STATE;
-    } else {
-        const pid_t ownerPid = IPCThreadState::self()->getCallingPid(); // TODO review
-        int32_t priority = isCallerInService()
-            ? kRealTimeAudioPriorityService : kRealTimeAudioPriorityClient;
-        serviceStream->setRegisteredThread(clientThreadId);
-        int err = android::requestPriority(ownerPid, clientThreadId,
-                                           priority, true /* isForApp */);
-        if (err != 0) {
-            ALOGE("AAudioService::registerAudioThread(%d) failed, errno = %d, priority = %d",
-                  clientThreadId, errno, priority);
-            result = AAUDIO_ERROR_INTERNAL;
-        }
-    }
-    return checkForPendingClose(serviceStream, result);
+    int32_t priority = isCallerInService()
+        ? kRealTimeAudioPriorityService : kRealTimeAudioPriorityClient;
+    return serviceStream->registerAudioThread(clientThreadId, priority);
 }
 
 aaudio_result_t AAudioService::unregisterAudioThread(aaudio_handle_t streamHandle,
                                                      pid_t clientThreadId) {
-    aaudio_result_t result = AAUDIO_OK;
     sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
     if (serviceStream.get() == nullptr) {
-        ALOGE("%s(), illegal stream handle = 0x%0x", __func__, streamHandle);
+        ALOGW("%s(), invalid streamHandle = 0x%0x", __func__, streamHandle);
         return AAUDIO_ERROR_INVALID_HANDLE;
     }
-    if (serviceStream->getRegisteredThread() != clientThreadId) {
-        ALOGE("%s(), wrong thread", __func__);
-        result = AAUDIO_ERROR_ILLEGAL_ARGUMENT;
-    } else {
-        serviceStream->setRegisteredThread(0);
-    }
-    return checkForPendingClose(serviceStream, result);
+    return serviceStream->unregisterAudioThread(clientThreadId);
 }
 
 aaudio_result_t AAudioService::startClient(aaudio_handle_t streamHandle,
@@ -332,22 +281,20 @@
                                            audio_port_handle_t *clientHandle) {
     sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
     if (serviceStream.get() == nullptr) {
-        ALOGE("%s(), illegal stream handle = 0x%0x", __func__, streamHandle);
+        ALOGW("%s(), invalid streamHandle = 0x%0x", __func__, streamHandle);
         return AAUDIO_ERROR_INVALID_HANDLE;
     }
-    aaudio_result_t result = serviceStream->startClient(client, attr, clientHandle);
-    return checkForPendingClose(serviceStream, result);
+    return serviceStream->startClient(client, attr, clientHandle);
 }
 
 aaudio_result_t AAudioService::stopClient(aaudio_handle_t streamHandle,
                                           audio_port_handle_t portHandle) {
     sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
     if (serviceStream.get() == nullptr) {
-        ALOGE("%s(), illegal stream handle = 0x%0x", __func__, streamHandle);
+        ALOGW("%s(), invalid streamHandle = 0x%0x", __func__, streamHandle);
         return AAUDIO_ERROR_INVALID_HANDLE;
     }
-    aaudio_result_t result = serviceStream->stopClient(portHandle);
-    return checkForPendingClose(serviceStream, result);
+    return serviceStream->stopClient(portHandle);
 }
 
 // This is only called internally when AudioFlinger wants to tear down a stream.
@@ -355,12 +302,13 @@
 aaudio_result_t AAudioService::disconnectStreamByPortHandle(audio_port_handle_t portHandle) {
     ALOGD("%s(%d) called", __func__, portHandle);
     sp<AAudioServiceStreamBase> serviceStream =
-            mStreamTracker.findStreamByPortHandleAndIncrement(portHandle);
+            mStreamTracker.findStreamByPortHandle(portHandle);
     if (serviceStream.get() == nullptr) {
         ALOGE("%s(), could not find stream with portHandle = %d", __func__, portHandle);
         return AAUDIO_ERROR_INVALID_HANDLE;
     }
+    // This is protected by a lock and will just return if already stopped.
     aaudio_result_t result = serviceStream->stop();
     serviceStream->disconnect();
-    return checkForPendingClose(serviceStream, result);
+    return result;
 }
diff --git a/services/oboeservice/AAudioService.h b/services/oboeservice/AAudioService.h
index 6a2ac1f..caf48a5 100644
--- a/services/oboeservice/AAudioService.h
+++ b/services/oboeservice/AAudioService.h
@@ -114,11 +114,6 @@
     sp<aaudio::AAudioServiceStreamBase> convertHandleToServiceStream(
             aaudio::aaudio_handle_t streamHandle);
 
-    bool releaseStream(const sp<aaudio::AAudioServiceStreamBase> &serviceStream);
-
-    aaudio_result_t checkForPendingClose(const sp<aaudio::AAudioServiceStreamBase> &serviceStream,
-                                         aaudio_result_t defaultResult);
-
     android::AudioClient            mAudioClient;
 
     aaudio::AAudioStreamTracker     mStreamTracker;
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index b09cbf4..ceefe93 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -90,14 +90,23 @@
 std::vector<android::sp<AAudioServiceStreamBase>>
         AAudioServiceEndpoint::disconnectRegisteredStreams() {
     std::vector<android::sp<AAudioServiceStreamBase>> streamsDisconnected;
-    std::lock_guard<std::mutex> lock(mLockStreams);
+    {
+        std::lock_guard<std::mutex> lock(mLockStreams);
+        mRegisteredStreams.swap(streamsDisconnected);
+    }
     mConnected.store(false);
-    for (const auto &stream : mRegisteredStreams) {
-        ALOGD("%s() - stop and disconnect port %d", __func__, stream->getPortHandle());
+    // We need to stop all the streams before we disconnect them.
+    // Otherwise there is a race condition where the first disconnected app
+    // tries to reopen a stream as MMAP but is blocked by the second stream,
+    // which hasn't stopped yet. Then the first app ends up with a Legacy stream.
+    for (const auto &stream : streamsDisconnected) {
+        ALOGD("%s() - stop(), port = %d", __func__, stream->getPortHandle());
         stream->stop();
+    }
+    for (const auto &stream : streamsDisconnected) {
+        ALOGD("%s() - disconnect(), port = %d", __func__, stream->getPortHandle());
         stream->disconnect();
     }
-    mRegisteredStreams.swap(streamsDisconnected);
     return streamsDisconnected;
 }
 
diff --git a/services/oboeservice/AAudioServiceEndpointShared.cpp b/services/oboeservice/AAudioServiceEndpointShared.cpp
index 21253c8..dc21886 100644
--- a/services/oboeservice/AAudioServiceEndpointShared.cpp
+++ b/services/oboeservice/AAudioServiceEndpointShared.cpp
@@ -168,13 +168,11 @@
 
 aaudio_result_t AAudioServiceEndpointShared::stopStream(sp<AAudioServiceStreamBase> sharedStream,
                                                         audio_port_handle_t clientHandle) {
-    // Don't lock here because the disconnectRegisteredStreams also uses the lock.
-
     // Ignore result.
     (void) getStreamInternal()->stopClient(clientHandle);
 
     if (--mRunningStreamCount == 0) { // atomic
-        stopSharingThread();
+        stopSharingThread(); // the sharing thread locks mLockStreams
         getStreamInternal()->requestStop();
     }
     return AAUDIO_OK;
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 531bfa1..663dae2 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -24,6 +24,7 @@
 
 #include <media/MediaMetricsItem.h>
 #include <media/TypeConverter.h>
+#include <mediautils/SchedulingPolicyService.h>
 
 #include "binding/IAAudioService.h"
 #include "binding/AAudioServiceMessage.h"
@@ -169,11 +170,16 @@
 }
 
 aaudio_result_t AAudioServiceStreamBase::close() {
+    std::lock_guard<std::mutex> lock(mLock);
+    return close_l();
+}
+
+aaudio_result_t AAudioServiceStreamBase::close_l() {
     if (getState() == AAUDIO_STREAM_STATE_CLOSED) {
         return AAUDIO_OK;
     }
 
-    stop();
+    stop_l();
 
     aaudio_result_t result = AAUDIO_OK;
     sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
@@ -185,7 +191,7 @@
         endpointManager.closeEndpoint(endpoint);
 
         // AAudioService::closeStream() prevents two threads from closing at the same time.
-        mServiceEndpoint.clear(); // endpoint will hold the pointer until this method returns.
+        mServiceEndpoint.clear(); // endpoint will hold the pointer after this method returns.
     }
 
     {
@@ -219,9 +225,18 @@
  * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
  */
 aaudio_result_t AAudioServiceStreamBase::start() {
+    std::lock_guard<std::mutex> lock(mLock);
+
     const int64_t beginNs = AudioClock::getNanoseconds();
     aaudio_result_t result = AAUDIO_OK;
 
+    if (auto state = getState();
+        state == AAUDIO_STREAM_STATE_CLOSED || state == AAUDIO_STREAM_STATE_DISCONNECTED) {
+        ALOGW("%s() already CLOSED, returns INVALID_STATE, handle = %d",
+                __func__, getHandle());
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+
     mediametrics::Defer defer([&] {
         mediametrics::LogItem(mMetricsId)
             .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_START)
@@ -231,7 +246,7 @@
             .record(); });
 
     if (isRunning()) {
-        return AAUDIO_OK;
+        return result;
     }
 
     setFlowing(false);
@@ -254,16 +269,21 @@
     return result;
 
 error:
-    disconnect();
+    disconnect_l();
     return result;
 }
 
 aaudio_result_t AAudioServiceStreamBase::pause() {
-    const int64_t beginNs = AudioClock::getNanoseconds();
+    std::lock_guard<std::mutex> lock(mLock);
+    return pause_l();
+}
+
+aaudio_result_t AAudioServiceStreamBase::pause_l() {
     aaudio_result_t result = AAUDIO_OK;
     if (!isRunning()) {
         return result;
     }
+    const int64_t beginNs = AudioClock::getNanoseconds();
 
     mediametrics::Defer defer([&] {
         mediametrics::LogItem(mMetricsId)
@@ -279,7 +299,7 @@
 
     result = stopTimestampThread();
     if (result != AAUDIO_OK) {
-        disconnect();
+        disconnect_l();
         return result;
     }
 
@@ -292,7 +312,7 @@
     result = endpoint->stopStream(this, mClientHandle);
     if (result != AAUDIO_OK) {
         ALOGE("%s() mServiceEndpoint returned %d, %s", __func__, result, getTypeText());
-        disconnect(); // TODO should we return or pause Base first?
+        disconnect_l(); // TODO should we return or pause Base first?
     }
 
     sendServiceEvent(AAUDIO_SERVICE_EVENT_PAUSED);
@@ -301,11 +321,16 @@
 }
 
 aaudio_result_t AAudioServiceStreamBase::stop() {
-    const int64_t beginNs = AudioClock::getNanoseconds();
+    std::lock_guard<std::mutex> lock(mLock);
+    return stop_l();
+}
+
+aaudio_result_t AAudioServiceStreamBase::stop_l() {
     aaudio_result_t result = AAUDIO_OK;
     if (!isRunning()) {
         return result;
     }
+    const int64_t beginNs = AudioClock::getNanoseconds();
 
     mediametrics::Defer defer([&] {
         mediametrics::LogItem(mMetricsId)
@@ -322,7 +347,7 @@
     sendCurrentTimestamp(); // warning - this calls a virtual function
     result = stopTimestampThread();
     if (result != AAUDIO_OK) {
-        disconnect();
+        disconnect_l();
         return result;
     }
 
@@ -336,7 +361,7 @@
     result = endpoint->stopStream(this, mClientHandle);
     if (result != AAUDIO_OK) {
         ALOGE("%s() stopStream returned %d, %s", __func__, result, getTypeText());
-        disconnect();
+        disconnect_l();
         // TODO what to do with result here?
     }
 
@@ -355,11 +380,12 @@
 }
 
 aaudio_result_t AAudioServiceStreamBase::flush() {
-    const int64_t beginNs = AudioClock::getNanoseconds();
+    std::lock_guard<std::mutex> lock(mLock);
     aaudio_result_t result = AAudio_isFlushAllowed(getState());
     if (result != AAUDIO_OK) {
         return result;
     }
+    const int64_t beginNs = AudioClock::getNanoseconds();
 
     mediametrics::Defer defer([&] {
         mediametrics::LogItem(mMetricsId)
@@ -404,16 +430,66 @@
 }
 
 void AAudioServiceStreamBase::disconnect() {
-    if (getState() != AAUDIO_STREAM_STATE_DISCONNECTED) {
+    std::lock_guard<std::mutex> lock(mLock);
+    disconnect_l();
+}
+
+void AAudioServiceStreamBase::disconnect_l() {
+    if (getState() != AAUDIO_STREAM_STATE_DISCONNECTED
+        && getState() != AAUDIO_STREAM_STATE_CLOSED) {
+
         mediametrics::LogItem(mMetricsId)
             .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DISCONNECT)
             .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState()))
             .record();
+
         sendServiceEvent(AAUDIO_SERVICE_EVENT_DISCONNECTED);
         setState(AAUDIO_STREAM_STATE_DISCONNECTED);
     }
 }
 
+aaudio_result_t AAudioServiceStreamBase::registerAudioThread(pid_t clientThreadId,
+        int priority) {
+    std::lock_guard<std::mutex> lock(mLock);
+    aaudio_result_t result = AAUDIO_OK;
+    if (getRegisteredThread() != AAudioServiceStreamBase::ILLEGAL_THREAD_ID) {
+        ALOGE("AAudioService::registerAudioThread(), thread already registered");
+        result = AAUDIO_ERROR_INVALID_STATE;
+    } else {
+        const pid_t ownerPid = IPCThreadState::self()->getCallingPid(); // TODO review
+        setRegisteredThread(clientThreadId);
+        int err = android::requestPriority(ownerPid, clientThreadId,
+                                           priority, true /* isForApp */);
+        if (err != 0) {
+            ALOGE("AAudioService::registerAudioThread(%d) failed, errno = %d, priority = %d",
+                  clientThreadId, errno, priority);
+            result = AAUDIO_ERROR_INTERNAL;
+        }
+    }
+    return result;
+}
+
+aaudio_result_t AAudioServiceStreamBase::unregisterAudioThread(pid_t clientThreadId) {
+    std::lock_guard<std::mutex> lock(mLock);
+    aaudio_result_t result = AAUDIO_OK;
+    if (getRegisteredThread() != clientThreadId) {
+        ALOGE("%s(), wrong thread", __func__);
+        result = AAUDIO_ERROR_ILLEGAL_ARGUMENT;
+    } else {
+        setRegisteredThread(0);
+    }
+    return result;
+}
+
+void AAudioServiceStreamBase::setState(aaudio_stream_state_t state) {
+    // CLOSED is a final state.
+    if (mState != AAUDIO_STREAM_STATE_CLOSED) {
+        mState = state;
+    } else {
+        ALOGW_IF(mState != state, "%s(%d) when already CLOSED", __func__, state);
+    }
+}
+
 aaudio_result_t AAudioServiceStreamBase::sendServiceEvent(aaudio_service_event_t event,
                                                           double  dataDouble) {
     AAudioServiceMessage command;
@@ -511,6 +587,7 @@
  * used to communicate with the underlying HAL or Service.
  */
 aaudio_result_t AAudioServiceStreamBase::getDescription(AudioEndpointParcelable &parcelable) {
+    std::lock_guard<std::mutex> lock(mLock);
     {
         std::lock_guard<std::mutex> lock(mUpMessageQueueLock);
         if (mUpMessageQueue == nullptr) {
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index 79dd738..94cc980 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -77,7 +77,7 @@
     // because we had to wait until we generated the handle.
     void logOpen(aaudio_handle_t streamHandle);
 
-    virtual aaudio_result_t close();
+    aaudio_result_t close();
 
     /**
      * Start the flow of audio data.
@@ -85,7 +85,7 @@
      * This is not guaranteed to be synchronous but it currently is.
      * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
      */
-    virtual aaudio_result_t start();
+    aaudio_result_t start();
 
     /**
      * Stop the flow of data so that start() can resume without loss of data.
@@ -93,7 +93,7 @@
      * This is not guaranteed to be synchronous but it currently is.
      * An AAUDIO_SERVICE_EVENT_PAUSED will be sent to the client when complete.
     */
-    virtual aaudio_result_t pause();
+    aaudio_result_t pause();
 
     /**
      * Stop the flow of data after the currently queued data has finished playing.
@@ -102,17 +102,14 @@
      * An AAUDIO_SERVICE_EVENT_STOPPED will be sent to the client when complete.
      *
      */
-    virtual aaudio_result_t stop();
-
-    aaudio_result_t stopTimestampThread();
+    aaudio_result_t stop();
 
     /**
      * Discard any data held by the underlying HAL or Service.
      *
      * An AAUDIO_SERVICE_EVENT_FLUSHED will be sent to the client when complete.
      */
-    virtual aaudio_result_t flush();
-
+    aaudio_result_t flush();
 
     virtual aaudio_result_t startClient(const android::AudioClient& client,
                                         const audio_attributes_t *attr __unused,
@@ -126,29 +123,19 @@
         return AAUDIO_ERROR_UNAVAILABLE;
     }
 
+    aaudio_result_t registerAudioThread(pid_t clientThreadId, int priority);
+
+    aaudio_result_t unregisterAudioThread(pid_t clientThreadId);
+
     bool isRunning() const {
         return mState == AAUDIO_STREAM_STATE_STARTED;
     }
 
-    // -------------------------------------------------------------------
-
-    /**
-     * Send a message to the client with an int64_t data value.
-     */
-    aaudio_result_t sendServiceEvent(aaudio_service_event_t event,
-                                     int64_t dataLong = 0);
-    /**
-     * Send a message to the client with an double data value.
-     */
-    aaudio_result_t sendServiceEvent(aaudio_service_event_t event,
-                                     double  dataDouble);
-
     /**
      * Fill in a parcelable description of stream.
      */
     aaudio_result_t getDescription(AudioEndpointParcelable &parcelable);
 
-
     void setRegisteredThread(pid_t pid) {
         mRegisteredClientThread = pid;
     }
@@ -262,9 +249,13 @@
     aaudio_result_t open(const aaudio::AAudioStreamRequest &request,
                          aaudio_sharing_mode_t sharingMode);
 
-    void setState(aaudio_stream_state_t state) {
-        mState = state;
-    }
+    // These must be called under mLock
+    virtual aaudio_result_t close_l();
+    virtual aaudio_result_t pause_l();
+    virtual aaudio_result_t stop_l();
+    void disconnect_l();
+
+    void setState(aaudio_stream_state_t state);
 
     /**
      * Device specific startup.
@@ -319,6 +310,19 @@
 
 private:
 
+    aaudio_result_t stopTimestampThread();
+
+    /**
+     * Send a message to the client with an int64_t data value.
+     */
+    aaudio_result_t sendServiceEvent(aaudio_service_event_t event,
+                                     int64_t dataLong = 0);
+    /**
+     * Send a message to the client with a double data value.
+     */
+    aaudio_result_t sendServiceEvent(aaudio_service_event_t event,
+                                     double dataDouble);
+
     /**
      * @return true if the queue is getting full.
      */
@@ -336,6 +340,10 @@
     // This indicate that a running stream should not be processed because of an error,
     // for example a full message queue. Note that this atomic is unrelated to mCloseNeeded.
     std::atomic<bool>       mSuspended{false};
+
+    // Locking order is important.
+    // Always acquire mLock before acquiring AAudioServiceEndpoint::mLockStreams
+    std::mutex              mLock; // Prevent start/stop/close etcetera from colliding
 };
 
 } /* namespace aaudio */
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 639a0a8..54d7d06 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -92,11 +92,11 @@
 }
 
 // Stop the flow of data such that start() can resume with loss of data.
-aaudio_result_t AAudioServiceStreamMMAP::pause() {
+aaudio_result_t AAudioServiceStreamMMAP::pause_l() {
     if (!isRunning()) {
         return AAUDIO_OK;
     }
-    aaudio_result_t result = AAudioServiceStreamBase::pause();
+    aaudio_result_t result = AAudioServiceStreamBase::pause_l();
     // TODO put before base::pause()?
     if (!mInService) {
         (void) stopClient(mClientHandle);
@@ -104,11 +104,11 @@
     return result;
 }
 
-aaudio_result_t AAudioServiceStreamMMAP::stop() {
+aaudio_result_t AAudioServiceStreamMMAP::stop_l() {
     if (!isRunning()) {
         return AAUDIO_OK;
     }
-    aaudio_result_t result = AAudioServiceStreamBase::stop();
+    aaudio_result_t result = AAudioServiceStreamBase::stop_l();
     // TODO put before base::stop()?
     if (!mInService) {
         (void) stopClient(mClientHandle);
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index 9105469..5902613 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -52,16 +52,6 @@
 
     aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
 
-    /**
-     * Stop the flow of data so that start() can resume without loss of data.
-     *
-     * This is not guaranteed to be synchronous but it currently is.
-     * An AAUDIO_SERVICE_EVENT_PAUSED will be sent to the client when complete.
-    */
-    aaudio_result_t pause() override;
-
-    aaudio_result_t stop() override;
-
     aaudio_result_t startClient(const android::AudioClient& client,
                                 const audio_attributes_t *attr,
                                 audio_port_handle_t *clientHandle) override;
@@ -72,6 +62,16 @@
 
 protected:
 
+    /**
+     * Stop the flow of data so that start() can resume without loss of data.
+     *
+     * This is not guaranteed to be synchronous but it currently is.
+     * An AAUDIO_SERVICE_EVENT_PAUSED will be sent to the client when complete.
+    */
+    aaudio_result_t pause_l() override;
+
+    aaudio_result_t stop_l() override;
+
     aaudio_result_t getAudioDataDescription(AudioEndpointParcelable &parcelable) override;
 
     aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) override;
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index 2ca847a..01b1c2e 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -203,9 +203,8 @@
     return result;
 }
 
-
-aaudio_result_t AAudioServiceStreamShared::close()  {
-    aaudio_result_t result = AAudioServiceStreamBase::close();
+aaudio_result_t AAudioServiceStreamShared::close_l()  {
+    aaudio_result_t result = AAudioServiceStreamBase::close_l();
 
     {
         std::lock_guard<std::mutex> lock(mAudioDataQueueLock);
diff --git a/services/oboeservice/AAudioServiceStreamShared.h b/services/oboeservice/AAudioServiceStreamShared.h
index 61769b5..abcb782 100644
--- a/services/oboeservice/AAudioServiceStreamShared.h
+++ b/services/oboeservice/AAudioServiceStreamShared.h
@@ -52,7 +52,7 @@
 
     aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
 
-    aaudio_result_t close() override;
+    aaudio_result_t close_l() override;
 
     /**
      * This must be locked when calling getAudioDataFifoBuffer_l() and while
diff --git a/services/oboeservice/AAudioStreamTracker.cpp b/services/oboeservice/AAudioStreamTracker.cpp
index 3328159..8e66b94 100644
--- a/services/oboeservice/AAudioStreamTracker.cpp
+++ b/services/oboeservice/AAudioStreamTracker.cpp
@@ -30,32 +30,20 @@
 using namespace android;
 using namespace aaudio;
 
-sp<AAudioServiceStreamBase> AAudioStreamTracker::decrementAndRemoveStreamByHandle(
+int32_t AAudioStreamTracker::removeStreamByHandle(
         aaudio_handle_t streamHandle) {
     std::lock_guard<std::mutex> lock(mHandleLock);
-    sp<AAudioServiceStreamBase> serviceStream;
-    auto it = mStreamsByHandle.find(streamHandle);
-    if (it != mStreamsByHandle.end()) {
-        sp<AAudioServiceStreamBase> tempStream = it->second;
-        // Does the caller need to close the stream?
-        // The reference count should never be negative.
-        // But it is safer to check for <= 0 than == 0.
-        if ((tempStream->decrementServiceReferenceCount_l() <= 0) && tempStream->isCloseNeeded()) {
-            serviceStream = tempStream; // Only return stream if ready to be closed.
-            mStreamsByHandle.erase(it);
-        }
-    }
-    return serviceStream;
+    auto count = mStreamsByHandle.erase(streamHandle);
+    return static_cast<int32_t>(count);
 }
 
-sp<AAudioServiceStreamBase> AAudioStreamTracker::getStreamByHandleAndIncrement(
+sp<AAudioServiceStreamBase> AAudioStreamTracker::getStreamByHandle(
         aaudio_handle_t streamHandle) {
     std::lock_guard<std::mutex> lock(mHandleLock);
     sp<AAudioServiceStreamBase> serviceStream;
     auto it = mStreamsByHandle.find(streamHandle);
     if (it != mStreamsByHandle.end()) {
         serviceStream = it->second;
-        serviceStream->incrementServiceReferenceCount_l();
     }
     return serviceStream;
 }
@@ -63,7 +51,7 @@
 // The port handle is only available when the stream is started.
 // So we have to iterate over all the streams.
 // Luckily this rarely happens.
-sp<AAudioServiceStreamBase> AAudioStreamTracker::findStreamByPortHandleAndIncrement(
+sp<AAudioServiceStreamBase> AAudioStreamTracker::findStreamByPortHandle(
         audio_port_handle_t portHandle) {
     std::lock_guard<std::mutex> lock(mHandleLock);
     sp<AAudioServiceStreamBase> serviceStream;
@@ -72,7 +60,6 @@
         auto candidate = it->second;
         if (candidate->getPortHandle() == portHandle) {
             serviceStream = candidate;
-            serviceStream->incrementServiceReferenceCount_l();
             break;
         }
         it++;
diff --git a/services/oboeservice/AAudioStreamTracker.h b/services/oboeservice/AAudioStreamTracker.h
index 57ec426..d1301a2 100644
--- a/services/oboeservice/AAudioStreamTracker.h
+++ b/services/oboeservice/AAudioStreamTracker.h
@@ -32,25 +32,20 @@
 
 public:
     /**
-     * Find the stream associated with the handle.
-     * Decrement its reference counter. If zero and the stream needs
-     * to be closed then remove the stream and return a pointer to the stream.
-     * Otherwise return null if it does not need to be closed.
+     * Remove any streams with the matching handle.
      *
      * @param streamHandle
-     * @return strong pointer to the stream if it needs to be closed, or nullptr
+     * @return number of streams removed
      */
-    android::sp<AAudioServiceStreamBase> decrementAndRemoveStreamByHandle(
-            aaudio_handle_t streamHandle);
+    int32_t removeStreamByHandle(aaudio_handle_t streamHandle);
 
     /**
      * Look up a stream based on the handle.
-     * Increment its service reference count if found.
      *
      * @param streamHandle
      * @return strong pointer to the stream if found, or nullptr
      */
-    android::sp<aaudio::AAudioServiceStreamBase> getStreamByHandleAndIncrement(
+    android::sp<aaudio::AAudioServiceStreamBase> getStreamByHandle(
             aaudio_handle_t streamHandle);
 
     /**
@@ -60,7 +55,7 @@
      * @param portHandle
      * @return strong pointer to the stream if found, or nullptr
      */
-    android::sp<aaudio::AAudioServiceStreamBase> findStreamByPortHandleAndIncrement(
+    android::sp<aaudio::AAudioServiceStreamBase> findStreamByPortHandle(
             audio_port_handle_t portHandle);
 
     /**
diff --git a/tools/mainline_hook_partial.sh b/tools/mainline_hook_partial.sh
new file mode 100755
index 0000000..3dc6163
--- /dev/null
+++ b/tools/mainline_hook_partial.sh
Binary files differ
diff --git a/tools/mainline_hook_project.sh b/tools/mainline_hook_project.sh
new file mode 100755
index 0000000..8d35470
--- /dev/null
+++ b/tools/mainline_hook_project.sh
@@ -0,0 +1,65 @@
+#!/bin/bash
+#set -x
+
+# called for repo projects that are part of the media mainline modules
+# this is for projects where the entire project is part of mainline.
+# we have a separate script for projects where only part of that project gets
+# pulled into mainline.
+#
+# if the project's PREUPLOAD.cfg points to this script, it is by definition a project
+# which is entirely within mainline.
+#
+# example PREUPLOAD.cfg using this script
+# [Hook Scripts]
+# mainline_hook = ${REPO_ROOT}/frameworks/av/tools/mainline_hook_project.sh
+#
+
+
+# tunables
+DEV_BRANCH=rvc-dev
+
+###
+RED=$(tput setaf 1)
+NORMAL=$(tput sgr0)
+
+## check the active branch:
+## * b131183694 d198c6a [goog/master] Fix to handle missing checks on error returned
+##
+current=`git branch -vv | grep -P "^\*[^\[]+\[goog/"|sed -e 's/^.*\[//' | sed -e 's/:.*$//'| sed -e 's/^goog\///'`
+if [ "${current}" = "" ] ; then
+        current=unknown
+fi
+
+if [ "${current}" = "${DEV_BRANCH}" ] ; then
+    # Change appears to be in mainline dev branch
+    exit 0
+fi
+
+## warn the user that about not being on the typical/desired branch.
+
+cat - <<EOF
+
+You are uploading repo  ${RED}${REPO_PATH}${NORMAL} to branch ${RED}${current}${NORMAL}. 
+The source of truth for ${RED}${REPO_PATH}${NORMAL} is branch ${RED}${DEV_BRANCH}${NORMAL}. 
+
+Please upload this change to branch ${RED}${DEV_BRANCH}${NORMAL} unless one or more of
+the following apply:
+- this is a security bug prohibited from disclosure before the next dessert release.
+  (moderate security bugs fall into this category).
+- this is new functionality prohibitied from disclosure before the next dessert release.
+EOF
+
+
+##
+## TODO: prompt the user y/n to continue right now instead of re-invoking with no-verify
+## this has to get around how repo buffers stdout from this script such that the output
+## is not flushed before we try to read the input.
+## 
+
+cat - <<EOF
+If you are sure you want to proceed uploading to branch ${RED}${current}${NORMAL},
+re-run your repo upload command with the '--no-verify' option
+
+EOF
+exit 1
+