audio: add implementation for TV related API

1. TunerConfiguration.
2. AudioDescriptionMixLevel getter/setter.
3. DualMonoMode getter/setter.
4. PlaybackRate getter/setter.

Test: TIS player with instrumented HAL
Test: atest AudioTrackTest#testTunerConfiguration
Test: atest AudioTrackTest#testDualMonoMode
Test: atest AudioTrackTest#testAudioDescriptionMixLevel
Test: atest AudioManagerTest#testGetAdditionalOutputDeviceDelay
Bug: 173482792
Change-Id: Idb22ce6714fa240b2b4b3b8637b16d3a51228fa1
diff --git a/media/libaudioclient/AidlConversion.cpp b/media/libaudioclient/AidlConversion.cpp
index 1ff4678..4c0db3e 100644
--- a/media/libaudioclient/AidlConversion.cpp
+++ b/media/libaudioclient/AidlConversion.cpp
@@ -2127,4 +2127,78 @@
     return convertReinterpret<int32_t>(legacy);
 }
 
+ConversionResult<audio_dual_mono_mode_t>
+aidl2legacy_AudioDualMonoMode_audio_dual_mono_mode_t(media::AudioDualMonoMode aidl) {
+    switch (aidl) {
+        case media::AudioDualMonoMode::OFF:
+            return AUDIO_DUAL_MONO_MODE_OFF;
+        case media::AudioDualMonoMode::LR:
+            return AUDIO_DUAL_MONO_MODE_LR;
+        case media::AudioDualMonoMode::LL:
+            return AUDIO_DUAL_MONO_MODE_LL;
+        case media::AudioDualMonoMode::RR:
+            return AUDIO_DUAL_MONO_MODE_RR;
+    }
+    return unexpected(BAD_VALUE);
+}
+
+ConversionResult<media::AudioDualMonoMode>
+legacy2aidl_audio_dual_mono_mode_t_AudioDualMonoMode(audio_dual_mono_mode_t legacy) {
+    switch (legacy) {
+        case AUDIO_DUAL_MONO_MODE_OFF:
+            return media::AudioDualMonoMode::OFF;
+        case AUDIO_DUAL_MONO_MODE_LR:
+            return media::AudioDualMonoMode::LR;
+        case AUDIO_DUAL_MONO_MODE_LL:
+            return media::AudioDualMonoMode::LL;
+        case AUDIO_DUAL_MONO_MODE_RR:
+            return media::AudioDualMonoMode::RR;
+    }
+    return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_timestretch_fallback_mode_t>
+aidl2legacy_int32_t_audio_timestretch_fallback_mode_t(int32_t aidl) {
+    return convertReinterpret<audio_timestretch_fallback_mode_t>(aidl);
+}
+
+ConversionResult<int32_t>
+legacy2aidl_audio_timestretch_fallback_mode_t_int32_t(audio_timestretch_fallback_mode_t legacy) {
+    return convertReinterpret<int32_t>(legacy);
+}
+
+ConversionResult<audio_timestretch_stretch_mode_t>
+aidl2legacy_int32_t_audio_timestretch_stretch_mode_t(int32_t aidl) {
+    return convertReinterpret<audio_timestretch_stretch_mode_t>(aidl);
+}
+
+ConversionResult<int32_t>
+legacy2aidl_audio_timestretch_stretch_mode_t_int32_t(audio_timestretch_stretch_mode_t legacy) {
+    return convertReinterpret<int32_t>(legacy);
+}
+
+ConversionResult<audio_playback_rate_t>
+aidl2legacy_AudioPlaybackRate_audio_playback_rate_t(const media::AudioPlaybackRate& aidl) {
+    audio_playback_rate_t legacy;
+    legacy.mSpeed = aidl.speed;
+    legacy.mPitch = aidl.pitch;
+    legacy.mFallbackMode = VALUE_OR_RETURN(
+            aidl2legacy_int32_t_audio_timestretch_fallback_mode_t(aidl.fallbackMode));
+    legacy.mStretchMode = VALUE_OR_RETURN(
+            aidl2legacy_int32_t_audio_timestretch_stretch_mode_t(aidl.stretchMode));
+    return legacy;
+}
+
+ConversionResult<media::AudioPlaybackRate>
+legacy2aidl_audio_playback_rate_t_AudioPlaybackRate(const audio_playback_rate_t& legacy) {
+    media::AudioPlaybackRate aidl;
+    aidl.speed = legacy.mSpeed;
+    aidl.pitch = legacy.mPitch;
+    aidl.fallbackMode = VALUE_OR_RETURN(
+            legacy2aidl_audio_timestretch_fallback_mode_t_int32_t(legacy.mFallbackMode));
+    aidl.stretchMode = VALUE_OR_RETURN(
+            legacy2aidl_audio_timestretch_stretch_mode_t_int32_t(legacy.mStretchMode));
+    return aidl;
+}
+
 }  // namespace android
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index a1708ee..004ae39 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -295,6 +295,7 @@
         "aidl/android/media/AudioConfigBase.aidl",
         "aidl/android/media/AudioContentType.aidl",
         "aidl/android/media/AudioDevice.aidl",
+        "aidl/android/media/AudioDualMonoMode.aidl",
         "aidl/android/media/AudioEncapsulationMode.aidl",
         "aidl/android/media/AudioEncapsulationMetadataType.aidl",
         "aidl/android/media/AudioFlag.aidl",
@@ -310,6 +311,7 @@
         "aidl/android/media/AudioOffloadInfo.aidl",
         "aidl/android/media/AudioOutputFlags.aidl",
         "aidl/android/media/AudioPatch.aidl",
+        "aidl/android/media/AudioPlaybackRate.aidl",
         "aidl/android/media/AudioPort.aidl",
         "aidl/android/media/AudioPortConfig.aidl",
         "aidl/android/media/AudioPortConfigType.aidl",
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index b1eb950..57bd04f 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -51,6 +51,8 @@
 #define WAIT_STREAM_END_TIMEOUT_SEC     120
 static const int kMaxLoopCountNotifications = 32;
 
+using ::android::aidl_utils::statusTFromBinderStatus;
+
 namespace android {
 // ---------------------------------------------------------------------------
 
@@ -1096,6 +1098,53 @@
     return mOriginalSampleRate;
 }
 
