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); }