Merge "Use routed devices throughout the audio framework" into main
diff --git a/media/libaaudio/fuzzer/Android.bp b/media/libaaudio/fuzzer/Android.bp
index 3b50744..5b48401 100644
--- a/media/libaaudio/fuzzer/Android.bp
+++ b/media/libaaudio/fuzzer/Android.bp
@@ -60,6 +60,7 @@
         "libaaudio",
         "libaaudio_internal",
         "libaudioclient",
+        "libaudiofoundation",
         "libaudioutils",
         "libbase_ndk",
         "libcutils",
diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h
index e19d526..136edcc 100644
--- a/media/libaaudio/include/aaudio/AAudio.h
+++ b/media/libaaudio/include/aaudio/AAudio.h
@@ -1909,11 +1909,33 @@
  * Available since API level 26.
  *
  * @param stream reference provided by AAudioStreamBuilder_openStream()
- * @return actual device ID
+ * @return actual device id. If there are multiple device ids used, the first device picked by
+ *         the audio policy engine will be returned.
  */
 AAUDIO_API int32_t AAudioStream_getDeviceId(AAudioStream* _Nonnull stream) __INTRODUCED_IN(26);
 
 /**
+ * Available since API level 36.
+ *
+ * Call this function after AAudioStreamBuilder_openStream().
+ * This function will crash if stream is null.
+ * An array of size 16 should generally be large enough to fit all device identifiers.
+ *
+ * @param stream reference provided by AAudioStreamBuilder_openStream().
+ * @param ids reference to an array of ids.
+ * @params numIds size allocated to the array of ids.
+ *         The input should be the size of the ids array.
+ *         The output will be the actual number of device ids.
+ * @return {@link #AAUDIO_OK} or an error code.
+ *         If numIds is null, return {@link #AAUDIO_ERROR_ILLEGAL_ARGUMENT}.
+ *         If numIds is smaller than the number of device ids, return
+ *         {@link #AAUDIO_ERROR_OUT_OF_RANGE}. The value of numIds will still be updated.
+ *         Otherwise, if ids is null, return {@link #AAUDIO_ERROR_ILLEGAL_ARGUMENT}.
+ */
+AAUDIO_API aaudio_result_t AAudioStream_getDeviceIds(AAudioStream* _Nonnull stream,
+        int32_t* _Nonnull ids, int32_t* _Nonnull numIds) __INTRODUCED_IN(36);
+
+/**
  * Available since API level 26.
  *
  * @param stream reference provided by AAudioStreamBuilder_openStream()
diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp
index ebb7637..cccb096 100644
--- a/media/libaaudio/src/Android.bp
+++ b/media/libaaudio/src/Android.bp
@@ -103,6 +103,7 @@
         "framework-permission-aidl-cpp",
         "libaaudio_internal",
         "libaudioclient",
+        "libaudiofoundation",
         "libaudioutils",
         "libbinder",
         "libcutils",
@@ -166,6 +167,7 @@
         "framework-permission-aidl-cpp",
         "libaudioclient",
         "libaudioclient_aidl_conversion",
+        "libaudiofoundation",
         "libaudioutils",
         "libbinder",
         "libcutils",
diff --git a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
index c53a897..37c1a98 100644
--- a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
@@ -34,7 +34,16 @@
 AAudioStreamConfiguration::AAudioStreamConfiguration(const StreamParameters& parcelable) {
     setChannelMask(parcelable.channelMask);
     setSampleRate(parcelable.sampleRate);
-    setDeviceId(parcelable.deviceId);
+    auto deviceIds = android::convertContainer<android::DeviceIdVector>(
+            parcelable.deviceIds, android::aidl2legacy_int32_t_audio_port_handle_t);
+    if (deviceIds.ok()) {
+        setDeviceIds(deviceIds.value());
+    } else {
+        ALOGE("deviceIds (%s) aidl2legacy conversion failed",
+              android::toString(parcelable.deviceIds).c_str());
+        android::DeviceIdVector emptyDeviceIds;
+        setDeviceIds(emptyDeviceIds);
+    }
     static_assert(sizeof(aaudio_sharing_mode_t) == sizeof(parcelable.sharingMode));
     setSharingMode(parcelable.sharingMode);
     auto convFormat = android::aidl2legacy_AudioFormatDescription_audio_format_t(
@@ -87,7 +96,15 @@
     StreamParameters result;
     result.channelMask = getChannelMask();
     result.sampleRate = getSampleRate();
-    result.deviceId = getDeviceId();
+    auto deviceIds = android::convertContainer<std::vector<int32_t>>(
+            getDeviceIds(), android::legacy2aidl_audio_port_handle_t_int32_t);
+    if (deviceIds.ok()) {
+        result.deviceIds = deviceIds.value();
+    } else {
+        ALOGE("deviceIds (%s) legacy2aidl conversion failed",
+              android::toString(getDeviceIds()).c_str());
+        result.deviceIds = {};
+    }
     static_assert(sizeof(aaudio_sharing_mode_t) == sizeof(result.sharingMode));
     result.sharingMode = getSharingMode();
     auto convAudioFormat = android::legacy2aidl_audio_format_t_AudioFormatDescription(getFormat());
diff --git a/media/libaaudio/src/binding/aidl/aaudio/StreamParameters.aidl b/media/libaaudio/src/binding/aidl/aaudio/StreamParameters.aidl
index a301da8..7d7abce 100644
--- a/media/libaaudio/src/binding/aidl/aaudio/StreamParameters.aidl
+++ b/media/libaaudio/src/binding/aidl/aaudio/StreamParameters.aidl
@@ -21,7 +21,7 @@
 parcelable StreamParameters {
     int                                       channelMask;  //          = AAUDIO_UNSPECIFIED;
     int                                       sampleRate;  //           = AAUDIO_UNSPECIFIED;
-    int                                       deviceId;  //             = AAUDIO_UNSPECIFIED;
+    int[]                                     deviceIds;  //            = null;
     int /* aaudio_sharing_mode_t */           sharingMode;  //          = AAUDIO_SHARING_MODE_SHARED;
     AudioFormatDescription                    audioFormat;  //          = AUDIO_FORMAT_DEFAULT;
     int /* aaudio_direction_t */              direction;  //            = AAUDIO_DIRECTION_OUTPUT;
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 99b90e2..6bc7dc2 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -121,7 +121,7 @@
     request.setSharingModeMatchRequired(isSharingModeMatchRequired());
     request.setInService(isInService());
 
-    request.getConfiguration().setDeviceId(getDeviceId());
+    request.getConfiguration().setDeviceIds(getDeviceIds());
     request.getConfiguration().setSampleRate(getSampleRate());
     request.getConfiguration().setDirection(getDirection());
     request.getConfiguration().setSharingMode(getSharingMode());
@@ -180,7 +180,7 @@
         setChannelMask(configurationOutput.getChannelMask());
     }
 
-    setDeviceId(configurationOutput.getDeviceId());
+    setDeviceIds(configurationOutput.getDeviceIds());
     setSessionId(configurationOutput.getSessionId());
     setSharingMode(configurationOutput.getSharingMode());
 
diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index fb87dd9..64152f0 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -95,7 +95,11 @@
                                                 int32_t deviceId)
 {
     AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
-    streamBuilder->setDeviceId(deviceId);
+    android::DeviceIdVector deviceIds;
+    if (deviceId != AAUDIO_UNSPECIFIED) {
+        deviceIds.push_back(deviceId);
+    }
+    streamBuilder->setDeviceIds(deviceIds);
 }
 
 AAUDIO_API void AAudioStreamBuilder_setPackageName(AAudioStreamBuilder* builder,
@@ -537,7 +541,33 @@
 AAUDIO_API int32_t AAudioStream_getDeviceId(AAudioStream* stream)
 {
     AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
-    return audioStream->getDeviceId();
+    auto deviceIds = audioStream->getDeviceIds();
+    if (deviceIds.empty()) {
+        return AAUDIO_UNSPECIFIED;
+    }
+    return deviceIds[0];
+}
+
+AAUDIO_API aaudio_result_t AAudioStream_getDeviceIds(AAudioStream* stream, int32_t* ids,
+                                                     int32_t* numIds)
+{
+    if (numIds == nullptr) {
+        return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
+    }
+    AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
+    auto deviceIds = audioStream->getDeviceIds();
+    if (*numIds < deviceIds.size()) {
+        *numIds = deviceIds.size();
+        return AAUDIO_ERROR_OUT_OF_RANGE;
+    }
+    if (ids == nullptr) {
+        return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
+    }
+    for (int i = 0; i < deviceIds.size(); i++) {
+        ids[i] = deviceIds[i];
+    }
+    *numIds = deviceIds.size();
+    return AAUDIO_OK;
 }
 
 AAUDIO_API aaudio_sharing_mode_t AAudioStream_getSharingMode(AAudioStream* stream)
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp
index 056918a..f504fa9 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.cpp
+++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp
@@ -27,7 +27,7 @@
 void AAudioStreamParameters::copyFrom(const AAudioStreamParameters &other) {
     mSamplesPerFrame      = other.mSamplesPerFrame;
     mSampleRate           = other.mSampleRate;
-    mDeviceId             = other.mDeviceId;
+    mDeviceIds            = other.mDeviceIds;
     mSessionId            = other.mSessionId;
     mSharingMode          = other.mSharingMode;
     mAudioFormat          = other.mAudioFormat;
@@ -74,9 +74,13 @@
         return AAUDIO_ERROR_OUT_OF_RANGE;
     }
 
-    if (mDeviceId < 0) {
-        ALOGD("deviceId out of range = %d", mDeviceId);
-        return AAUDIO_ERROR_OUT_OF_RANGE;
+    // TODO(b/379139078): Query AudioSystem::listAudioPorts
+    for (auto deviceId : mDeviceIds) {
+        if (deviceId < 0) {
+            ALOGE("deviceId out of range = %d, deviceIds = %s", deviceId,
+                      android::toString(mDeviceIds).c_str());
+            return AAUDIO_ERROR_OUT_OF_RANGE;
+        }
     }
 
     // All Session ID values are legal.
@@ -296,7 +300,7 @@
 }
 
 void AAudioStreamParameters::dump() const {
-    ALOGD("mDeviceId             = %6d", mDeviceId);
+    ALOGD("mDeviceIds            = %s",  android::toString(mDeviceIds).c_str());
     ALOGD("mSessionId            = %6d", mSessionId);
     ALOGD("mSampleRate           = %6d", mSampleRate);
     ALOGD("mSamplesPerFrame      = %6d", mSamplesPerFrame);
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.h b/media/libaaudio/src/core/AAudioStreamParameters.h
index cad27a7..c4c0a4f 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.h
+++ b/media/libaaudio/src/core/AAudioStreamParameters.h
@@ -20,6 +20,7 @@
 #include <stdint.h>
 
 #include <aaudio/AAudio.h>
+#include <media/AudioContainers.h>
 #include <utility/AAudioUtilities.h>
 
 namespace aaudio {
@@ -29,12 +30,12 @@
     AAudioStreamParameters() = default;
     virtual ~AAudioStreamParameters() = default;
 
-    int32_t getDeviceId() const {
-        return mDeviceId;
+    android::DeviceIdVector getDeviceIds() const {
+        return mDeviceIds;
     }
 
-    void setDeviceId(int32_t deviceId) {
-        mDeviceId = deviceId;
+    void setDeviceIds(const android::DeviceIdVector& deviceIds) {
+        mDeviceIds = deviceIds;
     }
 
     int32_t getSampleRate() const {
@@ -225,7 +226,7 @@
 
     int32_t                         mSamplesPerFrame      = AAUDIO_UNSPECIFIED;
     int32_t                         mSampleRate           = AAUDIO_UNSPECIFIED;
-    int32_t                         mDeviceId             = AAUDIO_UNSPECIFIED;
+    android::DeviceIdVector         mDeviceIds;
     aaudio_sharing_mode_t           mSharingMode          = AAUDIO_SHARING_MODE_SHARED;
     audio_format_t                  mAudioFormat          = AUDIO_FORMAT_DEFAULT;
     aaudio_direction_t              mDirection            = AAUDIO_DIRECTION_OUTPUT;
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index a75a2a1..8e3bcf7 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -79,7 +79,7 @@
     mSamplesPerFrame = builder.getSamplesPerFrame();
     mChannelMask = builder.getChannelMask();
     mSampleRate = builder.getSampleRate();
-    mDeviceId = builder.getDeviceId();
+    mDeviceIds = builder.getDeviceIds();
     mFormat = builder.getFormat();
     mSharingMode = builder.getSharingMode();
     mSharingModeMatchRequired = builder.isSharingModeMatchRequired();
@@ -204,7 +204,7 @@
     aaudio_result_t result = requestStart_l();
     if (result == AAUDIO_OK) {
         // We only call this for logging in "dumpsys audio". So ignore return code.
-        (void) mPlayerBase->startWithStatus(getDeviceId());
+        (void) mPlayerBase->startWithStatus(getDeviceIds());
     }
     return result;
 }
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index 3271882..3354adf 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -27,6 +27,7 @@
 #include <utils/StrongPointer.h>
 
 #include <aaudio/AAudio.h>
+#include <media/AudioContainers.h>
 #include <media/AudioSystem.h>
 #include <media/PlayerBase.h>
 #include <media/VolumeShaper.h>
@@ -268,8 +269,8 @@
         mPerformanceMode = performanceMode;
     }
 
-    int32_t getDeviceId() const {
-        return mDeviceId;
+    android::DeviceIdVector getDeviceIds() const {
+        return mDeviceIds;
     }
 
     aaudio_sharing_mode_t getSharingMode() const {
@@ -411,7 +412,7 @@
 
     // Implement AudioDeviceCallback
     void onAudioDeviceUpdate(audio_io_handle_t audioIo,
-            audio_port_handle_t deviceId) override {};
+            const android::DeviceIdVector& deviceIds) override {};
 
     // ============== I/O ===========================
     // A Stream will only implement read() or write() depending on its direction.
@@ -632,8 +633,8 @@
     }
     void setDisconnected();
 
-    void setDeviceId(int32_t deviceId) {
-        mDeviceId = deviceId;
+    void setDeviceIds(const android::DeviceIdVector& deviceIds) {
+        mDeviceIds = deviceIds;
     }
 
     // This should not be called after the open() call.
@@ -774,7 +775,7 @@
     int32_t                     mSampleRate = AAUDIO_UNSPECIFIED;
     int32_t                     mDeviceSampleRate = AAUDIO_UNSPECIFIED;
     int32_t                     mHardwareSampleRate = AAUDIO_UNSPECIFIED;
-    int32_t                     mDeviceId = AAUDIO_UNSPECIFIED;
+    android::DeviceIdVector     mDeviceIds;
     aaudio_sharing_mode_t       mSharingMode = AAUDIO_SHARING_MODE_SHARED;
     bool                        mSharingModeMatchRequired = false; // must match sharing mode requested
     audio_format_t              mFormat = AUDIO_FORMAT_DEFAULT;
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index b0dc669..73bd69f 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -307,8 +307,8 @@
           getSampleRate(), getSamplesPerFrame(), getChannelMask(), getFormat(),
           AAudio_convertSharingModeToShortText(getSharingMode()),
           AAudio_convertDirectionToText(getDirection()));
-    ALOGI("device = %6d, sessionId = %d, perfMode = %d, callback: %s with frames = %d",
-          getDeviceId(),
+    ALOGI("devices = %s, sessionId = %d, perfMode = %d, callback: %s with frames = %d",
+          android::toString(getDeviceIds()).c_str(),
           getSessionId(),
           getPerformanceMode(),
           ((getDataCallbackProc() != nullptr) ? "ON" : "OFF"),
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index 255bd0f..dfb9a01 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -260,36 +260,41 @@
 }
 
 void AudioStreamLegacy::onAudioDeviceUpdate(audio_io_handle_t /* audioIo */,
-            audio_port_handle_t deviceId) {
-    // Check for an invalid deviceId. Why change to UNSPECIFIED?
-    if (deviceId == AAUDIO_UNSPECIFIED) {
-        ALOGE("%s(, deviceId = AAUDIO_UNSPECIFIED)! Why?", __func__);
+            const android::DeviceIdVector& deviceIds) {
+    // Check for empty deviceIds. Callbacks for duplicating threads returns empty devices.
+    if (deviceIds.empty()) {
+        ALOGW("%s(empty deviceIds", __func__);
         return;
     }
+    android::DeviceIdVector oldDeviceIds = getDeviceIds();
     // Device routing is a common source of errors and DISCONNECTS.
     // Please leave this log in place. If there is a bug then this might
     // get called after the stream has been deleted so log before we
     // touch the stream object.
-    ALOGD("%s(deviceId = %d)", __func__, (int)deviceId);
-    if (getDeviceId() != AAUDIO_UNSPECIFIED
-            && getDeviceId() != deviceId
+    ALOGD("%s() devices %s => %s",
+            __func__, android::toString(oldDeviceIds).c_str(),
+            android::toString(deviceIds).c_str());
+    if (!oldDeviceIds.empty()
+            && !android::areDeviceIdsEqual(oldDeviceIds, deviceIds)
             && !isDisconnected()
             ) {
         // Note that isDataCallbackActive() is affected by state so call it before DISCONNECTING.
         // If we have a data callback and the stream is active, then ask the data callback
         // to DISCONNECT and call the error callback.
         if (isDataCallbackActive()) {
-            ALOGD("%s() request DISCONNECT in data callback, device %d => %d",
-                  __func__, (int) getDeviceId(), (int) deviceId);
+            ALOGD("%s() request DISCONNECT in data callback, devices %s => %s",
+                    __func__, android::toString(oldDeviceIds).c_str(),
+                    android::toString(deviceIds).c_str());
             // If the stream is stopped before the data callback has a chance to handle the
             // request then the requestStop_l() and requestPause() methods will handle it after
             // the callback has stopped.
             mRequestDisconnect.request();
         } else {
-            ALOGD("%s() DISCONNECT the stream now, device %d => %d",
-                  __func__, (int) getDeviceId(), (int) deviceId);
+            ALOGD("%s() DISCONNECT the stream now, devices %s => %s",
+                    __func__, android::toString(oldDeviceIds).c_str(),
+                    android::toString(deviceIds).c_str());
             forceDisconnect();
         }
     }
-    setDeviceId(deviceId);
+    setDeviceIds(deviceIds);
 }
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.h b/media/libaaudio/src/legacy/AudioStreamLegacy.h
index 53f6e06..a729161 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.h
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.h
@@ -95,7 +95,7 @@
                                      android::ExtendedTimestamp *extendedTimestamp);
 
     void onAudioDeviceUpdate(audio_io_handle_t audioIo,
-            audio_port_handle_t deviceId) override;
+            const android::DeviceIdVector& deviceIds) override;
 
     /*
      * Check to see whether a callback thread has requested a disconnected.
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index fe4bf2c..1591f7d 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -112,9 +112,7 @@
     mCallbackBufferSize = builder.getFramesPerDataCallback();
 
     // Don't call mAudioRecord->setInputDevice() because it will be overwritten by set()!
-    audio_port_handle_t selectedDeviceId = (getDeviceId() == AAUDIO_UNSPECIFIED)
-                                           ? AUDIO_PORT_HANDLE_NONE
-                                           : getDeviceId();
+    audio_port_handle_t selectedDeviceId = getFirstDeviceId(getDeviceIds());
 
     const audio_content_type_t contentType =
             AAudioConvert_contentTypeToInternal(builder.getContentType());
@@ -198,7 +196,8 @@
                  AudioGlobal_convertPerformanceModeToText(builder.getPerformanceMode()))
             .set(AMEDIAMETRICS_PROP_SHARINGMODE,
                  AudioGlobal_convertSharingModeToText(builder.getSharingMode()))
-            .set(AMEDIAMETRICS_PROP_ENCODINGCLIENT, toString(requestedFormat).c_str()).record();
+            .set(AMEDIAMETRICS_PROP_ENCODINGCLIENT,
+                 android::toString(requestedFormat).c_str()).record();
 
     // Get the actual values from the AudioRecord.
     setChannelMask(AAudioConvert_androidToAAudioChannelMask(
@@ -275,7 +274,7 @@
              perfMode, actualPerformanceMode);
 
     setState(AAUDIO_STREAM_STATE_OPEN);
-    setDeviceId(mAudioRecord->getRoutedDeviceId());
+    setDeviceIds(mAudioRecord->getRoutedDeviceIds());
 
     aaudio_session_id_t actualSessionId =
             (requestedSessionId == AAUDIO_SESSION_ID_NONE)
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 16c0bcd..2e57f0d 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -132,9 +132,7 @@
           notificationFrames, (uint)frameCount);
 
     // Don't call mAudioTrack->setDeviceId() because it will be overwritten by set()!
-    audio_port_handle_t selectedDeviceId = (getDeviceId() == AAUDIO_UNSPECIFIED)
-                                           ? AUDIO_PORT_HANDLE_NONE
-                                           : getDeviceId();
+    audio_port_handle_t selectedDeviceId = getFirstDeviceId(getDeviceIds());
 
     const audio_content_type_t contentType =
             AAudioConvert_contentTypeToInternal(builder.getContentType());
@@ -197,7 +195,8 @@
                  AudioGlobal_convertPerformanceModeToText(builder.getPerformanceMode()))
             .set(AMEDIAMETRICS_PROP_SHARINGMODE,
                  AudioGlobal_convertSharingModeToText(builder.getSharingMode()))
-            .set(AMEDIAMETRICS_PROP_ENCODINGCLIENT, toString(getFormat()).c_str()).record();
+            .set(AMEDIAMETRICS_PROP_ENCODINGCLIENT,
+                 android::toString(getFormat()).c_str()).record();
 
     doSetVolume();
 
@@ -233,7 +232,7 @@
         mBlockAdapter = nullptr;
     }
 
-    setDeviceId(mAudioTrack->getRoutedDeviceId());
+    setDeviceIds(mAudioTrack->getRoutedDeviceIds());
 
     aaudio_session_id_t actualSessionId =
             (requestedSessionId == AAUDIO_SESSION_ID_NONE)
@@ -317,7 +316,7 @@
     if (mAudioTrack->channelCount() != getSamplesPerFrame()
           || mAudioTrack->format() != getFormat()
           || mAudioTrack->getSampleRate() != getSampleRate()
-          || mAudioTrack->getRoutedDeviceId() != getDeviceId()
+          || !areDeviceIdsEqual(mAudioTrack->getRoutedDeviceIds(), getDeviceIds())
           || getBufferCapacityFromDevice() != getBufferCapacity()
           || getFramesPerBurstFromDevice() != getFramesPerBurst()) {
         AudioStreamLegacy::onNewIAudioTrack();
diff --git a/media/libaaudio/src/libaaudio.map.txt b/media/libaaudio/src/libaaudio.map.txt
index 13c19a1..36d76aa 100644
--- a/media/libaaudio/src/libaaudio.map.txt
+++ b/media/libaaudio/src/libaaudio.map.txt
@@ -72,6 +72,7 @@
     AAudioStream_getHardwareSampleRate;   # introduced=UpsideDownCake
     AAudio_getPlatformMMapPolicy; # introduced=36
     AAudio_getPlatformMMapExclusivePolicy; #introduced=36
+    AAudioStream_getDeviceIds; # introduced=36
 
     AAudioStreamBuilder_setTags; # systemapi
     AAudioStream_getTags; # systemapi
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index 1e6be68..9a4b45d 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -731,7 +731,7 @@
         mSelectedDeviceId = deviceId;
         if (mStatus == NO_ERROR) {
             if (mActive) {
-                if (mSelectedDeviceId != mRoutedDeviceId) {
+                if (getFirstDeviceId(mRoutedDeviceIds) != mSelectedDeviceId) {
                     // stop capture so that audio policy manager does not reject the new instance
                     // start request as only one capture can be active at a time.
                     if (mAudioRecord != 0) {
@@ -758,7 +758,7 @@
 }
 
 // must be called with mLock held
-void AudioRecord::updateRoutedDeviceId_l()
+void AudioRecord::updateRoutedDeviceIds_l()
 {
     // if the record is inactive, do not update actual device as the input stream maybe routed
     // from a device not relevant to this client because of other active use cases.
@@ -766,17 +766,21 @@
         return;
     }
     if (mInput != AUDIO_IO_HANDLE_NONE) {
-        audio_port_handle_t deviceId = AudioSystem::getDeviceIdForIo(mInput);
-        if (deviceId != AUDIO_PORT_HANDLE_NONE) {
-            mRoutedDeviceId = deviceId;
+        DeviceIdVector deviceIds;
+        status_t result = AudioSystem::getDeviceIdsForIo(mInput, deviceIds);
+        if (result != OK) {
+            ALOGW("%s: getDeviceIdsForIo returned: %d", __func__, result);
+        }
+        if (!deviceIds.empty()) {
+            mRoutedDeviceIds = deviceIds;
         }
      }
 }
 
-audio_port_handle_t AudioRecord::getRoutedDeviceId() {
+DeviceIdVector AudioRecord::getRoutedDeviceIds() {
     AutoMutex lock(mLock);
-    updateRoutedDeviceId_l();
-    return mRoutedDeviceId;
+    updateRoutedDeviceIds_l();
+    return mRoutedDeviceIds;
 }
 
 status_t AudioRecord::dump(int fd, const Vector<String16>& args __unused) const
@@ -794,10 +798,11 @@
                   mFrameCount, mReqFrameCount);
     result.appendFormat("  notif. frame count(%u), req. notif. frame count(%u)\n",
              mNotificationFramesAct, mNotificationFramesReq);
-    result.appendFormat("  input(%d), latency(%u), selected device Id(%d), routed device Id(%d)\n",
-                        mInput, mLatency, mSelectedDeviceId, mRoutedDeviceId);
-    result.appendFormat("  mic direction(%d) mic field dimension(%f)",
-                        mSelectedMicDirection, mSelectedMicFieldDimension);
+    result.appendFormat("  input(%d), latency(%u), selected device Id(%d)\n",
+                        mInput, mLatency, mSelectedDeviceId);
+    result.appendFormat("  routed device Ids(%s), mic direction(%d) mic field dimension(%f)",
+                        toString(mRoutedDeviceIds).c_str(), mSelectedMicDirection,
+                        mSelectedMicFieldDimension);
     ::write(fd, result.c_str(), result.size());
     return NO_ERROR;
 }
@@ -940,7 +945,7 @@
         mAwaitBoost = true;
     }
     mFlags = output.flags;
-    mRoutedDeviceId = output.selectedDeviceId;
+    mRoutedDeviceIds = { output.selectedDeviceId };
     mSessionId = output.sessionId;
     mSampleRate = output.sampleRate;
     mServerConfig = output.serverConfig;
@@ -1063,7 +1068,8 @@
         .set(AMEDIAMETRICS_PROP_SOURCE, toString(mAttributes.source).c_str())
         .set(AMEDIAMETRICS_PROP_THREADID, (int32_t)output.inputId)
         .set(AMEDIAMETRICS_PROP_SELECTEDDEVICEID, (int32_t)mSelectedDeviceId)
-        .set(AMEDIAMETRICS_PROP_ROUTEDDEVICEID, (int32_t)mRoutedDeviceId)
+        .set(AMEDIAMETRICS_PROP_ROUTEDDEVICEID, (int32_t)(getFirstDeviceId(mRoutedDeviceIds)))
+        .set(AMEDIAMETRICS_PROP_ROUTEDDEVICEIDS, toString(mRoutedDeviceIds).c_str())
         .set(AMEDIAMETRICS_PROP_ENCODING, toString(mFormat).c_str())
         .set(AMEDIAMETRICS_PROP_CHANNELMASK, (int32_t)mChannelMask)
         .set(AMEDIAMETRICS_PROP_FRAMECOUNT, (int32_t)mFrameCount)
@@ -1656,7 +1662,7 @@
 }
 
 void AudioRecord::onAudioDeviceUpdate(audio_io_handle_t audioIo,
-                                 audio_port_handle_t deviceId)
+                                      const DeviceIdVector& deviceIds)
 {
     sp<AudioSystem::AudioDeviceCallback> callback;
     {
@@ -1668,11 +1674,11 @@
         // only update device if the record is active as route changes due to other use cases are
         // irrelevant for this client
         if (mActive) {
-            mRoutedDeviceId = deviceId;
+            mRoutedDeviceIds = deviceIds;
         }
     }
     if (callback.get() != nullptr) {
-        callback->onAudioDeviceUpdate(mInput, mRoutedDeviceId);
+        callback->onAudioDeviceUpdate(mInput, mRoutedDeviceIds);
     }
 }
 
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 47d5dd5..b8dadb4 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -683,7 +683,7 @@
 
     if (ioDesc->getIoHandle() == AUDIO_IO_HANDLE_NONE) return Status::ok();
 
-    audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE;
+    DeviceIdVector deviceIds;
     std::vector<sp<AudioDeviceCallback>> callbacksToCall;
     {
         std::lock_guard _l(mMutex);
@@ -695,12 +695,12 @@
             case AUDIO_INPUT_OPENED:
             case AUDIO_INPUT_REGISTERED: {
                 if (sp<AudioIoDescriptor> oldDesc = getIoDescriptor_l(ioDesc->getIoHandle())) {
-                    deviceId = oldDesc->getDeviceId();
+                    deviceIds = oldDesc->getDeviceIds();
                 }
                 mIoDescriptors[ioDesc->getIoHandle()] = ioDesc;
 
-                if (ioDesc->getDeviceId() != AUDIO_PORT_HANDLE_NONE) {
-                    deviceId = ioDesc->getDeviceId();
+                if (!ioDesc->getDeviceIds().empty()) {
+                    deviceIds = ioDesc->getDeviceIds();
                     if (event == AUDIO_OUTPUT_OPENED || event == AUDIO_INPUT_OPENED) {
                         auto it = mAudioDeviceCallbacks.find(ioDesc->getIoHandle());
                         if (it != mAudioDeviceCallbacks.end()) {
@@ -741,11 +741,12 @@
                     break;
                 }
 
-                deviceId = oldDesc->getDeviceId();
+                deviceIds = oldDesc->getDeviceIds();
                 mIoDescriptors[ioDesc->getIoHandle()] = ioDesc;
 
-                if (deviceId != ioDesc->getDeviceId()) {
-                    deviceId = ioDesc->getDeviceId();
+                DeviceIdVector ioDescDeviceIds = ioDesc->getDeviceIds();
+                if (!areDeviceIdsEqual(deviceIds, ioDescDeviceIds)) {
+                    deviceIds = ioDescDeviceIds;
                     auto it = mAudioDeviceCallbacks.find(ioDesc->getIoHandle());
                     if (it != mAudioDeviceCallbacks.end()) {
                         callbacks = it->second;
@@ -773,7 +774,7 @@
                     auto it2 = cbks.find(ioDesc->getPortId());
                     if (it2 != cbks.end()) {
                         callbacks.emplace(ioDesc->getPortId(), it2->second);
-                        deviceId = oldDesc->getDeviceId();
+                        deviceIds = oldDesc->getDeviceIds();
                     }
                 }
             }
@@ -792,7 +793,7 @@
     // example getRoutedDevice that updates the device and tries to acquire mMutex.
     for (auto cb  : callbacksToCall) {
         // If callbacksToCall is not empty, it implies ioDesc->getIoHandle() and deviceId are valid
-        cb->onAudioDeviceUpdate(ioDesc->getIoHandle(), deviceId);
+        cb->onAudioDeviceUpdate(ioDesc->getIoHandle(), deviceIds);
     }
 
     return Status::ok();
@@ -1964,14 +1965,16 @@
     return afc->removeSupportedLatencyModesCallback(callback);
 }
 
-audio_port_handle_t AudioSystem::getDeviceIdForIo(audio_io_handle_t audioIo) {
+status_t AudioSystem::getDeviceIdsForIo(audio_io_handle_t audioIo, DeviceIdVector& deviceIds) {
     const sp<IAudioFlinger> af = get_audio_flinger();
     if (af == nullptr) return AudioFlingerServiceTraits::getError();
     const sp<AudioIoDescriptor> desc = getIoDescriptor(audioIo);
     if (desc == 0) {
-        return AUDIO_PORT_HANDLE_NONE;
+        deviceIds.clear();
+    } else {
+        deviceIds = desc->getDeviceIds();
     }
-    return desc->getDeviceId();
+    return OK;
 }
 
 status_t AudioSystem::acquireSoundTriggerSession(audio_session_t* session,
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index a9409eb..3591fbf 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -1736,7 +1736,7 @@
                 // allow track invalidation when track is not playing to propagate
                 // the updated mSelectedDeviceId
                 if (isPlaying_l()) {
-                    if (mSelectedDeviceId != mRoutedDeviceId) {
+                    if (getFirstDeviceId(mRoutedDeviceIds) != mSelectedDeviceId) {
                         android_atomic_or(CBLK_INVALID, &mCblk->mFlags);
                         mProxy->interrupt();
                     }
@@ -1759,7 +1759,7 @@
 }
 
 // must be called with mLock held
-void AudioTrack::updateRoutedDeviceId_l()
+void AudioTrack::updateRoutedDeviceIds_l()
 {
     // if the track is inactive, do not update actual device as the output stream maybe routed
     // to a device not relevant to this client because of other active use cases.
@@ -1767,17 +1767,21 @@
         return;
     }
     if (mOutput != AUDIO_IO_HANDLE_NONE) {
-        audio_port_handle_t deviceId = AudioSystem::getDeviceIdForIo(mOutput);
-        if (deviceId != AUDIO_PORT_HANDLE_NONE) {
-            mRoutedDeviceId = deviceId;
+        DeviceIdVector deviceIds;
+        status_t result = AudioSystem::getDeviceIdsForIo(mOutput, deviceIds);
+        if (result != OK) {
+            ALOGW("%s: getDeviceIdsForIo returned: %d", __func__, result);
+        }
+        if (!deviceIds.empty()) {
+            mRoutedDeviceIds = deviceIds;
         }
     }
 }
 
-audio_port_handle_t AudioTrack::getRoutedDeviceId() {
+DeviceIdVector AudioTrack::getRoutedDeviceIds() {
     AutoMutex lock(mLock);
-    updateRoutedDeviceId_l();
-    return mRoutedDeviceId;
+    updateRoutedDeviceIds_l();
+    return mRoutedDeviceIds;
 }
 
 status_t AudioTrack::attachAuxEffect(int effectId)
@@ -1937,7 +1941,7 @@
 
     mFrameCount = output.frameCount;
     mNotificationFramesAct = (uint32_t)output.notificationFrameCount;
-    mRoutedDeviceId = output.selectedDeviceId;
+    mRoutedDeviceIds = output.selectedDeviceIds;
     mSessionId = output.sessionId;
     mStreamType = output.streamType;
 
@@ -2106,7 +2110,8 @@
         .set(AMEDIAMETRICS_PROP_USAGE, toString(mAttributes.usage).c_str())
         .set(AMEDIAMETRICS_PROP_THREADID, (int32_t)output.outputId)
         .set(AMEDIAMETRICS_PROP_SELECTEDDEVICEID, (int32_t)mSelectedDeviceId)
-        .set(AMEDIAMETRICS_PROP_ROUTEDDEVICEID, (int32_t)mRoutedDeviceId)
+        .set(AMEDIAMETRICS_PROP_ROUTEDDEVICEID, (int32_t)(getFirstDeviceId(mRoutedDeviceIds)))
+        .set(AMEDIAMETRICS_PROP_ROUTEDDEVICEIDS, toString(mRoutedDeviceIds).c_str())
         .set(AMEDIAMETRICS_PROP_ENCODING, toString(mFormat).c_str())
         .set(AMEDIAMETRICS_PROP_CHANNELMASK, (int32_t)mChannelMask)
         .set(AMEDIAMETRICS_PROP_FRAMECOUNT, (int32_t)mFrameCount)
@@ -3555,8 +3560,8 @@
     result.appendFormat("  notif. frame count(%u), req. notif. frame count(%u),"
             " req. notif. per buff(%u)\n",
              mNotificationFramesAct, mNotificationFramesReq, mNotificationsPerBufferReq);
-    result.appendFormat("  latency (%d), selected device Id(%d), routed device Id(%d)\n",
-                        mLatency, mSelectedDeviceId, mRoutedDeviceId);
+    result.appendFormat("  latency (%d), selected device Id(%d), routed device Ids(%s)\n",
+                        mLatency, mSelectedDeviceId, toString(mRoutedDeviceIds).c_str());
     result.appendFormat("  output(%d) AF latency (%u) AF frame count(%zu) AF SampleRate(%u)\n",
                         mOutput, mAfLatency, mAfFrameCount, mAfSampleRate);
     ::write(fd, result.c_str(), result.size());
@@ -3623,7 +3628,7 @@
 
     // 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);
+        mAudioManager->playerEvent(mPlayerIId, PLAYER_UPDATE_PORT_ID, {mPortId});
     }
 }
 
@@ -3672,7 +3677,7 @@
 
 
 void AudioTrack::onAudioDeviceUpdate(audio_io_handle_t audioIo,
-                                 audio_port_handle_t deviceId)
+                                     const DeviceIdVector& deviceIds)
 {
     sp<AudioSystem::AudioDeviceCallback> callback;
     {
@@ -3684,12 +3689,12 @@
         // only update device if the track is active as route changes due to other use cases are
         // irrelevant for this client
         if (mState == STATE_ACTIVE) {
-            mRoutedDeviceId = deviceId;
+            mRoutedDeviceIds = deviceIds;
         }
     }
 
     if (callback.get() != nullptr) {
-        callback->onAudioDeviceUpdate(mOutput, mRoutedDeviceId);
+        callback->onAudioDeviceUpdate(mOutput, mRoutedDeviceIds);
     }
 }
 
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 168b47e..1523607 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -103,8 +103,8 @@
     aidl.flags = VALUE_OR_RETURN(legacy2aidl_audio_output_flags_t_int32_t_mask(flags));
     aidl.frameCount = VALUE_OR_RETURN(convertIntegral<int64_t>(frameCount));
     aidl.notificationFrameCount = VALUE_OR_RETURN(convertIntegral<int64_t>(notificationFrameCount));
-    aidl.selectedDeviceId = VALUE_OR_RETURN(
-            legacy2aidl_audio_port_handle_t_int32_t(selectedDeviceId));
+    aidl.selectedDeviceIds = VALUE_OR_RETURN(convertContainer<std::vector<int32_t>>(
+            selectedDeviceIds, legacy2aidl_audio_port_handle_t_int32_t));
     aidl.sessionId = VALUE_OR_RETURN(legacy2aidl_audio_session_t_int32_t(sessionId));
     aidl.sampleRate = VALUE_OR_RETURN(convertIntegral<int32_t>(sampleRate));
     aidl.streamType =  VALUE_OR_RETURN(
@@ -132,8 +132,8 @@
     legacy.frameCount = VALUE_OR_RETURN(convertIntegral<size_t>(aidl.frameCount));
     legacy.notificationFrameCount = VALUE_OR_RETURN(
             convertIntegral<size_t>(aidl.notificationFrameCount));
-    legacy.selectedDeviceId = VALUE_OR_RETURN(
-            aidl2legacy_int32_t_audio_port_handle_t(aidl.selectedDeviceId));
+    legacy.selectedDeviceIds = VALUE_OR_RETURN(convertContainer<DeviceIdVector>(
+            aidl.selectedDeviceIds, aidl2legacy_int32_t_audio_port_handle_t));
     legacy.sessionId = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_session_t(aidl.sessionId));
     legacy.sampleRate = VALUE_OR_RETURN(convertIntegral<uint32_t>(aidl.sampleRate));
     legacy.streamType = VALUE_OR_RETURN(
diff --git a/media/libaudioclient/PlayerBase.cpp b/media/libaudioclient/PlayerBase.cpp
index 651255a..5999040 100644
--- a/media/libaudioclient/PlayerBase.cpp
+++ b/media/libaudioclient/PlayerBase.cpp
@@ -30,8 +30,7 @@
 PlayerBase::PlayerBase() : BnPlayer(),
         mPanMultiplierL(1.0f), mPanMultiplierR(1.0f),
         mVolumeMultiplierL(1.0f), mVolumeMultiplierR(1.0f),
-        mPIId(PLAYER_PIID_INVALID), mLastReportedEvent(PLAYER_STATE_UNKNOWN),
-        mLastReportedDeviceId(AUDIO_PORT_HANDLE_NONE)
+        mPIId(PLAYER_PIID_INVALID), mLastReportedEvent(PLAYER_STATE_UNKNOWN)
 {
     ALOGD("PlayerBase::PlayerBase()");
     // use checkService() to avoid blocking if audio service is not up yet
@@ -68,7 +67,7 @@
     }
 
     if (mPIId != PLAYER_PIID_INVALID && portId != AUDIO_PORT_HANDLE_NONE) {
-        mAudioManager->playerEvent(mPIId, android::PLAYER_UPDATE_PORT_ID, portId);
+        mAudioManager->playerEvent(mPIId, android::PLAYER_UPDATE_PORT_ID, { portId });
     }
 }
 
@@ -80,13 +79,13 @@
 }
 
 //------------------------------------------------------------------------------
-void PlayerBase::servicePlayerEvent(player_state_t event, audio_port_handle_t deviceId) {
+void PlayerBase::servicePlayerEvent(player_state_t event, const DeviceIdVector& deviceIds) {
     if (mAudioManager != 0) {
         bool changed = false;
         {
             Mutex::Autolock _l(mDeviceIdLock);
-            changed = mLastReportedDeviceId != deviceId;
-            mLastReportedDeviceId = deviceId;
+            changed = !areDeviceIdsEqual(deviceIds, mLastReportedDeviceIds);
+            mLastReportedDeviceIds = deviceIds;
         }
 
         {
@@ -99,7 +98,7 @@
             }
         }
         if (changed && (mPIId != PLAYER_PIID_INVALID)) {
-            mAudioManager->playerEvent(mPIId, event, deviceId);
+            mAudioManager->playerEvent(mPIId, event, deviceIds);
         }
     }
 }
@@ -112,18 +111,18 @@
 }
 
 //FIXME temporary method while some player state is outside of this class
-void PlayerBase::reportEvent(player_state_t event, audio_port_handle_t deviceId) {
-    servicePlayerEvent(event, deviceId);
+void PlayerBase::reportEvent(player_state_t event, const DeviceIdVector& deviceIds) {
+    servicePlayerEvent(event, deviceIds);
 }
 
-void PlayerBase::baseUpdateDeviceId(audio_port_handle_t deviceId) {
-    servicePlayerEvent(PLAYER_UPDATE_DEVICE_ID, deviceId);
+void PlayerBase::baseUpdateDeviceIds(const DeviceIdVector& deviceIds) {
+    servicePlayerEvent(PLAYER_UPDATE_DEVICE_ID, deviceIds);
 }
 
-status_t PlayerBase::startWithStatus(audio_port_handle_t deviceId) {
+status_t PlayerBase::startWithStatus(const DeviceIdVector& deviceIds) {
     status_t status = playerStart();
     if (status == NO_ERROR) {
-        servicePlayerEvent(PLAYER_STATE_STARTED, deviceId);
+        servicePlayerEvent(PLAYER_STATE_STARTED, deviceIds);
     } else {
         ALOGW("PlayerBase::start() error %d", status);
     }
@@ -133,7 +132,7 @@
 status_t PlayerBase::pauseWithStatus() {
     status_t status = playerPause();
     if (status == NO_ERROR) {
-        servicePlayerEvent(PLAYER_STATE_PAUSED, AUDIO_PORT_HANDLE_NONE);
+        servicePlayerEvent(PLAYER_STATE_PAUSED, {});
     } else {
         ALOGW("PlayerBase::pause() error %d", status);
     }
@@ -144,7 +143,7 @@
     status_t status = playerStop();
 
     if (status == NO_ERROR) {
-        servicePlayerEvent(PLAYER_STATE_STOPPED, AUDIO_PORT_HANDLE_NONE);
+        servicePlayerEvent(PLAYER_STATE_STOPPED, {});
     } else {
         ALOGW("PlayerBase::stop() error %d", status);
     }
@@ -155,12 +154,12 @@
 // Implementation of IPlayer
 binder::Status PlayerBase::start() {
     ALOGD("PlayerBase::start() from IPlayer");
-    audio_port_handle_t deviceId;
+    DeviceIdVector deviceIds;
     {
         Mutex::Autolock _l(mDeviceIdLock);
-        deviceId = mLastReportedDeviceId;
+        deviceIds = mLastReportedDeviceIds;
     }
-    (void)startWithStatus(deviceId);
+    (void)startWithStatus(deviceIds);
     return binder::Status::ok();
 }
 
diff --git a/media/libaudioclient/TrackPlayerBase.cpp b/media/libaudioclient/TrackPlayerBase.cpp
index bc38251..7928c65 100644
--- a/media/libaudioclient/TrackPlayerBase.cpp
+++ b/media/libaudioclient/TrackPlayerBase.cpp
@@ -60,8 +60,8 @@
 }
 
 void TrackPlayerBase::SelfAudioDeviceCallback::onAudioDeviceUpdate(audio_io_handle_t __unused,
-                                                                   audio_port_handle_t deviceId) {
-    mSelf.baseUpdateDeviceId(deviceId);
+        const DeviceIdVector& deviceIds) {
+    mSelf.baseUpdateDeviceIds(deviceIds);
 }
 
 void TrackPlayerBase::doDestroy() {
diff --git a/media/libaudioclient/aidl/android/media/CreateTrackResponse.aidl b/media/libaudioclient/aidl/android/media/CreateTrackResponse.aidl
index ab60461..0c9a947 100644
--- a/media/libaudioclient/aidl/android/media/CreateTrackResponse.aidl
+++ b/media/libaudioclient/aidl/android/media/CreateTrackResponse.aidl
@@ -33,8 +33,8 @@
     int flags;
     long frameCount;
     long notificationFrameCount;
-    /** Interpreted as audio_port_handle_t. */
-    int selectedDeviceId;
+    /** Interpreted as audio_port_handle_t[]. */
+    int[] selectedDeviceIds;
     int sessionId;
     int sampleRate;
     AudioStreamType streamType;
diff --git a/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp b/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
index b0b7e03..ba5b3b1 100644
--- a/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
+++ b/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
@@ -396,7 +396,7 @@
         static_cast<audio_port_handle_t>(mFdp.ConsumeIntegral<int32_t>());
     record->setInputDevice(deviceId);
     record->getInputDevice();
-    record->getRoutedDeviceId();
+    record->getRoutedDeviceIds();
     record->getPortId();
 }
 
diff --git a/media/libaudioclient/include/media/AudioIoDescriptor.h b/media/libaudioclient/include/media/AudioIoDescriptor.h
index 405ec7d..961cc1c 100644
--- a/media/libaudioclient/include/media/AudioIoDescriptor.h
+++ b/media/libaudioclient/include/media/AudioIoDescriptor.h
@@ -69,12 +69,21 @@
     size_t getFrameCountHAL() const { return mFrameCountHAL; }
     uint32_t getLatency() const { return mLatency; }
     audio_port_handle_t getPortId() const { return mPortId; }
-    audio_port_handle_t getDeviceId() const {
-        if (mPatch.num_sources != 0 && mPatch.num_sinks != 0) {
-            // FIXME: the API only returns the first device in case of multiple device selection
-            return mIsInput ? mPatch.sources[0].id : mPatch.sinks[0].id;
+    std::vector<audio_port_handle_t> getDeviceIds() const {
+        std::vector<audio_port_handle_t> deviceIds;
+        if (mPatch.num_sources == 0 || mPatch.num_sinks == 0) {
+            return deviceIds;
         }
-        return AUDIO_PORT_HANDLE_NONE;
+        if (mIsInput) {
+            for (unsigned int i = 0; i < mPatch.num_sources; i++) {
+                deviceIds.push_back(mPatch.sources[i].id);
+            }
+        } else {
+            for (unsigned int i = 0; i < mPatch.num_sinks; i++) {
+                deviceIds.push_back(mPatch.sinks[i].id);
+            }
+        }
+        return deviceIds;
     }
     void setPatch(const audio_patch& patch) { mPatch = patch; }
 
@@ -88,7 +97,13 @@
                    (mIsInput ? audio_channel_in_mask_to_string(mChannelMask) :
                            audio_channel_out_mask_to_string(mChannelMask)))
            << ", frameCount " << mFrameCount << ", frameCountHAL " << mFrameCountHAL
-           << ", deviceId " << getDeviceId();
+           << ", deviceIds ";
+
+        std::vector<audio_port_handle_t> deviceIds = getDeviceIds();
+        for (auto deviceId : deviceIds) {
+            ss << deviceId << " ";
+        }
+
         return ss.str();
     }
 
diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h
index 25d91d3..80a756e 100644
--- a/media/libaudioclient/include/media/AudioRecord.h
+++ b/media/libaudioclient/include/media/AudioRecord.h
@@ -495,19 +495,19 @@
      */
             audio_port_handle_t getInputDevice();
 
-     /* Returns the ID of the audio device actually used by the input to which this AudioRecord
+     /* Returns the IDs of the audio devices actually used by the input to which this AudioRecord
       * is attached.
-      * The device ID is relevant only if the AudioRecord is active.
-      * When the AudioRecord is inactive, the device ID returned can be either:
-      * - AUDIO_PORT_HANDLE_NONE if the AudioRecord is not attached to any output.
-      * - The device ID used before paused or stopped.
+      * The device IDs is relevant only if the AudioRecord is active.
+      * When the AudioRecord is inactive, the device IDs returned can be either:
+      * - An empty vector if the AudioRecord is not attached to any output.
+      * - The device IDs used before paused or stopped.
       * - The device ID selected by audio policy manager of setOutputDevice() if the AudioRecord
       * has not been started yet.
       *
       * Parameters:
       *  none.
       */
-     audio_port_handle_t getRoutedDeviceId();
+     DeviceIdVector getRoutedDeviceIds();
 
     /* Add an AudioDeviceCallback. The caller will be notified when the audio device
      * to which this AudioRecord is routed is updated.
@@ -534,7 +534,7 @@
 
             // AudioSystem::AudioDeviceCallback> virtuals
             virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
-                                             audio_port_handle_t deviceId);
+                                             const DeviceIdVector& deviceIds);
 
 private:
     /* If nonContig is non-NULL, it is an output parameter that will be set to the number of
@@ -678,7 +678,7 @@
             // FIXME enum is faster than strcmp() for parameter 'from'
             status_t restoreRecord_l(const char *from);
 
-            void     updateRoutedDeviceId_l();
+            void     updateRoutedDeviceIds_l();
 
     sp<AudioRecordThread>   mAudioRecordThread;
     mutable Mutex           mLock;
@@ -810,7 +810,7 @@
     audio_port_handle_t     mSelectedDeviceId = AUDIO_PORT_HANDLE_NONE;
     // Device actually selected by AudioPolicyManager: This may not match the app
     // selection depending on other activity and connected devices
-    audio_port_handle_t     mRoutedDeviceId = AUDIO_PORT_HANDLE_NONE;
+    DeviceIdVector          mRoutedDeviceIds;
 
     wp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
 
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index fbc7629..45ede3c 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -767,7 +767,7 @@
         virtual ~AudioDeviceCallback() {}
 
         virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
-                                         audio_port_handle_t deviceId) = 0;
+                                         const DeviceIdVector& deviceIds) = 0;
     };
 
     static status_t addAudioDeviceCallback(const wp<AudioDeviceCallback>& callback,
@@ -793,7 +793,7 @@
     static status_t removeSupportedLatencyModesCallback(
             const sp<SupportedLatencyModesCallback>& callback);
 
-    static audio_port_handle_t getDeviceIdForIo(audio_io_handle_t audioIo);
+    static status_t getDeviceIdsForIo(audio_io_handle_t audioIo, DeviceIdVector& deviceIds);
 
     static status_t setVibratorInfos(const std::vector<media::AudioVibratorInfo>& vibratorInfos);
 
@@ -842,7 +842,8 @@
         status_t removeSupportedLatencyModesCallback(
                 const sp<SupportedLatencyModesCallback>& callback) EXCLUDES(mMutex);
 
-        audio_port_handle_t getDeviceIdForIo(audio_io_handle_t audioIo) EXCLUDES(mMutex);
+        status_t getDeviceIdsForIo(audio_io_handle_t audioIo, DeviceIdVector& deviceIds)
+                EXCLUDES(mMutex);
 
     private:
         mutable std::mutex mMutex;
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index de97863..330b5ee 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -835,18 +835,18 @@
      */
      audio_port_handle_t getOutputDevice();
 
-     /* Returns the ID of the audio device actually used by the output to which this AudioTrack is
+     /* Returns the IDs of the audio devices actually used by the output to which this AudioTrack is
       * attached.
       * When the AudioTrack is inactive, the device ID returned can be either:
-      * - AUDIO_PORT_HANDLE_NONE if the AudioTrack is not attached to any output.
-      * - The device ID used before paused or stopped.
+      * - An empty vector if the AudioTrack is not attached to any output.
+      * - The device IDs used before paused or stopped.
       * - The device ID selected by audio policy manager of setOutputDevice() if the AudioTrack
       * has not been started yet.
       *
       * Parameters:
       *  none.
       */
-     audio_port_handle_t getRoutedDeviceId();
+     DeviceIdVector getRoutedDeviceIds();
 
     /* Returns the unique session ID associated with this track.
      *
@@ -1089,7 +1089,7 @@
 
             // AudioSystem::AudioDeviceCallback> virtuals
             virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
-                                             audio_port_handle_t deviceId);
+                                             const DeviceIdVector& deviceIds);
 
     /* Obtain the pending duration in milliseconds for playback of pure PCM
      * (mixable without embedded timing) data remaining in AudioTrack.
@@ -1258,7 +1258,7 @@
 
             void     restartIfDisabled();
 
-            void     updateRoutedDeviceId_l();
+            void     updateRoutedDeviceIds_l();
 
             /* Sets the Dual Mono mode presentation on the output device. */
             status_t setDualMonoMode_l(audio_dual_mono_mode_t mode);
@@ -1482,9 +1482,9 @@
     // Device requested by the application.
     audio_port_handle_t mSelectedDeviceId = AUDIO_PORT_HANDLE_NONE;
 
-    // Device actually selected by AudioPolicyManager: This may not match the app
+    // Devices actually selected by AudioPolicyManager: This may not match the app
     // selection depending on other activity and connected devices.
-    audio_port_handle_t mRoutedDeviceId = AUDIO_PORT_HANDLE_NONE;
+    DeviceIdVector             mRoutedDeviceIds;
 
     sp<media::VolumeHandler>       mVolumeHandler;
 
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index 21ecb09..8292eef 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -109,7 +109,7 @@
         audio_output_flags_t flags;
         size_t frameCount;
         size_t notificationFrameCount;
-        audio_port_handle_t selectedDeviceId;
+        DeviceIdVector selectedDeviceIds;
         audio_session_t sessionId;
 
         /* output */
diff --git a/media/libaudioclient/include/media/PlayerBase.h b/media/libaudioclient/include/media/PlayerBase.h
index 5475f76..5df1a6e 100644
--- a/media/libaudioclient/include/media/PlayerBase.h
+++ b/media/libaudioclient/include/media/PlayerBase.h
@@ -22,6 +22,7 @@
 #include <utils/Mutex.h>
 
 #include "android/media/BnPlayer.h"
+#include "media/AudioContainers.h"
 
 namespace android {
 
@@ -44,14 +45,14 @@
             const media::VolumeShaperConfiguration& configuration,
             const media::VolumeShaperOperation& operation) override;
 
-            status_t startWithStatus(audio_port_handle_t deviceId);
+            status_t startWithStatus(const DeviceIdVector& deviceIds);
             status_t pauseWithStatus();
             status_t stopWithStatus();
 
             //FIXME temporary method while some player state is outside of this class
-            void reportEvent(player_state_t event, audio_port_handle_t deviceId);
+            void reportEvent(player_state_t event, const DeviceIdVector& deviceIds);
 
-            void baseUpdateDeviceId(audio_port_handle_t deviceId);
+            void baseUpdateDeviceIds(const DeviceIdVector& deviceIds);
 
             /**
              * Updates the mapping in the AudioService between portId and piid
@@ -80,7 +81,7 @@
     audio_unique_id_t mPIId;
 private:
             // report events to AudioService
-            void servicePlayerEvent(player_state_t event, audio_port_handle_t deviceId);
+            void servicePlayerEvent(player_state_t event, const DeviceIdVector& deviceIds);
             void serviceReleasePlayer();
 
     // native interface to AudioService
@@ -91,7 +92,7 @@
     player_state_t mLastReportedEvent;
 
     Mutex mDeviceIdLock;
-    audio_port_handle_t mLastReportedDeviceId;
+    DeviceIdVector mLastReportedDeviceIds GUARDED_BY(mDeviceIdLock);
 };
 
 } // namespace android
diff --git a/media/libaudioclient/include/media/TrackPlayerBase.h b/media/libaudioclient/include/media/TrackPlayerBase.h
index 8df9ff8..575b14c 100644
--- a/media/libaudioclient/include/media/TrackPlayerBase.h
+++ b/media/libaudioclient/include/media/TrackPlayerBase.h
@@ -60,7 +60,7 @@
             public:
                 SelfAudioDeviceCallback(PlayerBase& self);
                 virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
-                                                         audio_port_handle_t deviceId);
+                                                 const DeviceIdVector& deviceIds);
             private:
                 virtual ~SelfAudioDeviceCallback();
                 PlayerBase& mSelf;
diff --git a/media/libaudioclient/tests/audio_test_utils.cpp b/media/libaudioclient/tests/audio_test_utils.cpp
index 1599839..7d13939 100644
--- a/media/libaudioclient/tests/audio_test_utils.cpp
+++ b/media/libaudioclient/tests/audio_test_utils.cpp
@@ -27,12 +27,12 @@
 #define MAX_WAIT_TIME_MS 5000
 
 void OnAudioDeviceUpdateNotifier::onAudioDeviceUpdate(audio_io_handle_t audioIo,
-                                                      audio_port_handle_t deviceId) {
-    ALOGI("%s: audioIo=%d deviceId=%d", __func__, audioIo, deviceId);
+                                                      const DeviceIdVector& deviceIds) {
+    ALOGI("%s: audioIo=%d deviceIds=%s", __func__, audioIo, toString(deviceIds).c_str());
     {
         std::lock_guard lock(mMutex);
         mAudioIo = audioIo;
-        mDeviceId = deviceId;
+        mDeviceIds = deviceIds;
     }
     mCondition.notify_all();
 }
@@ -41,20 +41,23 @@
     std::unique_lock lock(mMutex);
     android::base::ScopedLockAssertion lock_assertion(mMutex);
     if (mAudioIo == AUDIO_IO_HANDLE_NONE ||
-        (expDeviceId != AUDIO_PORT_HANDLE_NONE && expDeviceId != mDeviceId)) {
+        (expDeviceId != AUDIO_PORT_HANDLE_NONE &&
+         std::find(mDeviceIds.begin(), mDeviceIds.end(), expDeviceId) == mDeviceIds.end())) {
         mCondition.wait_for(lock, std::chrono::milliseconds(500));
         if (mAudioIo == AUDIO_IO_HANDLE_NONE ||
-            (expDeviceId != AUDIO_PORT_HANDLE_NONE && expDeviceId != mDeviceId)) {
+            (expDeviceId != AUDIO_PORT_HANDLE_NONE &&
+             std::find(mDeviceIds.begin(), mDeviceIds.end(), expDeviceId) == mDeviceIds.end())) {
             return TIMED_OUT;
         }
     }
     return OK;
 }
 
-std::pair<audio_io_handle_t, audio_port_handle_t>
-OnAudioDeviceUpdateNotifier::getLastPortAndDevice() const {
+std::pair<audio_io_handle_t, DeviceIdVector> OnAudioDeviceUpdateNotifier::getLastPortAndDevices()
+        const {
     std::lock_guard lock(mMutex);
-    return {mAudioIo, mDeviceId};
+    ALOGI("%s: audioIo=%d deviceIds=%s", __func__, mAudioIo, toString(mDeviceIds).c_str());
+    return {mAudioIo, mDeviceIds};
 }
 
 AudioPlayback::AudioPlayback(uint32_t sampleRate, audio_format_t format,
@@ -761,13 +764,15 @@
     return BAD_VALUE;
 }
 
-bool patchContainsOutputDevice(audio_port_handle_t deviceId, audio_patch patch) {
+// Check if the patch matches all the output devices in the deviceIds vector.
+bool patchMatchesOutputDevices(const DeviceIdVector& deviceIds, audio_patch patch) {
+    DeviceIdVector patchDeviceIds;
     for (auto j = 0; j < patch.num_sinks; j++) {
-        if (patch.sinks[j].type == AUDIO_PORT_TYPE_DEVICE && patch.sinks[j].id == deviceId) {
-            return true;
+        if (patch.sinks[j].type == AUDIO_PORT_TYPE_DEVICE) {
+            patchDeviceIds.push_back(patch.sinks[j].id);
         }
     }
-    return false;
+    return areDeviceIdsEqual(deviceIds, patchDeviceIds);
 }
 
 bool patchContainsInputDevice(audio_port_handle_t deviceId, audio_patch patch) {
@@ -779,10 +784,10 @@
     return false;
 }
 
-bool checkPatchPlayback(audio_io_handle_t audioIo, audio_port_handle_t deviceId) {
+bool checkPatchPlayback(audio_io_handle_t audioIo, const DeviceIdVector& deviceIds) {
     struct audio_patch patch;
     if (getPatchForOutputMix(audioIo, patch) == OK) {
-        return patchContainsOutputDevice(deviceId, patch);
+        return patchMatchesOutputDevices(deviceIds, patch);
     }
     return false;
 }
diff --git a/media/libaudioclient/tests/audio_test_utils.h b/media/libaudioclient/tests/audio_test_utils.h
index 022ecf3..9ccc7da 100644
--- a/media/libaudioclient/tests/audio_test_utils.h
+++ b/media/libaudioclient/tests/audio_test_utils.h
@@ -52,9 +52,9 @@
                              audio_port_v7& port);
 status_t getPatchForOutputMix(audio_io_handle_t audioIo, audio_patch& patch);
 status_t getPatchForInputMix(audio_io_handle_t audioIo, audio_patch& patch);
-bool patchContainsOutputDevice(audio_port_handle_t deviceId, audio_patch patch);
+bool patchContainsOutputDevices(DeviceIdVector deviceIds, audio_patch patch);
 bool patchContainsInputDevice(audio_port_handle_t deviceId, audio_patch patch);
-bool checkPatchPlayback(audio_io_handle_t audioIo, audio_port_handle_t deviceId);
+bool checkPatchPlayback(audio_io_handle_t audioIo, const DeviceIdVector& deviceIds);
 bool checkPatchCapture(audio_io_handle_t audioIo, audio_port_handle_t deviceId);
 std::string dumpPort(const audio_port_v7& port);
 std::string dumpPortConfig(const audio_port_config& port);
@@ -62,13 +62,13 @@
 
 class OnAudioDeviceUpdateNotifier : public AudioSystem::AudioDeviceCallback {
   public:
-    void onAudioDeviceUpdate(audio_io_handle_t audioIo, audio_port_handle_t deviceId) override;
+    void onAudioDeviceUpdate(audio_io_handle_t audioIo, const DeviceIdVector& deviceIds) override;
     status_t waitForAudioDeviceCb(audio_port_handle_t expDeviceId = AUDIO_PORT_HANDLE_NONE);
-    std::pair<audio_io_handle_t, audio_port_handle_t> getLastPortAndDevice() const;
+    std::pair<audio_io_handle_t, DeviceIdVector> getLastPortAndDevices() const;
 
   private:
     audio_io_handle_t mAudioIo GUARDED_BY(mMutex) = AUDIO_IO_HANDLE_NONE;
-    audio_port_handle_t mDeviceId GUARDED_BY(mMutex) = AUDIO_PORT_HANDLE_NONE;
+    DeviceIdVector mDeviceIds GUARDED_BY(mMutex);
     mutable std::mutex mMutex;
     std::condition_variable mCondition;
 };
diff --git a/media/libaudioclient/tests/audioeffect_analyser.cpp b/media/libaudioclient/tests/audioeffect_analyser.cpp
index 199fb8b..3df5fd8 100644
--- a/media/libaudioclient/tests/audioeffect_analyser.cpp
+++ b/media/libaudioclient/tests/audioeffect_analyser.cpp
@@ -119,7 +119,8 @@
     CHECK_OK(capture->start(), "start recording failed")
     CHECK_OK(capture->audioProcess(), "recording process failed")
     CHECK_OK(cbCapture->waitForAudioDeviceCb(), "audio device callback notification timed out");
-    if (port.id != capture->getAudioRecordHandle()->getRoutedDeviceId()) {
+    DeviceIdVector routedDeviceIds = capture->getAudioRecordHandle()->getRoutedDeviceIds();
+    if (port.id != routedDeviceIds[0]) {
         CHECK_OK(BAD_VALUE, "Capture NOT routed on expected port")
     }
     CHECK_OK(getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
diff --git a/media/libaudioclient/tests/audiorecord_tests.cpp b/media/libaudioclient/tests/audiorecord_tests.cpp
index f2fee8b..550ce6c 100644
--- a/media/libaudioclient/tests/audiorecord_tests.cpp
+++ b/media/libaudioclient/tests/audiorecord_tests.cpp
@@ -123,12 +123,12 @@
     EXPECT_EQ(OK, mAC->getAudioRecordHandle()->addAudioDeviceCallback(cb));
     EXPECT_EQ(OK, mAC->start()) << "record creation failed";
     EXPECT_EQ(OK, cb->waitForAudioDeviceCb());
-    const auto [oldAudioIo, oldDeviceId] = cbOld->getLastPortAndDevice();
+    const auto [oldAudioIo, oldDeviceIds] = cbOld->getLastPortAndDevices();
     EXPECT_EQ(AUDIO_IO_HANDLE_NONE, oldAudioIo);
-    EXPECT_EQ(AUDIO_PORT_HANDLE_NONE, oldDeviceId);
-    const auto [audioIo, deviceId] = cb->getLastPortAndDevice();
+    EXPECT_TRUE(oldDeviceIds.empty());
+    const auto [audioIo, deviceIds] = cb->getLastPortAndDevices();
     EXPECT_NE(AUDIO_IO_HANDLE_NONE, audioIo);
-    EXPECT_NE(AUDIO_PORT_HANDLE_NONE, deviceId);
+    EXPECT_FALSE(deviceIds.empty());
     EXPECT_EQ(BAD_VALUE, mAC->getAudioRecordHandle()->removeAudioDeviceCallback(nullptr));
     EXPECT_EQ(INVALID_OPERATION, mAC->getAudioRecordHandle()->removeAudioDeviceCallback(cbOld));
     EXPECT_EQ(OK, mAC->getAudioRecordHandle()->removeAudioDeviceCallback(cb));
diff --git a/media/libaudioclient/tests/audiorouting_tests.cpp b/media/libaudioclient/tests/audiorouting_tests.cpp
index a3ab9d2..7957c10 100644
--- a/media/libaudioclient/tests/audiorouting_tests.cpp
+++ b/media/libaudioclient/tests/audiorouting_tests.cpp
@@ -64,8 +64,8 @@
         EXPECT_EQ(OK, ap->start()) << "audio track start failed";
         EXPECT_EQ(OK, ap->onProcess());
         EXPECT_EQ(OK, cb->waitForAudioDeviceCb());
-        const auto [audioIo, deviceId] = cb->getLastPortAndDevice();
-        EXPECT_TRUE(checkPatchPlayback(audioIo, deviceId));
+        const auto [audioIo, deviceIds] = cb->getLastPortAndDevices();
+        EXPECT_TRUE(checkPatchPlayback(audioIo, deviceIds));
         EXPECT_NE(0, ap->getAudioTrackHandle()->getFlags() & output_flags[i]);
         audio_patch patch;
         EXPECT_EQ(OK, getPatchForOutputMix(audioIo, patch));
@@ -127,8 +127,8 @@
     // capture should be routed to submix in port
     EXPECT_EQ(OK, capture->start()) << "start recording failed";
     EXPECT_EQ(OK, cbCapture->waitForAudioDeviceCb());
-    EXPECT_EQ(port.id, capture->getAudioRecordHandle()->getRoutedDeviceId())
-            << "Capture NOT routed on expected port";
+    DeviceIdVector routedDeviceIds = capture->getAudioRecordHandle()->getRoutedDeviceIds();
+    EXPECT_EQ(port.id, routedDeviceIds[0]) << "Capture NOT routed on expected port";
 
     // capture start should create submix out port
     status_t status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
@@ -138,8 +138,8 @@
     // playback should be routed to submix out as long as capture is active
     EXPECT_EQ(OK, playback->start()) << "audio track start failed";
     EXPECT_EQ(OK, cbPlayback->waitForAudioDeviceCb());
-    EXPECT_EQ(port.id, playback->getAudioTrackHandle()->getRoutedDeviceId())
-            << "Playback NOT routed on expected port";
+    routedDeviceIds = playback->getAudioTrackHandle()->getRoutedDeviceIds();
+    EXPECT_EQ(port.id, routedDeviceIds[0]) << "Playback NOT routed on expected port";
 
     capture->stop();
     playback->stop();
@@ -235,13 +235,13 @@
     // launch
     EXPECT_EQ(OK, captureA->start()) << "start recording failed";
     EXPECT_EQ(OK, cbCaptureA->waitForAudioDeviceCb());
-    EXPECT_EQ(port.id, captureA->getAudioRecordHandle()->getRoutedDeviceId())
-            << "Capture NOT routed on expected port";
+    DeviceIdVector routedDeviceIds = captureA->getAudioRecordHandle()->getRoutedDeviceIds();
+    EXPECT_EQ(port.id, routedDeviceIds[0]) << "Capture NOT routed on expected port";
 
     EXPECT_EQ(OK, captureB->start()) << "start recording failed";
     EXPECT_EQ(OK, cbCaptureB->waitForAudioDeviceCb());
-    EXPECT_EQ(port_mix.id, captureB->getAudioRecordHandle()->getRoutedDeviceId())
-            << "Capture NOT routed on expected port";
+    routedDeviceIds = captureB->getAudioRecordHandle()->getRoutedDeviceIds();
+    EXPECT_EQ(port_mix.id, routedDeviceIds[0]) << "Capture NOT routed on expected port";
 
     // as record started, expect submix out ports to be connected
     status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
@@ -255,8 +255,8 @@
     // check if playback routed to desired port
     EXPECT_EQ(OK, playback->start());
     EXPECT_EQ(OK, cbPlayback->waitForAudioDeviceCb());
-    EXPECT_EQ(port_mix.id, playback->getAudioTrackHandle()->getRoutedDeviceId())
-            << "Playback NOT routed on expected port";
+    routedDeviceIds = playback->getAudioTrackHandle()->getRoutedDeviceIds();
+    EXPECT_EQ(port_mix.id, routedDeviceIds[0]) << "Playback NOT routed on expected port";
 
     captureB->stop();
 
@@ -282,8 +282,8 @@
     playback->onProcess();
     // as captureA is active, it should re route to legacy submix
     EXPECT_EQ(OK, cbPlayback->waitForAudioDeviceCb(port.id));
-    EXPECT_EQ(port.id, playback->getAudioTrackHandle()->getRoutedDeviceId())
-            << "Playback NOT routed on expected port";
+    routedDeviceIds = playback->getAudioTrackHandle()->getRoutedDeviceIds();
+    EXPECT_EQ(port.id, routedDeviceIds[0]) << "Playback NOT routed on expected port";
 
     captureA->stop();
     playback->stop();
diff --git a/media/libaudioclient/tests/audiosystem_tests.cpp b/media/libaudioclient/tests/audiosystem_tests.cpp
index 742ca48..31cab78 100644
--- a/media/libaudioclient/tests/audiosystem_tests.cpp
+++ b/media/libaudioclient/tests/audiosystem_tests.cpp
@@ -108,7 +108,7 @@
 // UNIT TESTS
 TEST_F(AudioSystemTest, CheckServerSideValues) {
     ASSERT_NO_FATAL_FAILURE(createPlaybackSession());
-    const auto [pbAudioIo, _] = mCbPlayback->getLastPortAndDevice();
+    const auto [pbAudioIo, _] = mCbPlayback->getLastPortAndDevices();
     EXPECT_GT(mAF->sampleRate(pbAudioIo), 0);
     EXPECT_NE(mAF->format(pbAudioIo), AUDIO_FORMAT_INVALID);
     EXPECT_GT(mAF->frameCount(pbAudioIo), 0);
@@ -122,7 +122,7 @@
     EXPECT_LE(mAF->latency(pbAudioIo), mPlayback->getAudioTrackHandle()->latency());
 
     ASSERT_NO_FATAL_FAILURE(createRecordSession());
-    const auto [recAudioIo, __] = mCbRecord->getLastPortAndDevice();
+    const auto [recAudioIo, __] = mCbRecord->getLastPortAndDevices();
     EXPECT_GT(mAF->sampleRate(recAudioIo), 0);
     // EXPECT_NE(mAF->format(recAudioIo), AUDIO_FORMAT_INVALID);
     EXPECT_GT(mAF->frameCount(recAudioIo), 0);
diff --git a/media/libaudioclient/tests/audiotrack_tests.cpp b/media/libaudioclient/tests/audiotrack_tests.cpp
index cf7d926..d283c6c 100644
--- a/media/libaudioclient/tests/audiotrack_tests.cpp
+++ b/media/libaudioclient/tests/audiotrack_tests.cpp
@@ -157,20 +157,21 @@
     EXPECT_EQ(OK, ap->start()) << "audio track start failed";
     EXPECT_EQ(OK, ap->onProcess());
     EXPECT_EQ(OK, cb->waitForAudioDeviceCb());
-    const auto [oldAudioIo, oldDeviceId] = cbOld->getLastPortAndDevice();
+    const auto [oldAudioIo, oldDeviceIds] = cbOld->getLastPortAndDevices();
     EXPECT_EQ(AUDIO_IO_HANDLE_NONE, oldAudioIo);
-    EXPECT_EQ(AUDIO_PORT_HANDLE_NONE, oldDeviceId);
-    const auto [audioIo, deviceId] = cb->getLastPortAndDevice();
+    EXPECT_TRUE(oldDeviceIds.empty());
+    const auto [audioIo, deviceIds] = cb->getLastPortAndDevices();
     EXPECT_NE(AUDIO_IO_HANDLE_NONE, audioIo);
-    EXPECT_NE(AUDIO_PORT_HANDLE_NONE, deviceId);
+    EXPECT_FALSE(deviceIds.empty());
     EXPECT_EQ(audioIo, ap->getAudioTrackHandle()->getOutput());
-    EXPECT_EQ(deviceId, ap->getAudioTrackHandle()->getRoutedDeviceId());
+    DeviceIdVector routedDeviceIds = ap->getAudioTrackHandle()->getRoutedDeviceIds();
+    EXPECT_TRUE(areDeviceIdsEqual(routedDeviceIds, deviceIds));
     String8 keys;
     keys = ap->getAudioTrackHandle()->getParameters(keys);
     if (!keys.empty()) {
         std::cerr << "track parameters :: " << keys << std::endl;
     }
-    EXPECT_TRUE(checkPatchPlayback(audioIo, deviceId));
+    EXPECT_TRUE(checkPatchPlayback(audioIo, deviceIds));
     EXPECT_EQ(BAD_VALUE, ap->getAudioTrackHandle()->removeAudioDeviceCallback(nullptr));
     EXPECT_EQ(INVALID_OPERATION, ap->getAudioTrackHandle()->removeAudioDeviceCallback(cbOld));
     EXPECT_EQ(OK, ap->getAudioTrackHandle()->removeAudioDeviceCallback(cb));
diff --git a/media/libaudiofoundation/AudioContainers.cpp b/media/libaudiofoundation/AudioContainers.cpp
index 3e2066b..f3d295b 100644
--- a/media/libaudiofoundation/AudioContainers.cpp
+++ b/media/libaudiofoundation/AudioContainers.cpp
@@ -151,6 +151,12 @@
     return deviceIds[0];
 }
 
+bool areDeviceIdsEqual(const DeviceIdVector& first, const DeviceIdVector& second) {
+    const std::set<audio_port_handle_t> firstSet(first.begin(), first.end());
+    const std::set<audio_port_handle_t> secondSet(second.begin(), second.end());
+    return firstSet == secondSet;
+}
+
 AudioProfileAttributesMultimap createAudioProfilesAttrMap(audio_profile profiles[],
                                                           uint32_t first,
                                                           uint32_t last) {
diff --git a/media/libaudiofoundation/include/media/AudioContainers.h b/media/libaudiofoundation/include/media/AudioContainers.h
index 8d4665e..b6c0444 100644
--- a/media/libaudiofoundation/include/media/AudioContainers.h
+++ b/media/libaudiofoundation/include/media/AudioContainers.h
@@ -140,16 +140,21 @@
 }
 
 /**
- * Returns human readable string for a set of device ids.
+ * Returns human readable string for a vector of device ids.
  */
 std::string toString(const DeviceIdVector& deviceIds);
 
 /**
- * Returns the first device id of a set of device ids or AUDIO_PORT_HANDLE_NONE when its empty.
+ * Returns the first device id of a vector of device ids or AUDIO_PORT_HANDLE_NONE when its empty.
  */
 audio_port_handle_t getFirstDeviceId(const DeviceIdVector& deviceIds);
 
 /**
+ * Returns whether two vectors of device ids have the same elements.
+ */
+bool areDeviceIdsEqual(const DeviceIdVector& first, const DeviceIdVector& second);
+
+/**
  * Create audio profile attributes map by given audio profile array from the range of [first, last).
  *
  * @param profiles the array of audio profiles.
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 8a962c6..06dd27b 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -104,8 +104,8 @@
     ],
 
     shared_libs: [
-        "android.hidl.token@1.0-utils",
         "android.hardware.media.omx@1.0",
+        "android.hidl.token@1.0-utils",
         "libbinder",
         "libcutils",
         "libhidlbase",
@@ -116,8 +116,8 @@
     ],
 
     export_shared_lib_headers: [
-        "android.hidl.token@1.0-utils",
         "android.hardware.media.omx@1.0",
+        "android.hidl.token@1.0-utils",
         "libstagefright_foundation",
         "libui",
     ],
@@ -138,15 +138,15 @@
     ],
 
     cflags: [
+        "-Wall",
         "-Werror",
         "-Wno-error=deprecated-declarations",
-        "-Wall",
     ],
 
     sanitize: {
         misc_undefined: [
-            "unsigned-integer-overflow",
             "signed-integer-overflow",
+            "unsigned-integer-overflow",
         ],
         cfi: true,
     },
@@ -197,15 +197,15 @@
     ],
 
     cflags: [
+        "-Wall",
         "-Werror",
         "-Wno-error=deprecated-declarations",
-        "-Wall",
     ],
 
     sanitize: {
         misc_undefined: [
-            "unsigned-integer-overflow",
             "signed-integer-overflow",
+            "unsigned-integer-overflow",
         ],
         cfi: true,
     },
@@ -232,15 +232,15 @@
     ],
 
     cflags: [
+        "-Wall",
         "-Werror",
         "-Wno-error=deprecated-declarations",
-        "-Wall",
     ],
 
     sanitize: {
         misc_undefined: [
-            "unsigned-integer-overflow",
             "signed-integer-overflow",
+            "unsigned-integer-overflow",
         ],
         cfi: true,
     },
@@ -279,15 +279,15 @@
     ],
 
     cflags: [
+        "-Wall",
         "-Werror",
         "-Wno-error=deprecated-declarations",
-        "-Wall",
     ],
 
     sanitize: {
         misc_undefined: [
-            "unsigned-integer-overflow",
             "signed-integer-overflow",
+            "unsigned-integer-overflow",
         ],
         cfi: true,
     },
@@ -323,15 +323,15 @@
     ],
 
     cflags: [
+        "-Wall",
         "-Werror",
         "-Wno-error=deprecated-declarations",
-        "-Wall",
     ],
 
     sanitize: {
         misc_undefined: [
-            "unsigned-integer-overflow",
             "signed-integer-overflow",
+            "unsigned-integer-overflow",
         ],
         cfi: true,
     },
@@ -346,35 +346,35 @@
 
     srcs: [
         ":mediaextractorservice_aidl",
-        "IDataSource.cpp",
         "BufferingSettings.cpp",
-        "mediaplayer.cpp",
+        "CharacterEncodingDetector.cpp",
+        "IDataSource.cpp",
+        "IMediaDeathNotifier.cpp",
+        "IMediaExtractor.cpp",
         "IMediaHTTPConnection.cpp",
         "IMediaHTTPService.cpp",
-        "IMediaExtractor.cpp",
-        "IMediaPlayerService.cpp",
-        "IMediaPlayerClient.cpp",
-        "IMediaRecorderClient.cpp",
+        "IMediaMetadataRetriever.cpp",
         "IMediaPlayer.cpp",
+        "IMediaPlayerClient.cpp",
+        "IMediaPlayerService.cpp",
         "IMediaRecorder.cpp",
+        "IMediaRecorderClient.cpp",
         "IMediaSource.cpp",
         "IRemoteDisplay.cpp",
         "IRemoteDisplayClient.cpp",
         "IStreamSource.cpp",
-        "Metadata.cpp",
-        "mediarecorder.cpp",
-        "IMediaMetadataRetriever.cpp",
-        "mediametadataretriever.cpp",
-        "MediaScanner.cpp",
-        "MediaScannerClient.cpp",
-        "CharacterEncodingDetector.cpp",
-        "IMediaDeathNotifier.cpp",
         "MediaProfiles.cpp",
         "MediaResource.cpp",
         "MediaResourcePolicy.cpp",
-        "StringArray.cpp",
-        "NdkMediaFormatPriv.cpp",
+        "MediaScanner.cpp",
+        "MediaScannerClient.cpp",
+        "Metadata.cpp",
         "NdkMediaErrorPriv.cpp",
+        "NdkMediaFormatPriv.cpp",
+        "StringArray.cpp",
+        "mediametadataretriever.cpp",
+        "mediaplayer.cpp",
+        "mediarecorder.cpp",
     ],
 
     aidl: {
@@ -383,55 +383,57 @@
     },
 
     header_libs: [
+        "jni_headers",
         "libstagefright_headers",
         "media_ndk_headers",
-        "jni_headers",
     ],
 
     export_header_lib_headers: [
+        "jni_headers",
         "libstagefright_headers",
         "media_ndk_headers",
-        "jni_headers",
     ],
 
     shared_libs: [
         "android.hidl.token@1.0-utils",
         "audioclient-types-aidl-cpp",
         "av-types-aidl-cpp",
-        "liblog",
-        "libcutils",
-        "libutils",
         "libbinder",
         "libbinder_ndk",
         //"libsonivox",
+        "libcutils",
+        "liblog",
+        "libutils",
+        "framework-permission-aidl-cpp",
         "libandroidicu",
-        "libexpat",
-        "libcamera_client",
-        "libstagefright_foundation",
-        "libgui",
-        "libdl",
         "libaudioclient",
+        "libaudiofoundation",
+        "libcamera_client",
+        "libdl",
+        "libexpat",
+        "libgui",
         "libmedia_codeclist",
         "libmedia_omx",
-        "framework-permission-aidl-cpp",
+        "libstagefright_foundation",
     ],
 
     export_shared_lib_headers: [
         "libaudioclient",
+        "libaudiofoundation",
         "libbinder",
         //"libsonivox",
-        "libmedia_omx",
         "framework-permission-aidl-cpp",
+        "libmedia_omx",
     ],
 
     static_libs: [
-        "resourcemanager_aidl_interface-ndk",
         "framework-permission-aidl-cpp",
+        "resourcemanager_aidl_interface-ndk",
     ],
 
     export_static_lib_headers: [
-        "resourcemanager_aidl_interface-ndk",
         "framework-permission-aidl-cpp",
+        "resourcemanager_aidl_interface-ndk",
     ],
 
     export_include_dirs: [
@@ -439,17 +441,17 @@
     ],
 
     cflags: [
+        "-Wall",
         "-Werror",
         "-Wno-error=deprecated-declarations",
-        "-Wall",
     ],
 
     version_script: "exports.lds",
 
     sanitize: {
         misc_undefined: [
-            "unsigned-integer-overflow",
             "signed-integer-overflow",
+            "unsigned-integer-overflow",
         ],
         cfi: true,
     },
@@ -461,8 +463,8 @@
     host_supported: true,
 
     srcs: [
-        "NdkMediaFormatPriv.cpp",
         "NdkMediaErrorPriv.cpp",
+        "NdkMediaFormatPriv.cpp",
     ],
 
     header_libs: [
@@ -473,8 +475,8 @@
 
     cflags: [
         "-DEXPORT=__attribute__((visibility(\"default\")))",
-        "-Werror",
         "-Wall",
+        "-Werror",
     ],
 
     export_include_dirs: ["include"],
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index c9f361e..4967dda 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -567,23 +567,24 @@
         return reply.readInt32();
     }
 
-    status_t getRoutedDeviceId(audio_port_handle_t* deviceId)
+    status_t getRoutedDeviceIds(DeviceIdVector& deviceIds)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+        deviceIds.clear();
 
-        status_t status = remote()->transact(GET_ROUTED_DEVICE_ID, data, &reply);
+        status_t status = remote()->transact(GET_ROUTED_DEVICE_IDS, data, &reply);
         if (status != OK) {
-            ALOGE("getRoutedDeviceid: binder call failed: %d", status);
-            *deviceId = AUDIO_PORT_HANDLE_NONE;
+            ALOGE("getRoutedDeviceIds: binder call failed: %d", status);
             return status;
         }
 
         status = reply.readInt32();
-        if (status != NO_ERROR) {
-            *deviceId = AUDIO_PORT_HANDLE_NONE;
-        } else {
-            *deviceId = reply.readInt32();
+        if (status == NO_ERROR) {
+            int size = reply.readInt32();
+            for (int i = 0; i < size; i++) {
+                deviceIds.push_back(reply.readInt32());
+            }
         }
         return status;
     }
@@ -983,13 +984,16 @@
             }
             return NO_ERROR;
         }
-        case GET_ROUTED_DEVICE_ID: {
+        case GET_ROUTED_DEVICE_IDS: {
             CHECK_INTERFACE(IMediaPlayer, data, reply);
-            audio_port_handle_t deviceId;
-            status_t ret = getRoutedDeviceId(&deviceId);
+            DeviceIdVector deviceIds;
+            status_t ret = getRoutedDeviceIds(deviceIds);
             reply->writeInt32(ret);
             if (ret == NO_ERROR) {
-                reply->writeInt32(deviceId);
+                reply->writeInt32(deviceIds.size());
+                for (auto deviceId : deviceIds) {
+                    reply->writeInt32(deviceId);
+                }
             }
             return NO_ERROR;
         } break;
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index 755a147..1f04217 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -62,7 +62,7 @@
     RESUME,
     GET_METRICS,
     SET_INPUT_DEVICE,
-    GET_ROUTED_DEVICE_ID,
+    GET_ROUTED_DEVICE_IDS,
     ENABLE_AUDIO_DEVICE_CALLBACK,
     GET_ACTIVE_MICROPHONES,
     GET_PORT_ID,
@@ -392,24 +392,24 @@
         return reply.readInt32();;
     }
 
-    audio_port_handle_t getRoutedDeviceId(audio_port_handle_t *deviceId)
+    status_t getRoutedDeviceIds(DeviceIdVector& deviceIds)
     {
-        ALOGV("getRoutedDeviceId");
         Parcel data, reply;
         data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor());
+        deviceIds.clear();
 
-        status_t status = remote()->transact(GET_ROUTED_DEVICE_ID, data, &reply);
+        status_t status = remote()->transact(GET_ROUTED_DEVICE_IDS, data, &reply);
         if (status != OK) {
-            ALOGE("getRoutedDeviceid binder call failed: %d", status);
-            *deviceId = AUDIO_PORT_HANDLE_NONE;
+            ALOGE("getRoutedDeviceIds: binder call failed: %d", status);
             return status;
         }
 
         status = reply.readInt32();
-        if (status != NO_ERROR) {
-            *deviceId = AUDIO_PORT_HANDLE_NONE;
-        } else {
-            *deviceId = reply.readInt32();
+        if (status == NO_ERROR) {
+            int size = reply.readInt32();
+            for (int i = 0; i < size; i++) {
+                deviceIds.push_back(reply.readInt32());
+            }
         }
         return status;
     }
@@ -730,14 +730,17 @@
             }
             return NO_ERROR;
         } break;
-        case GET_ROUTED_DEVICE_ID: {
-            ALOGV("GET_ROUTED_DEVICE_ID");
+        case GET_ROUTED_DEVICE_IDS: {
+            ALOGV("GET_ROUTED_DEVICE_IDS");
             CHECK_INTERFACE(IMediaRecorder, data, reply);
-            audio_port_handle_t deviceId;
-            status_t status = getRoutedDeviceId(&deviceId);
-            reply->writeInt32(status);
-            if (status == NO_ERROR) {
-                reply->writeInt32(deviceId);
+            DeviceIdVector deviceIds;
+            status_t ret = getRoutedDeviceIds(deviceIds);
+            reply->writeInt32(ret);
+            if (ret == NO_ERROR) {
+                reply->writeInt32(deviceIds.size());
+                for (auto deviceId : deviceIds) {
+                    reply->writeInt32(deviceId);
+                }
             }
             return NO_ERROR;
         } break;
diff --git a/media/libmedia/include/media/IMediaPlayer.h b/media/libmedia/include/media/IMediaPlayer.h
index 28684d1..4c6f32c 100644
--- a/media/libmedia/include/media/IMediaPlayer.h
+++ b/media/libmedia/include/media/IMediaPlayer.h
@@ -23,6 +23,7 @@
 #include <utils/KeyedVector.h>
 #include <system/audio.h>
 
+#include <media/AudioContainers.h>
 #include <media/AudioResamplerPublic.h>
 #include <media/stagefright/MediaSource.h>
 #include <media/VolumeShaper.h>
@@ -135,7 +136,7 @@
 
     // AudioRouting
     virtual status_t        setOutputDevice(audio_port_handle_t deviceId) = 0;
-    virtual status_t        getRoutedDeviceId(audio_port_handle_t *deviceId) = 0;
+    virtual status_t        getRoutedDeviceIds(DeviceIdVector& deviceIds) = 0;
     virtual status_t        enableAudioDeviceCallback(bool enabled) = 0;
 protected:
 
@@ -184,7 +185,7 @@
         RELEASE_DRM,
         // AudioRouting
         SET_OUTPUT_DEVICE,
-        GET_ROUTED_DEVICE_ID,
+        GET_ROUTED_DEVICE_IDS,
         ENABLE_AUDIO_DEVICE_CALLBACK,
     };
 };
diff --git a/media/libmedia/include/media/IMediaRecorder.h b/media/libmedia/include/media/IMediaRecorder.h
index 05da5c2..8411ca7 100644
--- a/media/libmedia/include/media/IMediaRecorder.h
+++ b/media/libmedia/include/media/IMediaRecorder.h
@@ -20,6 +20,7 @@
 
 #include <android/media/MicrophoneInfoFw.h>
 #include <binder/IInterface.h>
+#include <media/AudioContainers.h>
 #include <system/audio.h>
 #include <vector>
 
@@ -71,7 +72,7 @@
     virtual sp<IGraphicBufferProducer> querySurfaceMediaSource() = 0;
 
     virtual status_t setInputDevice(audio_port_handle_t deviceId) = 0;
-    virtual status_t getRoutedDeviceId(audio_port_handle_t *deviceId) = 0;
+    virtual status_t getRoutedDeviceIds(DeviceIdVector& deviceIds) = 0;
     virtual status_t enableAudioDeviceCallback(bool enabled) = 0;
     virtual status_t getActiveMicrophones(
                         std::vector<media::MicrophoneInfoFw>* activeMicrophones) = 0;
diff --git a/media/libmedia/include/media/MediaRecorderBase.h b/media/libmedia/include/media/MediaRecorderBase.h
index 82ec9c5..e3698e3 100644
--- a/media/libmedia/include/media/MediaRecorderBase.h
+++ b/media/libmedia/include/media/MediaRecorderBase.h
@@ -69,7 +69,7 @@
     virtual status_t setInputSurface(const sp<PersistentSurface>& surface) = 0;
     virtual sp<IGraphicBufferProducer> querySurfaceMediaSource() const = 0;
     virtual status_t setInputDevice(audio_port_handle_t deviceId) = 0;
-    virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId) = 0;
+    virtual status_t getRoutedDeviceIds(DeviceIdVector& deviceIds) = 0;
     virtual void setAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback) = 0;
     virtual status_t enableAudioDeviceCallback(bool enabled) = 0;
     virtual status_t getActiveMicrophones(
diff --git a/media/libmedia/include/media/mediaplayer.h b/media/libmedia/include/media/mediaplayer.h
index 2f9b85e..7c612c3 100644
--- a/media/libmedia/include/media/mediaplayer.h
+++ b/media/libmedia/include/media/mediaplayer.h
@@ -281,7 +281,7 @@
             status_t        releaseDrm();
             // AudioRouting
             status_t        setOutputDevice(audio_port_handle_t deviceId);
-            audio_port_handle_t getRoutedDeviceId();
+            status_t        getRoutedDeviceIds(DeviceIdVector& deviceIds);
             status_t        enableAudioDeviceCallback(bool enabled);
 
 private:
diff --git a/media/libmedia/include/media/mediarecorder.h b/media/libmedia/include/media/mediarecorder.h
index 602f72e..1377d61 100644
--- a/media/libmedia/include/media/mediarecorder.h
+++ b/media/libmedia/include/media/mediarecorder.h
@@ -22,6 +22,7 @@
 #include <utils/threads.h>
 #include <utils/List.h>
 #include <utils/Errors.h>
+#include <media/AudioContainers.h>
 #include <media/IMediaRecorderClient.h>
 #include <media/IMediaDeathNotifier.h>
 #include <android/media/MicrophoneInfoFw.h>
@@ -266,7 +267,7 @@
     sp<IGraphicBufferProducer>     querySurfaceMediaSourceFromMediaServer();
     status_t    getMetrics(Parcel *reply);
     status_t    setInputDevice(audio_port_handle_t deviceId);
-    status_t    getRoutedDeviceId(audio_port_handle_t *deviceId);
+    status_t    getRoutedDeviceIds(DeviceIdVector& deviceIds);
     status_t    enableAudioDeviceCallback(bool enabled);
     status_t    getActiveMicrophones(std::vector<media::MicrophoneInfoFw>* activeMicrophones);
     status_t    setPreferredMicrophoneDirection(audio_microphone_direction_t direction);
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index b5c75b3..9d3fce7 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -1105,19 +1105,14 @@
     return mPlayer->setOutputDevice(deviceId);
 }
 
-audio_port_handle_t MediaPlayer::getRoutedDeviceId()
+status_t MediaPlayer::getRoutedDeviceIds(DeviceIdVector& deviceIds)
 {
     Mutex::Autolock _l(mLock);
     if (mPlayer == NULL) {
-        ALOGV("getRoutedDeviceId: player not init");
-        return AUDIO_PORT_HANDLE_NONE;
+        ALOGV("getRoutedDeviceIds: player not init");
+        return NO_INIT;
     }
-    audio_port_handle_t deviceId;
-    status_t status = mPlayer->getRoutedDeviceId(&deviceId);
-    if (status != NO_ERROR) {
-        return AUDIO_PORT_HANDLE_NONE;
-    }
-    return deviceId;
+    return mPlayer->getRoutedDeviceIds(deviceIds);
 }
 
 status_t MediaPlayer::enableAudioDeviceCallback(bool enabled)
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index 48f5e4b..e676d5a 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -858,17 +858,17 @@
     return mMediaRecorder->setInputDevice(deviceId);
 }
 
-status_t MediaRecorder::getRoutedDeviceId(audio_port_handle_t* deviceId)
+status_t MediaRecorder::getRoutedDeviceIds(DeviceIdVector& deviceIds)
 {
-    ALOGV("getRoutedDeviceId");
+    ALOGV("getRoutedDeviceIds");
 
     if (mMediaRecorder == NULL) {
         ALOGE("media recorder is not initialized yet");
         return INVALID_OPERATION;
     }
-    status_t status = mMediaRecorder->getRoutedDeviceId(deviceId);
+    status_t status = mMediaRecorder->getRoutedDeviceIds(deviceIds);
     if (status != NO_ERROR) {
-        *deviceId = AUDIO_PORT_HANDLE_NONE;
+        deviceIds.clear();
     }
     return status;
 }
diff --git a/media/libmediametrics/include/MediaMetricsConstants.h b/media/libmediametrics/include/MediaMetricsConstants.h
index 98c3382..a7b2077 100644
--- a/media/libmediametrics/include/MediaMetricsConstants.h
+++ b/media/libmediametrics/include/MediaMetricsConstants.h
@@ -183,6 +183,7 @@
 #define AMEDIAMETRICS_PROP_PLAYBACK_SPEED "playback.speed" // double value (AudioTrack)
 #define AMEDIAMETRICS_PROP_PLAYERIID      "playerIId"      // int32 (-1 invalid/unset IID)
 #define AMEDIAMETRICS_PROP_ROUTEDDEVICEID "routedDeviceId" // int32
+#define AMEDIAMETRICS_PROP_ROUTEDDEVICEIDS "routedDeviceIds" // string value
 #define AMEDIAMETRICS_PROP_SAMPLERATE     "sampleRate"     // int32
 #define AMEDIAMETRICS_PROP_SAMPLERATECLIENT "sampleRateClient" // int32
 #define AMEDIAMETRICS_PROP_SAMPLERATEHARDWARE "sampleRateHardware" // int32
diff --git a/media/libmediaplayerservice/Android.bp b/media/libmediaplayerservice/Android.bp
index a10c509..1d493e2 100644
--- a/media/libmediaplayerservice/Android.bp
+++ b/media/libmediaplayerservice/Android.bp
@@ -46,13 +46,14 @@
         "av-types-aidl-cpp",
         "framework-permission-aidl-cpp",
         "libaconfig_storage_read_api_cc",
-        "libaudioclient_aidl_conversion",
-        "libbase",
-        "libbinder_ndk",
         "libactivitymanager_aidl",
         "libandroid_net",
         "libaudioclient",
+        "libaudioclient_aidl_conversion",
+        "libaudiofoundation",
+        "libbase",
         "libbinder",
+        "libbinder_ndk",
         "libcamera_client",
         "libcodec2_client",
         "libcrypto",
@@ -81,25 +82,25 @@
     ],
 
     header_libs: [
-        "media_plugin_headers",
         "libmediautils_headers",
         "libstagefright_rtsp_headers",
         "libstagefright_webm_headers",
+        "media_plugin_headers",
     ],
 
     static_libs: [
         "com.android.media.flags.editing-aconfig-cc",
+        "framework-permission-aidl-cpp",
         "libplayerservice_datasource",
         "libstagefright_nuplayer",
         "libstagefright_rtsp",
         "libstagefright_timedtext",
-        "framework-permission-aidl-cpp",
     ],
 
     cflags: [
+        "-Wall",
         "-Werror",
         "-Wno-error=deprecated-declarations",
-        "-Wall",
     ],
 
     sanitize: {
@@ -115,8 +116,8 @@
     ],
 
     export_shared_lib_headers: [
-        "libmedia",
         "framework-permission-aidl-cpp",
+        "libmedia",
     ],
 
     export_header_lib_headers: [
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index b267c08..0067344 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -856,10 +856,13 @@
 
 void MediaPlayerService::Client::AudioDeviceUpdatedNotifier::onAudioDeviceUpdate(
         audio_io_handle_t audioIo,
-        audio_port_handle_t deviceId) {
+        const DeviceIdVector& deviceIds) {
+    ALOGD("onAudioDeviceUpdate deviceIds: %s", toString(deviceIds).c_str());
     sp<MediaPlayerBase> listener = mListener.promote();
     if (listener != NULL) {
-        listener->sendEvent(MEDIA_AUDIO_ROUTING_CHANGED, audioIo, deviceId);
+        // Java should query the new device ids once it gets the event.
+        // TODO(b/378505346): Pass the deviceIds to Java to avoid race conditions.
+        listener->sendEvent(MEDIA_AUDIO_ROUTING_CHANGED, audioIo);
     } else {
         ALOGW("listener for process %d death is gone", MEDIA_AUDIO_ROUTING_CHANGED);
     }
@@ -1750,13 +1753,13 @@
     return NO_INIT;
 }
 
-status_t MediaPlayerService::Client::getRoutedDeviceId(audio_port_handle_t* deviceId)
+status_t MediaPlayerService::Client::getRoutedDeviceIds(DeviceIdVector& deviceIds)
 {
-    ALOGV("[%d] getRoutedDeviceId", mConnId);
+    ALOGV("[%d] getRoutedDeviceIds", mConnId);
     {
         Mutex::Autolock l(mLock);
         if (mAudioOutput.get() != nullptr) {
-            return mAudioOutput->getRoutedDeviceId(deviceId);
+            return mAudioOutput->getRoutedDeviceIds(deviceIds);
         }
     }
     return NO_INIT;
@@ -1830,7 +1833,6 @@
       mFlags(AUDIO_OUTPUT_FLAG_NONE),
       mVolumeHandler(new media::VolumeHandler()),
       mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
-      mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE),
       mDeviceCallbackEnabled(false),
       mDeviceCallback(deviceCallback)
 {
@@ -2604,14 +2606,14 @@
     return NO_ERROR;
 }
 
-status_t MediaPlayerService::AudioOutput::getRoutedDeviceId(audio_port_handle_t* deviceId)
+status_t MediaPlayerService::AudioOutput::getRoutedDeviceIds(DeviceIdVector& deviceIds)
 {
-    ALOGV("getRoutedDeviceId");
+    ALOGV("getRoutedDeviceIds");
     Mutex::Autolock lock(mLock);
     if (mTrack != 0) {
-        mRoutedDeviceId = mTrack->getRoutedDeviceId();
+        mRoutedDeviceIds = mTrack->getRoutedDeviceIds();
     }
-    *deviceId = mRoutedDeviceId;
+    deviceIds = mRoutedDeviceIds;
     return NO_ERROR;
 }
 
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 76b7bcf..497ef79 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -28,6 +28,7 @@
 #include <utils/Vector.h>
 
 #include <media/AidlConversion.h>
+#include <media/AudioContainers.h>
 #include <media/AudioResamplerPublic.h>
 #include <media/AudioSystem.h>
 #include <media/AudioTrack.h>
@@ -148,7 +149,7 @@
 
         // AudioRouting
         virtual status_t        setOutputDevice(audio_port_handle_t deviceId);
-        virtual status_t        getRoutedDeviceId(audio_port_handle_t* deviceId);
+        virtual status_t        getRoutedDeviceIds(DeviceIdVector& deviceIds);
         virtual status_t        enableAudioDeviceCallback(bool enabled);
 
     private:
@@ -181,7 +182,7 @@
         audio_output_flags_t    mFlags;
         sp<media::VolumeHandler>       mVolumeHandler;
         audio_port_handle_t     mSelectedDeviceId;
-        audio_port_handle_t     mRoutedDeviceId;
+        DeviceIdVector          mRoutedDeviceIds;
         bool                    mDeviceCallbackEnabled;
         wp<AudioSystem::AudioDeviceCallback>        mDeviceCallback;
         mutable Mutex           mLock;
@@ -401,7 +402,7 @@
         virtual status_t releaseDrm();
         // AudioRouting
         virtual status_t setOutputDevice(audio_port_handle_t deviceId);
-        virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId);
+        virtual status_t getRoutedDeviceIds(DeviceIdVector& deviceIds);
         virtual status_t enableAudioDeviceCallback(bool enabled);
 
     private:
@@ -414,7 +415,7 @@
             ~AudioDeviceUpdatedNotifier() {}
 
             virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
-                                             audio_port_handle_t deviceId);
+                                             const DeviceIdVector& deviceIds);
 
         private:
             wp<MediaPlayerBase> mListener;
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index ed3ec89..53f4e61 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -409,10 +409,13 @@
 
 void MediaRecorderClient::AudioDeviceUpdatedNotifier::onAudioDeviceUpdate(
         audio_io_handle_t audioIo,
-        audio_port_handle_t deviceId) {
+        const DeviceIdVector& deviceIds) {
+    ALOGD("onAudioDeviceUpdate deviceIds: %s", toString(deviceIds).c_str());
     sp<IMediaRecorderClient> listener = mListener.promote();
     if (listener != NULL) {
-        listener->notify(MEDIA_RECORDER_AUDIO_ROUTING_CHANGED, audioIo, deviceId);
+        // Java should query the new device ids once it gets the event.
+        // TODO(b/378505346): Pass the deviceIds to Java to avoid race conditions.
+        listener->notify(MEDIA_RECORDER_AUDIO_ROUTING_CHANGED, audioIo, 0 /*ext2*/);
     } else {
         ALOGW("listener for process %d death is gone", MEDIA_RECORDER_AUDIO_ROUTING_CHANGED);
     }
@@ -550,11 +553,11 @@
     return NO_INIT;
 }
 
-status_t MediaRecorderClient::getRoutedDeviceId(audio_port_handle_t* deviceId) {
-    ALOGV("getRoutedDeviceId");
+status_t MediaRecorderClient::getRoutedDeviceIds(DeviceIdVector& deviceIds) {
+    ALOGV("getRoutedDeviceIds");
     Mutex::Autolock lock(mLock);
     if (mRecorder != NULL) {
-        return mRecorder->getRoutedDeviceId(deviceId);
+        return mRecorder->getRoutedDeviceIds(deviceIds);
     }
     return NO_INIT;
 }
diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h
index dec0c99..3b9ab07 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.h
+++ b/media/libmediaplayerservice/MediaRecorderClient.h
@@ -41,7 +41,7 @@
         virtual ~AudioDeviceUpdatedNotifier();
         virtual void onAudioDeviceUpdate(
                 audio_io_handle_t audioIo,
-                audio_port_handle_t deviceId);
+                const DeviceIdVector& deviceIds);
     private:
         wp<IMediaRecorderClient> mListener;
     };
