Add secondary output to audio tracks

The secondary are returned from mixes (from DAP loopback&render),
from getOutputForAttr.
All getOutputForAttr* of the stack are update.
Internal getOutputForAttr use descriptor, external one use handles.

The secondary output are saved in each track and the track is
invalidated if the list of secondary output changes.

In audio flinger, create a pair of recordTrack & patchTrack to pipe
the intercepted audio audio to the secondary output.

Test: adb shell audiorecorder --target /data/file.raw
Bug: 111453086
Change-Id: Id6523d9e383c15a0e39313d5f355df809b7e72fe
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index b83a441e..41a7ff0 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -872,13 +872,14 @@
                                         const audio_config_t *config,
                                         audio_output_flags_t flags,
                                         audio_port_handle_t *selectedDeviceId,
-                                        audio_port_handle_t *portId)
+                                        audio_port_handle_t *portId,
+                                        std::vector<audio_io_handle_t> *secondaryOutputs)
 {
     const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
     if (aps == 0) return NO_INIT;
     return aps->getOutputForAttr(attr, output, session, stream, pid, uid,
                                  config,
-                                 flags, selectedDeviceId, portId);
+                                 flags, selectedDeviceId, portId, secondaryOutputs);
 }
 
 status_t AudioSystem::startOutput(audio_port_handle_t portId)
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
index 0db56e8..d9f6e36 100644
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ b/media/libaudioclient/IAudioPolicyService.cpp
@@ -189,16 +189,17 @@
         return static_cast <audio_io_handle_t> (reply.readInt32());
     }
 
-    virtual status_t getOutputForAttr(const audio_attributes_t *attr,
-                                        audio_io_handle_t *output,
-                                        audio_session_t session,
-                                        audio_stream_type_t *stream,
-                                        pid_t pid,
-                                        uid_t uid,
-                                        const audio_config_t *config,
-                                        audio_output_flags_t flags,
-                                        audio_port_handle_t *selectedDeviceId,
-                                        audio_port_handle_t *portId)
+    status_t getOutputForAttr(const audio_attributes_t *attr,
+                              audio_io_handle_t *output,
+                              audio_session_t session,
+                              audio_stream_type_t *stream,
+                              pid_t pid,
+                              uid_t uid,
+                              const audio_config_t *config,
+                              audio_output_flags_t flags,
+                              audio_port_handle_t *selectedDeviceId,
+                              audio_port_handle_t *portId,
+                              std::vector<audio_io_handle_t> *secondaryOutputs) override
         {
             Parcel data, reply;
             data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
@@ -224,6 +225,10 @@
                 ALOGE("getOutputForAttr NULL portId - shouldn't happen");
                 return BAD_VALUE;
             }
+            if (secondaryOutputs == NULL) {
+                ALOGE("getOutputForAttr NULL secondaryOutputs - shouldn't happen");
+                return BAD_VALUE;
+            }
             if (attr == NULL) {
                 data.writeInt32(0);
             } else {
@@ -258,7 +263,9 @@
             }
             *selectedDeviceId = (audio_port_handle_t)reply.readInt32();
             *portId = (audio_port_handle_t)reply.readInt32();
-            return status;
+            secondaryOutputs->resize(reply.readInt32());
+            return reply.read(secondaryOutputs->data(),
+                              secondaryOutputs->size() * sizeof(audio_io_handle_t));
         }
 
     virtual status_t startOutput(audio_port_handle_t portId)
@@ -1300,16 +1307,19 @@
             audio_port_handle_t selectedDeviceId = data.readInt32();
             audio_port_handle_t portId = (audio_port_handle_t)data.readInt32();
             audio_io_handle_t output = 0;
+            std::vector<audio_io_handle_t> secondaryOutputs;
             status_t status = getOutputForAttr(hasAttributes ? &attr : NULL,
                     &output, session, &stream, pid, uid,
                     &config,
-                    flags, &selectedDeviceId, &portId);
+                    flags, &selectedDeviceId, &portId, &secondaryOutputs);
             reply->writeInt32(status);
             reply->writeInt32(output);
             reply->writeInt32(stream);
             reply->writeInt32(selectedDeviceId);
             reply->writeInt32(portId);
-            return NO_ERROR;
+            reply->writeInt32(secondaryOutputs.size());
+            return reply->write(secondaryOutputs.data(),
+                                secondaryOutputs.size() * sizeof(audio_io_handle_t));
         } break;
 
         case START_OUTPUT: {
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 87a9919..6060894 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -231,7 +231,8 @@
                                      const audio_config_t *config,
                                      audio_output_flags_t flags,
                                      audio_port_handle_t *selectedDeviceId,
-                                     audio_port_handle_t *portId);
+                                     audio_port_handle_t *portId,
+                                     std::vector<audio_io_handle_t> *secondaryOutputs);
     static status_t startOutput(audio_port_handle_t portId);
     static status_t stopOutput(audio_port_handle_t portId);
     static void releaseOutput(audio_port_handle_t portId);
diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h
index b2cda32..e89a55d 100644
--- a/media/libaudioclient/include/media/IAudioPolicyService.h
+++ b/media/libaudioclient/include/media/IAudioPolicyService.h
@@ -66,7 +66,8 @@
                                       const audio_config_t *config,
                                       audio_output_flags_t flags,
                                       audio_port_handle_t *selectedDeviceId,
-                                      audio_port_handle_t *portId) = 0;
+                                      audio_port_handle_t *portId,
+                                      std::vector<audio_io_handle_t> *secondaryOutputs) = 0;
     virtual status_t startOutput(audio_port_handle_t portId) = 0;
     virtual status_t stopOutput(audio_port_handle_t portId) = 0;
     virtual void releaseOutput(audio_port_handle_t portId) = 0;
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index bc99099..befabc5 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -292,13 +292,16 @@
         fullConfig.sample_rate = config->sample_rate;
         fullConfig.channel_mask = config->channel_mask;
         fullConfig.format = config->format;
+        std::vector<audio_io_handle_t> secondaryOutputs;
         ret = AudioSystem::getOutputForAttr(attr, &io,
                                             actualSessionId,
                                             &streamType, client.clientPid, client.clientUid,
                                             &fullConfig,
                                             (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ |
                                                     AUDIO_OUTPUT_FLAG_DIRECT),
-                                            deviceId, &portId);
+                                            deviceId, &portId, &secondaryOutputs);
+        ALOGW_IF(!secondaryOutputs.empty(),
+                 "%s does not support secondary outputs, ignoring them", __func__);
     } else {
         ret = AudioSystem::getInputForAttr(attr, &io,
                                               actualSessionId,
@@ -678,6 +681,7 @@
     status_t lStatus;
     audio_stream_type_t streamType;
     audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
+    std::vector<audio_io_handle_t> secondaryOutputs;
 
     bool updatePid = (input.clientInfo.clientPid == -1);
     const uid_t callingUid = IPCThreadState::self()->getCallingUid();
@@ -712,7 +716,7 @@
 
     lStatus = AudioSystem::getOutputForAttr(&input.attr, &output.outputId, sessionId, &streamType,
                                             clientPid, clientUid, &input.config, input.flags,
-                                            &output.selectedDeviceId, &portId);
+                                            &output.selectedDeviceId, &portId, &secondaryOutputs);
 
     if (lStatus != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) {
         ALOGE("createTrack() getOutputForAttr() return error %d or invalid output handle", lStatus);
@@ -785,6 +789,61 @@
         output.afLatencyMs = thread->latency();
         output.portId = portId;
 
+        if (lStatus == NO_ERROR) {
+            // Connect secondary outputs. Failure on a secondary output must not imped the primary
+            // Any secondary output setup failure will lead to a desync between the AP and AF until
+            // the track is destroyed.
+            TeePatches teePatches;
+            for (audio_io_handle_t secondaryOutput : secondaryOutputs) {
+                PlaybackThread *secondaryThread = checkPlaybackThread_l(secondaryOutput);
+                if (secondaryThread == NULL) {
+                    ALOGE("no playback thread found for secondary output %d", output.outputId);
+                    continue;
+                }
+
+                size_t frameCount = std::lcm(thread->frameCount(), secondaryThread->frameCount());
+
+                using namespace std::chrono_literals;
+                auto inChannelMask = audio_channel_mask_out_to_in(input.config.channel_mask);
+                sp patchRecord = new RecordThread::PatchRecord(nullptr /* thread */,
+                                                               output.sampleRate,
+                                                               inChannelMask,
+                                                               input.config.format,
+                                                               frameCount,
+                                                               NULL /* buffer */,
+                                                               (size_t)0 /* bufferSize */,
+                                                               AUDIO_INPUT_FLAG_DIRECT,
+                                                               0ns /* timeout */);
+                status_t status = patchRecord->initCheck();
+                if (status != NO_ERROR) {
+                    ALOGE("Secondary output patchRecord init failed: %d", status);
+                    continue;
+                }
+                sp patchTrack = new PlaybackThread::PatchTrack(secondaryThread,
+                                                               streamType,
+                                                               output.sampleRate,
+                                                               input.config.channel_mask,
+                                                               input.config.format,
+                                                               frameCount,
+                                                               patchRecord->buffer(),
+                                                               patchRecord->bufferSize(),
+                                                               output.flags,
+                                                               0ns /* timeout */);
+                status = patchTrack->initCheck();
+                if (status != NO_ERROR) {
+                    ALOGE("Secondary output patchTrack init failed: %d", status);
+                    continue;
+                }
+                teePatches.push_back({patchRecord, patchTrack});
+                secondaryThread->addPatchTrack(patchTrack);
+                patchTrack->setPeerProxy(patchRecord.get());
+                patchRecord->setPeerProxy(patchTrack.get());
+
+                patchTrack->start(); // patchRecord is NOT started as it has no thread
+            }
+            track->setTeePatches(std::move(teePatches));
+        }
+
         // move effect chain to this output thread if an effect on same session was waiting
         // for a track to be created
         if (lStatus == NO_ERROR && effectThread != NULL) {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index f16a196..1441e15 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -24,6 +24,7 @@
 #include <chrono>
 #include <deque>
 #include <map>
+#include <numeric>
 #include <optional>
 #include <set>
 #include <string>
@@ -528,6 +529,9 @@
     class EffectChain;
 
     struct AudioStreamIn;
+    struct TeePatch;
+    using TeePatches = std::vector<TeePatch>;
+
 
     struct  stream_type_t {
         stream_type_t()
@@ -727,6 +731,11 @@
             audioHwDev(dev), stream(in), flags(flags) {}
     };
 
+    struct TeePatch {
+        sp<RecordThread::PatchRecord> patchRecord;
+        sp<PlaybackThread::PatchTrack> patchTrack;
+    };
+
     // for mAudioSessionRefs only
     struct AudioSessionRef {
         AudioSessionRef(audio_session_t sessionid, pid_t pid) :
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 33048fc..4683525 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -43,9 +43,8 @@
 
             void        appendDumpHeader(String8& result);
             void        appendDump(String8& result, bool active);
-    virtual status_t    start(AudioSystem::sync_event_t event =
-                                    AudioSystem::SYNC_EVENT_NONE,
-                             audio_session_t triggerSession = AUDIO_SESSION_NONE);
+    virtual status_t    start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE,
+                              audio_session_t triggerSession = AUDIO_SESSION_NONE);
     virtual void        stop();
             void        pause();
 
@@ -129,6 +128,8 @@
             }
             sp<os::ExternalVibration> getExternalVibration() const { return mExternalVibration; }
 
+            void    setTeePatches(TeePatches teePatches);
+
 protected:
     // for numerous
     friend class PlaybackThread;
@@ -139,8 +140,8 @@
     DISALLOW_COPY_AND_ASSIGN(Track);
 
     // AudioBufferProvider interface
-    virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
-    // releaseBuffer() not overridden
+    status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) override;
+    void releaseBuffer(AudioBufferProvider::Buffer* buffer) override;
 
     // ExtendedAudioBufferProvider interface
     virtual size_t framesReady() const;
@@ -220,6 +221,8 @@
     sp<os::ExternalVibration>    mExternalVibration;
 
 private:
+    void                interceptBuffer(const AudioBufferProvider::Buffer& buffer);
+
     // The following fields are only for fast tracks, and should be in a subclass
     int                 mFastIndex; // index within FastMixerState::mFastTracks[];
                                     // either mFastIndex == -1 if not isFastTrack()
@@ -239,6 +242,7 @@
     audio_output_flags_t mFlags;
     // If the last track change was notified to the client with readAndClearHasChanged
     std::atomic_flag     mChangeNotified = ATOMIC_FLAG_INIT;
+    TeePatches  mTeePatches;
 };  // end of Track
 
 
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 5a70864..dd1eabf 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -8464,6 +8464,7 @@
         audio_output_flags_t flags =
                 (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT);
         audio_port_handle_t deviceId = mDeviceId;
