Merge "Update doc for aaudio data callback."
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index b6f8552..0d156a5 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -2198,6 +2198,10 @@
      * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#turnOnTorchWithStrengthLevel">CameraManager#turnOnTorchWithStrengthLevel</a>.
      * If this value is equal to 1, flashlight brightness control is not supported.
      * The value for this key will be null for devices with no flash unit.</p>
+     * <p>The maximum value is guaranteed to be safe to use for an indefinite duration in
+     * terms of device flashlight lifespan, but may be too bright for comfort for many
+     * use cases. Use the default torch brightness value to avoid problems with an
+     * over-bright flashlight.</p>
      */
     ACAMERA_FLASH_INFO_STRENGTH_MAXIMUM_LEVEL =                 // int32
             ACAMERA_FLASH_INFO_START + 2,
diff --git a/media/codec2/components/avc/C2SoftAvcEnc.cpp b/media/codec2/components/avc/C2SoftAvcEnc.cpp
index 8b46d3f..28cceca 100644
--- a/media/codec2/components/avc/C2SoftAvcEnc.cpp
+++ b/media/codec2/components/avc/C2SoftAvcEnc.cpp
@@ -392,9 +392,9 @@
     static C2R PictureQuantizationSetter(bool mayBlock,
                                          C2P<C2StreamPictureQuantizationTuning::output> &me) {
         (void)mayBlock;
-        (void)me;
 
-        // TODO: refactor with same algorithm in the SetQp()
+        // these are the ones we're going to set, so want them to default
+        // to the DEFAULT values for the codec
         int32_t iMin = DEFAULT_I_QP_MIN, pMin = DEFAULT_P_QP_MIN, bMin = DEFAULT_B_QP_MIN;
         int32_t iMax = DEFAULT_I_QP_MAX, pMax = DEFAULT_P_QP_MAX, bMax = DEFAULT_B_QP_MAX;
 
@@ -419,13 +419,14 @@
         ALOGV("PictureQuantizationSetter(entry): i %d-%d p %d-%d b %d-%d",
               iMin, iMax, pMin, pMax, bMin, bMax);
 
-        // ensure we have legal values
-        iMax = std::clamp(iMax, CODEC_QP_MIN, CODEC_QP_MAX);
-        iMin = std::clamp(iMin, CODEC_QP_MIN, CODEC_QP_MAX);
-        pMax = std::clamp(pMax, CODEC_QP_MIN, CODEC_QP_MAX);
-        pMin = std::clamp(pMin, CODEC_QP_MIN, CODEC_QP_MAX);
-        bMax = std::clamp(bMax, CODEC_QP_MIN, CODEC_QP_MAX);
-        bMin = std::clamp(bMin, CODEC_QP_MIN, CODEC_QP_MAX);
+        // min is clamped to [AVC_QP_MIN, max] to avoid error
+        // cases where layer.min > layer.max
+        iMax = std::clamp(iMax, AVC_QP_MIN, AVC_QP_MAX);
+        iMin = std::clamp(iMin, AVC_QP_MIN, iMax);
+        pMax = std::clamp(pMax, AVC_QP_MIN, AVC_QP_MAX);
+        pMin = std::clamp(pMin, AVC_QP_MIN, pMax);
+        bMax = std::clamp(bMax, AVC_QP_MIN, AVC_QP_MAX);
+        bMin = std::clamp(bMin, AVC_QP_MIN, bMax);
 
         // put them back into the structure
         for (size_t i = 0; i < me.v.flexCount(); ++i) {
@@ -819,7 +820,8 @@
     s_qp_ip.e_cmd = IVE_CMD_VIDEO_CTL;
     s_qp_ip.e_sub_cmd = IVE_CMD_CTL_SET_QP;
 
-    // TODO: refactor with same algorithm in the PictureQuantizationSetter()
+    // we resolved out-of-bound and unspecified values in PictureQuantizationSetter()
+    // so we can start with defaults that are overridden as needed.
     int32_t iMin = DEFAULT_I_QP_MIN, pMin = DEFAULT_P_QP_MIN, bMin = DEFAULT_B_QP_MIN;
     int32_t iMax = DEFAULT_I_QP_MAX, pMax = DEFAULT_P_QP_MAX, bMax = DEFAULT_B_QP_MAX;
 
diff --git a/media/codec2/components/avc/C2SoftAvcEnc.h b/media/codec2/components/avc/C2SoftAvcEnc.h
index 293867d..cde6604 100644
--- a/media/codec2/components/avc/C2SoftAvcEnc.h
+++ b/media/codec2/components/avc/C2SoftAvcEnc.h
@@ -103,8 +103,8 @@
 /** limits as specified by h264
  *  (QP_MIN==4 is actually a limitation of this SW codec, not the H.264 standard)
  **/
-#define CODEC_QP_MIN                4
-#define CODEC_QP_MAX                51
+#define AVC_QP_MIN                4
+#define AVC_QP_MAX                51
 
 
 #define MIN(a, b) ((a) < (b))? (a) : (b)
diff --git a/media/codec2/fuzzer/C2Fuzzer.cpp b/media/codec2/fuzzer/C2Fuzzer.cpp
index e469d8b..d6793e0 100644
--- a/media/codec2/fuzzer/C2Fuzzer.cpp
+++ b/media/codec2/fuzzer/C2Fuzzer.cpp
@@ -246,7 +246,8 @@
   bufferSource->parse();
   c2_status_t status = C2_OK;
   size_t numFrames = 0;
-  while (!bufferSource->isEos()) {
+  int32_t iterationCount = 0;
+  while (!bufferSource->isEos() && ++iterationCount <= kMaxIterations) {
     uint8_t* frame = nullptr;
     size_t frameSize = 0;
     FrameData frameData = bufferSource->getFrame();
diff --git a/media/codec2/fuzzer/C2Fuzzer.h b/media/codec2/fuzzer/C2Fuzzer.h
index da76885..4e3e3ea 100644
--- a/media/codec2/fuzzer/C2Fuzzer.h
+++ b/media/codec2/fuzzer/C2Fuzzer.h
@@ -37,6 +37,7 @@
 #define C2FUZZER_ALIGN(_sz, _align) (((_sz) + ((_align)-1)) & ~((_align)-1))
 
 constexpr std::chrono::milliseconds kC2FuzzerTimeOut = 5000ms;
+constexpr int32_t kMaxIterations = 100;
 constexpr int32_t kNumberOfC2WorkItems = 8;
 constexpr uint32_t kWidthOfVideo = 3840;
 constexpr uint32_t kHeightOfVideo = 2160;
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
index bd7ec0d..327717b 100644
--- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
@@ -372,9 +372,8 @@
             ULock l(queueLock);
             flushedIndices.emplace_back(frameID);
         }
-        char* data = (char*)malloc(bytesCount);
-        ASSERT_NE(data, nullptr);
-        eleStream.read(data, bytesCount);
+        std::vector<char> eleData(bytesCount);
+        eleStream.read(eleData.data(), bytesCount);
         // if we have reached at the end of input stream, signal eos
         if (eleStream.gcount() < bytesCount) {
             bytesCount = eleStream.gcount();
@@ -396,12 +395,11 @@
         ASSERT_EQ(0u, view.offset());
         ASSERT_EQ((size_t)bytesCount, view.size());
 
-        memcpy(view.base(), data, bytesCount);
+        memcpy(view.base(), eleData.data(), bytesCount);
         work->input.buffers.clear();
         work->input.buffers.emplace_back(new LinearBuffer(block));
         work->worklets.clear();
         work->worklets.emplace_back(new C2Worklet);
-        free(data);
 
         std::list<std::unique_ptr<C2Work>> items;
         items.push_back(std::move(work));
diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
index 67873fa..117d9ca 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
@@ -214,10 +214,8 @@
             calc_md5_cksum(uPlane, uvStride, cropWidth / 2, cropHeight / 2, au1_u_chksum);
             calc_md5_cksum(vPlane, uvStride, cropWidth / 2, cropHeight / 2, au1_v_chksum);
         } else if (bitDepth == 8 && layoutType == C2PlanarLayout::TYPE_YUV && colInc == 2) {
-            uint8_t* cbPlane = (uint8_t*)malloc(cropWidth * cropHeight / 4);
-            uint8_t* crPlane = (uint8_t*)malloc(cropWidth * cropHeight / 4);
-            ASSERT_NE(cbPlane, nullptr);
-            ASSERT_NE(crPlane, nullptr);
+            std::vector<uint8_t> cbPlane(cropWidth * cropHeight / 4);
+            std::vector<uint8_t> crPlane(cropWidth * cropHeight / 4);
             size_t count = 0;
             for (size_t k = 0; k < (cropHeight / 2); k++) {
                 for (size_t l = 0; l < (cropWidth); l = l + 2) {
@@ -227,10 +225,10 @@
                 }
             }
             calc_md5_cksum(yPlane, yStride, cropWidth, cropHeight, au1_y_chksum);
-            calc_md5_cksum(cbPlane, cropWidth / 2, cropWidth / 2, cropHeight / 2, au1_u_chksum);
-            calc_md5_cksum(crPlane, cropWidth / 2, cropWidth / 2, cropHeight / 2, au1_v_chksum);
-            free(cbPlane);
-            free(crPlane);
+            calc_md5_cksum(cbPlane.data(), cropWidth / 2, cropWidth / 2, cropHeight / 2,
+                           au1_u_chksum);
+            calc_md5_cksum(crPlane.data(), cropWidth / 2, cropWidth / 2, cropHeight / 2,
+                           au1_v_chksum);
         } else {
             mMd5Enable = false;
             ALOGV("Disabling MD5 chksm flag");
diff --git a/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp b/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp
index 6c8ce3c..c77eb22 100644
--- a/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp
+++ b/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp
@@ -666,6 +666,7 @@
                         grallocHandle, GraphicBuffer::CLONE_HANDLE,
                         width, height, format, 1, usage, stride);
 
+                native_handle_delete(grallocHandle);
                 std::shared_ptr<C2GraphicBlock> dstBlock;
                 C2BlockPool::local_id_t poolId = mIntf->getPoolId();
                 std::shared_ptr<C2BlockPool> pool;
@@ -684,6 +685,7 @@
                         grallocHandle, GraphicBuffer::CLONE_HANDLE,
                         width, height, format, 1, usage, stride);
 
+                native_handle_delete(grallocHandle);
                 Rect sourceCrop(0, 0, width, height);
 
                 renderengine::DisplaySettings clientCompositionDisplay;
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 296d7ed..43bdb21 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -1899,6 +1899,7 @@
         comp = state->comp;
     }
     status_t err = comp->stop();
+    mChannel->stopUseOutputSurface();
     if (err != C2_OK) {
         // TODO: convert err into status_t
         mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
@@ -1972,6 +1973,7 @@
     }
 
     mChannel->reset();
+    mChannel->stopUseOutputSurface();
     // thiz holds strong ref to this while the thread is running.
     sp<CCodec> thiz(this);
     std::thread([thiz, sendCallback] { thiz->release(sendCallback); }).detach();
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 5ecb130..4bf8dce 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -960,9 +960,9 @@
                     hdrDynamicInfo->m.data + hdrDynamicInfo->flexCount());
         }
         qbi.setHdrMetadata(hdr);
-
-        SetHdrMetadataToGralloc4Handle(hdrStaticInfo, hdrDynamicInfo, block.handle());
     }
+    SetMetadataToGralloc4Handle(dataSpace, hdrStaticInfo, hdrDynamicInfo, block.handle());
+
     // we don't have dirty regions
     qbi.setSurfaceDamage(Region::INVALID_REGION);
     android::IGraphicBufferProducer::QueueBufferOutput qbo;
@@ -1578,6 +1578,17 @@
     mFirstValidFrameIndex = mFrameIndex.load(std::memory_order_relaxed);
 }
 