@@ -80,7 +80,7 @@
     virtual     status_t   setInputSurface(const sp<PersistentSurface>& surface);
     virtual     sp<IGraphicBufferProducer> querySurfaceMediaSource();
     virtual     status_t   setInputDevice(audio_port_handle_t deviceId);
-    virtual     status_t   getRoutedDeviceId(audio_port_handle_t* deviceId);
+    virtual     status_t   getRoutedDeviceIds(DeviceIdVector& deviceIds);
     virtual     status_t   enableAudioDeviceCallback(bool enabled);
     virtual     status_t   getActiveMicrophones(
                               std::vector<media::MicrophoneInfoFw>* activeMicrophones);
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 23e7a47..fa42da2 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -2595,11 +2595,11 @@
     return NO_ERROR;
 }
 
-status_t StagefrightRecorder::getRoutedDeviceId(audio_port_handle_t* deviceId) {
-    ALOGV("getRoutedDeviceId");
+status_t StagefrightRecorder::getRoutedDeviceIds(DeviceIdVector& deviceIds) {
+    ALOGV("getRoutedDeviceIds");
 
     if (mAudioSourceNode != 0) {
-        status_t status = mAudioSourceNode->getRoutedDeviceId(deviceId);
+        status_t status = mAudioSourceNode->getRoutedDeviceIds(deviceIds);
         return status;
     }
     return NO_INIT;
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 0b6a5bb..4c5e62f 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -80,7 +80,7 @@
     // Querying a SurfaceMediaSourcer
     virtual sp<IGraphicBufferProducer> querySurfaceMediaSource() const;
     virtual status_t setInputDevice(audio_port_handle_t deviceId);
-    virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId);
+    virtual status_t getRoutedDeviceIds(DeviceIdVector& deviceIds);
     virtual void setAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
     virtual status_t enableAudioDeviceCallback(bool enabled);
     virtual status_t getActiveMicrophones(std::vector<media::MicrophoneInfoFw>* activeMicrophones);
diff --git a/media/libmediaplayerservice/fuzzer/Android.bp b/media/libmediaplayerservice/fuzzer/Android.bp
index e413241..a3285ee 100644
--- a/media/libmediaplayerservice/fuzzer/Android.bp
+++ b/media/libmediaplayerservice/fuzzer/Android.bp
@@ -82,6 +82,7 @@
         "libactivitymanager_aidl",
         "libandroid_net",
         "libaudioflinger",
+        "libaudiofoundation",
         "libcamera_client",
         "libcodec2_client",
         "libcrypto",
@@ -160,6 +161,7 @@
         "libactivitymanager_aidl",
         "libandroid_net",
         "libaudioclient",
+        "libaudiofoundation",
         "libcamera_client",
         "libcodec2_client",
         "libcrypto",
diff --git a/media/libmediaplayerservice/fuzzer/mediaplayer_fuzzer.cpp b/media/libmediaplayerservice/fuzzer/mediaplayer_fuzzer.cpp
index 15265bf..a52d751 100644
--- a/media/libmediaplayerservice/fuzzer/mediaplayer_fuzzer.cpp
+++ b/media/libmediaplayerservice/fuzzer/mediaplayer_fuzzer.cpp
@@ -482,8 +482,8 @@
                     mMediaPlayer->setOutputDevice(deviceId);
                 },
                 [&]() {
-                    audio_port_handle_t deviceId;
-                    mMediaPlayer->getRoutedDeviceId(&deviceId);
+                    DeviceIdVector deviceIds;
+                    mMediaPlayer->getRoutedDeviceIds(deviceIds);
                 },
                 [&]() { mMediaPlayer->enableAudioDeviceCallback(mFdp.ConsumeBool()); },
                 [&]() {
diff --git a/media/libmediaplayerservice/fuzzer/mediarecorder_fuzzer.cpp b/media/libmediaplayerservice/fuzzer/mediarecorder_fuzzer.cpp
index 3339ae8..b95cae7 100644
--- a/media/libmediaplayerservice/fuzzer/mediarecorder_fuzzer.cpp
+++ b/media/libmediaplayerservice/fuzzer/mediarecorder_fuzzer.cpp
@@ -116,7 +116,7 @@
     virtual ~TestAudioDeviceCallback() = default;
 
     void onAudioDeviceUpdate(audio_io_handle_t /*audioIo*/,
-                             audio_port_handle_t /*deviceId*/) override{};
+                             const DeviceIdVector& /*deviceIds*/) override{};
 };
 
 class TestCamera : public ICamera {
@@ -185,8 +185,8 @@
     int32_t max;
     mStfRecorder->getMaxAmplitude(&max);
 
-    int32_t deviceId;
-    mStfRecorder->getRoutedDeviceId(&deviceId);
+    DeviceIdVector deviceIds;
+    mStfRecorder->getRoutedDeviceIds(deviceIds);
 
     vector<android::media::MicrophoneInfoFw> activeMicrophones{};
     mStfRecorder->getActiveMicrophones(&activeMicrophones);
diff --git a/media/libmediaplayerservice/include/MediaPlayerInterface.h b/media/libmediaplayerservice/include/MediaPlayerInterface.h
index 495cf00..9fe0e95 100644
--- a/media/libmediaplayerservice/include/MediaPlayerInterface.h
+++ b/media/libmediaplayerservice/include/MediaPlayerInterface.h
@@ -26,6 +26,7 @@
 #include <utils/RefBase.h>
 
 #include <media/mediaplayer.h>
+#include <media/AudioContainers.h>
 #include <media/AudioResamplerPublic.h>
 #include <media/AudioTimestamp.h>
 #include <media/AVSyncSettings.h>
@@ -185,7 +186,7 @@
 
         // AudioRouting
         virtual status_t    setOutputDevice(audio_port_handle_t deviceId) = 0;
-        virtual status_t    getRoutedDeviceId(audio_port_handle_t* deviceId) = 0;
+        virtual status_t    getRoutedDeviceIds(DeviceIdVector& deviceIds) = 0;
         virtual status_t    enableAudioDeviceCallback(bool enabled) = 0;
     };
 
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 92bf35d..b466f18 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -47,8 +47,8 @@
     ],
 
     cflags: [
-        "-Werror",
         "-Wall",
+        "-Werror",
     ],
 
     header_libs: [
@@ -57,6 +57,8 @@
     ],
 
     shared_libs: [
+        "android.hardware.cas.native@1.0",
+        "android.hardware.drm@1.0",
         "libaudioutils",
         "libgui",
         "libhidlallocatorutils",
@@ -66,15 +68,13 @@
         "libstagefright_foundation",
         "libui",
         "libutils",
-        "android.hardware.cas.native@1.0",
-        "android.hardware.drm@1.0",
     ],
 
     sanitize: {
         cfi: true,
         misc_undefined: [
-            "unsigned-integer-overflow",
             "signed-integer-overflow",
+            "unsigned-integer-overflow",
         ],
     },
 }