+        std::vector<audio_io_handle_t> secondaryOutputs;
         ret = AudioSystem::getOutputForAttr(&mAttr, &io,
                                             mSessionId,
                                             &stream,
@@ -8472,7 +8473,10 @@
                                             &config,
                                             flags,
                                             &deviceId,
-                                            &portId);
+                                            &portId,
+                                            &secondaryOutputs);
+        ALOGD_IF(!secondaryOutputs.empty(),
+                 "MmapThread::start does not support secondary outputs, ignoring them");
     } else {
         audio_config_base_t config;
         config.sample_rate = mSampleRate;
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 863dc9e..37c3a2d 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -99,7 +99,7 @@
         mId(android_atomic_inc(&nextTrackId)),
         mTerminated(false),
         mType(type),
-        mThreadIoHandle(thread->id()),
+        mThreadIoHandle(thread ? thread->id() : AUDIO_IO_HANDLE_NONE),
         mPortId(portId),
         mIsInvalid(false)
 {
@@ -670,8 +670,7 @@
 }
 
 // AudioBufferProvider interface
-status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(
-        AudioBufferProvider::Buffer* buffer)
+status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer)
 {
     ServerProxy::Buffer buf;
     size_t desiredFrames = buffer->frameCount;
@@ -686,10 +685,39 @@
     } else {
         mAudioTrackServerProxy->tallyUnderrunFrames(0);
     }
-
     return status;
 }
 