+void CCodecBufferChannel::stopUseOutputSurface() {
+    if (mOutputSurface.lock()->surface) {
+        C2BlockPool::local_id_t outputPoolId;
+        {
+            Mutexed<BlockPools>::Locked pools(mBlockPools);
+            outputPoolId = pools->outputPoolId;
+        }
+        if (mComponent) mComponent->stopUsingOutputSurface(outputPoolId);
+    }
+}
+
 void CCodecBufferChannel::reset() {
     stop();
     if (mInputSurface != nullptr) {
@@ -1593,14 +1604,6 @@
         Mutexed<Output>::Locked output(mOutput);
         output->buffers.reset();
     }
-    if (mOutputSurface.lock()->surface) {
-        C2BlockPool::local_id_t outputPoolId;
-        {
-            Mutexed<BlockPools>::Locked pools(mBlockPools);
-            outputPoolId = pools->outputPoolId;
-        }
-        mComponent->stopUsingOutputSurface(outputPoolId);
-    }
 }
 
 void CCodecBufferChannel::release() {
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index f29a225..61fb06f 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -149,6 +149,12 @@
             std::map<size_t, sp<MediaCodecBuffer>> &&clientInputBuffers);
 
     /**
+     * Stop using buffers of the current output surface for other Codec
+     * instances to use the surface safely.
+     */
+    void stopUseOutputSurface();
+
+    /**
      * Stop queueing buffers to the component. This object should never queue
      * buffers after this call, until start() is called.
      */
diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp
index cde4c72..876c96d 100644
--- a/media/codec2/sfplugin/Codec2Buffer.cpp
+++ b/media/codec2/sfplugin/Codec2Buffer.cpp
@@ -843,6 +843,10 @@
         }
     }
     sp<ABuffer> aBuffer(alloc(align(width, 16) * align(height, 16) * bpp / 8));
+    if (aBuffer == nullptr) {
+        ALOGD("%s: failed to allocate buffer", __func__);
+        return nullptr;
+    }
     return new ConstGraphicBlockBuffer(
             format,
             aBuffer,
@@ -998,9 +1002,11 @@
 }
 
 using ::aidl::android::hardware::graphics::common::Cta861_3;
+using ::aidl::android::hardware::graphics::common::Dataspace;
 using ::aidl::android::hardware::graphics::common::Smpte2086;
 
 using ::android::gralloc4::MetadataType_Cta861_3;
+using ::android::gralloc4::MetadataType_Dataspace;
 using ::android::gralloc4::MetadataType_Smpte2086;
 using ::android::gralloc4::MetadataType_Smpte2094_40;
 
@@ -1156,7 +1162,8 @@
     return err;
 }
 
-c2_status_t SetHdrMetadataToGralloc4Handle(
+c2_status_t SetMetadataToGralloc4Handle(
+        android_dataspace_t dataSpace,
         const std::shared_ptr<const C2StreamHdrStaticMetadataInfo::output> &staticInfo,
         const std::shared_ptr<const C2StreamHdrDynamicMetadataInfo::output> &dynamicInfo,
         const C2Handle *const handle) {
@@ -1167,6 +1174,17 @@
         // Gralloc4 not supported; nothing to do
         return err;
     }
+    {
+        hidl_vec<uint8_t> metadata;
+        if (gralloc4::encodeDataspace(static_cast<Dataspace>(dataSpace), &metadata) == OK) {
+            Return<Error4> ret = mapper->set(buffer.get(), MetadataType_Dataspace, metadata);
+            if (!ret.isOk()) {
+                err = C2_REFUSED;
+            } else if (ret != Error4::NONE) {
+                err = C2_CORRUPTED;
+            }
+        }
+    }
     if (staticInfo && *staticInfo) {
         ALOGV("Setting static HDR info as gralloc4 metadata");
         std::optional<Smpte2086> smpte2086 = Smpte2086{
diff --git a/media/codec2/sfplugin/Codec2Buffer.h b/media/codec2/sfplugin/Codec2Buffer.h
index b02b042..b73acab 100644
--- a/media/codec2/sfplugin/Codec2Buffer.h
+++ b/media/codec2/sfplugin/Codec2Buffer.h
@@ -410,14 +410,16 @@
         std::shared_ptr<C2StreamHdrDynamicMetadataInfo::input> *dynamicInfo);
 
 /**
- * Set HDR metadata to Gralloc4 handle.
+ * Set metadata to Gralloc4 handle.
  *
+ * \param[in]   dataSpace   Dataspace to set.
  * \param[in]   staticInfo  HDR static info to set. Ignored if null or invalid.
  * \param[in]   dynamicInfo HDR dynamic info to set. Ignored if null or invalid.
  * \param[out]  handle      handle of the allocation.
  * \return C2_OK if successful
  */
-c2_status_t SetHdrMetadataToGralloc4Handle(
+c2_status_t SetMetadataToGralloc4Handle(
+        const android_dataspace_t dataSpace,
         const std::shared_ptr<const C2StreamHdrStaticMetadataInfo::output> &staticInfo,
         const std::shared_ptr<const C2StreamHdrDynamicMetadataInfo::output> &dynamicInfo,
         const C2Handle *const handle);
diff --git a/media/libaaudio/include/aaudio/AAudioTesting.h b/media/libaaudio/include/aaudio/AAudioTesting.h
index 0f2d7a2..01d97b6 100644
--- a/media/libaaudio/include/aaudio/AAudioTesting.h
+++ b/media/libaaudio/include/aaudio/AAudioTesting.h
@@ -87,7 +87,7 @@
  * @note This is only for testing. Do not use this in an application.
  * It may change or be removed at any time.
  *
- * @return true if the stream uses ther MMAP data path
+ * @return true if the stream uses the MMAP data path
  */
 AAUDIO_API bool AAudioStream_isMMapUsed(AAudioStream* stream);
 
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index ffbb32f..b18a569 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -29,6 +29,7 @@
 #include <audio_utils/clock.h>
 #include <audio_utils/primitives.h>
 #include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
 #include <media/AudioTrack.h>
 #include <utils/Log.h>
 #include <private/media/AudioTrackShared.h>
@@ -42,7 +43,9 @@
 
 #define WAIT_PERIOD_MS                  10
 #define WAIT_STREAM_END_TIMEOUT_SEC     120
+
 static const int kMaxLoopCountNotifications = 32;
+static constexpr char kAudioServiceName[] = "audio";
 
 using ::android::aidl_utils::statusTFromBinderStatus;
 using ::android::base::StringPrintf;
@@ -450,7 +453,7 @@
     std::string errorMessage;
     // Note mPortId is not valid until the track is created, so omit mPortId in ALOG for set.
     ALOGV("%s(): streamType %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
-          "flags #%x, notificationFrames %d, sessionId %d, transferType %d, uid %d, pid %d",
+          "flags %#x, notificationFrames %d, sessionId %d, transferType %d, uid %d, pid %d",
           __func__,
           streamType, sampleRate, format, channelMask, frameCount, flags, notificationFrames,
           sessionId, transferType, attributionSource.uid, attributionSource.pid);
@@ -1289,6 +1292,10 @@
                         legacy2aidl_audio_playback_rate_t_AudioPlaybackRate(playbackRate))));
         if (status == NO_ERROR) {
             mPlaybackRate = playbackRate;
+        } else if (status == INVALID_OPERATION
+                && playbackRate.mSpeed == 1.0f && mPlaybackRate.mPitch == 1.0f) {
+            mPlaybackRate = playbackRate;
+            return NO_ERROR;
         }
         return status;
     }
@@ -1945,6 +1952,9 @@
     }
 
     mPortId = output.portId;
+    // notify the upper layers about the new portId
+    triggerPortIdUpdate_l();
+
     // We retain a copy of the I/O handle, but don't own the reference
     mOutput = output.outputId;
     mRefreshRemaining = true;
@@ -3507,12 +3517,34 @@
     if (mPlayerIId == playerIId) return;
 
     mPlayerIId = playerIId;
+    triggerPortIdUpdate_l();
     mediametrics::LogItem(mMetricsId)
         .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETPLAYERIID)
         .set(AMEDIAMETRICS_PROP_PLAYERIID, playerIId)
         .record();
 }
 