@@ -88,9 +88,9 @@
     min_sdk_version: "29",
 
     srcs: [
-        "Utils.cpp",
-        "MediaSource.cpp",
         "HevcUtils.cpp",
+        "MediaSource.cpp",
+        "Utils.cpp",
     ],
 
     shared_libs: [
@@ -114,17 +114,17 @@
     ],
 
     cflags: [
-        "-Wno-multichar",
+        "-Wall",
         "-Werror",
         "-Wno-error=deprecated-declarations",
-        "-Wall",
+        "-Wno-multichar",
     ],
 
     sanitize: {
         cfi: true,
         misc_undefined: [
-            "unsigned-integer-overflow",
             "signed-integer-overflow",
+            "unsigned-integer-overflow",
         ],
     },
 
@@ -150,12 +150,11 @@
     ],
 
     shared_libs: [
-        "libbase",
-        "libcutils",
         "libEGL",
         "libGLESv1_CM",
         "libGLESv2",
-        "libvulkan",
+        "libbase",
+        "libcutils",
         "libgui",
         "liblog",
         "libprocessgroup",
@@ -163,6 +162,7 @@
         "libsync",
         "libui",
         "libutils",
+        "libvulkan",
     ],
 
     static_libs: [
@@ -174,18 +174,18 @@
     ],
 
     cflags: [
-        "-Wno-multichar",
+        "-Wall",
         "-Werror",
         "-Wno-error=deprecated-declarations",
-        "-Wall",
+        "-Wno-multichar",
     ],
 
     sanitize: {
         // TODO: re-enabled cfi for this lib after b/139945549 fixed
         cfi: false,
         misc_undefined: [
-            "unsigned-integer-overflow",
             "signed-integer-overflow",
+            "unsigned-integer-overflow",
         ],
     },
 }