+status_t AudioTrack::setDualMonoMode(audio_dual_mono_mode_t mode)
+{
+    AutoMutex lock(mLock);
+    return setDualMonoMode_l(mode);
+}
+
+status_t AudioTrack::setDualMonoMode_l(audio_dual_mono_mode_t mode)
+{
+    const status_t status = statusTFromBinderStatus(
+        mAudioTrack->setDualMonoMode(VALUE_OR_RETURN_STATUS(
+            legacy2aidl_audio_dual_mono_mode_t_AudioDualMonoMode(mode))));
+    if (status == NO_ERROR) mDualMonoMode = mode;
+    return status;
+}
+
+status_t AudioTrack::getDualMonoMode(audio_dual_mono_mode_t* mode) const
+{
+    AutoMutex lock(mLock);
+    media::AudioDualMonoMode mediaMode;
+    const status_t status = statusTFromBinderStatus(mAudioTrack->getDualMonoMode(&mediaMode));
+    if (status == NO_ERROR) {
+        *mode = VALUE_OR_RETURN_STATUS(
+                aidl2legacy_AudioDualMonoMode_audio_dual_mono_mode_t(mediaMode));
+    }
+    return status;
+}
+
+status_t AudioTrack::setAudioDescriptionMixLevel(float leveldB)
+{
+    AutoMutex lock(mLock);
+    return setAudioDescriptionMixLevel_l(leveldB);
+}
+
+status_t AudioTrack::setAudioDescriptionMixLevel_l(float leveldB)
+{
+    const status_t status = statusTFromBinderStatus(
+             mAudioTrack->setAudioDescriptionMixLevel(leveldB));
+    if (status == NO_ERROR) mAudioDescriptionMixLeveldB = leveldB;
+    return status;
+}
+
+status_t AudioTrack::getAudioDescriptionMixLevel(float* leveldB) const
+{
+    AutoMutex lock(mLock);
+    return statusTFromBinderStatus(mAudioTrack->getAudioDescriptionMixLevel(leveldB));
+}
+
 status_t AudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate)
 {
     AutoMutex lock(mLock);
@@ -1103,7 +1152,13 @@
         return NO_ERROR;
     }
     if (isOffloadedOrDirect_l()) {
-        return INVALID_OPERATION;
+        const status_t status = statusTFromBinderStatus(mAudioTrack->setPlaybackRateParameters(
+                VALUE_OR_RETURN_STATUS(
+                        legacy2aidl_audio_playback_rate_t_AudioPlaybackRate(playbackRate))));
+        if (status == NO_ERROR) {
+            mPlaybackRate = playbackRate;
+        }
+        return status;
     }
     if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
         return INVALID_OPERATION;
@@ -1168,9 +1223,18 @@
     return NO_ERROR;
 }
 
-const AudioPlaybackRate& AudioTrack::getPlaybackRate() const
+const AudioPlaybackRate& AudioTrack::getPlaybackRate()
 {
     AutoMutex lock(mLock);
+    if (isOffloadedOrDirect_l()) {
+        media::AudioPlaybackRate playbackRateTemp;
+        const status_t status = statusTFromBinderStatus(
+                mAudioTrack->getPlaybackRateParameters(&playbackRateTemp));
+        if (status == NO_ERROR) { // update local version if changed.
+            mPlaybackRate =
+                    aidl2legacy_AudioPlaybackRate_audio_playback_rate_t(playbackRateTemp).value();
+        }
+    }
     return mPlaybackRate;
 }
 
@@ -1771,6 +1835,13 @@
     mProxy->setPlaybackRate(playbackRateTemp);
     mProxy->setMinimum(mNotificationFramesAct);
 
+    if (mDualMonoMode != AUDIO_DUAL_MONO_MODE_OFF) {
+        setDualMonoMode_l(mDualMonoMode);
+    }
+    if (mAudioDescriptionMixLeveldB != -std::numeric_limits<float>::infinity()) {
+        setAudioDescriptionMixLevel_l(mAudioDescriptionMixLeveldB);
+    }
+
     mDeathNotifier = new DeathNotifier(this);
     IInterface::asBinder(mAudioTrack)->linkToDeath(mDeathNotifier, this);
 
diff --git a/media/libaudioclient/aidl/android/media/AudioDualMonoMode.aidl b/media/libaudioclient/aidl/android/media/AudioDualMonoMode.aidl
new file mode 100644
index 0000000..f6220c2
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioDualMonoMode.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+// TODO(b/175167149): Reconcile AudioDualMonoMode with framework-media-sources
+
+@Backing(type="int")
+enum AudioDualMonoMode {
+    OFF = 0,
+    LR = 1,
+    LL = 2,
+    RR = 3,
+}
diff --git a/media/libaudioclient/aidl/android/media/AudioPlaybackRate.aidl b/media/libaudioclient/aidl/android/media/AudioPlaybackRate.aidl
new file mode 100644
index 0000000..e29d398
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioPlaybackRate.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * The AudioPlaybackRate.
+ *
+ * See https://developer.android.com/reference/android/media/PlaybackParams.
+ * TODO(b/175166815): Reconcile with framework-media-sources PlaybackParams.aidl.
+ *       As this is used for native wire serialization, no need to define
+ *       audio_timestretch_stretch_mode_t and audio_timestretch_fallback_mode_t enums
+ *       until we attempt to unify with PlaybackParams.
+ *
+ * {@hide}
+ */
+parcelable AudioPlaybackRate {
+    /** Speed of audio playback, >= 0.f, 1.f nominal (system limits are further restrictive) */
+    float speed;
+    /** Pitch of audio, >= 0.f, 1.f nominal (system limits are further restrictive) */
+    float pitch;
+    /** Interpreted as audio_timestretch_stretch_mode_t */
+    int stretchMode;
+    /** Interpreted as audio_timestretch_fallback_mode_t */
+    int fallbackMode;
+}
diff --git a/media/libaudioclient/aidl/android/media/IAudioTrack.aidl b/media/libaudioclient/aidl/android/media/IAudioTrack.aidl
index 2b6c362..ac58925 100644
--- a/media/libaudioclient/aidl/android/media/IAudioTrack.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioTrack.aidl
@@ -16,6 +16,8 @@
 
 package android.media;
 
+import android.media.AudioDualMonoMode;
+import android.media.AudioPlaybackRate;
 import android.media.AudioTimestampInternal;
 import android.media.SharedFileRegion;
 import android.media.VolumeShaperConfiguration;