+void AudioFlinger::PlaybackThread::Track::releaseBuffer(AudioBufferProvider::Buffer* buffer)
+{
+    interceptBuffer(*buffer);
+    TrackBase::releaseBuffer(buffer);
+}
+
+// TODO: compensate for time shift between HW modules.
+void AudioFlinger::PlaybackThread::Track::interceptBuffer(
+        const AudioBufferProvider::Buffer& buffer) {
+    for (auto& sink : mTeePatches) {
+        RecordThread::PatchRecord& patchRecord = *sink.patchRecord;
+        AudioBufferProvider::Buffer patchBuffer;
+        patchBuffer.frameCount = buffer.frameCount;
+        auto status = patchRecord.getNextBuffer(&patchBuffer);
+        if (status != NO_ERROR) {
+           ALOGW("%s PathRecord getNextBuffer failed with error %d: %s",
+                 __func__, status, strerror(-status));
+           continue;
+        }
+        // FIXME: On buffer wrap, the frame count will be less then requested,
+        //        retry to write the rest. (unlikely due to lcm buffer sizing)
+        ALOGW_IF(patchBuffer.frameCount != buffer.frameCount,
+                 "%s PatchRecord can not provide big enough buffer %zu/%zu, dropping %zu frames",
+                 __func__, patchBuffer.frameCount, buffer.frameCount,
+                 buffer.frameCount - patchBuffer.frameCount);
+        memcpy(patchBuffer.raw, buffer.raw, patchBuffer.frameCount * mFrameSize);
+        patchRecord.releaseBuffer(&patchBuffer);
+    }
+}
+
 // releaseBuffer() is not overridden
 
 // ExtendedAudioBufferProvider interface
@@ -1081,6 +1109,10 @@
     };
 }
 
+void AudioFlinger::PlaybackThread::Track::setTeePatches(TeePatches teePatches) {
+    mTeePatches = std::move(teePatches);
+}
+
 status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& timestamp)
 {
     if (!isOffloaded() && !isDirect()) {
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index d7030f9..bb5441d 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -110,7 +110,8 @@
                                         const audio_config_t *config,
                                         audio_output_flags_t *flags,
                                         audio_port_handle_t *selectedDeviceId,
-                                        audio_port_handle_t *portId) = 0;
+                                        audio_port_handle_t *portId,
+                                        std::vector<audio_io_handle_t> *secondaryOutputs) = 0;
     // indicates to the audio policy manager that the output starts being used by corresponding stream.
     virtual status_t startOutput(audio_port_handle_t portId) = 0;
     // indicates to the audio policy manager that the output stops being used by corresponding stream.
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
index e5c7d48..1abce6f 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
@@ -68,13 +68,12 @@
      * Try to find an output descriptor for the given attributes.
      *
      * @param[in] attributes to consider fowr the research of output descriptor.
-     * @param[out] desc to return if an output could be found.
-     *
-     * @return NO_ERROR if an output was found for the given attribute (in this case, the
-     *                  descriptor output param is initialized), error code otherwise.
+     * @param[out] desc to return if an primary output could be found.
+     * @param[out] secondaryDesc other desc that the audio should be routed to.
      */
     status_t getOutputForAttr(audio_attributes_t attributes, uid_t uid,
-            sp<SwAudioOutputDescriptor> &desc);
+                sp<SwAudioOutputDescriptor> &primaryDesc,
+                std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs);
 
     sp<DeviceDescriptor> getDeviceAndMixForInputSource(audio_source_t inputSource,
                                                        const DeviceVector &availableDeviceTypes,
diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
index 4c069e4..2e44a60 100644
--- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
@@ -86,10 +86,12 @@
                           audio_attributes_t attributes, audio_config_base_t config,
                           audio_port_handle_t preferredDeviceId, audio_stream_type_t stream,
                           product_strategy_t strategy, audio_output_flags_t flags,
-                          bool isPreferredDeviceForExclusiveUse) :
+                          bool isPreferredDeviceForExclusiveUse,
+                          std::vector<wp<SwAudioOutputDescriptor>> secondaryOutputs) :
         ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId,
                          isPreferredDeviceForExclusiveUse),
-        mStream(stream), mStrategy(strategy), mFlags(flags) {}
+        mStream(stream), mStrategy(strategy), mFlags(flags),
+        mSecondaryOutputs(std::move(secondaryOutputs)) {}
     ~TrackClientDescriptor() override = default;
 
     using ClientDescriptor::dump;
@@ -99,11 +101,15 @@
     audio_output_flags_t flags() const { return mFlags; }
     audio_stream_type_t stream() const { return mStream; }
     product_strategy_t strategy() const { return mStrategy; }
+    const std::vector<wp<SwAudioOutputDescriptor>>& getSecondaryOutputs() const {
+        return mSecondaryOutputs;
+    };
 
 private:
     const audio_stream_type_t mStream;
     const product_strategy_t mStrategy;
     const audio_output_flags_t mFlags;