@@ -209,16 +209,16 @@
     ],
 
     cflags: [
-        "-Wno-multichar",
-        "-Werror",
         "-Wall",
+        "-Werror",
+        "-Wno-multichar",
     ],
 
     sanitize: {
         cfi: true,
         misc_undefined: [
-            "unsigned-integer-overflow",
             "signed-integer-overflow",
+            "unsigned-integer-overflow",
         ],
     },
 }
@@ -255,13 +255,13 @@
         "MediaCodecSource.cpp",
         "MediaExtractor.cpp",
         "MediaExtractorFactory.cpp",
+        "MediaMuxer.cpp",
         "MediaSource.cpp",
         "MediaSync.cpp",
         "MediaTrack.cpp",
-        "MediaMuxer.cpp",
         "NuMediaExtractor.cpp",
-        "OggWriter.cpp",
         "OMXClient.cpp",
+        "OggWriter.cpp",
         "OmxInfoBuilder.cpp",
         "RemoteMediaExtractor.cpp",
         "RemoteMediaSource.cpp",
@@ -270,13 +270,22 @@
         "SurfaceUtils.cpp",
         "ThrottledSource.cpp",
         "Utils.cpp",
-        "VideoFrameSchedulerBase.cpp",
         "VideoFrameScheduler.cpp",