@@ -81,4 +83,34 @@
 
     /** Gets the volume shaper state. */
     @nullable VolumeShaperState getVolumeShaperState(int id);
+
+    /**
+     * Returns DualMonoMode setting associated with this AudioTrack.
+     */
+    AudioDualMonoMode getDualMonoMode();
+
+    /**
+     * Sets DualMonoMode setting.
+     */
+    void setDualMonoMode(in AudioDualMonoMode mode);
+
+    /**
+     * Returns the AudioDescriptionMixLevel.
+     */
+    float getAudioDescriptionMixLevel();
+
+    /**
+     * Sets the AudioDescriptionMixLevel.
+     */
+    void setAudioDescriptionMixLevel(float leveldB);
+
+    /**
+     * Returns the AudioPlaybackRate.
+     */
+    AudioPlaybackRate getPlaybackRateParameters();
+
+    /**
+     * Sets the AudioPlaybackRate.
+     */
+    void setPlaybackRateParameters(in AudioPlaybackRate playbackRate);
 }
diff --git a/media/libaudioclient/include/media/AidlConversion.h b/media/libaudioclient/include/media/AidlConversion.h
index 56afe93..bde20cd 100644
--- a/media/libaudioclient/include/media/AidlConversion.h
+++ b/media/libaudioclient/include/media/AidlConversion.h
@@ -25,6 +25,7 @@
 #include <android/media/AudioClient.h>
 #include <android/media/AudioConfig.h>
 #include <android/media/AudioConfigBase.h>
+#include <android/media/AudioDualMonoMode.h>
 #include <android/media/AudioEncapsulationMode.h>
 #include <android/media/AudioEncapsulationMetadataType.h>
 #include <android/media/AudioFlag.h>
@@ -36,6 +37,7 @@
 #include <android/media/AudioMixLatencyClass.h>
 #include <android/media/AudioMode.h>
 #include <android/media/AudioOutputFlags.h>
+#include <android/media/AudioPlaybackRate.h>
 #include <android/media/AudioPort.h>
 #include <android/media/AudioPortConfigType.h>
 #include <android/media/AudioPortDeviceExt.h>
@@ -359,4 +361,24 @@
 ConversionResult<int32_t>
 legacy2aidl_volume_group_t_int32_t(volume_group_t legacy);
 
+ConversionResult<audio_dual_mono_mode_t>
+aidl2legacy_AudioDualMonoMode_audio_dual_mono_mode_t(media::AudioDualMonoMode aidl);
+ConversionResult<media::AudioDualMonoMode>
+legacy2aidl_audio_dual_mono_mode_t_AudioDualMonoMode(audio_dual_mono_mode_t legacy);
+
+ConversionResult<audio_timestretch_fallback_mode_t>
+aidl2legacy_int32_t_audio_timestretch_fallback_mode_t(int32_t aidl);
+ConversionResult<int32_t>
+legacy2aidl_audio_timestretch_fallback_mode_t_int32_t(audio_timestretch_fallback_mode_t legacy);
+
+ConversionResult<audio_timestretch_stretch_mode_t>
+aidl2legacy_int32_t_audio_timestretch_stretch_mode_t(int32_t aidl);
+ConversionResult<int32_t>
+legacy2aidl_audio_timestretch_stretch_mode_t_int32_t(audio_timestretch_stretch_mode_t legacy);
+
+ConversionResult<audio_playback_rate_t>
+aidl2legacy_AudioPlaybackRate_audio_playback_rate_t(const media::AudioPlaybackRate& aidl);
+ConversionResult<media::AudioPlaybackRate>
+legacy2aidl_audio_playback_rate_t_AudioPlaybackRate(const audio_playback_rate_t& legacy);
+
 }  // namespace android
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index 3728a16..d2696c7 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -515,6 +515,18 @@
      */
             uint32_t    getOriginalSampleRate() const;
 
+    /* Sets the Dual Mono mode presentation on the output device. */
+            status_t    setDualMonoMode(audio_dual_mono_mode_t mode);
+
+    /* Returns the Dual Mono mode presentation setting. */
+            status_t    getDualMonoMode(audio_dual_mono_mode_t* mode) const;
+
+    /* Sets the Audio Description Mix level in dB. */
+            status_t    setAudioDescriptionMixLevel(float leveldB);
+
+    /* Returns the Audio Description Mix level in dB. */
+            status_t    getAudioDescriptionMixLevel(float* leveldB) const;
+
     /* Set source playback rate for timestretch
      * 1.0 is normal speed: < 1.0 is slower, > 1.0 is faster
      * 1.0 is normal pitch: < 1.0 is lower pitch, > 1.0 is higher pitch
@@ -528,7 +540,7 @@
             status_t    setPlaybackRate(const AudioPlaybackRate &playbackRate);
 
     /* Return current playback rate */
-            const AudioPlaybackRate& getPlaybackRate() const;
+            const AudioPlaybackRate& getPlaybackRate();
 
     /* Enables looping and sets the start and end points of looping.
      * Only supported for static buffer mode.
@@ -1072,6 +1084,12 @@
 
             void     updateRoutedDeviceId_l();
 
+            /* Sets the Dual Mono mode presentation on the output device. */
+            status_t setDualMonoMode_l(audio_dual_mono_mode_t mode);
+
+            /* Sets the Audio Description Mix level in dB. */
+            status_t setAudioDescriptionMixLevel_l(float leveldB);
+
     // Next 4 fields may be changed if IAudioTrack is re-created, but always != 0
     sp<media::IAudioTrack>  mAudioTrack;
     sp<IMemory>             mCblkMemory;
@@ -1284,6 +1302,10 @@
 
     wp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
 