+    const std::vector<wp<SwAudioOutputDescriptor>> mSecondaryOutputs;
 };
 
 class RecordClientDescriptor: public ClientDescriptor
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index 32c90b1..6b6d9d2 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -157,30 +157,49 @@
 }
 
 status_t AudioPolicyMixCollection::getOutputForAttr(
-        audio_attributes_t attributes, uid_t uid, sp<SwAudioOutputDescriptor> &desc)
+        audio_attributes_t attributes, uid_t uid, sp<SwAudioOutputDescriptor> &primaryDesc,
+        std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs)
 {
     ALOGV("getOutputForAttr() querying %zu mixes:", size());
-    desc = 0;
+    primaryDesc = 0;
     for (size_t i = 0; i < size(); i++) {
         sp<AudioPolicyMix> policyMix = valueAt(i);
         sp<SwAudioOutputDescriptor> policyDesc = policyMix->getOutput();
         if (!policyDesc) {
-            ALOGV("Skiping %zu: Mix has no output", i);
+            ALOGV("%s: Skiping %zu: Mix has no output", __func__, i);
             continue;
         }
 
         AudioMix *mix = policyMix->getMix();
+        const bool primaryOutputMix = !is_mix_loopback_render(mix->mRouteFlags);
+
+        if (primaryOutputMix && primaryDesc != 0) {
+            ALOGV("%s: Skiping %zu: Primary output already found", __func__, i);
+            continue; // Primary output already found
+        }
+
         switch (mixMatch(mix, i, attributes, uid)) {
-            case MixMatchStatus::INVALID_MIX: return BAD_VALUE; // TODO: Do we really want to abort ?
-            case MixMatchStatus::NO_MATCH: continue; // skip the mix
+            case MixMatchStatus::INVALID_MIX: return BAD_VALUE; // TODO: Do we really want to abort?
+            case MixMatchStatus::NO_MATCH:
+                ALOGV("%s: Mix %zu: does not match", __func__, i);
+                continue; // skip the mix
             case MixMatchStatus::MATCH:;
         }
 
-        desc = policyMix->getOutput();
-        desc->mPolicyMix = mix;
-        return NO_ERROR;
+        policyDesc->mPolicyMix = mix;
+        if (primaryOutputMix) {
+            primaryDesc = policyDesc;
+            ALOGV("%s: Mix %zu: set primary desc", __func__, i);
+        } else {
+            if (policyDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) {
+                ALOGV("%s: Mix %zu ignored as secondaryOutput because not opened yet", __func__, i);
+            } else {
+                ALOGV("%s: Add a secondary desc %zu", __func__, i);
+                secondaryDescs->push_back(policyDesc);
+            }
+        }
     }
-    return BAD_VALUE;
+    return (primaryDesc == nullptr && secondaryDescs->empty()) ? BAD_VALUE : NO_ERROR;
 }
 
 AudioPolicyMixCollection::MixMatchStatus AudioPolicyMixCollection::mixMatch(
diff --git a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
index a6f6c3b..633c40e 100644
--- a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
@@ -85,7 +85,8 @@
          product_strategy_t strategy) :
     TrackClientDescriptor::TrackClientDescriptor(portId, uid, AUDIO_SESSION_NONE, attributes,
         AUDIO_CONFIG_BASE_INITIALIZER, AUDIO_PORT_HANDLE_NONE,
-        stream, strategy, AUDIO_OUTPUT_FLAG_NONE, false),
+        stream, strategy, AUDIO_OUTPUT_FLAG_NONE, false,
+        {} /* Sources do not support secondary outputs*/),
         mPatchDesc(patchDesc), mSrcDevice(srcDevice)
 {
 }
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 0eee3f2..02b85b4 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -36,6 +36,7 @@
 
 #include <inttypes.h>
 #include <math.h>
+#include <set>
 #include <unordered_set>
 #include <vector>
 
@@ -907,16 +908,18 @@
     return NO_ERROR;
 }
 
