Merge "Set min_sdk_version (for non-mainline files)" into rvc-dev
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/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/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/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 16457ac..8763c62 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
      */
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/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/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/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
index ecc56f5..823e11b 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) {
@@ -816,14 +854,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/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 1e4560c..a3fff35 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");
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 3773528..2fc1781 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -713,7 +713,7 @@
         return;
     } else {
         Mutexed<Output>::Locked output(mOutput);
-        if (output->buffers->numClientBuffers() >= output->numSlots) {
+        if (!output->buffers || output->buffers->numClientBuffers() >= output->numSlots) {
             return;
         }
     }
@@ -851,31 +851,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;
@@ -1396,6 +1404,30 @@
     }
 }
 
+void CCodecBufferChannel::reset() {
+    stop();
+    {
+        Mutexed<Input>::Locked input(mInput);
+        input->buffers.reset(new DummyInputBuffers(""));
+    }
+    {
+        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();
+    }
+}
+
+
 void CCodecBufferChannel::flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) {
     ALOGV("[%s] flush", mName);
     {
@@ -1426,7 +1458,9 @@
     }
     {
         Mutexed<Output>::Locked output(mOutput);
-        output->buffers->flush(flushedWork);
+        if (output->buffers) {
+            output->buffers->flush(flushedWork);
+        }
     }
     mReorderStash.lock()->flush();
     mPipelineWatcher.lock()->flush();
@@ -1464,20 +1498,25 @@
         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);
+        if (!output->buffers) {
+            return false;
+        }
+        if (outputFormat != nullptr) {
+            ALOGD("[%s] onWorkDone: output format changed to %s",
+                    mName, outputFormat->debugString().c_str());
+            output->buffers->setFormat(outputFormat);
 
-        AString mediaType;
-        if (outputFormat->findString(KEY_MIME, &mediaType)
-                && mediaType == MIMETYPE_AUDIO_RAW) {
-            int32_t channelCount;
-            int32_t sampleRate;
-            if (outputFormat->findInt32(KEY_CHANNEL_COUNT, &channelCount)
-                    && outputFormat->findInt32(KEY_SAMPLE_RATE, &sampleRate)) {
-                output->buffers->updateSkipCutBuffer(sampleRate, channelCount);
+            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);
+                }
             }
         }
     }