+        "VideoFrameSchedulerBase.cpp",
         "VideoRenderQualityTracker.cpp",
     ],
 
     shared_libs: [
-        "libstagefright_framecapture_utils",
+        "aconfig_mediacodec_flags_c_lib",
+        "android.hardware.cas.native@1.0",
+        "android.hardware.drm@1.0",
+        "android.hardware.media.omx@1.0",
+        "android.hidl.allocator@1.0",
+        "framework-permission-aidl-cpp",
+        "libaconfig_storage_read_api_cc",
+        "libaudioclient",
+        "libaudioclient_aidl_conversion",
+        "libaudiofoundation",
         "libaudioutils",
         "libbase",
         "libbinder",
@@ -289,30 +298,24 @@
         "libdl",
         "libdl_android",
         "libgui",
+        "libhidlallocatorutils",
+        "libhidlbase",
+        "libhidlmemory",
         "liblog",
         "libmedia",
         "libmedia_codeclist",
+        "libmedia_helper",
         "libmedia_omx",
         "libmedia_omx_client",
-        "libaudioclient",
         "libmediametrics",
-        "libui",
-        "libutils",
-        "libmedia_helper",
         "libsfplugin_ccodec",
         "libsfplugin_ccodec_utils",
         "libstagefright_codecbase",
         "libstagefright_foundation",
+        "libstagefright_framecapture_utils",
         "libstagefright_omx_utils",
-        "libhidlallocatorutils",
-        "libhidlbase",
-        "libhidlmemory",
-        "android.hidl.allocator@1.0",
-        "android.hardware.cas.native@1.0",
-        "android.hardware.drm@1.0",
-        "android.hardware.media.omx@1.0",
-        "framework-permission-aidl-cpp",
-        "libaudioclient_aidl_conversion",
+        "libui",
+        "libutils",
         "packagemanager_aidl-cpp",
         "server_configurable_flags",
         "libaconfig_storage_read_api_cc",
@@ -323,32 +326,32 @@
     static_libs: [
         "android.media.codec-aconfig-cc",
         "com.android.media.flags.editing-aconfig-cc",
-        "libstagefright_esds",
-        "libstagefright_color_conversion",
-        "libyuv",
-        "libstagefright_webm",
-        "libstagefright_timedtext",
-        "libogg",
-        "libstagefright_id3",
         "framework-permission-aidl-cpp",
-        "libmediandk_format",
         "libmedia_ndkformatpriv",
+        "libmediandk_format",
+        "libogg",
+        "libstagefright_color_conversion",
+        "libstagefright_esds",
+        "libstagefright_id3",
+        "libstagefright_timedtext",
+        "libstagefright_webm",
+        "libyuv",
     ],
 
     header_libs: [
         "libmediadrm_headers",
+        "libmediaformatshaper_headers",
         "libnativeloader-headers",
         "libstagefright_xmlparser_headers",
         "media_ndk_headers",
-        "libmediaformatshaper_headers",
     ],
 
     export_shared_lib_headers: [
+        "android.hidl.allocator@1.0",
+        "framework-permission-aidl-cpp",
         "libgui",
         "libhidlmemory",
         "libmedia",
-        "android.hidl.allocator@1.0",
-        "framework-permission-aidl-cpp",
     ],
 
     export_include_dirs: [
@@ -356,10 +359,10 @@
     ],
 
     cflags: [
-        "-Wno-multichar",
+        "-Wall",
         "-Werror",
         "-Wno-error=deprecated-declarations",
-        "-Wall",
+        "-Wno-multichar",
     ],
 
     version_script: "exports.lds",
@@ -374,8 +377,8 @@
     sanitize: {
         cfi: true,
         misc_undefined: [
-            "unsigned-integer-overflow",
             "signed-integer-overflow",
+            "unsigned-integer-overflow",
         ],
     },
 }
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 584dad6..f658d84 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -497,9 +497,9 @@
     return NO_INIT;
 }
 