-status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr,
-                                                 audio_io_handle_t *output,
-                                                 audio_session_t session,
-                                                 const audio_attributes_t *attr,
-                                                 audio_stream_type_t *stream,
-                                                 uid_t uid,
-                                                 const audio_config_t *config,
-                                                 audio_output_flags_t *flags,
-                                                 audio_port_handle_t *selectedDeviceId,
-                                                 bool *isRequestedDeviceForExclusiveUse)
+status_t AudioPolicyManager::getOutputForAttrInt(
+        audio_attributes_t *resultAttr,
+        audio_io_handle_t *output,
+        audio_session_t session,
+        const audio_attributes_t *attr,
+        audio_stream_type_t *stream,
+        uid_t uid,
+        const audio_config_t *config,
+        audio_output_flags_t *flags,
+        audio_port_handle_t *selectedDeviceId,
+        bool *isRequestedDeviceForExclusiveUse,
+        std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs)
 {
     DeviceVector outputDevices;
     const audio_port_handle_t requestedPortId = *selectedDeviceId;
@@ -935,19 +938,26 @@
     ALOGV("%s() attributes=%s stream=%s session %d selectedDeviceId %d", __func__,
           toString(*resultAttr).c_str(), toString(*stream).c_str(), session, requestedPortId);
 
-    // 1/ First check for explicit routing (eg. setPreferredDevice): NOTE: now handled by engine
-    // 2/ If no explict route, is there a matching dynamic policy that applies?
-    //    NOTE: new engine product strategy does not make use of dynamic routing, keep it for
-    //          remote-submix and legacy
-    sp<SwAudioOutputDescriptor> desc;
-    if (requestedDevice == nullptr &&
-            mPolicyMixes.getOutputForAttr(*resultAttr, uid, desc) == NO_ERROR) {
-        ALOG_ASSERT(desc != 0, "Invalid desc returned by getOutputForAttr");
-        if (!audio_has_proportional_frames(config->format)) {
-            return BAD_VALUE;
-        }
-        *output = desc->mIoHandle;
-        AudioMix *mix = desc->mPolicyMix;
+    // The primary output is the explicit routing (eg. setPreferredDevice) if specified,
+    //       otherwise, fallback to the dynamic policies, if none match, query the engine.
+    // Secondary outputs are always found by dynamic policies as the engine do not support them
+    sp<SwAudioOutputDescriptor> policyDesc;
+    if (mPolicyMixes.getOutputForAttr(*resultAttr, uid, policyDesc, secondaryDescs) != NO_ERROR) {
+        policyDesc = nullptr; // reset getOutputForAttr in case of failure
+        secondaryDescs->clear();
+    }
+    // Explicit routing is higher priority then any dynamic policy primary output
+    bool usePrimaryOutputFromPolicyMixes = requestedDevice == nullptr && policyDesc != nullptr;
+
+    // FIXME: in case of RENDER policy, the output capabilities should be checked
+    if ((usePrimaryOutputFromPolicyMixes || !secondaryDescs->empty())
+        && !audio_has_proportional_frames(config->format)) {
+        ALOGW("%s: audio loopback only supports proportional frames", __func__);
+        return BAD_VALUE;
+    }
+    if (usePrimaryOutputFromPolicyMixes) {
+        *output = policyDesc->mIoHandle;
+        AudioMix *mix = policyDesc->mPolicyMix;
         sp<DeviceDescriptor> deviceDesc =
                 mAvailableOutputDevices.getDevice(mix->mDeviceType,
                                                   mix->mDeviceAddress,
@@ -1022,7 +1032,8 @@
                                               const audio_config_t *config,
                                               audio_output_flags_t *flags,
                                               audio_port_handle_t *selectedDeviceId,
-                                              audio_port_handle_t *portId)
+                                              audio_port_handle_t *portId,
+                                              std::vector<audio_io_handle_t> *secondaryOutputs)
 {
     // The supplied portId must be AUDIO_PORT_HANDLE_NONE
     if (*portId != AUDIO_PORT_HANDLE_NONE) {
@@ -1031,11 +1042,18 @@
     const audio_port_handle_t requestedPortId = *selectedDeviceId;
     audio_attributes_t resultAttr;
     bool isRequestedDeviceForExclusiveUse = false;
+    std::vector<sp<SwAudioOutputDescriptor>> secondaryOutputDescs;
     status_t status = getOutputForAttrInt(&resultAttr, output, session, attr, stream, uid,
-            config, flags, selectedDeviceId, &isRequestedDeviceForExclusiveUse);
+            config, flags, selectedDeviceId, &isRequestedDeviceForExclusiveUse,
+            &secondaryOutputDescs);
     if (status != NO_ERROR) {
         return status;
     }
+    std::vector<wp<SwAudioOutputDescriptor>> weakSecondaryOutputDescs;
+    for (auto& secondaryDesc : secondaryOutputDescs) {
+        secondaryOutputs->push_back(secondaryDesc->mIoHandle);
+        weakSecondaryOutputDescs.push_back(secondaryDesc);
+    }
 
     audio_config_base_t clientConfig = {.sample_rate = config->sample_rate,
         .format = config->format,
@@ -1046,7 +1064,8 @@
         new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig,
                                   requestedPortId, *stream,
                                   mEngine->getProductStrategyForAttributes(resultAttr),
-                                  *flags, isRequestedDeviceForExclusiveUse);
+                                  *flags, isRequestedDeviceForExclusiveUse,
+                                  std::move(weakSecondaryOutputDescs));
     sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(*output);
     outputDesc->addClient(clientDesc);
 
@@ -1562,13 +1581,15 @@
         policyMix = outputDesc->mPolicyMix;
         audio_devices_t newDeviceType;
         address = policyMix->mDeviceAddress.string();
-        if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
-            newDeviceType = policyMix->mDeviceType;
-        } else {
+        if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_LOOP_BACK) == MIX_ROUTE_FLAG_LOOP_BACK) {
             newDeviceType = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+        } else {
+            newDeviceType = policyMix->mDeviceType;
         }
-        devices.add(mAvailableOutputDevices.getDevice(newDeviceType,
-                                                      String8(address), AUDIO_FORMAT_DEFAULT));
+        sp device = mAvailableOutputDevices.getDevice(newDeviceType, String8(address),
+                                                        AUDIO_FORMAT_DEFAULT);
+        ALOG_ASSERT(device, "%s: no device found t=%u, a=%s", __func__, newDeviceType, address);
+        devices.add(device);
     }
 
     // requiresMuteCheck is false when we can bypass mute strategy.