@@ -1601,6 +1640,9 @@
                         size_t numInputSlots = mInput.lock()->numSlots;
                         {
                             Mutexed<Output>::Locked output(mOutput);
+                            if (!output->buffers) {
+                                return false;
+                            }
                             output->outputDelay = outputDelay.value;
                             numOutputSlots = outputDelay.value + kSmoothnessFactor;
                             if (output->numSlots < numOutputSlots) {
@@ -1690,7 +1732,7 @@
 
     if (initData != nullptr) {
         Mutexed<Output>::Locked output(mOutput);
-        if (output->buffers->registerCsd(initData, &index, &outBuffer) == OK) {
+        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,8 +1747,8 @@
         }
     }
 
-    if (!buffer && !flags && outputFormat == nullptr) {
-        ALOGV("[%s] onWorkDone: nothing to report from the work (%lld)",
+    if (!buffer && !flags) {
+        ALOGV("[%s] onWorkDone: Not reporting output buffer (%lld)",
               mName, work->input.ordinal.frameIndex.peekull());
         return true;
     }
@@ -1753,6 +1795,9 @@
         }
 
         Mutexed<Output>::Locked output(mOutput);
+        if (!output->buffers) {
+            return;
+        }
         status_t err = output->buffers->registerBuffer(entry.buffer, &index, &outBuffer);
         if (err != OK) {
             bool outputBuffersChanged = false;
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index 0263211..f6e7024 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);
 
     /**
diff --git a/media/codec2/sfplugin/CCodecBuffers.h b/media/codec2/sfplugin/CCodecBuffers.h
index 6244acd..eec79f1 100644
--- a/media/codec2/sfplugin/CCodecBuffers.h
+++ b/media/codec2/sfplugin/CCodecBuffers.h
@@ -416,7 +416,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
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/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/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index 0bbceef..d4c421a 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(); });
 
@@ -742,6 +742,8 @@
     void *iMemPointer;
     audio_track_cblk_t* cblk;
     status_t status;
+    std::string flagsAsString;
+    std::string originalFlagsAsString;
 
     if (audioFlinger == 0) {
         ALOGE("%s(%d): Could not get audioflinger", __func__, mPortId);
@@ -920,13 +922,15 @@
     mDeathNotifier = new DeathNotifier(this);
     IInterface::asBinder(mAudioRecord)->linkToDeath(mDeathNotifier, this);
 
+    InputFlagConverter::toString(mFlags, flagsAsString);
+    InputFlagConverter::toString(mOrigFlags, originalFlagsAsString);
     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, flagsAsString.c_str())
+        .set(AMEDIAMETRICS_PROP_ORIGINALFLAGS, originalFlagsAsString.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 +1391,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..011b0fa 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -650,7 +650,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(mState))
             .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)status)
             .record(); });
@@ -783,7 +783,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(mState))
             .record();
         logBufferSizeUnderruns();
@@ -845,7 +845,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 +886,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(); });
 
@@ -1715,15 +1715,19 @@
     // 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.
+
+    std::string flagsAsString;
+    OutputFlagConverter::toString(mFlags, flagsAsString);
+    std::string originalFlagsAsString;
+    OutputFlagConverter::toString(mOrigFlags, originalFlagsAsString);
     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, flagsAsString.c_str())
+        .set(AMEDIAMETRICS_PROP_ORIGINALFLAGS, originalFlagsAsString.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 +2444,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/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/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..b916a78 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"
@@ -146,6 +151,7 @@
 #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
@@ -170,7 +176,9 @@
 #define AMEDIAMETRICS_PROP_EVENT_VALUE_PAUSE      "pause"  // AudioTrack
 #define AMEDIAMETRICS_PROP_EVENT_VALUE_READPARAMETERS "readParameters" // Thread
 #define AMEDIAMETRICS_PROP_EVENT_VALUE_RESTORE    "restore"
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETMODE    "setMode" // AudioFlinger
 #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/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/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 11f2f38..7fb5455 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>
@@ -2641,6 +2643,7 @@
                     }
 
                     mResourceManagerProxy->removeClient();
+                    mReleaseSurface.reset();
 
                     if (mReplyID != nullptr) {
                         (new AMessage)->postReply(mReplyID);
@@ -3430,19 +3433,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 (mCrypto) {
+            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 +3498,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);
@@ -3589,6 +3620,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));
     }
@@ -3687,7 +3719,7 @@
     }
 
     status_t err = OK;
-    if (hasCryptoOrDescrambler()) {
+    if (hasCryptoOrDescrambler() && !c2Buffer && !memory) {
         AString *errorDetailMsg;
         CHECK(msg->findPointer("errorDetailMsg", (void **)&errorDetailMsg));
 
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/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/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/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/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/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/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..4a4899f 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();
     }
@@ -2088,6 +2092,12 @@
         outputFlags = (audio_output_flags_t)(outputFlags | AUDIO_OUTPUT_FLAG_FAST);
     }
 
+    // Set DIRECT flag if current thread is DirectOutputThread. This can happen when the playback is
+    // rerouted to direct output thread by dynamic audio policy.
+    if (mType == DIRECT) {
+        *flags = (audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_DIRECT);
+    }
+
     // Check if requested flags are compatible with output stream flags
     if ((*flags & outputFlags) != *flags) {
         ALOGW("createTrack_l(): mismatch between requested flags (%08x) and output flags (%08x)",
@@ -4260,7 +4270,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 +4819,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 +4848,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;
@@ -8428,6 +8444,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 +8585,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 +8699,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),
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/TrackMetrics.h b/services/audioflinger/TrackMetrics.h
index 399c788..12bd341 100644
--- a/services/audioflinger/TrackMetrics.h
+++ b/services/audioflinger/TrackMetrics.h
@@ -67,16 +67,21 @@
         mIntervalStartTimeNs = systemTime();
     }
 
-    void logConstructor(pid_t creatorPid, uid_t creatorUid) const {
+    void logConstructor(pid_t creatorPid, uid_t creatorUid,
+            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();
+                    AMEDIAMETRICS_PROP_PREFIX_SERVER AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR);
+        // 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..73a40d3 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -602,7 +602,7 @@
     }
 
     // Once this item is logged by the server, the client can add properties.
-    mTrackMetrics.logConstructor(creatorPid, uid);
+    mTrackMetrics.logConstructor(creatorPid, uid, streamType);
 }
 
 AudioFlinger::PlaybackThread::Track::~Track()
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/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/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..2a9a4c4 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");
@@ -4635,6 +4638,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);
                 }
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..1a0881f 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,29 @@
             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());
+            mInputYuvBuffers.erase(it);
+            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 +660,67 @@
             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);
+            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 +729,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 +771,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 +785,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 +795,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 +808,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 +821,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 +837,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 +859,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 +869,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 +879,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 +889,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 +905,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 +919,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 +957,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 +1018,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 +1028,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 +1084,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 +1102,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 +1119,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 +1162,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 +1178,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 +1213,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 +1227,8 @@
         sp<ANativeWindow> outputANW = mOutputSurface;
         outputANW->cancelBuffer(mOutputSurface.get(), inputFrame->anb, /*fence*/ -1);
         inputFrame->anb = nullptr;
+
+        mDequeuedOutputBufferCnt--;
     }
 }
 