-status_t AudioSource::getRoutedDeviceId(audio_port_handle_t* deviceId) {
+status_t AudioSource::getRoutedDeviceIds(DeviceIdVector& deviceIds) {
     if (mRecord != 0) {
-        *deviceId = mRecord->getRoutedDeviceId();
+        deviceIds = mRecord->getRoutedDeviceIds();
         return NO_ERROR;
     }
     return NO_INIT;
diff --git a/media/libstagefright/include/media/stagefright/AudioSource.h b/media/libstagefright/include/media/stagefright/AudioSource.h
index 65d5246..51f6ac4 100644
--- a/media/libstagefright/include/media/stagefright/AudioSource.h
+++ b/media/libstagefright/include/media/stagefright/AudioSource.h
@@ -78,7 +78,7 @@
     virtual void signalBufferReturned(MediaBufferBase *buffer);
 
     status_t setInputDevice(audio_port_handle_t deviceId);
-    status_t getRoutedDeviceId(audio_port_handle_t* deviceId);
+    status_t getRoutedDeviceIds(DeviceIdVector& deviceIds);
     status_t addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
     status_t removeAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
 
diff --git a/media/libstagefright/webm/Android.bp b/media/libstagefright/webm/Android.bp
index 723131d..c3bd36e 100644
--- a/media/libstagefright/webm/Android.bp
+++ b/media/libstagefright/webm/Android.bp
@@ -11,8 +11,8 @@
     name: "libstagefright_webm",
 
     cflags: [
-        "-Werror",
         "-Wall",
+        "-Werror",
     ],
 
     sanitize: {
@@ -38,11 +38,12 @@
     export_include_dirs: ["include"],
 
     shared_libs: [
+        "framework-permission-aidl-cpp",
+        "libaudiofoundation",
         "libdatasource",
+        "liblog",
         "libstagefright_foundation",
         "libutils",
-        "liblog",
-        "framework-permission-aidl-cpp",
     ],
 
     header_libs: [
@@ -51,7 +52,6 @@
     ],
 }
 
-
 cc_library_headers {
     name: "libstagefright_webm_headers",
     export_include_dirs: ["include"],
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 2322780..b2edaf7 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1190,7 +1190,7 @@
                                             adjAttributionSource, &input.config, input.flags,
                                             &selectedDeviceIds, &portId, &secondaryOutputs,
                                             &isSpatialized, &isBitPerfect, &volume, &muted);
-    output.selectedDeviceId = getFirstDeviceId(selectedDeviceIds);
+    output.selectedDeviceIds = selectedDeviceIds;
 
     if (lStatus != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) {
         ALOGE("createTrack() getOutputForAttr() return error %d or invalid output handle", lStatus);
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index e42b39e..200175b 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -10921,7 +10921,7 @@
 
     // For mmap streams, once the routing has changed, they will be disconnected. It should be
     // okay to notify the client earlier before the new patch creation.
-    if (mDeviceIds != deviceIds) {
+    if (!areDeviceIdsEqual(deviceIds, mDeviceIds)) {
         if (const sp<MmapStreamCallback> callback = mCallback.promote()) {
             // The aaudioservice handle the routing changed event asynchronously. In that case,
             // it is safe to hold the lock here.
@@ -10945,7 +10945,7 @@
         *handle = AUDIO_PATCH_HANDLE_NONE;
     }
 
-    if (numDevices == 0 || mDeviceIds != deviceIds) {
+    if (numDevices == 0 || (!areDeviceIdsEqual(deviceIds, mDeviceIds))) {
         if (isOutput()) {
             sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED);
             mOutDeviceTypeAddrs = sinkDeviceTypeAddrs;
@@ -11110,8 +11110,7 @@
             if (const sp<MmapStreamCallback> callback = mCallback.promote()) {
                 // The aaudioservice handle the routing changed event asynchronously. In that case,
                 // it is safe to hold the lock here.
-                DeviceIdVector emptyDeviceIdVector;
-                callback->onRoutingChanged(emptyDeviceIdVector);
+                callback->onRoutingChanged({});
             } else if (mNoCallbackWarningCount < kMaxNoCallbackWarnings) {
                 ALOGW("Could not notify MMAP stream tear down: no onRoutingChanged callback!");
                 mNoCallbackWarningCount++;
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index b5ee2f2..243f1f1 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -119,8 +119,9 @@
         }
     }
 
-    ALOGV("findExclusiveEndpoint_l(), found %p for device = %d, sessionId = %d",
-          endpoint.get(), configuration.getDeviceId(), configuration.getSessionId());
+    ALOGV("findExclusiveEndpoint_l(), found %p for devices = %s, sessionId = %d",
+          endpoint.get(), toString(configuration.getDeviceIds()).c_str(),
+          configuration.getSessionId());
     return endpoint;
 }
 
@@ -137,8 +138,9 @@
         }
     }
 
-    ALOGV("findSharedEndpoint_l(), found %p for device = %d, sessionId = %d",
-          endpoint.get(), configuration.getDeviceId(), configuration.getSessionId());
+    ALOGV("findSharedEndpoint_l(), found %p for devices = %s, sessionId = %d",
+          endpoint.get(), toString(configuration.getDeviceIds()).c_str(),
+          configuration.getSessionId());
     return endpoint;
 }
 
@@ -192,8 +194,8 @@
     } else {
         const sp<AAudioServiceEndpointMMAP> endpointMMap =
                 new AAudioServiceEndpointMMAP(aaudioService);
-        ALOGV("%s(), no match so try to open MMAP %p for dev %d",
-              __func__, endpointMMap.get(), configuration.getDeviceId());
+        ALOGV("%s(), no match so try to open MMAP %p for devices %s",
+              __func__, endpointMMap.get(), toString(configuration.getDeviceIds()).c_str());
         endpoint = endpointMMap;
 
         const aaudio_result_t result = endpoint->open(request);
@@ -250,8 +252,9 @@
                 mSharedOpenCount++;
             }
         }