+void AudioTrack::triggerPortIdUpdate_l() {
+    if (mAudioManager == nullptr) {
+        // use checkService() to avoid blocking if audio service is not up yet
+        sp<IBinder> binder =
+            defaultServiceManager()->checkService(String16(kAudioServiceName));
+        if (binder == nullptr) {
+            ALOGE("%s(%d): binding to audio service failed.",
+                  __func__,
+                  mPlayerIId);
+            return;
+        }
+
+        mAudioManager = interface_cast<IAudioManager>(binder);
+    }
+
+    // first time when the track is created we do not have a valid piid
+    if (mPlayerIId != PLAYER_PIID_INVALID) {
+        mAudioManager->playerEvent(mPlayerIId, PLAYER_UPDATE_PORT_ID, mPortId);
+    }
+}
+
 status_t AudioTrack::addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback)
 {
 
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index 32576c2..e873607 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_AUDIOTRACK_H
 #define ANDROID_AUDIOTRACK_H
 
+#include <audiomanager/IAudioManager.h>
 #include <binder/IMemory.h>
 #include <cutils/sched_policy.h>
 #include <media/AudioSystem.h>
@@ -1145,6 +1146,8 @@
             void setAudioTrackCallback(const sp<media::IAudioTrackCallback>& callback) {
                 mAudioTrackCallback->setAudioTrackCallback(callback);
             }
+ private:
+            void triggerPortIdUpdate_l();
 
  protected:
     /* copying audio tracks is not allowed */
@@ -1419,6 +1422,9 @@
      */
     int                     mPlayerIId = -1;  // AudioManager.h PLAYER_PIID_INVALID
 
+    /** Interface for interacting with the AudioService. */
+    sp<IAudioManager>       mAudioManager;
+
     /**
      * mLogSessionId is a string identifying this AudioTrack for the metrics service.
      * It may be unique or shared with other objects.  An empty string means the
diff --git a/media/libmedia/tests/codeclist/Android.bp b/media/libmedia/tests/codeclist/Android.bp
index 2ed3126..d4494f6 100644
--- a/media/libmedia/tests/codeclist/Android.bp
+++ b/media/libmedia/tests/codeclist/Android.bp
@@ -25,7 +25,7 @@
 
 cc_test {
     name: "CodecListTest",
-    test_suites: ["device-tests", "mts"],
+    test_suites: ["device-tests", "mts-media"],
     gtest: true,
 
     // Support multilib variants (using different suffix per sub-architecture), which is needed on
diff --git a/media/libmediametrics/libmediametrics.map.txt b/media/libmediametrics/libmediametrics.map.txt
index c46281a..f37af64 100644
--- a/media/libmediametrics/libmediametrics.map.txt
+++ b/media/libmediametrics/libmediametrics.map.txt
@@ -1,29 +1,29 @@
 LIBMEDIAMETRICS_1 {
   global:
-    mediametrics_addDouble; # apex
-    mediametrics_addInt32; # apex
-    mediametrics_addInt64; # apex
-    mediametrics_addRate; # apex
-    mediametrics_count; # apex
-    mediametrics_create; # apex
-    mediametrics_delete; # apex
-    mediametrics_freeCString; # apex
-    mediametrics_getAttributes; # apex
-    mediametrics_getCString; # apex
-    mediametrics_getDouble; # apex
-    mediametrics_getInt32; # apex
-    mediametrics_getInt64; # apex
-    mediametrics_getKey; # apex
-    mediametrics_getRate; # apex
-    mediametrics_isEnabled; # apex
-    mediametrics_readable; # apex
-    mediametrics_selfRecord; # apex
-    mediametrics_setCString; # apex
-    mediametrics_setDouble; # apex
-    mediametrics_setInt32; # apex
-    mediametrics_setInt64; # apex
-    mediametrics_setRate; # apex
-    mediametrics_setUid; # apex
+    mediametrics_addDouble; # systemapi
+    mediametrics_addInt32; # systemapi
+    mediametrics_addInt64; # systemapi
+    mediametrics_addRate; # systemapi
+    mediametrics_count; # systemapi
+    mediametrics_create; # systemapi
+    mediametrics_delete; # systemapi
+    mediametrics_freeCString; # systemapi
+    mediametrics_getAttributes; # systemapi
+    mediametrics_getCString; # systemapi
+    mediametrics_getDouble; # systemapi
+    mediametrics_getInt32; # systemapi
+    mediametrics_getInt64; # systemapi
+    mediametrics_getKey; # systemapi
+    mediametrics_getRate; # systemapi
+    mediametrics_isEnabled; # systemapi
+    mediametrics_readable; # systemapi
+    mediametrics_selfRecord; # systemapi
+    mediametrics_setCString; # systemapi
+    mediametrics_setDouble; # systemapi
+    mediametrics_setInt32; # systemapi
+    mediametrics_setInt64; # systemapi
+    mediametrics_setRate; # systemapi
+    mediametrics_setUid; # systemapi
   local:
     *;
 };
diff --git a/media/libstagefright/DataConverter.cpp b/media/libstagefright/DataConverter.cpp
index 52be054..b53ac77 100644
--- a/media/libstagefright/DataConverter.cpp
+++ b/media/libstagefright/DataConverter.cpp
@@ -24,6 +24,10 @@
 #include <media/MediaCodecBuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AUtils.h>
+#include <system/audio.h>
+#include <audio_utils/primitives.h>
+#include <audio_utils/format.h>
+
 
 namespace android {
 
@@ -81,12 +85,38 @@
     return numSamples * mTargetSampleSize;
 }
 
+static audio_format_t getAudioFormat(AudioEncoding e) {
+    audio_format_t format = AUDIO_FORMAT_INVALID;
+    switch (e) {
+        case kAudioEncodingPcm16bit:
+            format = AUDIO_FORMAT_PCM_16_BIT;
+            break;
+        case kAudioEncodingPcm8bit:
+            format = AUDIO_FORMAT_PCM_8_BIT;
+            break;
+        case kAudioEncodingPcmFloat:
+            format = AUDIO_FORMAT_PCM_FLOAT;
+            break;
+       case kAudioEncodingPcm24bitPacked:
+            format = AUDIO_FORMAT_PCM_24_BIT_PACKED;
+            break;
+       case kAudioEncodingPcm32bit:
+            format = AUDIO_FORMAT_PCM_32_BIT;
+            break;
+        default:
+            ALOGE("Invalid AudioEncoding %d", e);
+        }
+        return format;
+}
 
 static size_t getAudioSampleSize(AudioEncoding e) {
     switch (e) {
-        case kAudioEncodingPcm16bit: return 2;
-        case kAudioEncodingPcm8bit:  return 1;
-        case kAudioEncodingPcmFloat: return 4;
+        case kAudioEncodingPcm16bit:
+        case kAudioEncodingPcm8bit:
+        case kAudioEncodingPcmFloat:
+        case kAudioEncodingPcm24bitPacked:
+        case kAudioEncodingPcm32bit:
+            return audio_bytes_per_sample(getAudioFormat(e));
         default: return 0;
     }
 }
@@ -116,7 +146,15 @@
     } else if (mTo == kAudioEncodingPcmFloat && mFrom == kAudioEncodingPcm16bit) {
         memcpy_to_float_from_i16((float*)tgt->base(), (const int16_t*)src->data(), src->size() / 2);
     } else {
-        return INVALID_OPERATION;
+        audio_format_t srcFormat = getAudioFormat(mFrom);
+        audio_format_t dstFormat = getAudioFormat(mTo);
+
+        if ((srcFormat == AUDIO_FORMAT_INVALID) || (dstFormat == AUDIO_FORMAT_INVALID))
+            return INVALID_OPERATION;
+
+        size_t frames = src->size() / audio_bytes_per_sample(srcFormat);
+        memcpy_by_audio_format((void*)tgt->base(), dstFormat, (void*)src->data(),
+                srcFormat, frames);
     }
     return OK;
 }
diff --git a/media/libstagefright/renderfright/tests/RenderEngineTest.cpp b/media/libstagefright/renderfright/tests/RenderEngineTest.cpp
index 2697ff4..3a67cc2 100644
--- a/media/libstagefright/renderfright/tests/RenderEngineTest.cpp
+++ b/media/libstagefright/renderfright/tests/RenderEngineTest.cpp
@@ -60,7 +60,7 @@
     }
 
     static sp<GraphicBuffer> allocateDefaultBuffer() {
-        return new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
+        return sp<GraphicBuffer>::make(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
                                  HAL_PIXEL_FORMAT_RGBA_8888, 1,
                                  GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
                                          GRALLOC_USAGE_HW_RENDER,
@@ -69,7 +69,7 @@
 
     // Allocates a 1x1 buffer to fill with a solid color
     static sp<GraphicBuffer> allocateSourceBuffer(uint32_t width, uint32_t height) {
-        return new GraphicBuffer(width, height, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+        return sp<GraphicBuffer>::make(width, height, HAL_PIXEL_FORMAT_RGBA_8888, 1,
                                  GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
                                          GRALLOC_USAGE_HW_TEXTURE,
                                  "input");
@@ -275,7 +275,7 @@
         renderengine::DisplaySettings settings;
         std::vector<const renderengine::LayerSettings*> layers;
         // Meaningless buffer since we don't do any drawing
-        sp<GraphicBuffer> buffer = new GraphicBuffer();
+        sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make();
         invokeDraw(settings, layers, buffer);
     }
 
diff --git a/media/libstagefright/renderfright/tests/RenderEngineThreadedTest.cpp b/media/libstagefright/renderfright/tests/RenderEngineThreadedTest.cpp
index 97c7442..b82586b 100644
--- a/media/libstagefright/renderfright/tests/RenderEngineThreadedTest.cpp
+++ b/media/libstagefright/renderfright/tests/RenderEngineThreadedTest.cpp
@@ -70,7 +70,7 @@
 }
 
 TEST_F(RenderEngineThreadedTest, bindExternalBuffer_withBuffer) {
-    sp<GraphicBuffer> buf = new GraphicBuffer();
+    sp<GraphicBuffer> buf = sp<GraphicBuffer>::make();
     EXPECT_CALL(*mRenderEngine, bindExternalTextureBuffer(0, buf, Eq(nullptr)))
             .WillOnce(Return(NO_ERROR));
     status_t result = mThreadedRE->bindExternalTextureBuffer(0, buf, nullptr);
@@ -83,7 +83,7 @@
 }
 
 TEST_F(RenderEngineThreadedTest, cacheExternalTextureBuffer_withBuffer) {
-    sp<GraphicBuffer> buf = new GraphicBuffer();
+    sp<GraphicBuffer> buf = sp<GraphicBuffer>::make();
     EXPECT_CALL(*mRenderEngine, cacheExternalTextureBuffer(buf));
     mThreadedRE->cacheExternalTextureBuffer(buf);
 }
@@ -198,7 +198,7 @@
 TEST_F(RenderEngineThreadedTest, drawLayers) {
     renderengine::DisplaySettings settings;
     std::vector<const renderengine::LayerSettings*> layers;
-    sp<GraphicBuffer> buffer = new GraphicBuffer();
+    sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make();
     base::unique_fd bufferFence;
     base::unique_fd drawFence;
 
diff --git a/media/libstagefright/rtsp/AAVCAssembler.cpp b/media/libstagefright/rtsp/AAVCAssembler.cpp
index 2f516d5..ddf797c 100644
--- a/media/libstagefright/rtsp/AAVCAssembler.cpp
+++ b/media/libstagefright/rtsp/AAVCAssembler.cpp
@@ -618,13 +618,14 @@
 
 int32_t AAVCAssembler::pickStartSeq(const Queue *queue,
         uint32_t first, int64_t play, int64_t jit) {
+    CHECK(!queue->empty());
     // pick the first sequence number has the start bit.
     sp<ABuffer> buffer = *(queue->begin());
     int32_t firstSeqNo = buffer->int32Data();
 
     // This only works for FU-A type & non-start sequence
-    unsigned nalType = buffer->data()[0] & 0x1f;
-    if (nalType != 28 || buffer->data()[1] & 0x80) {
+    int32_t nalType = buffer->size() >= 1 ? buffer->data()[0] & 0x1f : -1;
+    if (nalType != 28 || (buffer->size() >= 2 && buffer->data()[1] & 0x80)) {
         return firstSeqNo;
     }
 
@@ -634,7 +635,7 @@
         if (rtpTime + jit >= play) {
             break;
         }
-        if ((data[1] & 0x80)) {
+        if (it->size() >= 2 && (data[1] & 0x80)) {
             const int32_t seqNo = it->int32Data();
             ALOGE("finding [HEAD] pkt. \t Seq# (%d ~ )[%d", firstSeqNo, seqNo);
             firstSeqNo = seqNo;
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 6fcf119..d917772 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -985,7 +985,7 @@
 
     int type = storage->getType();
     if (type == MTP_STORAGE_REMOVABLE_RAM) {
-        std::string str = android::base::Trim((const char*)name);
+        std::string str = android::base::Trim(name);
         name.set(str.c_str());
     }
     ALOGV("name: %s format: 0x%04X (%s)\n", (const char*)name, format,
diff --git a/media/mtp/MtpStringBuffer.h b/media/mtp/MtpStringBuffer.h
index 4cec58a..30d1bc1 100644
--- a/media/mtp/MtpStringBuffer.h
+++ b/media/mtp/MtpStringBuffer.h
@@ -20,6 +20,7 @@
 #include <log/log.h>
 #include <stdint.h>
 #include <string>
+#include <string_view>
 
 // Max Character number of a MTP String
 #define MTP_STRING_MAX_CHARACTER_NUMBER             255
@@ -55,6 +56,7 @@
     inline int      size() const { return mString.length(); }
 
     inline operator const char*() const { return mString.c_str(); }
+    operator std::string_view() const { return mString; }
 };
 
 inline void MtpStringBuffer::append(const char* other) {
diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp
index 38e422d..7495082 100644
--- a/media/ndk/NdkMediaCodec.cpp
+++ b/media/ndk/NdkMediaCodec.cpp
@@ -529,22 +529,31 @@
         AMediaCodecOnAsyncNotifyCallback callback,
         void *userdata) {
 
-    Mutex::Autolock _l(mData->mAsyncCallbackLock);
-
-    if (mData->mAsyncNotify == NULL) {
-        mData->mAsyncNotify = new AMessage(kWhatAsyncNotify, mData->mHandler);
+    {
+        Mutex::Autolock _l(mData->mAsyncCallbackLock);
+        if (mData->mAsyncNotify == NULL) {
+            mData->mAsyncNotify = new AMessage(kWhatAsyncNotify, mData->mHandler);
+        }
+        // we set this ahead so that we can be ready
+        // to receive callbacks as soon as the next call is a
+        // success.
+        mData->mAsyncCallback = callback;
+        mData->mAsyncCallbackUserData = userdata;
     }
 
     // always call, codec may have been reset/re-configured since last call.
     status_t err = mData->mCodec->setCallback(mData->mAsyncNotify);
     if (err != OK) {
+        {
+            //The setup gone wrong. clean up the pointers.
+            Mutex::Autolock _l(mData->mAsyncCallbackLock);
+            mData->mAsyncCallback = {};
+            mData->mAsyncCallbackUserData = nullptr;
+        }
         ALOGE("setAsyncNotifyCallback: err(%d), failed to set async callback", err);
         return translate_error(err);
     }
 
-    mData->mAsyncCallback = callback;
-    mData->mAsyncCallbackUserData = userdata;
-
     return AMEDIA_OK;
 }
 
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index 59c1103..f4674de 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -183,7 +183,7 @@
     AMediaDrmSessionId asid = {sessionId.data(), sessionId.size()};
     int32_t dataSize = data.size();
     const uint8_t *dataPtr = data.data();
-    if (dataSize > 0) {
+    if (dataSize >= 0) {
         (*mEventListener)(mObj, &asid, ndkEventType, 0, dataPtr, dataSize);
     } else {
         ALOGE("invalid event data size=%d", dataSize);
diff --git a/media/utils/MethodStatistics.cpp b/media/utils/MethodStatistics.cpp
index b179b20..086757b 100644
--- a/media/utils/MethodStatistics.cpp
+++ b/media/utils/MethodStatistics.cpp
@@ -22,7 +22,8 @@
 
 std::shared_ptr<std::vector<std::string>>
 getStatisticsClassesForModule(std::string_view moduleName) {
-    static const std::map<std::string, std::shared_ptr<std::vector<std::string>>> m {
+    static const std::map<std::string, std::shared_ptr<std::vector<std::string>>,
+            std::less<> /* transparent comparator */> m {
         {
             METHOD_STATISTICS_MODULE_NAME_AUDIO_HIDL,
             std::shared_ptr<std::vector<std::string>>(
@@ -34,13 +35,14 @@
               })
         },
     };