@@ -1161,8 +1238,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 +1256,8 @@
         auto firstPendingFrame = mPendingInputFrames.begin();
         if (firstPendingFrame != mPendingInputFrames.end()) {
             updateCodecQualityLocked(firstPendingFrame->second.quality);
+        } else {
+            markTrackerIdle();
         }
     }
 }
@@ -1397,20 +1476,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 +1649,7 @@
 }
 
 bool HeicCompositeStream::threadLoop() {
-    int64_t currentTs = INT64_MAX;
+    int64_t frameNumber = -1;
     bool newInputAvailable = false;
 
     {
@@ -1600,19 +1665,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 +1698,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 +1712,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 +1744,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 +1765,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.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 19ecf4b..c059f55 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
      */
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/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 7916ddb..e54a99b 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -814,6 +814,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/mediametrics/Android.bp b/services/mediametrics/Android.bp
index 8e5bc79..c87fbd9 100644
--- a/services/mediametrics/Android.bp
+++ b/services/mediametrics/Android.bp
@@ -36,6 +36,7 @@
 
     srcs: [
         "AudioAnalytics.cpp",
+        "AudioPowerUsage.cpp",
         "iface_statsd.cpp",
         "MediaMetricsService.cpp",
         "statsd_audiopolicy.cpp",
@@ -55,6 +56,7 @@
 
     shared_libs: [
         "libbinder",
+        "libcutils",
         "liblog",
         "libmediametrics",
         "libmediautils",
diff --git a/services/mediametrics/AudioAnalytics.cpp b/services/mediametrics/AudioAnalytics.cpp
index fe3a34d..6138d32 100644
--- a/services/mediametrics/AudioAnalytics.cpp
+++ b/services/mediametrics/AudioAnalytics.cpp
@@ -49,47 +49,13 @@
                 // Perhaps report this.
             }));
 
-    // 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 +64,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 +73,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 +86,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 +99,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 +194,12 @@
         ss << s;
         ll -= l;
     }
+
+    if (ll > 0 && prefix == nullptr) {
+        auto [s, l] = mAudioPowerUsage.dump(ll);
+        ss << s;
+        ll -= l;
+    }
     return { ss.str(), lines - ll };
 }
 
