Merge "Revert "Force DIRECT flag on direct output thread."" 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/PREUPLOAD.cfg b/PREUPLOAD.cfg
index e63185d..ae920c0 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,2 +1,2 @@
 [Hook Scripts]
-mainline_hook = tools/mainline_hook.sh ${PREUPLOAD_COMMIT} "."
+mainline_hook = ${REPO_ROOT}/frameworks/av/tools/mainline_hook_partial.sh ${REPO_ROOT} ${PREUPLOAD_FILES}
diff --git a/apex/TEST_MAPPING b/apex/TEST_MAPPING
index a2e98cc..f036516 100644
--- a/apex/TEST_MAPPING
+++ b/apex/TEST_MAPPING
@@ -3,5 +3,30 @@
     {
       "path": "system/apex/tests"
     }
+  ],
+  "presubmit": [
+    // The following tests validate codec and drm path.
+    {
+      "name": "GtsMediaTestCases",
+      "options" : [
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "include-filter": "com.google.android.media.gts.WidevineGenericOpsTests"
+        }
+      ]
+    },
+    {
+      "name": "GtsExoPlayerTestCases",
+      "options" : [
+        {
+          "include-annotation": "android.platform.test.annotations.SocPresubmit"
+        },
+        {
+          "include-filter": "com.google.android.exoplayer.gts.DashTest#testWidevine23FpsH264Fixed"
+        }
+      ]
+    }
   ]
 }
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/apex/testing/test_manifest.json b/apex/testing/test_manifest.json
index ddd642e..e1295a2 100644
--- a/apex/testing/test_manifest.json
+++ b/apex/testing/test_manifest.json
@@ -1,4 +1,4 @@
 {
   "name": "com.android.media",
-  "version": 300000000
+  "version": 2147483647
 }
diff --git a/camera/CameraMetadata.cpp b/camera/CameraMetadata.cpp
index 4745ee8..135384a 100644
--- a/camera/CameraMetadata.cpp
+++ b/camera/CameraMetadata.cpp
@@ -17,9 +17,7 @@
 // #define LOG_NDEBUG 0
 
 #define LOG_TAG "Camera2-Metadata"
-#define ATRACE_TAG ATRACE_TAG_CAMERA
 #include <utils/Log.h>
-#include <utils/Trace.h>
 #include <utils/Errors.h>
 
 #include <binder/Parcel.h>
@@ -40,13 +38,11 @@
 CameraMetadata::CameraMetadata(size_t entryCapacity, size_t dataCapacity) :
         mLocked(false)
 {
-    ATRACE_CALL();
     mBuffer = allocate_camera_metadata(entryCapacity, dataCapacity);
 }
 
 CameraMetadata::CameraMetadata(const CameraMetadata &other) :
         mLocked(false) {
-    ATRACE_CALL();
     mBuffer = clone_camera_metadata(other.mBuffer);
 }
 
@@ -117,7 +113,6 @@
 }
 
 void CameraMetadata::clear() {
-    ATRACE_CALL();
     if (mLocked) {
         ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
         return;
@@ -129,7 +124,6 @@
 }
 
 void CameraMetadata::acquire(camera_metadata_t *buffer) {
-    ATRACE_CALL();
     if (mLocked) {
         ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
         return;
@@ -143,7 +137,6 @@
 }
 
 void CameraMetadata::acquire(CameraMetadata &other) {
-    ATRACE_CALL();
     if (mLocked) {
         ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
         return;
@@ -152,12 +145,10 @@
 }
 
 status_t CameraMetadata::append(const CameraMetadata &other) {
-    ATRACE_CALL();
     return append(other.mBuffer);
 }
 
 status_t CameraMetadata::append(const camera_metadata_t* other) {
-    ATRACE_CALL();
     if (mLocked) {
         ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
         return INVALID_OPERATION;
@@ -179,7 +170,6 @@
 }
 
 status_t CameraMetadata::sort() {
-    ATRACE_CALL();
     if (mLocked) {
         ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
         return INVALID_OPERATION;
@@ -310,7 +300,6 @@
 
 status_t CameraMetadata::updateImpl(uint32_t tag, const void *data,
         size_t data_count) {
-    ATRACE_CALL();
     status_t res;
     if (mLocked) {
         ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
@@ -368,13 +357,11 @@
 }
 
 bool CameraMetadata::exists(uint32_t tag) const {
-    ATRACE_CALL();
     camera_metadata_ro_entry entry;
     return find_camera_metadata_ro_entry(mBuffer, tag, &entry) == 0;
 }
 
 camera_metadata_entry_t CameraMetadata::find(uint32_t tag) {
-    ATRACE_CALL();
     status_t res;
     camera_metadata_entry entry;
     if (mLocked) {
@@ -391,7 +378,6 @@
 }
 
 camera_metadata_ro_entry_t CameraMetadata::find(uint32_t tag) const {
-    ATRACE_CALL();
     status_t res;
     camera_metadata_ro_entry entry;
     res = find_camera_metadata_ro_entry(mBuffer, tag, &entry);
@@ -403,7 +389,6 @@
 }
 
 status_t CameraMetadata::erase(uint32_t tag) {
-    ATRACE_CALL();
     camera_metadata_entry_t entry;
     status_t res;
     if (mLocked) {
@@ -434,7 +419,6 @@
 
 status_t CameraMetadata::removePermissionEntries(metadata_vendor_id_t vendorId,
         std::vector<int32_t> *tagsRemoved) {
-    ATRACE_CALL();
     uint32_t tagCount = 0;
     std::vector<uint32_t> tagsToRemove;
 
@@ -511,7 +495,6 @@
 }
 
 status_t CameraMetadata::resizeIfNeeded(size_t extraEntries, size_t extraData) {
-    ATRACE_CALL();
     if (mBuffer == NULL) {
         mBuffer = allocate_camera_metadata(extraEntries * 2, extraData * 2);
         if (mBuffer == NULL) {
@@ -551,7 +534,7 @@
 
 status_t CameraMetadata::readFromParcel(const Parcel& data,
                                         camera_metadata_t** out) {
-    ATRACE_CALL();
+
     status_t err = OK;
 
     camera_metadata_t* metadata = NULL;
@@ -642,7 +625,6 @@
 
 status_t CameraMetadata::writeToParcel(Parcel& data,
                                        const camera_metadata_t* metadata) {
-    ATRACE_CALL();
     status_t res = OK;
 
     /**
@@ -737,7 +719,7 @@
 }
 
 status_t CameraMetadata::readFromParcel(const Parcel *parcel) {
-    ATRACE_CALL();
+
     ALOGV("%s: parcel = %p", __FUNCTION__, parcel);
 
     status_t res = OK;
@@ -769,7 +751,7 @@
 }
 
 status_t CameraMetadata::writeToParcel(Parcel *parcel) const {
-    ATRACE_CALL();
+
     ALOGV("%s: parcel = %p", __FUNCTION__, parcel);
 
     if (parcel == NULL) {
@@ -798,7 +780,7 @@
 
 status_t CameraMetadata::getTagFromName(const char *name,
         const VendorTagDescriptor* vTags, uint32_t *tag) {
-    ATRACE_CALL();
+
     if (name == nullptr || tag == nullptr) return BAD_VALUE;
 
     size_t nameLength = strlen(name);
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/ICameraClient.cpp b/camera/ICameraClient.cpp
index 487b8b0..c02c81b 100644
--- a/camera/ICameraClient.cpp
+++ b/camera/ICameraClient.cpp
@@ -139,20 +139,18 @@
             CHECK_INTERFACE(ICameraClient, data, reply);
             int32_t msgType = data.readInt32();
             sp<IMemory> imageData = interface_cast<IMemory>(data.readStrongBinder());
-            camera_frame_metadata_t *metadata = NULL;
+            camera_frame_metadata_t metadata;
             if (data.dataAvail() > 0) {
-                metadata = new camera_frame_metadata_t;
-                metadata->number_of_faces = data.readInt32();
-                if (metadata->number_of_faces <= 0 ||
-                        metadata->number_of_faces > (int32_t)(INT32_MAX / sizeof(camera_face_t))) {
-                    ALOGE("%s: Too large face count: %d", __FUNCTION__, metadata->number_of_faces);
+                metadata.number_of_faces = data.readInt32();
+                if (metadata.number_of_faces <= 0 ||
+                        metadata.number_of_faces > (int32_t)(INT32_MAX / sizeof(camera_face_t))) {
+                    ALOGE("%s: Too large face count: %d", __FUNCTION__, metadata.number_of_faces);
                     return BAD_VALUE;
                 }
-                metadata->faces = (camera_face_t *) data.readInplace(
-                        sizeof(camera_face_t) * metadata->number_of_faces);
+                metadata.faces = (camera_face_t *) data.readInplace(
+                        sizeof(camera_face_t) * metadata.number_of_faces);
             }
-            dataCallback(msgType, imageData, metadata);
-            if (metadata) delete metadata;
+            dataCallback(msgType, imageData, &metadata);
             return NO_ERROR;
         } break;
         case DATA_CALLBACK_TIMESTAMP: {
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..d937865 100644
--- a/camera/ndk/impl/ACameraDevice.h
+++ b/camera/ndk/impl/ACameraDevice.h
@@ -267,8 +267,23 @@
     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;
+        // Whether the current sequence is completed (capture results are
+        // generated). May be set to true, but
+        // not removed from the map if not all inflight requests in the sequence
+        // have been completed.
+        bool isSequenceCompleted = false;
+        // Whether all inflight requests in the sequence are completed
+        // (capture results and buffers are generated). May be
+        // set to true, but not removed from the map yet if the capture results
+        // haven't been delivered to the app yet.
+        bool isInflightCompleted = false;
+        RequestLastFrameNumberHolder(int64_t lastFN) :
+                lastFrameNumber(lastFN) {}
+    };
+    std::map<int, RequestLastFrameNumberHolder> mSequenceLastFrameNumberMap;
 
     struct CallbackHolder {
         CallbackHolder(sp<ACameraCaptureSession>          session,
@@ -338,6 +353,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/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 16457ac..7d78571 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -1897,11 +1897,13 @@
      * <p>By using this control, the application gains a simpler way to control zoom, which can
      * be a combination of optical and digital zoom. For example, a multi-camera system may
      * contain more than one lens with different focal lengths, and the user can use optical
-     * zoom by switching between lenses. Using zoomRatio has benefits in the scenarios below:
-     * <em> Zooming in from a wide-angle lens to a telephoto lens: A floating-point ratio provides
-     *   better precision compared to an integer value of ACAMERA_SCALER_CROP_REGION.
-     * </em> Zooming out from a wide lens to an ultrawide lens: zoomRatio supports zoom-out whereas
-     *   ACAMERA_SCALER_CROP_REGION doesn't.</p>
+     * zoom by switching between lenses. Using zoomRatio has benefits in the scenarios below:</p>
+     * <ul>
+     * <li>Zooming in from a wide-angle lens to a telephoto lens: A floating-point ratio provides
+     *   better precision compared to an integer value of ACAMERA_SCALER_CROP_REGION.</li>
+     * <li>Zooming out from a wide lens to an ultrawide lens: zoomRatio supports zoom-out whereas
+     *   ACAMERA_SCALER_CROP_REGION doesn't.</li>
+     * </ul>
      * <p>To illustrate, here are several scenarios of different zoom ratios, crop regions,
      * and output streams, for a hypothetical camera device with an active array of size
      * <code>(2000,1500)</code>.</p>
@@ -4381,8 +4383,8 @@
     ACAMERA_SENSOR_AVAILABLE_TEST_PATTERN_MODES =               // int32[n]
             ACAMERA_SENSOR_START + 25,
     /**
-     * <p>Duration between the start of first row exposure
-     * and the start of last row exposure.</p>
+     * <p>Duration between the start of exposure for the first row of the image sensor,
+     * and the start of exposure for one past the last row of the image sensor.</p>
      *
      * <p>Type: int64</p>
      *
@@ -4391,12 +4393,22 @@
      *   <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
      * </ul></p>
      *
-     * <p>This is the exposure time skew between the first and last
-     * row exposure start times. The first row and the last row are
-     * the first and last rows inside of the
+     * <p>This is the exposure time skew between the first and <code>(last+1)</code> row exposure start times. The
+     * first row and the last row are the first and last rows inside of the
      * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.</p>
-     * <p>For typical camera sensors that use rolling shutters, this is also equivalent
-     * to the frame readout time.</p>
+     * <p>For typical camera sensors that use rolling shutters, this is also equivalent to the frame
+     * readout time.</p>
+     * <p>If the image sensor is operating in a binned or cropped mode due to the current output
+     * target resolutions, it's possible this skew is reported to be larger than the exposure
+     * time, for example, since it is based on the full array even if a partial array is read
+     * out. Be sure to scale the number to cover the section of the sensor actually being used
+     * for the outputs you care about. So if your output covers N rows of the active array of
+     * height H, scale this value by N/H to get the total skew for that viewport.</p>
+     * <p><em>Note:</em> Prior to Android 11, this field was described as measuring duration from
+     * first to last row of the image sensor, which is not equal to the frame readout time for a
+     * rolling shutter sensor. Implementations generally reported the latter value, so to resolve
+     * the inconsistency, the description has been updated to range from (first, last+1) row
+     * exposure start, instead.</p>
      *
      * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
      */
@@ -6042,10 +6054,11 @@
      * </ul></p>
      *
      * <p>The accuracy of the frame timestamp synchronization determines the physical cameras'
-     * ability to start exposure at the same time. If the sensorSyncType is CALIBRATED,
-     * the physical camera sensors usually run in master-slave mode so that their shutter
-     * time is synchronized. For APPROXIMATE sensorSyncType, the camera sensors usually run in
-     * master-master mode, and there could be offset between their start of exposure.</p>
+     * ability to start exposure at the same time. If the sensorSyncType is CALIBRATED, the
+     * physical camera sensors usually run in leader/follower mode where one sensor generates a
+     * timing signal for the other, so that their shutter time is synchronized. For APPROXIMATE
+     * sensorSyncType, the camera sensors usually run in leader/leader mode, where both sensors
+     * use their own timing generator, and there could be offset between their start of exposure.</p>
      * <p>In both cases, all images generated for a particular capture request still carry the same
      * timestamps, so that they can be used to look up the matching frame number and
      * onCaptureStarted callback.</p>
@@ -8076,19 +8089,35 @@
      * <li>ACAMERA_LENS_POSE_REFERENCE</li>
      * <li>ACAMERA_LENS_DISTORTION</li>
      * </ul>
-     * <p>The field of view of all non-RAW physical streams must be the same or as close as
-     * possible to that of non-RAW logical streams. If the requested FOV is outside of the
-     * range supported by the physical camera, the physical stream for that physical camera
-     * will use either the maximum or minimum scaler crop region, depending on which one is
-     * closer to the requested FOV. For example, for a logical camera with wide-tele lens
-     * configuration where the wide lens is the default, if the logical camera's crop region
-     * is set to maximum, the physical stream for the tele lens will be configured to its
-     * maximum crop region. On the other hand, if the logical camera has a normal-wide lens
-     * configuration where the normal lens is the default, when the logical camera's crop
-     * region is set to maximum, the FOV of the logical streams will be that of the normal
-     * lens. The FOV of the physical streams for the wide lens will be the same as the
-     * logical stream, by making the crop region smaller than its active array size to
-     * compensate for the smaller focal length.</p>
+     * <p>The field of view of non-RAW physical streams must not be smaller than that of the
+     * non-RAW logical streams, or the maximum field-of-view of the physical camera,
+     * whichever is smaller. The application should check the physical capture result
+     * metadata for how the physical streams are cropped or zoomed. More specifically, given
+     * the physical camera result metadata, the effective horizontal field-of-view of the
+     * physical camera is:</p>
+     * <pre><code>fov = 2 * atan2(cropW * sensorW / (2 * zoomRatio * activeArrayW), focalLength)
+     * </code></pre>
+     * <p>where the equation parameters are the physical camera's crop region width, physical
+     * sensor width, zoom ratio, active array width, and focal length respectively. Typically
+     * the physical stream of active physical camera has the same field-of-view as the
+     * logical streams. However, the same may not be true for physical streams from
+     * non-active physical cameras. For example, if the logical camera has a wide-ultrawide
+     * configuration where the wide lens is the default, when the crop region is set to the
+     * logical camera's active array size, (and the zoom ratio set to 1.0 starting from
+     * Android 11), a physical stream for the ultrawide camera may prefer outputing images
+     * with larger field-of-view than that of the wide camera for better stereo matching
+     * margin or more robust motion tracking. At the same time, the physical non-RAW streams'
+     * field of view must not be smaller than the requested crop region and zoom ratio, as
+     * long as it's within the physical lens' capability. For example, for a logical camera
+     * with wide-tele lens configuration where the wide lens is the default, if the logical
+     * camera's crop region is set to maximum size, and zoom ratio set to 1.0, the physical
+     * stream for the tele lens will be configured to its maximum size crop region (no zoom).</p>
+     * <p><em>Deprecated:</em> Prior to Android 11, the field of view of all non-RAW physical streams
+     * cannot be larger than that of non-RAW logical streams. If the logical camera has a
+     * wide-ultrawide lens configuration where the wide lens is the default, when the logical
+     * camera's crop region is set to maximum size, the FOV of the physical streams for the
+     * ultrawide lens will be the same as the logical stream, by making the crop region
+     * smaller than its active array size to compensate for the smaller focal length.</p>
      * <p>Even if the underlying physical cameras have different RAW characteristics (such as
      * size or CFA pattern), a logical camera can still advertise RAW capability. In this
      * case, when the application configures a RAW stream, the camera device will make sure
diff --git a/drm/TEST_MAPPING b/drm/TEST_MAPPING
new file mode 100644
index 0000000..2595e3e
--- /dev/null
+++ b/drm/TEST_MAPPING
@@ -0,0 +1,27 @@
+{
+  "presubmit": [
+    // The following tests validate codec and drm path.
+    {
+      "name": "GtsMediaTestCases",
+      "options" : [
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "include-filter": "com.google.android.media.gts.WidevineGenericOpsTests"
+        }
+      ]
+    },
+    {
+      "name": "GtsExoPlayerTestCases",
+      "options" : [
+        {
+          "include-annotation": "android.platform.test.annotations.SocPresubmit"
+        },
+        {
+          "include-filter": "com.google.android.exoplayer.gts.DashTest#testWidevine23FpsH264Fixed"
+        }
+      ]
+    }
+  ]
+}
diff --git a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
index 3ecf6d5..1495703 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
@@ -148,14 +148,17 @@
     // Calculate the output buffer size and determine if any subsamples are
     // encrypted.
     size_t destSize = 0;
+    size_t srcSize = 0;
     bool haveEncryptedSubsamples = false;
     for (size_t i = 0; i < subSamples.size(); i++) {
         const SubSample &subSample = subSamples[i];
-        if (__builtin_add_overflow(destSize, subSample.numBytesOfClearData, &destSize)) {
+        if (__builtin_add_overflow(destSize, subSample.numBytesOfClearData, &destSize) ||
+            __builtin_add_overflow(srcSize, subSample.numBytesOfClearData, &srcSize)) {
             _hidl_cb(Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE, 0, "subsample clear size overflow");
             return Void();
         }
-        if (__builtin_add_overflow(destSize, subSample.numBytesOfEncryptedData, &destSize)) {
+        if (__builtin_add_overflow(destSize, subSample.numBytesOfEncryptedData, &destSize) ||
+            __builtin_add_overflow(srcSize, subSample.numBytesOfEncryptedData, &srcSize)) {
             _hidl_cb(Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE, 0, "subsample encrypted size overflow");
             return Void();
         }
@@ -164,7 +167,7 @@
         }
     }
 
-    if (destSize > destBuffer.size) {
+    if (destSize > destBuffer.size || srcSize > source.size) {
         _hidl_cb(Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE, 0, "subsample sum too large");
         return Void();
     }
diff --git a/include/drm/TEST_MAPPING b/include/drm/TEST_MAPPING
new file mode 100644
index 0000000..28e432e
--- /dev/null
+++ b/include/drm/TEST_MAPPING
@@ -0,0 +1,26 @@
+{
+  "presubmit": [
+    {
+      "name": "GtsMediaTestCases",
+      "options" : [
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "include-filter": "com.google.android.media.gts.WidevineGenericOpsTests"
+        }
+      ]
+    },
+    {
+      "name": "GtsExoPlayerTestCases",
+      "options" : [
+        {
+          "include-annotation": "android.platform.test.annotations.SocPresubmit"
+        },
+        {
+          "include-filter": "com.google.android.exoplayer.gts.DashTest#testWidevine23FpsH264Fixed"
+        }
+      ]
+    }
+  ]
+}
diff --git a/include/media/TEST_MAPPING b/include/media/TEST_MAPPING
new file mode 100644
index 0000000..ac697da
--- /dev/null
+++ b/include/media/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "imports": [
+    {
+      "path": "frameworks/av/include/drm"
+    }
+  ]
+}
diff --git a/include/private/media/TEST_MAPPING b/include/private/media/TEST_MAPPING
new file mode 100644
index 0000000..ac697da
--- /dev/null
+++ b/include/private/media/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "imports": [
+    {
+      "path": "frameworks/av/include/drm"
+    }
+  ]
+}
diff --git a/media/audioserver/audioserver.rc b/media/audioserver/audioserver.rc
index 6cb0c73..f05c2d2 100644
--- a/media/audioserver/audioserver.rc
+++ b/media/audioserver/audioserver.rc
@@ -20,6 +20,14 @@
     # Keep the original service names for backward compatibility
     stop vendor.audio-hal-2-0
     stop audio-hal-2-0
+    # See b/155364397. Need to have HAL service running for VTS.
+    # Can't use 'restart' because then HAL service would restart
+    # audioserver bringing it back into running state.
+    start vendor.audio-hal
+    start vendor.audio-hal-4-0-msd
+    # Keep the original service names for backward compatibility
+    start vendor.audio-hal-2-0
+    start audio-hal-2-0
 
 on property:init.svc.audioserver=running
     start vendor.audio-hal
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/components/aac/C2SoftAacDec.cpp b/media/codec2/components/aac/C2SoftAacDec.cpp
index f39620e..677f316 100644
--- a/media/codec2/components/aac/C2SoftAacDec.cpp
+++ b/media/codec2/components/aac/C2SoftAacDec.cpp
@@ -89,11 +89,18 @@
         addParameter(
                 DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT)
                 .withDefault(new C2StreamChannelCountInfo::output(0u, 1))
-                .withFields({C2F(mChannelCount, value).inRange(1, 8)})
+                .withFields({C2F(mChannelCount, value).inRange(1, MAX_CHANNEL_COUNT)})
                 .withSetter(Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps)
                 .build());
 
         addParameter(
+                DefineParam(mMaxChannelCount, C2_PARAMKEY_MAX_CHANNEL_COUNT)
+                .withDefault(new C2StreamMaxChannelCountInfo::input(0u, MAX_CHANNEL_COUNT))
+                .withFields({C2F(mMaxChannelCount, value).inRange(1, MAX_CHANNEL_COUNT)})
+                .withSetter(Setter<decltype(*mMaxChannelCount)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
                 DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
                 .withDefault(new C2StreamBitrateInfo::input(0u, 64000))
                 .withFields({C2F(mBitrate, value).inRange(8000, 960000)})
@@ -225,6 +232,7 @@
     int32_t getDrcAttenuationFactor() const { return mDrcAttenuationFactor->value * 127. + 0.5; }
     int32_t getDrcEffectType() const { return mDrcEffectType->value; }
     int32_t getDrcAlbumMode() const { return mDrcAlbumMode->value; }
+    u_int32_t getMaxChannelCount() const { return mMaxChannelCount->value; }
     int32_t getDrcOutputLoudness() const { return (mDrcOutputLoudness->value <= 0 ? -mDrcOutputLoudness->value * 4. + 0.5 : -1); }
 
 private:
@@ -241,6 +249,7 @@
     std::shared_ptr<C2StreamDrcAttenuationFactorTuning::input> mDrcAttenuationFactor;
     std::shared_ptr<C2StreamDrcEffectTypeTuning::input> mDrcEffectType;
     std::shared_ptr<C2StreamDrcAlbumModeTuning::input> mDrcAlbumMode;
+    std::shared_ptr<C2StreamMaxChannelCountInfo::input> mMaxChannelCount;
     std::shared_ptr<C2StreamDrcOutputLoudnessTuning::output> mDrcOutputLoudness;
     // TODO Add : C2StreamAacSbrModeTuning
 };
@@ -366,9 +375,10 @@
     ALOGV("AAC decoder using MPEG-D DRC album mode %d", albumMode);
     aacDecoder_SetParam(mAACDecoder, AAC_UNIDRC_ALBUM_MODE, albumMode);
 
-    // By default, the decoder creates a 5.1 channel downmix signal.
-    // For seven and eight channel input streams, enable 6.1 and 7.1 channel output
-    aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, -1);
+    // AAC_PCM_MAX_OUTPUT_CHANNELS
+    u_int32_t maxChannelCount = mIntf->getMaxChannelCount();
+    ALOGV("AAC decoder using maximum output channel count %d", maxChannelCount);
+    aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, maxChannelCount);
 
     return status;
 }
@@ -707,6 +717,11 @@
         ALOGV("AAC decoder using MPEG-D DRC album mode %d", albumMode);
         aacDecoder_SetParam(mAACDecoder, AAC_UNIDRC_ALBUM_MODE, albumMode);
 
+        // AAC_PCM_MAX_OUTPUT_CHANNELS
+        int32_t maxChannelCount = mIntf->getMaxChannelCount();
+        ALOGV("AAC decoder using maximum output channel count %d", maxChannelCount);
+        aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, maxChannelCount);
+
         mDrcWrap.update();
 
         UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0];
@@ -776,7 +791,6 @@
 
                 // After an error, replace bufferSize with the sum of the
                 // decodedSizes to resynchronize the in/out lists.
-                inInfo.decodedSizes.pop_back();
                 inInfo.bufferSize = std::accumulate(
                         inInfo.decodedSizes.begin(), inInfo.decodedSizes.end(), 0);
 
@@ -847,6 +861,51 @@
                     ALOGE("Getting output loudness failed");
                 }
             }
+
+            // update config with values used for decoding:
+            //    Album mode, target reference level, DRC effect type, DRC attenuation and boost
+            //    factor, DRC compression mode, encoder target level and max channel count
+            // with input values as they were not modified by decoder
+
+            C2StreamDrcAttenuationFactorTuning::input currentAttenuationFactor(0u,
+                    (C2FloatValue) (attenuationFactor/127.));
+            work->worklets.front()->output.configUpdate.push_back(
+                    C2Param::Copy(currentAttenuationFactor));
+
+            C2StreamDrcBoostFactorTuning::input currentBoostFactor(0u,
+                    (C2FloatValue) (boostFactor/127.));
+            work->worklets.front()->output.configUpdate.push_back(
+                    C2Param::Copy(currentBoostFactor));
+
+            C2StreamDrcCompressionModeTuning::input currentCompressMode(0u,
+                    (C2Config::drc_compression_mode_t) compressMode);
+            work->worklets.front()->output.configUpdate.push_back(
+                    C2Param::Copy(currentCompressMode));
+
+            C2StreamDrcEncodedTargetLevelTuning::input currentEncodedTargetLevel(0u,
+                    (C2FloatValue) (encTargetLevel*-0.25));
+            work->worklets.front()->output.configUpdate.push_back(
+                    C2Param::Copy(currentEncodedTargetLevel));
+
+            C2StreamDrcAlbumModeTuning::input currentAlbumMode(0u,
+                    (C2Config::drc_album_mode_t) albumMode);
+            work->worklets.front()->output.configUpdate.push_back(
+                    C2Param::Copy(currentAlbumMode));
+
+            C2StreamDrcTargetReferenceLevelTuning::input currentTargetRefLevel(0u,
+                    (float) (targetRefLevel*-0.25));
+            work->worklets.front()->output.configUpdate.push_back(
+                    C2Param::Copy(currentTargetRefLevel));
+
+            C2StreamDrcEffectTypeTuning::input currentEffectype(0u,
+                    (C2Config::drc_effect_type_t) effectType);
+            work->worklets.front()->output.configUpdate.push_back(
+                    C2Param::Copy(currentEffectype));
+
+            C2StreamMaxChannelCountInfo::input currentMaxChannelCnt(0u, maxChannelCount);
+            work->worklets.front()->output.configUpdate.push_back(
+                    C2Param::Copy(currentMaxChannelCnt));
+
         } while (decoderErr == AAC_DEC_OK);
     }
 
diff --git a/media/codec2/components/aac/C2SoftAacEnc.cpp b/media/codec2/components/aac/C2SoftAacEnc.cpp
index 4db94f5..2e85915 100644
--- a/media/codec2/components/aac/C2SoftAacEnc.cpp
+++ b/media/codec2/components/aac/C2SoftAacEnc.cpp
@@ -293,6 +293,30 @@
     return OK;
 }
 
+static void MaybeLogTimestampWarning(
+        long long lastFrameEndTimestampUs, long long inputTimestampUs) {
+    using Clock = std::chrono::steady_clock;
+    thread_local Clock::time_point sLastLogTimestamp{};
+    thread_local int32_t sOverlapCount = -1;
+    if (Clock::now() - sLastLogTimestamp > std::chrono::minutes(1) || sOverlapCount < 0) {
+        AString countMessage = "";
+        if (sOverlapCount > 0) {
+            countMessage = AStringPrintf(
+                    "(%d overlapping timestamp detected since last log)", sOverlapCount);
+        }
+        ALOGI("Correcting overlapping timestamp: last frame ended at %lldus but "
+                "current frame is starting at %lldus. Using the last frame's end timestamp %s",
+                lastFrameEndTimestampUs, inputTimestampUs, countMessage.c_str());
+        sLastLogTimestamp = Clock::now();
+        sOverlapCount = 0;
+    } else {
+        ALOGV("Correcting overlapping timestamp: last frame ended at %lldus but "
+                "current frame is starting at %lldus. Using the last frame's end timestamp",
+                lastFrameEndTimestampUs, inputTimestampUs);
+        ++sOverlapCount;
+    }
+}
+
 void C2SoftAacEnc::process(
         const std::unique_ptr<C2Work> &work,
         const std::shared_ptr<C2BlockPool> &pool) {
@@ -366,9 +390,7 @@
     }
     c2_cntr64_t inputTimestampUs = work->input.ordinal.timestamp;
     if (inputTimestampUs < mLastFrameEndTimestampUs.value_or(inputTimestampUs)) {
-        ALOGW("Correcting overlapping timestamp: last frame ended at %lldus but "
-              "current frame is starting at %lldus. Using the last frame's end timestamp",
-              mLastFrameEndTimestampUs->peekll(), inputTimestampUs.peekll());
+        MaybeLogTimestampWarning(mLastFrameEndTimestampUs->peekll(), inputTimestampUs.peekll());
         inputTimestampUs = *mLastFrameEndTimestampUs;
     }
     if (capacity > 0) {
diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp b/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp
index 6578ad2..f7943be 100644
--- a/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp
+++ b/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp
@@ -336,11 +336,10 @@
                 memset(output, 0, outSamples * sizeof(int16_t));
             } else {
                 int16_t FT;
-                RX_State_wb rx_state;
                 int16_t numRecSamples;
 
                 mime_unsorting(const_cast<uint8_t *>(&input[1]),
-                               mInputSampleBuffer, &FT, &FM, 1, &rx_state);
+                               mInputSampleBuffer, &FT, &FM, 1, &mRxState);
                 pvDecoder_AmrWb(FM, mInputSampleBuffer, output, &numRecSamples,
                                 mDecoderBuf, FT, mDecoderCookie);
                 if (numRecSamples != outSamples) {
diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrDec.h b/media/codec2/components/amr_nb_wb/C2SoftAmrDec.h
index 6384450..afe1537 100644
--- a/media/codec2/components/amr_nb_wb/C2SoftAmrDec.h
+++ b/media/codec2/components/amr_nb_wb/C2SoftAmrDec.h
@@ -18,6 +18,8 @@
 #define ANDROID_C2_SOFT_AMR_DEC_H_
 
 #include <SimpleC2Component.h>
+#include "gsmamr_dec.h"
+#include "pvamrwbdecoder.h"
 
 
 namespace android {
@@ -51,6 +53,7 @@
     void *mAmrHandle;
     void *mDecoderBuf;
     int16_t *mDecoderCookie;
+    RX_State_wb mRxState{};
 
     int16_t mInputSampleBuffer[477];
 
diff --git a/media/codec2/components/base/SimpleC2Interface.cpp b/media/codec2/components/base/SimpleC2Interface.cpp
index 5c019f3..29740d1 100644
--- a/media/codec2/components/base/SimpleC2Interface.cpp
+++ b/media/codec2/components/base/SimpleC2Interface.cpp
@@ -39,6 +39,16 @@
     setDerivedInstance(this);
 
     addParameter(
+            DefineParam(mApiFeatures, C2_PARAMKEY_API_FEATURES)
+            .withConstValue(new C2ApiFeaturesSetting(C2Config::api_feature_t(
+                    API_REFLECTION |
+                    API_VALUES |
+                    API_CURRENT_VALUES |
+                    API_DEPENDENCY |
+                    API_SAME_INPUT_BUFFER)))
+            .build());
+
+    addParameter(
             DefineParam(mName, C2_PARAMKEY_COMPONENT_NAME)
             .withConstValue(AllocSharedString<C2ComponentNameSetting>(name.c_str()))
             .build());
@@ -305,7 +315,6 @@
     Clients need to handle the following base params due to custom dependency.
 
     std::shared_ptr<C2ApiLevelSetting> mApiLevel;
-    std::shared_ptr<C2ApiFeaturesSetting> mApiFeatures;
     std::shared_ptr<C2ComponentAttributesSetting> mAttrib;
 
     std::shared_ptr<C2PortSuggestedBufferCountTuning::input> mSuggestedInputBufferCount;
diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.cpp b/media/codec2/components/vpx/C2SoftVpxEnc.cpp
index 74e105e..7e9090f 100644
--- a/media/codec2/components/vpx/C2SoftVpxEnc.cpp
+++ b/media/codec2/components/vpx/C2SoftVpxEnc.cpp
@@ -460,8 +460,8 @@
 
     const C2ConstGraphicBlock inBuffer =
         inputBuffer->data().graphicBlocks().front();
-    if (inBuffer.width() != mSize->width ||
-        inBuffer.height() != mSize->height) {
+    if (inBuffer.width() < mSize->width ||
+        inBuffer.height() < mSize->height) {
         ALOGE("unexpected Input buffer attributes %d(%d) x %d(%d)",
               inBuffer.width(), mSize->width, inBuffer.height(),
               mSize->height);
@@ -472,8 +472,8 @@
     bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
     vpx_image_t raw_frame;
     const C2PlanarLayout &layout = rView->layout();
-    uint32_t width = rView->width();
-    uint32_t height = rView->height();
+    uint32_t width = mSize->width;
+    uint32_t height = mSize->height;
     if (width > 0x8000 || height > 0x8000) {
         ALOGE("Image too big: %u x %u", width, height);
         work->result = C2_BAD_VALUE;
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..0251ec2 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
@@ -92,7 +92,10 @@
         for (size_t i = 0; i < updates.size(); ++i) {
             C2Param* param = updates[i].get();
             if (param->index() == C2StreamInitDataInfo::output::PARAM_TYPE) {
-                csd = true;
+                C2StreamInitDataInfo::output* csdBuffer =
+                        (C2StreamInitDataInfo::output*)(param);
+                size_t csdSize = csdBuffer->flexCount();
+                if (csdSize > 0) csd = true;
             } else if ((param->index() == C2StreamSampleRateInfo::output::PARAM_TYPE) ||
                        (param->index() == C2StreamChannelCountInfo::output::PARAM_TYPE) ||
                        (param->index() == C2StreamPictureSizeInfo::output::PARAM_TYPE)) {
@@ -160,4 +163,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..12ed725 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,12 @@
         mEos = false;
         mFramesReceived = 0;
         mTimestampUs = 0u;
+        mWorkResult = C2_OK;
+        mReorderDepth = -1;
         mTimestampDevTest = false;
+        mMd5Offset = 0;
+        mMd5Enable = false;
+        mRefMd5 = nullptr;
         if (mCompName == unknown_comp) mDisableTest = true;
 
         C2SecureModeTuning secureModeTuning{};
@@ -134,43 +136,130 @@
     // 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) {
             if (!work->worklets.empty()) {
                 // For decoder components current timestamp always exceeds
-                // previous timestamp
+                // previous timestamp if output is in display order
                 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()) {
-                    EXPECT_GE((work->worklets.front()->output.ordinal.timestamp.peeku()),
-                              mTimestampUs);
-                    mTimestampUs = work->worklets.front()->output.ordinal.timestamp.peeku();
-
-                    ULock l(mQueueLock);
-                    if (mTimestampDevTest) {
-                        bool tsHit = false;
-                        std::list<uint64_t>::iterator it = mTimestampUslist.begin();
-                        while (it != mTimestampUslist.end()) {
-                            if (*it == mTimestampUs) {
-                                mTimestampUslist.erase(it);
-                                tsHit = true;
-                                break;
-                            }
-                            it++;
+                    if (mReorderDepth < 0) {
+                        C2PortReorderBufferDepthTuning::output reorderBufferDepth;
+                        mComponent->query({&reorderBufferDepth}, {}, C2_MAY_BLOCK,
+                                          nullptr);
+                        mReorderDepth = reorderBufferDepth.value;
+                        if (mReorderDepth > 0) {
+                            // TODO: Add validation for reordered output
+                            mTimestampDevTest = false;
                         }
-                        if (tsHit == false) {
-                            if (mTimestampUslist.empty() == false) {
-                                EXPECT_EQ(tsHit, true) << "TimeStamp not recognized";
-                            } else {
-                                std::cout << "[   INFO   ] Received non-zero "
-                                             "output / TimeStamp not recognized \n";
+                    }
+                    if (mTimestampDevTest) {
+                        EXPECT_GE((work->worklets.front()->output.ordinal.timestamp.peeku()),
+                                  mTimestampUs);
+                        mTimestampUs = work->worklets.front()->output.ordinal.timestamp.peeku();
+
+                        ULock l(mQueueLock);
+                        {
+                            bool tsHit = false;
+                            std::list<uint64_t>::iterator it = mTimestampUslist.begin();
+                            while (it != mTimestampUslist.end()) {
+                                if (*it == mTimestampUs) {
+                                    mTimestampUslist.erase(it);
+                                    tsHit = true;
+                                    break;
+                                }
+                                it++;
+                            }
+                            if (tsHit == false) {
+                                if (mTimestampUslist.empty() == false) {
+                                    EXPECT_EQ(tsHit, true) << "TimeStamp not recognized";
+                                } else {
+                                    std::cout << "[   INFO   ] Received non-zero "
+                                                 "output / TimeStamp not recognized \n";
+                                }
                             }
                         }
                     }
+                    if (mMd5Enable) {
+                        compareMd5Chksm(work);
+                    }
                 }
-                bool mCsd;
+                bool mCsd = false;
                 workDone(mComponent, work, mFlushedIndices, mQueueLock, mQueueCondition, mWorkQueue,
                          mEos, mCsd, mFramesReceived);
                 (void)mCsd;
@@ -195,11 +284,17 @@
 
     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;
+    int32_t mReorderDepth;
     uint32_t mFramesReceived;
     C2BlockPool::local_id_t mBlockPoolId;
     std::shared_ptr<C2BlockPool> mLinearPool;
@@ -268,54 +363,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 +560,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 +590,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 +652,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 +661,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 +671,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 +779,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 +788,7 @@
     }
 
     if (mTimestampDevTest) EXPECT_EQ(mTimestampUslist.empty(), true);
+    ASSERT_EQ(mWorkResult, C2_OK);
 }
 
 // thumbnail test
@@ -639,26 +797,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 +819,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 +874,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 +945,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 +1006,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 +1017,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 +1129,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 +1151,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 +1169,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 ecc56f5..ecaf3a8 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
@@ -41,7 +41,7 @@
         : C2Buffer({block->share(C2Rect(block->width(), block->height()), ::C2Fence())}) {}
 };
 
-static std::vector<std::tuple<std::string, std::string, std::string, std::string>>
+static std::vector<std::tuple<std::string, std::string, std::string, std::string, std::string>>
         kEncodeTestParameters;
 static std::vector<std::tuple<std::string, std::string, std::string, std::string>>
         kEncodeResolutionTestParameters;
@@ -100,6 +100,7 @@
         }
         mEos = false;
         mCsd = false;
+        mConfigBPictures = false;
         mFramesReceived = 0;
         mFailedWorkReceived = 0;
         mTimestampUs = 0u;
@@ -120,7 +121,7 @@
     // Get the test parameters from GetParam call.
     virtual void getParams() {}
 
-    bool setupConfigParam(int32_t nWidth, int32_t nHeight);
+    bool setupConfigParam(int32_t nWidth, int32_t nHeight, int32_t nBFrame = 0);
 
     // callback function to process onWorkDone received by Listener
     void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
@@ -130,8 +131,10 @@
                 // previous timestamp
                 typedef std::unique_lock<std::mutex> ULock;
                 if (!mTimestampUslist.empty()) {
-                    EXPECT_GE((work->worklets.front()->output.ordinal.timestamp.peeku()),
-                              mTimestampUs);
+                    if (!mConfigBPictures) {
+                        EXPECT_GE((work->worklets.front()->output.ordinal.timestamp.peeku()),
+                                  mTimestampUs);
+                    }
                     mTimestampUs = work->worklets.front()->output.ordinal.timestamp.peeku();
                     // Currently this lock is redundant as no mTimestampUslist is only initialized
                     // before queuing any work to component. Once AdaptiveTest is added similar to
@@ -192,7 +195,7 @@
     bool mEos;
     bool mCsd;
     bool mDisableTest;
-    bool mConfig;
+    bool mConfigBPictures;
     bool mTimestampDevTest;
     standardComp mCompName;
     uint32_t mFramesReceived;
@@ -269,13 +272,27 @@
 }
 
 // Set Default config param.
-bool Codec2VideoEncHidlTestBase::setupConfigParam(int32_t nWidth, int32_t nHeight) {
+bool Codec2VideoEncHidlTestBase::setupConfigParam(int32_t nWidth, int32_t nHeight,
+                                                  int32_t nBFrame) {
+    c2_status_t status = C2_OK;
+    std::vector<std::unique_ptr<C2Param>> configParam;
     std::vector<std::unique_ptr<C2SettingResult>> failures;
-    C2StreamPictureSizeInfo::input inputSize(0u, nWidth, nHeight);
-    std::vector<C2Param*> configParam{&inputSize};
-    c2_status_t status = mComponent->config(configParam, C2_DONT_BLOCK, &failures);
-    if (status == C2_OK && failures.size() == 0u) return true;
-    return false;
+
+    configParam.push_back(std::make_unique<C2StreamPictureSizeInfo::input>(0u, nWidth, nHeight));
+
+    if (nBFrame > 0) {
+        std::unique_ptr<C2StreamGopTuning::output> gop =
+                C2StreamGopTuning::output::AllocUnique(2 /* flexCount */, 0u /* stream */);
+        gop->m.values[0] = {P_FRAME, UINT32_MAX};
+        gop->m.values[1] = {C2Config::picture_type_t(P_FRAME | B_FRAME), uint32_t(nBFrame)};
+        configParam.push_back(std::move(gop));
+    }
+
+    for (const std::unique_ptr<C2Param>& param : configParam) {
+        status = mComponent->config({param.get()}, C2_DONT_BLOCK, &failures);
+        if (status != C2_OK || failures.size() != 0u) return false;
+    }
+    return true;
 }
 
 // LookUpTable of clips for component testing
@@ -388,7 +405,7 @@
 class Codec2VideoEncEncodeTest
     : public Codec2VideoEncHidlTestBase,
       public ::testing::WithParamInterface<
-              std::tuple<std::string, std::string, std::string, std::string>> {
+              std::tuple<std::string, std::string, std::string, std::string, std::string>> {
     void getParams() {
         mInstanceName = std::get<0>(GetParam());
         mComponentName = std::get<1>(GetParam());
@@ -405,6 +422,7 @@
     bool signalEOS = !std::get<2>(GetParam()).compare("true");
     // Send an empty frame to receive CSD data from encoder.
     bool sendEmptyFirstFrame = !std::get<3>(GetParam()).compare("true");
+    mConfigBPictures = !std::get<4>(GetParam()).compare("true");
 
     strcpy(mURL, sResourceDir.c_str());
     GetURLForComponent(mURL);
@@ -428,10 +446,30 @@
         inputFrames--;
     }
 
-    if (!setupConfigParam(nWidth, nHeight)) {
+    if (!setupConfigParam(nWidth, nHeight, mConfigBPictures ? 1 : 0)) {
         std::cout << "[   WARN   ] Test Skipped \n";
         return;
     }
+    std::vector<std::unique_ptr<C2Param>> inParams;
+    c2_status_t c2_status = mComponent->query({}, {C2StreamGopTuning::output::PARAM_TYPE},
+                                              C2_DONT_BLOCK, &inParams);
+
+    if (c2_status != C2_OK || inParams.size() == 0) {
+        std::cout << "[   WARN   ] Bframe not supported for " << mComponentName
+                  << " resetting num BFrames to 0\n";
+        mConfigBPictures = false;
+    } else {
+        size_t offset = sizeof(C2Param);
+        C2Param* param = inParams[0].get();
+        int32_t numBFrames = *(int32_t*)((uint8_t*)param + offset);
+
+        if (!numBFrames) {
+            std::cout << "[   WARN   ] Bframe not supported for " << mComponentName
+                      << " resetting num BFrames to 0\n";
+            mConfigBPictures = false;
+        }
+    }
+
     ASSERT_EQ(mComponent->start(), C2_OK);
 
     if (sendEmptyFirstFrame) {
@@ -454,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;
@@ -463,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) {
@@ -472,16 +510,17 @@
         ASSERT_TRUE(false);
     }
 
-    if (!mCsd && (mCompName != vp8 && mCompName != vp9)) {
-        ASSERT_TRUE(false) << "CSD Buffer not received";
-    }
-
-    if (mCsd && (mCompName == vp8 || mCompName == vp9)) {
-        ASSERT_TRUE(false) << "CSD Buffer not expected";
+    if (mCompName == vp8 || mCompName == h263) {
+        ASSERT_FALSE(mCsd) << "CSD Buffer not expected";
+    } else if (mCompName != vp9) {
+        ASSERT_TRUE(mCsd) << "CSD Buffer not received";
     }
 
     if (mTimestampDevTest) EXPECT_EQ(mTimestampUslist.empty(), true);
     ASSERT_EQ(mComponent->stop(), C2_OK);
+
+    // TODO: (b/155534991)
+    // Add assert for mFailedWorkReceived
 }
 
 TEST_P(Codec2VideoEncHidlTest, EOSTest) {
@@ -522,13 +561,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;
@@ -549,9 +588,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
@@ -561,29 +608,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,
@@ -600,25 +632,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);
 }
 
@@ -653,7 +673,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";
@@ -708,7 +728,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);
@@ -816,14 +836,14 @@
 int main(int argc, char** argv) {
     kTestParameters = getTestParameters(C2Component::DOMAIN_VIDEO, C2Component::KIND_ENCODER);
     for (auto params : kTestParameters) {
-        kEncodeTestParameters.push_back(
-                std::make_tuple(std::get<0>(params), std::get<1>(params), "true", "true"));
-        kEncodeTestParameters.push_back(
-                std::make_tuple(std::get<0>(params), std::get<1>(params), "true", "false"));
-        kEncodeTestParameters.push_back(
-                std::make_tuple(std::get<0>(params), std::get<1>(params), "false", "true"));
-        kEncodeTestParameters.push_back(
-                std::make_tuple(std::get<0>(params), std::get<1>(params), "false", "false"));
+        constexpr char const* kBoolString[] = { "false", "true" };
+        for (size_t i = 0; i < 1 << 3; ++i) {
+            kEncodeTestParameters.push_back(std::make_tuple(
+                    std::get<0>(params), std::get<1>(params),
+                    kBoolString[i & 1],
+                    kBoolString[(i >> 1) & 1],
+                    kBoolString[(i >> 2) & 1]));
+        }
 
         kEncodeResolutionTestParameters.push_back(
                 std::make_tuple(std::get<0>(params), std::get<1>(params), "52", "18"));
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/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 1e4560c..1972d3f 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -1379,7 +1379,7 @@
         state->set(STOPPING);
     }
 
-    mChannel->stop();
+    mChannel->reset();
     (new AMessage(kWhatStop, this))->post();
 }
 
@@ -1406,9 +1406,6 @@
         // TODO: convert err into status_t
         mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
     }
-    // Assure buffers are not owned when stop() was called without flush().
-    std::list<std::unique_ptr<C2Work>> flushedWork;
-    mChannel->flush(flushedWork);
 
     {
         Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
@@ -1468,7 +1465,7 @@
         }
     }
 
-    mChannel->stop();
+    mChannel->reset();
     // thiz holds strong ref to this while the thread is running.
     sp<CCodec> thiz(this);
     std::thread([thiz, sendCallback] { thiz->release(sendCallback); }).detach();
@@ -1495,6 +1492,7 @@
         state->set(RELEASED);
         state->comp.reset();
     }
+    (new AMessage(kWhatRelease, this))->post();
     if (sendCallback) {
         mCallback->onReleaseCompleted();
     }
@@ -1759,6 +1757,12 @@
             flush();
             break;
         }
+        case kWhatRelease: {
+            mChannel->release();
+            mClient.reset();
+            mClientListener.reset();
+            break;
+        }
         case kWhatCreateInputSurface: {
             // Surface operations may be briefly blocking.
             setDeadline(now, 1500ms, "createInputSurface");
@@ -1861,7 +1865,7 @@
                 config->mInputSurface->onInputBufferDone(work->input.ordinal.frameIndex);
             }
             mChannel->onWorkDone(
-                    std::move(work), changed ? config->mOutputFormat : nullptr,
+                    std::move(work), changed ? config->mOutputFormat->dup() : nullptr,
                     initData.hasChanged() ? initData.update().get() : nullptr);
             break;
         }
@@ -1951,11 +1955,98 @@
             inputSurface->getHalInterface()));
 }
 
-static void MaybeLogUnrecognizedName(const char *func, const std::string &name) {
-    thread_local std::set<std::string> sLogged{};
-    if (sLogged.insert(name).second) {
-        ALOGW("%s: Unrecognized interface name: %s", func, name.c_str());
+class IntfCache {
+public:
+    IntfCache() = default;
+
+    status_t init(const std::string &name) {
+        std::shared_ptr<Codec2Client::Interface> intf{
+            Codec2Client::CreateInterfaceByName(name.c_str())};
+        if (!intf) {
+            ALOGW("IntfCache [%s]: Unrecognized interface name", name.c_str());
+            mInitStatus = NO_INIT;
+            return NO_INIT;
+        }
+        const static C2StreamUsageTuning::input sUsage{0u /* stream id */};
+        mFields.push_back(C2FieldSupportedValuesQuery::Possible(
+                C2ParamField{&sUsage, &sUsage.value}));
+        c2_status_t err = intf->querySupportedValues(mFields, C2_MAY_BLOCK);
+        if (err != C2_OK) {
+            ALOGW("IntfCache [%s]: failed to query usage supported value (err=%d)",
+                    name.c_str(), err);
+            mFields[0].status = err;
+        }
+        std::vector<std::unique_ptr<C2Param>> params;
+        err = intf->query(
+                {&mApiFeatures},
+                {C2PortAllocatorsTuning::input::PARAM_TYPE},
+                C2_MAY_BLOCK,
+                &params);
+        if (err != C2_OK && err != C2_BAD_INDEX) {
+            ALOGW("IntfCache [%s]: failed to query api features (err=%d)",
+                    name.c_str(), err);
+        }
+        while (!params.empty()) {
+            C2Param *param = params.back().release();
+            params.pop_back();
+            if (!param) {
+                continue;
+            }
+            if (param->type() == C2PortAllocatorsTuning::input::PARAM_TYPE) {
+                mInputAllocators.reset(
+                        C2PortAllocatorsTuning::input::From(params[0].get()));
+            }
+        }
+        mInitStatus = OK;
+        return OK;
     }
+
+    status_t initCheck() const { return mInitStatus; }
+
+    const C2FieldSupportedValuesQuery &getUsageSupportedValues() const {
+        CHECK_EQ(1u, mFields.size());
+        return mFields[0];
+    }
+
+    const C2ApiFeaturesSetting &getApiFeatures() const {
+        return mApiFeatures;
+    }
+
+    const C2PortAllocatorsTuning::input &getInputAllocators() const {
+        static std::unique_ptr<C2PortAllocatorsTuning::input> sInvalidated = []{
+            std::unique_ptr<C2PortAllocatorsTuning::input> param =
+                C2PortAllocatorsTuning::input::AllocUnique(0);
+            param->invalidate();
+            return param;
+        }();
+        return mInputAllocators ? *mInputAllocators : *sInvalidated;
+    }
+
+private:
+    status_t mInitStatus{NO_INIT};
+
+    std::vector<C2FieldSupportedValuesQuery> mFields;
+    C2ApiFeaturesSetting mApiFeatures;
+    std::unique_ptr<C2PortAllocatorsTuning::input> mInputAllocators;
+};
+
+static const IntfCache &GetIntfCache(const std::string &name) {
+    static IntfCache sNullIntfCache;
+    static std::mutex sMutex;
+    static std::map<std::string, IntfCache> sCache;
+    std::unique_lock<std::mutex> lock{sMutex};
+    auto it = sCache.find(name);
+    if (it == sCache.end()) {
+        lock.unlock();
+        IntfCache intfCache;
+        status_t err = intfCache.init(name);
+        if (err != OK) {
+            return sNullIntfCache;
+        }
+        lock.lock();
+        it = sCache.insert({name, std::move(intfCache)}).first;
+    }
+    return it->second;
 }
 
 static status_t GetCommonAllocatorIds(
@@ -1973,24 +2064,16 @@
     }
     bool firstIteration = true;
     for (const std::string &name : names) {
-        std::shared_ptr<Codec2Client::Interface> intf{
-            Codec2Client::CreateInterfaceByName(name.c_str())};
-        if (!intf) {
-            MaybeLogUnrecognizedName(__FUNCTION__, name);
+        const IntfCache &intfCache = GetIntfCache(name);
+        if (intfCache.initCheck() != OK) {
             continue;
         }
-        std::vector<std::unique_ptr<C2Param>> params;
-        c2_status_t err = intf->query(
-                {}, {C2PortAllocatorsTuning::input::PARAM_TYPE}, C2_MAY_BLOCK, &params);
+        const C2PortAllocatorsTuning::input &allocators = intfCache.getInputAllocators();
         if (firstIteration) {
             firstIteration = false;
-            if (err == C2_OK && params.size() == 1u) {
-                C2PortAllocatorsTuning::input *allocators =
-                    C2PortAllocatorsTuning::input::From(params[0].get());
-                if (allocators && allocators->flexCount() > 0) {
-                    ids->insert(allocators->m.values,
-                                allocators->m.values + allocators->flexCount());
-                }
+            if (allocators && allocators.flexCount() > 0) {
+                ids->insert(allocators.m.values,
+                            allocators.m.values + allocators.flexCount());
             }
             if (ids->empty()) {
                 // The component does not advertise allocators. Use default.
@@ -1999,24 +2082,20 @@
             continue;
         }
         bool filtered = false;
-        if (err == C2_OK && params.size() == 1u) {
-            C2PortAllocatorsTuning::input *allocators =
-                C2PortAllocatorsTuning::input::From(params[0].get());
-            if (allocators && allocators->flexCount() > 0) {
-                filtered = true;
-                for (auto it = ids->begin(); it != ids->end(); ) {
-                    bool found = false;
-                    for (size_t j = 0; j < allocators->flexCount(); ++j) {
-                        if (allocators->m.values[j] == *it) {
-                            found = true;
-                            break;
-                        }
+        if (allocators && allocators.flexCount() > 0) {
+            filtered = true;
+            for (auto it = ids->begin(); it != ids->end(); ) {
+                bool found = false;
+                for (size_t j = 0; j < allocators.flexCount(); ++j) {
+                    if (allocators.m.values[j] == *it) {
+                        found = true;
+                        break;
                     }
-                    if (found) {
-                        ++it;
-                    } else {
-                        it = ids->erase(it);
-                    }
+                }
+                if (found) {
+                    ++it;
+                } else {
+                    it = ids->erase(it);
                 }
             }
         }
@@ -2048,23 +2127,16 @@
     *minUsage = 0;
     *maxUsage = ~0ull;
     for (const std::string &name : names) {
-        std::shared_ptr<Codec2Client::Interface> intf{
-            Codec2Client::CreateInterfaceByName(name.c_str())};
-        if (!intf) {
-            MaybeLogUnrecognizedName(__FUNCTION__, name);
+        const IntfCache &intfCache = GetIntfCache(name);
+        if (intfCache.initCheck() != OK) {
             continue;
         }
-        std::vector<C2FieldSupportedValuesQuery> fields;
-        fields.push_back(C2FieldSupportedValuesQuery::Possible(
-                C2ParamField{&sUsage, &sUsage.value}));
-        c2_status_t err = intf->querySupportedValues(fields, C2_MAY_BLOCK);
-        if (err != C2_OK) {
+        const C2FieldSupportedValuesQuery &usageSupportedValues =
+            intfCache.getUsageSupportedValues();
+        if (usageSupportedValues.status != C2_OK) {
             continue;
         }
-        if (fields[0].status != C2_OK) {
-            continue;
-        }
-        const C2FieldSupportedValues &supported = fields[0].values;
+        const C2FieldSupportedValues &supported = usageSupportedValues.values;
         if (supported.type != C2FieldSupportedValues::FLAGS) {
             continue;
         }
@@ -2085,6 +2157,17 @@
 // static
 status_t CCodec::CanFetchLinearBlock(
         const std::vector<std::string> &names, const C2MemoryUsage &usage, bool *isCompatible) {
+    for (const std::string &name : names) {
+        const IntfCache &intfCache = GetIntfCache(name);
+        if (intfCache.initCheck() != OK) {
+            continue;
+        }
+        const C2ApiFeaturesSetting &features = intfCache.getApiFeatures();
+        if (features && !(features.value & API_SAME_INPUT_BUFFER)) {
+            *isCompatible = false;
+            return OK;
+        }
+    }
     uint64_t minUsage = usage.expected;
     uint64_t maxUsage = ~0ull;
     std::set<C2Allocator::id_t> allocators;
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 3773528..0626c8d 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") {}
@@ -707,13 +618,14 @@
 }
 
 void CCodecBufferChannel::feedInputBufferIfAvailableInternal() {
-    if (mInputMetEos ||
-           mReorderStash.lock()->hasPending() ||
-           mPipelineWatcher.lock()->pipelineFull()) {
+    if (mInputMetEos || mPipelineWatcher.lock()->pipelineFull()) {
         return;
-    } else {
+    }
+    {
         Mutexed<Output>::Locked output(mOutput);
-        if (output->buffers->numClientBuffers() >= output->numSlots) {
+        if (!output->buffers ||
+                output->buffers->hasPending() ||
+                output->buffers->numClientBuffers() >= output->numSlots) {
             return;
         }
     }
@@ -820,6 +732,9 @@
     std::shared_ptr<const C2StreamHdr10PlusInfo::output> hdr10PlusInfo =
         std::static_pointer_cast<const C2StreamHdr10PlusInfo::output>(
                 c2Buffer->getInfo(C2StreamHdr10PlusInfo::output::PARAM_TYPE));
+    if (hdr10PlusInfo && hdr10PlusInfo->flexCount() == 0) {
+        hdr10PlusInfo.reset();
+    }
 
     {
         Mutexed<OutputSurface>::Locked output(mOutputSurface);
@@ -851,31 +766,39 @@
     if (hdrStaticInfo || hdr10PlusInfo) {
         HdrMetadata hdr;
         if (hdrStaticInfo) {
-            struct android_smpte2086_metadata smpte2086_meta = {
-                .displayPrimaryRed = {
-                    hdrStaticInfo->mastering.red.x, hdrStaticInfo->mastering.red.y
-                },
-                .displayPrimaryGreen = {
-                    hdrStaticInfo->mastering.green.x, hdrStaticInfo->mastering.green.y
-                },
-                .displayPrimaryBlue = {
-                    hdrStaticInfo->mastering.blue.x, hdrStaticInfo->mastering.blue.y
-                },
-                .whitePoint = {
-                    hdrStaticInfo->mastering.white.x, hdrStaticInfo->mastering.white.y
-                },
-                .maxLuminance = hdrStaticInfo->mastering.maxLuminance,
-                .minLuminance = hdrStaticInfo->mastering.minLuminance,
-            };
-
-            struct android_cta861_3_metadata cta861_meta = {
-                .maxContentLightLevel = hdrStaticInfo->maxCll,
-                .maxFrameAverageLightLevel = hdrStaticInfo->maxFall,
-            };
-
-            hdr.validTypes = HdrMetadata::SMPTE2086 | HdrMetadata::CTA861_3;
-            hdr.smpte2086 = smpte2086_meta;
-            hdr.cta8613 = cta861_meta;
+            // If mastering max and min luminance fields are 0, do not use them.
+            // It indicates the value may not be present in the stream.
+            if (hdrStaticInfo->mastering.maxLuminance > 0.0f &&
+                hdrStaticInfo->mastering.minLuminance > 0.0f) {
+                struct android_smpte2086_metadata smpte2086_meta = {
+                    .displayPrimaryRed = {
+                        hdrStaticInfo->mastering.red.x, hdrStaticInfo->mastering.red.y
+                    },
+                    .displayPrimaryGreen = {
+                        hdrStaticInfo->mastering.green.x, hdrStaticInfo->mastering.green.y
+                    },
+                    .displayPrimaryBlue = {
+                        hdrStaticInfo->mastering.blue.x, hdrStaticInfo->mastering.blue.y
+                    },
+                    .whitePoint = {
+                        hdrStaticInfo->mastering.white.x, hdrStaticInfo->mastering.white.y
+                    },
+                    .maxLuminance = hdrStaticInfo->mastering.maxLuminance,
+                    .minLuminance = hdrStaticInfo->mastering.minLuminance,
+                };
+                hdr.validTypes |= HdrMetadata::SMPTE2086;
+                hdr.smpte2086 = smpte2086_meta;
+            }
+            // If the content light level fields are 0, do not use them, it
+            // indicates the value may not be present in the stream.
+            if (hdrStaticInfo->maxCll > 0.0f && hdrStaticInfo->maxFall > 0.0f) {
+                struct android_cta861_3_metadata cta861_meta = {
+                    .maxContentLightLevel = hdrStaticInfo->maxCll,
+                    .maxFrameAverageLightLevel = hdrStaticInfo->maxFall,
+                };
+                hdr.validTypes |= HdrMetadata::CTA861_3;
+                hdr.cta8613 = cta861_meta;
+            }
         }
         if (hdr10PlusInfo) {
             hdr.validTypes |= HdrMetadata::HDR10PLUS;
@@ -981,17 +904,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;
@@ -1008,6 +920,12 @@
 
     if (inputFormat != nullptr) {
         bool graphic = (iStreamFormat.value == C2BufferData::GRAPHIC);
+        C2Config::api_feature_t apiFeatures = C2Config::api_feature_t(
+                API_REFLECTION |
+                API_VALUES |
+                API_CURRENT_VALUES |
+                API_DEPENDENCY |
+                API_SAME_INPUT_BUFFER);
         std::shared_ptr<C2BlockPool> pool;
         {
             Mutexed<BlockPools>::Locked pools(mBlockPools);
@@ -1019,14 +937,15 @@
             // query C2PortAllocatorsTuning::input from component. If an allocator ID is obtained
             // from component, create the input block pool with given ID. Otherwise, use default IDs.
             std::vector<std::unique_ptr<C2Param>> params;
-            err = mComponent->query({ },
+            C2ApiFeaturesSetting featuresSetting{apiFeatures};
+            err = mComponent->query({ &featuresSetting },
                                     { C2PortAllocatorsTuning::input::PARAM_TYPE },
                                     C2_DONT_BLOCK,
                                     &params);
             if ((err != C2_OK && err != C2_BAD_INDEX) || params.size() != 1) {
                 ALOGD("[%s] Query input allocators returned %zu params => %s (%u)",
                         mName, params.size(), asString(err), err);
-            } else if (err == C2_OK && params.size() == 1) {
+            } else if (params.size() == 1) {
                 C2PortAllocatorsTuning::input *inputAllocators =
                     C2PortAllocatorsTuning::input::From(params[0].get());
                 if (inputAllocators && inputAllocators->flexCount() > 0) {
@@ -1041,6 +960,9 @@
                     }
                 }
             }
+            if (featuresSetting) {
+                apiFeatures = featuresSetting.value;
+            }
 
             // TODO: use C2Component wrapper to associate this pool with ourselves
             if ((poolMask >> pools->inputAllocatorId) & 1) {
@@ -1074,7 +996,10 @@
         input->numSlots = numInputSlots;
         input->extraBuffers.flush();
         input->numExtraSlots = 0u;
-        if (!buffersBoundToCodec) {
+        bool conforming = (apiFeatures & API_SAME_INPUT_BUFFER);
+        // For encrypted content, framework decrypts source buffer (ashmem) into
+        // C2Buffers. Thus non-conforming codecs can process these.
+        if (!buffersBoundToCodec && (hasCryptoOrDescrambler() || conforming)) {
             input->buffers.reset(new SlotInputBuffers(mName));
         } else if (graphic) {
             if (mInputSurface) {
@@ -1260,6 +1185,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) {
@@ -1329,62 +1261,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;
 }
 
@@ -1394,8 +1362,36 @@
     if (mInputSurface != nullptr) {
         mInputSurface.reset();
     }
+    mPipelineWatcher.lock()->flush();
 }
 
+void CCodecBufferChannel::reset() {
+    stop();
+    {
+        Mutexed<Input>::Locked input(mInput);
+        input->buffers.reset(new DummyInputBuffers(""));
+        input->extraBuffers.flush();
+    }
+    {
+        Mutexed<Output>::Locked output(mOutput);
+        output->buffers.reset();
+    }
+}
+
+void CCodecBufferChannel::release() {
+    mComponent.reset();
+    mInputAllocator.reset();
+    mOutputSurface.lock()->surface.clear();
+    {
+        Mutexed<BlockPools>::Locked blockPools{mBlockPools};
+        blockPools->inputPool.reset();
+        blockPools->outputPoolIntf.reset();
+    }
+    setCrypto(nullptr);
+    setDescrambler(nullptr);
+}
+
+
 void CCodecBufferChannel::flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) {
     ALOGV("[%s] flush", mName);
     {
@@ -1426,9 +1422,11 @@
     }
     {
         Mutexed<Output>::Locked output(mOutput);
-        output->buffers->flush(flushedWork);
+        if (output->buffers) {
+            output->buffers->flush(flushedWork);
+            output->buffers->flushStash();
+        }
     }
-    mReorderStash.lock()->flush();
     mPipelineWatcher.lock()->flush();
 }
 
@@ -1464,45 +1462,41 @@
         std::unique_ptr<C2Work> work,
         const sp<AMessage> &outputFormat,
         const C2StreamInitDataInfo::output *initData) {
-    if (outputFormat != nullptr) {
+    {
         Mutexed<Output>::Locked output(mOutput);
-        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 (!output->buffers) {
+            return false;
         }
     }
 
-    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
@@ -1538,8 +1532,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;
@@ -1551,17 +1547,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 {
@@ -1576,7 +1574,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()) {
@@ -1585,7 +1584,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()) {
@@ -1593,16 +1593,22 @@
                     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;
                         size_t numInputSlots = mInput.lock()->numSlots;
                         {
                             Mutexed<Output>::Locked output(mOutput);
+                            if (!output->buffers) {
+                                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()) {
@@ -1621,7 +1627,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) {
@@ -1665,9 +1671,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)
@@ -1688,9 +1691,19 @@
           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->registerCsd(initData, &index, &outBuffer) == OK) {
+        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);
             ALOGV("[%s] onWorkDone: csd index = %zu [%p]", mName, index, outBuffer.get());
@@ -1705,10 +1718,10 @@
         }
     }
 
-    if (!buffer && !flags && outputFormat == nullptr) {
-        ALOGV("[%s] onWorkDone: nothing to report from the work (%lld)",
+    if (notifyClient && !buffer && !flags) {
+        ALOGV("[%s] onWorkDone: Not reporting output buffer (%lld)",
               mName, work->input.ordinal.frameIndex.peekull());
-        return true;
+        notifyClient = false;
     }
 
     if (buffer) {
@@ -1727,63 +1740,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);
-        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);
-
-            output.unlock();
-            reorder.unlock();
-
-            if (outputBuffersChanged) {
-                mCCodecCallback->onOutputBuffersChanged();
-            }
+        if (!output->buffers) {
             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);
+        action = output->buffers->popFromStashAndRegister(
+                &c2Buffer, &index, &outBuffer);
+        switch (action) {
+        case OutputBuffers::SKIP:
+            return;
+        case OutputBuffers::DISCARD:
+            break;
+        case OutputBuffers::NOTIFY_CLIENT:
+            output.unlock();
+            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();
+            break;
+        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;
+        }
     }
 }
 
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index 0263211..046c5c3 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -138,6 +138,16 @@
      */
     void stop();
 
+    /**
+     * Stop queueing buffers to the component and release all buffers.
+     */
+    void reset();
+
+    /**
+     * Release all resources.
+     */
+    void release();
+
     void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork);
 
     /**
@@ -296,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 6244acd..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;
 };
 
 /**
@@ -416,7 +644,7 @@
             size_t *index,
             sp<Codec2Buffer> *buffer,
             std::function<bool(const sp<Codec2Buffer> &)> match =
-                [](const sp<Codec2Buffer> &) { return true; });
+                [](const sp<Codec2Buffer> &buffer) { return (buffer != nullptr); });
 
     /**
      * Return the buffer from the client, and get the C2Buffer object back from
@@ -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/Android.bp b/media/extractors/Android.bp
index bb42580..7c4e62f 100644
--- a/media/extractors/Android.bp
+++ b/media/extractors/Android.bp
@@ -24,6 +24,9 @@
         "libmediandk#29",
     ],
 
+    // extractors are supposed to work on Q(29)
+    min_sdk_version: "29",
+
     relative_install_path: "extractors",
 
     compile_multilib: "first",
diff --git a/media/extractors/TEST_MAPPING b/media/extractors/TEST_MAPPING
new file mode 100644
index 0000000..abefb0f
--- /dev/null
+++ b/media/extractors/TEST_MAPPING
@@ -0,0 +1,17 @@
+{
+  "presubmit": [
+    // TODO(b/153661591) enable test once the bug is fixed
+    // This tests the extractor path
+    // {
+    //    "name": "GtsYouTubeTestCases",
+    //    "options" : [
+    //      {
+    //        "include-annotation": "android.platform.test.annotations.Presubmit"
+    //      },
+    //      {
+    //        "include-filter": "com.google.android.youtube.gts.SimultaneousClearAndProtectedDecodeTest#testSimultaneousClearAndProtectedDecode"
+    //      }
+    //    ]
+    //  }
+  ]
+}
diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp
index a051648..b88e4e8 100644
--- a/media/extractors/mkv/MatroskaExtractor.cpp
+++ b/media/extractors/mkv/MatroskaExtractor.cpp
@@ -814,11 +814,13 @@
             int32_t sampleRate;
             if (!AMediaFormat_getInt32(trackInfo->mMeta, AMEDIAFORMAT_KEY_SAMPLE_RATE,
                                        &sampleRate)) {
+                mbuf->release();
                 return AMEDIA_ERROR_MALFORMED;
             }
             int64_t durationUs;
             if (!AMediaFormat_getInt64(trackInfo->mMeta, AMEDIAFORMAT_KEY_DURATION,
                                        &durationUs)) {
+                mbuf->release();
                 return AMEDIA_ERROR_MALFORMED;
             }
             // TODO: Explore if this can be handled similar to MPEG4 extractor where padding is
@@ -981,6 +983,7 @@
         while (mPendingFrames.empty()) {
             media_status_t err = readBlock();
             if (err != OK) {
+                buffer->release();
                 clearPendingFrames();
                 return err;
             }
@@ -1000,6 +1003,7 @@
             while (mPendingFrames.empty()) {
                 media_status_t err = readBlock();
                 if (err != OK) {
+                    buffer->release();
                     clearPendingFrames();
                     return err;
                 }
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 54f1fa2..ba24274 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>
@@ -149,6 +150,7 @@
 
     bool mIsHeif;
     bool mIsAudio;
+    bool mIsUsac = false;
     sp<ItemTable> mItemTable;
 
     /* Shift start offset (move to earlier time) when media_time > 0,
@@ -204,6 +206,7 @@
         Vector<size_t> encryptedsizes;
     };
     Vector<Sample> mCurrentSamples;
+    std::map<off64_t, uint32_t> mDrmOffsets;
 
     MPEG4Source(const MPEG4Source &);
     MPEG4Source &operator=(const MPEG4Source &);
@@ -4454,7 +4457,7 @@
     //AOT_SLS              = 38, /**< SLS                                       */
     //AOT_ER_AAC_ELD       = 39, /**< AAC Enhanced Low Delay                    */
 
-    //AOT_USAC             = 42, /**< USAC                                      */
+    AOT_USAC               = 42, /**< USAC                                      */
     //AOT_SAOC             = 43, /**< SAOC                                      */
     //AOT_LD_MPEGS         = 44, /**< Low Delay MPEG Surround                   */
 
@@ -4602,7 +4605,7 @@
     ABitReader br(csd, csd_size);
     uint32_t objectType = br.getBits(5);
 
-    if (objectType == 31) {  // AAC-ELD => additional 6 bits
+    if (objectType == AOT_ESCAPE) {  // AAC-ELD => additional 6 bits
         objectType = 32 + br.getBits(6);
     }
 
@@ -4979,6 +4982,12 @@
     mIsPcm = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW);
     mIsAudio = !strncasecmp(mime, "audio/", 6);
 
+    int32_t aacObjectType = -1;
+
+    if (AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_AAC_PROFILE, &aacObjectType)) {
+        mIsUsac = (aacObjectType == AOT_USAC);
+    }
+
     if (mIsPcm) {
         int32_t numChannels = 0;
         int32_t bitsPerSample = 0;
@@ -5153,6 +5162,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 +5226,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 +5423,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(
@@ -5951,10 +5981,10 @@
             }
 
             uint32_t syncSampleIndex = sampleIndex;
-            // assume every audio sample is a sync sample. This works around
+            // assume every non-USAC audio sample is a sync sample. This works around
             // seek issues with files that were incorrectly written with an
             // empty or single-sample stss block for the audio track
-            if (err == OK && !mIsAudio) {
+            if (err == OK && (!mIsAudio || mIsUsac)) {
                 err = mSampleTable->findSyncSampleNear(
                         sampleIndex, &syncSampleIndex, findFlags);
             }
diff --git a/media/libaaudio/src/binding/AAudioServiceMessage.h b/media/libaaudio/src/binding/AAudioServiceMessage.h
index 3981454..62927a0 100644
--- a/media/libaaudio/src/binding/AAudioServiceMessage.h
+++ b/media/libaaudio/src/binding/AAudioServiceMessage.h
@@ -44,6 +44,8 @@
 struct AAudioMessageEvent {
     aaudio_service_event_t event;
     union {
+        // Align so that 32 and 64-bit code can exchange messages through shared memory.
+        alignas(8)
         double  dataDouble;
         int64_t dataLong;
     };
@@ -57,8 +59,10 @@
         EVENT,
     };
 
-    code what;
+    code      what;
     union {
+        // Align so that 32 and 64-bit code can exchange messages through shared memory.
+        alignas(8)
         AAudioMessageTimestamp timestamp; // what == TIMESTAMP
         AAudioMessageEvent event;         // what == EVENT
     };
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..f5c75ca 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();
@@ -140,6 +141,7 @@
         case AAUDIO_STREAM_STATE_PAUSED:
         case AAUDIO_STREAM_STATE_STOPPING:
         case AAUDIO_STREAM_STATE_STOPPED:
+        case AAUDIO_STREAM_STATE_FLUSHING:
         case AAUDIO_STREAM_STATE_FLUSHED:
             break; // Proceed with starting.
 
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..b0dc59e 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();
@@ -345,13 +345,17 @@
     // Enable callback before starting AudioRecord to avoid shutting
     // down because of a race condition.
     mCallbackEnabled.store(true);
+    aaudio_stream_state_t originalState = getState();
+    // Set before starting the callback so that we are in the correct state
+    // before updateStateMachine() can be called by the callback.
+    setState(AAUDIO_STREAM_STATE_STARTING);
     mFramesWritten.reset32(); // service writes frames
     mTimestampPosition.reset32();
     status_t err = mAudioRecord->start(); // resets position to zero
     if (err != OK) {
+        mCallbackEnabled.store(false);
+        setState(originalState);
         return AAudioConvert_androidToAAudioResult(err);
-    } else {
-        setState(AAUDIO_STREAM_STATE_STARTING);
     }
     return AAUDIO_OK;
 }
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 1120f05..4869480 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!
@@ -307,11 +307,15 @@
     // Enable callback before starting AudioTrack to avoid shutting
     // down because of a race condition.
     mCallbackEnabled.store(true);
+    aaudio_stream_state_t originalState = getState();
+    // Set before starting the callback so that we are in the correct state
+    // before updateStateMachine() can be called by the callback.
+    setState(AAUDIO_STREAM_STATE_STARTING);
     err = mAudioTrack->start();
     if (err != OK) {
+        mCallbackEnabled.store(false);
+        setState(originalState);
         return AAudioConvert_androidToAAudioResult(err);
-    } else {
-        setState(AAUDIO_STREAM_STATE_STARTING);
     }
     return AAUDIO_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/libaaudio/tests/test_various.cpp b/media/libaaudio/tests/test_various.cpp
index 1c26615..a20c799 100644
--- a/media/libaaudio/tests/test_various.cpp
+++ b/media/libaaudio/tests/test_various.cpp
@@ -124,7 +124,7 @@
 }
 
 enum FunctionToCall {
-    CALL_START, CALL_STOP, CALL_PAUSE, CALL_FLUSH
+    CALL_START, CALL_STOP, CALL_PAUSE, CALL_FLUSH, CALL_RELEASE
 };
 
 void checkStateTransition(aaudio_performance_mode_t perfMode,
@@ -177,11 +177,27 @@
             } else if (originalState == AAUDIO_STREAM_STATE_PAUSED) {
                 ASSERT_EQ(AAUDIO_OK, AAudioStream_requestPause(aaudioStream));
                 inputState = AAUDIO_STREAM_STATE_PAUSING;
+            } else if (originalState == AAUDIO_STREAM_STATE_FLUSHING) {
+                ASSERT_EQ(AAUDIO_OK, AAudioStream_requestPause(aaudioStream));
+                // We can only flush() after pause is complete.
+                ASSERT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream,
+                                                                 AAUDIO_STREAM_STATE_PAUSING,
+                                                                 &state,
+                                                                 1000 * NANOS_PER_MILLISECOND));
+                ASSERT_EQ(AAUDIO_STREAM_STATE_PAUSED, state);
+                ASSERT_EQ(AAUDIO_OK, AAudioStream_requestFlush(aaudioStream));
+                // That will put the stream into the FLUSHING state.
+                // The FLUSHING state will persist until we process functionToCall.
+                // That is because the transition to FLUSHED is caused by the callback,
+                // or by calling write() or waitForStateChange(). But those will not
+                // occur.
+            } else if (originalState == AAUDIO_STREAM_STATE_CLOSING) {
+                ASSERT_EQ(AAUDIO_OK, AAudioStream_release(aaudioStream));
             }
         }
     }
 
-    // Wait until past transitional state.
+    // Wait until we get past the transitional state if requested.
     if (inputState != AAUDIO_STREAM_STATE_UNINITIALIZED) {
         ASSERT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream,
                                                              inputState,
@@ -208,12 +224,20 @@
             EXPECT_EQ(expectedResult, AAudioStream_requestFlush(aaudioStream));
             transitionalState = AAUDIO_STREAM_STATE_FLUSHING;
             break;
+        case FunctionToCall::CALL_RELEASE:
+            EXPECT_EQ(expectedResult, AAudioStream_release(aaudioStream));
+            // Set to UNINITIALIZED so the waitForStateChange() below will
+            // will return immediately with the current state.
+            transitionalState = AAUDIO_STREAM_STATE_UNINITIALIZED;
+            break;
     }
 
-    EXPECT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream,
-                                                         transitionalState,
-                                                         &state,
-                                                         1000 * NANOS_PER_MILLISECOND));
+    EXPECT_EQ(AAUDIO_OK,
+            AAudioStream_waitForStateChange(aaudioStream,
+                    transitionalState,
+                    &state,
+                    1000 * NANOS_PER_MILLISECOND));
+
     // We should not change state when a function fails.
     if (expectedResult != AAUDIO_OK) {
         ASSERT_EQ(originalState, expectedState);
@@ -493,6 +517,88 @@
         AAUDIO_STREAM_STATE_FLUSHED);
 }
 
+// FLUSHING ================================================================
+TEST(test_various, aaudio_state_lowlat_flushing_start) {
+checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+        AAUDIO_STREAM_STATE_FLUSHING,
+        FunctionToCall::CALL_START,
+        AAUDIO_OK,
+        AAUDIO_STREAM_STATE_STARTED);
+}
+
+TEST(test_various, aaudio_state_none_flushing_start) {
+checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE,
+        AAUDIO_STREAM_STATE_FLUSHING,
+        FunctionToCall::CALL_START,
+        AAUDIO_OK,
+        AAUDIO_STREAM_STATE_STARTED);
+}
+
+TEST(test_various, aaudio_state_lowlat_flushing_release) {
+checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+        AAUDIO_STREAM_STATE_FLUSHING,
+        FunctionToCall::CALL_RELEASE,
+        AAUDIO_OK,
+        AAUDIO_STREAM_STATE_CLOSING);
+}
+
+TEST(test_various, aaudio_state_none_flushing_release) {
+checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE,
+        AAUDIO_STREAM_STATE_FLUSHING,
+        FunctionToCall::CALL_RELEASE,
+        AAUDIO_OK,
+        AAUDIO_STREAM_STATE_CLOSING);
+}
+
+TEST(test_various, aaudio_state_lowlat_starting_release) {
+checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+        AAUDIO_STREAM_STATE_STARTING,
+        FunctionToCall::CALL_RELEASE,
+        AAUDIO_OK,
+        AAUDIO_STREAM_STATE_CLOSING);
+}
+
+TEST(test_various, aaudio_state_none_starting_release) {
+checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE,
+        AAUDIO_STREAM_STATE_STARTING,
+        FunctionToCall::CALL_RELEASE,
+        AAUDIO_OK,
+        AAUDIO_STREAM_STATE_CLOSING);
+}
+
+// CLOSING ================================================================
+TEST(test_various, aaudio_state_lowlat_closing_start) {
+checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+        AAUDIO_STREAM_STATE_CLOSING,
+        FunctionToCall::CALL_START,
+        AAUDIO_ERROR_INVALID_STATE,
+        AAUDIO_STREAM_STATE_CLOSING);
+}
+
+TEST(test_various, aaudio_state_none_closing_start) {
+checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE,
+        AAUDIO_STREAM_STATE_CLOSING,
+        FunctionToCall::CALL_START,
+        AAUDIO_ERROR_INVALID_STATE,
+        AAUDIO_STREAM_STATE_CLOSING);
+}
+
+TEST(test_various, aaudio_state_lowlat_closing_stop) {
+checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+        AAUDIO_STREAM_STATE_CLOSING,
+        FunctionToCall::CALL_STOP,
+        AAUDIO_ERROR_INVALID_STATE,
+        AAUDIO_STREAM_STATE_CLOSING);
+}
+
+TEST(test_various, aaudio_state_none_closing_stop) {
+checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE,
+        AAUDIO_STREAM_STATE_CLOSING,
+        FunctionToCall::CALL_STOP,
+        AAUDIO_ERROR_INVALID_STATE,
+        AAUDIO_STREAM_STATE_CLOSING);
+}
+
 // ==========================================================================
 TEST(test_various, aaudio_set_buffer_size) {
 
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 0bbceef..df47def 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -405,7 +405,7 @@
                     ? AMEDIAMETRICS_PROP_CALLERNAME_VALUE_UNKNOWN
                     : mCallerName.c_str())
             .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_START)
-            .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+            .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(systemTime() - beginNs))
             .set(AMEDIAMETRICS_PROP_STATE, stateToString(mActive))
             .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)status)
             .record(); });
@@ -481,7 +481,7 @@
     mediametrics::Defer defer([&] {
         mediametrics::LogItem(mMetricsId)
             .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_STOP)
-            .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+            .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(systemTime() - beginNs))
             .set(AMEDIAMETRICS_PROP_STATE, stateToString(mActive))
             .record(); });
 
@@ -771,7 +771,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 |
@@ -923,10 +923,10 @@
     mMetricsId = std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_RECORD) + std::to_string(mPortId);
     mediametrics::LogItem(mMetricsId)
         .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CREATE)
-        .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+        .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(systemTime() - beginNs))
         // the following are immutable (at least until restore)
-        .set(AMEDIAMETRICS_PROP_FLAGS, (int32_t)mFlags)
-        .set(AMEDIAMETRICS_PROP_ORIGINALFLAGS, (int32_t)mOrigFlags)
+        .set(AMEDIAMETRICS_PROP_FLAGS, toString(mFlags).c_str())
+        .set(AMEDIAMETRICS_PROP_ORIGINALFLAGS, toString(mOrigFlags).c_str())
         .set(AMEDIAMETRICS_PROP_SESSIONID, (int32_t)mSessionId)
         .set(AMEDIAMETRICS_PROP_TRACKID, mPortId)
         .set(AMEDIAMETRICS_PROP_SOURCE, toString(mAttributes.source).c_str())
@@ -1387,7 +1387,7 @@
     mediametrics::Defer defer([&] {
         mediametrics::LogItem(mMetricsId)
             .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_RESTORE)
-            .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+            .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(systemTime() - beginNs))
             .set(AMEDIAMETRICS_PROP_STATE, stateToString(mActive))
             .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result)
             .set(AMEDIAMETRICS_PROP_WHERE, from)
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index ca80dc4..32129f0 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)
@@ -650,17 +658,11 @@
                     ? AMEDIAMETRICS_PROP_CALLERNAME_VALUE_UNKNOWN
                     : mCallerName.c_str())
             .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_START)
-            .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+            .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(systemTime() - beginNs))
             .set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
             .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;
 
@@ -783,10 +785,11 @@
     mediametrics::Defer defer([&]() {
         mediametrics::LogItem(mMetricsId)
             .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_STOP)
-            .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+            .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));
@@ -845,7 +848,7 @@
     mediametrics::Defer defer([&]() {
         mediametrics::LogItem(mMetricsId)
             .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_FLUSH)
-            .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+            .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(systemTime() - beginNs))
             .set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
             .record(); });
 
@@ -886,7 +889,7 @@
     mediametrics::Defer defer([&]() {
         mediametrics::LogItem(mMetricsId)
             .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_PAUSE)
-            .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+            .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(systemTime() - beginNs))
             .set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
             .record(); });
 
@@ -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);
         }
     }
@@ -1715,15 +1712,15 @@
     // The creation of the audio track by AudioFlinger (in the code above)
     // is the first log of the AudioTrack and must be present before
     // any AudioTrack client logs will be accepted.
+
     mMetricsId = std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK) + std::to_string(mPortId);
     mediametrics::LogItem(mMetricsId)
         .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CREATE)
         // the following are immutable
-        .set(AMEDIAMETRICS_PROP_FLAGS, (int32_t)mFlags)
-        .set(AMEDIAMETRICS_PROP_ORIGINALFLAGS, (int32_t)mOrigFlags)
+        .set(AMEDIAMETRICS_PROP_FLAGS, toString(mFlags).c_str())
+        .set(AMEDIAMETRICS_PROP_ORIGINALFLAGS, toString(mOrigFlags).c_str())
         .set(AMEDIAMETRICS_PROP_SESSIONID, (int32_t)mSessionId)
         .set(AMEDIAMETRICS_PROP_TRACKID, mPortId) // dup from key
-        .set(AMEDIAMETRICS_PROP_STREAMTYPE, toString(mStreamType).c_str())
         .set(AMEDIAMETRICS_PROP_CONTENTTYPE, toString(mAttributes.content_type).c_str())
         .set(AMEDIAMETRICS_PROP_USAGE, toString(mAttributes.usage).c_str())
         .set(AMEDIAMETRICS_PROP_THREADID, (int32_t)output.outputId)
@@ -2440,7 +2437,7 @@
     mediametrics::Defer defer([&] {
         mediametrics::LogItem(mMetricsId)
             .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_RESTORE)
-            .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+            .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(systemTime() - beginNs))
             .set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
             .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result)
             .set(AMEDIAMETRICS_PROP_WHERE, from)
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/libaudiofoundation/DeviceDescriptorBase.cpp b/media/libaudiofoundation/DeviceDescriptorBase.cpp
index ef7576e..3dbe37d 100644
--- a/media/libaudiofoundation/DeviceDescriptorBase.cpp
+++ b/media/libaudiofoundation/DeviceDescriptorBase.cpp
@@ -80,9 +80,28 @@
     toAudioPortConfig(&port->active_config);
     port->id = mId;
     port->ext.device.type = mDeviceTypeAddr.mType;
+    port->ext.device.encapsulation_modes = mEncapsulationModes;
+    port->ext.device.encapsulation_metadata_types = mEncapsulationMetadataTypes;
     (void)audio_utils_strlcpy_zerofill(port->ext.device.address, mDeviceTypeAddr.getAddress());
 }
 
+status_t DeviceDescriptorBase::setEncapsulationModes(uint32_t encapsulationModes) {
+    if ((encapsulationModes & ~AUDIO_ENCAPSULATION_MODE_ALL_POSITION_BITS) != 0) {
+        return BAD_VALUE;
+    }
+    mEncapsulationModes = encapsulationModes & ~(1 << AUDIO_ENCAPSULATION_MODE_NONE);
+    return NO_ERROR;
+}
+
+status_t DeviceDescriptorBase::setEncapsulationMetadataTypes(uint32_t encapsulationMetadataTypes) {
+    if ((encapsulationMetadataTypes & ~AUDIO_ENCAPSULATION_METADATA_TYPE_ALL_POSITION_BITS) != 0) {
+        return BAD_VALUE;
+    }
+    mEncapsulationMetadataTypes =
+            encapsulationMetadataTypes & ~(1 << AUDIO_ENCAPSULATION_METADATA_TYPE_NONE);
+    return NO_ERROR;
+}
+
 void DeviceDescriptorBase::dump(std::string *dst, int spaces, int index,
                                 const char* extraInfo, bool verbose) const
 {
@@ -98,6 +117,12 @@
     dst->append(base::StringPrintf("%*s- type: %-48s\n",
             spaces, "", ::android::toString(mDeviceTypeAddr.mType).c_str()));
 
+    dst->append(base::StringPrintf(
+            "%*s- supported encapsulation modes: %u", spaces, "", mEncapsulationModes));
+    dst->append(base::StringPrintf(
+            "%*s- supported encapsulation metadata types: %u",
+            spaces, "", mEncapsulationMetadataTypes));
+
     if (mDeviceTypeAddr.mAddress.size() != 0) {
         dst->append(base::StringPrintf(
                 "%*s- address: %-32s\n", spaces, "", mDeviceTypeAddr.getAddress()));
@@ -135,6 +160,8 @@
     if ((status = AudioPort::writeToParcel(parcel)) != NO_ERROR) return status;
     if ((status = AudioPortConfig::writeToParcel(parcel)) != NO_ERROR) return status;
     if ((status = parcel->writeParcelable(mDeviceTypeAddr)) != NO_ERROR) return status;
+    if ((status = parcel->writeUint32(mEncapsulationModes)) != NO_ERROR) return status;
+    if ((status = parcel->writeUint32(mEncapsulationMetadataTypes)) != NO_ERROR) return status;
     return status;
 }
 
@@ -144,6 +171,8 @@
     if ((status = AudioPort::readFromParcel(parcel)) != NO_ERROR) return status;
     if ((status = AudioPortConfig::readFromParcel(parcel)) != NO_ERROR) return status;
     if ((status = parcel->readParcelable(&mDeviceTypeAddr)) != NO_ERROR) return status;
+    if ((status = parcel->readUint32(&mEncapsulationModes)) != NO_ERROR) return status;
+    if ((status = parcel->readUint32(&mEncapsulationMetadataTypes)) != NO_ERROR) return status;
     return status;
 }
 
diff --git a/media/libaudiofoundation/include/media/DeviceDescriptorBase.h b/media/libaudiofoundation/include/media/DeviceDescriptorBase.h
index 4c03667..af04721 100644
--- a/media/libaudiofoundation/include/media/DeviceDescriptorBase.h
+++ b/media/libaudiofoundation/include/media/DeviceDescriptorBase.h
@@ -55,6 +55,9 @@
     // AudioPort
     virtual void toAudioPort(struct audio_port *port) const;
 
+    status_t setEncapsulationModes(uint32_t encapsulationModes);
+    status_t setEncapsulationMetadataTypes(uint32_t encapsulationMetadataTypes);
+
     void dump(std::string *dst, int spaces, int index,
               const char* extraInfo = nullptr, bool verbose = true) const;
     void log() const;
@@ -67,6 +70,8 @@
 
 protected:
     AudioDeviceTypeAddr mDeviceTypeAddr;
+    uint32_t mEncapsulationModes = 0;
+    uint32_t mEncapsulationMetadataTypes = 0;
 };
 
 using DeviceDescriptorBaseVector = std::vector<sp<DeviceDescriptorBase>>;
diff --git a/media/libaudiofoundation/tests/audiofoundation_parcelable_test.cpp b/media/libaudiofoundation/tests/audiofoundation_parcelable_test.cpp
index 5baa072..068b5d8 100644
--- a/media/libaudiofoundation/tests/audiofoundation_parcelable_test.cpp
+++ b/media/libaudiofoundation/tests/audiofoundation_parcelable_test.cpp
@@ -131,6 +131,9 @@
     desc->setAudioProfiles(getAudioProfileVectorForTest());
     desc->applyAudioPortConfig(&TEST_AUDIO_PORT_CONFIG);
     desc->setAddress("DeviceDescriptorBaseTestAddress");
+    ASSERT_EQ(desc->setEncapsulationModes(1 << AUDIO_ENCAPSULATION_MODE_HANDLE), NO_ERROR);
+    ASSERT_EQ(desc->setEncapsulationMetadataTypes(
+            AUDIO_ENCAPSULATION_METADATA_TYPE_ALL_POSITION_BITS), NO_ERROR);
 
     ASSERT_EQ(data.writeParcelable(*desc), NO_ERROR);
     data.setDataPosition(0);
diff --git a/media/libaudiohal/impl/ConversionHelperHidl.cpp b/media/libaudiohal/impl/ConversionHelperHidl.cpp
index f29b0f3..ebed5fd 100644
--- a/media/libaudiohal/impl/ConversionHelperHidl.cpp
+++ b/media/libaudiohal/impl/ConversionHelperHidl.cpp
@@ -41,16 +41,25 @@
     bool keepFormatValue = halKeys.size() == 2 &&
          (halKeys.get(String8(AudioParameter::keyStreamSupportedChannels), value) == NO_ERROR ||
          halKeys.get(String8(AudioParameter::keyStreamSupportedSamplingRates), value) == NO_ERROR);
+    // When querying encapsulation capabilities, "keyRouting=<value>" pair is used to identify
+    // the device. We need to transform it into a single key string so that it is carried over to
+    // the legacy HAL via HIDL.
+    bool keepRoutingValue =
+            halKeys.get(String8(AUDIO_PARAMETER_DEVICE_SUP_ENCAPSULATION_MODES),
+                        value) == NO_ERROR ||
+            halKeys.get(String8(AUDIO_PARAMETER_DEVICE_SUP_ENCAPSULATION_METADATA_TYPES),
+                        value) == NO_ERROR;
 
     for (size_t i = 0; i < halKeys.size(); ++i) {
         String8 key;
         status_t status = halKeys.getAt(i, key);
         if (status != OK) return status;
-        if (keepFormatValue && key == AudioParameter::keyFormat) {
-            AudioParameter formatParam;
+        if ((keepFormatValue && key == AudioParameter::keyFormat) ||
+            (keepRoutingValue && key == AudioParameter::keyRouting)) {
+            AudioParameter keepValueParam;
             halKeys.getAt(i, key, value);
-            formatParam.add(key, value);
-            key = formatParam.toString();
+            keepValueParam.add(key, value);
+            key = keepValueParam.toString();
         }
         (*hidlKeys)[i] = key.string();
     }
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..3fd3fc3 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",
@@ -31,10 +33,8 @@
     path: "aidl",
 }
 
-aidl_interface {
-    name: "resourcemanager_aidl_interface",
-    unstable: true,
-    local_include_dir: "aidl",
+filegroup {
+    name: "resourcemanager_aidl",
     srcs: [
         "aidl/android/media/IResourceManagerClient.aidl",
         "aidl/android/media/IResourceManagerService.aidl",
@@ -43,6 +43,16 @@
         "aidl/android/media/MediaResourceParcel.aidl",
         "aidl/android/media/MediaResourcePolicyParcel.aidl",
     ],
+    path: "aidl",
+}
+
+aidl_interface {
+    name: "resourcemanager_aidl_interface",
+    unstable: true,
+    local_include_dir: "aidl",
+    srcs: [
+        ":resourcemanager_aidl",
+    ],
 }
 
 cc_library_shared {
@@ -184,6 +194,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/libmediahelper/AudioParameter.cpp b/media/libmediahelper/AudioParameter.cpp
index 9f34035..fc8306c 100644
--- a/media/libmediahelper/AudioParameter.cpp
+++ b/media/libmediahelper/AudioParameter.cpp
@@ -53,6 +53,10 @@
 const char * const AudioParameter::valueListSeparator = AUDIO_PARAMETER_VALUE_LIST_SEPARATOR;
 const char * const AudioParameter::keyReconfigA2dp = AUDIO_PARAMETER_RECONFIG_A2DP;
 const char * const AudioParameter::keyReconfigA2dpSupported = AUDIO_PARAMETER_A2DP_RECONFIG_SUPPORTED;
+// const char * const AudioParameter::keyDeviceSupportedEncapsulationModes =
+//        AUDIO_PARAMETER_DEVICE_SUP_ENCAPSULATION_MODES;
+// const char * const AudioParameter::keyDeviceSupportedEncapsulationMetadataTypes =
+//        AUDIO_PARAMETER_DEVICE_SUP_ENCAPSULATION_METADATA_TYPES;
 
 AudioParameter::AudioParameter(const String8& keyValuePairs)
 {
diff --git a/media/libmediahelper/include/media/AudioParameter.h b/media/libmediahelper/include/media/AudioParameter.h
index 3c190f2..66d8dfb 100644
--- a/media/libmediahelper/include/media/AudioParameter.h
+++ b/media/libmediahelper/include/media/AudioParameter.h
@@ -92,6 +92,18 @@
     static const char * const keyReconfigA2dp;
     static const char * const keyReconfigA2dpSupported;
 
+    // For querying device supported encapsulation capabilities. All returned values are integer,
+    // which are bit fields composed from using encapsulation capability values as position bits.
+    // Encapsulation capability values are defined in audio_encapsulation_mode_t and
+    // audio_encapsulation_metadata_type_t. For instance, if the supported encapsulation mode is
+    // AUDIO_ENCAPSULATION_MODE_ELEMENTARY_STREAM, the returned value is
+    // "supEncapsulationModes=1 << AUDIO_ENCAPSULATION_MODE_HANDLE".
+    // When querying device supported encapsulation capabilities, the key should use with device
+    // type and address so that it is able to identify the device. The device will be a key. The
+    // device type will be the value of key AUDIO_PARAMETER_STREAM_ROUTING.
+    // static const char * const keyDeviceSupportedEncapsulationModes;
+    // static const char * const keyDeviceSupportedEncapsulationMetadataTypes;
+
     String8 toString() const { return toStringImpl(true); }
     String8 keysToString() const { return toStringImpl(false); }
 
diff --git a/media/libmediametrics/include/MediaMetricsConstants.h b/media/libmediametrics/include/MediaMetricsConstants.h
index ec0e133..84388c9 100644
--- a/media/libmediametrics/include/MediaMetricsConstants.h
+++ b/media/libmediametrics/include/MediaMetricsConstants.h
@@ -37,6 +37,9 @@
 // They must be appended with another value to make a key.
 #define AMEDIAMETRICS_KEY_PREFIX_AUDIO "audio."
 
+// Device related key prefix.
+#define AMEDIAMETRICS_KEY_PREFIX_AUDIO_DEVICE  AMEDIAMETRICS_KEY_PREFIX_AUDIO "device."
+
 // The AudioMmap key appends the "trackId" to the prefix.
 // This is the AudioFlinger equivalent of the AAudio Stream.
 // TODO: unify with AMEDIAMETRICS_KEY_PREFIX_AUDIO_STREAM
@@ -92,6 +95,7 @@
 #define AMEDIAMETRICS_PROP_SUFFIX_CHAR_DUPLICATES_ALLOWED '#'
 
 #define AMEDIAMETRICS_PROP_ALLOWUID       "_allowUid"      // int32_t, allow client uid to post
+#define AMEDIAMETRICS_PROP_AUDIOMODE      "audioMode"      // string (audio.flinger)
 #define AMEDIAMETRICS_PROP_AUXEFFECTID    "auxEffectId"    // int32 (AudioTrack)
 #define AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES "bufferSizeFrames" // int32
 #define AMEDIAMETRICS_PROP_BUFFERCAPACITYFRAMES "bufferCapacityFrames" // int32
@@ -112,6 +116,7 @@
 #define AMEDIAMETRICS_PROP_DURATIONNS     "durationNs"     // int64 duration time span
 #define AMEDIAMETRICS_PROP_ENCODING       "encoding"       // string value of format
 #define AMEDIAMETRICS_PROP_EVENT          "event#"         // string value (often func name)
+#define AMEDIAMETRICS_PROP_EXECUTIONTIMENS "executionTimeNs"  // time to execute the event
 
 // TODO: fix inconsistency in flags: AudioRecord / AudioTrack int32,  AudioThread string
 #define AMEDIAMETRICS_PROP_FLAGS          "flags"
@@ -142,10 +147,12 @@
 #define AMEDIAMETRICS_PROP_THREADID       "threadId"       // int32 value io handle
 #define AMEDIAMETRICS_PROP_THROTTLEMS     "throttleMs"     // double
 #define AMEDIAMETRICS_PROP_TRACKID        "trackId"        // int32 port id of track/record
+#define AMEDIAMETRICS_PROP_TRAITS         "traits"         // string
 #define AMEDIAMETRICS_PROP_TYPE           "type"           // string (thread type)
 #define AMEDIAMETRICS_PROP_UNDERRUN       "underrun"       // int32
 #define AMEDIAMETRICS_PROP_UNDERRUNFRAMES "underrunFrames" // int64_t from Thread
 #define AMEDIAMETRICS_PROP_USAGE          "usage"          // string attributes (ATrack)
+#define AMEDIAMETRICS_PROP_VOICEVOLUME    "voiceVolume"    // double (audio.flinger)
 #define AMEDIAMETRICS_PROP_VOLUME_LEFT    "volume.left"    // double (AudioTrack)
 #define AMEDIAMETRICS_PROP_VOLUME_RIGHT   "volume.right"   // double (AudioTrack)
 #define AMEDIAMETRICS_PROP_WHERE          "where"          // string value
@@ -169,8 +176,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_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..439dbe8 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -432,7 +432,7 @@
             }
             if (mDataSource == nullptr) {
                 ALOGD("FileSource local");
-                mDataSource = new PlayerServiceFileSource(mFd.get(), mOffset, mLength);
+                mDataSource = new PlayerServiceFileSource(dup(mFd.get()), mOffset, mLength);
             }
         }
 
@@ -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/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 24afd43..dc144b2 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -116,6 +116,7 @@
     updateMetrics("destructor");
     logMetrics("destructor");
 
+    Mutex::Autolock autoLock(mMetricsLock);
     if (mMetricsItem != NULL) {
         delete mMetricsItem;
         mMetricsItem = NULL;
@@ -129,6 +130,8 @@
 status_t NuPlayerDriver::setUID(uid_t uid) {
     mPlayer->setUID(uid);
     mClientUid = uid;
+
+    Mutex::Autolock autoLock(mMetricsLock);
     if (mMetricsItem) {
         mMetricsItem->setUid(mClientUid);
     }
@@ -542,10 +545,50 @@
     }
     ALOGV("updateMetrics(%p) from %s at state %d", this, where, mState);
 
-    // gather the final stats for this record
+    // avoid nested locks by gathering our data outside of the metrics lock.
+
+    // final track statistics for this record
     Vector<sp<AMessage>> trackStats;
     mPlayer->getStats(&trackStats);
 
+    // getDuration() uses mLock
+    int duration_ms = -1;
+    getDuration(&duration_ms);
+
+    mPlayer->updateInternalTimers();
+
+    int64_t playingTimeUs;
+    int64_t rebufferingTimeUs;
+    int32_t rebufferingEvents;
+    bool rebufferingAtExit;
+    {
+        Mutex::Autolock autoLock(mLock);
+
+        playingTimeUs = mPlayingTimeUs;
+        rebufferingTimeUs = mRebufferingTimeUs;
+        rebufferingEvents = mRebufferingEvents;
+        rebufferingAtExit = mRebufferingAtExit;
+    }
+
+    // finish the rest of the gathering under our mutex to avoid metrics races.
+    // some of the fields we read are updated under mLock.
+    Mutex::Autolock autoLock(mMetricsLock);
+
+    if (mMetricsItem == NULL) {
+        return;
+    }
+
+    mMetricsItem->setInt64(kPlayerDuration, duration_ms);
+    mMetricsItem->setInt64(kPlayerPlaying, (playingTimeUs+500)/1000 );
+
+    if (rebufferingEvents != 0) {
+        mMetricsItem->setInt64(kPlayerRebuffering, (rebufferingTimeUs+500)/1000 );
+        mMetricsItem->setInt32(kPlayerRebufferingCount, rebufferingEvents);
+        mMetricsItem->setInt32(kPlayerRebufferingAtExit, rebufferingAtExit);
+    }
+
+    mMetricsItem->setCString(kPlayerDataSourceType, mPlayer->getDataSourceType());
+
     if (trackStats.size() > 0) {
         for (size_t i = 0; i < trackStats.size(); ++i) {
             const sp<AMessage> &stats = trackStats.itemAt(i);
@@ -590,26 +633,6 @@
             }
         }
     }
-
-    // always provide duration and playing time, even if they have 0/unknown values.
-
-    // getDuration() uses mLock for mutex -- careful where we use it.
-    int duration_ms = -1;
-    getDuration(&duration_ms);
-    mMetricsItem->setInt64(kPlayerDuration, duration_ms);
-
-    mPlayer->updateInternalTimers();
-
-    mMetricsItem->setInt64(kPlayerPlaying, (mPlayingTimeUs+500)/1000 );
-
-    if (mRebufferingEvents != 0) {
-        mMetricsItem->setInt64(kPlayerRebuffering, (mRebufferingTimeUs+500)/1000 );
-        mMetricsItem->setInt32(kPlayerRebufferingCount, mRebufferingEvents);
-        mMetricsItem->setInt32(kPlayerRebufferingAtExit, mRebufferingAtExit);
-
-    }
-
-    mMetricsItem->setCString(kPlayerDataSourceType, mPlayer->getDataSourceType());
 }
 
 
@@ -619,6 +642,9 @@
     }
     ALOGV("logMetrics(%p) from %s at state %d", this, where, mState);
 
+    // ensure mMetricsItem stability while we write it out
+    Mutex::Autolock autoLock(mMetricsLock);
+
     if (mMetricsItem == NULL || mMetricsItem->isEnabled() == false) {
         return;
     }
@@ -777,11 +803,16 @@
 
 status_t NuPlayerDriver::getParameter(int key, Parcel *reply) {
 
-    if (key == FOURCC('m','t','r','X') && mMetricsItem != NULL) {
+    if (key == FOURCC('m','t','r','X')) {
         // mtrX -- a play on 'metrics' (not matrix)
         // gather current info all together, parcel it, and send it back
         updateMetrics("api");
-        mMetricsItem->writeToParcel(reply);
+
+        // ensure mMetricsItem stability while writing to parcel
+        Mutex::Autolock autoLock(mMetricsLock);
+        if (mMetricsItem != NULL) {
+            mMetricsItem->writeToParcel(reply);
+        }
         return OK;
     }
 
@@ -1005,12 +1036,15 @@
             // when we have an error, add it to the analytics for this playback.
             // ext1 is our primary 'error type' value. Only add ext2 when non-zero.
             // [test against msg is due to fall through from previous switch value]
-            if (msg == MEDIA_ERROR && mMetricsItem != NULL) {
-                mMetricsItem->setInt32(kPlayerError, ext1);
-                if (ext2 != 0) {
-                    mMetricsItem->setInt32(kPlayerErrorCode, ext2);
+            if (msg == MEDIA_ERROR) {
+                Mutex::Autolock autoLock(mMetricsLock);
+                if (mMetricsItem != NULL) {
+                    mMetricsItem->setInt32(kPlayerError, ext1);
+                    if (ext2 != 0) {
+                        mMetricsItem->setInt32(kPlayerErrorCode, ext2);
+                    }
+                    mMetricsItem->setCString(kPlayerErrorState, stateString(mState).c_str());
                 }
-                mMetricsItem->setCString(kPlayerErrorState, stateString(mState).c_str());
             }
             mAtEOS = true;
             break;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index 7001f4a..f4b1968 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -142,6 +142,7 @@
     uint32_t mPlayerFlags;
 
     mediametrics::Item *mMetricsItem;
+    mutable Mutex mMetricsLock;
     uid_t mClientUid;
 
     bool mAtEOS;
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..90a0777 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 (fallocate64(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 = fallocate64(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).
@@ -1791,9 +1889,13 @@
     ALOGD("ftruncate mPreAllocateFileEndOffset:%" PRId64 " mOffset:%" PRIu64
           " mMdatEndOffset:%" PRIu64 " diff:%" PRId64, mPreAllocateFileEndOffset, mOffset,
           mMdatEndOffset, mPreAllocateFileEndOffset - endOffset);
-    if(ftruncate(mFd, endOffset) == -1) {
+    if (ftruncate64(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 11f2f38..5d17f97 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -15,6 +15,7 @@
  */
 
 //#define LOG_NDEBUG 0
+#include "hidl/HidlSupport.h"
 #define LOG_TAG "MediaCodec"
 #include <utils/Log.h>
 
@@ -37,6 +38,7 @@
 #include <cutils/properties.h>
 #include <gui/BufferQueue.h>
 #include <gui/Surface.h>
+#include <hidlmemory/FrameworkUtils.h>
 #include <mediadrm/ICrypto.h>
 #include <media/IOMX.h>
 #include <media/MediaCodecBuffer.h>
@@ -55,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>
@@ -93,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 */
@@ -617,7 +623,6 @@
       mFlags(0),
       mStickyError(OK),
       mSoftRenderer(NULL),
-      mMetricsHandle(0),
       mIsVideo(false),
       mVideoWidth(0),
       mVideoHeight(0),
@@ -677,6 +682,8 @@
         mIndexOfFirstFrameWhenLowLatencyOn = -1;
         mInputBufferCounter = 0;
     }
+
+    mLifetimeStartNs = systemTime(SYSTEM_TIME_MONOTONIC);
 }
 
 void MediaCodec::updateMediametrics() {
@@ -685,7 +692,6 @@
         return;
     }
 
-
     if (mLatencyHist.getCount() != 0 ) {
         mediametrics_setInt64(mMetricsHandle, kCodecLatencyMax, mLatencyHist.getMax());
         mediametrics_setInt64(mMetricsHandle, kCodecLatencyMin, mLatencyHist.getMin());
@@ -701,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);
@@ -738,7 +749,6 @@
         }
     }
 
-
     // spit the data (if any) into the supplied analytics record
     if (recentHist.getCount()!= 0 ) {
         mediametrics_setInt64(item, kCodecRecentLatencyMax, recentHist.getMax());
@@ -1472,9 +1482,9 @@
     return PostAndAwaitResponse(msg, &response);
 }
 
-status_t MediaCodec::releaseAsync() {
+status_t MediaCodec::releaseAsync(const sp<AMessage> &notify) {
     sp<AMessage> msg = new AMessage(kWhatRelease, this);
-    msg->setInt32("async", 1);
+    msg->setMessage("async", notify);
     sp<AMessage> response;
     return PostAndAwaitResponse(msg, &response);
 }
@@ -2307,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(),
@@ -2320,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 &&
@@ -2331,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;
                 }
@@ -2459,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) {
@@ -2641,10 +2690,16 @@
                     }
 
                     mResourceManagerProxy->removeClient();
+                    mReleaseSurface.reset();
 
                     if (mReplyID != nullptr) {
                         (new AMessage)->postReply(mReplyID);
                     }
+                    if (mAsyncReleaseCompleteNotification != nullptr) {
+                        flushMediametrics();
+                        mAsyncReleaseCompleteNotification->post();
+                        mAsyncReleaseCompleteNotification.clear();
+                    }
                     break;
                 }
 
@@ -3031,22 +3086,23 @@
                 break;
             }
 
-            int32_t async = 0;
-            if (msg->findInt32("async", &async) && async) {
-                if ((mState ==  CONFIGURED || mState == STARTED || mState == FLUSHED)
-                       && mSurface != NULL) {
+            sp<AMessage> asyncNotify;
+            if (msg->findMessage("async", &asyncNotify) && asyncNotify != nullptr) {
+                if (mSurface != NULL) {
                     if (!mReleaseSurface) {
                         mReleaseSurface.reset(new ReleaseSurface);
                     }
-                    status_t err = connectToSurface(mReleaseSurface->getSurface());
-                    ALOGW_IF(err != OK, "error connecting to release surface: err = %d", err);
-                    if (err == OK && !(mFlags & kFlagUsesSoftwareRenderer)) {
-                        err = mCodec->setSurface(mReleaseSurface->getSurface());
-                        ALOGW_IF(err != OK, "error setting release surface: err = %d", err);
-                    }
-                    if (err == OK) {
-                        (void)disconnectFromSurface();
-                        mSurface = mReleaseSurface->getSurface();
+                    if (mSurface != mReleaseSurface->getSurface()) {
+                        status_t err = connectToSurface(mReleaseSurface->getSurface());
+                        ALOGW_IF(err != OK, "error connecting to release surface: err = %d", err);
+                        if (err == OK && !(mFlags & kFlagUsesSoftwareRenderer)) {
+                            err = mCodec->setSurface(mReleaseSurface->getSurface());
+                            ALOGW_IF(err != OK, "error setting release surface: err = %d", err);
+                        }
+                        if (err == OK) {
+                            (void)disconnectFromSurface();
+                            mSurface = mReleaseSurface->getSurface();
+                        }
                     }
                 }
             }
@@ -3063,10 +3119,11 @@
                 pushBlankBuffersToNativeWindow(mSurface.get());
             }
 
-            if (async) {
+            if (asyncNotify != nullptr) {
                 mResourceManagerProxy->markClientForPendingRemoval();
                 (new AMessage)->postReply(mReplyID);
                 mReplyID = 0;
+                mAsyncReleaseCompleteNotification = asyncNotify;
             }
 
             break;
@@ -3146,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;
@@ -3430,19 +3495,42 @@
     sp<ABuffer> csd = *mCSD.begin();
     mCSD.erase(mCSD.begin());
     std::shared_ptr<C2Buffer> c2Buffer;
+    sp<hardware::HidlMemory> memory;
+    size_t offset = 0;
 
-    if ((mFlags & kFlagUseBlockModel) && mOwnerName.startsWith("codec2::")) {
-        std::shared_ptr<C2LinearBlock> block =
-            FetchLinearBlock(csd->size(), {std::string{mComponentName.c_str()}});
-        C2WriteView view{block->map().get()};
-        if (view.error() != C2_OK) {
-            return -EINVAL;
+    if (mFlags & kFlagUseBlockModel) {
+        if (hasCryptoOrDescrambler()) {
+            constexpr size_t kInitialDealerCapacity = 1048576;  // 1MB
+            thread_local sp<MemoryDealer> sDealer = new MemoryDealer(
+                    kInitialDealerCapacity, "CSD(1MB)");
+            sp<IMemory> mem = sDealer->allocate(csd->size());
+            if (mem == nullptr) {
+                size_t newDealerCapacity = sDealer->getMemoryHeap()->getSize() * 2;
+                while (csd->size() * 2 > newDealerCapacity) {
+                    newDealerCapacity *= 2;
+                }
+                sDealer = new MemoryDealer(
+                        newDealerCapacity,
+                        AStringPrintf("CSD(%dMB)", newDealerCapacity / 1048576).c_str());
+                mem = sDealer->allocate(csd->size());
+            }
+            memcpy(mem->unsecurePointer(), csd->data(), csd->size());
+            ssize_t heapOffset;
+            memory = hardware::fromHeap(mem->getMemory(&heapOffset, nullptr));
+            offset += heapOffset;
+        } else {
+            std::shared_ptr<C2LinearBlock> block =
+                FetchLinearBlock(csd->size(), {std::string{mComponentName.c_str()}});
+            C2WriteView view{block->map().get()};
+            if (view.error() != C2_OK) {
+                return -EINVAL;
+            }
+            if (csd->size() > view.capacity()) {
+                return -EINVAL;
+            }
+            memcpy(view.base(), csd->data(), csd->size());
+            c2Buffer = C2Buffer::CreateLinearBuffer(block->share(0, csd->size(), C2Fence{}));
         }
-        if (csd->size() > view.capacity()) {
-            return -EINVAL;
-        }
-        memcpy(view.base(), csd->data(), csd->size());
-        c2Buffer = C2Buffer::CreateLinearBuffer(block->share(0, csd->size(), C2Fence{}));
     } else {
         const BufferInfo &info = mPortBuffers[kPortIndexInput][bufferIndex];
         const sp<MediaCodecBuffer> &codecInputData = info.mData;
@@ -3472,6 +3560,11 @@
             new WrapperObject<std::shared_ptr<C2Buffer>>{c2Buffer}};
         msg->setObject("c2buffer", obj);
         msg->setMessage("tunings", new AMessage);
+    } else if (memory) {
+        sp<WrapperObject<sp<hardware::HidlMemory>>> obj{
+            new WrapperObject<sp<hardware::HidlMemory>>{memory}};
+        msg->setObject("memory", obj);
+        msg->setMessage("tunings", new AMessage);
     }
 
     return onQueueInputBuffer(msg);
@@ -3531,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];
 
@@ -3589,6 +3685,7 @@
     } else if (msg->findObject("memory", &obj)) {
         CHECK(obj);
         memory = static_cast<WrapperObject<sp<hardware::HidlMemory>> *>(obj.get())->value;
+        CHECK(msg->findSize("offset", &offset));
     } else {
         CHECK(msg->findSize("offset", &offset));
     }
@@ -3660,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) {
@@ -3687,7 +3803,7 @@
     }
 
     status_t err = OK;
-    if (hasCryptoOrDescrambler()) {
+    if (hasCryptoOrDescrambler() && !c2Buffer && !memory) {
         AString *errorDetailMsg;
         CHECK(msg->findPointer("errorDetailMsg", (void **)&errorDetailMsg));
 
@@ -3725,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/amrnb/common/Android.bp b/media/libstagefright/codecs/amrnb/common/Android.bp
index bcf63d5..59a791d 100644
--- a/media/libstagefright/codecs/amrnb/common/Android.bp
+++ b/media/libstagefright/codecs/amrnb/common/Android.bp
@@ -2,6 +2,7 @@
     name: "libstagefright_amrnb_common",
     vendor_available: true,
     host_supported: true,
+    min_sdk_version: "29",
 
     srcs: [
         "src/add.cpp",
diff --git a/media/libstagefright/codecs/amrnb/dec/Android.bp b/media/libstagefright/codecs/amrnb/dec/Android.bp
index 3381d2e..b8e00b3 100644
--- a/media/libstagefright/codecs/amrnb/dec/Android.bp
+++ b/media/libstagefright/codecs/amrnb/dec/Android.bp
@@ -2,6 +2,7 @@
     name: "libstagefright_amrnbdec",
     vendor_available: true,
     host_supported: true,
+    min_sdk_version: "29",
 
     srcs: [
         "src/a_refl.cpp",
diff --git a/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp
index 34dd011..cdfc03a 100644
--- a/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp
+++ b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp
@@ -20,9 +20,6 @@
 
 #include "SoftAMR.h"
 
-#include "gsmamr_dec.h"
-#include "pvamrwbdecoder.h"
-
 #include <media/stagefright/foundation/ADebug.h>
 
 namespace android {
@@ -470,11 +467,10 @@
                 memset(outPtr, 0, kNumSamplesPerFrameWB * sizeof(int16_t));
             } else if (mode < 9) {
                 int16 frameType;
-                RX_State_wb rx_state;
                 mime_unsorting(
                         const_cast<uint8_t *>(&inputPtr[1]),
                         mInputSampleBuffer,
-                        &frameType, &mode, 1, &rx_state);
+                        &frameType, &mode, 1, &mRxState);
 
                 int16_t numSamplesOutput;
                 pvDecoder_AmrWb(
diff --git a/media/libstagefright/codecs/amrnb/dec/SoftAMR.h b/media/libstagefright/codecs/amrnb/dec/SoftAMR.h
index 869b81d..d5aaed3 100644
--- a/media/libstagefright/codecs/amrnb/dec/SoftAMR.h
+++ b/media/libstagefright/codecs/amrnb/dec/SoftAMR.h
@@ -19,6 +19,8 @@
 #define SOFT_AMR_H_
 
 #include <media/stagefright/omx/SimpleSoftOMXComponent.h>
+#include "gsmamr_dec.h"
+#include "pvamrwbdecoder.h"
 
 namespace android {
 
@@ -60,6 +62,7 @@
     void *mState;
     void *mDecoderBuf;
     int16_t *mDecoderCookie;
+    RX_State_wb mRxState{};
 
     size_t mInputBufferCount;
     int64_t mAnchorTimeUs;
diff --git a/media/libstagefright/codecs/amrnb/enc/Android.bp b/media/libstagefright/codecs/amrnb/enc/Android.bp
index 438ed04..73a1d4b 100644
--- a/media/libstagefright/codecs/amrnb/enc/Android.bp
+++ b/media/libstagefright/codecs/amrnb/enc/Android.bp
@@ -1,6 +1,7 @@
 cc_library_static {
     name: "libstagefright_amrnbenc",
     vendor_available: true,
+    min_sdk_version: "29",
 
     srcs: [
         "src/amrencode.cpp",
diff --git a/media/libstagefright/codecs/amrwb/Android.bp b/media/libstagefright/codecs/amrwb/Android.bp
index d8cb568..204cbe3 100644
--- a/media/libstagefright/codecs/amrwb/Android.bp
+++ b/media/libstagefright/codecs/amrwb/Android.bp
@@ -2,6 +2,7 @@
     name: "libstagefright_amrwbdec",
     vendor_available: true,
     host_supported: true,
+    min_sdk_version: "29",
 
     srcs: [
         "src/agc2_amr_wb.cpp",
diff --git a/media/libstagefright/codecs/amrwb/fuzzer/amrwb_dec_fuzzer.cpp b/media/libstagefright/codecs/amrwb/fuzzer/amrwb_dec_fuzzer.cpp
index 6dc0270..592a6ec 100644
--- a/media/libstagefright/codecs/amrwb/fuzzer/amrwb_dec_fuzzer.cpp
+++ b/media/libstagefright/codecs/amrwb/fuzzer/amrwb_dec_fuzzer.cpp
@@ -65,6 +65,7 @@
 }
 
 void Codec::decodeFrames(const uint8_t *data, size_t size) {
+  RX_State_wb rx_state{};
   while (size > 0) {
     uint8_t modeByte = *data;
     bool quality = modeByte & 0x01;
@@ -81,7 +82,6 @@
     memcpy(inputBuf, data, minSize);
     int16 frameMode = mode;
     int16 frameType;
-    RX_State_wb rx_state;
     mime_unsorting(inputBuf, inputSampleBuf, &frameType, &frameMode, quality, &rx_state);
 
     int16_t numSamplesOutput;
diff --git a/media/libstagefright/codecs/amrwb/test/AmrwbDecoderTest.cpp b/media/libstagefright/codecs/amrwb/test/AmrwbDecoderTest.cpp
index 2aad81b..7221b92 100644
--- a/media/libstagefright/codecs/amrwb/test/AmrwbDecoderTest.cpp
+++ b/media/libstagefright/codecs/amrwb/test/AmrwbDecoderTest.cpp
@@ -74,6 +74,7 @@
     uint8_t inputBuf[kInputBufferSize];
     int16_t inputSampleBuf[kMaxSourceDataUnitSize];
     int16_t outputBuf[kOutputBufferSize];
+    RX_State_wb rx_state{};
 
     while (frameCount > 0) {
         uint8_t modeByte;
@@ -98,7 +99,6 @@
 
             int16 frameMode = mode;
             int16 frameType;
-            RX_State_wb rx_state;
             mime_unsorting(inputBuf, inputSampleBuf, &frameType, &frameMode, 1, &rx_state);
 
             int16_t numSamplesOutput;
diff --git a/media/libstagefright/codecs/amrwb/test/amrwbdec_test.cpp b/media/libstagefright/codecs/amrwb/test/amrwbdec_test.cpp
index b04bafd..1bc90e8 100644
--- a/media/libstagefright/codecs/amrwb/test/amrwbdec_test.cpp
+++ b/media/libstagefright/codecs/amrwb/test/amrwbdec_test.cpp
@@ -110,6 +110,7 @@
 
     // Decode loop.
     int retVal = EXIT_SUCCESS;
+    RX_State_wb rx_state{};
     while (1) {
         // Read mode.
         uint8_t modeByte;
@@ -134,7 +135,6 @@
             if (bytesRead != frameSize) break;
 
             int16 frameType, frameMode;
-            RX_State_wb rx_state;
             frameMode = mode;
             mime_unsorting(
                     (uint8_t *)inputBuf,
diff --git a/media/libstagefright/codecs/amrwbenc/Android.bp b/media/libstagefright/codecs/amrwbenc/Android.bp
index 084be0a..64f302c 100644
--- a/media/libstagefright/codecs/amrwbenc/Android.bp
+++ b/media/libstagefright/codecs/amrwbenc/Android.bp
@@ -1,6 +1,7 @@
 cc_library_static {
     name: "libstagefright_amrwbenc",
     vendor_available: true,
+    min_sdk_version: "29",
 
     srcs: [
         "src/autocorr.c",
diff --git a/media/libstagefright/codecs/common/Android.bp b/media/libstagefright/codecs/common/Android.bp
index c5a076a..260a60a 100644
--- a/media/libstagefright/codecs/common/Android.bp
+++ b/media/libstagefright/codecs/common/Android.bp
@@ -1,6 +1,7 @@
 cc_library {
     name: "libstagefright_enc_common",
     vendor_available: true,
+    min_sdk_version: "29",
 
     srcs: ["cmnMemory.c"],
 
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/codecs/mp3dec/Android.bp b/media/libstagefright/codecs/mp3dec/Android.bp
index b630524..96106f1 100644
--- a/media/libstagefright/codecs/mp3dec/Android.bp
+++ b/media/libstagefright/codecs/mp3dec/Android.bp
@@ -1,6 +1,7 @@
 cc_library_static {
     name: "libstagefright_mp3dec",
     vendor_available: true,
+    min_sdk_version: "29",
 
     srcs: [
         "src/pvmp3_normalize.cpp",
diff --git a/media/libstagefright/data/media_codecs_sw.xml b/media/libstagefright/data/media_codecs_sw.xml
index dd2eed3..6571162 100644
--- a/media/libstagefright/data/media_codecs_sw.xml
+++ b/media/libstagefright/data/media_codecs_sw.xml
@@ -295,12 +295,12 @@
             <Feature name="bitrate-modes" value="VBR,CBR" />
         </MediaCodec>
         <MediaCodec name="c2.android.hevc.encoder" type="video/hevc" variant="!slow-cpu">
-            <!-- profiles and levels:  ProfileMain : MainTierLevel51 -->
-            <Limit name="size" min="2x2" max="512x512" />
+            <!-- profiles and levels:  ProfileMain : MainTierLevel3 -->
+            <Limit name="size" min="2x2" max="960x544" />
             <Limit name="alignment" value="2x2" />
             <Limit name="block-size" value="8x8" />
-            <Limit name="block-count" range="1-4096" /> <!-- max 512x512 -->
-            <Limit name="blocks-per-second" range="1-122880" />
+            <Limit name="block-count" range="1-8160" /> <!-- max 960x544 -->
+            <Limit name="blocks-per-second" range="1-244880" />
             <Limit name="frame-rate" range="1-120" />
             <Limit name="bitrate" range="1-10000000" />
             <Limit name="complexity" range="0-10"  default="0" />
diff --git a/media/libstagefright/flac/dec/Android.bp b/media/libstagefright/flac/dec/Android.bp
index d65a663..32b2075 100644
--- a/media/libstagefright/flac/dec/Android.bp
+++ b/media/libstagefright/flac/dec/Android.bp
@@ -1,6 +1,7 @@
 cc_library {
     name: "libstagefright_flacdec",
     vendor_available: true,
+    min_sdk_version: "29",
 
     srcs: [
         "FLACDecoder.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/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index e0324e3..4324c45 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -615,7 +615,7 @@
                 if (mIsVariantPlaylist) {
                     return ERROR_MALFORMED;
                 }
-                err = parseCipherInfo(line, &itemMeta, mBaseURI);
+                err = parseCipherInfo(line, &itemMeta);
             } else if (line.startsWith("#EXT-X-ENDLIST")) {
                 mIsComplete = true;
             } else if (line.startsWith("#EXT-X-PLAYLIST-TYPE:EVENT")) {
@@ -936,7 +936,7 @@
 
 // static
 status_t M3UParser::parseCipherInfo(
-        const AString &line, sp<AMessage> *meta, const AString &baseURI) {
+        const AString &line, sp<AMessage> *meta) {
     ssize_t colonPos = line.find(":");
 
     if (colonPos < 0) {
@@ -985,13 +985,9 @@
                     val = tmp;
                 }
 
-                AString absURI;
-                if (MakeURL(baseURI.c_str(), val.c_str(), &absURI)) {
-                    val = absURI;
-                } else {
-                    ALOGE("failed to make absolute url for %s.",
-                            uriDebugString(baseURI).c_str());
-                }
+                // To save space, we only store the partial Uri here
+                // The full Uri will be constructed from this plus
+                // the base Uri as needed by PlaylistFetcher
             }
 
             key.insert(AString("cipher-"), 0);
@@ -1003,6 +999,14 @@
     return OK;
 }
 
+AString M3UParser::getFullCipherUri(const AString &partial) {
+    AString full;
+    if (MakeURL(mBaseURI.c_str(), partial.c_str(), &full)) {
+        return full;
+    }
+    return AString("");
+}
+
 // static
 status_t M3UParser::parseByteRange(
         const AString &line, uint64_t curOffset,
diff --git a/media/libstagefright/httplive/M3UParser.h b/media/libstagefright/httplive/M3UParser.h
index c85335a..9f7a66a 100644
--- a/media/libstagefright/httplive/M3UParser.h
+++ b/media/libstagefright/httplive/M3UParser.h
@@ -55,6 +55,8 @@
     bool getTypeURI(size_t index, const char *key, AString *uri) const;
     bool hasType(size_t index, const char *key) const;
 
+    AString getFullCipherUri(const AString &partial);
+
 protected:
     virtual ~M3UParser();
 
@@ -99,7 +101,7 @@
             const AString &line, sp<AMessage> *meta) const;
 
     static status_t parseCipherInfo(
-            const AString &line, sp<AMessage> *meta, const AString &baseURI);
+            const AString &line, sp<AMessage> *meta);
 
     static status_t parseByteRange(
             const AString &line, uint64_t curOffset,
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index fdcde29..b23aa8a 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -345,6 +345,7 @@
         ALOGE("Missing key uri");
         return ERROR_MALFORMED;
     }
+    keyURI = mPlaylist->getFullCipherUri(keyURI);
 
     ssize_t index = mAESKeyForURI.indexOfKey(keyURI);
 
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 63a9dad..f7e6c27 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -139,7 +139,7 @@
     // object.
     status_t release();
 
-    status_t releaseAsync();
+    status_t releaseAsync(const sp<AMessage> &notify);
 
     status_t flush();
 
@@ -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();
@@ -382,6 +383,7 @@
     sp<AMessage> mInputFormat;
     sp<AMessage> mCallback;
     sp<AMessage> mOnFrameRenderedNotification;
+    sp<AMessage> mAsyncReleaseCompleteNotification;
 
     sp<ResourceManagerServiceProxy> mResourceManagerProxy;
 
@@ -514,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/mpeg2ts/Android.bp b/media/libstagefright/mpeg2ts/Android.bp
index 42afea3..fbb2d0c 100644
--- a/media/libstagefright/mpeg2ts/Android.bp
+++ b/media/libstagefright/mpeg2ts/Android.bp
@@ -46,4 +46,6 @@
     whole_static_libs: [
         "libstagefright_metadatautils",
     ],
+
+    min_sdk_version: "29",
 }
diff --git a/media/libstagefright/omx/OMXMaster.cpp b/media/libstagefright/omx/OMXMaster.cpp
index 8c5ca6e..094b1f5 100644
--- a/media/libstagefright/omx/OMXMaster.cpp
+++ b/media/libstagefright/omx/OMXMaster.cpp
@@ -16,6 +16,7 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "OMXMaster"
+#include <android-base/properties.h>
 #include <utils/Log.h>
 
 #include <media/stagefright/omx/OMXMaster.h>
@@ -67,6 +68,10 @@
 }
 
 void OMXMaster::addPlugin(const char *libname) {
+    if (::android::base::GetIntProperty("vendor.media.omx", int64_t(1)) == 0) {
+        return;
+    }
+
     void *libHandle = android_load_sphal_library(libname, RTLD_NOW);
 
     if (libHandle == NULL) {
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/mediaserver/Android.bp b/media/mediaserver/Android.bp
index a968890..afca7c4 100644
--- a/media/mediaserver/Android.bp
+++ b/media/mediaserver/Android.bp
@@ -15,13 +15,14 @@
     srcs: ["main_mediaserver.cpp"],
 
     shared_libs: [
-        "libresourcemanagerservice",
+        "android.hardware.media.omx@1.0",
+        "libandroidicu",
+        "libbinder",
+        "libhidlbase",
         "liblog",
         "libmediaplayerservice",
+        "libresourcemanagerservice",
         "libutils",
-        "libbinder",
-        "libandroidicu",
-        "android.hardware.media.omx@1.0",
     ],
 
     static_libs: [
diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp
index 7b22b05..316732b 100644
--- a/media/mediaserver/main_mediaserver.cpp
+++ b/media/mediaserver/main_mediaserver.cpp
@@ -22,6 +22,7 @@
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
 #include <binder/IServiceManager.h>
+#include <hidl/HidlTransportSupport.h>
 #include <utils/Log.h>
 #include "RegisterExtensions.h"
 
@@ -42,6 +43,8 @@
     MediaPlayerService::instantiate();
     ResourceManagerService::instantiate();
     registerExtensions();
+    ::android::hardware::configureRpcThreadpool(16, false);
     ProcessState::self()->startThreadPool();
     IPCThreadState::self()->joinThreadPool();
+    ::android::hardware::joinRpcThreadpool();
 }
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/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index de7ae40..f014209 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1176,6 +1176,10 @@
             mPlaybackThreads.valueAt(i)->setMode(mode);
     }
 
+    mediametrics::LogItem(mMetricsId)
+        .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETMODE)
+        .set(AMEDIAMETRICS_PROP_AUDIOMODE, toString(mode))
+        .record();
     return ret;
 }
 
@@ -1741,6 +1745,10 @@
     ret = dev->setVoiceVolume(value);
     mHardwareStatus = AUDIO_HW_IDLE;
 
+    mediametrics::LogItem(mMetricsId)
+        .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOICEVOLUME)
+        .set(AMEDIAMETRICS_PROP_VOICEVOLUME, (double)value)
+        .record();
     return ret;
 }
 
diff --git a/services/audioflinger/AudioStreamOut.cpp b/services/audioflinger/AudioStreamOut.cpp
index d13cb8f..7e06096 100644
--- a/services/audioflinger/AudioStreamOut.cpp
+++ b/services/audioflinger/AudioStreamOut.cpp
@@ -164,6 +164,10 @@
         stream = outStream;
         mHalFormatHasProportionalFrames = audio_has_proportional_frames(config->format);
         status = stream->getFrameSize(&mHalFrameSize);
+        LOG_ALWAYS_FATAL_IF(status != OK, "Error retrieving frame size from HAL: %d", status);
+        LOG_ALWAYS_FATAL_IF(mHalFrameSize <= 0, "Error frame size was %zu but must be greater than"
+                " zero", mHalFrameSize);
+
     }
 
     return status;
diff --git a/services/audioflinger/DeviceEffectManager.cpp b/services/audioflinger/DeviceEffectManager.cpp
index a3c3b84..5ff7215 100644
--- a/services/audioflinger/DeviceEffectManager.cpp
+++ b/services/audioflinger/DeviceEffectManager.cpp
@@ -144,7 +144,8 @@
         write(fd, result.string(), result.size());
     }
 
-    write(fd, "\nDevice Effects:\n", sizeof("\nDevice Effects:\n"));
+    String8 heading("\nDevice Effects:\n");
+    write(fd, heading.string(), heading.size());
     for (const auto& iter : mDeviceEffects) {
         String8 outStr;
         outStr.appendFormat("%*sEffect for device %s address %s:\n", 2, "",
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 55f2952..3dfeb83 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -2812,7 +2812,7 @@
     if (t == nullptr) {
         return false;
     }
-    return t->type() == ThreadBase::OFFLOAD || t->type() == ThreadBase::MMAP;
+    return t->isOffloadOrMmap();
 }
 
 uint32_t AudioFlinger::EffectChain::EffectCallback::sampleRate() const {
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/ThreadMetrics.h b/services/audioflinger/ThreadMetrics.h
index 7989de1..6526655 100644
--- a/services/audioflinger/ThreadMetrics.h
+++ b/services/audioflinger/ThreadMetrics.h
@@ -58,9 +58,11 @@
     // 2) We come out of standby
     void logBeginInterval() {
         std::lock_guard l(mLock);
-        if (mDevices != mCreatePatchDevices) {
+        // The devices we look for change depend on whether the Thread is input or output.
+        const std::string& patchDevices = mIsOut ? mCreatePatchOutDevices : mCreatePatchInDevices;
+        if (mDevices != patchDevices) {
             deliverCumulativeMetrics(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP);
-            mDevices = mCreatePatchDevices; // set after endAudioIntervalGroup
+            mDevices = patchDevices; // set after endAudioIntervalGroup
             resetIntervalGroupMetrics();
             deliverDeviceMetrics(
                     AMEDIAMETRICS_PROP_EVENT_VALUE_BEGINAUDIOINTERVALGROUP, mDevices.c_str());
@@ -80,12 +82,14 @@
             .record();
     }
 
-    void logCreatePatch(const std::string& devices) {
+    void logCreatePatch(const std::string& inDevices, const std::string& outDevices) {
         std::lock_guard l(mLock);
-        mCreatePatchDevices = devices;
+        mCreatePatchInDevices = inDevices;
+        mCreatePatchOutDevices = outDevices;
         mediametrics::LogItem(mMetricsId)
             .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CREATEAUDIOPATCH)
-            .set(AMEDIAMETRICS_PROP_OUTPUTDEVICES, devices)
+            .set(AMEDIAMETRICS_PROP_INPUTDEVICES, inDevices)
+            .set(AMEDIAMETRICS_PROP_OUTPUTDEVICES, outDevices)
             .record();
     }
 
@@ -115,11 +119,13 @@
         mDeviceLatencyMs.add(latencyMs);
     }
 
-    // TODO: further implement this.
-    void logUnderrunFrames(size_t count, size_t frames) {
+    void logUnderrunFrames(size_t frames) {
         std::lock_guard l(mLock);
-        mUnderrunCount = count;
-        mUnderrunFrames = frames;
+        if (mLastUnderrun == false && frames > 0) {
+            ++mUnderrunCount; // count non-continguous underrun sequences.
+        }
+        mLastUnderrun = (frames > 0);
+        mUnderrunFrames += frames;
     }
 
     const std::string& getMetricsId() const {
@@ -164,6 +170,7 @@
 
         mDeviceLatencyMs.reset();
 
+        mLastUnderrun = false;
         mUnderrunCount = 0;
         mUnderrunFrames = 0;
     }
@@ -174,8 +181,9 @@
     mutable           std::mutex mLock;
 
     // Devices in the interval group.
-    std::string       mDevices GUARDED_BY(mLock);
-    std::string       mCreatePatchDevices GUARDED_BY(mLock);
+    std::string       mDevices GUARDED_BY(mLock); // last input or output devices based on mIsOut.
+    std::string       mCreatePatchInDevices GUARDED_BY(mLock);
+    std::string       mCreatePatchOutDevices GUARDED_BY(mLock);
 
     // Number of intervals and playing time
     int32_t           mIntervalCount GUARDED_BY(mLock) = 0;
@@ -187,8 +195,9 @@
     audio_utils::Statistics<double> mDeviceLatencyMs GUARDED_BY(mLock);
 
     // underrun count and frames
-    int64_t           mUnderrunCount GUARDED_BY(mLock) = 0;
-    int64_t           mUnderrunFrames GUARDED_BY(mLock) = 0;
+    bool              mLastUnderrun GUARDED_BY(mLock) = false; // checks consecutive underruns
+    int64_t           mUnderrunCount GUARDED_BY(mLock) = 0;    // number of consecutive underruns
+    int64_t           mUnderrunFrames GUARDED_BY(mLock) = 0;   // total estimated frames underrun
 };
 
 } // namespace android
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 594baf8..2af27d8 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -484,8 +484,10 @@
         return "RECORD";
     case OFFLOAD:
         return "OFFLOAD";
-    case MMAP:
-        return "MMAP";
+    case MMAP_PLAYBACK:
+        return "MMAP_PLAYBACK";
+    case MMAP_CAPTURE:
+        return "MMAP_CAPTURE";
     default:
         return "unknown";
     }
@@ -967,8 +969,10 @@
         return String16("AudioIn");
     case OFFLOAD:
         return String16("AudioOffload");
-    case MMAP:
-        return String16("Mmap");
+    case MMAP_PLAYBACK:
+        return String16("MmapPlayback");
+    case MMAP_CAPTURE:
+        return String16("MmapCapture");
     default:
         ALOG_ASSERT(false);
         return String16("AudioUnknown");
@@ -1477,7 +1481,7 @@
 }
 
 void AudioFlinger::ThreadBase::onEffectEnable(const sp<EffectModule>& effect) {
-    if (mType == OFFLOAD || mType == MMAP) {
+    if (isOffloadOrMmap()) {
         Mutex::Autolock _l(mLock);
         broadcast_l();
     }
@@ -1493,7 +1497,7 @@
 }
 
 void AudioFlinger::ThreadBase::onEffectDisable() {
-    if (mType == OFFLOAD || mType == MMAP) {
+    if (isOffloadOrMmap()) {
         Mutex::Autolock _l(mLock);
         broadcast_l();
     }
@@ -4260,7 +4264,7 @@
     const std::string patchSinksAsString = patchSinksToString(patch);
 
     mThreadMetrics.logEndInterval();
-    mThreadMetrics.logCreatePatch(patchSinksAsString);
+    mThreadMetrics.logCreatePatch(/* inDevices */ {}, patchSinksAsString);
     mThreadMetrics.logBeginInterval();
     // also dispatch to active AudioTracks for MediaMetrics
     for (const auto &track : mActiveTracks) {
@@ -4809,19 +4813,24 @@
     // DeferredOperations handles statistics after setting mixerStatus.
     class DeferredOperations {
     public:
-        explicit DeferredOperations(mixer_state *mixerStatus)
-            : mMixerStatus(mixerStatus) {}
+        DeferredOperations(mixer_state *mixerStatus, ThreadMetrics *threadMetrics)
+            : mMixerStatus(mixerStatus)
+            , mThreadMetrics(threadMetrics) {}
 
         // when leaving scope, tally frames properly.
         ~DeferredOperations() {
             // Tally underrun frames only if we are actually mixing (MIXER_TRACKS_READY)
             // because that is when the underrun occurs.
             // We do not distinguish between FastTracks and NormalTracks here.
+            size_t maxUnderrunFrames = 0;
             if (*mMixerStatus == MIXER_TRACKS_READY && mUnderrunFrames.size() > 0) {
                 for (const auto &underrun : mUnderrunFrames) {
                     underrun.first->tallyUnderrunFrames(underrun.second);
+                    maxUnderrunFrames = max(underrun.second, maxUnderrunFrames);
                 }
             }
+            // send the max underrun frames for this mixer period
+            mThreadMetrics->logUnderrunFrames(maxUnderrunFrames);
         }
 
         // tallyUnderrunFrames() is called to update the track counters
@@ -4833,8 +4842,9 @@
 
     private:
         const mixer_state * const mMixerStatus;
+        ThreadMetrics * const mThreadMetrics;
         std::vector<std::pair<sp<Track>, size_t>> mUnderrunFrames;
-    } deferredOperations(&mixerStatus);
+    } deferredOperations(&mixerStatus, &mThreadMetrics);
     // implicit nested scope for variable capture
 
     bool noFastHapticTrack = true;
@@ -6046,10 +6056,6 @@
     bool trackPaused = false;
     bool trackStopped = false;
 
-    if ((mType == DIRECT) && audio_is_linear_pcm(mFormat) && !usesHwAvSync()) {
-        return !mStandby;
-    }
-
     // do not put the HAL in standby when paused. AwesomePlayer clear the offloaded AudioTrack
     // after a timeout and we will enter standby then.
     if (mTracks.size() > 0) {
@@ -7073,6 +7079,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);
 
@@ -7160,14 +7168,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);
@@ -7241,6 +7268,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);
@@ -7325,8 +7356,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 */) {
@@ -8393,13 +8426,14 @@
     }
     result = mInput->stream->getFrameSize(&mFrameSize);
     LOG_ALWAYS_FATAL_IF(result != OK, "Error retrieving frame size from HAL: %d", result);
+    LOG_ALWAYS_FATAL_IF(mFrameSize <= 0, "Error frame size was %zu but must be greater than zero",
+            mFrameSize);
     result = mInput->stream->getBufferSize(&mBufferSize);
     LOG_ALWAYS_FATAL_IF(result != OK, "Error retrieving buffer size from HAL: %d", result);
     mFrameCount = mBufferSize / mFrameSize;
-    ALOGV("%p RecordThread params: mChannelCount=%u, mFormat=%#x, mFrameSize=%lld, "
-            "mBufferSize=%lld, mFrameCount=%lld",
-            this, mChannelCount, mFormat, (long long)mFrameSize, (long long)mBufferSize,
-            (long long)mFrameCount);
+    ALOGV("%p RecordThread params: mChannelCount=%u, mFormat=%#x, mFrameSize=%zu, "
+            "mBufferSize=%zu, mFrameCount=%zu",
+            this, mChannelCount, mFormat, mFrameSize, mBufferSize, mFrameCount);
     // This is the formula for calculating the temporary buffer size.
     // With 7 HAL buffers, we can guarantee ability to down-sample the input by ratio of 6:1 to
     // 1 full output buffer, regardless of the alignment of the available input.
@@ -8428,6 +8462,17 @@
 
     // AudioRecord mSampleRate and mChannelCount are constant due to AudioRecord API constraints.
     // But if thread's mSampleRate or mChannelCount changes, how will that affect active tracks?
+
+    audio_input_flags_t flags = mInput->flags;
+    mediametrics::LogItem item(mThreadMetrics.getMetricsId());
+    item.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_READPARAMETERS)
+        .set(AMEDIAMETRICS_PROP_ENCODING, formatToString(mFormat).c_str())
+        .set(AMEDIAMETRICS_PROP_FLAGS, toString(flags).c_str())
+        .set(AMEDIAMETRICS_PROP_SAMPLERATE, (int32_t)mSampleRate)
+        .set(AMEDIAMETRICS_PROP_CHANNELMASK, (int32_t)mChannelMask)
+        .set(AMEDIAMETRICS_PROP_CHANNELCOUNT, (int32_t)mChannelCount)
+        .set(AMEDIAMETRICS_PROP_FRAMECOUNT, (int32_t)mFrameCount)
+        .record();
 }
 
 uint32_t AudioFlinger::RecordThread::getInputFramesLost()
@@ -8558,7 +8603,7 @@
 
     const std::string pathSourcesAsString = patchSourcesToString(patch);
     mThreadMetrics.logEndInterval();
-    mThreadMetrics.logCreatePatch(pathSourcesAsString);
+    mThreadMetrics.logCreatePatch(pathSourcesAsString, /* outDevices */ {});
     mThreadMetrics.logBeginInterval();
     // also dispatch to active AudioRecords
     for (const auto &track : mActiveTracks) {
@@ -8672,7 +8717,7 @@
 AudioFlinger::MmapThread::MmapThread(
         const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id,
         AudioHwDevice *hwDev, sp<StreamHalInterface> stream, bool systemReady, bool isOut)
-    : ThreadBase(audioFlinger, id, MMAP, systemReady, isOut),
+    : ThreadBase(audioFlinger, id, (isOut ? MMAP_PLAYBACK : MMAP_CAPTURE), systemReady, isOut),
       mSessionId(AUDIO_SESSION_NONE),
       mPortId(AUDIO_PORT_HANDLE_NONE),
       mHalStream(stream), mHalDevice(hwDev->hwDevice()), mAudioHwDev(hwDev),
@@ -8964,6 +9009,8 @@
     LOG_ALWAYS_FATAL_IF(!audio_is_linear_pcm(mFormat), "HAL format %#x is not linear pcm", mFormat);
     result = mHalStream->getFrameSize(&mFrameSize);
     LOG_ALWAYS_FATAL_IF(result != OK, "Error retrieving frame size from HAL: %d", result);
+    LOG_ALWAYS_FATAL_IF(mFrameSize <= 0, "Error frame size was %zu but must be greater than zero",
+            mFrameSize);
     result = mHalStream->getBufferSize(&mBufferSize);
     LOG_ALWAYS_FATAL_IF(result != OK, "Error retrieving buffer size from HAL: %d", result);
     mFrameCount = mBufferSize / mFrameSize;
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 5b8c081..c1ac2e4 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -30,7 +30,8 @@
         DUPLICATING,        // Thread class is DuplicatingThread
         RECORD,             // Thread class is RecordThread
         OFFLOAD,            // Thread class is OffloadThread
-        MMAP                // control thread for MMAP stream
+        MMAP_PLAYBACK,      // Thread class for MMAP playback stream
+        MMAP_CAPTURE,       // Thread class for MMAP capture stream
         // If you add any values here, also update ThreadBase::threadTypeToString()
     };
 
@@ -332,6 +333,17 @@
 
                 bool        isOutput() const { return mIsOut; }
 
+                bool        isOffloadOrMmap() const {
+                    switch (mType) {
+                    case OFFLOAD:
+                    case MMAP_PLAYBACK:
+                    case MMAP_CAPTURE:
+                        return true;
+                    default:
+                        return false;
+                    }
+                }
+
     virtual     sp<StreamHalInterface> stream() const = 0;
 
                 sp<EffectHandle> createEffect_l(
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/TrackMetrics.h b/services/audioflinger/TrackMetrics.h
index 399c788..af16448 100644
--- a/services/audioflinger/TrackMetrics.h
+++ b/services/audioflinger/TrackMetrics.h
@@ -67,16 +67,23 @@
         mIntervalStartTimeNs = systemTime();
     }
 
-    void logConstructor(pid_t creatorPid, uid_t creatorUid) const {
+    void logConstructor(pid_t creatorPid, uid_t creatorUid,
+            const std::string& traits = {},
+            audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT) const {
         // Once this item is logged by the server, the client can add properties.
         // no lock required, all local or const variables.
-        mediametrics::LogItem(mMetricsId)
-            .setPid(creatorPid)
+        mediametrics::LogItem item(mMetricsId);
+        item.setPid(creatorPid)
             .setUid(creatorUid)
             .set(AMEDIAMETRICS_PROP_ALLOWUID, (int32_t)creatorUid)
             .set(AMEDIAMETRICS_PROP_EVENT,
                     AMEDIAMETRICS_PROP_PREFIX_SERVER AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR)
-            .record();
+            .set(AMEDIAMETRICS_PROP_TRAITS, traits);
+        // log streamType from the service, since client doesn't know chosen streamType.
+        if (streamType != AUDIO_STREAM_DEFAULT) {
+            item.set(AMEDIAMETRICS_PROP_STREAMTYPE, toString(streamType).c_str());
+        }
+        item.record();
     }
 
     // Called when we are removed from the Thread.
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 58c61c9..126015f 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -602,7 +602,8 @@
     }
 
     // Once this item is logged by the server, the client can add properties.
-    mTrackMetrics.logConstructor(creatorPid, uid);
+    const char * const traits = sharedBuffer == 0 ? "" : "static";
+    mTrackMetrics.logConstructor(creatorPid, uid, traits, streamType);
 }
 
 AudioFlinger::PlaybackThread::Track::~Track()
@@ -804,7 +805,7 @@
     status_t status = mServerProxy->obtainBuffer(&buf);
     buffer->frameCount = buf.mFrameCount;
     buffer->raw = buf.mRaw;
-    if (buf.mFrameCount == 0 && !isStopping() && !isStopped() && !isPaused()) {
+    if (buf.mFrameCount == 0 && !isStopping() && !isStopped() && !isPaused() && !isOffloaded()) {
         ALOGV("%s(%d): underrun,  framesReady(%zu) < framesDesired(%zd), state: %d",
                 __func__, mId, buf.mFrameCount, desiredFrames, mState);
         mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
@@ -978,7 +979,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 +1516,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/Android.bp b/services/audiopolicy/common/managerdefinitions/Android.bp
index fad3c5b..57f0b5b 100644
--- a/services/audiopolicy/common/managerdefinitions/Android.bp
+++ b/services/audiopolicy/common/managerdefinitions/Android.bp
@@ -25,12 +25,14 @@
         "libhidlbase",
         "liblog",
         "libmedia",
+        "libmedia_helper",
         "libutils",
         "libxml2",
     ],
     export_shared_lib_headers: [
         "libaudiofoundation",
         "libmedia",
+        "libmedia_helper",
     ],
     static_libs: [
         "libaudioutils",
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/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
index a6562d7..dd1499c 100644
--- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
@@ -28,6 +28,8 @@
 
 namespace android {
 
+class AudioPolicyClientInterface;
+
 class DeviceDescriptor : public DeviceDescriptorBase,
                          public PolicyAudioPort, public PolicyAudioPortConfig
 {
@@ -87,6 +89,8 @@
     void importAudioPortAndPickAudioProfile(const sp<PolicyAudioPort>& policyPort,
                                             bool force = false);
 
+    void setEncapsulationInfoFromHal(AudioPolicyClientInterface *clientInterface);
+
     void dump(String8 *dst, int spaces, int index, bool verbose = true) const;
 
 private:
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index d5272bc..d6d472b 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -690,7 +690,9 @@
         const sp<SwAudioOutputDescriptor> outputDesc = this->valueAt(i);
         if (outputDesc->isActive(volumeSource, inPastMs, sysTime)
                 && (!(outputDesc->devices()
-                        .containsDeviceAmongTypes(getAllOutRemoteDevices())))) {
+                        .containsDeviceAmongTypes(getAllOutRemoteDevices())
+                        || outputDesc->devices()
+                            .onlyContainsDevicesWithType(AUDIO_DEVICE_OUT_TELEPHONY_TX)))) {
             return true;
         }
     }
@@ -722,7 +724,11 @@
         const sp<SwAudioOutputDescriptor> otherDesc = valueAt(i);
         if (desc->sharesHwModuleWith(otherDesc) &&
                 otherDesc->isStrategyActive(ps, inPastMs, sysTime)) {
-            return true;
+            if (desc == otherDesc
+                    || !otherDesc->devices()
+                            .onlyContainsDevicesWithType(AUDIO_DEVICE_OUT_TELEPHONY_TX)) {
+                return true;
+            }
         }
     }
     return false;
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/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
index 86dbba8..a29e60e 100644
--- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
@@ -17,9 +17,12 @@
 #define LOG_TAG "APM::Devices"
 //#define LOG_NDEBUG 0
 
-#include <audio_utils/string.h>
-#include <media/TypeConverter.h>
 #include <set>
+
+#include <AudioPolicyInterface.h>
+#include <audio_utils/string.h>
+#include <media/AudioParameter.h>
+#include <media/TypeConverter.h>
 #include "DeviceDescriptor.h"
 #include "TypeConverter.h"
 #include "HwModule.h"
@@ -165,6 +168,29 @@
     policyPort->pickAudioProfile(mSamplingRate, mChannelMask, mFormat);
 }
 
+void DeviceDescriptor::setEncapsulationInfoFromHal(
+        AudioPolicyClientInterface *clientInterface) {
+    AudioParameter param(String8(mDeviceTypeAddr.getAddress()));
+    param.addInt(String8(AudioParameter::keyRouting), mDeviceTypeAddr.mType);
+    param.addKey(String8(AUDIO_PARAMETER_DEVICE_SUP_ENCAPSULATION_MODES));
+    param.addKey(String8(AUDIO_PARAMETER_DEVICE_SUP_ENCAPSULATION_METADATA_TYPES));
+    String8 reply = clientInterface->getParameters(AUDIO_IO_HANDLE_NONE, param.toString());
+    AudioParameter repliedParameters(reply);
+    int value;
+    if (repliedParameters.getInt(
+            String8(AUDIO_PARAMETER_DEVICE_SUP_ENCAPSULATION_MODES), value) == NO_ERROR) {
+        if (setEncapsulationModes(value) != NO_ERROR) {
+            ALOGE("Failed to set encapsulation mode(%d)", value);
+        }
+    }
+    if (repliedParameters.getInt(
+            String8(AUDIO_PARAMETER_DEVICE_SUP_ENCAPSULATION_METADATA_TYPES), value) == NO_ERROR) {
+        if (setEncapsulationMetadataTypes(value) != NO_ERROR) {
+            ALOGE("Failed to set encapsulation metadata types(%d)", value);
+        }
+    }
+}
+
 void DeviceDescriptor::dump(String8 *dst, int spaces, int index, bool verbose) const
 {
     String8 extraInfo;
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index bcf6f38..c5c13e9 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -178,6 +178,9 @@
                 return INVALID_OPERATION;
             }
 
+            // Populate encapsulation information when a output device is connected.
+            device->setEncapsulationInfoFromHal(mpClientInterface);
+
             // outputs should never be empty here
             ALOG_ASSERT(outputs.size() != 0, "setDeviceConnectionState():"
                     "checkOutputsForDevice() returned no outputs but status OK");
@@ -1099,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__,
@@ -2874,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++) {
@@ -2993,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;
 }
@@ -3006,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) {
@@ -3046,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;
 }
 
@@ -3817,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*/),
@@ -4635,6 +4655,7 @@
                 if (!device->isAttached()) {
                     device->attach(hwModule);
                     mAvailableOutputDevices.add(device);
+                    device->setEncapsulationInfoFromHal(mpClientInterface);
                     if (newDevices) newDevices->add(device);
                     setEngineDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_AVAILABLE);
                 }
@@ -5218,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;
@@ -5743,15 +5770,6 @@
     DeviceVector filteredDevices = outputDesc->filterSupportedDevices(devices);
     DeviceVector prevDevices = outputDesc->devices();
 
-    // no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current
-    // output profile or if new device is not supported AND previous device(s) is(are) still
-    // available (otherwise reset device must be done on the output)
-    if (!devices.isEmpty() && filteredDevices.isEmpty() &&
-            !mAvailableOutputDevices.filter(prevDevices).empty()) {
-        ALOGV("%s: unsupported device %s for output", __func__, devices.toString().c_str());
-        return 0;
-    }
-
     ALOGV("setOutputDevices() prevDevice %s", prevDevices.toString().c_str());
 
     if (!filteredDevices.isEmpty()) {
@@ -5766,6 +5784,17 @@
         muteWaitMs = 0;
     }
 
+    // no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current
+    // output profile or if new device is not supported AND previous device(s) is(are) still
+    // available (otherwise reset device must be done on the output)
+    if (!devices.isEmpty() && filteredDevices.isEmpty() &&
+            !mAvailableOutputDevices.filter(prevDevices).empty()) {
+        ALOGV("%s: unsupported device %s for output", __func__, devices.toString().c_str());
+        // restore previous device after evaluating strategy mute state
+        outputDesc->setDevices(prevDevices);
+        return muteWaitMs;
+    }
+
     // Do not change the routing if:
     //      the requested device is AUDIO_DEVICE_NONE
     //      OR the requested device is the same as current device
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 9577160..34d07b6 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -593,7 +593,7 @@
     }
 
     // including successes gets very verbose
-    // but once we cut over to westworld, log them all.
+    // but once we cut over to statsd, log them all.
     if (status != NO_ERROR) {
 
         static constexpr char kAudioPolicy[] = "audiopolicy";
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 9b61e74..e847f9f 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -566,11 +566,13 @@
 
         auto canCaptureIfInCallOrCommunication = [&](const auto &recordClient) REQUIRES(mLock) {
             bool canCaptureCall = recordClient->canCaptureOutput;
-            bool canCaptureCommunication = recordClient->canCaptureOutput
-                || recordClient->uid == mPhoneStateOwnerUid
-                || isServiceUid(mPhoneStateOwnerUid);
-            return !(isInCall && !canCaptureCall)
-                && !(isInCommunication && !canCaptureCommunication);
+            return !(isInCall && !canCaptureCall);
+//TODO(b/160260850): restore restriction to mode owner once fix for misbehaving apps is merged
+//            bool canCaptureCommunication = recordClient->canCaptureOutput
+//                || recordClient->uid == mPhoneStateOwnerUid
+//                || isServiceUid(mPhoneStateOwnerUid);
+//            return !(isInCall && !canCaptureCall)
+//                && !(isInCommunication && !canCaptureCommunication);
         };
 
         // By default allow capture if:
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index c9d2c68..af1e01d 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);
         }
@@ -485,9 +496,6 @@
         clientToDisconnect->notifyError(
                 hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED,
                 CaptureResultExtras{});
-        // Ensure not in binder RPC so client disconnect PID checks work correctly
-        LOG_ALWAYS_FATAL_IF(CameraThreadState::getCallingPid() != getpid(),
-                "onDeviceStatusChanged must be called from the camera service process!");
         clientToDisconnect->disconnect();
     }
 }
@@ -1366,7 +1374,12 @@
             Mutex::Autolock l(mLogLock);
             mEventLog.add(msg);
 
-            return -EBUSY;
+            auto current = mActiveClientManager.get(cameraId);
+            if (current != nullptr) {
+                return -EBUSY; // CAMERA_IN_USE
+            } else {
+                return -EUSERS; // MAX_CAMERAS_IN_USE
+            }
         }
 
         for (auto& i : evicted) {
@@ -1627,7 +1640,7 @@
                     cameraId.string(), clientName8.string(), clientPid);
         }
 
-        // Enforce client permissions and do basic sanity checks
+        // Enforce client permissions and do basic validity checks
         if(!(ret = validateConnectLocked(cameraId, clientName8,
                 /*inout*/clientUid, /*inout*/clientPid, /*out*/originalClientPid)).isOk()) {
             return ret;
@@ -1658,6 +1671,10 @@
                     return STATUS_ERROR_FMT(ERROR_CAMERA_IN_USE,
                             "Higher-priority client using camera, ID \"%s\" currently unavailable",
                             cameraId.string());
+                case -EUSERS:
+                    return STATUS_ERROR_FMT(ERROR_MAX_CAMERAS_IN_USE,
+                            "Too many cameras already open, cannot open camera \"%s\"",
+                            cameraId.string());
                 default:
                     return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
                             "Unexpected error %s (%d) opening camera \"%s\"",
@@ -3757,13 +3774,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 +3892,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 +3909,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/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index ebb0555..b043c0b 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -1777,6 +1777,14 @@
         case hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER:
             ALOGW("%s: Received recoverable error %d from HAL - ignoring, requestId %" PRId32,
                     __FUNCTION__, errorCode, resultExtras.requestId);
+
+            if ((hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST == errorCode) ||
+                    (hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT == errorCode)) {
+                Mutex::Autolock al(mLatestRequestMutex);
+
+                mLatestFailedRequestId = resultExtras.requestId;
+                mLatestRequestSignal.signal();
+            }
             mCaptureSequencer->notifyError(errorCode, resultExtras);
             return;
         default:
@@ -2303,7 +2311,7 @@
 
 status_t Camera2Client::waitUntilRequestIdApplied(int32_t requestId, nsecs_t timeout) {
     Mutex::Autolock l(mLatestRequestMutex);
-    while (mLatestRequestId != requestId) {
+    while ((mLatestRequestId != requestId) && (mLatestFailedRequestId != requestId)) {
         nsecs_t startTime = systemTime();
 
         auto res = mLatestRequestSignal.waitRelative(mLatestRequestMutex, timeout);
@@ -2312,7 +2320,7 @@
         timeout -= (systemTime() - startTime);
     }
 
-    return OK;
+    return (mLatestRequestId == requestId) ? OK : DEAD_OBJECT;
 }
 
 void Camera2Client::notifyRequestId(int32_t requestId) {
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index 3144e0e..03ca44a 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -237,6 +237,7 @@
     mutable Mutex mLatestRequestMutex;
     Condition mLatestRequestSignal;
     int32_t mLatestRequestId = -1;
+    int32_t mLatestFailedRequestId = -1;
     status_t waitUntilRequestIdApplied(int32_t requestId, nsecs_t timeout);
     status_t waitUntilCurrentRequestIdLocked();
 };
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 20333d1..dbc863b 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -230,7 +230,7 @@
     previewFpsRange[1] = fastInfo.bestStillCaptureFpsRange[1];
 
     // PREVIEW_FRAME_RATE / SUPPORTED_PREVIEW_FRAME_RATES are deprecated, but
-    // still have to do something sane for them
+    // still have to do something reasonable for them
 
     // NOTE: Not scaled like FPS range values are.
     int previewFps = fpsFromRange(previewFpsRange[0], previewFpsRange[1]);
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 964c96a..9d3874f 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -205,7 +205,7 @@
     virtual void notifyRepeatingRequestError(long lastFrameNumber);
 
     // utility function to convert AIDL SessionConfiguration to HIDL
-    // streamConfiguration. Also checks for sanity of SessionConfiguration and
+    // streamConfiguration. Also checks for validity of SessionConfiguration and
     // returns a non-ok binder::Status if the passed in session configuration
     // isn't valid.
     static binder::Status
diff --git a/services/camera/libcameraservice/api2/CompositeStream.cpp b/services/camera/libcameraservice/api2/CompositeStream.cpp
index b47ee2e..a61dac7 100644
--- a/services/camera/libcameraservice/api2/CompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/CompositeStream.cpp
@@ -28,19 +28,19 @@
 namespace android {
 namespace camera3 {
 
-CompositeStream::CompositeStream(wp<CameraDeviceBase> device,
+CompositeStream::CompositeStream(sp<CameraDeviceBase> device,
         wp<hardware::camera2::ICameraDeviceCallbacks> cb) :
         mDevice(device),
         mRemoteCallback(cb),
         mNumPartialResults(1),
         mErrorState(false) {
-    sp<CameraDeviceBase> cameraDevice = device.promote();
-    if (cameraDevice.get() != nullptr) {
-        CameraMetadata staticInfo = cameraDevice->info();
+    if (device != nullptr) {
+        CameraMetadata staticInfo = device->info();
         camera_metadata_entry_t entry = staticInfo.find(ANDROID_REQUEST_PARTIAL_RESULT_COUNT);
         if (entry.count > 0) {
             mNumPartialResults = entry.data.i32[0];
         }
+        mStatusTracker = device->getStatusTracker();
     }
 }
 
@@ -174,7 +174,7 @@
             ret = onStreamBufferError(resultExtras);
             break;
         case hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST:
-            // Invalid request, this shouldn't affect composite streams.
+            onRequestError(resultExtras);
             break;
         default:
             ALOGE("%s: Unrecoverable error: %d detected!", __FUNCTION__, errorCode);
@@ -186,7 +186,7 @@
     return ret;
 }
 
-void CompositeStream::notifyError(int64_t frameNumber) {
+void CompositeStream::notifyError(int64_t frameNumber, int32_t requestId) {
     sp<hardware::camera2::ICameraDeviceCallbacks> remoteCb =
         mRemoteCallback.promote();
 
@@ -194,6 +194,7 @@
         CaptureResultExtras extras;
         extras.errorStreamId = getStreamId();
         extras.frameNumber = frameNumber;
+        extras.requestId = requestId;
         remoteCb->onDeviceError(
                 hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER,
                 extras);
diff --git a/services/camera/libcameraservice/api2/CompositeStream.h b/services/camera/libcameraservice/api2/CompositeStream.h
index e5baf1a..5f62d47 100644
--- a/services/camera/libcameraservice/api2/CompositeStream.h
+++ b/services/camera/libcameraservice/api2/CompositeStream.h
@@ -38,7 +38,7 @@
 class CompositeStream : public camera3::Camera3StreamBufferListener {
 
 public:
-    CompositeStream(wp<CameraDeviceBase> device, wp<hardware::camera2::ICameraDeviceCallbacks> cb);
+    CompositeStream(sp<CameraDeviceBase> device, wp<hardware::camera2::ICameraDeviceCallbacks> cb);
     virtual ~CompositeStream() {}
 
     status_t createStream(const std::vector<sp<Surface>>& consumers,
@@ -95,7 +95,7 @@
     status_t registerCompositeStreamListener(int32_t streamId);
     void eraseResult(int64_t frameNumber);
     void flagAnErrorFrameNumber(int64_t frameNumber);
-    void notifyError(int64_t frameNumber);
+    void notifyError(int64_t frameNumber, int32_t requestId);
 
     // Subclasses should check for buffer errors from internal streams and return 'true' in
     // case the error notification should remain within camera service.
@@ -105,11 +105,16 @@
     // internal processing needs result data.
     virtual void onResultError(const CaptureResultExtras& resultExtras) = 0;
 
+    // Subclasses can decide how to handle request errors depending on whether
+    // or not the internal processing needs clean up.
+    virtual void onRequestError(const CaptureResultExtras& /*resultExtras*/) {}
+
     // Device and/or service is in unrecoverable error state.
     // Composite streams should behave accordingly.
     void enableErrorState();
 
     wp<CameraDeviceBase>   mDevice;
+    wp<camera3::StatusTracker> mStatusTracker;
     wp<hardware::camera2::ICameraDeviceCallbacks> mRemoteCallback;
 
     mutable Mutex          mMutex;
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
index 16ce52c..c6859be 100644
--- a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
@@ -29,7 +29,7 @@
 namespace android {
 namespace camera3 {
 
-DepthCompositeStream::DepthCompositeStream(wp<CameraDeviceBase> device,
+DepthCompositeStream::DepthCompositeStream(sp<CameraDeviceBase> device,
         wp<hardware::camera2::ICameraDeviceCallbacks> cb) :
         CompositeStream(device, cb),
         mBlobStreamId(-1),
@@ -43,9 +43,8 @@
         mProducerListener(new ProducerListener()),
         mMaxJpegSize(-1),
         mIsLogicalCamera(false) {
-    sp<CameraDeviceBase> cameraDevice = device.promote();
-    if (cameraDevice.get() != nullptr) {
-        CameraMetadata staticInfo = cameraDevice->info();
+    if (device != nullptr) {
+        CameraMetadata staticInfo = device->info();
         auto entry = staticInfo.find(ANDROID_JPEG_MAX_SIZE);
         if (entry.count > 0) {
             mMaxJpegSize = entry.data.i32[0];
@@ -385,7 +384,8 @@
     }
 
     if ((inputFrame->error || mErrorState) && !inputFrame->errorNotified) {
-        notifyError(inputFrame->frameNumber);
+        //TODO: Figure out correct requestId
+        notifyError(inputFrame->frameNumber, -1 /*requestId*/);
         inputFrame->errorNotified = true;
     }
 }
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.h b/services/camera/libcameraservice/api2/DepthCompositeStream.h
index 1bf714d..cab52b6 100644
--- a/services/camera/libcameraservice/api2/DepthCompositeStream.h
+++ b/services/camera/libcameraservice/api2/DepthCompositeStream.h
@@ -41,7 +41,7 @@
         public CpuConsumer::FrameAvailableListener {
 
 public:
-    DepthCompositeStream(wp<CameraDeviceBase> device,
+    DepthCompositeStream(sp<CameraDeviceBase> device,
             wp<hardware::camera2::ICameraDeviceCallbacks> cb);
     ~DepthCompositeStream() override;
 
@@ -80,8 +80,9 @@
         bool                      error;
         bool                      errorNotified;
         int64_t                   frameNumber;
+        int32_t                   requestId;
 
-        InputFrame() : error(false), errorNotified(false), frameNumber(-1) { }
+        InputFrame() : error(false), errorNotified(false), frameNumber(-1), requestId(-1) { }
     };
 
     // Helper methods
diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
index f335c20..a63f402 100644
--- a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
@@ -45,7 +45,7 @@
 namespace android {
 namespace camera3 {
 
-HeicCompositeStream::HeicCompositeStream(wp<CameraDeviceBase> device,
+HeicCompositeStream::HeicCompositeStream(sp<CameraDeviceBase> device,
         wp<hardware::camera2::ICameraDeviceCallbacks> cb) :
         CompositeStream(device, cb),
         mUseHeic(false),
@@ -68,7 +68,8 @@
         mLockedAppSegmentBufferCnt(0),
         mCodecOutputCounter(0),
         mQuality(-1),
-        mGridTimestampUs(0) {
+        mGridTimestampUs(0),
+        mStatusId(StatusTracker::NO_STATUS_ID) {
 }
 
 HeicCompositeStream::~HeicCompositeStream() {
@@ -188,9 +189,17 @@
     }
 
     mOutputSurface = consumers[0];
-    res = registerCompositeStreamListener(getStreamId());
+    res = registerCompositeStreamListener(mMainImageStreamId);
     if (res != OK) {
-        ALOGE("%s: Failed to register HAL main image stream", __FUNCTION__);
+        ALOGE("%s: Failed to register HAL main image stream: %s (%d)", __FUNCTION__,
+                strerror(-res), res);
+        return res;
+    }
+
+    res = registerCompositeStreamListener(mAppSegmentStreamId);
+    if (res != OK) {
+        ALOGE("%s: Failed to register HAL app segment stream: %s (%d)", __FUNCTION__,
+                strerror(-res), res);
         return res;
     }
 
@@ -224,6 +233,19 @@
         mOutputSurface->disconnect(NATIVE_WINDOW_API_CAMERA);
         mOutputSurface.clear();
     }
+
+    sp<StatusTracker> statusTracker = mStatusTracker.promote();
+    if (statusTracker != nullptr && mStatusId != StatusTracker::NO_STATUS_ID) {
+        statusTracker->removeComponent(mStatusId);
+        mStatusId = StatusTracker::NO_STATUS_ID;
+    }
+
+    if (mPendingInputFrames.size() > 0) {
+        ALOGW("%s: mPendingInputFrames has %zu stale entries",
+                __FUNCTION__, mPendingInputFrames.size());
+        mPendingInputFrames.clear();
+    }
+
     return res;
 }
 
@@ -232,9 +254,16 @@
 
     if (bufferInfo.mError) return;
 
-    mCodecOutputBufferTimestamps.push(bufferInfo.mTimestamp);
-    ALOGV("%s: [%" PRId64 "]: Adding codecOutputBufferTimestamp (%zu timestamps in total)",
-            __FUNCTION__, bufferInfo.mTimestamp, mCodecOutputBufferTimestamps.size());
+    if (bufferInfo.mStreamId == mMainImageStreamId) {
+        mMainImageFrameNumbers.push(bufferInfo.mFrameNumber);
+        mCodecOutputBufferFrameNumbers.push(bufferInfo.mFrameNumber);
+        ALOGV("%s: [%" PRId64 "]: Adding main image frame number (%zu frame numbers in total)",
+                __FUNCTION__, bufferInfo.mFrameNumber, mMainImageFrameNumbers.size());
+    } else if (bufferInfo.mStreamId == mAppSegmentStreamId) {
+        mAppSegmentFrameNumbers.push(bufferInfo.mFrameNumber);
+        ALOGV("%s: [%" PRId64 "]: Adding app segment frame number (%zu frame numbers in total)",
+                __FUNCTION__, bufferInfo.mFrameNumber, mAppSegmentFrameNumbers.size());
+    }
 }
 
 // We need to get the settings early to handle the case where the codec output
@@ -264,7 +293,7 @@
         quality = entry.data.i32[0];
     }
 
-    mSettingsByFrameNumber[frameNumber] = std::make_pair(orientation, quality);
+    mSettingsByFrameNumber[frameNumber] = {orientation, quality};
 }
 
 void HeicCompositeStream::onFrameAvailable(const BufferItem& item) {
@@ -479,6 +508,11 @@
         return res;
     }
 
+    sp<camera3::StatusTracker> statusTracker = mStatusTracker.promote();
+    if (statusTracker != nullptr) {
+        mStatusId = statusTracker->addComponent();
+    }
+
     run("HeicCompositeStreamProc");
 
     return NO_ERROR;
@@ -524,30 +558,44 @@
     }
 
     if (mSettingsByFrameNumber.find(resultExtras.frameNumber) != mSettingsByFrameNumber.end()) {
-        ALOGV("%s: [%" PRId64 "]: frameNumber %" PRId64, __FUNCTION__,
-                timestamp, resultExtras.frameNumber);
-        mFrameNumberMap.emplace(resultExtras.frameNumber, timestamp);
-        mSettingsByTimestamp[timestamp] = mSettingsByFrameNumber[resultExtras.frameNumber];
-        mSettingsByFrameNumber.erase(resultExtras.frameNumber);
+        ALOGV("%s: [%" PRId64 "]: timestamp %" PRId64 ", requestId %d", __FUNCTION__,
+                resultExtras.frameNumber, timestamp, resultExtras.requestId);
+        mSettingsByFrameNumber[resultExtras.frameNumber].shutterNotified = true;
+        mSettingsByFrameNumber[resultExtras.frameNumber].timestamp = timestamp;
+        mSettingsByFrameNumber[resultExtras.frameNumber].requestId = resultExtras.requestId;
         mInputReadyCondition.signal();
     }
 }
 
 void HeicCompositeStream::compilePendingInputLocked() {
-    while (!mSettingsByTimestamp.empty()) {
-        auto it = mSettingsByTimestamp.begin();
-        mPendingInputFrames[it->first].orientation = it->second.first;
-        mPendingInputFrames[it->first].quality = it->second.second;
-        mSettingsByTimestamp.erase(it);
+    auto i = mSettingsByFrameNumber.begin();
+    while (i != mSettingsByFrameNumber.end()) {
+        if (i->second.shutterNotified) {
+            mPendingInputFrames[i->first].orientation = i->second.orientation;
+            mPendingInputFrames[i->first].quality = i->second.quality;
+            mPendingInputFrames[i->first].timestamp = i->second.timestamp;
+            mPendingInputFrames[i->first].requestId = i->second.requestId;
+            ALOGV("%s: [%" PRId64 "]: timestamp is %" PRId64, __FUNCTION__,
+                    i->first, i->second.timestamp);
+            i = mSettingsByFrameNumber.erase(i);
 
-        // Set encoder quality if no inflight encoding
-        if (mPendingInputFrames.size() == 1) {
-            int32_t newQuality = mPendingInputFrames.begin()->second.quality;
-            updateCodecQualityLocked(newQuality);
+            // Set encoder quality if no inflight encoding
+            if (mPendingInputFrames.size() == 1) {
+                sp<StatusTracker> statusTracker = mStatusTracker.promote();
+                if (statusTracker != nullptr) {
+                    statusTracker->markComponentActive(mStatusId);
+                    ALOGV("%s: Mark component as active", __FUNCTION__);
+                }
+
+                int32_t newQuality = mPendingInputFrames.begin()->second.quality;
+                updateCodecQualityLocked(newQuality);
+            }
+        } else {
+            i++;
         }
     }
 
-    while (!mInputAppSegmentBuffers.empty()) {
+    while (!mInputAppSegmentBuffers.empty() && mAppSegmentFrameNumbers.size() > 0) {
         CpuConsumer::LockedBuffer imgBuffer;
         auto it = mInputAppSegmentBuffers.begin();
         auto res = mAppSegmentConsumer->lockNextBuffer(&imgBuffer);
@@ -569,17 +617,30 @@
             continue;
         }
 
-        if ((mPendingInputFrames.find(imgBuffer.timestamp) != mPendingInputFrames.end()) &&
-                (mPendingInputFrames[imgBuffer.timestamp].error)) {
+        if (mPendingInputFrames.find(mAppSegmentFrameNumbers.front()) == mPendingInputFrames.end()) {
+            ALOGE("%s: mPendingInputFrames doesn't contain frameNumber %" PRId64, __FUNCTION__,
+                    mAppSegmentFrameNumbers.front());
+            mInputAppSegmentBuffers.erase(it);
+            mAppSegmentFrameNumbers.pop();
+            continue;
+        }
+
+        int64_t frameNumber = mAppSegmentFrameNumbers.front();
+        // If mPendingInputFrames doesn't contain the expected frame number, the captured
+        // input app segment frame must have been dropped via a buffer error.  Simply
+        // return the buffer to the buffer queue.
+        if ((mPendingInputFrames.find(frameNumber) == mPendingInputFrames.end()) ||
+                (mPendingInputFrames[frameNumber].error)) {
             mAppSegmentConsumer->unlockBuffer(imgBuffer);
         } else {
-            mPendingInputFrames[imgBuffer.timestamp].appSegmentBuffer = imgBuffer;
+            mPendingInputFrames[frameNumber].appSegmentBuffer = imgBuffer;
             mLockedAppSegmentBufferCnt++;
         }
         mInputAppSegmentBuffers.erase(it);
+        mAppSegmentFrameNumbers.pop();
     }
 
-    while (!mInputYuvBuffers.empty() && !mYuvBufferAcquired) {
+    while (!mInputYuvBuffers.empty() && !mYuvBufferAcquired && mMainImageFrameNumbers.size() > 0) {
         CpuConsumer::LockedBuffer imgBuffer;
         auto it = mInputYuvBuffers.begin();
         auto res = mMainImageConsumer->lockNextBuffer(&imgBuffer);
@@ -600,59 +661,68 @@
             continue;
         }
 
-        if ((mPendingInputFrames.find(imgBuffer.timestamp) != mPendingInputFrames.end()) &&
-                (mPendingInputFrames[imgBuffer.timestamp].error)) {
+        if (mPendingInputFrames.find(mMainImageFrameNumbers.front()) == mPendingInputFrames.end()) {
+            ALOGE("%s: mPendingInputFrames doesn't contain frameNumber %" PRId64, __FUNCTION__,
+                    mMainImageFrameNumbers.front());
+            mInputYuvBuffers.erase(it);
+            mMainImageFrameNumbers.pop();
+            continue;
+        }
+
+        int64_t frameNumber = mMainImageFrameNumbers.front();
+        // If mPendingInputFrames doesn't contain the expected frame number, the captured
+        // input main image must have been dropped via a buffer error. Simply
+        // return the buffer to the buffer queue.
+        if ((mPendingInputFrames.find(frameNumber) == mPendingInputFrames.end()) ||
+                (mPendingInputFrames[frameNumber].error)) {
             mMainImageConsumer->unlockBuffer(imgBuffer);
         } else {
-            mPendingInputFrames[imgBuffer.timestamp].yuvBuffer = imgBuffer;
+            mPendingInputFrames[frameNumber].yuvBuffer = imgBuffer;
             mYuvBufferAcquired = true;
         }
         mInputYuvBuffers.erase(it);
+        mMainImageFrameNumbers.pop();
     }
 
     while (!mCodecOutputBuffers.empty()) {
         auto it = mCodecOutputBuffers.begin();
-        // Bitstream buffer timestamp doesn't necessarily directly correlate with input
-        // buffer timestamp. Assume encoder input to output is FIFO, use a queue
-        // to look up timestamp.
-        int64_t bufferTime = -1;
-        if (mCodecOutputBufferTimestamps.empty()) {
-            ALOGV("%s: Failed to find buffer timestamp for codec output buffer!", __FUNCTION__);
+        // Assume encoder input to output is FIFO, use a queue to look up
+        // frameNumber when handling codec outputs.
+        int64_t bufferFrameNumber = -1;
+        if (mCodecOutputBufferFrameNumbers.empty()) {
+            ALOGV("%s: Failed to find buffer frameNumber for codec output buffer!", __FUNCTION__);
             break;
         } else {
-            // Direct mapping between camera timestamp (in ns) and codec timestamp (in us).
-            bufferTime = mCodecOutputBufferTimestamps.front();
+            // Direct mapping between camera frame number and codec timestamp (in us).
+            bufferFrameNumber = mCodecOutputBufferFrameNumbers.front();
             mCodecOutputCounter++;
             if (mCodecOutputCounter == mNumOutputTiles) {
-                mCodecOutputBufferTimestamps.pop();
+                mCodecOutputBufferFrameNumbers.pop();
                 mCodecOutputCounter = 0;
             }
 
-            mPendingInputFrames[bufferTime].codecOutputBuffers.push_back(*it);
-            ALOGV("%s: [%" PRId64 "]: Pushing codecOutputBuffers (time %" PRId64 " us)",
-                    __FUNCTION__, bufferTime, it->timeUs);
+            mPendingInputFrames[bufferFrameNumber].codecOutputBuffers.push_back(*it);
+            ALOGV("%s: [%" PRId64 "]: Pushing codecOutputBuffers (frameNumber %" PRId64 ")",
+                    __FUNCTION__, bufferFrameNumber, it->timeUs);
         }
         mCodecOutputBuffers.erase(it);
     }
 
-    while (!mFrameNumberMap.empty()) {
-        auto it = mFrameNumberMap.begin();
-        mPendingInputFrames[it->second].frameNumber = it->first;
-        ALOGV("%s: [%" PRId64 "]: frameNumber is %" PRId64, __FUNCTION__, it->second, it->first);
-        mFrameNumberMap.erase(it);
-    }
-
     while (!mCaptureResults.empty()) {
         auto it = mCaptureResults.begin();
-        // Negative timestamp indicates that something went wrong during the capture result
+        // Negative frame number indicates that something went wrong during the capture result
         // collection process.
-        if (it->first >= 0) {
-            if (mPendingInputFrames[it->first].frameNumber == std::get<0>(it->second)) {
-                mPendingInputFrames[it->first].result =
+        int64_t frameNumber = std::get<0>(it->second);
+        if (it->first >= 0 &&
+                mPendingInputFrames.find(frameNumber) != mPendingInputFrames.end()) {
+            if (mPendingInputFrames[frameNumber].timestamp == it->first) {
+                mPendingInputFrames[frameNumber].result =
                         std::make_unique<CameraMetadata>(std::get<1>(it->second));
             } else {
                 ALOGE("%s: Capture result frameNumber/timestamp mapping changed between "
-                        "shutter and capture result!", __FUNCTION__);
+                        "shutter and capture result! before: %" PRId64 ", after: %" PRId64,
+                        __FUNCTION__, mPendingInputFrames[frameNumber].timestamp,
+                        it->first);
             }
         }
         mCaptureResults.erase(it);
@@ -661,22 +731,24 @@
     // mErrorFrameNumbers stores frame number of dropped buffers.
     auto it = mErrorFrameNumbers.begin();
     while (it != mErrorFrameNumbers.end()) {
-        bool frameFound = false;
-        for (auto &inputFrame : mPendingInputFrames) {
-            if (inputFrame.second.frameNumber == *it) {
-                inputFrame.second.error = true;
-                frameFound = true;
-                break;
-            }
-        }
-
-        if (frameFound) {
-            it = mErrorFrameNumbers.erase(it);
+        if (mPendingInputFrames.find(*it) != mPendingInputFrames.end()) {
+            mPendingInputFrames[*it].error = true;
         } else {
+            //Error callback is guaranteed to arrive after shutter notify, which
+            //results in mPendingInputFrames being populated.
             ALOGW("%s: Not able to find failing input with frame number: %" PRId64, __FUNCTION__,
                     *it);
-            it++;
         }
+        it = mErrorFrameNumbers.erase(it);
+    }
+
+    // mExifErrorFrameNumbers stores the frame number of dropped APP_SEGMENT buffers
+    it = mExifErrorFrameNumbers.begin();
+    while (it != mExifErrorFrameNumbers.end()) {
+        if (mPendingInputFrames.find(*it) != mPendingInputFrames.end()) {
+            mPendingInputFrames[*it].exifError = true;
+        }
+        it = mExifErrorFrameNumbers.erase(it);
     }
 
     // Distribute codec input buffers to be filled out from YUV output
@@ -701,8 +773,8 @@
     }
 }
 
-bool HeicCompositeStream::getNextReadyInputLocked(int64_t *currentTs /*out*/) {
-    if (currentTs == nullptr) {
+bool HeicCompositeStream::getNextReadyInputLocked(int64_t *frameNumber /*out*/) {
+    if (frameNumber == nullptr) {
         return false;
     }
 
@@ -715,7 +787,8 @@
         // This makes sure that muxer gets created only when an output tile is
         // generated, because right now we only handle 1 HEIC output buffer at a
         // time (max dequeued buffer count is 1).
-        bool appSegmentReady = (it.second.appSegmentBuffer.data != nullptr) &&
+        bool appSegmentReady =
+                (it.second.appSegmentBuffer.data != nullptr || it.second.exifError) &&
                 !it.second.appSegmentWritten && it.second.result != nullptr &&
                 it.second.muxer != nullptr;
         bool codecOutputReady = !it.second.codecOutputBuffers.empty();
@@ -724,9 +797,8 @@
         bool hasOutputBuffer = it.second.muxer != nullptr ||
                 (mDequeuedOutputBufferCnt < kMaxOutputSurfaceProducerCount);
         if ((!it.second.error) &&
-                (it.first < *currentTs) &&
                 (appSegmentReady || (codecOutputReady && hasOutputBuffer) || codecInputReady)) {
-            *currentTs = it.first;
+            *frameNumber = it.first;
             if (it.second.format == nullptr && mFormat != nullptr) {
                 it.second.format = mFormat->dup();
             }
@@ -738,16 +810,12 @@
     return newInputAvailable;
 }
 
-int64_t HeicCompositeStream::getNextFailingInputLocked(int64_t *currentTs /*out*/) {
+int64_t HeicCompositeStream::getNextFailingInputLocked() {
     int64_t res = -1;
-    if (currentTs == nullptr) {
-        return res;
-    }
 
     for (const auto& it : mPendingInputFrames) {
-        if (it.second.error && !it.second.errorNotified && (it.first < *currentTs)) {
-            *currentTs = it.first;
-            res = it.second.frameNumber;
+        if (it.second.error) {
+            res = it.first;
             break;
         }
     }
@@ -755,12 +823,13 @@
     return res;
 }
 
-status_t HeicCompositeStream::processInputFrame(nsecs_t timestamp,
+status_t HeicCompositeStream::processInputFrame(int64_t frameNumber,
         InputFrame &inputFrame) {
     ATRACE_CALL();
     status_t res = OK;
 
-    bool appSegmentReady = inputFrame.appSegmentBuffer.data != nullptr &&
+    bool appSegmentReady =
+            (inputFrame.appSegmentBuffer.data != nullptr || inputFrame.exifError) &&
             !inputFrame.appSegmentWritten && inputFrame.result != nullptr &&
             inputFrame.muxer != nullptr;
     bool codecOutputReady = inputFrame.codecOutputBuffers.size() > 0;
@@ -770,8 +839,9 @@
             (mDequeuedOutputBufferCnt < kMaxOutputSurfaceProducerCount);
 
     ALOGV("%s: [%" PRId64 "]: appSegmentReady %d, codecOutputReady %d, codecInputReady %d,"
-            " dequeuedOutputBuffer %d", __FUNCTION__, timestamp, appSegmentReady,
-            codecOutputReady, codecInputReady, mDequeuedOutputBufferCnt);
+            " dequeuedOutputBuffer %d, timestamp %" PRId64, __FUNCTION__, frameNumber,
+            appSegmentReady, codecOutputReady, codecInputReady, mDequeuedOutputBufferCnt,
+            inputFrame.timestamp);
 
     // Handle inputs for Hevc tiling
     if (codecInputReady) {
@@ -791,7 +861,7 @@
     // codecOutputReady must be true. Otherwise, appSegmentReady is guaranteed
     // to be false, and the function must have returned early.
     if (inputFrame.muxer == nullptr) {
-        res = startMuxerForInputFrame(timestamp, inputFrame);
+        res = startMuxerForInputFrame(frameNumber, inputFrame);
         if (res != OK) {
             ALOGE("%s: Failed to create and start muxer: %s (%d)", __FUNCTION__,
                     strerror(-res), res);
@@ -801,7 +871,7 @@
 
     // Write JPEG APP segments data to the muxer.
     if (appSegmentReady) {
-        res = processAppSegment(timestamp, inputFrame);
+        res = processAppSegment(frameNumber, inputFrame);
         if (res != OK) {
             ALOGE("%s: Failed to process JPEG APP segments: %s (%d)", __FUNCTION__,
                     strerror(-res), res);
@@ -811,7 +881,7 @@
 
     // Write media codec bitstream buffers to muxer.
     while (!inputFrame.codecOutputBuffers.empty()) {
-        res = processOneCodecOutputFrame(timestamp, inputFrame);
+        res = processOneCodecOutputFrame(frameNumber, inputFrame);
         if (res != OK) {
             ALOGE("%s: Failed to process codec output frame: %s (%d)", __FUNCTION__,
                     strerror(-res), res);
@@ -821,7 +891,7 @@
 
     if (inputFrame.pendingOutputTiles == 0) {
         if (inputFrame.appSegmentWritten) {
-            res = processCompletedInputFrame(timestamp, inputFrame);
+            res = processCompletedInputFrame(frameNumber, inputFrame);
             if (res != OK) {
                 ALOGE("%s: Failed to process completed input frame: %s (%d)", __FUNCTION__,
                         strerror(-res), res);
@@ -837,7 +907,7 @@
     return res;
 }
 
-status_t HeicCompositeStream::startMuxerForInputFrame(nsecs_t timestamp, InputFrame &inputFrame) {
+status_t HeicCompositeStream::startMuxerForInputFrame(int64_t frameNumber, InputFrame &inputFrame) {
     sp<ANativeWindow> outputANW = mOutputSurface;
 
     auto res = outputANW->dequeueBuffer(mOutputSurface.get(), &inputFrame.anb, &inputFrame.fenceFd);
@@ -851,7 +921,7 @@
     // Combine current thread id, stream id and timestamp to uniquely identify image.
     std::ostringstream tempOutputFile;
     tempOutputFile << "HEIF-" << pthread_self() << "-"
-            << getStreamId() << "-" << timestamp;
+            << getStreamId() << "-" << frameNumber;
     inputFrame.fileFd = syscall(__NR_memfd_create, tempOutputFile.str().c_str(), MFD_CLOEXEC);
     if (inputFrame.fileFd < 0) {
         ALOGE("%s: Failed to create file %s. Error no is %d", __FUNCTION__,
@@ -889,22 +959,27 @@
     }
 
     ALOGV("%s: [%" PRId64 "]: Muxer started for inputFrame", __FUNCTION__,
-            timestamp);
+            frameNumber);
     return OK;
 }
 
-status_t HeicCompositeStream::processAppSegment(nsecs_t timestamp, InputFrame &inputFrame) {
+status_t HeicCompositeStream::processAppSegment(int64_t frameNumber, InputFrame &inputFrame) {
     size_t app1Size = 0;
-    auto appSegmentSize = findAppSegmentsSize(inputFrame.appSegmentBuffer.data,
-            inputFrame.appSegmentBuffer.width * inputFrame.appSegmentBuffer.height,
-            &app1Size);
-    if (appSegmentSize == 0) {
-        ALOGE("%s: Failed to find JPEG APP segment size", __FUNCTION__);
-        return NO_INIT;
+    size_t appSegmentSize = 0;
+    if (!inputFrame.exifError) {
+        appSegmentSize = findAppSegmentsSize(inputFrame.appSegmentBuffer.data,
+                inputFrame.appSegmentBuffer.width * inputFrame.appSegmentBuffer.height,
+                &app1Size);
+        if (appSegmentSize == 0) {
+            ALOGE("%s: Failed to find JPEG APP segment size", __FUNCTION__);
+            return NO_INIT;
+        }
     }
 
     std::unique_ptr<ExifUtils> exifUtils(ExifUtils::create());
-    auto exifRes = exifUtils->initialize(inputFrame.appSegmentBuffer.data, app1Size);
+    auto exifRes = inputFrame.exifError ?
+            exifUtils->initializeEmpty() :
+            exifUtils->initialize(inputFrame.appSegmentBuffer.data, app1Size);
     if (!exifRes) {
         ALOGE("%s: Failed to initialize ExifUtils object!", __FUNCTION__);
         return BAD_VALUE;
@@ -945,7 +1020,7 @@
 
     sp<ABuffer> aBuffer = new ABuffer(appSegmentBuffer, appSegmentBufferSize);
     auto res = inputFrame.muxer->writeSampleData(aBuffer, inputFrame.trackIndex,
-            timestamp, MediaCodec::BUFFER_FLAG_MUXER_DATA);
+            inputFrame.timestamp, MediaCodec::BUFFER_FLAG_MUXER_DATA);
     delete[] appSegmentBuffer;
 
     if (res != OK) {
@@ -955,13 +1030,14 @@
     }
 
     ALOGV("%s: [%" PRId64 "]: appSegmentSize is %zu, width %d, height %d, app1Size %zu",
-          __FUNCTION__, timestamp, appSegmentSize, inputFrame.appSegmentBuffer.width,
+          __FUNCTION__, frameNumber, appSegmentSize, inputFrame.appSegmentBuffer.width,
           inputFrame.appSegmentBuffer.height, app1Size);
 
     inputFrame.appSegmentWritten = true;
     // Release the buffer now so any pending input app segments can be processed
     mAppSegmentConsumer->unlockBuffer(inputFrame.appSegmentBuffer);
     inputFrame.appSegmentBuffer.data = nullptr;
+    inputFrame.exifError = false;
     mLockedAppSegmentBufferCnt--;
 
     return OK;
@@ -1010,7 +1086,7 @@
     return OK;
 }
 
-status_t HeicCompositeStream::processOneCodecOutputFrame(nsecs_t timestamp,
+status_t HeicCompositeStream::processOneCodecOutputFrame(int64_t frameNumber,
         InputFrame &inputFrame) {
     auto it = inputFrame.codecOutputBuffers.begin();
     sp<MediaCodecBuffer> buffer;
@@ -1028,7 +1104,7 @@
 
     sp<ABuffer> aBuffer = new ABuffer(buffer->data(), buffer->size());
     res = inputFrame.muxer->writeSampleData(
-            aBuffer, inputFrame.trackIndex, timestamp, 0 /*flags*/);
+            aBuffer, inputFrame.trackIndex, inputFrame.timestamp, 0 /*flags*/);
     if (res != OK) {
         ALOGE("%s: Failed to write buffer index %d to muxer: %s (%d)",
                 __FUNCTION__, it->index, strerror(-res), res);
@@ -1045,11 +1121,11 @@
     inputFrame.codecOutputBuffers.erase(inputFrame.codecOutputBuffers.begin());
 
     ALOGV("%s: [%" PRId64 "]: Output buffer index %d",
-        __FUNCTION__, timestamp, it->index);
+        __FUNCTION__, frameNumber, it->index);
     return OK;
 }
 
-status_t HeicCompositeStream::processCompletedInputFrame(nsecs_t timestamp,
+status_t HeicCompositeStream::processCompletedInputFrame(int64_t frameNumber,
         InputFrame &inputFrame) {
     sp<ANativeWindow> outputANW = mOutputSurface;
     inputFrame.muxer->stop();
@@ -1088,7 +1164,7 @@
     blobHeader->blobId = static_cast<CameraBlobId>(0x00FE);
     blobHeader->blobSize = fSize;
 
-    res = native_window_set_buffers_timestamp(mOutputSurface.get(), timestamp);
+    res = native_window_set_buffers_timestamp(mOutputSurface.get(), inputFrame.timestamp);
     if (res != OK) {
         ALOGE("%s: Stream %d: Error setting timestamp: %s (%d)",
                __FUNCTION__, getStreamId(), strerror(-res), res);
@@ -1104,13 +1180,14 @@
     inputFrame.anb = nullptr;
     mDequeuedOutputBufferCnt--;
 
-    ALOGV("%s: [%" PRId64 "]", __FUNCTION__, timestamp);
-    ATRACE_ASYNC_END("HEIC capture", inputFrame.frameNumber);
+    ALOGV("%s: [%" PRId64 "]", __FUNCTION__, frameNumber);
+    ATRACE_ASYNC_END("HEIC capture", frameNumber);
     return OK;
 }
 
 
-void HeicCompositeStream::releaseInputFrameLocked(InputFrame *inputFrame /*out*/) {
+void HeicCompositeStream::releaseInputFrameLocked(int64_t frameNumber,
+        InputFrame *inputFrame /*out*/) {
     if (inputFrame == nullptr) {
         return;
     }
@@ -1138,9 +1215,9 @@
         inputFrame->codecInputBuffers.erase(it);
     }
 
-    if ((inputFrame->error || mErrorState) && !inputFrame->errorNotified) {
-        notifyError(inputFrame->frameNumber);
-        inputFrame->errorNotified = true;
+    if (inputFrame->error || mErrorState) {
+        ALOGV("%s: notifyError called for frameNumber %" PRId64, __FUNCTION__, frameNumber);
+        notifyError(frameNumber, inputFrame->requestId);
     }
 
     if (inputFrame->fileFd >= 0) {
@@ -1152,6 +1229,8 @@
         sp<ANativeWindow> outputANW = mOutputSurface;
         outputANW->cancelBuffer(mOutputSurface.get(), inputFrame->anb, /*fence*/ -1);
         inputFrame->anb = nullptr;
+
+        mDequeuedOutputBufferCnt--;
     }
 }
 
@@ -1161,8 +1240,8 @@
     while (it != mPendingInputFrames.end()) {
         auto& inputFrame = it->second;
         if (inputFrame.error ||
-            (inputFrame.appSegmentWritten && inputFrame.pendingOutputTiles == 0)) {
-            releaseInputFrameLocked(&inputFrame);
+                (inputFrame.appSegmentWritten && inputFrame.pendingOutputTiles == 0)) {
+            releaseInputFrameLocked(it->first, &inputFrame);
             it = mPendingInputFrames.erase(it);
             inputFrameDone = true;
         } else {
@@ -1179,6 +1258,8 @@
         auto firstPendingFrame = mPendingInputFrames.begin();
         if (firstPendingFrame != mPendingInputFrames.end()) {
             updateCodecQualityLocked(firstPendingFrame->second.quality);
+        } else {
+            markTrackerIdle();
         }
     }
 }
@@ -1397,20 +1478,6 @@
     return expectedSize;
 }
 
-int64_t HeicCompositeStream::findTimestampInNsLocked(int64_t timeInUs) {
-    for (const auto& fn : mFrameNumberMap) {
-        if (timeInUs == ns2us(fn.second)) {
-            return fn.second;
-        }
-    }
-    for (const auto& inputFrame : mPendingInputFrames) {
-        if (timeInUs == ns2us(inputFrame.first)) {
-            return inputFrame.first;
-        }
-    }
-    return -1;
-}
-
 status_t HeicCompositeStream::copyOneYuvTile(sp<MediaCodecBuffer>& codecBuffer,
         const CpuConsumer::LockedBuffer& yuvBuffer,
         size_t top, size_t left, size_t width, size_t height) {
@@ -1584,7 +1651,7 @@
 }
 
 bool HeicCompositeStream::threadLoop() {
-    int64_t currentTs = INT64_MAX;
+    int64_t frameNumber = -1;
     bool newInputAvailable = false;
 
     {
@@ -1600,19 +1667,25 @@
 
         while (!newInputAvailable) {
             compilePendingInputLocked();
-            newInputAvailable = getNextReadyInputLocked(&currentTs);
+            newInputAvailable = getNextReadyInputLocked(&frameNumber);
 
             if (!newInputAvailable) {
-                auto failingFrameNumber = getNextFailingInputLocked(&currentTs);
+                auto failingFrameNumber = getNextFailingInputLocked();
                 if (failingFrameNumber >= 0) {
-                    // We cannot erase 'mPendingInputFrames[currentTs]' at this point because it is
-                    // possible for two internal stream buffers to fail. In such scenario the
-                    // composite stream should notify the client about a stream buffer error only
-                    // once and this information is kept within 'errorNotified'.
-                    // Any present failed input frames will be removed on a subsequent call to
-                    // 'releaseInputFramesLocked()'.
-                    releaseInputFrameLocked(&mPendingInputFrames[currentTs]);
-                    currentTs = INT64_MAX;
+                    releaseInputFrameLocked(failingFrameNumber,
+                            &mPendingInputFrames[failingFrameNumber]);
+
+                    // It's okay to remove the entry from mPendingInputFrames
+                    // because:
+                    // 1. Only one internal stream (main input) is critical in
+                    // backing the output stream.
+                    // 2. If captureResult/appSegment arrives after the entry is
+                    // removed, they are simply skipped.
+                    mPendingInputFrames.erase(failingFrameNumber);
+                    if (mPendingInputFrames.size() == 0) {
+                        markTrackerIdle();
+                    }
+                    return true;
                 }
 
                 auto ret = mInputReadyCondition.waitRelative(mMutex, kWaitDuration);
@@ -1627,12 +1700,13 @@
         }
     }
 
-    auto res = processInputFrame(currentTs, mPendingInputFrames[currentTs]);
+    auto res = processInputFrame(frameNumber, mPendingInputFrames[frameNumber]);
     Mutex::Autolock l(mMutex);
     if (res != OK) {
-        ALOGE("%s: Failed processing frame with timestamp: %" PRIu64 ": %s (%d)",
-                __FUNCTION__, currentTs, strerror(-res), res);
-        mPendingInputFrames[currentTs].error = true;
+        ALOGE("%s: Failed processing frame with timestamp: %" PRIu64 ", frameNumber: %"
+                PRId64 ": %s (%d)", __FUNCTION__, mPendingInputFrames[frameNumber].timestamp,
+                frameNumber, strerror(-res), res);
+        mPendingInputFrames[frameNumber].error = true;
     }
 
     releaseInputFramesLocked();
@@ -1640,14 +1714,26 @@
     return true;
 }
 
+void HeicCompositeStream::flagAnExifErrorFrameNumber(int64_t frameNumber) {
+    Mutex::Autolock l(mMutex);
+    mExifErrorFrameNumbers.emplace(frameNumber);
+    mInputReadyCondition.signal();
+}
+
 bool HeicCompositeStream::onStreamBufferError(const CaptureResultExtras& resultExtras) {
     bool res = false;
+    int64_t frameNumber = resultExtras.frameNumber;
+
     // Buffer errors concerning internal composite streams should not be directly visible to
     // camera clients. They must only receive a single buffer error with the public composite
     // stream id.
-    if ((resultExtras.errorStreamId == mAppSegmentStreamId) ||
-            (resultExtras.errorStreamId == mMainImageStreamId)) {
-        flagAnErrorFrameNumber(resultExtras.frameNumber);
+    if (resultExtras.errorStreamId == mAppSegmentStreamId) {
+        ALOGV("%s: APP_SEGMENT frameNumber: %" PRId64, __FUNCTION__, frameNumber);
+        flagAnExifErrorFrameNumber(frameNumber);
+        res = true;
+    } else if (resultExtras.errorStreamId == mMainImageStreamId) {
+        ALOGV("%s: YUV frameNumber: %" PRId64, __FUNCTION__, frameNumber);
+        flagAnErrorFrameNumber(frameNumber);
         res = true;
     }
 
@@ -1660,16 +1746,16 @@
     Mutex::Autolock l(mMutex);
 
     int64_t timestamp = -1;
-    for (const auto& fn : mFrameNumberMap) {
+    for (const auto& fn : mSettingsByFrameNumber) {
         if (fn.first == resultExtras.frameNumber) {
-            timestamp = fn.second;
+            timestamp = fn.second.timestamp;
             break;
         }
     }
     if (timestamp == -1) {
         for (const auto& inputFrame : mPendingInputFrames) {
-            if (inputFrame.second.frameNumber == resultExtras.frameNumber) {
-                timestamp = inputFrame.first;
+            if (inputFrame.first == resultExtras.frameNumber) {
+                timestamp = inputFrame.second.timestamp;
                 break;
             }
         }
@@ -1681,9 +1767,33 @@
     }
 
     mCaptureResults.emplace(timestamp, std::make_tuple(resultExtras.frameNumber, CameraMetadata()));
+    ALOGV("%s: timestamp %" PRId64 ", frameNumber %" PRId64, __FUNCTION__,
+            timestamp, resultExtras.frameNumber);
     mInputReadyCondition.signal();
 }
 
+void HeicCompositeStream::onRequestError(const CaptureResultExtras& resultExtras) {
+    auto frameNumber = resultExtras.frameNumber;
+    ALOGV("%s: frameNumber: %" PRId64, __FUNCTION__, frameNumber);
+    Mutex::Autolock l(mMutex);
+    auto numRequests = mSettingsByFrameNumber.erase(frameNumber);
+    if (numRequests == 0) {
+        // Pending request has been populated into mPendingInputFrames
+        mErrorFrameNumbers.emplace(frameNumber);
+        mInputReadyCondition.signal();
+    } else {
+        // REQUEST_ERROR was received without onShutter.
+    }
+}
+
+void HeicCompositeStream::markTrackerIdle() {
+    sp<StatusTracker> statusTracker = mStatusTracker.promote();
+    if (statusTracker != nullptr) {
+        statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);
+        ALOGV("%s: Mark component as idle", __FUNCTION__);
+    }
+}
+
 void HeicCompositeStream::CodecCallbackHandler::onMessageReceived(const sp<AMessage> &msg) {
     sp<HeicCompositeStream> parent = mParent.promote();
     if (parent == nullptr) return;
diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.h b/services/camera/libcameraservice/api2/HeicCompositeStream.h
index 8fc521e..33ca69a 100644
--- a/services/camera/libcameraservice/api2/HeicCompositeStream.h
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.h
@@ -37,7 +37,7 @@
 class HeicCompositeStream : public CompositeStream, public Thread,
         public CpuConsumer::FrameAvailableListener {
 public:
-    HeicCompositeStream(wp<CameraDeviceBase> device,
+    HeicCompositeStream(sp<CameraDeviceBase> device,
             wp<hardware::camera2::ICameraDeviceCallbacks> cb);
     ~HeicCompositeStream() override;
 
@@ -81,6 +81,7 @@
     bool threadLoop() override;
     bool onStreamBufferError(const CaptureResultExtras& resultExtras) override;
     void onResultError(const CaptureResultExtras& resultExtras) override;
+    void onRequestError(const CaptureResultExtras& resultExtras) override;
 
 private:
     //
@@ -156,9 +157,10 @@
         CpuConsumer::LockedBuffer          yuvBuffer;
         std::vector<CodecInputBufferInfo>  codecInputBuffers;
 
-        bool                      error;
-        bool                      errorNotified;
-        int64_t                   frameNumber;
+        bool                      error;     // Main input image buffer error
+        bool                      exifError; // Exif/APP_SEGMENT buffer error
+        int64_t                   timestamp;
+        int32_t                   requestId;
 
         sp<AMessage>              format;
         sp<MediaMuxer>            muxer;
@@ -172,30 +174,29 @@
         size_t                    codecInputCounter;
 
         InputFrame() : orientation(0), quality(kDefaultJpegQuality), error(false),
-                       errorNotified(false), frameNumber(-1), fenceFd(-1), fileFd(-1),
-                       trackIndex(-1), anb(nullptr), appSegmentWritten(false),
+                       exifError(false), timestamp(-1), requestId(-1), fenceFd(-1),
+                       fileFd(-1), trackIndex(-1), anb(nullptr), appSegmentWritten(false),
                        pendingOutputTiles(0), codecInputCounter(0) { }
     };
 
     void compilePendingInputLocked();
-    // Find first complete and valid frame with smallest timestamp
-    bool getNextReadyInputLocked(int64_t *currentTs /*out*/);
-    // Find next failing frame number with smallest timestamp and return respective frame number
-    int64_t getNextFailingInputLocked(int64_t *currentTs /*out*/);
+    // Find first complete and valid frame with smallest frame number
+    bool getNextReadyInputLocked(int64_t *frameNumber /*out*/);
+    // Find next failing frame number with smallest frame number and return respective frame number
+    int64_t getNextFailingInputLocked();
 
-    status_t processInputFrame(nsecs_t timestamp, InputFrame &inputFrame);
+    status_t processInputFrame(int64_t frameNumber, InputFrame &inputFrame);
     status_t processCodecInputFrame(InputFrame &inputFrame);
-    status_t startMuxerForInputFrame(nsecs_t timestamp, InputFrame &inputFrame);
-    status_t processAppSegment(nsecs_t timestamp, InputFrame &inputFrame);
-    status_t processOneCodecOutputFrame(nsecs_t timestamp, InputFrame &inputFrame);
-    status_t processCompletedInputFrame(nsecs_t timestamp, InputFrame &inputFrame);
+    status_t startMuxerForInputFrame(int64_t frameNumber, InputFrame &inputFrame);
+    status_t processAppSegment(int64_t frameNumber, InputFrame &inputFrame);
+    status_t processOneCodecOutputFrame(int64_t frameNumber, InputFrame &inputFrame);
+    status_t processCompletedInputFrame(int64_t frameNumber, InputFrame &inputFrame);
 
-    void releaseInputFrameLocked(InputFrame *inputFrame /*out*/);
+    void releaseInputFrameLocked(int64_t frameNumber, InputFrame *inputFrame /*out*/);
     void releaseInputFramesLocked();
 
     size_t findAppSegmentsSize(const uint8_t* appSegmentBuffer, size_t maxSize,
             size_t* app1SegmentSize);
-    int64_t findTimestampInNsLocked(int64_t timeInUs);
     status_t copyOneYuvTile(sp<MediaCodecBuffer>& codecBuffer,
             const CpuConsumer::LockedBuffer& yuvBuffer,
             size_t top, size_t left, size_t width, size_t height);
@@ -218,12 +219,14 @@
     sp<CpuConsumer>   mAppSegmentConsumer;
     sp<Surface>       mAppSegmentSurface;
     size_t            mAppSegmentMaxSize;
+    std::queue<int64_t> mAppSegmentFrameNumbers;
     CameraMetadata    mStaticInfo;
 
     int               mMainImageStreamId, mMainImageSurfaceId;
     sp<Surface>       mMainImageSurface;
     sp<CpuConsumer>   mMainImageConsumer; // Only applicable for HEVC codec.
     bool              mYuvBufferAcquired; // Only applicable to HEVC codec
+    std::queue<int64_t> mMainImageFrameNumbers;
 
     static const int32_t kMaxOutputSurfaceProducerCount = 1;
     sp<Surface>       mOutputSurface;
@@ -231,9 +234,22 @@
     int32_t           mDequeuedOutputBufferCnt;
 
     // Map from frame number to JPEG setting of orientation+quality
-    std::map<int64_t, std::pair<int32_t, int32_t>> mSettingsByFrameNumber;
-    // Map from timestamp to JPEG setting of orientation+quality
-    std::map<int64_t, std::pair<int32_t, int32_t>> mSettingsByTimestamp;
+    struct HeicSettings {
+        int32_t orientation;
+        int32_t quality;
+        int64_t timestamp;
+        int32_t requestId;
+        bool shutterNotified;
+
+        HeicSettings() : orientation(0), quality(95), timestamp(0),
+                requestId(-1), shutterNotified(false) {}
+        HeicSettings(int32_t _orientation, int32_t _quality) :
+                orientation(_orientation),
+                quality(_quality), timestamp(0),
+                requestId(-1), shutterNotified(false) {}
+
+    };
+    std::map<int64_t, HeicSettings> mSettingsByFrameNumber;
 
     // Keep all incoming APP segment Blob buffer pending further processing.
     std::vector<int64_t> mInputAppSegmentBuffers;
@@ -241,7 +257,7 @@
 
     // Keep all incoming HEIC blob buffer pending further processing.
     std::vector<CodecOutputBufferInfo> mCodecOutputBuffers;
-    std::queue<int64_t> mCodecOutputBufferTimestamps;
+    std::queue<int64_t> mCodecOutputBufferFrameNumbers;
     size_t mCodecOutputCounter;
     int32_t mQuality;
 
@@ -253,11 +269,19 @@
     // Artificial strictly incremental YUV grid timestamp to make encoder happy.
     int64_t mGridTimestampUs;
 
-    // In most common use case, entries are accessed in order.
+    // Indexed by frame number. In most common use case, entries are accessed in order.
     std::map<int64_t, InputFrame> mPendingInputFrames;
 
     // Function pointer of libyuv row copy.
     void (*mFnCopyRow)(const uint8_t* src, uint8_t* dst, int width);
+
+    // A set of APP_SEGMENT error frame numbers
+    std::set<int64_t> mExifErrorFrameNumbers;
+    void flagAnExifErrorFrameNumber(int64_t frameNumber);
+
+    // The status id for tracking the active/idle status of this composite stream
+    int mStatusId;
+    void markTrackerIdle();
 };
 
 }; // namespace camera3
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 3662a65..a537ef5 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -33,6 +33,7 @@
 #include "camera/CaptureResult.h"
 #include "gui/IGraphicBufferProducer.h"
 #include "device3/Camera3StreamInterface.h"
+#include "device3/StatusTracker.h"
 #include "binder/Status.h"
 #include "FrameProducer.h"
 
@@ -362,6 +363,10 @@
     virtual status_t setRotateAndCropAutoBehavior(
             camera_metadata_enum_android_scaler_rotate_and_crop_t rotateAndCropValue) = 0;
 
+    /**
+     * Get the status tracker of the camera device
+     */
+    virtual wp<camera3::StatusTracker> getStatusTracker() = 0;
 };
 
 }; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index b00a2d9..4a509aa 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -60,6 +60,7 @@
 #include "device3/Camera3SharedOutputStream.h"
 #include "CameraService.h"
 #include "utils/CameraThreadState.h"
+#include "utils/TraceHFR.h"
 
 #include <algorithm>
 #include <tuple>
@@ -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;
 }
 
@@ -2825,7 +2835,7 @@
 }
 
 void Camera3Device::removeInFlightMapEntryLocked(int idx) {
-    ATRACE_CALL();
+    ATRACE_HFR_CALL();
     nsecs_t duration = mInFlightMap.valueAt(idx).maxExpectedDuration;
     mInFlightMap.removeItemsAt(idx, 1);
 
@@ -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 19ecf4b..408f1f9 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -231,6 +231,9 @@
     status_t setRotateAndCropAutoBehavior(
             camera_metadata_enum_android_scaler_rotate_and_crop_t rotateAndCropValue);
 
+    // Get the status trackeer for the camera device
+    wp<camera3::StatusTracker> getStatusTracker() { return mStatusTracker; }
+
     /**
      * Helper functions to map between framework and HIDL values
      */
@@ -635,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,
@@ -1014,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/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index ef0d919..bda2961 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -269,8 +269,6 @@
         }
     }
 
-    mBufferReturnedSignal.signal();
-
     if (output) {
         mLastTimestamp = timestamp;
     }
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
index 750f64d..448379c 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
@@ -55,7 +55,6 @@
     // number of output buffers that are currently acquired by HAL. This will be
     // Redundant when camera3 streams are no longer bidirectional streams.
     size_t            mHandoutOutputBufferCount;
-    Condition         mBufferReturnedSignal;
     uint32_t          mFrameCount;
     // Last received output buffer's timestamp
     nsecs_t           mLastTimestamp;
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/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index e1d35e8..01ca006 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -21,6 +21,7 @@
 #include <utils/Log.h>
 #include <utils/Trace.h>
 #include "Camera3OutputStream.h"
+#include "utils/TraceHFR.h"
 
 #ifndef container_of
 #define container_of(ptr, type, member) \
@@ -160,7 +161,7 @@
 
 status_t Camera3OutputStream::getBufferLocked(camera3_stream_buffer *buffer,
         const std::vector<size_t>&) {
-    ATRACE_CALL();
+    ATRACE_HFR_CALL();
 
     ANativeWindowBuffer* anb;
     int fenceFd = -1;
@@ -190,7 +191,7 @@
 status_t Camera3OutputStream::returnBufferLocked(
         const camera3_stream_buffer &buffer,
         nsecs_t timestamp, const std::vector<size_t>& surface_ids) {
-    ATRACE_CALL();
+    ATRACE_HFR_CALL();
 
     status_t res = returnAnyBufferLocked(buffer, timestamp, /*output*/true, surface_ids);
 
@@ -516,7 +517,7 @@
 }
 
 status_t Camera3OutputStream::getBufferLockedCommon(ANativeWindowBuffer** anb, int* fenceFd) {
-    ATRACE_CALL();
+    ATRACE_HFR_CALL();
     status_t res;
 
     if ((res = getBufferPreconditionCheckLocked()) != OK) {
diff --git a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
index 603f516..eea5ef1 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
@@ -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 request/result error notifications, arrived.
     if (request.numBuffersLeft == 0 &&
             (request.skipResultMetadata ||
             (request.haveResultMetadata && shutterTimestamp != 0))) {
@@ -434,7 +434,17 @@
             states.useHalBufManager, states.listener,
             request.pendingOutputBuffers.array(),
             request.pendingOutputBuffers.size(), 0, /*timestampIncreasing*/true,
-            request.outputSurfaces, request.resultExtras);
+            request.outputSurfaces, request.resultExtras,
+            request.errorBufStrategy);
+
+        // Note down the just completed frame number
+        if (request.hasInputBuffer) {
+            states.lastCompletedReprocessFrameNumber = frameNumber;
+        } else if (request.zslCapture) {
+            states.lastCompletedZslFrameNumber = frameNumber;
+        } else {
+            states.lastCompletedRegularFrameNumber = frameNumber;
+        }
 
         removeInFlightMapEntryLocked(states, idx);
         ALOGVV("%s: removed frame %d from InFlightMap", __FUNCTION__, frameNumber);
@@ -487,10 +497,13 @@
         InFlightRequest &request = states.inflightMap.editValueAt(idx);
         ALOGVV("%s: got InFlightRequest requestId = %" PRId32
                 ", frameNumber = %" PRId64 ", burstId = %" PRId32
-                ", partialResultCount = %d, hasCallback = %d",
+                ", partialResultCount = %d/%d, hasCallback = %d, num_output_buffers %d"
+                ", usePartialResult = %d",
                 __FUNCTION__, request.resultExtras.requestId,
                 request.resultExtras.frameNumber, request.resultExtras.burstId,
-                result->partial_result, request.hasCallback);
+                result->partial_result, states.numPartialResults,
+                request.hasCallback, result->num_output_buffers,
+                states.usePartialResult);
         // 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.
@@ -555,6 +568,7 @@
                     request.collectedPartialResult);
             }
             request.haveResultMetadata = true;
+            request.errorBufStrategy = ERROR_BUF_RETURN_NOTIFY;
         }
 
         uint32_t numBuffersReturned = result->num_output_buffers;
@@ -581,18 +595,14 @@
             request.sensorTimestamp = entry.data.i64[0];
         }
 
-        // If shutter event isn't received yet, append the output buffers to
-        // the in-flight request. Otherwise, return the output buffers to
-        // streams.
-        if (shutterTimestamp == 0) {
-            request.pendingOutputBuffers.appendArray(result->output_buffers,
+        // If shutter event isn't received yet, do not return the pending output
+        // buffers.
+        request.pendingOutputBuffers.appendArray(result->output_buffers,
                 result->num_output_buffers);
-        } else {
-            bool timestampIncreasing = !(request.zslCapture || request.hasInputBuffer);
-            returnOutputBuffers(states.useHalBufManager, states.listener,
-                result->output_buffers, result->num_output_buffers,
-                shutterTimestamp, timestampIncreasing,
-                request.outputSurfaces, request.resultExtras);
+        if (shutterTimestamp != 0) {
+            returnAndRemovePendingOutputBuffers(
+                states.useHalBufManager, states.listener,
+                request);
         }
 
         if (result->result != NULL && !isPartialResult) {
@@ -791,10 +801,26 @@
         const camera3_stream_buffer_t *outputBuffers, size_t numBuffers,
         nsecs_t timestamp, bool timestampIncreasing,
         const SurfaceMap& outputSurfaces,
-        const CaptureResultExtras &inResultExtras) {
+        const CaptureResultExtras &inResultExtras,
+        ERROR_BUF_STRATEGY errorBufStrategy) {
 
     for (size_t i = 0; i < numBuffers; i++)
     {
+        Camera3StreamInterface *stream = Camera3Stream::cast(outputBuffers[i].stream);
+        int streamId = stream->getId();
+
+        // Call notify(ERROR_BUFFER) if necessary.
+        if (outputBuffers[i].status == CAMERA3_BUFFER_STATUS_ERROR &&
+                errorBufStrategy == ERROR_BUF_RETURN_NOTIFY) {
+            if (listener != nullptr) {
+                CaptureResultExtras extras = inResultExtras;
+                extras.errorStreamId = streamId;
+                listener->notifyError(
+                        hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER,
+                        extras);
+            }
+        }
+
         if (outputBuffers[i].buffer == nullptr) {
             if (!useHalBufManager) {
                 // With HAL buffer management API, HAL sometimes will have to return buffers that
@@ -805,20 +831,23 @@
             continue;
         }
 
-        Camera3StreamInterface *stream = Camera3Stream::cast(outputBuffers[i].stream);
-        int streamId = stream->getId();
         const auto& it = outputSurfaces.find(streamId);
         status_t res = OK;
-        if (it != outputSurfaces.end()) {
-            res = stream->returnBuffer(
-                    outputBuffers[i], timestamp, timestampIncreasing, it->second,
-                    inResultExtras.frameNumber);
-        } else {
-            res = stream->returnBuffer(
-                    outputBuffers[i], timestamp, timestampIncreasing, std::vector<size_t> (),
-                    inResultExtras.frameNumber);
-        }
 
+        // Do not return the buffer if the buffer status is error, and the error
+        // buffer strategy is CACHE.
+        if (outputBuffers[i].status != CAMERA3_BUFFER_STATUS_ERROR ||
+                errorBufStrategy != ERROR_BUF_CACHE) {
+            if (it != outputSurfaces.end()) {
+                res = stream->returnBuffer(
+                        outputBuffers[i], timestamp, timestampIncreasing, it->second,
+                        inResultExtras.frameNumber);
+            } else {
+                res = stream->returnBuffer(
+                        outputBuffers[i], timestamp, timestampIncreasing, std::vector<size_t> (),
+                        inResultExtras.frameNumber);
+            }
+        }
         // Note: stream may be deallocated at this point, if this buffer was
         // the last reference to it.
         if (res == NO_INIT || res == DEAD_OBJECT) {
@@ -848,6 +877,28 @@
     }
 }
 
+void returnAndRemovePendingOutputBuffers(bool useHalBufManager,
+        sp<NotificationListener> listener, InFlightRequest& request) {
+    bool timestampIncreasing = !(request.zslCapture || request.hasInputBuffer);
+    returnOutputBuffers(useHalBufManager, listener,
+            request.pendingOutputBuffers.array(),
+            request.pendingOutputBuffers.size(),
+            request.shutterTimestamp, timestampIncreasing,
+            request.outputSurfaces, request.resultExtras,
+            request.errorBufStrategy);
+
+    // Remove error buffers that are not cached.
+    for (auto iter = request.pendingOutputBuffers.begin();
+            iter != request.pendingOutputBuffers.end(); ) {
+        if (request.errorBufStrategy != ERROR_BUF_CACHE ||
+                iter->status != CAMERA3_BUFFER_STATUS_ERROR) {
+            iter = request.pendingOutputBuffers.erase(iter);
+        } else {
+            iter++;
+        }
+    }
+}
+
 void notifyShutter(CaptureOutputStates& states, const camera3_shutter_msg_t &msg) {
     ATRACE_CALL();
     ssize_t idx;
@@ -899,6 +950,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
@@ -908,13 +965,8 @@
                     r.hasInputBuffer, r.zslCapture && r.stillCapture,
                     r.rotateAndCropAuto, r.cameraIdsWithZoom, r.physicalMetadatas);
             }
-            bool timestampIncreasing = !(r.zslCapture || r.hasInputBuffer);
-            returnOutputBuffers(
-                    states.useHalBufManager, states.listener,
-                    r.pendingOutputBuffers.array(),
-                    r.pendingOutputBuffers.size(), r.shutterTimestamp, timestampIncreasing,
-                    r.outputSurfaces, r.resultExtras);
-            r.pendingOutputBuffers.clear();
+            returnAndRemovePendingOutputBuffers(
+                    states.useHalBufManager, states.listener, r);
 
             removeInFlightRequestIfReadyLocked(states, idx);
         }
@@ -968,7 +1020,6 @@
             break;
         case hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST:
         case hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT:
-        case hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER:
             {
                 std::lock_guard<std::mutex> l(states.inflightLock);
                 ssize_t idx = states.inflightMap.indexOfKey(msg.frame_number);
@@ -976,7 +1027,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 +1041,22 @@
                             }
                             r.physicalCameraIds.erase(iter);
                             resultExtras.errorPhysicalCameraId = physicalCameraId;
-                        } else {
-                            logicalDeviceResultError = true;
+                            physicalDeviceResultError = true;
                         }
                     }
 
-                    if (logicalDeviceResultError
-                            ||  hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST ==
-                            errorCode) {
+                    if (!physicalDeviceResultError) {
                         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 (hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT
+                                == errorCode) {
+                            r.errorBufStrategy = ERROR_BUF_RETURN_NOTIFY;
+                        } else {
+                            // errorCode is ERROR_CAMERA_REQUEST
+                            r.errorBufStrategy = ERROR_BUF_RETURN;
+                        }
+
+                        // Check whether the buffers returned. If they returned,
+                        // remove inflight request.
                         removeInFlightRequestIfReadyLocked(states, idx);
                     }
                 } else {
@@ -1024,6 +1074,10 @@
                         states.cameraId.string(), __FUNCTION__);
             }
             break;
+        case hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER:
+            // Do not depend on HAL ERROR_CAMERA_BUFFER to send buffer error
+            // callback to the app. Rather, use STATUS_ERROR of image buffers.
+            break;
         default:
             // SET_ERR calls notifyError
             SET_ERR("Unknown error message from HAL: %d", msg.error_code);
@@ -1338,8 +1392,14 @@
                 request.pendingOutputBuffers.array(),
                 request.pendingOutputBuffers.size(), 0,
                 /*timestampIncreasing*/true, request.outputSurfaces,
-                request.resultExtras);
+                request.resultExtras, request.errorBufStrategy);
+            ALOGW("%s: Frame %d |  Timestamp: %" PRId64 ", metadata"
+                    " arrived: %s, buffers left: %d.\n", __FUNCTION__,
+                    states.inflightMap.keyAt(idx), request.shutterTimestamp,
+                    request.haveResultMetadata ? "true" : "false",
+                    request.numBuffersLeft);
         }
+
         states.inflightMap.clear();
         states.inflightIntf.onInflightMapFlushedLocked();
     }
diff --git a/services/camera/libcameraservice/device3/Camera3OutputUtils.h b/services/camera/libcameraservice/device3/Camera3OutputUtils.h
index fbb47f8..9946312 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputUtils.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputUtils.h
@@ -44,7 +44,9 @@
     /**
      * Helper methods shared between Camera3Device/Camera3OfflineSession for HAL callbacks
      */
-    // helper function to return the output buffers to output streams.
+
+    // helper function to return the output buffers to output streams. The
+    // function also optionally calls notify(ERROR_BUFFER).
     void returnOutputBuffers(
             bool useHalBufManager,
             sp<NotificationListener> listener, // Only needed when outputSurfaces is not empty
@@ -53,13 +55,25 @@
             // The following arguments are only meant for surface sharing use case
             const SurfaceMap& outputSurfaces = SurfaceMap{},
             // Used to send buffer error callback when failing to return buffer
-            const CaptureResultExtras &resultExtras = CaptureResultExtras{});
+            const CaptureResultExtras &resultExtras = CaptureResultExtras{},
+            ERROR_BUF_STRATEGY errorBufStrategy = ERROR_BUF_RETURN);
+
+    // helper function to return the output buffers to output streams, and
+    // remove the returned buffers from the inflight request's pending buffers
+    // vector.
+    void returnAndRemovePendingOutputBuffers(
+            bool useHalBufManager,
+            sp<NotificationListener> listener, // Only needed when outputSurfaces is not empty
+            InFlightRequest& request);
 
     // Camera3Device/Camera3OfflineSession internal states used in notify/processCaptureResult
     // callbacks
     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/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 7916ddb..20f6168 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -22,6 +22,7 @@
 #include <utils/Trace.h>
 #include "device3/Camera3Stream.h"
 #include "device3/StatusTracker.h"
+#include "utils/TraceHFR.h"
 
 #include <cutils/properties.h>
 
@@ -601,7 +602,7 @@
 status_t Camera3Stream::getBuffer(camera3_stream_buffer *buffer,
         nsecs_t waitBufferTimeout,
         const std::vector<size_t>& surface_ids) {
-    ATRACE_CALL();
+    ATRACE_HFR_CALL();
     Mutex::Autolock l(mLock);
     status_t res = OK;
 
@@ -682,7 +683,7 @@
 status_t Camera3Stream::returnBuffer(const camera3_stream_buffer &buffer,
         nsecs_t timestamp, bool timestampIncreasing,
          const std::vector<size_t>& surface_ids, uint64_t frameNumber) {
-    ATRACE_CALL();
+    ATRACE_HFR_CALL();
     Mutex::Autolock l(mLock);
 
     // Check if this buffer is outstanding.
@@ -814,6 +815,8 @@
     info.mError = (buffer.status == CAMERA3_BUFFER_STATUS_ERROR);
     info.mFrameNumber = frameNumber;
     info.mTimestamp = timestamp;
+    info.mStreamId = getId();
+
     // TODO: rest of fields
 
     for (it = mBufferListenerList.begin(), end = mBufferListenerList.end();
diff --git a/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h b/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h
index d0aee27..170da5a 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h
@@ -29,6 +29,7 @@
 public:
 
     struct BufferInfo {
+        int mStreamId;
         bool mOutput; // if false then input buffer
         Rect mCrop;
         uint32_t mTransform;
diff --git a/services/camera/libcameraservice/device3/InFlightRequest.h b/services/camera/libcameraservice/device3/InFlightRequest.h
index 424043b..da4f228 100644
--- a/services/camera/libcameraservice/device3/InFlightRequest.h
+++ b/services/camera/libcameraservice/device3/InFlightRequest.h
@@ -32,7 +32,18 @@
 
 namespace camera3 {
 
+typedef enum {
+    // Cache the buffers with STATUS_ERROR within InFlightRequest
+    ERROR_BUF_CACHE,
+    // Return the buffers with STATUS_ERROR to the buffer queue
+    ERROR_BUF_RETURN,
+    // Return the buffers with STATUS_ERROR to the buffer queue, and call
+    // notify(ERROR_BUFFER) as well
+    ERROR_BUF_RETURN_NOTIFY
+} ERROR_BUF_STRATEGY;
+
 struct InFlightRequest {
+
     // Set by notify() SHUTTER call.
     nsecs_t shutterTimestamp;
     // Set by process_capture_result().
@@ -43,6 +54,9 @@
     // Decremented by calls to process_capture_result with valid output
     // and input buffers
     int     numBuffersLeft;
+
+    // The inflight request is considered complete if all buffers are returned
+
     CaptureResultExtras resultExtras;
     // If this request has any input buffer
     bool hasInputBuffer;
@@ -79,6 +93,10 @@
     // REQUEST/RESULT error.
     bool skipResultMetadata;
 
+    // Whether the buffers with STATUS_ERROR should be cached as pending buffers,
+    // returned to the buffer queue, or returned to the buffer queue and notify with ERROR_BUFFER.
+    ERROR_BUF_STRATEGY errorBufStrategy;
+
     // The physical camera ids being requested.
     std::set<String8> physicalCameraIds;
 
@@ -114,6 +132,7 @@
             hasCallback(true),
             maxExpectedDuration(kDefaultExpectedDuration),
             skipResultMetadata(false),
+            errorBufStrategy(ERROR_BUF_CACHE),
             stillCapture(false),
             zslCapture(false),
             rotateAndCropAuto(false) {
@@ -134,6 +153,7 @@
             hasCallback(hasAppCallback),
             maxExpectedDuration(maxDuration),
             skipResultMetadata(false),
+            errorBufStrategy(ERROR_BUF_CACHE),
             physicalCameraIds(physicalCameraIdSet),
             stillCapture(isStillCapture),
             zslCapture(isZslCapture),
diff --git a/services/camera/libcameraservice/tests/ClientManagerTest.cpp b/services/camera/libcameraservice/tests/ClientManagerTest.cpp
new file mode 100644
index 0000000..6a38427
--- /dev/null
+++ b/services/camera/libcameraservice/tests/ClientManagerTest.cpp
@@ -0,0 +1,108 @@
+/*
+ * 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 "ClientManagerTest"
+
+#include "../utils/ClientManager.h"
+#include <gtest/gtest.h>
+
+using namespace android::resource_policy;
+
+struct TestClient {
+    TestClient(int id, int32_t cost, const std::set<int>& conflictingKeys, int32_t ownerId,
+            int32_t score, int32_t state, bool isVendorClient) :
+            mId(id), mCost(cost), mConflictingKeys(conflictingKeys),
+            mOwnerId(ownerId), mScore(score), mState(state), mIsVendorClient(isVendorClient) {};
+    int mId;
+    int32_t mCost;    // Int 0..100
+    std::set<int> mConflictingKeys;
+    int32_t mOwnerId; // PID
+    int32_t mScore;   // Priority
+    int32_t mState;   // Foreground/background etc
+    bool mIsVendorClient;
+};
+
+using TestClientDescriptor = ClientDescriptor<int, TestClient>;
+using TestDescriptorPtr = std::shared_ptr<TestClientDescriptor>;
+
+TestDescriptorPtr makeDescFromTestClient(const TestClient& tc) {
+    return std::make_shared<TestClientDescriptor>(/*ID*/tc.mId, tc, tc.mCost, tc.mConflictingKeys,
+            tc.mScore, tc.mOwnerId, tc.mState, tc.mIsVendorClient);
+}
+
+class TestClientManager : public ClientManager<int, TestClient> {
+public:
+    TestClientManager() {}
+    virtual ~TestClientManager() {}
+};
+
+
+// Test ClientMager behavior when there is only one single owner
+// The expected behavior is that if one owner (application or vendor) is trying
+// to open second camera, it may succeed or not, but the first opened camera
+// should never be evicted.
+TEST(ClientManagerTest, SingleOwnerMultipleCamera) {
+
+    TestClientManager cm;
+    TestClient cam0Client(/*ID*/0, /*cost*/100, /*conflicts*/{1},
+            /*ownerId*/ 1000, /*score*/50, /*state*/ 1, /*isVendorClient*/ false);
+    auto cam0Desc = makeDescFromTestClient(cam0Client);
+    auto evicted = cm.addAndEvict(cam0Desc);
+    ASSERT_EQ(evicted.size(), 0u) << "Evicted list must be empty";
+
+    TestClient cam1Client(/*ID*/1, /*cost*/100, /*conflicts*/{0},
+            /*ownerId*/ 1000, /*score*/50, /*state*/ 1, /*isVendorClient*/ false);
+    auto cam1Desc = makeDescFromTestClient(cam1Client);
+
+    // 1. Check with conflicting devices, new client would be evicted
+    auto wouldBeEvicted = cm.wouldEvict(cam1Desc);
+    ASSERT_EQ(wouldBeEvicted.size(), 1u) << "Evicted list length must be 1";
+    ASSERT_EQ(wouldBeEvicted[0]->getKey(), cam1Desc->getKey()) << "cam1 must be evicted";
+
+    cm.removeAll();
+
+    TestClient cam2Client(/*ID*/2, /*cost*/100, /*conflicts*/{},
+            /*ownerId*/ 1000, /*score*/50, /*state*/ 1, /*isVendorClient*/ false);
+    auto cam2Desc = makeDescFromTestClient(cam2Client);
+    evicted = cm.addAndEvict(cam2Desc);
+    ASSERT_EQ(evicted.size(), 0u) << "Evicted list must be empty";
+
+    TestClient cam3Client(/*ID*/3, /*cost*/100, /*conflicts*/{},
+            /*ownerId*/ 1000, /*score*/50, /*state*/ 1, /*isVendorClient*/ false);
+    auto cam3Desc = makeDescFromTestClient(cam3Client);
+
+    // 2. Check without conflicting devices, the pre-existing client won't be evicted
+    // In this case, the new client would be granted, but could later be rejected by HAL due to
+    // resource cost.
+    wouldBeEvicted = cm.wouldEvict(cam3Desc);
+    ASSERT_EQ(wouldBeEvicted.size(), 0u) << "Evicted list must be empty";
+
+    cm.removeAll();
+
+    evicted = cm.addAndEvict(cam0Desc);
+    ASSERT_EQ(evicted.size(), 0u) << "Evicted list must be empty";
+
+    TestClient cam0ClientNew(/*ID*/0, /*cost*/100, /*conflicts*/{1},
+            /*ownerId*/ 1000, /*score*/50, /*state*/ 1, /*isVendorClient*/ false);
+    auto cam0DescNew = makeDescFromTestClient(cam0ClientNew);
+    wouldBeEvicted = cm.wouldEvict(cam0DescNew);
+
+    // 3. Check opening the same camera twice will evict the older client
+    ASSERT_EQ(wouldBeEvicted.size(), 1u) << "Evicted list length must be 1";
+    ASSERT_EQ(wouldBeEvicted[0], cam0Desc) << "cam0 (old) must be evicted";
+}
+
diff --git a/services/camera/libcameraservice/utils/ClientManager.h b/services/camera/libcameraservice/utils/ClientManager.h
index 35d25bf..64be6c5 100644
--- a/services/camera/libcameraservice/utils/ClientManager.h
+++ b/services/camera/libcameraservice/utils/ClientManager.h
@@ -496,6 +496,20 @@
                 evictList.clear();
                 evictList.push_back(client);
                 return evictList;
+            } else if (conflicting && owner == curOwner) {
+                // Pre-existing conflicting client with the same client owner exists
+                // Open the same device twice -> most recent open wins
+                // Otherwise let the existing client wins to avoid behaviors difference
+                // due to how HAL advertising conflicting devices (which is hidden from
+                // application)
+                if (curKey == key) {
+                    evictList.push_back(i);
+                    totalCost -= curCost;
+                } else {
+                    evictList.clear();
+                    evictList.push_back(client);
+                    return evictList;
+                }
             } else if (conflicting || ((totalCost > mMaxCost && curCost > 0) &&
                     (curPriority >= priority) &&
                     !(highestPriorityOwner == owner && owner == curOwner))) {
diff --git a/services/camera/libcameraservice/utils/SessionConfigurationUtils.h b/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
index fb519d9..cfb9f17 100644
--- a/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
+++ b/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
@@ -32,7 +32,7 @@
 class SessionConfigurationUtils {
 public:
     // utility function to convert AIDL SessionConfiguration to HIDL
-    // streamConfiguration. Also checks for sanity of SessionConfiguration and
+    // streamConfiguration. Also checks for validity of SessionConfiguration and
     // returns a non-ok binder::Status if the passed in session configuration
     // isn't valid.
     static binder::Status
diff --git a/services/camera/libcameraservice/utils/TraceHFR.h b/services/camera/libcameraservice/utils/TraceHFR.h
new file mode 100644
index 0000000..3a1900f
--- /dev/null
+++ b/services/camera/libcameraservice/utils/TraceHFR.h
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_SERVERS_ENABLE_HFR_TRACES_H_
+#define ANDROID_SERVERS_ENABLE_HFR_TRACES_H_
+
+#include <utils/Trace.h>
+
+#ifdef HFR_ENABLE_TRACING
+#define ATRACE_HFR_CALL() ATRACE_CALL()
+#else
+#define ATRACE_HFR_CALL()
+#endif
+
+#endif
diff --git a/services/mediacodec/Android.bp b/services/mediacodec/Android.bp
index 4bf103c..c4efbaa 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",
@@ -32,8 +33,6 @@
 
     init_rc: ["mediaswcodec.rc"],
 
-    required: ["mediaswcodec.policy"],
-
     cflags: [
         "-Werror",
         "-Wall",
diff --git a/services/mediametrics/AnalyticsState.h b/services/mediametrics/AnalyticsState.h
index b648947..09c0b4c 100644
--- a/services/mediametrics/AnalyticsState.h
+++ b/services/mediametrics/AnalyticsState.h
@@ -93,7 +93,7 @@
         int32_t ll = lines;
 
         if (ll > 0) {
-            ss << "TransactionLog:\n";
+            ss << "TransactionLog: gc(" << mTransactionLog.getGarbageCollectionCount() << ")\n";
             --ll;
         }
         if (ll > 0) {
@@ -102,7 +102,7 @@
             ll -= l;
         }
         if (ll > 0) {
-            ss << "TimeMachine:\n";
+            ss << "TimeMachine: gc(" << mTimeMachine.getGarbageCollectionCount() << ")\n";
             --ll;
         }
         if (ll > 0) {
diff --git a/services/mediametrics/Android.bp b/services/mediametrics/Android.bp
index 8e5bc79..f033d5c 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",
@@ -16,26 +102,26 @@
         "libutils",
     ],
     header_libs: [
+        "libaudioutils_headers",
         "libmediametrics_headers",
     ],
 
     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",
+        "cleaner.cpp",
         "iface_statsd.cpp",
         "MediaMetricsService.cpp",
         "statsd_audiopolicy.cpp",
@@ -47,6 +133,7 @@
         "statsd_extractor.cpp",
         "statsd_nuplayer.cpp",
         "statsd_recorder.cpp",
+        "StringUtils.cpp"
     ],
 
     proto: {
@@ -54,8 +141,11 @@
     },
 
     shared_libs: [
+        "libbase", // android logging
         "libbinder",
+        "libcutils",
         "liblog",
+        "libmedia_helper",
         "libmediametrics",
         "libmediautils",
         "libmemunreachable",
@@ -71,11 +161,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 fe3a34d..29801a4 100644
--- a/services/mediametrics/AudioAnalytics.cpp
+++ b/services/mediametrics/AudioAnalytics.cpp
@@ -16,20 +16,148 @@
 
 //#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
 
-// Enable for testing of delivery to statsd
-// #define 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"
 
 namespace android::mediametrics {
 
-AudioAnalytics::AudioAnalytics()
+// Enable for testing of delivery to statsd. Caution if this is enabled, all protos MUST exist.
+#define STATSD_ENABLE
+
+#ifdef STATSD_ENABLE
+#define CONDITION(INT_VALUE) (INT_VALUE)  // allow value
+#else
+#define CONDITION(INT_VALUE) (int(0))     // mask value since the proto may not be defined yet.
+#endif
+
+// Maximum length of a device name.
+static constexpr size_t STATSD_DEVICE_NAME_MAX_LENGTH = 32;
+
+// Transmit Enums to statsd in integer or strings  (this must match the atoms.proto)
+static constexpr bool STATSD_USE_INT_FOR_ENUM = false;
+
+// derive types based on integer or strings.
+using short_enum_type_t = std::conditional_t<STATSD_USE_INT_FOR_ENUM, int32_t, std::string>;
+using long_enum_type_t = std::conditional_t<STATSD_USE_INT_FOR_ENUM, int64_t, std::string>;
+
+// Convert std::string to char *
+template <typename T>
+auto ENUM_EXTRACT(const T& x) {
+    if constexpr (std::is_same_v<std::decay_t<T>, std::string>) {
+        return x.c_str();
+    } else {
+        return x;
+    }
+}
+
+static constexpr const auto LOG_LEVEL = android::base::VERBOSE;
+
+static constexpr int PREVIOUS_STATE_EXPIRE_SEC = 60 * 60; // 1 hour.
+
+/*
+ * For logging purposes, we list all of the MediaMetrics atom fields,
+ * which can then be associated with consecutive arguments to the statsd write.
+ */
+
+static constexpr const char * const AudioRecordDeviceUsageFields[] = {
+    "mediametrics_audiorecorddeviceusage_reported", // proto number
+    "devices",
+    "device_names",
+    "device_time_nanos",
+    "encoding",
+    "frame_count",
+    "interval_count",
+    "sample_rate",
+    "flags",
+    "package_name",
+    "selected_device_id",
+    "caller",
+    "source",
+};
+
+static constexpr const char * const AudioThreadDeviceUsageFields[] = {
+    "mediametrics_audiothreaddeviceusage_reported",
+    "devices",
+    "device_names",
+    "device_time_nanos",
+    "encoding",
+    "frame_count",
+    "interval_count",
+    "sample_rate",
+    "flags",
+    "xruns",
+    "type",
+};
+
+static constexpr const char * const AudioTrackDeviceUsageFields[] = {
+    "mediametrics_audiotrackdeviceusage_reported",
+    "devices",
+    "device_names",
+    "device_time_nanos",
+    "encoding",
+    "frame_count",
+    "interval_count",
+    "sample_rate",
+    "flags",
+    "xruns",
+    "package_name",
+    "device_latency_millis",
+    "device_startup_millis",
+    "device_volume",
+    "selected_device_id",
+    "stream_type",
+    "usage",
+    "content_type",
+    "caller",
+    "traits",
+};
+
+static constexpr const char * const AudioDeviceConnectionFields[] = {
+    "mediametrics_audiodeviceconnection_reported",
+    "input_devices",
+    "output_devices",
+    "device_names",
+    "result",
+    "time_to_connect_millis",
+    "connection_count",
+};
+
+/**
+ * sendToStatsd is a helper method that sends the arguments to statsd
+ * and returns a pair { result, summary_string }.
+ */
+template <size_t N, typename ...Types>
+std::pair<int, std::string> sendToStatsd(const char * const (& fields)[N], Types ... args)
 {
+    int result = 0;
+    std::stringstream ss;
+
+#ifdef STATSD_ENABLE
+    result = android::util::stats_write(args...);
+    ss << "result:" << result;
+#endif
+    ss << " { ";
+    stringutils::fieldPrint(ss, fields, args...);
+    ss << "}";
+    return { result, ss.str() };
+}
+
+AudioAnalytics::AudioAnalytics()
+    : mDeliverStatistics(property_get_bool(PROP_AUDIO_ANALYTICS_CLOUD_ENABLED, true))
+{
+    SetMinimumLogSeverity(android::base::DEBUG); // for LOG().
     ALOGD("%s", __func__);
 
     // Add action to save AnalyticsState if audioserver is restarted.
@@ -47,49 +175,28 @@
                 // to end of full expression.
                 mAnalyticsState->clear();  // TODO: filter the analytics state.
                 // Perhaps report this.
+
+                // Set up a timer to expire the previous audio state to save space.
+                // Use the transaction log size as a cookie to see if it is the
+                // same as before.  A benign race is possible where a state is cleared early.
+                const size_t size = mPreviousAnalyticsState->transactionLog().size();
+                mTimedAction.postIn(
+                        std::chrono::seconds(PREVIOUS_STATE_EXPIRE_SEC), [this, size](){
+                    if (mPreviousAnalyticsState->transactionLog().size() == size) {
+                        ALOGD("expiring previous audio state after %d seconds.",
+                                PREVIOUS_STATE_EXPIRE_SEC);
+                        mPreviousAnalyticsState->clear();  // removes data from the state.
+                    }
+                });
             }));
 
-    // Check underruns
+    // Handle device use record statistics
     mActions.addAction(
-        AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD "*." AMEDIAMETRICS_PROP_EVENT,
-        std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_UNDERRUN),
+        AMEDIAMETRICS_KEY_PREFIX_AUDIO_RECORD "*." AMEDIAMETRICS_PROP_EVENT,
+        std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP),
         std::make_shared<AnalyticsActions::Function>(
             [this](const std::shared_ptr<const android::mediametrics::Item> &item){
-                std::string threadId = item->getKey().substr(
-                        sizeof(AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD) - 1);
-                std::string outputDevices;
-                mAnalyticsState->timeMachine().get(
-                        item->getKey(), AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices);
-                ALOGD("(key=%s) Thread underrun event detected on io handle:%s device:%s",
-                        item->getKey().c_str(), threadId.c_str(), outputDevices.c_str());
-                if (outputDevices.find("AUDIO_DEVICE_OUT_BLUETOOTH") != std::string::npos) {
-                    // report this for Bluetooth
-                }
-            }));
-
-    // Check latencies, playback and startup
-    mActions.addAction(
-        AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK "*." AMEDIAMETRICS_PROP_LATENCYMS,
-        std::monostate{},  // accept any value
-        std::make_shared<AnalyticsActions::Function>(
-            [this](const std::shared_ptr<const android::mediametrics::Item> &item){
-                double latencyMs{};
-                double startupMs{};
-                if (!item->get(AMEDIAMETRICS_PROP_LATENCYMS, &latencyMs)
-                        || !item->get(AMEDIAMETRICS_PROP_STARTUPMS, &startupMs)) return;
-
-                std::string trackId = item->getKey().substr(
-                        sizeof(AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK) - 1);
-                std::string thread = getThreadFromTrack(item->getKey());
-                std::string outputDevices;
-                mAnalyticsState->timeMachine().get(
-                        thread, AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices);
-                ALOGD("(key=%s) Track latencyMs:%lf startupMs:%lf detected on port:%s device:%s",
-                        item->getKey().c_str(), latencyMs, startupMs,
-                        trackId.c_str(), outputDevices.c_str());
-                if (outputDevices.find("AUDIO_DEVICE_OUT_BLUETOOTH") != std::string::npos) {
-                    // report this for Bluetooth
-                }
+                mDeviceUse.endAudioIntervalGroup(item, DeviceUse::RECORD);
             }));
 
     // Handle device use thread statistics
@@ -98,7 +205,7 @@
         std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP),
         std::make_shared<AnalyticsActions::Function>(
             [this](const std::shared_ptr<const android::mediametrics::Item> &item){
-                mDeviceUse.endAudioIntervalGroup(item, false /* isTrack */);
+                mDeviceUse.endAudioIntervalGroup(item, DeviceUse::THREAD);
             }));
 
     // Handle device use track statistics
@@ -107,10 +214,11 @@
         std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP),
         std::make_shared<AnalyticsActions::Function>(
             [this](const std::shared_ptr<const android::mediametrics::Item> &item){
-                mDeviceUse.endAudioIntervalGroup(item, true /* isTrack */);
+                mDeviceUse.endAudioIntervalGroup(item, DeviceUse::TRACK);
             }));
 
-    // Handle device routing statistics
+
+    // Handle device connection statistics
 
     // We track connections (not disconnections) for the time to connect.
     // TODO: consider BT requests in their A2dp service
@@ -119,7 +227,7 @@
     // AudioDeviceBroker.postA2dpActiveDeviceChange
     mActions.addAction(
         "audio.device.a2dp.state",
-        std::string("connected"),
+        "connected",
         std::make_shared<AnalyticsActions::Function>(
             [this](const std::shared_ptr<const android::mediametrics::Item> &item){
                 mDeviceConnection.a2dpConnected(item);
@@ -132,6 +240,60 @@
             [this](const std::shared_ptr<const android::mediametrics::Item> &item){
                 mDeviceConnection.createPatch(item);
             }));
+
+    // Called from BT service
+    mActions.addAction(
+        AMEDIAMETRICS_KEY_PREFIX_AUDIO_DEVICE
+        "postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent"
+        "." AMEDIAMETRICS_PROP_STATE,
+        "connected",
+        std::make_shared<AnalyticsActions::Function>(
+            [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+                mDeviceConnection.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(item);
+            }));
+
+    // Handle power usage
+    mActions.addAction(
+        AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK "*." AMEDIAMETRICS_PROP_EVENT,
+        std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP),
+        std::make_shared<AnalyticsActions::Function>(
+            [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+                mAudioPowerUsage.checkTrackRecord(item, true /* isTrack */);
+            }));
+
+    mActions.addAction(
+        AMEDIAMETRICS_KEY_PREFIX_AUDIO_RECORD "*." AMEDIAMETRICS_PROP_EVENT,
+        std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP),
+        std::make_shared<AnalyticsActions::Function>(
+            [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+                mAudioPowerUsage.checkTrackRecord(item, false /* isTrack */);
+            }));
+
+    mActions.addAction(
+        AMEDIAMETRICS_KEY_AUDIO_FLINGER "." AMEDIAMETRICS_PROP_EVENT,
+        std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_SETMODE),
+        std::make_shared<AnalyticsActions::Function>(
+            [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+                // ALOGD("(key=%s) Audioflinger setMode", item->getKey().c_str());
+                mAudioPowerUsage.checkMode(item);
+            }));
+
+    mActions.addAction(
+        AMEDIAMETRICS_KEY_AUDIO_FLINGER "." AMEDIAMETRICS_PROP_EVENT,
+        std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOICEVOLUME),
+        std::make_shared<AnalyticsActions::Function>(
+            [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+                // ALOGD("(key=%s) Audioflinger setVoiceVolume", item->getKey().c_str());
+                mAudioPowerUsage.checkVoiceVolume(item);
+            }));
+
+    mActions.addAction(
+        AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD "*." AMEDIAMETRICS_PROP_EVENT,
+        std::string("createAudioPatch"),
+        std::make_shared<AnalyticsActions::Function>(
+            [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+                mAudioPowerUsage.checkCreatePatch(item);
+            }));
 }
 
 AudioAnalytics::~AudioAnalytics()
@@ -173,6 +335,27 @@
         ss << s;
         ll -= l;
     }
+
+    if (ll > 0) {
+        // Print the statsd atoms we sent out.
+        const std::string statsd = mStatsdLog.dumpToString("  " /* prefix */, ll - 1);
+        const size_t n = std::count(statsd.begin(), statsd.end(), '\n') + 1; // we control this.
+        if ((size_t)ll >= n) {
+            if (n == 1) {
+                ss << "Statsd atoms: empty or truncated\n";
+            } else {
+                ss << "Statsd atoms:\n" << statsd;
+            }
+            ll -= n;
+        }
+    }
+
+    if (ll > 0 && prefix == nullptr) {
+        auto [s, l] = mAudioPowerUsage.dump(ll);
+        ss << s;
+        ll -= l;
+    }
+
     return { ss.str(), lines - ll };
 }
 
@@ -199,11 +382,12 @@
 
 // DeviceUse helper class.
 void AudioAnalytics::DeviceUse::endAudioIntervalGroup(
-       const std::shared_ptr<const android::mediametrics::Item> &item, bool isTrack) const {
+       const std::shared_ptr<const android::mediametrics::Item> &item, ItemType itemType) const {
     const std::string& key = item->getKey();
     const std::string id = key.substr(
-            (isTrack ? sizeof(AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK)
-            : sizeof(AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD))
+            (itemType == THREAD ? sizeof(AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD)
+            : itemType == TRACK ? sizeof(AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK)
+            : sizeof(AMEDIAMETRICS_KEY_PREFIX_AUDIO_RECORD))
              - 1);
     // deliver statistics
     int64_t deviceTimeNs = 0;
@@ -215,41 +399,182 @@
     int32_t frameCount = 0;
     mAudioAnalytics.mAnalyticsState->timeMachine().get(
             key, AMEDIAMETRICS_PROP_FRAMECOUNT, &frameCount);
+    std::string inputDevicePairs;
+    mAudioAnalytics.mAnalyticsState->timeMachine().get(
+            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);
-    int32_t underrun = 0;
+    std::string flags;
     mAudioAnalytics.mAnalyticsState->timeMachine().get(
-            key, AMEDIAMETRICS_PROP_UNDERRUN, &underrun);
+            key, AMEDIAMETRICS_PROP_FLAGS, &flags);
+
+    // We may have several devices.
+    // 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 (oss.tellp() > 0) oss << "|";  // delimit devices with '|'.
+            oss << device;
+            outputDeviceBits += types::lookup<types::OUTPUT_DEVICE, long_enum_type_t>(device);
+        }
+    }
+    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;
-    std::string name;
+
+    std::string inputDeviceNames;  // not filled currently.
+    std::string outputDeviceNames;
     if (outputDevices.find("AUDIO_DEVICE_OUT_BLUETOOTH") != std::string::npos) {
         isBluetooth = true;
         mAudioAnalytics.mAnalyticsState->timeMachine().get(
-            "audio.device.bt_a2dp", AMEDIAMETRICS_PROP_NAME, &name);
+            "audio.device.bt_a2dp", AMEDIAMETRICS_PROP_NAME, &outputDeviceNames);
+        // Remove | if present
+        stringutils::replace(outputDeviceNames, "|", '?');
+        if (outputDeviceNames.size() > STATSD_DEVICE_NAME_MAX_LENGTH) {
+            outputDeviceNames.resize(STATSD_DEVICE_NAME_MAX_LENGTH); // truncate
+        }
     }
 
-    // We may have several devices.  We only list the first device.
-    // TODO: consider whether we should list all the devices separated by |
-    std::string firstDevice = "unknown";
-    auto devaddrvec = MediaMetricsService::getDeviceAddressPairs(outputDevices);
-    if (devaddrvec.size() != 0) {
-        firstDevice = devaddrvec[0].first;
-        // DO NOT show the address.
-    }
-
-    if (isTrack) {
+    switch (itemType) {
+    case RECORD: {
         std::string callerName;
+        const bool clientCalled = mAudioAnalytics.mAnalyticsState->timeMachine().get(
+                key, AMEDIAMETRICS_PROP_CALLERNAME, &callerName) == OK;
+
+        std::string packageName;
+        int64_t versionCode = 0;
+        int32_t uid = -1;
         mAudioAnalytics.mAnalyticsState->timeMachine().get(
-                key, AMEDIAMETRICS_PROP_CALLERNAME, &callerName);
+                key, AMEDIAMETRICS_PROP_ALLOWUID, &uid);
+        if (uid != -1) {
+            std::tie(packageName, versionCode) =
+                    MediaMetricsService::getSanitizedPackageNameAndVersionCode(uid);
+        }
+
+        int32_t selectedDeviceId = 0;
+        mAudioAnalytics.mAnalyticsState->timeMachine().get(
+                key, AMEDIAMETRICS_PROP_SELECTEDDEVICEID, &selectedDeviceId);
+        std::string source;
+        mAudioAnalytics.mAnalyticsState->timeMachine().get(
+                key, AMEDIAMETRICS_PROP_SOURCE, &source);
+
+        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(LOG_LEVEL) << "key:" << key
+              << " id:" << id
+              << " inputDevices:" << inputDevices << "(" << inputDeviceBits
+              << ") inputDeviceNames:" << inputDeviceNames
+              << " deviceTimeNs:" << deviceTimeNs
+              << " encoding:" << encoding << "(" << encodingForStats
+              << ") frameCount:" << frameCount
+              << " intervalCount:" << intervalCount
+              << " sampleRate:" << sampleRate
+              << " flags:" << flags << "(" << flagsForStats
+              << ") packageName:" << packageName
+              << " selectedDeviceId:" << selectedDeviceId
+              << " callerName:" << callerName << "(" << callerNameForStats
+              << ") source:" << source << "(" << sourceForStats << ")";
+        if (clientCalled  // only log if client app called AudioRecord.
+                && mAudioAnalytics.mDeliverStatistics) {
+            const auto [ result, str ] = sendToStatsd(AudioRecordDeviceUsageFields,
+                    CONDITION(android::util::MEDIAMETRICS_AUDIORECORDDEVICEUSAGE_REPORTED)
+                    , ENUM_EXTRACT(inputDeviceBits)
+                    , inputDeviceNames.c_str()
+                    , deviceTimeNs
+                    , ENUM_EXTRACT(encodingForStats)
+                    , frameCount
+                    , intervalCount
+                    , sampleRate
+                    , ENUM_EXTRACT(flagsForStats)
+
+                    , packageName.c_str()
+                    , selectedDeviceId
+                    , ENUM_EXTRACT(callerNameForStats)
+                    , ENUM_EXTRACT(sourceForStats)
+                    );
+            ALOGV("%s: statsd %s", __func__, str.c_str());
+            mAudioAnalytics.mStatsdLog.log("%s", str.c_str());
+        }
+    } break;
+    case THREAD: {
+        std::string type;
+        mAudioAnalytics.mAnalyticsState->timeMachine().get(
+                key, AMEDIAMETRICS_PROP_TYPE, &type);
+        int32_t underrun = 0; // zero for record types
+        mAudioAnalytics.mAnalyticsState->timeMachine().get(
+                key, AMEDIAMETRICS_PROP_UNDERRUN, &underrun);
+
+        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(LOG_LEVEL) << "key:" << key
+              << " id:" << id
+              << " inputDevices:" << inputDevices << "(" << inputDeviceBits
+              << ") outputDevices:" << outputDevices << "(" << outputDeviceBits
+              << ") inputDeviceNames:" << inputDeviceNames
+              << " outputDeviceNames:" << outputDeviceNames
+              << " deviceTimeNs:" << deviceTimeNs
+              << " encoding:" << encoding << "(" << encodingForStats
+              << ") frameCount:" << frameCount
+              << " intervalCount:" << intervalCount
+              << " sampleRate:" << sampleRate
+              << " underrun:" << underrun
+              << " flags:" << flags << "(" << flagsForStats
+              << ") type:" << type << "(" << typeForStats
+              << ")";
+        if (mAudioAnalytics.mDeliverStatistics) {
+            const auto [ result, str ] = sendToStatsd(AudioThreadDeviceUsageFields,
+                CONDITION(android::util::MEDIAMETRICS_AUDIOTHREADDEVICEUSAGE_REPORTED)
+                , isInput ? ENUM_EXTRACT(inputDeviceBits) : ENUM_EXTRACT(outputDeviceBits)
+                , isInput ? inputDeviceNames.c_str() : outputDeviceNames.c_str()
+                , deviceTimeNs
+                , ENUM_EXTRACT(encodingForStats)
+                , frameCount
+                , intervalCount
+                , sampleRate
+                , ENUM_EXTRACT(flagsForStats)
+                , underrun
+                , ENUM_EXTRACT(typeForStats)
+            );
+            ALOGV("%s: statsd %s", __func__, str.c_str());
+            mAudioAnalytics.mStatsdLog.log("%s", str.c_str());
+        }
+    } break;
+    case TRACK: {
+        std::string callerName;
+        const bool clientCalled = mAudioAnalytics.mAnalyticsState->timeMachine().get(
+                key, AMEDIAMETRICS_PROP_CALLERNAME, &callerName) == OK;
+
         std::string contentType;
         mAudioAnalytics.mAnalyticsState->timeMachine().get(
                 key, AMEDIAMETRICS_PROP_CONTENTTYPE, &contentType);
@@ -280,82 +605,83 @@
         int32_t selectedDeviceId = 0;
         mAudioAnalytics.mAnalyticsState->timeMachine().get(
                 key, AMEDIAMETRICS_PROP_SELECTEDDEVICEID, &selectedDeviceId);
-
+        std::string streamType;
+        mAudioAnalytics.mAnalyticsState->timeMachine().get(
+                key, AMEDIAMETRICS_PROP_STREAMTYPE, &streamType);
+        std::string traits;
+        mAudioAnalytics.mAnalyticsState->timeMachine().get(
+                key, AMEDIAMETRICS_PROP_TRAITS, &traits);
+        int32_t underrun = 0;
+        mAudioAnalytics.mAnalyticsState->timeMachine().get(
+                key, AMEDIAMETRICS_PROP_UNDERRUN, &underrun);
         std::string usage;
         mAudioAnalytics.mAnalyticsState->timeMachine().get(
                 key, AMEDIAMETRICS_PROP_USAGE, &usage);
 
-        ALOGD("(key=%s) id:%s endAudioIntervalGroup device:%s name:%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 "
-                 "selectedDevceId:%d usage:%s",
-                key.c_str(), id.c_str(), firstDevice.c_str(), name.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, usage.c_str());
-#ifdef STATSD
-        if (mAudioAnalytics.mDeliverStatistics) {
-            (void)android::util::stats_write(
-                    android::util::MEDIAMETRICS_AUDIOTRACKDEVICEUSAGE_REPORTED
-                    /* timestamp, */
-                    /* mediaApexVersion, */
-                    , firstDevice.c_str()
-                    , name.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 traitsForStats =
+                 types::lookup<types::TRACK_TRAITS, short_enum_type_t>(traits);
+        const auto usageForStats = types::lookup<types::USAGE, short_enum_type_t>(usage);
+
+        LOG(LOG_LEVEL) << "key:" << key
+              << " id:" << id
+              << " outputDevices:" << outputDevices << "(" << outputDeviceBits
+              << ") outputDeviceNames:" << outputDeviceNames
+              << " 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
+              << ") traits:" << traits << "(" << traitsForStats
+              << ") usage:" << usage << "(" << usageForStats
+              << ")";
+        if (clientCalled // only log if client app called AudioTracks
+                && mAudioAnalytics.mDeliverStatistics) {
+            const auto [ result, str ] = sendToStatsd(AudioTrackDeviceUsageFields,
+                    CONDITION(android::util::MEDIAMETRICS_AUDIOTRACKDEVICEUSAGE_REPORTED)
+                    , ENUM_EXTRACT(outputDeviceBits)
+                    , outputDeviceNames.c_str()
                     , deviceTimeNs
-                    , encoding.c_str()
+                    , ENUM_EXTRACT(encodingForStats)
                     , frameCount
                     , intervalCount
                     , sampleRate
+                    , ENUM_EXTRACT(flagsForStats)
                     , underrun
-
                     , packageName.c_str()
                     , (float)deviceLatencyMs
                     , (float)deviceStartupMs
                     , (float)deviceVolume
                     , selectedDeviceId
-                    , usage.c_str()
-                    , contentType.c_str()
-                    , callerName.c_str()
+                    , ENUM_EXTRACT(streamTypeForStats)
+                    , ENUM_EXTRACT(usageForStats)
+                    , ENUM_EXTRACT(contentTypeForStats)
+                    , ENUM_EXTRACT(callerNameForStats)
+                    , ENUM_EXTRACT(traitsForStats)
                     );
+            ALOGV("%s: statsd %s", __func__, str.c_str());
+            mAudioAnalytics.mStatsdLog.log("%s", str.c_str());
         }
-#endif
-    } else {
-
-        std::string flags;
-        mAudioAnalytics.mAnalyticsState->timeMachine().get(
-                key, AMEDIAMETRICS_PROP_FLAGS, &flags);
-
-        ALOGD("(key=%s) id:%s endAudioIntervalGroup device:%s name:%s "
-                 "deviceTimeNs:%lld encoding:%s frameCount:%d intervalCount:%d "
-                 "sampleRate:%d underrun:%d "
-                 "flags:%s",
-                key.c_str(), id.c_str(), firstDevice.c_str(), name.c_str(),
-                (long long)deviceTimeNs, encoding.c_str(), frameCount, intervalCount,
-                sampleRate, underrun,
-                flags.c_str());
-#ifdef STATSD
-        if (mAudioAnalytics.mDeliverStatistics) {
-            (void)android::util::stats_write(
-                android::util::MEDIAMETRICS_AUDIOTHREADDEVICEUSAGE_REPORTED
-                /* timestamp, */
-                /* mediaApexVersion, */
-                , firstDevice.c_str()
-                , name.c_str()
-                , deviceTimeNs
-                , encoding.c_str()
-                , frameCount
-                , intervalCount
-                , sampleRate
-                , underrun
-            );
-        }
-#endif
+        } break;
     }
 
     // Report this as needed.
@@ -368,49 +694,148 @@
 void AudioAnalytics::DeviceConnection::a2dpConnected(
        const std::shared_ptr<const android::mediametrics::Item> &item) {
     const std::string& key = item->getKey();
-
-    const int64_t connectedAtNs = item->getTimestamp();
+    const int64_t atNs = item->getTimestamp();
     {
         std::lock_guard l(mLock);
-        mA2dpTimeConnectedNs = connectedAtNs;
-         ++mA2dpConnectedAttempts;
+        mA2dpConnectionServiceNs = atNs;
+        ++mA2dpConnectionServices;
+
+        if (mA2dpConnectionRequestNs == 0) {
+            mAudioAnalytics.mTimedAction.postIn(std::chrono::seconds(5), [this](){ expire(); });
+        }
+        // This sets the time we were connected.  Now we look for the delta in the future.
     }
     std::string name;
     item->get(AMEDIAMETRICS_PROP_NAME, &name);
-    ALOGD("(key=%s) a2dp connected device:%s "
-             "connectedAtNs:%lld",
-            key.c_str(), name.c_str(),
-            (long long)connectedAtNs);
-    // Note - we need to be able to cancel a timed event
-    mAudioAnalytics.mTimedAction.postIn(std::chrono::seconds(5), [this](){ expire(); });
-    // This sets the time we were connected.  Now we look for the delta in the future.
+    ALOGD("(key=%s) a2dp connected device:%s atNs:%lld",
+            key.c_str(), name.c_str(), (long long)atNs);
 }
 
 void AudioAnalytics::DeviceConnection::createPatch(
        const std::shared_ptr<const android::mediametrics::Item> &item) {
     std::lock_guard l(mLock);
-    if (mA2dpTimeConnectedNs == 0) return; // ignore
+    if (mA2dpConnectionServiceNs == 0) return; // patch unrelated to us.
     const std::string& key = item->getKey();
     std::string outputDevices;
     item->get(AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices);
-    if (outputDevices.find("AUDIO_DEVICE_OUT_BLUETOOTH") != std::string::npos) {
+    if (outputDevices.find("AUDIO_DEVICE_OUT_BLUETOOTH_A2DP") != std::string::npos) {
         // TODO compare address
-        const int64_t timeDiff = item->getTimestamp() - mA2dpTimeConnectedNs;
-        ALOGD("(key=%s) A2DP device connection time: %lld", key.c_str(), (long long)timeDiff);
-        mA2dpTimeConnectedNs = 0; // reset counter.
-        ++mA2dpConnectedSuccesses;
+        int64_t timeDiffNs = item->getTimestamp();
+        if (mA2dpConnectionRequestNs == 0) {
+            ALOGD("%s: A2DP create patch didn't see a connection request", __func__);
+            timeDiffNs -= mA2dpConnectionServiceNs;
+        } else {
+            timeDiffNs -= mA2dpConnectionRequestNs;
+        }
+
+        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(LOG_LEVEL) << "key:" << key
+                << " A2DP SUCCESS"
+                << " outputDevices:" << outputDeviceBits
+                << " deviceName:" << mA2dpDeviceName
+                << " connectionTimeMs:" <<  connectionTimeMs;
+        if (mAudioAnalytics.mDeliverStatistics) {
+            const long_enum_type_t inputDeviceBits{};
+
+            const auto [ result, str ] = sendToStatsd(AudioDeviceConnectionFields,
+                    CONDITION(android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED)
+                    , ENUM_EXTRACT(inputDeviceBits)
+                    , ENUM_EXTRACT(outputDeviceBits)
+                    , mA2dpDeviceName.c_str()
+                    , types::DEVICE_CONNECTION_RESULT_SUCCESS
+                    , connectionTimeMs
+                    , /* connection_count */ 1
+                    );
+            ALOGV("%s: statsd %s", __func__, str.c_str());
+            mAudioAnalytics.mStatsdLog.log("%s", str.c_str());
+        }
     }
 }
 
+// Called through AudioManager when the BT service wants to enable
+void AudioAnalytics::DeviceConnection::postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+        const std::shared_ptr<const android::mediametrics::Item> &item) {
+    const int64_t atNs = item->getTimestamp();
+    const std::string& key = item->getKey();
+    std::string state;
+    item->get(AMEDIAMETRICS_PROP_STATE, &state);
+    if (state != "connected") return;
+
+    std::string name;
+    item->get(AMEDIAMETRICS_PROP_NAME, &name);
+    {
+        std::lock_guard l(mLock);
+        mA2dpConnectionRequestNs = atNs;
+        ++mA2dpConnectionRequests;
+        mA2dpDeviceName = name;
+    }
+    ALOGD("(key=%s) a2dp connection name:%s request atNs:%lld",
+            key.c_str(), name.c_str(), (long long)atNs);
+    // TODO: attempt to cancel a timed event, rather than let it expire.
+    mAudioAnalytics.mTimedAction.postIn(std::chrono::seconds(5), [this](){ expire(); });
+}
+
 void AudioAnalytics::DeviceConnection::expire() {
     std::lock_guard l(mLock);
-    if (mA2dpTimeConnectedNs == 0) return; // ignore
+    if (mA2dpConnectionRequestNs == 0) return; // ignore (this was an internal connection).
 
-    // An expiration may occur because there is no audio playing.
+    const long_enum_type_t inputDeviceBits{};
+    const auto outputDeviceBits = types::lookup<types::OUTPUT_DEVICE, long_enum_type_t>(
+            "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP");
+
+    if (mA2dpConnectionServiceNs == 0) {
+        ++mA2dpConnectionJavaServiceCancels;  // service did not connect to A2DP
+
+        LOG(LOG_LEVEL) << "A2DP CANCEL"
+                << " outputDevices:" << outputDeviceBits
+                << " deviceName:" << mA2dpDeviceName;
+        if (mAudioAnalytics.mDeliverStatistics) {
+            const auto [ result, str ] = sendToStatsd(AudioDeviceConnectionFields,
+                    CONDITION(android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED)
+                    , ENUM_EXTRACT(inputDeviceBits)
+                    , ENUM_EXTRACT(outputDeviceBits)
+                    , mA2dpDeviceName.c_str()
+                    , types::DEVICE_CONNECTION_RESULT_JAVA_SERVICE_CANCEL
+                    , /* connection_time_ms */ 0.f
+                    , /* connection_count */ 1
+                    );
+            ALOGV("%s: statsd %s", __func__, str.c_str());
+            mAudioAnalytics.mStatsdLog.log("%s", str.c_str());
+        }
+        return;
+    }
+
+    // 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");
-    ++mA2dpConnectedFailures; // this is not a true failure.
-    mA2dpTimeConnectedNs = 0;
+    mA2dpConnectionRequestNs = 0;
+    mA2dpConnectionServiceNs = 0;
+    ++mA2dpConnectionUnknowns;  // connection result unknown
+
+    LOG(LOG_LEVEL) << "A2DP UNKNOWN"
+            << " outputDevices:" << outputDeviceBits
+            << " deviceName:" << mA2dpDeviceName;
+    if (mAudioAnalytics.mDeliverStatistics) {
+        const auto [ result, str ] = sendToStatsd(AudioDeviceConnectionFields,
+                CONDITION(android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED)
+                , ENUM_EXTRACT(inputDeviceBits)
+                , ENUM_EXTRACT(outputDeviceBits)
+                , mA2dpDeviceName.c_str()
+                , types::DEVICE_CONNECTION_RESULT_UNKNOWN
+                , /* connection_time_ms */ 0.f
+                , /* connection_count */ 1
+                );
+        ALOGV("%s: statsd %s", __func__, str.c_str());
+        mAudioAnalytics.mStatsdLog.log("%s", str.c_str());
+    }
 }
 
-} // namespace android
+} // namespace android::mediametrics
diff --git a/services/mediametrics/AudioAnalytics.h b/services/mediametrics/AudioAnalytics.h
index eb9c228..df097b1 100644
--- a/services/mediametrics/AudioAnalytics.h
+++ b/services/mediametrics/AudioAnalytics.h
@@ -17,8 +17,10 @@
 #pragma once
 
 #include <android-base/thread_annotations.h>
+#include <audio_utils/SimpleLog.h>
 #include "AnalyticsActions.h"
 #include "AnalyticsState.h"
+#include "AudioPowerUsage.h"
 #include "TimedAction.h"
 #include "Wrap.h"
 
@@ -26,6 +28,9 @@
 
 class AudioAnalytics
 {
+    // AudioAnalytics action / state helper classes
+    friend AudioPowerUsage;
+
 public:
     AudioAnalytics();
     ~AudioAnalytics();
@@ -72,10 +77,24 @@
         // underlying state is locked.
         mPreviousAnalyticsState->clear();
         mAnalyticsState->clear();
+
+        // Clear power usage state.
+        mAudioPowerUsage.clear();
     }
 
 private:
 
+    /*
+     * AudioAnalytics class does not contain a monitor mutex.
+     * Instead, all of its variables are individually locked for access.
+     * Since data and items are generally added only (gc removes it), this is a reasonable
+     * compromise for availability/concurrency versus consistency.
+     *
+     * It is possible for concurrent threads to be reading and writing inside of AudioAnalytics.
+     * Reads based on a prior time (e.g. one second) in the past from the TimeMachine can be
+     * used to achieve better consistency if needed.
+     */
+
     /**
      * Checks for any pending actions for a particular item.
      *
@@ -91,31 +110,40 @@
      */
     std::string getThreadFromTrack(const std::string& track) const;
 
-    const bool mDeliverStatistics __unused = true;
+    const bool mDeliverStatistics;
 
     // Actions is individually locked
     AnalyticsActions mActions;
 
     // AnalyticsState is individually locked, and we use SharedPtrWrap
     // to allow safe access even if the shared pointer changes underneath.
-
+    // These wrap pointers always point to a valid state object.
     SharedPtrWrap<AnalyticsState> mAnalyticsState;
     SharedPtrWrap<AnalyticsState> mPreviousAnalyticsState;
 
     TimedAction mTimedAction; // locked internally
 
+    SimpleLog mStatsdLog{16 /* log lines */}; // locked internally
+
     // DeviceUse is a nested class which handles audio device usage accounting.
     // We define this class at the end to ensure prior variables all properly constructed.
     // TODO: Track / Thread interaction
     // TODO: Consider statistics aggregation.
     class DeviceUse {
     public:
+        enum ItemType {
+            RECORD = 0,
+            THREAD = 1,
+            TRACK = 2,
+        };
+
         explicit DeviceUse(AudioAnalytics &audioAnalytics) : mAudioAnalytics{audioAnalytics} {}
 
         // Called every time an endAudioIntervalGroup message is received.
         void endAudioIntervalGroup(
                 const std::shared_ptr<const android::mediametrics::Item> &item,
-                bool isTrack) const;
+                ItemType itemType) const;
+
     private:
         AudioAnalytics &mAudioAnalytics;
     } mDeviceUse{*this};
@@ -137,6 +165,10 @@
         void createPatch(
                 const std::shared_ptr<const android::mediametrics::Item> &item);
 
+        // Called through AudioManager when the BT service wants to notify connection
+        void postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+                const std::shared_ptr<const android::mediametrics::Item> &item);
+
         // When the timer expires.
         void expire();
 
@@ -144,11 +176,20 @@
         AudioAnalytics &mAudioAnalytics;
 
         mutable std::mutex mLock;
-        int64_t mA2dpTimeConnectedNs GUARDED_BY(mLock) = 0;
-        int32_t mA2dpConnectedAttempts GUARDED_BY(mLock) = 0;
-        int32_t mA2dpConnectedSuccesses GUARDED_BY(mLock) = 0;
-        int32_t mA2dpConnectedFailures GUARDED_BY(mLock) = 0;
+        std::string mA2dpDeviceName;
+        int64_t mA2dpConnectionRequestNs GUARDED_BY(mLock) = 0;  // Time for BT service request.
+        int64_t mA2dpConnectionServiceNs GUARDED_BY(mLock) = 0;  // Time audio service agrees.
+
+        int32_t mA2dpConnectionRequests GUARDED_BY(mLock) = 0;
+        int32_t mA2dpConnectionServices GUARDED_BY(mLock) = 0;
+
+        // See the statsd atoms.proto
+        int32_t mA2dpConnectionSuccesses GUARDED_BY(mLock) = 0;
+        int32_t mA2dpConnectionJavaServiceCancels GUARDED_BY(mLock) = 0;
+        int32_t mA2dpConnectionUnknowns GUARDED_BY(mLock) = 0;
     } mDeviceConnection{*this};
+
+    AudioPowerUsage mAudioPowerUsage{this};
 };
 
 } // namespace android::mediametrics
diff --git a/services/mediametrics/AudioPowerUsage.cpp b/services/mediametrics/AudioPowerUsage.cpp
new file mode 100644
index 0000000..cca6b41
--- /dev/null
+++ b/services/mediametrics/AudioPowerUsage.cpp
@@ -0,0 +1,395 @@
+/*
+ * 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 "AudioPowerUsage"
+#include <utils/Log.h>
+
+#include "AudioAnalytics.h"
+#include "MediaMetricsService.h"
+#include "StringUtils.h"
+#include <map>
+#include <sstream>
+#include <string>
+#include <audio_utils/clock.h>
+#include <cutils/properties.h>
+#include <statslog.h>
+#include <sys/timerfd.h>
+#include <system/audio-base.h>
+
+// property to disable audio power use metrics feature, default is enabled
+#define PROP_AUDIO_METRICS_DISABLED "persist.media.audio_metrics.power_usage_disabled"
+#define AUDIO_METRICS_DISABLED_DEFAULT (false)
+
+// property to set how long to send audio power use metrics data to statsd, default is 24hrs
+#define PROP_AUDIO_METRICS_INTERVAL_HR "persist.media.audio_metrics.interval_hr"
+#define INTERVAL_HR_DEFAULT (24)
+
+// for Audio Power Usage Metrics
+#define AUDIO_POWER_USAGE_KEY_AUDIO_USAGE     "audio.power.usage"
+
+#define AUDIO_POWER_USAGE_PROP_DEVICE         "device"     // int32
+#define AUDIO_POWER_USAGE_PROP_DURATION_NS    "durationNs" // int64
+#define AUDIO_POWER_USAGE_PROP_TYPE           "type"       // int32
+#define AUDIO_POWER_USAGE_PROP_VOLUME         "volume"     // double
+
+namespace android::mediametrics {
+
+/* static */
+bool AudioPowerUsage::typeFromString(const std::string& type_string, int32_t& type) {
+    static std::map<std::string, int32_t> typeTable = {
+        { "AUDIO_STREAM_VOICE_CALL",          VOIP_CALL_TYPE },
+        { "AUDIO_STREAM_SYSTEM",              MEDIA_TYPE },
+        { "AUDIO_STREAM_RING",                RINGTONE_NOTIFICATION_TYPE },
+        { "AUDIO_STREAM_MUSIC",               MEDIA_TYPE },
+        { "AUDIO_STREAM_ALARM",               ALARM_TYPE },
+        { "AUDIO_STREAM_NOTIFICATION",        RINGTONE_NOTIFICATION_TYPE },
+
+        { "AUDIO_CONTENT_TYPE_SPEECH",        VOIP_CALL_TYPE },
+        { "AUDIO_CONTENT_TYPE_MUSIC",         MEDIA_TYPE },
+        { "AUDIO_CONTENT_TYPE_MOVIE",         MEDIA_TYPE },
+        { "AUDIO_CONTENT_TYPE_SONIFICATION",  RINGTONE_NOTIFICATION_TYPE },
+
+        { "AUDIO_USAGE_MEDIA",                MEDIA_TYPE },
+        { "AUDIO_USAGE_VOICE_COMMUNICATION",  VOIP_CALL_TYPE },
+        { "AUDIO_USAGE_ALARM",                ALARM_TYPE },
+        { "AUDIO_USAGE_NOTIFICATION",         RINGTONE_NOTIFICATION_TYPE },
+
+        { "AUDIO_SOURCE_CAMCORDER",           CAMCORDER_TYPE },
+        { "AUDIO_SOURCE_VOICE_COMMUNICATION", VOIP_CALL_TYPE },
+        { "AUDIO_SOURCE_DEFAULT",             RECORD_TYPE },
+        { "AUDIO_SOURCE_MIC",                 RECORD_TYPE },
+        { "AUDIO_SOURCE_UNPROCESSED",         RECORD_TYPE },
+        { "AUDIO_SOURCE_VOICE_RECOGNITION",   RECORD_TYPE },
+    };
+
+    auto it = typeTable.find(type_string);
+    if (it == typeTable.end()) {
+        type = UNKNOWN_TYPE;
+        return false;
+    }
+
+    type = it->second;
+    return true;
+}
+
+/* static */
+bool AudioPowerUsage::deviceFromString(const std::string& device_string, int32_t& device) {
+    static std::map<std::string, int32_t> deviceTable = {
+        { "AUDIO_DEVICE_OUT_EARPIECE",             OUTPUT_EARPIECE },
+        { "AUDIO_DEVICE_OUT_SPEAKER_SAFE",         OUTPUT_SPEAKER_SAFE },
+        { "AUDIO_DEVICE_OUT_SPEAKER",              OUTPUT_SPEAKER },
+        { "AUDIO_DEVICE_OUT_WIRED_HEADSET",        OUTPUT_WIRED_HEADSET },
+        { "AUDIO_DEVICE_OUT_WIRED_HEADPHONE",      OUTPUT_WIRED_HEADSET },
+        { "AUDIO_DEVICE_OUT_BLUETOOTH_SCO",        OUTPUT_BLUETOOTH_SCO },
+        { "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP",       OUTPUT_BLUETOOTH_A2DP },
+        { "AUDIO_DEVICE_OUT_USB_HEADSET",          OUTPUT_USB_HEADSET },
+        { "AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET", OUTPUT_BLUETOOTH_SCO },
+
+        { "AUDIO_DEVICE_IN_BUILTIN_MIC",           INPUT_BUILTIN_MIC },
+        { "AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET", INPUT_BLUETOOTH_SCO },
+        { "AUDIO_DEVICE_IN_WIRED_HEADSET",         INPUT_WIRED_HEADSET_MIC },
+        { "AUDIO_DEVICE_IN_USB_DEVICE",            INPUT_USB_HEADSET_MIC },
+        { "AUDIO_DEVICE_IN_BACK_MIC",              INPUT_BUILTIN_BACK_MIC },
+    };
+
+    auto it = deviceTable.find(device_string);
+    if (it == deviceTable.end()) {
+        device = 0;
+        return false;
+    }
+
+    device = it->second;
+    return true;
+}
+
+int32_t AudioPowerUsage::deviceFromStringPairs(const std::string& device_strings) {
+    int32_t deviceMask = 0;
+    const auto devaddrvec = stringutils::getDeviceAddressPairs(device_strings);
+    for (const auto &[device, addr] : devaddrvec) {
+        int32_t combo_device = 0;
+        deviceFromString(device, combo_device);
+        deviceMask |= combo_device;
+    }
+    return deviceMask;
+}
+
+/* static */
+void AudioPowerUsage::sendItem(const std::shared_ptr<const mediametrics::Item>& item)
+{
+    int32_t type;
+    if (!item->getInt32(AUDIO_POWER_USAGE_PROP_TYPE, &type)) return;
+
+    int32_t device;
+    if (!item->getInt32(AUDIO_POWER_USAGE_PROP_DEVICE, &device)) return;
+
+    int64_t duration_ns;
+    if (!item->getInt64(AUDIO_POWER_USAGE_PROP_DURATION_NS, &duration_ns)) return;
+
+    double volume;
+    if (!item->getDouble(AUDIO_POWER_USAGE_PROP_VOLUME, &volume)) return;
+
+    (void)android::util::stats_write(android::util::AUDIO_POWER_USAGE_DATA_REPORTED,
+                                         device,
+                                         (int32_t)(duration_ns / NANOS_PER_SECOND),
+                                         (float)volume,
+                                         type);
+}
+
+bool AudioPowerUsage::saveAsItem_l(
+        int32_t device, int64_t duration_ns, int32_t type, double average_vol)
+{
+    ALOGV("%s: (%#x, %d, %lld, %f)", __func__, device, type,
+                                   (long long)duration_ns, average_vol );
+    if (duration_ns == 0) {
+        return true; // skip duration 0 usage
+    }
+    if (device == 0) {
+        return true; //ignore unknown device
+    }
+
+    for (const auto& item : mItems) {
+        int32_t item_type = 0, item_device = 0;
+        double item_volume = 0.;
+        int64_t item_duration_ns = 0;
+        item->getInt32(AUDIO_POWER_USAGE_PROP_DEVICE, &item_device);
+        item->getInt64(AUDIO_POWER_USAGE_PROP_DURATION_NS, &item_duration_ns);
+        item->getInt32(AUDIO_POWER_USAGE_PROP_TYPE, &item_type);
+        item->getDouble(AUDIO_POWER_USAGE_PROP_VOLUME, &item_volume);
+
+        // aggregate by device and type
+        if (item_device == device && item_type == type) {
+            int64_t final_duration_ns = item_duration_ns + duration_ns;
+            double final_volume = (device & INPUT_DEVICE_BIT) ? 1.0:
+                            ((item_volume * item_duration_ns +
+                            average_vol * duration_ns) / final_duration_ns);
+
+            item->setInt64(AUDIO_POWER_USAGE_PROP_DURATION_NS, final_duration_ns);
+            item->setDouble(AUDIO_POWER_USAGE_PROP_VOLUME, final_volume);
+            item->setTimestamp(systemTime(SYSTEM_TIME_REALTIME));
+
+            ALOGV("%s: update (%#x, %d, %lld, %f) --> (%lld, %f)", __func__,
+                  device, type,
+                  (long long)item_duration_ns, item_volume,
+                  (long long)final_duration_ns, final_volume);
+
+            return true;
+        }
+    }
+
+    auto sitem = std::make_shared<mediametrics::Item>(AUDIO_POWER_USAGE_KEY_AUDIO_USAGE);
+    sitem->setTimestamp(systemTime(SYSTEM_TIME_REALTIME));
+    sitem->setInt32(AUDIO_POWER_USAGE_PROP_DEVICE, device);
+    sitem->setInt64(AUDIO_POWER_USAGE_PROP_DURATION_NS, duration_ns);
+    sitem->setInt32(AUDIO_POWER_USAGE_PROP_TYPE, type);
+    sitem->setDouble(AUDIO_POWER_USAGE_PROP_VOLUME, average_vol);
+    mItems.emplace_back(sitem);
+    return true;
+}
+
+void AudioPowerUsage::checkTrackRecord(
+        const std::shared_ptr<const mediametrics::Item>& item, bool isTrack)
+{
+    const std::string key = item->getKey();
+
+    int64_t deviceTimeNs = 0;
+    if (!item->getInt64(AMEDIAMETRICS_PROP_DEVICETIMENS, &deviceTimeNs)) {
+        return;
+    }
+    double deviceVolume = 1.;
+    if (isTrack && !item->getDouble(AMEDIAMETRICS_PROP_DEVICEVOLUME, &deviceVolume)) {
+        return;
+    }
+    int32_t type = 0;
+    std::string type_string;
+    if ((isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
+               key, AMEDIAMETRICS_PROP_STREAMTYPE, &type_string) == OK) ||
+        (!isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
+               key, AMEDIAMETRICS_PROP_SOURCE, &type_string) == OK)) {
+        typeFromString(type_string, type);
+
+        if (isTrack && type == UNKNOWN_TYPE &&
+                   mAudioAnalytics->mAnalyticsState->timeMachine().get(
+                   key, AMEDIAMETRICS_PROP_USAGE, &type_string) == OK) {
+            typeFromString(type_string, type);
+        }
+        if (isTrack && type == UNKNOWN_TYPE &&
+                   mAudioAnalytics->mAnalyticsState->timeMachine().get(
+                   key, AMEDIAMETRICS_PROP_CONTENTTYPE, &type_string) == OK) {
+            typeFromString(type_string, type);
+        }
+        ALOGV("type = %s => %d", type_string.c_str(), type);
+    }
+
+    int32_t device = 0;
+    std::string device_strings;
+    if ((isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
+         key, AMEDIAMETRICS_PROP_OUTPUTDEVICES, &device_strings) == OK) ||
+        (!isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
+         key, AMEDIAMETRICS_PROP_INPUTDEVICES, &device_strings) == OK)) {
+
+        device = deviceFromStringPairs(device_strings);
+        ALOGV("device = %s => %d", device_strings.c_str(), device);
+    }
+    std::lock_guard l(mLock);
+    saveAsItem_l(device, deviceTimeNs, type, deviceVolume);
+}
+
+void AudioPowerUsage::checkMode(const std::shared_ptr<const mediametrics::Item>& item)
+{
+    std::string mode;
+    if (!item->getString(AMEDIAMETRICS_PROP_AUDIOMODE, &mode)) return;
+
+    std::lock_guard l(mLock);
+    if (mode == mMode) return;  // no change in mode.
+
+    if (mMode == "AUDIO_MODE_IN_CALL") { // leaving call mode
+        const int64_t endCallNs = item->getTimestamp();
+        const int64_t durationNs = endCallNs - mDeviceTimeNs;
+        if (durationNs > 0) {
+            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
+        mStartCallNs = item->getTimestamp(); // advisory only
+
+        mDeviceVolume = 0;
+        mVolumeTimeNs = mStartCallNs;
+        mDeviceTimeNs = mStartCallNs;
+    }
+    ALOGV("%s: new mode:%s  old mode:%s", __func__, mode.c_str(), mMode.c_str());
+    mMode = mode;
+}
+
+void AudioPowerUsage::checkVoiceVolume(const std::shared_ptr<const mediametrics::Item>& item)
+{
+    double voiceVolume = 0.;
+    if (!item->getDouble(AMEDIAMETRICS_PROP_VOICEVOLUME, &voiceVolume)) return;
+
+    std::lock_guard l(mLock);
+    if (voiceVolume == mVoiceVolume) return;  // no change in volume
+
+    // we only track average device volume when we are in-call
+    if (mMode == "AUDIO_MODE_IN_CALL") {
+        const int64_t timeNs = item->getTimestamp();
+        const int64_t durationNs = timeNs - mDeviceTimeNs;
+        if (durationNs > 0) {
+            mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
+                    mVoiceVolume * double(timeNs - mVolumeTimeNs)) / durationNs;
+            mVolumeTimeNs = timeNs;
+        }
+    }
+    ALOGV("%s: new voice volume:%lf  old voice volume:%lf", __func__, voiceVolume, mVoiceVolume);
+    mVoiceVolume = voiceVolume;
+}
+
+void AudioPowerUsage::checkCreatePatch(const std::shared_ptr<const mediametrics::Item>& item)
+{
+    std::string outputDevices;
+    if (!item->get(AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices)) return;
+
+    const std::string& key = item->getKey();
+    std::string flags;
+    if (mAudioAnalytics->mAnalyticsState->timeMachine().get(
+         key, AMEDIAMETRICS_PROP_FLAGS, &flags) != OK) return;
+
+    if (flags.find("AUDIO_OUTPUT_FLAG_PRIMARY") == std::string::npos) return;
+
+    const int32_t device = deviceFromStringPairs(outputDevices);
+
+    std::lock_guard l(mLock);
+    if (mPrimaryDevice == device) return;
+
+    if (mMode == "AUDIO_MODE_IN_CALL") {
+        // Save statistics
+        const int64_t endDeviceNs = item->getTimestamp();
+        const int64_t durationNs = endDeviceNs - mDeviceTimeNs;
+        if (durationNs > 0) {
+            mDeviceVolume = (mDeviceVolume * double(mVolumeTimeNs - mDeviceTimeNs) +
+                    mVoiceVolume * double(endDeviceNs - mVolumeTimeNs)) / durationNs;
+            saveAsItem_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
+        }
+        // reset statistics
+        mDeviceVolume = 0;
+        mDeviceTimeNs = endDeviceNs;
+        mVolumeTimeNs = endDeviceNs;
+    }
+    ALOGV("%s: new primary device:%#x  old primary device:%#x", __func__, device, mPrimaryDevice);
+    mPrimaryDevice = device;
+}
+
+AudioPowerUsage::AudioPowerUsage(AudioAnalytics *audioAnalytics)
+    : mAudioAnalytics(audioAnalytics)
+    , mDisabled(property_get_bool(PROP_AUDIO_METRICS_DISABLED, AUDIO_METRICS_DISABLED_DEFAULT))
+    , mIntervalHours(property_get_int32(PROP_AUDIO_METRICS_INTERVAL_HR, INTERVAL_HR_DEFAULT))
+{
+    ALOGD("%s", __func__);
+    ALOGI_IF(mDisabled, "AudioPowerUsage is disabled.");
+    collect(); // send items
+}
+
+AudioPowerUsage::~AudioPowerUsage()
+{
+    ALOGD("%s", __func__);
+}
+
+void AudioPowerUsage::clear()
+{
+    std::lock_guard _l(mLock);
+    mItems.clear();
+}
+
+void AudioPowerUsage::collect()
+{
+    std::lock_guard _l(mLock);
+    for (const auto &item : mItems) {
+        sendItem(item);
+    }
+    mItems.clear();
+    mAudioAnalytics->mTimedAction.postIn(
+        mIntervalHours <= 0 ? std::chrono::seconds(5) : std::chrono::hours(mIntervalHours),
+        [this](){ collect(); });
+}
+
+std::pair<std::string, int32_t> AudioPowerUsage::dump(int limit) const {
+    if (limit <= 2) {
+        return {{}, 0};
+    }
+    std::lock_guard _l(mLock);
+    if (mDisabled) {
+        return {"AudioPowerUsage disabled\n", 1};
+    }
+    if (mItems.empty()) {
+        return {"AudioPowerUsage empty\n", 1};
+    }
+
+    int slot = 1;
+    std::stringstream ss;
+    ss << "AudioPowerUsage:\n";
+    for (const auto &item : mItems) {
+        if (slot >= limit - 1) {
+            ss << "-- AudioPowerUsage may be truncated!\n";
+            ++slot;
+            break;
+        }
+        ss << " " << slot << " " << item->toString() << "\n";
+        slot++;
+    }
+    return { ss.str(), slot };
+}
+
+} // namespace android::mediametrics
diff --git a/services/mediametrics/AudioPowerUsage.h b/services/mediametrics/AudioPowerUsage.h
new file mode 100644
index 0000000..446ff4f
--- /dev/null
+++ b/services/mediametrics/AudioPowerUsage.h
@@ -0,0 +1,105 @@
+/*
+ * 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 <android-base/thread_annotations.h>
+#include <deque>
+#include <media/MediaMetricsItem.h>
+#include <mutex>
+#include <thread>
+
+namespace android::mediametrics {
+
+class AudioAnalytics;
+
+class AudioPowerUsage {
+public:
+    explicit AudioPowerUsage(AudioAnalytics *audioAnalytics);
+    ~AudioPowerUsage();
+
+    void checkTrackRecord(const std::shared_ptr<const mediametrics::Item>& item, bool isTrack);
+    void checkMode(const std::shared_ptr<const mediametrics::Item>& item);
+    void checkVoiceVolume(const std::shared_ptr<const mediametrics::Item>& item);
+    void checkCreatePatch(const std::shared_ptr<const mediametrics::Item>& item);
+    void clear();
+
+    /**
+     * Returns a pair consisting of the dump string, and the number of lines in the string.
+     *
+     * The number of lines in the returned pair is used as an optimization
+     * for subsequent line limiting.
+     *
+     * \param lines the maximum number of lines in the string returned.
+     */
+    std::pair<std::string, int32_t> dump(int32_t lines = INT32_MAX) const;
+
+    // align with message AudioUsageDataReported in frameworks/base/cmds/statsd/src/atoms.proto
+    enum AudioType {
+        UNKNOWN_TYPE = 0,
+        VOICE_CALL_TYPE = 1,            // voice call
+        VOIP_CALL_TYPE = 2,             // voip call, including uplink and downlink
+        MEDIA_TYPE = 3,                 // music and system sound
+        RINGTONE_NOTIFICATION_TYPE = 4, // ringtone and notification
+        ALARM_TYPE = 5,                 // alarm type
+        // record type
+        CAMCORDER_TYPE = 6,             // camcorder
+        RECORD_TYPE = 7,                // other recording
+    };
+
+    enum AudioDevice {
+        OUTPUT_EARPIECE         = 0x1,
+        OUTPUT_SPEAKER          = 0x2,
+        OUTPUT_WIRED_HEADSET    = 0x4,
+        OUTPUT_USB_HEADSET      = 0x8,
+        OUTPUT_BLUETOOTH_SCO    = 0x10,
+        OUTPUT_BLUETOOTH_A2DP   = 0x20,
+        OUTPUT_SPEAKER_SAFE     = 0x40,
+
+        INPUT_DEVICE_BIT        = 0x40000000,
+        INPUT_BUILTIN_MIC       = INPUT_DEVICE_BIT | 0x1, // non-negative positive int32.
+        INPUT_BUILTIN_BACK_MIC  = INPUT_DEVICE_BIT | 0x2,
+        INPUT_WIRED_HEADSET_MIC = INPUT_DEVICE_BIT | 0x4,
+        INPUT_USB_HEADSET_MIC   = INPUT_DEVICE_BIT | 0x8,
+        INPUT_BLUETOOTH_SCO     = INPUT_DEVICE_BIT | 0x10,
+    };
+
+    static bool typeFromString(const std::string& type_string, int32_t& type);
+    static bool deviceFromString(const std::string& device_string, int32_t& device);
+    static int32_t deviceFromStringPairs(const std::string& device_strings);
+private:
+    bool saveAsItem_l(int32_t device, int64_t duration, int32_t type, double average_vol)
+         REQUIRES(mLock);
+    static void sendItem(const std::shared_ptr<const mediametrics::Item>& item);
+    void collect();
+
+    AudioAnalytics * const mAudioAnalytics;
+    const bool mDisabled;
+    const int32_t mIntervalHours;
+
+    mutable std::mutex mLock;
+    std::deque<std::shared_ptr<mediametrics::Item>> mItems GUARDED_BY(mLock);
+
+    double mVoiceVolume GUARDED_BY(mLock) = 0.;
+    double mDeviceVolume GUARDED_BY(mLock) = 0.;
+    int64_t mStartCallNs GUARDED_BY(mLock) = 0; // advisory only
+    int64_t mVolumeTimeNs GUARDED_BY(mLock) = 0;
+    int64_t mDeviceTimeNs GUARDED_BY(mLock) = 0;
+    int32_t mPrimaryDevice GUARDED_BY(mLock) = OUTPUT_SPEAKER;
+    std::string mMode GUARDED_BY(mLock) {"AUDIO_MODE_NORMAL"};
+};
+
+} // namespace android::mediametrics
diff --git a/services/mediametrics/AudioTypes.cpp b/services/mediametrics/AudioTypes.cpp
new file mode 100644
index 0000000..aa44447
--- /dev/null
+++ b/services/mediametrics/AudioTypes.cpp
@@ -0,0 +1,433 @@
+/*
+ * 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 {
+
+const std::unordered_map<std::string, int32_t>& getAudioCallerNameMap() {
+    // 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{
+        {"unknown",       0},           // callerName not set
+        {"aaudio",        1},           // Native AAudio
+        {"java",          2},           // Java API layer
+        {"media",         3},           // libmedia (mediaplayer)
+        {"opensles",      4},           // Open SLES
+        {"rtp",           5},           // RTP communication
+        {"soundpool",     6},           // SoundPool
+        {"tonegenerator", 7},           // dial tones
+        // R values above.
+    };
+    return map;
+}
+
+// A map in case we need to return a flag for input devices.
+// This is 64 bits (and hence not the same as audio_device_t) because we need extra
+// bits to represent new devices.
+// NOT USED FOR R.  We do not use int64 flags.
+// This can be out of date for now, as it is unused even for string validation
+// (instead TypeConverter<InputDeviceTraits> is used).
+const 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;
+}
+
+// A map in case we need to return a flag for output devices.
+// This is 64 bits (and hence not the same as audio_device_t) because we need extra
+// bits to represent new devices.
+// NOT USED FOR R.  We do not use int64 flags.
+// This can be out of date for now, as it is unused even for string validation
+// (instead TypeConverter<OutputDeviceTraits> is used).
+const 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;
+}
+
+const std::unordered_map<std::string, int32_t>& getAudioThreadTypeMap() {
+    // 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;
+}
+
+const std::unordered_map<std::string, int32_t>& getAudioTrackTraitsMap() {
+    // DO NOT MODIFY VALUES (OK to add new ones).
+    static std::unordered_map<std::string, int32_t> map{
+        {"static",        (1 << 0)},  // A static track
+        // 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 <typename M>
+std::string validateStringFromMap(const std::string &str, const M& map)
+{
+    if (str.empty()) return {};
+
+    const auto result = stringutils::split(str, "|");
+    std::stringstream ss;
+    for (const auto &s : result) {
+        if (map.count(s) > 0) {
+            if (ss.tellp() > 0) ss << "|";
+            ss << s;
+        }
+    }
+    return ss.str();
+}
+
+template <typename M>
+typename M::mapped_type flagsFromMap(const std::string &str, const M& map)
+{
+    if (str.empty()) return {};
+
+    const auto result = stringutils::split(str, "|");
+    typename M::mapped_type value{};
+    for (const auto &s : result) {
+        auto it = map.find(s);
+        if (it == map.end()) continue;
+        value |= it->second;
+    }
+    return value;
+}
+
+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 "";
+    }
+    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 "";
+    }
+    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 "";
+    }
+    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 "";
+    }
+    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 "";
+    }
+    return usage.c_str() + sizeof("AUDIO_USAGE");
+}
+
+template <>
+int64_t lookup<INPUT_DEVICE>(const std::string &inputDevice)
+{
+    // NOT USED FOR R.
+    // Returns a set of bits, each one representing a device in inputDevice.
+    // This is a 64 bit integer, not the same as audio_device_t.
+    return flagsFromMap(inputDevice, getAudioDeviceInMap());
+}
+
+template <>
+std::string lookup<INPUT_DEVICE>(const std::string &inputDevice)
+{
+    return stringFromFlags<InputDeviceTraits>(inputDevice, sizeof("AUDIO_DEVICE_IN"));
+}
+
+template <>
+int64_t lookup<OUTPUT_DEVICE>(const std::string &outputDevice)
+{
+    // NOT USED FOR R.
+    // Returns a set of bits, each one representing a device in outputDevice.
+    // This is a 64 bit integer, not the same as audio_device_t.
+    return flagsFromMap(outputDevice, getAudioDeviceOutMap());
+}
+
+template <>
+std::string lookup<OUTPUT_DEVICE>(const std::string &outputDevice)
+{
+    return stringFromFlags<OutputDeviceTraits>(outputDevice, sizeof("AUDIO_DEVICE_OUT"));
+}
+
+template <>
+int32_t lookup<CALLER_NAME>(const std::string &callerName)
+{
+    auto& map = getAudioCallerNameMap();
+    auto it = map.find(callerName);
+    if (it == map.end()) {
+        return 0;      // return unknown
+    }
+    return it->second;
+}
+
+template <>
+std::string lookup<CALLER_NAME>(const std::string &callerName)
+{
+    auto& map = getAudioCallerNameMap();
+    auto it = map.find(callerName);
+    if (it == map.end()) {
+        return "";
+    }
+    return callerName;
+}
+
+template <>
+int32_t lookup<THREAD_TYPE>(const std::string &threadType)
+{
+    auto& map = getAudioThreadTypeMap();
+    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 = getAudioThreadTypeMap();
+    auto it = map.find(threadType);
+    if (it == map.end()) {
+        return "";
+    }
+    return threadType;
+}
+
+bool isInputThreadType(const std::string &threadType)
+{
+    return threadType == "RECORD" || threadType == "MMAP_CAPTURE";
+}
+
+template <>
+std::string lookup<TRACK_TRAITS>(const std::string &traits)
+{
+    return validateStringFromMap(traits, getAudioTrackTraitsMap());
+}
+
+template <>
+int32_t lookup<TRACK_TRAITS>(const std::string &traits)
+{
+    return flagsFromMap(traits, getAudioTrackTraitsMap());
+}
+
+} // namespace android::mediametrics::types
diff --git a/services/mediametrics/AudioTypes.h b/services/mediametrics/AudioTypes.h
new file mode 100644
index 0000000..e1deeb1
--- /dev/null
+++ b/services/mediametrics/AudioTypes.h
@@ -0,0 +1,77 @@
+/*
+ * 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.
+// In R we do not use the integer codes, but rather we can use these maps
+// to validate correct strings.
+const std::unordered_map<std::string, int32_t>& getAudioCallerNameMap();
+const std::unordered_map<std::string, int64_t>& getAudioDeviceInMap();
+const std::unordered_map<std::string, int64_t>& getAudioDeviceOutMap();
+const std::unordered_map<std::string, int32_t>& getAudioThreadTypeMap();
+const std::unordered_map<std::string, int32_t>& getAudioTrackTraitsMap();
+
+// Enumeration for the device connection results.
+enum DeviceConnectionResult : int32_t {
+    DEVICE_CONNECTION_RESULT_SUCCESS = 0,              // Audio delivered
+    DEVICE_CONNECTION_RESULT_UNKNOWN = 1,              // Success is unknown.
+    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,
+    TRACK_TRAITS,
+    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..48e766e 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);
     }
@@ -236,7 +185,7 @@
     }
 
     if (!isTrusted || item->getTimestamp() == 0) {
-        // Westworld logs two times for events: ElapsedRealTimeNs (BOOTTIME) and
+        // Statsd logs two times for events: ElapsedRealTimeNs (BOOTTIME) and
         // WallClockTimeNs (REALTIME), but currently logs REALTIME to cloud.
         //
         // For consistency and correlation with other logging mechanisms
@@ -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..792b7f0 100644
--- a/services/mediametrics/MediaMetricsService.h
+++ b/services/mediametrics/MediaMetricsService.h
@@ -65,7 +65,7 @@
     static nsecs_t roundTime(nsecs_t timeNs);
 
     /**
-     * Returns true if we should use uid for package name when uploading to WestWorld.
+     * Returns true if we should use uid for package name when uploading to statsd.
      */
     static bool useUidForPackage(const std::string& package, const std::string& installer);
 
@@ -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..7a8bbee
--- /dev/null
+++ b/services/mediametrics/StringUtils.h
@@ -0,0 +1,71 @@
+/*
+ * 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 {
+
+/**
+ * fieldPrint is a helper method that logs to a stringstream a sequence of
+ * field names (in a fixed size array) together with a variable number of arg parameters.
+ *
+ * stringstream << field[0] << ":" << arg0 << " ";
+ * stringstream << field[1] << ":" << arg1 << " ";
+ * ...
+ * stringstream << field[N-1] << ":" << arg{N-1} << " ";
+ *
+ * The number of fields must exactly match the (variable) arguments.
+ *
+ * Example:
+ *
+ * const char * const fields[] = { "integer" };
+ * std::stringstream ss;
+ * fieldPrint(ss, fields, int(10));
+ */
+template <size_t N, typename... Targs>
+void fieldPrint(std::stringstream& ss, const char * const (& fields)[N], Targs... args) {
+    static_assert(N == sizeof...(args));          // guarantee #fields == #args
+    auto fptr = fields;                           // get a pointer to the base of fields array
+    ((ss << *fptr++ << ":" << args << " "), ...); // (fold expression), send to stringstream.
+}
+
+/**
+ * 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..ce579b3 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;
@@ -217,10 +220,10 @@
 
     using History = std::map<std::string /* key */, std::shared_ptr<KeyHistory>>;
 
-    static inline constexpr size_t kTimeSequenceMaxElements = 100;
-    static inline constexpr size_t kKeyMaxProperties = 100;
-    static inline constexpr size_t kKeyLowWaterMark = 500;
-    static inline constexpr size_t kKeyHighWaterMark = 1000;
+    static inline constexpr size_t kTimeSequenceMaxElements = 50;
+    static inline constexpr size_t kKeyMaxProperties = 50;
+    static inline constexpr size_t kKeyLowWaterMark = 400;
+    static inline constexpr size_t kKeyHighWaterMark = 500;
 
     // Estimated max data space usage is 3KB * kKeyHighWaterMark.
 
@@ -252,6 +255,7 @@
         {
             std::lock_guard lock2(other.mLock);
             mHistory = other.mHistory;
+            mGarbageCollectionCount = other.mGarbageCollectionCount.load();
         }
 
         // Now that we safely have our own shared pointers, let's dup them
@@ -417,6 +421,7 @@
     void clear() {
         std::lock_guard lock(mLock);
         mHistory.clear();
+        mGarbageCollectionCount = 0;
     }
 
     /**
@@ -442,7 +447,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;
@@ -450,6 +455,10 @@
         return { ss.str(), lines - ll };
     }
 
+    size_t getGarbageCollectionCount() const {
+        return mGarbageCollectionCount;
+    }
+
 private:
 
     // Obtains the lock for a KeyHistory.
@@ -493,8 +502,6 @@
         // TODO: something better than this for garbage collection.
         if (mHistory.size() < mKeyHighWaterMark) return false;
 
-        ALOGD("%s: garbage collection", __func__);
-
         // erase everything explicitly expired.
         std::multimap<int64_t, std::string> accessList;
         // use a stale vector with precise type to avoid type erasure overhead in garbage
@@ -531,12 +538,16 @@
         ALOGD("%s(%zu, %zu): key size:%zu",
                 __func__, mKeyLowWaterMark, mKeyHighWaterMark,
                 mHistory.size());
+
+        ++mGarbageCollectionCount;
         return true;
     }
 
     const size_t mKeyLowWaterMark = kKeyLowWaterMark;
     const size_t mKeyHighWaterMark = kKeyHighWaterMark;
 
+    std::atomic<size_t> mGarbageCollectionCount{};
+
     /**
      * Locking Strategy
      *
diff --git a/services/mediametrics/TransactionLog.h b/services/mediametrics/TransactionLog.h
index 8a22826..0ca4639 100644
--- a/services/mediametrics/TransactionLog.h
+++ b/services/mediametrics/TransactionLog.h
@@ -43,9 +43,9 @@
     // Transaction Log between the Low Water Mark and the High Water Mark.
 
     // low water mark
-    static inline constexpr size_t kLogItemsLowWater = 5000;
+    static inline constexpr size_t kLogItemsLowWater = 1700;
     // high water mark
-    static inline constexpr size_t kLogItemsHighWater = 10000;
+    static inline constexpr size_t kLogItemsHighWater = 2000;
 
     // Estimated max data usage is 1KB * kLogItemsHighWater.
 
@@ -79,6 +79,7 @@
         std::lock_guard lock2(other.mLock);
         mLog = other.mLog;
         mItemMap = other.mItemMap;
+        mGarbageCollectionCount = other.mGarbageCollectionCount.load();
 
         return *this;
     }
@@ -181,6 +182,11 @@
         std::lock_guard lock(mLock);
         mLog.clear();
         mItemMap.clear();
+        mGarbageCollectionCount = 0;
+    }
+
+    size_t getGarbageCollectionCount() const {
+        return mGarbageCollectionCount;
     }
 
 private:
@@ -216,8 +222,6 @@
     bool gc(std::vector<std::any>& garbage) REQUIRES(mLock) {
         if (mLog.size() < mHighWaterMark) return false;
 
-        ALOGD("%s: garbage collection", __func__);
-
         auto eraseEnd = mLog.begin();
         size_t toRemove = mLog.size() - mLowWaterMark;
         // remove at least those elements.
@@ -265,6 +269,7 @@
         ALOGD("%s(%zu, %zu): log size:%zu item map size:%zu, item map items:%zu",
                 __func__, mLowWaterMark, mHighWaterMark,
                 mLog.size(), mItemMap.size(), itemMapCount);
+        ++mGarbageCollectionCount;
         return true;
     }
 
@@ -287,6 +292,8 @@
     const size_t mLowWaterMark = kLogItemsLowWater;
     const size_t mHighWaterMark = kLogItemsHighWater;
 
+    std::atomic<size_t> mGarbageCollectionCount{};
+
     mutable std::mutex mLock;
 
     MapTimeItem mLog GUARDED_BY(mLock);
diff --git a/services/mediametrics/cleaner.cpp b/services/mediametrics/cleaner.cpp
new file mode 100644
index 0000000..e746842
--- /dev/null
+++ b/services/mediametrics/cleaner.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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 "MetricsCleaner"
+#include <utils/Log.h>
+
+#include "cleaner.h"
+
+namespace android::mediametrics {
+
+// place time into buckets at 0,1,2,4,8,16,32 seconds and then at minute boundaries.
+// time is rounded up to the next boundary.
+//
+int64_t bucket_time_minutes(int64_t in_millis) {
+
+    const int64_t SEC_TO_MS = 1000;
+    const int64_t MIN_TO_MS = (60 * SEC_TO_MS);
+
+    if (in_millis <= 0) {
+        return 0;
+    }
+    if (in_millis <= 32 * SEC_TO_MS) {
+        for (int sec = 1; sec <= 32; sec *= 2) {
+            if (in_millis <= sec * SEC_TO_MS) {
+                return sec * SEC_TO_MS;
+            }
+        }
+    }
+    /* up to next 1 minute boundary */
+    int64_t minutes = (in_millis + MIN_TO_MS - 1) / MIN_TO_MS;
+    in_millis = minutes * MIN_TO_MS;
+    return in_millis;
+}
+
+} // namespace android::mediametrics
diff --git a/services/mediametrics/cleaner.h b/services/mediametrics/cleaner.h
new file mode 100644
index 0000000..72e24f9
--- /dev/null
+++ b/services/mediametrics/cleaner.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+#ifndef MEDIAMETRICS_CLEANER_H
+#define MEDIAMETRICS_CLEANER_H
+
+namespace android::mediametrics {
+
+// break time into buckets at 1,2,4,8,16,32 seconds
+// and then at minute boundaries
+//
+extern int64_t bucket_time_minutes(int64_t incomingMs);
+
+} // namespace android::mediametrics
+
+#endif  // MEDIAMETRICS_CLEANER_H
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..ec9354f 100644
--- a/services/mediametrics/statsd_codec.cpp
+++ b/services/mediametrics/statsd_codec.cpp
@@ -31,6 +31,7 @@
 
 #include <statslog.h>
 
+#include "cleaner.h"
 #include "MediaMetricsService.h"
 #include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
 #include "iface_statsd.h"
@@ -39,7 +40,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 +169,23 @@
     }
     // android.media.mediacodec.latency.hist    NOT EMITTED
 
+    // 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)) {
+        lifetimeMs = mediametrics::bucket_time_minutes(lifetimeMs);
+        metrics_proto.set_lifetime_millis(lifetimeMs);
+    }
+
     std::string serialized;
     if (!metrics_proto.SerializeToString(&serialized)) {
         ALOGE("Failed to serialize codec metrics");
@@ -188,4 +206,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/Android.bp b/services/mediametrics/tests/Android.bp
index bdeda30..c2e0759 100644
--- a/services/mediametrics/tests/Android.bp
+++ b/services/mediametrics/tests/Android.bp
@@ -21,6 +21,10 @@
         "libutils",
     ],
 
+    header_libs: [
+        "libaudioutils_headers",
+    ],
+
     srcs: [
         "mediametrics_tests.cpp",
     ],
diff --git a/services/mediametrics/tests/mediametrics_tests.cpp b/services/mediametrics/tests/mediametrics_tests.cpp
index b465ecd..478355b 100644
--- a/services/mediametrics/tests/mediametrics_tests.cpp
+++ b/services/mediametrics/tests/mediametrics_tests.cpp
@@ -20,9 +20,14 @@
 #include "MediaMetricsService.h"
 
 #include <stdio.h>
+#include <unordered_set>
 
 #include <gtest/gtest.h>
 #include <media/MediaMetricsItem.h>
+#include <system/audio.h>
+
+#include "AudioTypes.h"
+#include "StringUtils.h"
 
 using namespace android;
 
@@ -35,6 +40,15 @@
     return count;
 }
 
+template <typename M>
+ssize_t countDuplicates(const M& map) {
+    std::unordered_set<typename M::mapped_type> s;
+    for (const auto &m : map) {
+        s.emplace(m.second);
+    }
+    return map.size() - s.size();
+}
+
 TEST(mediametrics_tests, startsWith) {
   std::string s("test");
   ASSERT_EQ(true, android::mediametrics::startsWith(s, "te"));
@@ -803,7 +817,7 @@
 
   // TODO: Verify contents of AudioAnalytics.
   // Currently there is no getter API in AudioAnalytics besides dump.
-  ASSERT_EQ(9, audioAnalytics.dump(1000).second /* lines */);
+  ASSERT_EQ(11, audioAnalytics.dump(1000).second /* lines */);
 
   ASSERT_EQ(NO_ERROR, audioAnalytics.submit(item, true /* isTrusted */));
   // untrusted entities can add to an existing key
@@ -839,7 +853,7 @@
 
   // TODO: Verify contents of AudioAnalytics.
   // Currently there is no getter API in AudioAnalytics besides dump.
-  ASSERT_EQ(9, audioAnalytics.dump(1000).second /* lines */);
+  ASSERT_EQ(11, audioAnalytics.dump(1000).second /* lines */);
 
   ASSERT_EQ(NO_ERROR, audioAnalytics.submit(item, true /* isTrusted */));
   // untrusted entities can add to an existing key
@@ -884,12 +898,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 +911,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 +919,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);
@@ -925,6 +939,132 @@
     ASSERT_EQ((size_t)1, timedAction.size());
 }
 
+// Ensure we don't introduce unexpected duplicates into our maps.
+TEST(mediametrics_tests, audio_types_tables) {
+    using namespace android::mediametrics::types;
+
+    ASSERT_EQ(0, countDuplicates(getAudioCallerNameMap()));
+    ASSERT_EQ(2, countDuplicates(getAudioDeviceInMap()));  // has dups
+    ASSERT_EQ(1, countDuplicates(getAudioDeviceOutMap())); // has dups
+    ASSERT_EQ(0, countDuplicates(getAudioThreadTypeMap()));
+    ASSERT_EQ(0, countDuplicates(getAudioTrackTraitsMap()));
+}
+
+// Check our string validation (before logging to statsd).
+// This variant checks the logged, possibly shortened string.
+TEST(mediametrics_tests, audio_types_string) {
+    using namespace android::mediametrics::types;
+
+    ASSERT_EQ("java", (lookup<CALLER_NAME, std::string>)("java"));
+    ASSERT_EQ("", (lookup<CALLER_NAME, std::string>)("random"));
+
+    ASSERT_EQ("SPEECH", (lookup<CONTENT_TYPE, std::string>)("AUDIO_CONTENT_TYPE_SPEECH"));
+    ASSERT_EQ("", (lookup<CONTENT_TYPE, std::string>)("random"));
+
+    ASSERT_EQ("FLAC", (lookup<ENCODING, std::string>)("AUDIO_FORMAT_FLAC"));
+    ASSERT_EQ("", (lookup<ENCODING, std::string>)("random"));
+
+    ASSERT_EQ("USB_DEVICE", (lookup<INPUT_DEVICE, std::string>)("AUDIO_DEVICE_IN_USB_DEVICE"));
+    ASSERT_EQ("BUILTIN_MIC|WIRED_HEADSET", (lookup<INPUT_DEVICE, std::string>)(
+            "AUDIO_DEVICE_IN_BUILTIN_MIC|AUDIO_DEVICE_IN_WIRED_HEADSET"));
+    ASSERT_EQ("", (lookup<INPUT_DEVICE, std::string>)("random"));
+
+    ASSERT_EQ("RAW", (lookup<INPUT_FLAG, std::string>)("AUDIO_INPUT_FLAG_RAW"));
+    ASSERT_EQ("HW_AV_SYNC|VOIP_TX", (lookup<INPUT_FLAG, std::string>)(
+            "AUDIO_INPUT_FLAG_HW_AV_SYNC|AUDIO_INPUT_FLAG_VOIP_TX"));
+    ASSERT_EQ("", (lookup<INPUT_FLAG, std::string>)("random"));
+
+    ASSERT_EQ("BLUETOOTH_SCO_CARKIT",
+            (lookup<OUTPUT_DEVICE, std::string>)("AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT"));
+    ASSERT_EQ("SPEAKER|HDMI", (lookup<OUTPUT_DEVICE, std::string>)(
+            "AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_HDMI"));
+    ASSERT_EQ("", (lookup<OUTPUT_DEVICE, std::string>)("random"));
+
+    ASSERT_EQ("PRIMARY", (lookup<OUTPUT_FLAG, std::string>)("AUDIO_OUTPUT_FLAG_PRIMARY"));
+    ASSERT_EQ("DEEP_BUFFER|NON_BLOCKING", (lookup<OUTPUT_FLAG, std::string>)(
+            "AUDIO_OUTPUT_FLAG_DEEP_BUFFER|AUDIO_OUTPUT_FLAG_NON_BLOCKING"));
+    ASSERT_EQ("", (lookup<OUTPUT_FLAG, std::string>)("random"));
+
+    ASSERT_EQ("MIC", (lookup<SOURCE_TYPE, std::string>)("AUDIO_SOURCE_MIC"));
+    ASSERT_EQ("", (lookup<SOURCE_TYPE, std::string>)("random"));
+
+    ASSERT_EQ("TTS", (lookup<STREAM_TYPE, std::string>)("AUDIO_STREAM_TTS"));
+    ASSERT_EQ("", (lookup<STREAM_TYPE, std::string>)("random"));
+
+    ASSERT_EQ("DIRECT", (lookup<THREAD_TYPE, std::string>)("DIRECT"));
+    ASSERT_EQ("", (lookup<THREAD_TYPE, std::string>)("random"));
+
+    ASSERT_EQ("static", (lookup<TRACK_TRAITS, std::string>)("static"));
+    ASSERT_EQ("", (lookup<TRACK_TRAITS, std::string>)("random"));
+
+    ASSERT_EQ("VOICE_COMMUNICATION",
+            (lookup<USAGE, std::string>)("AUDIO_USAGE_VOICE_COMMUNICATION"));
+    ASSERT_EQ("", (lookup<USAGE, std::string>)("random"));
+}
+
+// Check our string validation (before logging to statsd).
+// This variant checks integral value logging.
+TEST(mediametrics_tests, audio_types_integer) {
+    using namespace android::mediametrics::types;
+
+    ASSERT_EQ(2, (lookup<CALLER_NAME, int32_t>)("java"));
+    ASSERT_EQ(0, (lookup<CALLER_NAME, int32_t>)("random")); // 0 == unknown
+
+    ASSERT_EQ((int32_t)AUDIO_CONTENT_TYPE_SPEECH,
+            (lookup<CONTENT_TYPE, int32_t>)("AUDIO_CONTENT_TYPE_SPEECH"));
+    ASSERT_EQ((int32_t)AUDIO_CONTENT_TYPE_UNKNOWN, (lookup<CONTENT_TYPE, int32_t>)("random"));
+
+    ASSERT_EQ((int32_t)AUDIO_FORMAT_FLAC, (lookup<ENCODING, int32_t>)("AUDIO_FORMAT_FLAC"));
+    ASSERT_EQ((int32_t)AUDIO_FORMAT_INVALID, (lookup<ENCODING, int32_t>)("random"));
+
+    ASSERT_EQ(getAudioDeviceInMap().at("AUDIO_DEVICE_IN_USB_DEVICE"),
+            (lookup<INPUT_DEVICE, int64_t>)("AUDIO_DEVICE_IN_USB_DEVICE"));
+    ASSERT_EQ(getAudioDeviceInMap().at("AUDIO_DEVICE_IN_BUILTIN_MIC")
+            | getAudioDeviceInMap().at("AUDIO_DEVICE_IN_WIRED_HEADSET"),
+            (lookup<INPUT_DEVICE, int64_t>)(
+            "AUDIO_DEVICE_IN_BUILTIN_MIC|AUDIO_DEVICE_IN_WIRED_HEADSET"));
+    ASSERT_EQ(0, (lookup<INPUT_DEVICE, int64_t>)("random"));
+
+    ASSERT_EQ((int32_t)AUDIO_INPUT_FLAG_RAW,
+            (lookup<INPUT_FLAG, int32_t>)("AUDIO_INPUT_FLAG_RAW"));
+    ASSERT_EQ((int32_t)AUDIO_INPUT_FLAG_HW_AV_SYNC
+            | (int32_t)AUDIO_INPUT_FLAG_VOIP_TX,
+            (lookup<INPUT_FLAG, int32_t>)(
+            "AUDIO_INPUT_FLAG_HW_AV_SYNC|AUDIO_INPUT_FLAG_VOIP_TX"));
+    ASSERT_EQ(0, (lookup<INPUT_FLAG, int32_t>)("random"));
+
+    ASSERT_EQ(getAudioDeviceOutMap().at("AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT"),
+            (lookup<OUTPUT_DEVICE, int64_t>)("AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT"));
+    ASSERT_EQ(getAudioDeviceOutMap().at("AUDIO_DEVICE_OUT_SPEAKER")
+            | getAudioDeviceOutMap().at("AUDIO_DEVICE_OUT_HDMI"),
+            (lookup<OUTPUT_DEVICE, int64_t>)(
+            "AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_HDMI"));
+    ASSERT_EQ(0, (lookup<OUTPUT_DEVICE, int64_t>)("random"));
+
+    ASSERT_EQ((int32_t)AUDIO_OUTPUT_FLAG_PRIMARY,
+            (lookup<OUTPUT_FLAG, int32_t>)("AUDIO_OUTPUT_FLAG_PRIMARY"));
+    ASSERT_EQ((int32_t)AUDIO_OUTPUT_FLAG_DEEP_BUFFER | (int32_t)AUDIO_OUTPUT_FLAG_NON_BLOCKING,
+            (lookup<OUTPUT_FLAG, int32_t>)(
+            "AUDIO_OUTPUT_FLAG_DEEP_BUFFER|AUDIO_OUTPUT_FLAG_NON_BLOCKING"));
+    ASSERT_EQ(0, (lookup<OUTPUT_FLAG, int32_t>)("random"));
+
+    ASSERT_EQ((int32_t)AUDIO_SOURCE_MIC, (lookup<SOURCE_TYPE, int32_t>)("AUDIO_SOURCE_MIC"));
+    ASSERT_EQ((int32_t)AUDIO_SOURCE_DEFAULT, (lookup<SOURCE_TYPE, int32_t>)("random"));
+
+    ASSERT_EQ((int32_t)AUDIO_STREAM_TTS, (lookup<STREAM_TYPE, int32_t>)("AUDIO_STREAM_TTS"));
+    ASSERT_EQ((int32_t)AUDIO_STREAM_DEFAULT, (lookup<STREAM_TYPE, int32_t>)("random"));
+
+    ASSERT_EQ(1, (lookup<THREAD_TYPE, int32_t>)("DIRECT"));
+    ASSERT_EQ(-1, (lookup<THREAD_TYPE, int32_t>)("random"));
+
+    ASSERT_EQ(getAudioTrackTraitsMap().at("static"), (lookup<TRACK_TRAITS, int32_t>)("static"));
+    ASSERT_EQ(0, (lookup<TRACK_TRAITS, int32_t>)("random"));
+
+    ASSERT_EQ((int32_t)AUDIO_USAGE_VOICE_COMMUNICATION,
+            (lookup<USAGE, int32_t>)("AUDIO_USAGE_VOICE_COMMUNICATION"));
+    ASSERT_EQ((int32_t)AUDIO_USAGE_UNKNOWN, (lookup<USAGE, int32_t>)("random"));
+}
+
 #if 0
 // Stress test code for garbage collection, you need to enable AID_SHELL as trusted to run
 // in MediaMetricsService.cpp.
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index ff45c87..3d36f8e 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -57,9 +57,11 @@
         ALOGW("ResourceManagerService is dead as well.");
         return;
     }
-    service->removeResource(mPid, mClientId, false);
 
     service->overridePid(mPid, -1);
+    // thiz is freed in the call below, so it must be last call referring thiz
+    service->removeResource(mPid, mClientId, false);
+
 }
 
 template <typename T>
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/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index 9dab770..0843e0b 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -23,10 +23,10 @@
 #include <map>
 #include <mutex>
 #include <sstream>
+#include <thread>
 #include <utils/Singleton.h>
 #include <vector>
 
-
 #include "AAudioEndpointManager.h"
 #include "AAudioServiceEndpoint.h"
 
@@ -36,7 +36,6 @@
 #include "AAudioServiceEndpointPlay.h"
 #include "AAudioServiceEndpointMMAP.h"
 
-
 #define AAUDIO_BUFFER_CAPACITY_MIN    4 * 512
 #define AAUDIO_SAMPLE_RATE_DEFAULT    48000
 
@@ -48,7 +47,6 @@
 using namespace android;  // TODO just import names needed
 using namespace aaudio;   // TODO just import names needed
 
-
 AAudioServiceEndpointMMAP::AAudioServiceEndpointMMAP(AAudioService &audioService)
         : mMmapStream(nullptr)
         , mAAudioService(audioService) {}
@@ -318,9 +316,8 @@
     return 0; // TODO
 }
 
-// This is called by AudioFlinger when it wants to destroy a stream.
-void AAudioServiceEndpointMMAP::onTearDown(audio_port_handle_t portHandle) {
-    ALOGD("%s(portHandle = %d) called", __func__, portHandle);
+// This is called by onTearDown() in a separate thread to avoid deadlocks.
+void AAudioServiceEndpointMMAP::handleTearDownAsync(audio_port_handle_t portHandle) {
     // Are we tearing down the EXCLUSIVE MMAP stream?
     if (isStreamRegistered(portHandle)) {
         ALOGD("%s(%d) tearing down this entire MMAP endpoint", __func__, portHandle);
@@ -333,6 +330,13 @@
     }
 };
 
+// This is called by AudioFlinger when it wants to destroy a stream.
+void AAudioServiceEndpointMMAP::onTearDown(audio_port_handle_t portHandle) {
+    ALOGD("%s(portHandle = %d) called", __func__, portHandle);
+    std::thread asyncTask(&AAudioServiceEndpointMMAP::handleTearDownAsync, this, portHandle);
+    asyncTask.detach();
+}
+
 void AAudioServiceEndpointMMAP::onVolumeChanged(audio_channel_mask_t channels,
                                               android::Vector<float> values) {
     // TODO Do we really need a different volume for each channel?
@@ -345,12 +349,20 @@
     }
 };
 
-void AAudioServiceEndpointMMAP::onRoutingChanged(audio_port_handle_t deviceId) {
+void AAudioServiceEndpointMMAP::onRoutingChanged(audio_port_handle_t portHandle) {
+    const int32_t deviceId = static_cast<int32_t>(portHandle);
     ALOGD("%s() called with dev %d, old = %d", __func__, deviceId, getDeviceId());
-    if (getDeviceId() != AUDIO_PORT_HANDLE_NONE  && getDeviceId() != deviceId) {
-        disconnectRegisteredStreams();
+    if (getDeviceId() != deviceId) {
+        if (getDeviceId() != AUDIO_PORT_HANDLE_NONE) {
+            std::thread asyncTask([this, deviceId]() {
+                disconnectRegisteredStreams();
+                setDeviceId(deviceId);
+            });
+            asyncTask.detach();
+        } else {
+            setDeviceId(deviceId);
+        }
     }
-    setDeviceId(deviceId);
 };
 
 /**
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.h b/services/oboeservice/AAudioServiceEndpointMMAP.h
index f599066..3d10861 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.h
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.h
@@ -68,13 +68,15 @@
 
     aaudio_result_t getTimestamp(int64_t *positionFrames, int64_t *timeNanos) override;
 
+    void handleTearDownAsync(audio_port_handle_t portHandle);
+
     // -------------- Callback functions for MmapStreamCallback ---------------------
-    void onTearDown(audio_port_handle_t handle) override;
+    void onTearDown(audio_port_handle_t portHandle) override;
 
     void onVolumeChanged(audio_channel_mask_t channels,
                          android::Vector<float> values) override;
 
-    void onRoutingChanged(audio_port_handle_t deviceId) override;
+    void onRoutingChanged(audio_port_handle_t portHandle) override;
     // ------------------------------------------------------------------------------
 
     aaudio_result_t getDownDataDescription(AudioEndpointParcelable &parcelable);
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 dba9fb9..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,19 +225,28 @@
  * 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)
-            .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(AudioClock::getNanoseconds() - beginNs))
+            .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(AudioClock::getNanoseconds() - beginNs))
             .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState()))
             .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result)
             .record(); });
 
     if (isRunning()) {
-        return AAUDIO_OK;
+        return result;
     }
 
     setFlowing(false);
@@ -254,21 +269,26 @@
     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)
             .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_PAUSE)
-            .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(AudioClock::getNanoseconds() - beginNs))
+            .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(AudioClock::getNanoseconds() - beginNs))
             .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState()))
             .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result)
             .record(); });
@@ -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,16 +321,21 @@
 }
 
 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)
             .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_STOP)
-            .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(AudioClock::getNanoseconds() - beginNs))
+            .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(AudioClock::getNanoseconds() - beginNs))
             .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState()))
             .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result)
             .record(); });
@@ -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,16 +380,17 @@
 }
 
 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)
             .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_FLUSH)
-            .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(AudioClock::getNanoseconds() - beginNs))
+            .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(AudioClock::getNanoseconds() - beginNs))
             .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState()))
             .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result)
             .record(); });
@@ -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
@@ -0,0 +1,200 @@
+#!/bin/bash
+#set -x
+
+# used for projects where some files are mainline, some are not
+# we get a list of the files/directories out of the project's root.
+#
+# invocation   $0  ${repo_root} ${preupload_files}
+#
+# Example PREUPLOAD.cfg:
+#
+# [Hook Scripts]
+# mainline_hook = ${REPO_ROOT}/frameworks/av/tools/mainline_hook_partial.sh ${REPO_ROOT} ${PREUPLOAD_FILES}
+#
+# MainlineFiles.cfg syntax:
+#
+# 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/"
+#
+
+## tunables:
+##
+DEV_BRANCH=rvc-dev
+filelist_file=MainlineFiles.cfg
+
+###
+
+REPO_ROOT=$1; shift
+# the rest of the command line is the file list
+PREUPLOAD_FILES="$*"
+
+RED=$(tput setaf 1)
+NORMAL=$(tput sgr0)
+
+## get the active branch:
+## * <localbranch> <shainfo> [goog/master] Fix to handle missing checks on error returned
+## strip this down to "master"
+##
+current=`git branch -vv | grep -P "^\*[^\[]+\[goog/"|sed -e 's/^.*\[//' | sed -e 's/:.*$//'| sed -e 's/^goog\///'`
+if [ "${current}" = "" ] ; then
+        current=unknown
+fi
+
+## figure out whether which files are for mainline and which are not
+if [ "${PREUPLOAD_FILES}" = "" ] ; then
+    # empty files? what's up there, i suppose we'll let that go
+    exit 0
+fi
+
+## get the list of files out of the project's root
+## figure out which way I'm going .. 
+## use list of files to scan PREUPLOAD_FILES
+## use PREUPLOAD_FILES to scan the list of good/bad from the project root
+##
+## remember to do an exclude, so I can say
+## include/these/files/
+## EXCLUDE include/these/files/nested/
+##
+## and it should all be prefix based stuff...
+
+if [ ! -f ${REPO_ROOT}/${REPO_PATH}/${filelist_file} ] ; then
+    echo "Poorly Configured project, missing ${filelist_file} in root of project"
+    exit 1
+fi
+
+# is 1st arg a prefix of 2nd arg
+beginswith() { case $2 in "$1"*) true;; *) false;; esac; }
+
+exclusions=""
+inclusions=""
+while read p1 p2
+do
+    # ignore comment lines in the file
+    # ignore empty lines in the file
+    if beginswith "#" "${p1}" ; then
+        # ignore this line
+        true
+    elif [ -z "${p1}" ] ; then
+        # ignore blanks
+        true
+    elif [ ${p1} = "EXCLUDE" ] ; then
+        # add to the exclusion list
+        if [ ! -z ${p2} ] ; then
+            exlusions="${exclusions} ${p2}"
+        fi
+    elif [ ${p1} = "INCLUDE" ] ; then
+        # add to the inclusion list
+        if [ ! -z ${p2} ] ; then
+            inclusions="${inclusions} ${p2}"
+        fi
+    elif [ ! -z ${p1} ] ; then
+        inclusions="${inclusions} ${p1}"
+    fi
+done < ${REPO_ROOT}/${REPO_PATH}/${filelist_file}
+
+# so we can play with array syntax
+#INCLUSIONS=( ${inclusions} )
+#EXCLUSIONS=( ${exclusions} )
+
+mainline_yes=""
+mainline_no=""
+
+# is it part of the list of mainline files/directories?
+for path in ${PREUPLOAD_FILES} ; do
+    #echo is ${path} a mainline file...
+    for aprefix in ${inclusions} .. ; do
+        #echo compare against ${aprefix} ...
+        if [ "${aprefix}" = ".." ] ; then
+            mainline_no="${mainline_no} ${path}"
+        elif beginswith ${aprefix} ${path} ; then
+            mainline_yes="${mainline_yes} ${path}"
+            break       # on to next uploaded file
+        fi
+    done
+done
+
+# TODO: audit the yes list to see if some should be moved to the no list
+
+# 3 situations
+# -- everything is on mainline (mainline_yes non-empty, other empty)
+# -- some is mainline, some is not (files_* both non-empty)
+# -- none is mainline   (mainline_yes empty, other non_empty
+# -- both empty only happens if PREUPLOAD_FILES is empty, covered above
+
+if [ -z "${mainline_yes}" ] ; then
+    # no mainline files, everything else is non-mainline, let it go 
+    exit 0
+fi
+
+result=0
+if [ ! -z "${mainline_no}" ] ; then
+        # mixed bag, suggest (not insist) that developer split them.
+        result=1
+        cat - <<EOF
+This CL contains files contains both mainline and non-mainline files.  Consider separating
+them into separate CLs. It may also be appropriate to update the list of mainline
+files in ${RED}${REPO_ROOT}/${filelist_file}${NORMAL}.
+
+EOF
+        echo "===== Mainline files ====="
+        echo -e ${RED}
+        echo ${mainline_yes} | sed -e 's/ /
/g'
+        echo -e ${NORMAL}
+
+        echo "===== Non-Mainline files ====="
+        echo -e ${RED}
+        echo ${mainline_no} | sed -e 's/ /
/g'
+        echo -e ${NORMAL}
+
+fi
+
+if [ "${current}" != "${DEV_BRANCH}" ] ; then
+    # Change is not in the desired mainline dev branch
+    result=1
+
+    #echo -e "${RED}"
+    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
+    #echo -e "${NORMAL}"
+
+fi
+
+## since stdout is buffered in a way that complicates the below, we're just going
+## to tell the user what they can do to get around this check instead of asking them
+## as part of this run of the command.
+
+if [ ${result} != 0 ] ; then
+    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
+fi
+exit ${result}
+
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
+