-        ALOGV("%s(), created endpoint %p, requested device = %d, dir = %d",
-              __func__, endpoint.get(), configuration.getDeviceId(), (int)direction);
+        ALOGV("%s(), created endpoint %p, requested device = %s, dir = %d",
+              __func__, endpoint.get(), android::toString(configuration.getDeviceIds()).c_str(),
+              (int)direction);
         IPCThreadState::self()->restoreCallingIdentity(token);
     }
 
@@ -289,8 +292,9 @@
 
         serviceEndpoint->close();
         mExclusiveCloseCount++;
-        ALOGV("%s() %p for device %d",
-              __func__, serviceEndpoint.get(), serviceEndpoint->getDeviceId());
+        ALOGV("%s() %p for devices %s",
+              __func__, serviceEndpoint.get(),
+              android::toString(serviceEndpoint->getDeviceIds()).c_str());
     }
 }
 
@@ -313,7 +317,8 @@
         serviceEndpoint->close();
 
         mSharedCloseCount++;
-        ALOGV("%s(%p) closed for device %d",
-              __func__, serviceEndpoint.get(), serviceEndpoint->getDeviceId());
+        ALOGV("%s(%p) closed for device %s",
+              __func__, serviceEndpoint.get(),
+              android::toString(serviceEndpoint->getDeviceIds()).c_str());
     }
 }
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index e49e9e7..c677619 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -57,7 +57,7 @@
     result << "    Direction:            " << ((getDirection() == AAUDIO_DIRECTION_OUTPUT)
                                    ? "OUTPUT" : "INPUT") << "\n";
     result << "    Requested Device Id:  " << mRequestedDeviceId << "\n";
-    result << "    Device Id:            " << getDeviceId() << "\n";
+    result << "    Device Ids:           " << android::toString(getDeviceIds()).c_str() << "\n";
     result << "    Sample Rate:          " << getSampleRate() << "\n";
     result << "    Channel Count:        " << getSamplesPerFrame() << "\n";
     result << "    Channel Mask:         0x" << std::hex << getChannelMask() << std::dec << "\n";
@@ -155,8 +155,8 @@
     if (configuration.getDirection() != getDirection()) {
         return false;
     }
-    if (configuration.getDeviceId() != AAUDIO_UNSPECIFIED &&
-        configuration.getDeviceId() != getDeviceId()) {
+    if (!configuration.getDeviceIds().empty() &&
+        !android::areDeviceIdsEqual(configuration.getDeviceIds(), getDeviceIds())) {
         return false;
     }
     if (configuration.getSessionId() != AAUDIO_SESSION_ID_ALLOCATE &&
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index 59bb98e..66918c1 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -105,7 +105,7 @@
     aaudio_result_t result = AAUDIO_OK;
     mAudioDataWrapper = std::make_unique<SharedMemoryWrapper>();
     copyFrom(request.getConstantConfiguration());
-    mRequestedDeviceId = getDeviceId();
+    mRequestedDeviceId = android::getFirstDeviceId(getDeviceIds());
 
     mMmapClient.attributionSource = request.getAttributionSource();
     // TODO b/182392769: use attribution source util
@@ -173,11 +173,13 @@
         audio_config_base_t* config) {
     aaudio_result_t result = AAUDIO_OK;
     audio_config_base_t currentConfig = *config;
-    audio_port_handle_t deviceId;
+    android::DeviceIdVector deviceIds;
 
     const audio_attributes_t attributes = getAudioAttributesFrom(this);
 
-    deviceId = mRequestedDeviceId;
+    if (mRequestedDeviceId != AAUDIO_UNSPECIFIED) {
+        deviceIds.push_back(mRequestedDeviceId);
+    }
 
     const aaudio_direction_t direction = getDirection();
 
@@ -202,14 +204,9 @@
 
     // Open HAL stream. Set mMmapStream
     ALOGD("%s trying to open MMAP stream with format=%#x, "
-          "sample_rate=%u, channel_mask=%#x, device=%d",
+          "sample_rate=%u, channel_mask=%#x, device=%s",
           __func__, config->format, config->sample_rate,
-          config->channel_mask, deviceId);
-
-    android::DeviceIdVector deviceIds;
-    if (deviceId != AAUDIO_UNSPECIFIED) {
-        deviceIds.push_back(deviceId);
-    }
+          config->channel_mask, android::toString(deviceIds).c_str());
 
     const std::lock_guard<std::mutex> lock(mMmapStreamLock);
     const status_t status = MmapStreamInterface::openMmapStream(streamDirection,
@@ -233,12 +230,11 @@
         config->channel_mask = currentConfig.channel_mask;
         return AAUDIO_ERROR_UNAVAILABLE;
     }
-    deviceId = android::getFirstDeviceId(deviceIds);
 
-    if (deviceId == AAUDIO_UNSPECIFIED) {
-        ALOGW("%s() - openMmapStream() failed to set deviceId", __func__);
+    if (deviceIds.empty()) {
+        ALOGW("%s() - openMmapStream() failed to set deviceIds", __func__);
     }
-    setDeviceId(deviceId);
+    setDeviceIds(deviceIds);
 
     if (sessionId == AUDIO_SESSION_ALLOCATE) {
         ALOGW("%s() - openMmapStream() failed to set sessionId", __func__);
@@ -250,8 +246,8 @@
             : (aaudio_session_id_t) sessionId;
     setSessionId(actualSessionId);
 
-    ALOGD("%s(format = 0x%X) deviceId = %d, sessionId = %d",
-          __func__, config->format, getDeviceId(), getSessionId());
+    ALOGD("%s(format = 0x%X) deviceIds = %s, sessionId = %d",
+          __func__, config->format, toString(getDeviceIds()).c_str(), getSessionId());
 
     // Create MMAP/NOIRQ buffer.
     result = createMmapBuffer_l();
@@ -280,9 +276,9 @@
 
     mDataReportOffsetNanos = ((int64_t)mTimestampGracePeriodMs) * AAUDIO_NANOS_PER_MILLISECOND;
 
-    ALOGD("%s() got rate = %d, channels = %d channelMask = %#x, deviceId = %d, capacity = %d\n",
+    ALOGD("%s() got rate = %d, channels = %d channelMask = %#x, deviceIds = %s, capacity = %d\n",
           __func__, getSampleRate(), getSamplesPerFrame(), getChannelMask(),
-          deviceId, getBufferCapacity());
+          android::toString(deviceIds).c_str(), getBufferCapacity());
 
     ALOGD("%s() got format = 0x%X = %s, frame size = %d, burst size = %d",
           __func__, getFormat(), audio_format_to_string(getFormat()),
@@ -293,7 +289,11 @@
 error:
     close_l();
     // restore original requests
-    setDeviceId(mRequestedDeviceId);
+    android::DeviceIdVector requestedDeviceIds;
+    if (mRequestedDeviceId != AAUDIO_UNSPECIFIED) {
+        requestedDeviceIds.push_back(mRequestedDeviceId);
+    }
+    setDeviceIds(requestedDeviceIds);
     setSessionId(requestedSessionId);
     return result;
 }
@@ -491,27 +491,26 @@
 };
 
 void AAudioServiceEndpointMMAP::onRoutingChanged(const android::DeviceIdVector& deviceIds) {
-    const auto deviceId = android::getFirstDeviceId(deviceIds);
-    // TODO(b/367816690): Compare the new and saved device sets.
-    ALOGD("%s() called with dev %d, old = %d", __func__, deviceId, getDeviceId());
-    if (getDeviceId() != deviceId) {
-        if (getDeviceId() != AUDIO_PORT_HANDLE_NONE) {
+    ALOGD("%s() called with dev %s, old = %s", __func__, android::toString(deviceIds).c_str(),
+          android::toString(getDeviceIds()).c_str());
+    if (!android::areDeviceIdsEqual(getDeviceIds(), deviceIds)) {
+        if (!getDeviceIds().empty()) {
             // When there is a routing changed, mmap stream should be disconnected. Set `mConnected`
-            // as false here so that there won't be a new stream connect to this endpoint.
+            // as false here so that there won't be a new stream connected to this endpoint.
             mConnected.store(false);
             const android::sp<AAudioServiceEndpointMMAP> holdEndpoint(this);
-            std::thread asyncTask([holdEndpoint, deviceId]() {
+            std::thread asyncTask([holdEndpoint, deviceIds]() {
                 ALOGD("onRoutingChanged() asyncTask launched");
                 // When routing changed, the stream is disconnected and cannot be used except for
                 // closing. In that case, it should be safe to release all registered streams.
                 // This can help release service side resource in case the client doesn't close
                 // the stream after receiving disconnect event.
                 holdEndpoint->releaseRegisteredStreams();
-                holdEndpoint->setDeviceId(deviceId);
+                holdEndpoint->setDeviceIds(deviceIds);
             });
             asyncTask.detach();
         } else {
-            setDeviceId(deviceId);
+            setDeviceIds(deviceIds);
         }
     }
 };
diff --git a/services/oboeservice/AAudioServiceEndpointShared.cpp b/services/oboeservice/AAudioServiceEndpointShared.cpp
index 5e1e594..f54de5e 100644
--- a/services/oboeservice/AAudioServiceEndpointShared.cpp
+++ b/services/oboeservice/AAudioServiceEndpointShared.cpp
@@ -64,7 +64,7 @@
     const AAudioStreamConfiguration &configuration = request.getConstantConfiguration();
 
     copyFrom(configuration);
-    mRequestedDeviceId = configuration.getDeviceId();
+    mRequestedDeviceId = android::getFirstDeviceId(configuration.getDeviceIds());
 
     AudioStreamBuilder builder;
     builder.copyFrom(configuration);
@@ -79,7 +79,7 @@
 
     setSampleRate(mStreamInternal->getSampleRate());
     setChannelMask(mStreamInternal->getChannelMask());
-    setDeviceId(mStreamInternal->getDeviceId());
+    setDeviceIds(mStreamInternal->getDeviceIds());
     setSessionId(mStreamInternal->getSessionId());
     setFormat(AUDIO_FORMAT_PCM_FLOAT); // force for mixer
     setHardwareSampleRate(mStreamInternal->getHardwareSampleRate());
@@ -220,7 +220,7 @@
 void AAudioServiceEndpointShared::handleDisconnectRegisteredStreamsAsync() {
     android::sp<AAudioServiceEndpointShared> holdEndpoint(this);
     // When there is a routing changed, mmap stream should be disconnected. Set `mConnected`
-    // as false here so that there won't be a new stream connect to this endpoint.
+    // as false here so that there won't be a new stream connected to this endpoint.
     mConnected.store(false);
     std::thread asyncTask([holdEndpoint]() {
         // When handling disconnection, the service side has disconnected. In that case,
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 78cf706..1c24f18 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -127,7 +127,8 @@
         .set(AMEDIAMETRICS_PROP_DIRECTION,
                 AudioGlobal_convertDirectionToText(getDirection()))
         .set(AMEDIAMETRICS_PROP_ENCODING, toString(getFormat()).c_str())
-        .set(AMEDIAMETRICS_PROP_ROUTEDDEVICEID, (int32_t)getDeviceId())
+        .set(AMEDIAMETRICS_PROP_ROUTEDDEVICEID, android::getFirstDeviceId(getDeviceIds()))
+        .set(AMEDIAMETRICS_PROP_ROUTEDDEVICEIDS, android::toString(getDeviceIds()).c_str())
         .set(AMEDIAMETRICS_PROP_SAMPLERATE, (int32_t)getSampleRate())
         .set(AMEDIAMETRICS_PROP_SESSIONID, (int32_t)getSessionId())
         .set(AMEDIAMETRICS_PROP_SOURCE, toString(attributes.source).c_str())
diff --git a/services/oboeservice/fuzzer/oboeservice_fuzzer.cpp b/services/oboeservice/fuzzer/oboeservice_fuzzer.cpp
index f5c2e6c..e80f51d 100644
--- a/services/oboeservice/fuzzer/oboeservice_fuzzer.cpp
+++ b/services/oboeservice/fuzzer/oboeservice_fuzzer.cpp
@@ -372,7 +372,8 @@
     request.setAttributionSource(attributionSource);
     request.setInService(fdp.ConsumeBool());
 
-    request.getConfiguration().setDeviceId(fdp.ConsumeIntegral<int32_t>());
+    android::DeviceIdVector DeviceIdVector = { fdp.ConsumeIntegral<int32_t>() };
+    request.getConfiguration().setDeviceIds(DeviceIdVector);
     request.getConfiguration().setSampleRate(fdp.ConsumeIntegral<int32_t>());
     request.getConfiguration().setChannelMask((aaudio_channel_mask_t)(
         fdp.ConsumeBool()