@@ -199,11 +226,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,6 +243,9 @@
     int32_t frameCount = 0;
     mAudioAnalytics.mAnalyticsState->timeMachine().get(
             key, AMEDIAMETRICS_PROP_FRAMECOUNT, &frameCount);
+    std::string inputDevices;
+    mAudioAnalytics.mAnalyticsState->timeMachine().get(
+            key, AMEDIAMETRICS_PROP_INPUTDEVICES, &inputDevices);
     int32_t intervalCount = 0;
     mAudioAnalytics.mAnalyticsState->timeMachine().get(
             key, AMEDIAMETRICS_PROP_INTERVALCOUNT, &intervalCount);
@@ -224,29 +255,128 @@
     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.
+    // Strings allow us to mix input and output devices together.
+    // TODO: review if we want to separate them.
+    std::stringstream ss;
+    for (const auto& devicePairs : { outputDevices, inputDevices }) {
+        const auto devaddrvec = MediaMetricsService::getDeviceAddressPairs(devicePairs);
+        for (const auto& [device, addr] : devaddrvec) {
+            if (ss.tellp() > 0) ss << "|";  // delimit devices with '|'.
+            ss << device;
+        }
+    }
+    std::string devices = ss.str();
 
     // Get connected device name if from bluetooth.
     bool isBluetooth = false;
-    std::string name;
+    std::string deviceNames; // we only have one device name at this time.
     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, &deviceNames);
+        // We don't check if deviceName is sanitized.
+        // TODO: remove reserved chars such as '|' and replace with a char like '_'.
     }
 
-    // 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.
-    }
+    switch (itemType) {
+    case RECORD: {
+        std::string callerName;
+        mAudioAnalytics.mAnalyticsState->timeMachine().get(
+                key, AMEDIAMETRICS_PROP_CALLERNAME, &callerName);
 
-    if (isTrack) {
+        std::string packageName;
+        int64_t versionCode = 0;
+        int32_t uid = -1;
+        mAudioAnalytics.mAnalyticsState->timeMachine().get(
+                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);
+
+        ALOGD("(key=%s) id:%s endAudioIntervalGroup devices:%s deviceNames:%s "
+                 "deviceTimeNs:%lld encoding:%s frameCount:%d intervalCount:%d "
+                 "sampleRate:%d "
+                 "packageName:%s "
+                 "selectedDeviceId:%d "
+                 "callerName:%s source:%s",
+                key.c_str(), id.c_str(), devices.c_str(), deviceNames.c_str(),
+                (long long)deviceTimeNs, encoding.c_str(), frameCount, intervalCount,
+                sampleRate,
+                packageName.c_str(), selectedDeviceId,
+                callerName.c_str(), source.c_str());
+
+#ifdef STATSD
+        if (mAudioAnalytics.mDeliverStatistics) {
+            (void)android::util::stats_write(
+                    android::util::MEDIAMETRICS_AUDIORECORDDEVICEUSAGE_REPORTED
+                    /* timestamp, */
+                    /* mediaApexVersion, */
+                    , devices.c_str()
+                    , deviceNames.c_str()
+                    , deviceTimeNs
+                    , encoding.c_str()
+                    , frameCount
+                    , intervalCount
+                    , sampleRate
+                    , flags.c_str()
+
+                    , packageName.c_str()
+                    , selectedDeviceId
+                    , callerName.c_str()
+                    , source.c_str()
+                    );
+        }
+#endif
+    } 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);
+        ALOGD("(key=%s) id:%s endAudioIntervalGroup devices:%s deviceNames:%s "
+                 "deviceTimeNs:%lld encoding:%s frameCount:%d intervalCount:%d "
+                 "sampleRate:%d underrun:%d "
+                 "flags:%s type:%s",
+                key.c_str(), id.c_str(), devices.c_str(), deviceNames.c_str(),
+                (long long)deviceTimeNs, encoding.c_str(), frameCount, intervalCount,
+                sampleRate, underrun,
+                flags.c_str(), type.c_str());
+#ifdef STATSD
+        if (mAudioAnalytics.mDeliverStatistics) {
+            (void)android::util::stats_write(
+                android::util::MEDIAMETRICS_AUDIOTHREADDEVICEUSAGE_REPORTED
+                /* timestamp, */
+                /* mediaApexVersion, */
+                , devices.c_str()
+                , deviceNames.c_str()
+                , deviceTimeNs
+                , encoding.c_str()
+                , frameCount
+                , intervalCount
+                , sampleRate
+                , flags.c_str()
+
+                , underrun
+                , type.c_str()
+            );
+        }
+#endif
+    } break;
+    case TRACK: {
         std::string callerName;
         mAudioAnalytics.mAnalyticsState->timeMachine().get(
                 key, AMEDIAMETRICS_PROP_CALLERNAME, &callerName);
@@ -280,38 +410,44 @@
         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);
