Merge changes from topic "APC"
* changes:
Propagate track status to interception patch track
Add secondary output to audio tracks
Audioflinger: add timeout to PatchTrack
Audiopolicy: Move AudioPolicyMixCollection in helper function
Add MIX_ROUTE_FLAG_LOOP_BACK_RENDER
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/AudioPolicy.h b/media/libaudioclient/include/media/AudioPolicy.h
index 786fb9a..bf8d627 100644
--- a/media/libaudioclient/include/media/AudioPolicy.h
+++ b/media/libaudioclient/include/media/AudioPolicy.h
@@ -49,8 +49,12 @@
#define MIX_STATE_IDLE 0
#define MIX_STATE_MIXING 1
+/** Control to which device some audio is rendered */
#define MIX_ROUTE_FLAG_RENDER 0x1
+/** Loop back some audio instead of rendering it */
#define MIX_ROUTE_FLAG_LOOP_BACK (0x1 << 1)
+/** Loop back some audio while it is rendered */
+#define MIX_ROUTE_FLAG_LOOP_BACK_AND_RENDER (MIX_ROUTE_FLAG_RENDER | MIX_ROUTE_FLAG_LOOP_BACK)
#define MIX_ROUTE_FLAG_ALL (MIX_ROUTE_FLAG_RENDER | MIX_ROUTE_FLAG_LOOP_BACK)
#define MAX_MIXES_PER_POLICY 10
@@ -119,6 +123,11 @@
#define RECORD_CONFIG_EVENT_START 1
#define RECORD_CONFIG_EVENT_STOP 0
+static inline bool is_mix_loopback_render(uint32_t routeFlags) {
+ return (routeFlags & MIX_ROUTE_FLAG_LOOP_BACK_AND_RENDER)
+ == MIX_ROUTE_FLAG_LOOP_BACK_AND_RENDER;
+}
+
}; // namespace android
#endif // ANDROID_AUDIO_POLICY_H
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..7733071 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,59 @@
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());
+ }
+ 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 d8c0da5..1441e15 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -21,8 +21,11 @@
#include "Configuration.h"
#include <atomic>
#include <mutex>
+#include <chrono>
#include <deque>
#include <map>
+#include <numeric>
+#include <optional>
#include <set>
#include <string>
#include <vector>
@@ -526,6 +529,9 @@
class EffectChain;
struct AudioStreamIn;
+ struct TeePatch;
+ using TeePatches = std::vector<TeePatch>;
+
struct stream_type_t {
stream_type_t()
@@ -725,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 bad3ca8..3f62bc3 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,12 @@
sp<os::ExternalVibration> mExternalVibration;
private:
+ void interceptBuffer(const AudioBufferProvider::Buffer& buffer);
+ template <class F>
+ void forEachTeePatchTrack(F f) {
+ for (auto& tp : mTeePatches) { f(tp.patchTrack); }
+ };
+
// 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 +246,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
@@ -318,7 +326,7 @@
}; // end of OutputTrack
// playback track, used by PatchPanel
-class PatchTrack : public Track, public PatchProxyBufferProvider {
+class PatchTrack : public Track, public PatchTrackBase {
public:
PatchTrack(PlaybackThread *playbackThread,
@@ -329,7 +337,8 @@
size_t frameCount,
void *buffer,
size_t bufferSize,
- audio_output_flags_t flags);
+ audio_output_flags_t flags,
+ const Timeout& timeout = {});
virtual ~PatchTrack();
virtual status_t start(AudioSystem::sync_event_t event =
@@ -345,12 +354,7 @@
const struct timespec *timeOut = NULL);
virtual void releaseBuffer(Proxy::Buffer* buffer);
- void setPeerProxy(PatchProxyBufferProvider *proxy) { mPeerProxy = proxy; }
-
private:
void restartIfDisabled();
- sp<ClientProxy> mProxy;
- PatchProxyBufferProvider* mPeerProxy;
- struct timespec mPeerTimeout;
}; // end of PatchTrack
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 32af7d5..ab4af33 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -113,7 +113,7 @@
};
// playback track, used by PatchPanel
-class PatchRecord : virtual public RecordTrack, public PatchProxyBufferProvider {
+class PatchRecord : public RecordTrack, public PatchTrackBase {
public:
PatchRecord(RecordThread *recordThread,
@@ -123,7 +123,8 @@
size_t frameCount,
void *buffer,
size_t bufferSize,
- audio_input_flags_t flags);
+ audio_input_flags_t flags,
+ const Timeout& timeout = {});
virtual ~PatchRecord();
// AudioBufferProvider interface
@@ -134,11 +135,4 @@
virtual status_t obtainBuffer(Proxy::Buffer *buffer,
const struct timespec *timeOut = NULL);
virtual void releaseBuffer(Proxy::Buffer *buffer);
-
- void setPeerProxy(PatchProxyBufferProvider *proxy) { mPeerProxy = proxy; }
-
-private:
- sp<ClientProxy> mProxy;
- PatchProxyBufferProvider* mPeerProxy;
- struct timespec mPeerTimeout;
}; // end of PatchRecord
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/TrackBase.h b/services/audioflinger/TrackBase.h
index c94639b..0ba0ab4 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -329,3 +329,19 @@
const struct timespec *requested = NULL) = 0;
virtual void releaseBuffer(Proxy::Buffer* buffer) = 0;
};
+
+class PatchTrackBase : public PatchProxyBufferProvider
+{
+public:
+ using Timeout = std::optional<std::chrono::nanoseconds>;
+ PatchTrackBase(sp<ClientProxy> proxy, const ThreadBase& thread,
+ const Timeout& timeout);
+ void setPeerTimeout(std::chrono::nanoseconds timeout);
+ void setPeerProxy(PatchProxyBufferProvider *proxy) { mPeerProxy = proxy; }
+
+protected:
+ const sp<ClientProxy> mProxy;
+ PatchProxyBufferProvider* mPeerProxy = nullptr;
+ struct timespec mPeerTimeout{};
+
+};
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index e4af656..57dd568 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)
{
@@ -277,6 +277,27 @@
return NO_ERROR;
}
+AudioFlinger::ThreadBase::PatchTrackBase::PatchTrackBase(sp<ClientProxy> proxy,
+ const ThreadBase& thread,
+ const Timeout& timeout)
+ : mProxy(proxy)
+{
+ if (timeout) {
+ setPeerTimeout(*timeout);
+ } else {
+ // Double buffer mixer
+ uint64_t mixBufferNs = ((uint64_t)2 * thread.frameCount() * 1000000000) /
+ thread.sampleRate();
+ setPeerTimeout(std::chrono::nanoseconds{mixBufferNs});
+ }
+}
+
+void AudioFlinger::ThreadBase::PatchTrackBase::setPeerTimeout(std::chrono::nanoseconds timeout) {
+ mPeerTimeout.tv_sec = timeout.count() / std::nano::den;
+ mPeerTimeout.tv_nsec = timeout.count() % std::nano::den;
+}
+
+
// ----------------------------------------------------------------------------
// Playback
// ----------------------------------------------------------------------------
@@ -504,6 +525,7 @@
AudioSystem::releaseOutput(mPortId);
}
}
+ forEachTeePatchTrack([](auto patchTrack) { patchTrack->destroy(); });
}
void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result)
@@ -649,8 +671,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;
@@ -665,10 +686,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
@@ -816,6 +866,9 @@
} else {
status = BAD_VALUE;
}
+ if (status == NO_ERROR) {
+ forEachTeePatchTrack([](auto patchTrack) { patchTrack->start(); });
+ }
return status;
}
@@ -849,6 +902,7 @@
__func__, mId, (int)mThreadIoHandle);
}
}
+ forEachTeePatchTrack([](auto patchTrack) { patchTrack->stop(); });
}
void AudioFlinger::PlaybackThread::Track::pause()
@@ -881,6 +935,8 @@
break;
}
}
+ // Pausing the TeePatch to avoid a glitch on underrun, at the cost of buffered audio loss.
+ forEachTeePatchTrack([](auto patchTrack) { patchTrack->pause(); });
}
void AudioFlinger::PlaybackThread::Track::flush()
@@ -942,6 +998,8 @@
// because the hardware buffer could hold a large amount of audio
playbackThread->broadcast_l();
}
+ // Flush the Tee to avoid on resume playing old data and glitching on the transition to new data
+ forEachTeePatchTrack([](auto patchTrack) { patchTrack->flush(); });
}
// must be called with thread lock held
@@ -1060,6 +1118,11 @@
};
}
+void AudioFlinger::PlaybackThread::Track::setTeePatches(TeePatches teePatches) {
+ forEachTeePatchTrack([](auto patchTrack) { patchTrack->destroy(); });
+ mTeePatches = std::move(teePatches);
+}
+
status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& timestamp)
{
if (!isOffloaded() && !isDirect()) {
@@ -1615,19 +1678,16 @@
size_t frameCount,
void *buffer,
size_t bufferSize,
- audio_output_flags_t flags)
+ audio_output_flags_t flags,
+ const Timeout& timeout)
: Track(playbackThread, NULL, streamType,
audio_attributes_t{} /* currently unused for patch track */,
sampleRate, format, channelMask, frameCount,
buffer, bufferSize, nullptr /* sharedBuffer */,
AUDIO_SESSION_NONE, AID_AUDIOSERVER, flags, TYPE_PATCH),
- mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, true, true))
+ PatchTrackBase(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, true, true),
+ *playbackThread, timeout)
{
- uint64_t mixBufferNs = ((uint64_t)2 * playbackThread->frameCount() * 1000000000) /
- playbackThread->sampleRate();
- mPeerTimeout.tv_sec = mixBufferNs / 1000000000;
- mPeerTimeout.tv_nsec = (int) (mixBufferNs % 1000000000);
-
ALOGV("%s(%d): sampleRate %d mPeerTimeout %d.%03d sec",
__func__, mId, sampleRate,
(int)mPeerTimeout.tv_sec,
@@ -2088,19 +2148,16 @@
size_t frameCount,
void *buffer,
size_t bufferSize,
- audio_input_flags_t flags)
+ audio_input_flags_t flags,
+ const Timeout& timeout)
: RecordTrack(recordThread, NULL,
audio_attributes_t{} /* currently unused for patch track */,
sampleRate, format, channelMask, frameCount,
buffer, bufferSize, AUDIO_SESSION_NONE, AID_AUDIOSERVER,
flags, TYPE_PATCH),
- mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, false, true))
+ PatchTrackBase(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, false, true),
+ *recordThread, timeout)
{
- uint64_t mixBufferNs = ((uint64_t)2 * recordThread->frameCount() * 1000000000) /
- recordThread->sampleRate();
- mPeerTimeout.tv_sec = mixBufferNs / 1000000000;
- mPeerTimeout.tv_nsec = (int) (mixBufferNs % 1000000000);
-
ALOGV("%s(%d): sampleRate %d mPeerTimeout %d.%03d sec",
__func__, mId, sampleRate,
(int)mPeerTimeout.tv_sec,
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 7296c95..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,
@@ -99,6 +98,11 @@
status_t getDevicesForUid(uid_t uid, Vector<AudioDeviceTypeAddr>& devices) const;
void dump(String8 *dst) const;
+
+private:
+ enum class MixMatchStatus { MATCH, NO_MATCH, INVALID_MIX };
+ MixMatchStatus mixMatch(const AudioMix* mix, size_t mixIndex,
+ audio_attributes_t attributes, uid_t uid);
};
} // namespace android
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 2489e76..6b6d9d2 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -156,128 +156,163 @@
}
}
-status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attributes, uid_t uid,
- sp<SwAudioOutputDescriptor> &desc)
+status_t AudioPolicyMixCollection::getOutputForAttr(
+ 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("%s: Skiping %zu: Mix has no output", __func__, i);
+ continue;
+ }
+
AudioMix *mix = policyMix->getMix();
+ const bool primaryOutputMix = !is_mix_loopback_render(mix->mRouteFlags);
- if (mix->mMixType == MIX_TYPE_PLAYERS) {
- // TODO if adding more player rules (currently only 2), make rule handling "generic"
- // as there is no difference in the treatment of usage- or uid-based rules
- bool hasUsageMatchRules = false;
- bool hasUsageExcludeRules = false;
- bool usageMatchFound = false;
- bool usageExclusionFound = false;
+ if (primaryOutputMix && primaryDesc != 0) {
+ ALOGV("%s: Skiping %zu: Primary output already found", __func__, i);
+ continue; // Primary output already found
+ }
- bool hasUidMatchRules = false;
- bool hasUidExcludeRules = false;
- bool uidMatchFound = false;
- bool uidExclusionFound = false;
+ switch (mixMatch(mix, i, attributes, uid)) {
+ 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:;
+ }
- bool hasAddrMatch = false;
-
- // iterate over all mix criteria to list what rules this mix contains
- for (size_t j = 0; j < mix->mCriteria.size(); j++) {
- ALOGV(" getOutputForAttr: mix %zu: inspecting mix criteria %zu of %zu",
- i, j, mix->mCriteria.size());
-
- // if there is an address match, prioritize that match
- if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
- strncmp(attributes.tags + strlen("addr="),
- mix->mDeviceAddress.string(),
- AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
- hasAddrMatch = true;
- break;
- }
-
- switch (mix->mCriteria[j].mRule) {
- case RULE_MATCH_ATTRIBUTE_USAGE:
- ALOGV("\tmix has RULE_MATCH_ATTRIBUTE_USAGE for usage %d",
- mix->mCriteria[j].mValue.mUsage);
- hasUsageMatchRules = true;
- if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
- // found one match against all allowed usages
- usageMatchFound = true;
- }
- break;
- case RULE_EXCLUDE_ATTRIBUTE_USAGE:
- ALOGV("\tmix has RULE_EXCLUDE_ATTRIBUTE_USAGE for usage %d",
- mix->mCriteria[j].mValue.mUsage);
- hasUsageExcludeRules = true;
- if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
- // found this usage is to be excluded
- usageExclusionFound = true;
- }
- break;
- case RULE_MATCH_UID:
- ALOGV("\tmix has RULE_MATCH_UID for uid %d", mix->mCriteria[j].mValue.mUid);
- hasUidMatchRules = true;
- if (mix->mCriteria[j].mValue.mUid == uid) {
- // found one UID match against all allowed UIDs
- uidMatchFound = true;
- }
- break;
- case RULE_EXCLUDE_UID:
- ALOGV("\tmix has RULE_EXCLUDE_UID for uid %d", mix->mCriteria[j].mValue.mUid);
- hasUidExcludeRules = true;
- if (mix->mCriteria[j].mValue.mUid == uid) {
- // found this UID is to be excluded
- uidExclusionFound = true;
- }
- break;
- default:
- break;
- }
-
- // consistency checks: for each "dimension" of rules (usage, uid...), we can
- // only have MATCH rules, or EXCLUDE rules in each dimension, not a combination
- if (hasUsageMatchRules && hasUsageExcludeRules) {
- ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_ATTRIBUTE_USAGE"
- " and RULE_EXCLUDE_ATTRIBUTE_USAGE in mix %zu", i);
- return BAD_VALUE;
- }
- if (hasUidMatchRules && hasUidExcludeRules) {
- ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_UID"
- " and RULE_EXCLUDE_UID in mix %zu", i);
- return BAD_VALUE;
- }
-
- if ((hasUsageExcludeRules && usageExclusionFound)
- || (hasUidExcludeRules && uidExclusionFound)) {
- break; // stop iterating on criteria because an exclusion was found (will fail)
- }
-
- }//iterate on mix criteria
-
- // determine if exiting on success (or implicit failure as desc is 0)
- if (hasAddrMatch ||
- !((hasUsageExcludeRules && usageExclusionFound) ||
- (hasUsageMatchRules && !usageMatchFound) ||
- (hasUidExcludeRules && uidExclusionFound) ||
- (hasUidMatchRules && !uidMatchFound))) {
- ALOGV("\tgetOutputForAttr will use mix %zu", i);
- desc = policyMix->getOutput();
+ 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 (primaryDesc == nullptr && secondaryDescs->empty()) ? BAD_VALUE : NO_ERROR;
+}
- } else if (mix->mMixType == MIX_TYPE_RECORDERS) {
- if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE &&
- strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
+AudioPolicyMixCollection::MixMatchStatus AudioPolicyMixCollection::mixMatch(
+ const AudioMix* mix, size_t mixIndex, audio_attributes_t attributes, uid_t uid) {
+
+ if (mix->mMixType == MIX_TYPE_PLAYERS) {
+ // TODO if adding more player rules (currently only 2), make rule handling "generic"
+ // as there is no difference in the treatment of usage- or uid-based rules
+ bool hasUsageMatchRules = false;
+ bool hasUsageExcludeRules = false;
+ bool usageMatchFound = false;
+ bool usageExclusionFound = false;
+
+ bool hasUidMatchRules = false;
+ bool hasUidExcludeRules = false;
+ bool uidMatchFound = false;
+ bool uidExclusionFound = false;
+
+ bool hasAddrMatch = false;
+
+ // iterate over all mix criteria to list what rules this mix contains
+ for (size_t j = 0; j < mix->mCriteria.size(); j++) {
+ ALOGV(" getOutputForAttr: mix %zu: inspecting mix criteria %zu of %zu",
+ mixIndex, j, mix->mCriteria.size());
+
+ // if there is an address match, prioritize that match
+ if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
strncmp(attributes.tags + strlen("addr="),
mix->mDeviceAddress.string(),
AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
- desc = policyMix->getOutput();
+ hasAddrMatch = true;
+ break;
}
+
+ switch (mix->mCriteria[j].mRule) {
+ case RULE_MATCH_ATTRIBUTE_USAGE:
+ ALOGV("\tmix has RULE_MATCH_ATTRIBUTE_USAGE for usage %d",
+ mix->mCriteria[j].mValue.mUsage);
+ hasUsageMatchRules = true;
+ if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
+ // found one match against all allowed usages
+ usageMatchFound = true;
+ }
+ break;
+ case RULE_EXCLUDE_ATTRIBUTE_USAGE:
+ ALOGV("\tmix has RULE_EXCLUDE_ATTRIBUTE_USAGE for usage %d",
+ mix->mCriteria[j].mValue.mUsage);
+ hasUsageExcludeRules = true;
+ if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
+ // found this usage is to be excluded
+ usageExclusionFound = true;
+ }
+ break;
+ case RULE_MATCH_UID:
+ ALOGV("\tmix has RULE_MATCH_UID for uid %d", mix->mCriteria[j].mValue.mUid);
+ hasUidMatchRules = true;
+ if (mix->mCriteria[j].mValue.mUid == uid) {
+ // found one UID match against all allowed UIDs
+ uidMatchFound = true;
+ }
+ break;
+ case RULE_EXCLUDE_UID:
+ ALOGV("\tmix has RULE_EXCLUDE_UID for uid %d", mix->mCriteria[j].mValue.mUid);
+ hasUidExcludeRules = true;
+ if (mix->mCriteria[j].mValue.mUid == uid) {
+ // found this UID is to be excluded
+ uidExclusionFound = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ // consistency checks: for each "dimension" of rules (usage, uid...), we can
+ // only have MATCH rules, or EXCLUDE rules in each dimension, not a combination
+ if (hasUsageMatchRules && hasUsageExcludeRules) {
+ ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_ATTRIBUTE_USAGE"
+ " and RULE_EXCLUDE_ATTRIBUTE_USAGE in mix %zu", mixIndex);
+ return MixMatchStatus::INVALID_MIX;
+ }
+ if (hasUidMatchRules && hasUidExcludeRules) {
+ ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_UID"
+ " and RULE_EXCLUDE_UID in mix %zu", mixIndex);
+ return MixMatchStatus::INVALID_MIX;
+ }
+
+ if ((hasUsageExcludeRules && usageExclusionFound)
+ || (hasUidExcludeRules && uidExclusionFound)) {
+ break; // stop iterating on criteria because an exclusion was found (will fail)
+ }
+
+ }//iterate on mix criteria
+
+ // determine if exiting on success (or implicit failure as desc is 0)
+ if (hasAddrMatch ||
+ !((hasUsageExcludeRules && usageExclusionFound) ||
+ (hasUsageMatchRules && !usageMatchFound) ||
+ (hasUidExcludeRules && uidExclusionFound) ||
+ (hasUidMatchRules && !uidMatchFound))) {
+ ALOGV("\tgetOutputForAttr will use mix %zu", mixIndex);
+ return MixMatchStatus::MATCH;
}
- if (desc != 0) {
- desc->mPolicyMix = mix;
- return NO_ERROR;
+
+ } else if (mix->mMixType == MIX_TYPE_RECORDERS) {
+ if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE &&
+ strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
+ strncmp(attributes.tags + strlen("addr="),
+ mix->mDeviceAddress.string(),
+ AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
+ return MixMatchStatus::MATCH;
}
}
- return BAD_VALUE;
+ return MixMatchStatus::NO_MATCH;
}
sp<DeviceDescriptor> AudioPolicyMixCollection::getDeviceAndMixForOutput(
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/common/managerdefinitions/src/TypeConverter.cpp b/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp
index 6f48eae..7c76d8a 100644
--- a/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp
@@ -45,6 +45,7 @@
const RouteFlagTypeConverter::Table RouteFlagTypeConverter::mTable[] = {
MAKE_STRING_FROM_ENUM(MIX_ROUTE_FLAG_RENDER),
MAKE_STRING_FROM_ENUM(MIX_ROUTE_FLAG_LOOP_BACK),
+ MAKE_STRING_FROM_ENUM(MIX_ROUTE_FLAG_LOOP_BACK_AND_RENDER),
MAKE_STRING_FROM_ENUM(MIX_ROUTE_FLAG_ALL),
TERMINATOR
};
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 0352f3d..ccec93f 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -38,6 +38,7 @@
#include <inttypes.h>
#include <math.h>
+#include <set>
#include <unordered_set>
#include <vector>
@@ -909,16 +910,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;
@@ -937,19 +940,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,
@@ -1024,7 +1034,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) {
@@ -1033,11 +1044,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,
@@ -1048,7 +1066,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);
@@ -1564,13 +1583,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.
@@ -2611,18 +2632,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;
@@ -2637,7 +2664,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;
}
@@ -2681,6 +2708,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;
@@ -2748,7 +2777,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;
@@ -3637,9 +3666,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;
@@ -4788,6 +4819,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) {
@@ -4876,6 +4908,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);
}