+    // Cached values to restore along with the AudioTrack.
+    audio_dual_mono_mode_t mDualMonoMode = AUDIO_DUAL_MONO_MODE_OFF;
+    float mAudioDescriptionMixLeveldB = -std::numeric_limits<float>::infinity();
+
 private:
     class MediaMetrics {
       public:
diff --git a/media/libaudiohal/impl/ConversionHelperHidl.cpp b/media/libaudiohal/impl/ConversionHelperHidl.cpp
index ebed5fd..2927936 100644
--- a/media/libaudiohal/impl/ConversionHelperHidl.cpp
+++ b/media/libaudiohal/impl/ConversionHelperHidl.cpp
@@ -50,12 +50,20 @@
             halKeys.get(String8(AUDIO_PARAMETER_DEVICE_SUP_ENCAPSULATION_METADATA_TYPES),
                         value) == NO_ERROR;
 
+    const bool keepDelayValue =
+            halKeys.get(String8(AudioParameter::keyAdditionalOutputDeviceDelay),
+                        value) == NO_ERROR ||
+            halKeys.get(String8(AudioParameter::keyMaxAdditionalOutputDeviceDelay),
+                        value) == NO_ERROR;
+
     for (size_t i = 0; i < halKeys.size(); ++i) {
         String8 key;
         status_t status = halKeys.getAt(i, key);
         if (status != OK) return status;
         if ((keepFormatValue && key == AudioParameter::keyFormat) ||
-            (keepRoutingValue && key == AudioParameter::keyRouting)) {
+            (keepRoutingValue && key == AudioParameter::keyRouting) ||
+            (keepDelayValue && key == AudioParameter::keyAdditionalOutputDeviceDelay) ||
+            (keepDelayValue && key == AudioParameter::keyMaxAdditionalOutputDeviceDelay)) {
             AudioParameter keepValueParam;
             halKeys.getAt(i, key, value);
             keepValueParam.add(key, value);
diff --git a/media/libaudiohal/impl/StreamHalHidl.cpp b/media/libaudiohal/impl/StreamHalHidl.cpp
index 3a1fce8..097bd12 100644
--- a/media/libaudiohal/impl/StreamHalHidl.cpp
+++ b/media/libaudiohal/impl/StreamHalHidl.cpp
@@ -636,6 +636,32 @@
 #endif
 
 #if MAJOR_VERSION < 6
+status_t StreamOutHalHidl::getDualMonoMode(audio_dual_mono_mode_t* mode __unused) {
+    return INVALID_OPERATION;
+}
+
+status_t StreamOutHalHidl::setDualMonoMode(audio_dual_mono_mode_t mode __unused) {
+    return INVALID_OPERATION;
+}
+
+status_t StreamOutHalHidl::getAudioDescriptionMixLevel(float* leveldB __unused) {
+    return INVALID_OPERATION;
+}
+
+status_t StreamOutHalHidl::setAudioDescriptionMixLevel(float leveldB __unused) {
+    return INVALID_OPERATION;
+}
+
+status_t StreamOutHalHidl::getPlaybackRateParameters(
+        audio_playback_rate_t* playbackRate __unused) {
+    return INVALID_OPERATION;
+}
+
+status_t StreamOutHalHidl::setPlaybackRateParameters(
+        const audio_playback_rate_t& playbackRate __unused) {
+    return INVALID_OPERATION;
+}
+
 status_t StreamOutHalHidl::setEventCallback(
         const sp<StreamOutHalInterfaceEventCallback>& callback __unused) {
     // Codec format callback is supported starting from audio HAL V6.0
@@ -643,6 +669,73 @@
 }
 #else
 
+status_t StreamOutHalHidl::getDualMonoMode(audio_dual_mono_mode_t* mode) {
+    if (mStream == 0) return NO_INIT;
+    Result retval;
+    Return<void> ret = mStream->getDualMonoMode(
+            [&](Result r, DualMonoMode hidlMode) {
+                retval = r;
+                if (retval == Result::OK) {
+                    *mode = static_cast<audio_dual_mono_mode_t>(hidlMode);
+                }
+            });
+    return processReturn("getDualMonoMode", ret, retval);
+}
+
+status_t StreamOutHalHidl::setDualMonoMode(audio_dual_mono_mode_t mode) {
+    if (mStream == 0) return NO_INIT;
+    return processReturn(
+            "setDualMonoMode", mStream->setDualMonoMode(static_cast<DualMonoMode>(mode)));
+}
+
+status_t StreamOutHalHidl::getAudioDescriptionMixLevel(float* leveldB) {
+    if (mStream == 0) return NO_INIT;
+    Result retval;
+    Return<void> ret = mStream->getAudioDescriptionMixLevel(
+            [&](Result r, float hidlLeveldB) {
+                retval = r;
+                if (retval == Result::OK) {
+                    *leveldB = hidlLeveldB;
+                }
+            });
+    return processReturn("getAudioDescriptionMixLevel", ret, retval);
+}
+
+status_t StreamOutHalHidl::setAudioDescriptionMixLevel(float leveldB) {
+    if (mStream == 0) return NO_INIT;
+    return processReturn(
+            "setAudioDescriptionMixLevel", mStream->setAudioDescriptionMixLevel(leveldB));
+}
+
+status_t StreamOutHalHidl::getPlaybackRateParameters(audio_playback_rate_t* playbackRate) {
+    if (mStream == 0) return NO_INIT;
+    Result retval;
+    Return<void> ret = mStream->getPlaybackRateParameters(
+            [&](Result r, PlaybackRate hidlPlaybackRate) {
+                retval = r;
+                if (retval == Result::OK) {
+                    playbackRate->mSpeed = hidlPlaybackRate.speed;
+                    playbackRate->mPitch = hidlPlaybackRate.pitch;
+                    playbackRate->mStretchMode =
+                        static_cast<audio_timestretch_stretch_mode_t>(
+                            hidlPlaybackRate.timestretchMode);
+                    playbackRate->mFallbackMode =
+                        static_cast<audio_timestretch_fallback_mode_t>(
+                            hidlPlaybackRate.fallbackMode);
+                }
+            });
+    return processReturn("getPlaybackRateParameters", ret, retval);
+}
+
+status_t StreamOutHalHidl::setPlaybackRateParameters(const audio_playback_rate_t& playbackRate) {
+    if (mStream == 0) return NO_INIT;
+    return processReturn(
+            "setPlaybackRateParameters", mStream->setPlaybackRateParameters(
+                PlaybackRate{playbackRate.mSpeed, playbackRate.mPitch,
+                    static_cast<TimestretchMode>(playbackRate.mStretchMode),
+                    static_cast<TimestretchFallbackMode>(playbackRate.mFallbackMode)}));
+}
+
 #include PATH(android/hardware/audio/FILE_VERSION/IStreamOutEventCallback.h)
 
 namespace {
diff --git a/media/libaudiohal/impl/StreamHalHidl.h b/media/libaudiohal/impl/StreamHalHidl.h
index 95855fe..7dfc78f 100644
--- a/media/libaudiohal/impl/StreamHalHidl.h
+++ b/media/libaudiohal/impl/StreamHalHidl.h
@@ -173,6 +173,24 @@
     void onDrainReady();
     void onError();
 
+    // Returns the Dual Mono mode presentation setting.
+    status_t getDualMonoMode(audio_dual_mono_mode_t* mode) override;
+
+    // Sets the Dual Mono mode presentation on the output device.
+    status_t setDualMonoMode(audio_dual_mono_mode_t mode) override;
+
+    // Returns the Audio Description Mix level in dB.
+    status_t getAudioDescriptionMixLevel(float* leveldB) override;
+
+    // Sets the Audio Description Mix level in dB.
+    status_t setAudioDescriptionMixLevel(float leveldB) override;
+
+    // Retrieves current playback rate parameters.
+    status_t getPlaybackRateParameters(audio_playback_rate_t* playbackRate) override;
+
+    // Sets the playback rate parameters that control playback behavior.
+    status_t setPlaybackRateParameters(const audio_playback_rate_t& playbackRate) override;
+
     status_t setEventCallback(const sp<StreamOutHalInterfaceEventCallback>& callback) override;
 
     // Methods used by StreamCodecFormatCallback (HIDL).
diff --git a/media/libaudiohal/impl/StreamHalLocal.cpp b/media/libaudiohal/impl/StreamHalLocal.cpp
index f544e06..a3f2fb4 100644
--- a/media/libaudiohal/impl/StreamHalLocal.cpp
+++ b/media/libaudiohal/impl/StreamHalLocal.cpp
@@ -311,6 +311,36 @@
     return mStream->get_mmap_position(mStream, position);
 }
 
+status_t StreamOutHalLocal::getDualMonoMode(audio_dual_mono_mode_t* mode) {
+    if (mStream->get_dual_mono_mode == nullptr) return INVALID_OPERATION;
+    return mStream->get_dual_mono_mode(mStream, mode);
+}
+
+status_t StreamOutHalLocal::setDualMonoMode(audio_dual_mono_mode_t mode) {
+    if (mStream->set_dual_mono_mode == nullptr) return INVALID_OPERATION;
+    return mStream->set_dual_mono_mode(mStream, mode);
+}
+
+status_t StreamOutHalLocal::getAudioDescriptionMixLevel(float* leveldB) {
+    if (mStream->get_audio_description_mix_level == nullptr) return INVALID_OPERATION;
+    return mStream->get_audio_description_mix_level(mStream, leveldB);
+}
+
+status_t StreamOutHalLocal::setAudioDescriptionMixLevel(float leveldB) {
+    if (mStream->set_audio_description_mix_level == nullptr) return INVALID_OPERATION;
+    return mStream->set_audio_description_mix_level(mStream, leveldB);
+}
+
+status_t StreamOutHalLocal::getPlaybackRateParameters(audio_playback_rate_t* playbackRate) {
+    if (mStream->get_playback_rate_parameters == nullptr) return INVALID_OPERATION;
+    return mStream->get_playback_rate_parameters(mStream, playbackRate);
+}
+
+status_t StreamOutHalLocal::setPlaybackRateParameters(const audio_playback_rate_t& playbackRate) {
+    if (mStream->set_playback_rate_parameters == nullptr) return INVALID_OPERATION;
+    return mStream->set_playback_rate_parameters(mStream, &playbackRate);
+}
+
 status_t StreamOutHalLocal::setEventCallback(
         const sp<StreamOutHalInterfaceEventCallback>& callback) {
     if (mStream->set_event_callback == nullptr) {
diff --git a/media/libaudiohal/impl/StreamHalLocal.h b/media/libaudiohal/impl/StreamHalLocal.h
index 8e5180f..e228104 100644
--- a/media/libaudiohal/impl/StreamHalLocal.h
+++ b/media/libaudiohal/impl/StreamHalLocal.h
@@ -156,6 +156,24 @@
     // Called when the metadata of the stream's source has been changed.
     status_t updateSourceMetadata(const SourceMetadata& sourceMetadata) override;
 
+    // Returns the Dual Mono mode presentation setting.
+    status_t getDualMonoMode(audio_dual_mono_mode_t* mode) override;
+
+    // Sets the Dual Mono mode presentation on the output device.
+    status_t setDualMonoMode(audio_dual_mono_mode_t mode) override;
+
+    // Returns the Audio Description Mix level in dB.
+    status_t getAudioDescriptionMixLevel(float* leveldB) override;
+
+    // Sets the Audio Description Mix level in dB.
+    status_t setAudioDescriptionMixLevel(float leveldB) override;
+
+    // Retrieves current playback rate parameters.
+    status_t getPlaybackRateParameters(audio_playback_rate_t* playbackRate) override;
+
+    // Sets the playback rate parameters that control playback behavior.
+    status_t setPlaybackRateParameters(const audio_playback_rate_t& playbackRate) override;
+
     status_t setEventCallback(const sp<StreamOutHalInterfaceEventCallback>& callback) override;
 
   private:
diff --git a/media/libaudiohal/include/media/audiohal/StreamHalInterface.h b/media/libaudiohal/include/media/audiohal/StreamHalInterface.h
index 523705e..097e9a2 100644
--- a/media/libaudiohal/include/media/audiohal/StreamHalInterface.h
+++ b/media/libaudiohal/include/media/audiohal/StreamHalInterface.h
@@ -160,12 +160,31 @@
     struct SourceMetadata {
         std::vector<playback_track_metadata_v7_t> tracks;
     };
+
     /**
      * Called when the metadata of the stream's source has been changed.
      * @param sourceMetadata Description of the audio that is played by the clients.
      */
     virtual status_t updateSourceMetadata(const SourceMetadata& sourceMetadata) = 0;
 
+    // Returns the Dual Mono mode presentation setting.
+    virtual status_t getDualMonoMode(audio_dual_mono_mode_t* mode) = 0;
+
+    // Sets the Dual Mono mode presentation on the output device.
+    virtual status_t setDualMonoMode(audio_dual_mono_mode_t mode) = 0;
+
+    // Returns the Audio Description Mix level in dB.
+    virtual status_t getAudioDescriptionMixLevel(float* leveldB) = 0;
+
+    // Sets the Audio Description Mix level in dB.
+    virtual status_t setAudioDescriptionMixLevel(float leveldB) = 0;
+
+    // Retrieves current playback rate parameters.
+    virtual status_t getPlaybackRateParameters(audio_playback_rate_t* playbackRate) = 0;
+
+    // Sets the playback rate parameters that control playback behavior.
+    virtual status_t setPlaybackRateParameters(const audio_playback_rate_t& playbackRate) = 0;
+
     virtual status_t setEventCallback(const sp<StreamOutHalInterfaceEventCallback>& callback) = 0;
 
   protected:
diff --git a/media/libmediahelper/AudioParameter.cpp b/media/libmediahelper/AudioParameter.cpp
index fc8306c..382a920 100644
--- a/media/libmediahelper/AudioParameter.cpp
+++ b/media/libmediahelper/AudioParameter.cpp
@@ -57,6 +57,10 @@
 //        AUDIO_PARAMETER_DEVICE_SUP_ENCAPSULATION_MODES;
 // const char * const AudioParameter::keyDeviceSupportedEncapsulationMetadataTypes =
 //        AUDIO_PARAMETER_DEVICE_SUP_ENCAPSULATION_METADATA_TYPES;
+const char * const AudioParameter::keyAdditionalOutputDeviceDelay =
+        AUDIO_PARAMETER_DEVICE_ADDITIONAL_OUTPUT_DELAY;
+const char * const AudioParameter::keyMaxAdditionalOutputDeviceDelay =
+        AUDIO_PARAMETER_DEVICE_MAX_ADDITIONAL_OUTPUT_DELAY;
 
 AudioParameter::AudioParameter(const String8& keyValuePairs)
 {
diff --git a/media/libmediahelper/AudioValidator.cpp b/media/libmediahelper/AudioValidator.cpp
index e2fd8ae..7eddbe1 100644
--- a/media/libmediahelper/AudioValidator.cpp
+++ b/media/libmediahelper/AudioValidator.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <media/AudioValidator.h>
+#include <cmath>
 
 namespace android {
 
@@ -121,4 +122,62 @@
     return safetyNetLog(status, bugNumber);
 }
 
+/* static */
+status_t AudioValidator::validateAudioDescriptionMixLevel(float leveldB)
+{
+    constexpr float MAX_AUDIO_DESCRIPTION_MIX_LEVEL = 48.f;
+    return std::isnan(leveldB) || leveldB > MAX_AUDIO_DESCRIPTION_MIX_LEVEL ? BAD_VALUE : OK;
+}
+
+/* static */
+status_t AudioValidator::validateDualMonoMode(audio_dual_mono_mode_t dualMonoMode)
+{
+    switch (dualMonoMode) {
+        case AUDIO_DUAL_MONO_MODE_OFF:
+        case AUDIO_DUAL_MONO_MODE_LR:
+        case AUDIO_DUAL_MONO_MODE_LL:
+        case AUDIO_DUAL_MONO_MODE_RR:
+        return OK;
+    }
+    return BAD_VALUE;
+}
+
+/* static */
+status_t AudioValidator::validatePlaybackRateFallbackMode(
+        audio_timestretch_fallback_mode_t fallbackMode)
+{
+    switch (fallbackMode) {
+        case AUDIO_TIMESTRETCH_FALLBACK_CUT_REPEAT:
+            // This is coarse sounding timestretching used for internal debugging,
+            // not intended for general use.
+            break; // warning if not listed.
+        case AUDIO_TIMESTRETCH_FALLBACK_DEFAULT:
+        case AUDIO_TIMESTRETCH_FALLBACK_MUTE:
+        case AUDIO_TIMESTRETCH_FALLBACK_FAIL:
+            return OK;
+    }
+    return BAD_VALUE;
+}
+
+/* static */
+status_t AudioValidator::validatePlaybackRateStretchMode(
+        audio_timestretch_stretch_mode_t stretchMode)
+{
+    switch (stretchMode) {
+        case AUDIO_TIMESTRETCH_STRETCH_DEFAULT:
+        case AUDIO_TIMESTRETCH_STRETCH_VOICE:
+            return OK;
+    }
+    return BAD_VALUE;
+}
+
+/* static */
+status_t AudioValidator::validatePlaybackRate(
+        const audio_playback_rate_t& playbackRate)
+{
+    if (playbackRate.mSpeed < 0.f || playbackRate.mPitch < 0.f) return BAD_VALUE;
+    return validatePlaybackRateFallbackMode(playbackRate.mFallbackMode) ?:
+            validatePlaybackRateStretchMode(playbackRate.mStretchMode);
+}
+
 }; // namespace android
diff --git a/media/libmediahelper/include/media/AudioParameter.h b/media/libmediahelper/include/media/AudioParameter.h
index 66d8dfb..9a6ca8a 100644
--- a/media/libmediahelper/include/media/AudioParameter.h
+++ b/media/libmediahelper/include/media/AudioParameter.h
@@ -104,6 +104,9 @@
     // static const char * const keyDeviceSupportedEncapsulationModes;
     // static const char * const keyDeviceSupportedEncapsulationMetadataTypes;
 
+    static const char * const keyAdditionalOutputDeviceDelay;
+    static const char * const keyMaxAdditionalOutputDeviceDelay;
+
     String8 toString() const { return toStringImpl(true); }
     String8 keysToString() const { return toStringImpl(false); }
 
diff --git a/media/libmediahelper/include/media/AudioValidator.h b/media/libmediahelper/include/media/AudioValidator.h
index 008868e..56c2fa6 100644
--- a/media/libmediahelper/include/media/AudioValidator.h
+++ b/media/libmediahelper/include/media/AudioValidator.h
@@ -65,7 +65,7 @@
      * Otherwise, return BAD_VALUE.
      */
     static status_t validateAudioPort(
-            const struct audio_port_v7& port, std::string_view ugNumber = {});
+            const struct audio_port_v7& port, std::string_view bugNumber = {});
 
     /**
      * Return NO_ERROR only when there is no error with the given audio patch.
@@ -73,6 +73,36 @@
      */
     static status_t validateAudioPatch(
             const struct audio_patch& patch, std::string_view bugNumber = {});
+
+    /**
+     * Return NO_ERROR if leveldB is acceptable, otherwise BAD_VALUE.
+     */
+    static status_t validateAudioDescriptionMixLevel(float leveldB);
+
+    /**
+     * Return NO_ERROR if dualMonoMode is one of the enum values, otherwise BAD_VALUE.
+     */
+    static status_t validateDualMonoMode(audio_dual_mono_mode_t dualMonoMode);
+
+    /**
+     * Return NO_ERROR if fallbackMode is one of the enum values, otherwise BAD_VALUE.
+     */
+    static status_t validatePlaybackRateFallbackMode(
+            audio_timestretch_fallback_mode_t fallbackMode);
+
+    /**
+     * Return NO_ERROR if fallbackMode is one of the enum values, otherwise BAD_VALUE.
+     */
+    static status_t validatePlaybackRateStretchMode(audio_timestretch_stretch_mode_t stretchMode);
+
+    /**
+     * Return NO_ERROR if playbackRate is acceptable - the enums are correct and the
+     * rate and speed non-negative, otherwise BAD_VALUE.
+     *
+     * This is a basic bounds check - the system might have stricter requirements for
+     * playbackRate on a particular stream / device.
+     */
+    static status_t validatePlaybackRate(const audio_playback_rate_t& playbackRate);
 };
 
 }; // namespace android
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 1d530a4..1cfdffc 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -643,6 +643,14 @@
         binder::Status getVolumeShaperState(
                 int32_t id,
                 std::optional<media::VolumeShaperState>* _aidl_return) override;
+        binder::Status getDualMonoMode(media::AudioDualMonoMode* _aidl_return) override;
+        binder::Status setDualMonoMode(media::AudioDualMonoMode mode) override;
+        binder::Status getAudioDescriptionMixLevel(float* _aidl_return) override;
+        binder::Status setAudioDescriptionMixLevel(float leveldB) override;
+        binder::Status getPlaybackRateParameters(
+                media::AudioPlaybackRate* _aidl_return) override;
+        binder::Status setPlaybackRateParameters(
+                const media::AudioPlaybackRate& playbackRate) override;
 
     private:
         const sp<PlaybackThread::Track> mTrack;
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index dbc190c..c70d6f9 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -117,6 +117,12 @@
             int         auxEffectId() const { return mAuxEffectId; }
     virtual status_t    getTimestamp(AudioTimestamp& timestamp);
             void        signal();
+            status_t    getDualMonoMode(audio_dual_mono_mode_t* mode);
+            status_t    setDualMonoMode(audio_dual_mono_mode_t mode);
+            status_t    getAudioDescriptionMixLevel(float* leveldB);
+            status_t    setAudioDescriptionMixLevel(float leveldB);
+            status_t    getPlaybackRateParameters(audio_playback_rate_t* playbackRate);
+            status_t    setPlaybackRateParameters(const audio_playback_rate_t& playbackRate);
 
 // implement FastMixerState::VolumeProvider interface
     virtual gain_minifloat_packed_t getVolumeLR();
@@ -281,6 +287,10 @@
     /** How many frames should be in the buffer before the track is considered ready */
     const size_t        mFrameCountToBeReady;
 
+    audio_dual_mono_mode_t mDualMonoMode = AUDIO_DUAL_MONO_MODE_OFF;
+    float               mAudioDescriptionMixLevel = -std::numeric_limits<float>::infinity();
+    audio_playback_rate_t  mPlaybackRateParameters = AUDIO_PLAYBACK_RATE_INITIALIZER;
+
 private:
     void                interceptBuffer(const AudioBufferProvider::Buffer& buffer);
     template <class F>
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 06cbe87..86c92ea 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -33,6 +33,7 @@
 
 #include <media/nbaio/Pipe.h>
 #include <media/nbaio/PipeReader.h>
+#include <media/AudioValidator.h>
 #include <media/RecordBufferConverter.h>
 #include <mediautils/ServiceUtilities.h>
 #include <audio_utils/minifloat.h>
@@ -52,9 +53,17 @@
 #define ALOGVV(a...) do { } while(0)
 #endif
 
+// TODO: Remove when this is put into AidlConversionUtil.h
+#define VALUE_OR_RETURN_BINDER_STATUS(x)    \
+    ({                                      \
+       auto _tmp = (x);                     \
+       if (!_tmp.ok()) return ::android::aidl_utils::binderStatusFromStatusT(_tmp.error()); \
+       std::move(_tmp.value());             \
+     })
+
 namespace android {
 
-using aidl_utils::binderStatusFromStatusT;
+using ::android::aidl_utils::binderStatusFromStatusT;
 using binder::Status;
 using media::VolumeShaper;
 // ----------------------------------------------------------------------------
@@ -415,6 +424,64 @@
     return Status::ok();
 }
 
+Status AudioFlinger::TrackHandle::getDualMonoMode(media::AudioDualMonoMode* _aidl_return)
+{
+    audio_dual_mono_mode_t mode = AUDIO_DUAL_MONO_MODE_OFF;
+    const status_t status = mTrack->getDualMonoMode(&mode)
+            ?: AudioValidator::validateDualMonoMode(mode);
+    if (status == OK) {
+        *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(
+                legacy2aidl_audio_dual_mono_mode_t_AudioDualMonoMode(mode));
+    }
+    return binderStatusFromStatusT(status);
+}
+
+Status AudioFlinger::TrackHandle::setDualMonoMode(
+        media::AudioDualMonoMode mode)
+{
+    const auto localMonoMode = VALUE_OR_RETURN_BINDER_STATUS(
+            aidl2legacy_AudioDualMonoMode_audio_dual_mono_mode_t(mode));
+    return binderStatusFromStatusT(AudioValidator::validateDualMonoMode(localMonoMode)
+            ?: mTrack->setDualMonoMode(localMonoMode));
+}
+
+Status AudioFlinger::TrackHandle::getAudioDescriptionMixLevel(float* _aidl_return)
+{
+    float leveldB = -std::numeric_limits<float>::infinity();
+    const status_t status = mTrack->getAudioDescriptionMixLevel(&leveldB)
+            ?: AudioValidator::validateAudioDescriptionMixLevel(leveldB);
+    if (status == OK) *_aidl_return = leveldB;
+    return binderStatusFromStatusT(status);
+}
+
+Status AudioFlinger::TrackHandle::setAudioDescriptionMixLevel(float leveldB)
+{
+    return binderStatusFromStatusT(AudioValidator::validateAudioDescriptionMixLevel(leveldB)
+             ?: mTrack->setAudioDescriptionMixLevel(leveldB));
+}
+
+Status AudioFlinger::TrackHandle::getPlaybackRateParameters(
+        media::AudioPlaybackRate* _aidl_return)
+{
+    audio_playback_rate_t localPlaybackRate{};
+    status_t status = mTrack->getPlaybackRateParameters(&localPlaybackRate)
+            ?: AudioValidator::validatePlaybackRate(localPlaybackRate);
+    if (status == NO_ERROR) {
+        *_aidl_return = VALUE_OR_RETURN_BINDER_STATUS(
+                legacy2aidl_audio_playback_rate_t_AudioPlaybackRate(localPlaybackRate));
+    }
+    return binderStatusFromStatusT(status);
+}
+
+Status AudioFlinger::TrackHandle::setPlaybackRateParameters(
+        const media::AudioPlaybackRate& playbackRate)
+{
+    const audio_playback_rate_t localPlaybackRate = VALUE_OR_RETURN_BINDER_STATUS(
+            aidl2legacy_AudioPlaybackRate_audio_playback_rate_t(playbackRate));
+    return binderStatusFromStatusT(AudioValidator::validatePlaybackRate(localPlaybackRate)
+            ?: mTrack->setPlaybackRateParameters(localPlaybackRate));
+}
+
 // ----------------------------------------------------------------------------
 //      AppOp for audio playback
 // -------------------------------
@@ -1500,6 +1567,108 @@
     }
 }
 