+        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 "
+        ALOGD("(key=%s) id:%s endAudioIntervalGroup devices:%s deviceNames:%s "
                  "deviceTimeNs:%lld encoding:%s frameCount:%d intervalCount:%d "
                  "sampleRate:%d underrun:%d "
                  "callerName:%s contentType:%s "
-                 "deviceLatencyMs:%lf deviceStartupMs:%lf deviceVolume:%lf"
+                 "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(),
+                 "selectedDeviceId:%d streamType:%s usage:%s",
+                key.c_str(), id.c_str(), devices.c_str(), deviceNames.c_str(),
                 (long long)deviceTimeNs, encoding.c_str(), frameCount, intervalCount,
                 sampleRate, underrun,
                 callerName.c_str(), contentType.c_str(),
                 deviceLatencyMs, deviceStartupMs, deviceVolume,
                 packageName.c_str(), playbackPitch, playbackSpeed,
-                selectedDeviceId, usage.c_str());
+                selectedDeviceId, streamType.c_str(), 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()
+                    , devices.c_str()
+                    , deviceNames.c_str()
                     , deviceTimeNs
                     , encoding.c_str()
                     , frameCount
                     , intervalCount
                     , sampleRate
+                    , flags.c_str()
                     , underrun
 
                     , packageName.c_str()
@@ -319,43 +455,14 @@
                     , (float)deviceStartupMs
                     , (float)deviceVolume
                     , selectedDeviceId
+                    , streamType.c_str()
                     , usage.c_str()
                     , contentType.c_str()
                     , callerName.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 +475,123 @@
 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;
+        int64_t timeDiff = item->getTimestamp();
+        if (mA2dpConnectionRequestNs == 0) {
+            ALOGD("%s: A2DP create patch didn't see a connection request", __func__);
+            timeDiff -= mA2dpConnectionServiceNs;
+        } else {
+            timeDiff -= mA2dpConnectionRequestNs;
+        }
         ALOGD("(key=%s) A2DP device connection time: %lld", key.c_str(), (long long)timeDiff);
-        mA2dpTimeConnectedNs = 0; // reset counter.
-        ++mA2dpConnectedSuccesses;
+        mA2dpConnectionRequestNs = 0;
+        mA2dpConnectionServiceNs = 0;
+        ++mA2dpConnectionSuccesses;
+
+#ifdef STATSD
+        if (mAudioAnalytics.mDeliverStatistics) {
+            (void)android::util::stats_write(
+                    android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED
+                    /* timestamp, */
+                    /* mediaApexVersion, */
+                    , "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP"
+                    , android::util::MEDIAMETRICS_AUDIO_DEVICE_CONNECTION_REPORTED__RESULT__SUCCESS
+                    , /* connection_time_ms */ timeDiff * 1e-6 /* NS to MS */
+                    , /* connection_count */ 1
+                    );
+        }
+#endif
     }
 }
 
