Use routed devices throughout the audio framework

After you open an audio stream, you can call getRoutedDevice() to
get which output device is actually used.

However, if you play an ALARM with a headset attached, audio comes
out of both the speaker and the headset. This is not properly reflected
with our current APIs.

Throughout the audio framework, we should use DeviceIdVector instead
of a single device when getRoutedDevice(s) are called.

This change touches many files because onAudioDeviceUpdate() has many
callers.

BYPASS_LARGE_CHANGE_WARNING

Bug: 367816690
Test: atest AAudioTests
Test: atest audiorecord_tests
Test: atest audiosystem_tests
Test: atest audiotrack_tests
Test: adb shell /data/fuzz/arm64/libaaudio_fuzzer/libaaudio_fuzzer
Test: adb shell /data/fuzz/arm64/mediaplayer_fuzzer/mediaplayer_fuzzer
Test: adb shell /data/fuzz/arm64/mediarecorder_fuzzer/mediarecorder_fuzzer
Test: OboeTester Test Output with Alarm and USB
Flag: EXEMPT refactor
Change-Id: I5e32ac5d1c24229e60112ef00d82af41d8eff6e7
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));