+status_t AudioFlinger::PlaybackThread::Track::getDualMonoMode(audio_dual_mono_mode_t* mode)
+{
+    status_t status = INVALID_OPERATION;
+    if (isOffloadedOrDirect()) {
+        sp<ThreadBase> thread = mThread.promote();
+        if (thread != nullptr) {
+            PlaybackThread *t = (PlaybackThread *)thread.get();
+            Mutex::Autolock _l(t->mLock);
+            status = t->mOutput->stream->getDualMonoMode(mode);
+            ALOGD_IF((status == NO_ERROR) && (mDualMonoMode != *mode),
+                    "%s: mode %d inconsistent", __func__, mDualMonoMode);
+        }
+    }
+    return status;
+}
+
+status_t AudioFlinger::PlaybackThread::Track::setDualMonoMode(audio_dual_mono_mode_t mode)
+{
+    status_t status = INVALID_OPERATION;
+    if (isOffloadedOrDirect()) {
+        sp<ThreadBase> thread = mThread.promote();
+        if (thread != nullptr) {
+            auto t = static_cast<PlaybackThread *>(thread.get());
+            Mutex::Autolock lock(t->mLock);
+            status = t->mOutput->stream->setDualMonoMode(mode);
+            if (status == NO_ERROR) {
+                mDualMonoMode = mode;
+            }
+        }
+    }
+    return status;
+}
+
+status_t AudioFlinger::PlaybackThread::Track::getAudioDescriptionMixLevel(float* leveldB)
+{
+    status_t status = INVALID_OPERATION;
+    if (isOffloadedOrDirect()) {
+        sp<ThreadBase> thread = mThread.promote();
+        if (thread != nullptr) {
+            auto t = static_cast<PlaybackThread *>(thread.get());
+            Mutex::Autolock lock(t->mLock);
+            status = t->mOutput->stream->getAudioDescriptionMixLevel(leveldB);
+            ALOGD_IF((status == NO_ERROR) && (mAudioDescriptionMixLevel != *leveldB),
+                    "%s: level %.3f inconsistent", __func__, mAudioDescriptionMixLevel);
+        }
+    }
+    return status;
+}
+
+status_t AudioFlinger::PlaybackThread::Track::setAudioDescriptionMixLevel(float leveldB)
+{
+    status_t status = INVALID_OPERATION;
+    if (isOffloadedOrDirect()) {
+        sp<ThreadBase> thread = mThread.promote();
+        if (thread != nullptr) {
+            auto t = static_cast<PlaybackThread *>(thread.get());
+            Mutex::Autolock lock(t->mLock);
+            status = t->mOutput->stream->setAudioDescriptionMixLevel(leveldB);
+            if (status == NO_ERROR) {
+                mAudioDescriptionMixLevel = leveldB;
+            }
+        }
+    }
+    return status;
+}
+
+status_t AudioFlinger::PlaybackThread::Track::getPlaybackRateParameters(
+        audio_playback_rate_t* playbackRate)
+{
+    status_t status = INVALID_OPERATION;
+    if (isOffloadedOrDirect()) {
+        sp<ThreadBase> thread = mThread.promote();
+        if (thread != nullptr) {
+            auto t = static_cast<PlaybackThread *>(thread.get());
+            Mutex::Autolock lock(t->mLock);
+            status = t->mOutput->stream->getPlaybackRateParameters(playbackRate);
+            ALOGD_IF((status == NO_ERROR) &&
+                    !isAudioPlaybackRateEqual(mPlaybackRateParameters, *playbackRate),
+                    "%s: playbackRate inconsistent", __func__);
+        }
+    }
+    return status;
+}
+
+status_t AudioFlinger::PlaybackThread::Track::setPlaybackRateParameters(
+        const audio_playback_rate_t& playbackRate)
+{
+    status_t status = INVALID_OPERATION;
+    if (isOffloadedOrDirect()) {
+        sp<ThreadBase> thread = mThread.promote();
+        if (thread != nullptr) {
+            auto t = static_cast<PlaybackThread *>(thread.get());
+            Mutex::Autolock lock(t->mLock);
+            status = t->mOutput->stream->setPlaybackRateParameters(playbackRate);
+            if (status == NO_ERROR) {
+                mPlaybackRateParameters = playbackRate;
+            }
+        }
+    }
+    return status;
+}
+
 //To be called with thread lock held
 bool AudioFlinger::PlaybackThread::Track::isResumePending() {
 
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index b872709..6b08f7c 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -527,6 +527,9 @@
         lConfig.offload_info.duration_us = -1;
         lConfig.offload_info.has_video = true; // conservative
         lConfig.offload_info.is_streaming = true; // likely
+        lConfig.offload_info.encapsulation_mode = lConfig.offload_info.encapsulation_mode;
+        lConfig.offload_info.content_id = lConfig.offload_info.content_id;
+        lConfig.offload_info.sync_id = lConfig.offload_info.sync_id;
     }
 
     mFlags = (audio_output_flags_t)(mFlags | flags);