+// 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::lock_guard l(mLock);
+        mA2dpConnectionRequestNs = atNs;
+        ++mA2dpConnectionRequests;
+    }
+    ALOGD("(key=%s) a2dp connection request atNs:%lld",
+            key.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).
+    if (mA2dpConnectionServiceNs == 0) {
+        ALOGD("A2DP device connection service cancels");
+        ++mA2dpConnectionJavaServiceCancels;  // service did not connect to A2DP
 
-    // An expiration may occur because there is no audio playing.
+#ifdef STATSD
+        if (mAudioAnalytics.mDeliverStatistics) {
+            (void)android::util::stats_write(
+                    android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED
+                    /* timestamp, */
+                    /* mediaApexVersion, */
+                    , "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP"
+                    , android::util::MEDIAMETRICS_AUDIO_DEVICE_CONNECTION_REPORTED__RESULT__JAVA_SERVICE_CANCEL
+                    , /* connection_time_ms */ 0.f
+                    , /* connection_count */ 1
+                    );
+        }
+#endif
+        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;
+    ALOGD("A2DP device connection expired, state unknown");
+    mA2dpConnectionRequestNs = 0;
+    mA2dpConnectionServiceNs = 0;
+    ++mA2dpConnectionUnknowns;  // connection result unknown
+#ifdef STATSD
+    if (mAudioAnalytics.mDeliverStatistics) {
+        (void)android::util::stats_write(
+                android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED
+                /* timestamp, */
+                /* mediaApexVersion, */
+                , "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP"
+                , android::util::MEDIAMETRICS_AUDIO_DEVICE_CONNECTION_REPORTED__RESULT__UNKNOWN
+                , /* connection_time_ms */ 0.f
+                , /* connection_count */ 1
+                );
+    }
+#endif
 }
 
 } // namespace android
diff --git a/services/mediametrics/AudioAnalytics.h b/services/mediametrics/AudioAnalytics.h
index eb9c228..9089d6f 100644
--- a/services/mediametrics/AudioAnalytics.h
+++ b/services/mediametrics/AudioAnalytics.h
@@ -19,6 +19,7 @@
 #include <android-base/thread_annotations.h>
 #include "AnalyticsActions.h"
 #include "AnalyticsState.h"
+#include "AudioPowerUsage.h"
 #include "TimedAction.h"
 #include "Wrap.h"
 
@@ -26,6 +27,9 @@
 
 class AudioAnalytics
 {
+    // AudioAnalytics action / state helper classes
+    friend AudioPowerUsage;
+
 public:
     AudioAnalytics();
     ~AudioAnalytics();
@@ -72,10 +76,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.
      *
@@ -110,12 +128,19 @@
     // 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 +162,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 +173,19 @@
         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;
+        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..b1615bd
--- /dev/null
+++ b/services/mediametrics/AudioPowerUsage.cpp
@@ -0,0 +1,394 @@
+/*
+ * 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 <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 westworld, 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 = MediaMetricsService::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 (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 * (mVolumeTimeNs - mDeviceTimeNs) +
+                    mVoiceVolume * (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 * (mVolumeTimeNs - mDeviceTimeNs) +
+                    mVoiceVolume * (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 * (mVolumeTimeNs - mDeviceTimeNs) +
+                    mVoiceVolume * (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
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/tests/mediametrics_tests.cpp b/services/mediametrics/tests/mediametrics_tests.cpp
index b465ecd..f7988f1 100644
--- a/services/mediametrics/tests/mediametrics_tests.cpp
+++ b/services/mediametrics/tests/mediametrics_tests.cpp
@@ -803,7 +803,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(10, audioAnalytics.dump(1000).second /* lines */);
 
   ASSERT_EQ(NO_ERROR, audioAnalytics.submit(item, true /* isTrusted */));
   // untrusted entities can add to an existing key
@@ -839,7 +839,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(10, audioAnalytics.dump(1000).second /* lines */);
 
   ASSERT_EQ(NO_ERROR, audioAnalytics.submit(item, true /* isTrusted */));
   // untrusted entities can add to an existing key
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/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index dba9fb9..531bfa1 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -225,7 +225,7 @@
     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(); });
@@ -268,7 +268,7 @@
     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(); });
@@ -310,7 +310,7 @@
     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(); });
@@ -364,7 +364,7 @@
     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(); });