@@ -2609,18 +2630,24 @@
     // examine each mix's route type
     for (size_t i = 0; i < mixes.size(); i++) {
         AudioMix mix = mixes[i];
-        // we only support MIX_ROUTE_FLAG_LOOP_BACK or MIX_ROUTE_FLAG_RENDER, not the combination
-        if ((mix.mRouteFlags & MIX_ROUTE_FLAG_ALL) == MIX_ROUTE_FLAG_ALL) {
+        // Only capture of playback is allowed in LOOP_BACK & RENDER mode
+        if (is_mix_loopback_render(mix.mRouteFlags) && mix.mMixType != MIX_TYPE_PLAYERS) {
+            ALOGE("Unsupported Policy Mix %zu of %zu: "
+                  "Only capture of playback is allowed in LOOP_BACK & RENDER mode",
+                   i, mixes.size());
             res = INVALID_OPERATION;
             break;
         }
+        // LOOP_BACK and LOOP_BACK | RENDER have the same remote submix backend and are handled
+        // in the same way.
         if ((mix.mRouteFlags & MIX_ROUTE_FLAG_LOOP_BACK) == MIX_ROUTE_FLAG_LOOP_BACK) {
-            ALOGV("registerPolicyMixes() mix %zu of %zu is LOOP_BACK", i, mixes.size());
+            ALOGV("registerPolicyMixes() mix %zu of %zu is LOOP_BACK %d", i, mixes.size(),
+                  mix.mRouteFlags);
             if (rSubmixModule == 0) {
                 rSubmixModule = mHwModules.getModuleFromName(
                         AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX);
                 if (rSubmixModule == 0) {
-                    ALOGE(" Unable to find audio module for submix, aborting mix %zu registration",
+                    ALOGE("Unable to find audio module for submix, aborting mix %zu registration",
                             i);
                     res = INVALID_OPERATION;
                     break;
@@ -2635,7 +2662,7 @@
             }
 
             if (mPolicyMixes.registerMix(address, mix, 0 /*output desc*/) != NO_ERROR) {
-                ALOGE(" Error registering mix %zu for address %s", i, address.string());
+                ALOGE("Error registering mix %zu for address %s", i, address.string());
                 res = INVALID_OPERATION;
                 break;
             }
@@ -2679,6 +2706,8 @@
 
                 if (desc->supportedDevices().contains(device)) {
                     if (mPolicyMixes.registerMix(address, mix, desc) != NO_ERROR) {
+                        ALOGE("Could not register mix RENDER,  dev=0x%X addr=%s", type,
+                              address.string());
                         res = INVALID_OPERATION;
                     } else {
                         foundOutput = true;
@@ -2746,7 +2775,7 @@
             rSubmixModule->removeOutputProfile(address);
             rSubmixModule->removeInputProfile(address);
 
-        } if ((mix.mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
+        } else if ((mix.mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
             if (mPolicyMixes.unregisterMix(mix.mDeviceAddress) != NO_ERROR) {
                 res = INVALID_OPERATION;
                 continue;
@@ -3635,9 +3664,11 @@
         audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE;
         audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
         bool isRequestedDeviceForExclusiveUse = false;
+        std::vector<sp<SwAudioOutputDescriptor>> secondaryOutputs;
         getOutputForAttrInt(&resultAttr, &output, AUDIO_SESSION_NONE,
                 &attributes, &stream, sourceDesc->uid(), &config, &flags,
-                &selectedDeviceId, &isRequestedDeviceForExclusiveUse);
+                &selectedDeviceId, &isRequestedDeviceForExclusiveUse,
+                &secondaryOutputs);
         if (output == AUDIO_IO_HANDLE_NONE) {
             ALOGV("%s no output for device %08x", __FUNCTION__, sinkDevices.types());
             return INVALID_OPERATION;
@@ -4782,6 +4813,7 @@
     // output is suspended before any tracks are moved to it
     checkA2dpSuspend();
     checkOutputForAllStrategies();
+    checkSecondaryOutputs();
     if (onOutputsChecked != nullptr && onOutputsChecked()) checkA2dpSuspend();
     updateDevicesAndOutputs();
     if (mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD) != 0) {
@@ -4870,6 +4902,29 @@
     }
 }
 
+void AudioPolicyManager::checkSecondaryOutputs() {
+    std::set<audio_stream_type_t> streamsToInvalidate;
+    for (size_t i = 0; i < mOutputs.size(); i++) {
+        const sp<SwAudioOutputDescriptor>& outputDescriptor = mOutputs[i];
+        for (const sp<TrackClientDescriptor>& client : outputDescriptor->getClientIterable()) {
+            // FIXME code duplicated from getOutputForAttrInt
+            sp<SwAudioOutputDescriptor> desc;
+            std::vector<sp<SwAudioOutputDescriptor>> secondaryDescs;
+            mPolicyMixes.getOutputForAttr(client->attributes(), client->uid(), desc,
+                                          &secondaryDescs);
+            if (!std::equal(client->getSecondaryOutputs().begin(),
+                            client->getSecondaryOutputs().end(),
+                            secondaryDescs.begin(), secondaryDescs.end())) {
+                streamsToInvalidate.insert(client->stream());
+            }
+        }
+    }
+    for (audio_stream_type_t stream : streamsToInvalidate) {
+        ALOGD("%s Invalidate stream %d due to secondary output change", __func__, stream);
+        mpClientInterface->invalidateStream(stream);
+    }
+}
+
 void AudioPolicyManager::checkA2dpSuspend()
 {
     audio_io_handle_t a2dpOutput = mOutputs.getA2dpOutput();
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 73c3b56..70ad6ac 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -113,15 +113,16 @@
         virtual void setSystemProperty(const char* property, const char* value);
         virtual status_t initCheck();
         virtual audio_io_handle_t getOutput(audio_stream_type_t stream);
-        virtual status_t getOutputForAttr(const audio_attributes_t *attr,
-                                          audio_io_handle_t *output,
-                                          audio_session_t session,
-                                          audio_stream_type_t *stream,
-                                          uid_t uid,
-                                          const audio_config_t *config,
-                                          audio_output_flags_t *flags,
-                                          audio_port_handle_t *selectedDeviceId,
-                                          audio_port_handle_t *portId);
+        status_t getOutputForAttr(const audio_attributes_t *attr,
+                                  audio_io_handle_t *output,
+                                  audio_session_t session,
+                                  audio_stream_type_t *stream,
+                                  uid_t uid,
+                                  const audio_config_t *config,
+                                  audio_output_flags_t *flags,
+                                  audio_port_handle_t *selectedDeviceId,
+                                  audio_port_handle_t *portId,
+                                  std::vector<audio_io_handle_t> *secondaryOutputs) override;
         virtual status_t startOutput(audio_port_handle_t portId);
         virtual status_t stopOutput(audio_port_handle_t portId);
         virtual void releaseOutput(audio_port_handle_t portId);
@@ -431,6 +432,10 @@
          */
         void checkOutputForAllStrategies();
 
+        // Same as checkOutputForStrategy but for secondary outputs. Make sure if a secondary
+        // output condition changes, the track is properly rerouted
+        void checkSecondaryOutputs();
+
         // manages A2DP output suspend/restore according to phone state and BT SCO usage
         void checkA2dpSuspend();
 
@@ -711,7 +716,8 @@
                 const audio_config_t *config,
                 audio_output_flags_t *flags,
                 audio_port_handle_t *selectedDeviceId,
-                bool *isRequestedDeviceForExclusiveUse);
+                bool *isRequestedDeviceForExclusiveUse,
+                std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs);
         // internal method to return the output handle for the given device and format
         audio_io_handle_t getOutputForDevices(
                 const DeviceVector &devices,
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 7768ea3..8ddf824 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -175,7 +175,8 @@
                                               const audio_config_t *config,
                                               audio_output_flags_t flags,
                                               audio_port_handle_t *selectedDeviceId,
-                                              audio_port_handle_t *portId)
+                                              audio_port_handle_t *portId,
+                                              std::vector<audio_io_handle_t> *secondaryOutputs)
 {
     if (mAudioPolicyManager == NULL) {
         return NO_INIT;
@@ -193,7 +194,8 @@
     AutoCallerClear acc;
     status_t result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid,
                                                  config,
-                                                 &flags, selectedDeviceId, portId);
+                                                 &flags, selectedDeviceId, portId,
+                                                 secondaryOutputs);
 
     // FIXME: Introduce a way to check for the the telephony device before opening the output
     if ((result == NO_ERROR) &&
@@ -205,9 +207,10 @@
         flags = originalFlags;
         *selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
         *portId = AUDIO_PORT_HANDLE_NONE;
-        result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid,
-                                                 config,
-                                                 &flags, selectedDeviceId, portId);
+        secondaryOutputs->clear();
+        result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid, config,
+                                                       &flags, selectedDeviceId, portId,
+                                                       secondaryOutputs);
     }
 
     if (result == NO_ERROR) {
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index ee293a7..8cd6e81 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -74,16 +74,17 @@
     virtual status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config);
     virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage);
     virtual audio_io_handle_t getOutput(audio_stream_type_t stream);
-    virtual status_t getOutputForAttr(const audio_attributes_t *attr,
-                                      audio_io_handle_t *output,
-                                      audio_session_t session,
-                                      audio_stream_type_t *stream,
-                                      pid_t pid,
-                                      uid_t uid,
-                                      const audio_config_t *config,
-                                      audio_output_flags_t flags,
-                                      audio_port_handle_t *selectedDeviceId,
-                                      audio_port_handle_t *portId);
+    status_t getOutputForAttr(const audio_attributes_t *attr,
+                              audio_io_handle_t *output,
+                              audio_session_t session,
+                              audio_stream_type_t *stream,
+                              pid_t pid,
+                              uid_t uid,
+                              const audio_config_t *config,
+                              audio_output_flags_t flags,
+                              audio_port_handle_t *selectedDeviceId,
+                              audio_port_handle_t *portId,
+                              std::vector<audio_io_handle_t> *secondaryOutputs) override;
     virtual status_t startOutput(audio_port_handle_t portId);
     virtual status_t stopOutput(audio_port_handle_t portId);
     virtual void releaseOutput(audio_port_handle_t portId);
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index e9f4657..de5670c 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -214,7 +214,7 @@
     *portId = AUDIO_PORT_HANDLE_NONE;
     ASSERT_EQ(OK, mManager->getOutputForAttr(
                     &attr, &output, AUDIO_SESSION_NONE, &stream, 0 /*uid*/, &config, &flags,
-                    selectedDeviceId, portId));
+                    selectedDeviceId, portId, {}));
     ASSERT_NE(AUDIO_PORT_HANDLE_NONE, *portId);
 }