-    auto it = m.find({moduleName.begin(), moduleName.end()});
+    auto it = m.find(moduleName);
     if (it == m.end()) return {};
     return it->second;
 }
 
 static void addClassesToMap(const std::shared_ptr<std::vector<std::string>> &classNames,
-        std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>> &map) {
+        std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>,
+                std::less<> /* transparent comparator */> &map) {
     if (classNames) {
         for (const auto& className : *classNames) {
             map.emplace(className, std::make_shared<MethodStatistics<std::string>>());
@@ -51,17 +53,18 @@
 // singleton statistics for DeviceHalHidl StreamOutHalHidl StreamInHalHidl
 std::shared_ptr<MethodStatistics<std::string>>
 getStatisticsForClass(std::string_view className) {
-    static const std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>> m =
+    static const std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>,
+            std::less<> /* transparent comparator */> m =
         // copy elided initialization of map m.
         [](){
-            std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>> m;
+            std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>, std::less<>> m;
             addClassesToMap(
                     getStatisticsClassesForModule(METHOD_STATISTICS_MODULE_NAME_AUDIO_HIDL),
                     m);
             return m;
         }();
 
-    auto it = m.find({className.begin(), className.end()});
+    auto it = m.find(className);
     if (it == m.end()) return {};
     return it->second;
 }
diff --git a/media/utils/TimeCheck.cpp b/media/utils/TimeCheck.cpp
index 0848eb3..e53d70f 100644
--- a/media/utils/TimeCheck.cpp
+++ b/media/utils/TimeCheck.cpp
@@ -21,6 +21,7 @@
 #include <android-base/logging.h>
 #include <audio_utils/clock.h>
 #include <mediautils/EventLog.h>
+#include <mediautils/FixedString.h>
 #include <mediautils/MethodStatistics.h>
 #include <mediautils/TimeCheck.h>
 #include <utils/Log.h>
@@ -138,11 +139,11 @@
     return getTimeCheckThread().toString();
 }
 
-TimeCheck::TimeCheck(std::string tag, OnTimerFunc&& onTimer, uint32_t timeoutMs,
+TimeCheck::TimeCheck(std::string_view tag, OnTimerFunc&& onTimer, uint32_t timeoutMs,
         bool crashOnTimeout)
-    : mTimeCheckHandler(new TimeCheckHandler{
-            std::move(tag), std::move(onTimer), crashOnTimeout,
-            std::chrono::system_clock::now(), gettid()})
+    : mTimeCheckHandler{ std::make_shared<TimeCheckHandler>(
+            tag, std::move(onTimer), crashOnTimeout,
+            std::chrono::system_clock::now(), gettid()) }
     , mTimerHandle(timeoutMs == 0
               ? getTimeCheckThread().trackTask(mTimeCheckHandler->tag)
               : getTimeCheckThread().scheduleTask(
@@ -231,14 +232,14 @@
             mediautils::getStatisticsForClass(className);
     if (!statistics) return {}; // empty TimeCheck.
     return mediautils::TimeCheck(
-            std::string(className).append("::").append(methodName),
-            [ clazz = std::string(className), method = std::string(methodName),
+            FixedString62(className).append("::").append(methodName),
+            [ safeMethodName = FixedString30(methodName),
               stats = std::move(statistics) ]
             (bool timeout, float elapsedMs) {
                     if (timeout) {
                         ; // ignored, there is no timeout value.
                     } else {
-                        stats->event(method, elapsedMs);
+                        stats->event(safeMethodName.asStringView(), elapsedMs);
                     }
             }, 0 /* timeoutMs */);
 }
diff --git a/media/utils/TimerThread.cpp b/media/utils/TimerThread.cpp
index 6de6b13..09783ed 100644
--- a/media/utils/TimerThread.cpp
+++ b/media/utils/TimerThread.cpp
@@ -31,17 +31,15 @@
 extern std::string_view timeSuffix(std::string_view time1, std::string_view time2);
 
 TimerThread::Handle TimerThread::scheduleTask(
-        std::string tag, std::function<void()>&& func, std::chrono::milliseconds timeout) {
+        std::string_view tag, std::function<void()>&& func, std::chrono::milliseconds timeout) {
     const auto now = std::chrono::system_clock::now();
-    std::shared_ptr<const Request> request{
-            new Request{ now, now + timeout, gettid(), std::move(tag) }};
+    auto request = std::make_shared<const Request>(now, now + timeout, gettid(), tag);
     return mMonitorThread.add(std::move(request), std::move(func), timeout);
 }
 
-TimerThread::Handle TimerThread::trackTask(std::string tag) {
+TimerThread::Handle TimerThread::trackTask(std::string_view tag) {
     const auto now = std::chrono::system_clock::now();
-    std::shared_ptr<const Request> request{
-            new Request{ now, now, gettid(), std::move(tag) }};
+    auto request = std::make_shared<const Request>(now, now, gettid(), tag);
     return mNoTimeoutMap.add(std::move(request));
 }
 
@@ -106,10 +104,10 @@
 //
 /* static */
 bool TimerThread::isRequestFromHal(const std::shared_ptr<const Request>& request) {
-    const size_t hidlPos = request->tag.find("Hidl");
+    const size_t hidlPos = request->tag.asStringView().find("Hidl");
     if (hidlPos == std::string::npos) return false;
     // should be a separator afterwards Hidl which indicates the string was in the class.
-    const size_t separatorPos = request->tag.find("::", hidlPos);
+    const size_t separatorPos = request->tag.asStringView().find("::", hidlPos);
     return separatorPos != std::string::npos;
 }
 
diff --git a/media/utils/include/mediautils/FixedString.h b/media/utils/include/mediautils/FixedString.h
new file mode 100644
index 0000000..047aa82
--- /dev/null
+++ b/media/utils/include/mediautils/FixedString.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2022 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 <algorithm>
+#include <string>
+#include <string_view>
+
+namespace android::mediautils {
+
+/*
+ * FixedString is a stack allocatable string buffer that supports
+ * simple appending of other strings and string_views.
+ *
+ * It is designed for no-malloc operation when std::string
+ * small buffer optimization is insufficient.
+ *
+ * To keep code small, use asStringView() for operations on this.
+ *
+ * Notes:
+ * 1) Appending beyond the internal buffer size results in truncation.
+ *
+ * Alternatives:
+ * 1) If you want a sharable copy-on-write string implementation,
+ *    consider using the legacy android::String8().
+ * 2) Using std::string with a fixed stack allocator may suit your needs,
+ *    but exception avoidance is tricky.
+ * 3) Using C++20 ranges https://en.cppreference.com/w/cpp/ranges if you don't
+ *    need backing store.  Be careful about allocation with ranges.
+ *
+ * Good small sizes are multiples of 16 minus 2, e.g. 14, 30, 46, 62.
+ *
+ * Implementation Notes:
+ * 1) No iterators or [] for FixedString - please convert to std::string_view.
+ * 2) For small N (e.g. less than 62), consider a change to always zero fill and
+ *    potentially prevent always zero terminating (if one only does append).
+ *
+ * Possible arguments to create/append:
+ * 1) A FixedString.
+ * 2) A single char.
+ * 3) A char * pointer.
+ * 4) A std::string.
+ * 5) A std::string_view (or something convertible to it).
+ *
+ * Example:
+ *
+ * FixedString s1(std::string("a"));    // ctor
+ * s1 << "bc" << 'd' << '\n';           // streaming append
+ * s1 += "hello";                       // += append
+ * ASSERT_EQ(s1, "abcd\nhello");
+ */
+template <uint32_t N>
+struct FixedString
+{
+    // Find the best size type.
+    using strsize_t = std::conditional_t<(N > 255), uint32_t, uint8_t>;
+
+    // constructors
+    FixedString() { // override default
+        buffer_[0] = '\0';
+    }
+
+    FixedString(const FixedString& other) { // override default.
+        copyFrom<N>(other);
+    }
+
+    // The following constructor is not explicit to allow
+    // FixedString<8> s = "abcd";
+    template <typename ...Types>
+    FixedString(Types&&... args) {
+        append(std::forward<Types>(args)...);
+    }
+
+    // copy assign (copyFrom checks for equality and returns *this).
+    FixedString& operator=(const FixedString& other) { // override default.
+        return copyFrom<N>(other);
+    }
+
+    template <typename ...Types>
+    FixedString& operator=(Types&&... args) {
+        size_ = 0;
+        return append(std::forward<Types>(args)...);
+    }
+
+    // operator equals
+    bool operator==(const char *s) const {
+        return strncmp(c_str(), s, capacity() + 1) == 0;
+    }
+
+    bool operator==(std::string_view s) const {
+        return size() == s.size() && memcmp(data(), s.data(), size()) == 0;
+    }
+
+    // operator not-equals
+    template <typename T>
+    bool operator!=(const T& other) const {
+        return !operator==(other);
+    }
+
+    // operator +=
+    template <typename ...Types>
+    FixedString& operator+=(Types&&... args) {
+        return append(std::forward<Types>(args)...);
+    }
+
+    // conversion to std::string_view.
+    operator std::string_view() const {
+        return asStringView();
+    }
+
+    // basic observers
+    size_t buffer_offset() const { return offsetof(std::decay_t<decltype(*this)>, buffer_); }
+    static constexpr uint32_t capacity() { return N; }
+    uint32_t size() const { return size_; }
+    uint32_t remaining() const { return size_ >= N ? 0 : N - size_; }
+    bool empty() const { return size_ == 0; }
+    bool full() const { return size_ == N; }  // when full, possible truncation risk.
+    char * data() { return buffer_; }
+    const char * data() const { return buffer_; }
+    const char * c_str() const { return buffer_; }
+
+    inline std::string_view asStringView() const {
+        return { buffer_, static_cast<size_t>(size_) };
+    }
+    inline std::string asString() const {
+        return { buffer_, static_cast<size_t>(size_) };
+    }
+
+    void clear() { size_ = 0; buffer_[0] = 0; }
+
+    // Implementation of append - using templates
+    // to guarantee precedence in the presence of ambiguity.
+    //
+    // Consider C++20 template overloading through constraints and concepts.
+    template <typename T>
+    FixedString& append(const T& t) {
+        using decayT = std::decay_t<T>;
+        if constexpr (is_specialization_v<decayT, FixedString>) {
+            // A FixedString<U>
+            if (size_ == 0) {
+                // optimization to erase everything.
+                return copyFrom(t);
+            } else {
+                return appendStringView({t.data(), t.size()});
+            }
+        } else if constexpr(std::is_same_v<decayT, char>) {
+            if (size_ < N) {
+                buffer_[size_++] = t;
+                buffer_[size_] = '\0';
+            }
+            return *this;
+        } else if constexpr(std::is_same_v<decayT, char *>) {
+            // Some char* ptr.
+            return appendString(t);
+        } else if constexpr (std::is_convertible_v<decayT, std::string_view>) {
+            // std::string_view, std::string, or some other convertible type.
+            return appendStringView(t);
+        } else /* constexpr */ {
+            static_assert(dependent_false_v<T>, "non-matching append type");
+        }
+    }
+
+    FixedString& appendStringView(std::string_view s) {
+        uint32_t total = std::min(static_cast<size_t>(N - size_), s.size());
+        memcpy(buffer_ + size_, s.data(), total);
+        size_ += total;
+        buffer_[size_] = '\0';
+        return *this;
+    }
+
+    FixedString& appendString(const char *s) {
+        // strncpy zero pads to the end,
+        // strlcpy returns total expected length,
+        // we don't have strncpy_s in Bionic,
+        // so we write our own here.
+        while (size_ < N && *s != '\0') {
+            buffer_[size_++] = *s++;
+        }
+        buffer_[size_] = '\0';
+        return *this;
+    }
+
+    // Copy initialize the struct.
+    // Note: We are POD but customize the copying for acceleration
+    // of moving small strings embedded in a large buffers.
+    template <uint32_t U>
+    FixedString& copyFrom(const FixedString<U>& other) {
+        if ((void*)this != (void*)&other) { // not a self-assignment
+            if (other.size() == 0) {
+                size_ = 0;
+                buffer_[0] = '\0';
+                return *this;
+            }
+            constexpr size_t kSizeToCopyWhole = 64;
+            if constexpr (N == U &&
+                    sizeof(*this) == sizeof(other) &&
+                    sizeof(*this) <= kSizeToCopyWhole) {
+                // As we have the same str size type, we can just
+                // memcpy with fixed size, which can be easily optimized.
+                memcpy(static_cast<void*>(this), static_cast<const void*>(&other), sizeof(*this));
+                return *this;
+            }
+            if constexpr (std::is_same_v<strsize_t, typename FixedString<U>::strsize_t>) {
+                constexpr size_t kAlign = 8;  // align to a multiple of 8.
+                static_assert((kAlign & (kAlign - 1)) == 0); // power of 2.
+                // we check any alignment issues.
+                if (buffer_offset() == other.buffer_offset() && other.size() <= capacity()) {
+                    // improve on standard POD copying by reducing size.
+                    const size_t mincpy = buffer_offset() + other.size() + 1 /* nul */;
+                    const size_t maxcpy = std::min(sizeof(*this), sizeof(other));
+                    const size_t cpysize = std::min(mincpy + kAlign - 1 & ~(kAlign - 1), maxcpy);
+                    memcpy(static_cast<void*>(this), static_cast<const void*>(&other), cpysize);
+                    return *this;
+                }
+            }
+            size_ = std::min(other.size(), capacity());
+            memcpy(buffer_, other.data(), size_);
+            buffer_[size_] = '\0';  // zero terminate.
+        }
+        return *this;
+    }
+
+private:
+    //  Template helper methods
+
+    template <typename Test, template <uint32_t> class Ref>
+    struct is_specialization : std::false_type {};
+
+    template <template <uint32_t> class Ref, uint32_t UU>
+    struct is_specialization<Ref<UU>, Ref>: std::true_type {};
+
+    template <typename Test, template <uint32_t> class Ref>
+    static inline constexpr bool is_specialization_v = is_specialization<Test, Ref>::value;
+
+    // For static assert(false) we need a template version to avoid early failure.
+    template <typename T>
+    static inline constexpr bool dependent_false_v = false;
+
+    // POD variables
+    strsize_t size_ = 0;
+    char buffer_[N + 1 /* allow zero termination */];
+};
+
+// Stream operator syntactic sugar.
+// Example:
+// s << 'b' << "c" << "d" << '\n';
+template <uint32_t N, typename ...Types>
+FixedString<N>& operator<<(FixedString<N>& fs, Types&&... args) {
+    return fs.append(std::forward<Types>(args)...);
+}
+
+// We do not use a default size for fixed string as changing
+// the default size would lead to different behavior - we want the
+// size to be explicitly known.
+
+// FixedString62 of 62 chars fits in one typical cache line.
+using FixedString62 = FixedString<62>;
+
+// Slightly smaller
+using FixedString30 = FixedString<30>;
+
+// Since we have added copy and assignment optimizations,
+// we are no longer trivially assignable and copyable.
+// But we check standard layout here to prevent inclusion of unacceptable members or virtuals.
+static_assert(std::is_standard_layout_v<FixedString62>);
+static_assert(std::is_standard_layout_v<FixedString30>);
+
+}  // namespace android::mediautils
diff --git a/media/utils/include/mediautils/MethodStatistics.h b/media/utils/include/mediautils/MethodStatistics.h
index 700fbaa..6d7e990 100644
--- a/media/utils/include/mediautils/MethodStatistics.h
+++ b/media/utils/include/mediautils/MethodStatistics.h
@@ -55,15 +55,23 @@
     /**
      * Adds a method event, typically execution time in ms.
      */
-    void event(Code code, FloatType executeMs) {
+    template <typename C>
+    void event(C&& code, FloatType executeMs) {
         std::lock_guard lg(mLock);
-        mStatisticsMap[code].add(executeMs);
+        auto it = mStatisticsMap.lower_bound(code);
+        if (it != mStatisticsMap.end() && it->first == code) {
+            it->second.add(executeMs);
+        } else {
+            // StatsType ctor takes an optional array of data for initialization.
+            FloatType dataArray[1] = { executeMs };
+            mStatisticsMap.emplace_hint(it, std::forward<C>(code), dataArray);
+        }
     }
 
     /**
      * Returns the name for the method code.
      */
-    std::string getMethodForCode(Code code) const {
+    std::string getMethodForCode(const Code& code) const {
         auto it = mMethodMap.find(code);
         return it == mMethodMap.end() ? std::to_string((int)code) : it->second;
     }
@@ -71,7 +79,7 @@
     /**
      * Returns the number of times the method was invoked by event().
      */
-    size_t getMethodCount(Code code) const {
+    size_t getMethodCount(const Code& code) const {
         std::lock_guard lg(mLock);
         auto it = mStatisticsMap.find(code);
         return it == mStatisticsMap.end() ? 0 : it->second.getN();
@@ -80,7 +88,7 @@
     /**
      * Returns the statistics object for the method.
      */
-    StatsType getStatistics(Code code) const {
+    StatsType getStatistics(const Code& code) const {
         std::lock_guard lg(mLock);
         auto it = mStatisticsMap.find(code);
         return it == mStatisticsMap.end() ? StatsType{} : it->second;
@@ -107,9 +115,10 @@
     }
 
 private:
-    const std::map<Code, std::string> mMethodMap;
+    // Note: we use a transparent comparator std::less<> for heterogeneous key lookup.
+    const std::map<Code, std::string, std::less<>> mMethodMap;
     mutable std::mutex mLock;
-    std::map<Code, StatsType> mStatisticsMap GUARDED_BY(mLock);
+    std::map<Code, StatsType, std::less<>> mStatisticsMap GUARDED_BY(mLock);
 };
 
 // Managed Statistics support.
diff --git a/media/utils/include/mediautils/TimeCheck.h b/media/utils/include/mediautils/TimeCheck.h
index ef03aef..038e7dd 100644
--- a/media/utils/include/mediautils/TimeCheck.h
+++ b/media/utils/include/mediautils/TimeCheck.h
@@ -60,7 +60,7 @@
      *                  the TimeCheck object is destroyed or leaves scope.
      * \param crashOnTimeout true if the object issues an abort on timeout.
      */
-    explicit TimeCheck(std::string tag, OnTimerFunc&& onTimer = {},
+    explicit TimeCheck(std::string_view tag, OnTimerFunc&& onTimer = {},
             uint32_t timeoutMs = kDefaultTimeOutMs, bool crashOnTimeout = true);
 
     TimeCheck() = default;
@@ -79,7 +79,17 @@
     // The usage here is const safe.
     class TimeCheckHandler {
     public:
-        const std::string tag;
+        template <typename S, typename F>
+        TimeCheckHandler(S&& _tag, F&& _onTimer, bool _crashOnTimeout,
+            const std::chrono::system_clock::time_point& _startTime,
+            pid_t _tid)
+            : tag(std::forward<S>(_tag))
+            , onTimer(std::forward<F>(_onTimer))
+            , crashOnTimeout(_crashOnTimeout)
+            , startTime(_startTime)
+            , tid(_tid)
+            {}
+        const FixedString62 tag;
         const OnTimerFunc onTimer;
         const bool crashOnTimeout;
         const std::chrono::system_clock::time_point startTime;
diff --git a/media/utils/include/mediautils/TimerThread.h b/media/utils/include/mediautils/TimerThread.h
index ffee602..349bffd 100644
--- a/media/utils/include/mediautils/TimerThread.h
+++ b/media/utils/include/mediautils/TimerThread.h
@@ -27,6 +27,8 @@
 
 #include <android-base/thread_annotations.h>
 
+#include <mediautils/FixedString.h>
+
 namespace android::mediautils {
 
 /**
@@ -54,7 +56,7 @@
      * \returns       a handle that can be used for cancellation.
      */
     Handle scheduleTask(
-            std::string tag, std::function<void()>&& func, std::chrono::milliseconds timeout);
+            std::string_view tag, std::function<void()>&& func, std::chrono::milliseconds timeout);
 
     /**
      * Tracks a task that shows up on toString() until cancelled.
@@ -62,7 +64,7 @@
      * \param tag     string associated with the task.
      * \returns       a handle that can be used for cancellation.
      */
-    Handle trackTask(std::string tag);
+    Handle trackTask(std::string_view tag);
 
     /**
      * Cancels a task previously scheduled with scheduleTask()
@@ -129,12 +131,22 @@
     // To minimize movement of data, we pass around shared_ptrs to Requests.
     // These are allocated and deallocated outside of the lock.
     struct Request {
+        Request(const std::chrono::system_clock::time_point& _scheduled,
+                const std::chrono::system_clock::time_point& _deadline,
+                pid_t _tid,
+                std::string_view _tag)
+            : scheduled(_scheduled)
+            , deadline(_deadline)
+            , tid(_tid)
+            , tag(_tag)
+            {}
+
         const std::chrono::system_clock::time_point scheduled;
         const std::chrono::system_clock::time_point deadline; // deadline := scheduled + timeout
                                                               // if deadline == scheduled, no
                                                               // timeout, task not executed.
         const pid_t tid;
-        const std::string tag;
+        const FixedString62 tag;
 
         std::string toString() const;
     };
diff --git a/media/utils/tests/Android.bp b/media/utils/tests/Android.bp
index f0e3c1e..602cada 100644
--- a/media/utils/tests/Android.bp
+++ b/media/utils/tests/Android.bp
@@ -132,6 +132,20 @@
 }
 
 cc_test {
+    name: "mediautils_fixedstring_tests",
+
+    defaults: ["libmediautils_tests_defaults"],
+
+    shared_libs: [
+        "libaudioutils",
+    ],
+
+    srcs: [
+        "mediautils_fixedstring_tests.cpp",
+    ],
+}
+
+cc_test {
     name: "mediautils_scopedstatistics_tests",
 
     defaults: ["libmediautils_tests_defaults"],
diff --git a/media/utils/tests/mediautils_fixedstring_tests.cpp b/media/utils/tests/mediautils_fixedstring_tests.cpp
new file mode 100644
index 0000000..7ab9a9f
--- /dev/null
+++ b/media/utils/tests/mediautils_fixedstring_tests.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2022 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_TAG "mediautils_fixedstring_tests"
+
+#include <mediautils/FixedString.h>
+
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+using namespace android::mediautils;
+
+TEST(mediautils_fixedstring_tests, ctor) {
+    FixedString<8> s0("abcde");
+
+    ASSERT_FALSE(s0.empty());
+    ASSERT_EQ(8U, s0.capacity());
+
+    ASSERT_EQ(5U, s0.size());
+    ASSERT_EQ(3U, s0.remaining());
+    ASSERT_EQ(0, strcmp(s0.c_str(), "abcde"));
+
+    ASSERT_EQ(0, strcmp(s0.data(), "abcde"));
+
+    // overflow
+    FixedString<8> s1("abcdefghijk");
+    ASSERT_EQ(8U, s1.size());
+    ASSERT_TRUE(s1.full());
+    ASSERT_EQ(0U, s1.remaining());
+    ASSERT_EQ(0, strcmp(s1.c_str(), "abcdefgh"));
+
+    // overflow
+    FixedString<8> s2(std::string("abcdefghijk"));
+    ASSERT_TRUE(s2.full());
+
+    ASSERT_EQ(8U, s2.size());
+    ASSERT_EQ(0, strcmp(s2.c_str(), "abcdefgh"));
+
+    // complex
+    ASSERT_EQ(s1, s2);
+    ASSERT_EQ(FixedString<12>().append(s1), s2);
+    ASSERT_NE(s1, "bcd");
+
+    // string and stringview
+    ASSERT_EQ(s1.asString(), s1.asStringView());
+
+    FixedString30 s3;
+    s3 = std::string("abcd");
+    ASSERT_EQ(s3, "abcd");
+
+    s3.clear();
+    ASSERT_EQ(s3, "");
+    ASSERT_NE(s3, "abcd");
+    ASSERT_EQ(0U, s3.size());
+}
+
+TEST(mediautils_fixedstring_tests, append) {
+    FixedString<8> s0;
+    ASSERT_EQ(0U, s0.size());
+    ASSERT_EQ(0, strcmp(s0.c_str(), ""));
+    ASSERT_TRUE(s0.empty());
+    ASSERT_FALSE(s0.full());
+
+    s0.append("abc");
+    ASSERT_EQ(3U, s0.size());
+    ASSERT_EQ(0, strcmp(s0.c_str(), "abc"));
+
+    s0.append(std::string("d"));
+    ASSERT_EQ(4U, s0.size());
+    ASSERT_EQ(0, strcmp(s0.c_str(), "abcd"));
+
+    // overflow
+    s0.append("efghijk");
+    ASSERT_EQ(8U, s0.size());
+    ASSERT_EQ(0, strcmp(s0.c_str(), "abcdefgh"));
+    ASSERT_TRUE(s0.full());
+
+    // concatenated
+    ASSERT_EQ(FixedString62("abcd"),
+            FixedString<8>("ab").append("c").append(FixedString<8>("d")));
+    ASSERT_EQ(FixedString<12>("a").append(FixedString<12>("b")), "ab");
+}
+
+TEST(mediautils_fixedstring_tests, plus_equals) {
+    FixedString<8> s0;
+    ASSERT_EQ(0U, s0.size());
+    ASSERT_EQ(0, strcmp(s0.c_str(), ""));
+
+    s0 += "abc";
+    s0 += "def";
+    ASSERT_EQ(s0, "abcdef");
+}
+
+TEST(mediautils_fixedstring_tests, stream_operator) {
+    FixedString<8> s0('a');
+
+    s0 << 'b' << "c" << "d" << '\n';
+    ASSERT_EQ(s0, "abcd\n");
+}
+
+TEST(mediautils_fixedstring_tests, examples) {
+    FixedString30 s1(std::string("a"));
+    s1 << "bc" << 'd' << '\n';
+    s1 += "hello";
+
+    ASSERT_EQ(s1, "abcd\nhello");
+
+    FixedString30 s2;
+    for (const auto &c : s1.asStringView()) {
+        s2.append(c);
+    };
+    ASSERT_EQ(s1, s2);
+
+    FixedString30 s3(std::move(s1));
+}
+
+// Ensure type alias works fine as well.
+using FixedString1024 = FixedString<1024>;
+
+TEST(mediautils_fixedstring_tests, copy) {
+    FixedString1024 s0("abc");
+    FixedString62 s1(s0);
+
+    ASSERT_EQ(3U, s1.size());
+    ASSERT_EQ(0, strcmp(s1.c_str(), "abc"));
+    ASSERT_EQ(s0, s1);
+
+    FixedString<1024> s2(s1);
+    ASSERT_EQ(3U, s2.size());
+    ASSERT_EQ(0, strcmp(s2.c_str(), "abc"));
+    ASSERT_EQ(s2, "abc");
+    ASSERT_NE(s2, "def");
+    ASSERT_EQ(s2, std::string("abc"));
+    ASSERT_NE(s2, std::string("def"));
+    ASSERT_EQ(s1, s2);
+    ASSERT_EQ(s0, s2);
+    ASSERT_EQ(s2, FixedString62(FixedString1024("abc")));
+}
diff --git a/services/audioflinger/Android.bp b/services/audioflinger/Android.bp
index a08879e..420ae52 100644
--- a/services/audioflinger/Android.bp
+++ b/services/audioflinger/Android.bp
@@ -71,6 +71,7 @@
         "liblog",
         "libbinder",
         "libaudioclient",
+        "libaudiomanager",
         "libmedialogservice",
         "libmediametrics",
         "libmediautils",
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 7a9027c..a68f63e 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -54,7 +54,7 @@
 #include <cutils/properties.h>
 
 #include <system/audio.h>
-#include <audiomanager/AudioManager.h>
+#include <audiomanager/IAudioManager.h>
 
 #include "AudioFlinger.h"
 #include "NBAIO_Tee.h"
@@ -114,11 +114,12 @@
 using media::audio::common::AudioMMapPolicyType;
 using android::content::AttributionSourceState;
 
-static const char kDeadlockedString[] = "AudioFlinger may be deadlocked\n";
-static const char kHardwareLockedString[] = "Hardware lock is taken\n";
-static const char kClientLockedString[] = "Client lock is taken\n";
-static const char kNoEffectsFactory[] = "Effects Factory is absent\n";
+static constexpr char kDeadlockedString[] = "AudioFlinger may be deadlocked\n";
+static constexpr char kHardwareLockedString[] = "Hardware lock is taken\n";
+static constexpr char kClientLockedString[] = "Client lock is taken\n";
+static constexpr char kNoEffectsFactory[] = "Effects Factory is absent\n";
 
+static constexpr char kAudioServiceName[] = "audio";
 
 nsecs_t AudioFlinger::mStandbyTimeInNsecs = kDefaultStandbyTimeInNsecs;
 
@@ -2722,9 +2723,28 @@
         ThreadBase *thread = (ThreadBase *)mMmapThreads.valueAt(i).get();
         thread->systemReady();
     }
+
+    // Java services are ready, so we can create a reference to AudioService
+    getOrCreateAudioManager();
+
     return NO_ERROR;
 }
 
+sp<IAudioManager> AudioFlinger::getOrCreateAudioManager()
+{
+    if (mAudioManager.load() == nullptr) {
+        // use checkService() to avoid blocking
+        sp<IBinder> binder =
+            defaultServiceManager()->checkService(String16(kAudioServiceName));
+        if (binder != nullptr) {
+            mAudioManager = interface_cast<IAudioManager>(binder);
+        } else {
+            ALOGE("%s(): binding to audio service failed.", __func__);
+        }
+    }
+    return mAudioManager.load();
+}
+
 status_t AudioFlinger::getMicrophones(std::vector<media::MicrophoneInfo> *microphones)
 {
     AutoMutex lock(mHardwareLock);
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index fc4c807..08594e2 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -117,6 +117,7 @@
 class DevicesFactoryHalInterface;
 class EffectsFactoryHalInterface;
 class FastMixer;
+class IAudioManager;
 class PassthruBufferProvider;
 class RecordBufferConverter;
 class ServerProxy;
@@ -988,6 +989,8 @@
                                       size_t rejectedKVPSize, const String8& rejectedKVPs,
                                       uid_t callingUid);
 
+    sp<IAudioManager> getOrCreateAudioManager();
+
 public:
     // These methods read variables atomically without mLock,
     // though the variables are updated with mLock.
@@ -1029,6 +1032,9 @@
              std::vector<media::audio::common::AudioMMapPolicyInfo>> mPolicyInfos;
     int32_t mAAudioBurstsPerBuffer = 0;
     int32_t mAAudioHwBurstMinMicros = 0;
+
+    /** Interface for interacting with the AudioService. */
+    mediautils::atomic_sp<IAudioManager>       mAudioManager;
 };
 
 #undef INCLUDING_FROM_AUDIOFLINGER_H
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 6a138bb..e52d1ee 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -204,6 +204,12 @@
     float getSpeed() const { return mSpeed; }
     bool isSpatialized() const override { return mIsSpatialized; }
 
+    /**
+     * Updates the mute state and notifies the audio service. Call this only when holding player
+     * thread lock.
+     */
+    void processMuteEvent_l(const sp<IAudioManager>& audioManager, mute_state_t muteState);
+
 protected:
     // for numerous
     friend class PlaybackThread;
@@ -354,6 +360,11 @@
     TeePatches  mTeePatches;
     const float         mSpeed;
     const bool          mIsSpatialized;
+
+    // TODO: replace PersistableBundle with own struct
+    // access these two variables only when holding player thread lock.
+    std::unique_ptr<os::PersistableBundle> mMuteEventExtras;
+    mute_state_t        mMuteState;
 };  // end of Track
 
 
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index e127431..77e2421 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -31,6 +31,7 @@
 #include <sys/syscall.h>
 #include <cutils/bitops.h>
 #include <cutils/properties.h>
+#include <binder/PersistableBundle.h>
 #include <media/AudioContainers.h>
 #include <media/AudioDeviceTypeAddr.h>
 #include <media/AudioParameter.h>
@@ -178,6 +179,11 @@
 // Direct output thread minimum sleep time in idle or active(underrun) state
 static const nsecs_t kDirectMinSleepTimeUs = 10000;
 
+// Minimum amount of time between checking to see if the timestamp is advancing
+// for underrun detection. If we check too frequently, we may not detect a
+// timestamp update and will falsely detect underrun.
+static const nsecs_t kMinimumTimeBetweenTimestampChecksNs = 150 /* ms */ * 1000;
+
 // The universal constant for ubiquitous 20ms value. The value of 20ms seems to provide a good
 // balance between power consumption and latency, and allows threads to be scheduled reliably
 // by the CFS scheduler.
@@ -2040,7 +2046,8 @@
         mFastTrackAvailMask(((1 << FastMixerState::sMaxFastTracks) - 1) & ~1),
         mHwSupportsPause(false), mHwPaused(false), mFlushPending(false),
         mLeftVolFloat(-1.0), mRightVolFloat(-1.0),
-        mDownStreamPatch{}
+        mDownStreamPatch{},
+        mIsTimestampAdvancing(kMinimumTimeBetweenTimestampChecksNs)
 {
     snprintf(mThreadName, kThreadNameLength, "AudioOut_%X", id);
     mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mThreadName);
@@ -5369,6 +5376,12 @@
                     volume = masterVolume * mStreamTypes[track->streamType()].volume;
                 }
 
+                track->processMuteEvent_l(mAudioFlinger->getOrCreateAudioManager(),
+                    /*muteState=*/{masterVolume == 0.f,
+                                   mStreamTypes[track->streamType()].volume == 0.f,
+                                   mStreamTypes[track->streamType()].mute,
+                                   track->isPlaybackRestricted()});
+
                 handleVoipVolume_l(&volume);
 
                 // cache the combined master volume and stream type volume for fast mixer; this
@@ -5533,6 +5546,12 @@
                 v = 0;
             }
 
+            track->processMuteEvent_l(mAudioFlinger->getOrCreateAudioManager(),
+                /*muteState=*/{masterVolume == 0.f,
+                               mStreamTypes[track->streamType()].volume == 0.f,
+                               mStreamTypes[track->streamType()].mute,
+                               track->isPlaybackRestricted()});
+
             handleVoipVolume_l(&v);
 
             if (track->isPausing()) {
@@ -5910,18 +5929,35 @@
     return trackCount;
 }
 
-bool AudioFlinger::PlaybackThread::checkRunningTimestamp()
+bool AudioFlinger::PlaybackThread::IsTimestampAdvancing::check(AudioStreamOut * output)
 {
+    // Check the timestamp to see if it's advancing once every 150ms. If we check too frequently, we
+    // could falsely detect that the frame position has stalled due to underrun because we haven't
+    // given the Audio HAL enough time to update.
+    const nsecs_t nowNs = systemTime();
+    if (nowNs - mPreviousNs < mMinimumTimeBetweenChecksNs) {
+        return mLatchedValue;
+    }
+    mPreviousNs = nowNs;
+    mLatchedValue = false;
+    // Determine if the presentation position is still advancing.
     uint64_t position = 0;
     struct timespec unused;
-    const status_t ret = mOutput->getPresentationPosition(&position, &unused);
+    const status_t ret = output->getPresentationPosition(&position, &unused);
     if (ret == NO_ERROR) {
-        if (position != mLastCheckedTimestampPosition) {
-            mLastCheckedTimestampPosition = position;
-            return true;
+        if (position != mPreviousPosition) {
+            mPreviousPosition = position;
+            mLatchedValue = true;
         }
     }
-    return false;
+    return mLatchedValue;
+}
+
+void AudioFlinger::PlaybackThread::IsTimestampAdvancing::clear()
+{
+    mLatchedValue = true;
+    mPreviousPosition = 0;
+    mPreviousNs = 0;
 }
 
 // isTrackAllowed_l() must be called with ThreadBase::mLock held
@@ -6146,6 +6182,12 @@
         right *= v * mMasterBalanceRight;
     }
 
+    track->processMuteEvent_l(mAudioFlinger->getOrCreateAudioManager(),
+        /*muteState=*/{mMasterMute,
+                       mStreamTypes[track->streamType()].volume == 0.f,
+                       mStreamTypes[track->streamType()].mute,
+                       track->isPlaybackRestricted()});
+
     if (lastTrack) {
         track->setFinalVolume((left + right) / 2.f);
         if (left != mLeftVolFloat || right != mRightVolFloat) {
@@ -6356,9 +6398,9 @@
                 // No buffers for this track. Give it a few chances to
                 // fill a buffer, then remove it from active list.
                 // Only consider last track started for mixer state control
+                bool isTimestampAdvancing = mIsTimestampAdvancing.check(mOutput);
                 if (--(track->mRetryCount) <= 0) {
-                    const bool running = checkRunningTimestamp();
-                    if (running) { // still running, give us more time.
+                    if (isTimestampAdvancing) { // HAL is still playing audio, give us more time.
                         track->mRetryCount = kMaxTrackRetriesOffload;
                     } else {
                         ALOGV("BUFFER TIMEOUT: remove track(%d) from active list", trackId);
@@ -6939,9 +6981,9 @@
             } else {
                 // No buffers for this track. Give it a few chances to
                 // fill a buffer, then remove it from active list.
+                bool isTimestampAdvancing = mIsTimestampAdvancing.check(mOutput);
                 if (--(track->mRetryCount) <= 0) {
-                    const bool running = checkRunningTimestamp();
-                    if (running) { // still running, give us more time.
+                    if (isTimestampAdvancing) { // HAL is still playing audio, give us more time.
                         track->mRetryCount = kMaxTrackRetriesOffload;
                     } else {
                         ALOGV("OffloadThread: BUFFER TIMEOUT: remove track(%d) from active list",
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index fc37bd2..b477d65 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -624,7 +624,7 @@
     virtual     void        dumpTracks_l(int fd __unused, const Vector<String16>& args __unused) { }
 
 
-    friend class AudioFlinger;      // for mEffectChains
+    friend class AudioFlinger;      // for mEffectChains and mAudioManager
 
                 const type_t            mType;
 
@@ -1177,7 +1177,7 @@
     volatile int32_t                mSuspended;
 
     int64_t                         mBytesWritten;
-    int64_t                         mFramesWritten; // not reset on standby
+    std::atomic<int64_t>            mFramesWritten; // not reset on standby
     int64_t                         mLastFramesWritten = -1; // track changes in timestamp
                                                              // server frames written.
     int64_t                         mSuspendedFrames; // not reset on standby
@@ -1391,6 +1391,7 @@
     virtual     bool        hasFastMixer() const = 0;
     virtual     FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex __unused) const
                                 { FastTrackUnderruns dummy; return dummy; }
+                const std::atomic<int64_t>& framesWritten() const { return mFramesWritten; }
 
 protected:
                 // accessed by both binder threads and within threadLoop(), lock on mutex needed
@@ -1408,13 +1409,38 @@
 
                 std::atomic_bool mCheckOutputStageEffects{};
 
-                // A differential check on the timestamps to see if there is a change in the
-                // timestamp frame position between the last call to checkRunningTimestamp.
-                uint64_t mLastCheckedTimestampPosition = ~0LL;
 
-                bool checkRunningTimestamp();
+                // Provides periodic checking for timestamp advancement for underrun detection.
+                class IsTimestampAdvancing {
+                public:
+                    // The timestamp will not be checked any faster than the specified time.
+                    IsTimestampAdvancing(nsecs_t minimumTimeBetweenChecksNs)
+                        :   mMinimumTimeBetweenChecksNs(minimumTimeBetweenChecksNs)
+                    {
+                        clear();
+                    }
+                    // Check if the presentation position has advanced in the last periodic time.
+                    bool check(AudioStreamOut * output);
+                    // Clear the internal state when the playback state changes for the output
+                    // stream.
+                    void clear();
+                private:
+                    // The minimum time between timestamp checks.
+                    const nsecs_t mMinimumTimeBetweenChecksNs;
+                    // Add differential check on the timestamps to see if there is a change in the
+                    // timestamp frame position between the last call to check.
+                    uint64_t mPreviousPosition;
+                    // The time at which the last check occurred, to ensure we don't check too
+                    // frequently, giving the Audio HAL enough time to update its timestamps.
+                    nsecs_t mPreviousNs;
+                    // The valued is latched so we don't check timestamps too frequently.
+                    bool mLatchedValue;
+                };
+                IsTimestampAdvancing mIsTimestampAdvancing;
 
-    virtual     void flushHw_l() { mLastCheckedTimestampPosition = ~0LL; }
+    virtual     void flushHw_l() {
+                    mIsTimestampAdvancing.clear();
+                }
 };
 
 class MixerThread : public PlaybackThread {
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 2dfa1b2..3f9c306 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -1094,12 +1094,22 @@
                     __func__, mId, (int)mThreadIoHandle);
         }
 
-        // states to reset position info for non-offloaded/direct tracks
-        if (!isOffloaded() && !isDirect()
+        PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+
+        // states to reset position info for pcm tracks
+        if (audio_is_linear_pcm(mFormat)
                 && (state == IDLE || state == STOPPED || state == FLUSHED)) {
             mFrameMap.reset();
+
+            if (!isFastTrack() && (isDirect() || isOffloaded())) {
+                // Start point of track -> sink frame map. If the HAL returns a
+                // frame position smaller than the first written frame in
+                // updateTrackFrameInfo, the timestamp can be interpolated
+                // instead of using a larger value.
+                mFrameMap.push(mAudioTrackServerProxy->framesReleased(),
+                               playbackThread->framesWritten());
+            }
         }
-        PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
         if (isFastTrack()) {
             // refresh fast track underruns on start because that field is never cleared
             // by the fast mixer; furthermore, the same track can be recycled, i.e. start
@@ -1484,6 +1494,39 @@
     }
 }
 
+// must be called with player thread lock held
+void AudioFlinger::PlaybackThread::Track::processMuteEvent_l(const sp<
+    IAudioManager>& audioManager, mute_state_t muteState)
+{
+    if (mMuteState == muteState) {
+        // mute state did not change, do nothing
+        return;
+    }
+
+    status_t result = UNKNOWN_ERROR;
+    if (audioManager && mPortId != AUDIO_PORT_HANDLE_NONE) {
+        if (mMuteEventExtras == nullptr) {
+            mMuteEventExtras = std::make_unique<os::PersistableBundle>();
+        }
+        mMuteEventExtras->putInt(String16(kExtraPlayerEventMuteKey),
+                                 static_cast<int>(muteState));
+
+        result = audioManager->portEvent(mPortId,
+                                         PLAYER_UPDATE_MUTED,
+                                         mMuteEventExtras);
+    }
+
+    if (result == OK) {
+        mMuteState = muteState;
+    } else {
+        ALOGW("%s(%d): cannot process mute state for port ID %d, status error %d",
+              __func__,
+              id(),
+              mPortId,
+              result);
+    }
+}
+
 status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& timestamp)
 {
     if (!isOffloaded() && !isDirect()) {
diff --git a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
index b036e12..60b16ac 100644
--- a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
+++ b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
@@ -25,11 +25,11 @@
 const engineConfig::ProductStrategies gOrderedStrategies = {
     {"STRATEGY_PHONE",
      {
-         {"phone", AUDIO_STREAM_VOICE_CALL, "AUDIO_STREAM_VOICE_CALL",
+         {AUDIO_STREAM_VOICE_CALL, "AUDIO_STREAM_VOICE_CALL",
           {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_SOURCE_DEFAULT,
             AUDIO_FLAG_NONE, ""}},
          },
-         {"sco", AUDIO_STREAM_BLUETOOTH_SCO, "AUDIO_STREAM_BLUETOOTH_SCO",
+         {AUDIO_STREAM_BLUETOOTH_SCO, "AUDIO_STREAM_BLUETOOTH_SCO",
           {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_SCO,
             ""}},
          }
@@ -37,11 +37,11 @@
     },
     {"STRATEGY_SONIFICATION",
      {
-         {"ring", AUDIO_STREAM_RING, "AUDIO_STREAM_RING",
+         {AUDIO_STREAM_RING, "AUDIO_STREAM_RING",
           {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE,
             AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}}
          },
-         {"alarm", AUDIO_STREAM_ALARM, "AUDIO_STREAM_ALARM",
+         {AUDIO_STREAM_ALARM, "AUDIO_STREAM_ALARM",
           {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ALARM, AUDIO_SOURCE_DEFAULT,
             AUDIO_FLAG_NONE, ""}},
          }
@@ -49,7 +49,7 @@
     },
     {"STRATEGY_ENFORCED_AUDIBLE",
      {
-         {"", AUDIO_STREAM_ENFORCED_AUDIBLE, "AUDIO_STREAM_ENFORCED_AUDIBLE",
+         {AUDIO_STREAM_ENFORCED_AUDIBLE, "AUDIO_STREAM_ENFORCED_AUDIBLE",
           {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT,
             AUDIO_FLAG_AUDIBILITY_ENFORCED, ""}}
          }
@@ -57,7 +57,7 @@
     },
     {"STRATEGY_ACCESSIBILITY",
      {
-         {"", AUDIO_STREAM_ACCESSIBILITY, "AUDIO_STREAM_ACCESSIBILITY",
+         {AUDIO_STREAM_ACCESSIBILITY, "AUDIO_STREAM_ACCESSIBILITY",
           {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY,
             AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}}
          }
@@ -65,7 +65,7 @@
     },
     {"STRATEGY_SONIFICATION_RESPECTFUL",
      {
-         {"", AUDIO_STREAM_NOTIFICATION, "AUDIO_STREAM_NOTIFICATION",
+         {AUDIO_STREAM_NOTIFICATION, "AUDIO_STREAM_NOTIFICATION",
           {
               {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION, AUDIO_SOURCE_DEFAULT,
                AUDIO_FLAG_NONE, ""},
@@ -77,11 +77,11 @@
     },
     {"STRATEGY_MEDIA",
      {
-         {"assistant", AUDIO_STREAM_ASSISTANT, "AUDIO_STREAM_ASSISTANT",
+         {AUDIO_STREAM_ASSISTANT, "AUDIO_STREAM_ASSISTANT",
           {{AUDIO_CONTENT_TYPE_SPEECH, AUDIO_USAGE_ASSISTANT,
             AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}}
          },
-         {"music", AUDIO_STREAM_MUSIC, "AUDIO_STREAM_MUSIC",
+         {AUDIO_STREAM_MUSIC, "AUDIO_STREAM_MUSIC",
           {
               {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_MEDIA, AUDIO_SOURCE_DEFAULT,
                AUDIO_FLAG_NONE, ""},
@@ -95,7 +95,7 @@
                AUDIO_FLAG_NONE, ""}
           },
          },
-         {"system", AUDIO_STREAM_SYSTEM, "AUDIO_STREAM_SYSTEM",
+         {AUDIO_STREAM_SYSTEM, "AUDIO_STREAM_SYSTEM",
           {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANCE_SONIFICATION,
             AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}}
          }
@@ -103,7 +103,7 @@
     },
     {"STRATEGY_DTMF",
      {
-         {"", AUDIO_STREAM_DTMF, "AUDIO_STREAM_DTMF",
+         {AUDIO_STREAM_DTMF, "AUDIO_STREAM_DTMF",
           {
               {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING,
                AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}
@@ -113,7 +113,7 @@
     },
     {"STRATEGY_CALL_ASSISTANT",
      {
-         {"", AUDIO_STREAM_CALL_ASSISTANT, "AUDIO_STREAM_CALL_ASSISTANT",
+         {AUDIO_STREAM_CALL_ASSISTANT, "AUDIO_STREAM_CALL_ASSISTANT",
           {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_CALL_ASSISTANT, AUDIO_SOURCE_DEFAULT,
             AUDIO_FLAG_NONE, ""}}
          }
@@ -121,7 +121,7 @@
     },
     {"STRATEGY_TRANSMITTED_THROUGH_SPEAKER",
      {
-         {"", AUDIO_STREAM_TTS, "AUDIO_STREAM_TTS",
+         {AUDIO_STREAM_TTS, "AUDIO_STREAM_TTS",
           {
               {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT,
                 AUDIO_FLAG_BEACON, ""},
@@ -140,7 +140,7 @@
 const engineConfig::ProductStrategies gOrderedSystemStrategies = {
     {"rerouting",
      {
-         {"", AUDIO_STREAM_REROUTING, "AUDIO_STREAM_REROUTING",
+         {AUDIO_STREAM_REROUTING, "AUDIO_STREAM_REROUTING",
           {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_VIRTUAL_SOURCE, AUDIO_SOURCE_DEFAULT,
             AUDIO_FLAG_NONE, ""}}
          }
@@ -148,7 +148,7 @@
     },
     {"patch",
      {
-         {"", AUDIO_STREAM_PATCH, "AUDIO_STREAM_PATCH",
+         {AUDIO_STREAM_PATCH, "AUDIO_STREAM_PATCH",
           {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT,
             AUDIO_FLAG_NONE, ""}}
          }
diff --git a/services/audiopolicy/engine/config/include/EngineConfig.h b/services/audiopolicy/engine/config/include/EngineConfig.h
index 2ebb7df..8036eea 100644
--- a/services/audiopolicy/engine/config/include/EngineConfig.h
+++ b/services/audiopolicy/engine/config/include/EngineConfig.h
@@ -35,7 +35,6 @@
 using StreamVector = std::vector<audio_stream_type_t>;
 
 struct AttributesGroup {
-    std::string name;
     audio_stream_type_t stream;
     std::string volumeGroup;
     AttributesVector attributesVect;
diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp
index 6f560d5..ac117f0 100644
--- a/services/audiopolicy/engine/config/src/EngineConfig.cpp
+++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp
@@ -57,7 +57,6 @@
     static constexpr const char *collectionTag = "AttributesGroups";
 
     struct Attributes {
-        static constexpr const char *name = "name";
         static constexpr const char *streamType = "streamType";
         static constexpr const char *volumeGroup = "volumeGroup";
     };
@@ -313,12 +312,6 @@
 status_t AttributesGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *child,
                                             Collection &attributesGroup)
 {
-    std::string name = getXmlAttribute(child, Attributes::name);
-    if (name.empty()) {
-        ALOGV("AttributesGroupTraits No attribute %s found", Attributes::name);
-    }
-    ALOGV("%s: %s = %s", __FUNCTION__, Attributes::name, name.c_str());
-
     std::string volumeGroup = getXmlAttribute(child, Attributes::volumeGroup);
     if (volumeGroup.empty()) {
         ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::volumeGroup);
@@ -339,7 +332,7 @@
     AttributesVector attributesVect;
     deserializeAttributesCollection(doc, child, attributesVect);
 
-    attributesGroup.push_back({name, streamType, volumeGroup, attributesVect});
+    attributesGroup.push_back({streamType, volumeGroup, attributesVect});
     return NO_ERROR;
 }
 
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index 08d6453..d67e6c4 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -78,7 +78,7 @@
     case AUDIO_POLICY_FORCE_FOR_COMMUNICATION:
         if (config != AUDIO_POLICY_FORCE_SPEAKER && config != AUDIO_POLICY_FORCE_BT_SCO &&
             config != AUDIO_POLICY_FORCE_NONE) {
-            ALOGW("setForceUse() invalid config %d for FOR_COMMUNICATION", config);
+            ALOGW("setForceUse() invalid config %d for COMMUNICATION", config);
             return BAD_VALUE;
         }
         break;
@@ -88,14 +88,14 @@
             config != AUDIO_POLICY_FORCE_ANALOG_DOCK &&
             config != AUDIO_POLICY_FORCE_DIGITAL_DOCK && config != AUDIO_POLICY_FORCE_NONE &&
             config != AUDIO_POLICY_FORCE_NO_BT_A2DP && config != AUDIO_POLICY_FORCE_SPEAKER ) {
-            ALOGW("setForceUse() invalid config %d for FOR_MEDIA", config);
+            ALOGW("setForceUse() invalid config %d for MEDIA", config);
             return BAD_VALUE;
         }
         break;
     case AUDIO_POLICY_FORCE_FOR_RECORD:
         if (config != AUDIO_POLICY_FORCE_BT_SCO && config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY &&
             config != AUDIO_POLICY_FORCE_NONE) {
-            ALOGW("setForceUse() invalid config %d for FOR_RECORD", config);
+            ALOGW("setForceUse() invalid config %d for RECORD", config);
             return BAD_VALUE;
         }
         break;
@@ -105,19 +105,22 @@
             config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY &&
             config != AUDIO_POLICY_FORCE_ANALOG_DOCK &&
             config != AUDIO_POLICY_FORCE_DIGITAL_DOCK) {
-            ALOGW("setForceUse() invalid config %d for FOR_DOCK", config);
+            ALOGW("setForceUse() invalid config %d for DOCK", config);
+            return BAD_VALUE;
         }
         break;
     case AUDIO_POLICY_FORCE_FOR_SYSTEM:
         if (config != AUDIO_POLICY_FORCE_NONE &&
             config != AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) {
-            ALOGW("setForceUse() invalid config %d for FOR_SYSTEM", config);
+            ALOGW("setForceUse() invalid config %d for SYSTEM", config);
+            return BAD_VALUE;
         }
         break;
     case AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO:
         if (config != AUDIO_POLICY_FORCE_NONE &&
             config != AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED) {
             ALOGW("setForceUse() invalid config %d for HDMI_SYSTEM_AUDIO", config);
+            return BAD_VALUE;
         }
         break;
     case AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND:
@@ -131,13 +134,13 @@
         break;
     case AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING:
         if (config != AUDIO_POLICY_FORCE_BT_SCO && config != AUDIO_POLICY_FORCE_NONE) {
-            ALOGW("setForceUse() invalid config %d for FOR_VIBRATE_RINGING", config);
+            ALOGW("setForceUse() invalid config %d for VIBRATE_RINGING", config);
             return BAD_VALUE;
         }
         break;
     default:
         ALOGW("setForceUse() invalid usage %d", usage);
-        break; // TODO return BAD_VALUE?
+        return BAD_VALUE;
     }
     return EngineBase::setForceUse(usage, config);
 }
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index dea329a..c21c6a2 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -7850,6 +7850,9 @@
 
     addOutput(output, desc);
 
+    sp<DeviceDescriptor> speaker = mAvailableOutputDevices.getDevice(
+            AUDIO_DEVICE_OUT_SPEAKER, String8(""), AUDIO_FORMAT_DEFAULT);
+
     if (audio_is_remote_submix_device(deviceType) && address != "0") {
         sp<AudioPolicyMix> policyMix;
         if (mPolicyMixes.getAudioPolicyMix(deviceType, address, policyMix) == NO_ERROR) {
@@ -7860,13 +7863,13 @@
                     address.string());
         }
 
-    } else if (hasPrimaryOutput() && profile->getModule()
-                != mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY)
+    } else if (hasPrimaryOutput() && speaker != nullptr
+            && mPrimaryOutput->supportsDevice(speaker) && !desc->supportsDevice(speaker)
             && ((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0)) {
         // no duplicated output for:
         // - direct outputs
         // - outputs used by dynamic policy mixes
-        // - outputs opened on the primary HW module
+        // - outputs that supports SPEAKER while the primary output does not.
         audio_io_handle_t duplicatedOutput = AUDIO_IO_HANDLE_NONE;
 
         //TODO: configure audio effect output stage here
diff --git a/services/audiopolicy/service/Spatializer.cpp b/services/audiopolicy/service/Spatializer.cpp
index b24ab14..20c7a5d 100644
--- a/services/audiopolicy/service/Spatializer.cpp
+++ b/services/audiopolicy/service/Spatializer.cpp
@@ -84,6 +84,7 @@
         kWhatOnFramesProcessed,    // AudioEffect::EVENT_FRAMES_PROCESSED
         kWhatOnHeadToStagePose,    // SpatializerPoseController::Listener::onHeadToStagePose
         kWhatOnActualModeChange,   // SpatializerPoseController::Listener::onActualModeChange
+        kWhatOnLatencyModesChanged, // Spatializer::onSupportedLatencyModesChanged
     };
     static constexpr const char *kNumFramesKey = "numFrames";
     static constexpr const char *kModeKey = "mode";
@@ -93,15 +94,27 @@
     static constexpr const char *kRotation0Key = "rotation0";
     static constexpr const char *kRotation1Key = "rotation1";
     static constexpr const char *kRotation2Key = "rotation2";
+    static constexpr const char *kLatencyModesKey = "latencyModes";
+
+    class LatencyModes : public RefBase {
+    public:
+        LatencyModes(audio_io_handle_t output,
+                const std::vector<audio_latency_mode_t>& latencyModes)
+            : mOutput(output), mLatencyModes(latencyModes) {}
+        ~LatencyModes() = default;
+
+        audio_io_handle_t mOutput;
+        std::vector<audio_latency_mode_t> mLatencyModes;
+    };
 
     void onMessageReceived(const sp<AMessage> &msg) override {
+        sp<Spatializer> spatializer = mSpatializer.promote();
+        if (spatializer == nullptr) {
+            ALOGW("%s: Cannot promote spatializer", __func__);
+            return;
+        }
         switch (msg->what()) {
             case kWhatOnFramesProcessed: {
-                sp<Spatializer> spatializer = mSpatializer.promote();
-                if (spatializer == nullptr) {
-                    ALOGW("%s: Cannot promote spatializer", __func__);
-                    return;
-                }
                 int numFrames;
                 if (!msg->findInt32(kNumFramesKey, &numFrames)) {
                     ALOGE("%s: Cannot find num frames!", __func__);
@@ -112,11 +125,6 @@
                 }
                 } break;
             case kWhatOnHeadToStagePose: {
-                sp<Spatializer> spatializer = mSpatializer.promote();
-                if (spatializer == nullptr) {
-                    ALOGW("%s: Cannot promote spatializer", __func__);
-                    return;
-                }
                 std::vector<float> headToStage(sHeadPoseKeys.size());
                 for (size_t i = 0 ; i < sHeadPoseKeys.size(); i++) {
                     if (!msg->findFloat(sHeadPoseKeys[i], &headToStage[i])) {
@@ -127,18 +135,25 @@
                 spatializer->onHeadToStagePoseMsg(headToStage);
                 } break;
             case kWhatOnActualModeChange: {
-                sp<Spatializer> spatializer = mSpatializer.promote();
-                if (spatializer == nullptr) {
-                    ALOGW("%s: Cannot promote spatializer", __func__);
-                    return;
-                }
                 int mode;
-                if (!msg->findInt32(EngineCallbackHandler::kModeKey, &mode)) {
+                if (!msg->findInt32(kModeKey, &mode)) {
                     ALOGE("%s: Cannot find actualMode!", __func__);
                     return;
                 }
                 spatializer->onActualModeChangeMsg(static_cast<HeadTrackingMode>(mode));
                 } break;
+
+            case kWhatOnLatencyModesChanged: {
+                sp<RefBase> object;
+                if (!msg->findObject(kLatencyModesKey, &object)) {
+                    ALOGE("%s: Cannot find latency modes!", __func__);
+                    return;
+                }
+                sp<LatencyModes> latencyModes = static_cast<LatencyModes*>(object.get());
+                spatializer->onSupportedLatencyModesChangedMsg(
+                    latencyModes->mOutput, std::move(latencyModes->mLatencyModes));
+                } break;
+
             default:
                 LOG_ALWAYS_FATAL("Invalid callback message %d", msg->what());
         }
@@ -784,9 +799,21 @@
 
 void Spatializer::onSupportedLatencyModesChanged(
         audio_io_handle_t output, const std::vector<audio_latency_mode_t>& modes) {
+    ALOGV("%s output %d num modes %zu", __func__, (int)output, modes.size());
+    sp<AMessage> msg =
+            new AMessage(EngineCallbackHandler::kWhatOnLatencyModesChanged, mHandler);
+    msg->setObject(EngineCallbackHandler::kLatencyModesKey,
+        sp<EngineCallbackHandler::LatencyModes>::make(output, modes));
+    msg->post();
+}
+
+void Spatializer::onSupportedLatencyModesChangedMsg(
+        audio_io_handle_t output, std::vector<audio_latency_mode_t>&& modes) {
     std::lock_guard lock(mLock);
+    ALOGV("%s output %d mOutput %d num modes %zu",
+            __func__, (int)output, (int)mOutput, modes.size());
     if (output == mOutput) {
-        mSupportedLatencyModes = modes;
+        mSupportedLatencyModes = std::move(modes);
         checkSensorsState_l();
     }
 }
diff --git a/services/audiopolicy/service/Spatializer.h b/services/audiopolicy/service/Spatializer.h
index 4aebcea..bc95fe4 100644
--- a/services/audiopolicy/service/Spatializer.h
+++ b/services/audiopolicy/service/Spatializer.h
@@ -168,6 +168,8 @@
 
     void onHeadToStagePoseMsg(const std::vector<float>& headToStage);
     void onActualModeChangeMsg(media::HeadTrackingMode mode);
+    void onSupportedLatencyModesChangedMsg(
+            audio_io_handle_t output, std::vector<audio_latency_mode_t>&& modes);
 
     static constexpr int kMaxEffectParamValues = 10;
     /**
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index 6f43270..f926b88 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -105,11 +105,6 @@
           TClientBase::mCameraIdStr.string());
     status_t res;
 
-    // Verify ops permissions
-    res = TClientBase::startCameraOps();
-    if (res != OK) {
-        return res;
-    }
     IPCTransport providerTransport = IPCTransport::INVALID;
     res = providerPtr->getCameraIdIPCTransport(TClientBase::mCameraIdStr.string(),
             &providerTransport);
@@ -145,6 +140,14 @@
         return res;
     }
 
+    // Verify ops permissions
+    res = TClientBase::startCameraOps();
+    if (res != OK) {
+        TClientBase::finishCameraOps();
+        mDevice.clear();
+        return res;
+    }
+
     wp<NotificationListener> weakThis(this);
     res = mDevice->setNotifyCallback(weakThis);
     if (res != OK) {