From 153f92d57b6a1d2fdc7fa601bbc86db31646a8f9 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Tue, 18 Dec 2018 18:33:28 -0800 Subject: [PATCH] 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 --- media/libaudioclient/AudioSystem.cpp | 5 +- media/libaudioclient/IAudioPolicyService.cpp | 36 +++-- .../include/media/AudioSystem.h | 3 +- .../include/media/IAudioPolicyService.h | 3 +- services/audioflinger/AudioFlinger.cpp | 63 ++++++++- services/audioflinger/AudioFlinger.h | 9 ++ services/audioflinger/PlaybackTracks.h | 14 +- services/audioflinger/Threads.cpp | 6 +- services/audioflinger/Tracks.cpp | 40 +++++- services/audiopolicy/AudioPolicyInterface.h | 3 +- .../include/AudioPolicyMix.h | 9 +- .../include/ClientDescriptor.h | 10 +- .../managerdefinitions/src/AudioPolicyMix.cpp | 37 +++-- .../src/ClientDescriptor.cpp | 3 +- .../managerdefault/AudioPolicyManager.cpp | 131 +++++++++++++----- .../managerdefault/AudioPolicyManager.h | 26 ++-- .../service/AudioPolicyInterfaceImpl.cpp | 13 +- .../audiopolicy/service/AudioPolicyService.h | 21 +-- .../tests/audiopolicymanager_tests.cpp | 2 +- 19 files changed, 323 insertions(+), 111 deletions(-) diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index b83a441e32..41a7ff0624 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -872,13 +872,14 @@ status_t AudioSystem::getOutputForAttr(const audio_attributes_t *attr, 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 *secondaryOutputs) { const sp& 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 0db56e8609..d9f6e3678b 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -189,16 +189,17 @@ public: return static_cast (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 *secondaryOutputs) override { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); @@ -224,6 +225,10 @@ public: 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 @@ public: } *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 @@ status_t BnAudioPolicyService::onTransact( 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 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 87a9919126..60608943cd 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -231,7 +231,8 @@ public: 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 *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 b2cda32561..e89a55d173 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -66,7 +66,8 @@ public: 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 *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 bc990993d2..befabc5d82 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -292,13 +292,16 @@ status_t AudioFlinger::openMmapStream(MmapStreamInterface::stream_direction_t di fullConfig.sample_rate = config->sample_rate; fullConfig.channel_mask = config->channel_mask; fullConfig.format = config->format; + std::vector 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 @@ sp AudioFlinger::createTrack(const CreateTrackInput& input, status_t lStatus; audio_stream_type_t streamType; audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE; + std::vector secondaryOutputs; bool updatePid = (input.clientInfo.clientPid == -1); const uid_t callingUid = IPCThreadState::self()->getCallingUid(); @@ -712,7 +716,7 @@ sp AudioFlinger::createTrack(const CreateTrackInput& input, 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 @@ sp AudioFlinger::createTrack(const CreateTrackInput& input, 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 f16a196ac6..1441e15f19 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -528,6 +529,9 @@ private: class EffectChain; struct AudioStreamIn; + struct TeePatch; + using TeePatches = std::vector; + struct stream_type_t { stream_type_t() @@ -727,6 +731,11 @@ using effect_buffer_t = int16_t; audioHwDev(dev), stream(in), flags(flags) {} }; + struct TeePatch { + sp patchRecord; + sp 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 33048fc366..468352545a 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -43,9 +43,8 @@ public: 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 @@ public: } sp getExternalVibration() const { return mExternalVibration; } + void setTeePatches(TeePatches teePatches); + protected: // for numerous friend class PlaybackThread; @@ -139,8 +140,8 @@ protected: 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 @@ protected: sp 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 @@ private: 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 5a70864f06..dd1eabf9a1 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -8464,6 +8464,7 @@ status_t AudioFlinger::MmapThread::start(const AudioClient& client, 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 secondaryOutputs; ret = AudioSystem::getOutputForAttr(&mAttr, &io, mSessionId, &stream, @@ -8472,7 +8473,10 @@ status_t AudioFlinger::MmapThread::start(const AudioClient& client, &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 863dc9e006..37c3a2d8b8 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -99,7 +99,7 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( 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 @@ uint32_t AudioFlinger::PlaybackThread::Track::sampleRate() const { } // 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 @@ status_t AudioFlinger::PlaybackThread::Track::getNextBuffer( } 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::copyMetadataTo(MetadataInserter& backI }; } +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 d7030f9b11..bb5441d2c8 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -110,7 +110,8 @@ public: 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 *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 e5c7d48bab..1abce6f93d 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h @@ -68,13 +68,12 @@ public: * 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 &desc); + sp &primaryDesc, + std::vector> *secondaryDescs); sp 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 4c069e40dc..2e44a60d9b 100644 --- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h @@ -86,10 +86,12 @@ public: 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> 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 @@ public: 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>& 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> mSecondaryOutputs; }; class RecordClientDescriptor: public ClientDescriptor diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index 32c90b1114..6b6d9d274c 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -157,30 +157,49 @@ void AudioPolicyMixCollection::closeOutput(sp &desc) } status_t AudioPolicyMixCollection::getOutputForAttr( - audio_attributes_t attributes, uid_t uid, sp &desc) + audio_attributes_t attributes, uid_t uid, sp &primaryDesc, + std::vector> *secondaryDescs) { ALOGV("getOutputForAttr() querying %zu mixes:", size()); - desc = 0; + primaryDesc = 0; for (size_t i = 0; i < size(); i++) { sp policyMix = valueAt(i); sp 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 a6f6c3b5c8..633c40e343 100644 --- a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp @@ -85,7 +85,8 @@ SourceClientDescriptor::SourceClientDescriptor(audio_port_handle_t portId, uid_t 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 0eee3f2c31..02b85b43a9 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -36,6 +36,7 @@ #include #include +#include #include #include @@ -907,16 +908,18 @@ status_t AudioPolicyManager::getAudioAttributes(audio_attributes_t *dstAttr, 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> *secondaryDescs) { DeviceVector outputDevices; const audio_port_handle_t requestedPortId = *selectedDeviceId; @@ -935,19 +938,26 @@ status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr, 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 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 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 deviceDesc = mAvailableOutputDevices.getDevice(mix->mDeviceType, mix->mDeviceAddress, @@ -1022,7 +1032,8 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, 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 *secondaryOutputs) { // The supplied portId must be AUDIO_PORT_HANDLE_NONE if (*portId != AUDIO_PORT_HANDLE_NONE) { @@ -1031,11 +1042,18 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, const audio_port_handle_t requestedPortId = *selectedDeviceId; audio_attributes_t resultAttr; bool isRequestedDeviceForExclusiveUse = false; + std::vector> 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> 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 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig, requestedPortId, *stream, mEngine->getProductStrategyForAttributes(resultAttr), - *flags, isRequestedDeviceForExclusiveUse); + *flags, isRequestedDeviceForExclusiveUse, + std::move(weakSecondaryOutputDescs)); sp outputDesc = mOutputs.valueFor(*output); outputDesc->addClient(clientDesc); @@ -1562,13 +1581,15 @@ status_t AudioPolicyManager::startSource(const sp& outp 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 @@ status_t AudioPolicyManager::registerPolicyMixes(const Vector& mixes) // 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 @@ status_t AudioPolicyManager::registerPolicyMixes(const Vector& mixes) } 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 @@ status_t AudioPolicyManager::registerPolicyMixes(const Vector& mixes) 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 @@ status_t AudioPolicyManager::unregisterPolicyMixes(Vector mixes) 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 @@ status_t AudioPolicyManager::connectAudioSource(const sp audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE; audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE; bool isRequestedDeviceForExclusiveUse = false; + std::vector> 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 @@ void AudioPolicyManager::checkForDeviceAndOutputChanges(std::function on // 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::checkOutputForAllStrategies() } } +void AudioPolicyManager::checkSecondaryOutputs() { + std::set streamsToInvalidate; + for (size_t i = 0; i < mOutputs.size(); i++) { + const sp& outputDescriptor = mOutputs[i]; + for (const sp& client : outputDescriptor->getClientIterable()) { + // FIXME code duplicated from getOutputForAttrInt + sp desc; + std::vector> 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 73c3b5683e..70ad6ac7aa 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -113,15 +113,16 @@ public: 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 *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 @@ protected: */ 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 @@ private: const audio_config_t *config, audio_output_flags_t *flags, audio_port_handle_t *selectedDeviceId, - bool *isRequestedDeviceForExclusiveUse); + bool *isRequestedDeviceForExclusiveUse, + std::vector> *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 7768ea347c..8ddf82435c 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -175,7 +175,8 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr, 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 *secondaryOutputs) { if (mAudioPolicyManager == NULL) { return NO_INIT; @@ -193,7 +194,8 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr, 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 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr, 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 ee293a7496..8cd6e8157e 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -74,16 +74,17 @@ public: 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 *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 e9f465795f..de5670c7c6 100644 --- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp +++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp @@ -214,7 +214,7 @@ void AudioPolicyManagerTest::getOutputForAttr( *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); }