PatchPanel: Cleanup and refactoring
* Unified software patch endpoints management code.
* Noticed that Patch::clearConnections is only called before
getting rid of Patch instance, so there is no need
to explicitly clear references to endpoints' Track and Thread.
* Fixed out-of-memory handling when creating tracks for
a software patch.
* Factored out finding HAL Device by module handle.
Test: verify transitions to/from BT while playing media and making calls
Change-Id: If6459c477054d6dff60dfa13f2d99ee2d6e887ad
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index f28132d..e3e53b7 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -2293,7 +2293,7 @@
delete out;
}
-void AudioFlinger::closeOutputInternal_l(const sp<PlaybackThread>& thread)
+void AudioFlinger::closeThreadInternal_l(const sp<PlaybackThread>& thread)
{
mPlaybackThreads.removeItem(thread->mId);
thread->exit();
@@ -2586,7 +2586,7 @@
delete in;
}
-void AudioFlinger::closeInputInternal_l(const sp<RecordThread>& thread)
+void AudioFlinger::closeThreadInternal_l(const sp<RecordThread>& thread)
{
mRecordThreads.removeItem(thread->mId);
closeInputFinish(thread);
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 692a904..7cfe542 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -791,9 +791,9 @@
// for use from destructor
status_t closeOutput_nonvirtual(audio_io_handle_t output);
- void closeOutputInternal_l(const sp<PlaybackThread>& thread);
+ void closeThreadInternal_l(const sp<PlaybackThread>& thread);
status_t closeInput_nonvirtual(audio_io_handle_t input);
- void closeInputInternal_l(const sp<RecordThread>& thread);
+ void closeThreadInternal_l(const sp<RecordThread>& thread);
void setAudioHwSyncForSession_l(PlaybackThread *thread, audio_session_t sessionId);
status_t checkStreamType(audio_stream_type_t stream) const;
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index 3ae198b..eaf1120 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -125,18 +125,16 @@
if (iter != mPatches.end()) {
ALOGV("%s() removing patch handle %d", __func__, *handle);
Patch &removedPatch = iter->second;
- halHandle = removedPatch.mHalHandle;
// free resources owned by the removed patch if applicable
// 1) if a software patch is present, release the playback and capture threads and
// tracks created. This will also release the corresponding audio HAL patches
- if ((removedPatch.mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE) ||
- (removedPatch.mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE)) {
+ if (removedPatch.isSoftware()) {
removedPatch.clearConnections(this);
}
// 2) if the new patch and old patch source or sink are devices from different
// hw modules, clear the audio HAL patches now because they will not be updated
// by call to create_audio_patch() below which will happen on a different HW module
- if (halHandle != AUDIO_PATCH_HANDLE_NONE) {
+ if (removedPatch.mHalHandle != AUDIO_PATCH_HANDLE_NONE) {
audio_module_handle_t hwModule = AUDIO_MODULE_HANDLE_NONE;
const struct audio_patch &oldPatch = removedPatch.mAudioPatch;
if (oldPatch.sources[0].type == AUDIO_PORT_TYPE_DEVICE &&
@@ -153,16 +151,12 @@
// these special patches are only created by the policy manager but just
// in case, systematically clear the HAL patch.
// Note that removedPatch.mAudioPatch.num_sinks cannot be 0 here because
- // halHandle would be AUDIO_PATCH_HANDLE_NONE in this case.
+ // removedPatch.mHalHandle would be AUDIO_PATCH_HANDLE_NONE in this case.
hwModule = oldPatch.sinks[0].ext.device.hw_module;
}
- if (hwModule != AUDIO_MODULE_HANDLE_NONE) {
- ssize_t index = mAudioFlinger.mAudioHwDevs.indexOfKey(hwModule);
- if (index >= 0) {
- sp<DeviceHalInterface> hwDevice =
- mAudioFlinger.mAudioHwDevs.valueAt(index)->hwDevice();
- hwDevice->releaseAudioPatch(halHandle);
- }
+ sp<DeviceHalInterface> hwDevice = findHwDeviceByModule(hwModule);
+ if (hwDevice != 0) {
+ hwDevice->releaseAudioPatch(removedPatch.mHalHandle);
}
}
mPatches.erase(iter);
@@ -217,12 +211,14 @@
sp<ThreadBase> thread =
mAudioFlinger.checkPlaybackThread_l(patch->sources[1].ext.mix.handle);
- newPatch.mPlaybackThread = (MixerThread *)thread.get();
if (thread == 0) {
ALOGW("%s() cannot get playback thread", __func__);
status = INVALID_OPERATION;
goto exit;
}
+ // existing playback thread is reused, so it is not closed when patch is cleared
+ newPatch.mPlayback.setThread(
+ reinterpret_cast<PlaybackThread*>(thread.get()), false /*closeThread*/);
} else {
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
audio_devices_t device = patch->sinks[0].ext.device.type;
@@ -235,13 +231,12 @@
device,
address,
AUDIO_OUTPUT_FLAG_NONE);
- newPatch.mPlaybackThread = (PlaybackThread *)thread.get();
- ALOGV("mAudioFlinger.openOutput_l() returned %p",
- newPatch.mPlaybackThread.get());
- if (newPatch.mPlaybackThread == 0) {
+ ALOGV("mAudioFlinger.openOutput_l() returned %p", thread.get());
+ if (thread == 0) {
status = NO_MEMORY;
goto exit;
}
+ newPatch.mPlayback.setThread(reinterpret_cast<PlaybackThread*>(thread.get()));
}
audio_devices_t device = patch->sources[0].ext.device.type;
String8 address = String8(patch->sources[0].ext.device.address);
@@ -251,18 +246,18 @@
if (patch->sources[0].config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) {
config.sample_rate = patch->sources[0].sample_rate;
} else {
- config.sample_rate = newPatch.mPlaybackThread->sampleRate();
+ config.sample_rate = newPatch.mPlayback.thread()->sampleRate();
}
if (patch->sources[0].config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
config.channel_mask = patch->sources[0].channel_mask;
} else {
- config.channel_mask =
- audio_channel_in_mask_from_count(newPatch.mPlaybackThread->channelCount());
+ config.channel_mask = audio_channel_in_mask_from_count(
+ newPatch.mPlayback.thread()->channelCount());
}
if (patch->sources[0].config_mask & AUDIO_PORT_CONFIG_FORMAT) {
config.format = patch->sources[0].format;
} else {
- config.format = newPatch.mPlaybackThread->format();
+ config.format = newPatch.mPlayback.thread()->format();
}
audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
sp<ThreadBase> thread = mAudioFlinger.openInput_l(srcModule,
@@ -272,13 +267,13 @@
address,
AUDIO_SOURCE_MIC,
AUDIO_INPUT_FLAG_NONE);
- newPatch.mRecordThread = (RecordThread *)thread.get();
ALOGV("mAudioFlinger.openInput_l() returned %p inChannelMask %08x",
- newPatch.mRecordThread.get(), config.channel_mask);
- if (newPatch.mRecordThread == 0) {
+ thread.get(), config.channel_mask);
+ if (thread == 0) {
status = NO_MEMORY;
goto exit;
}
+ newPatch.mRecord.setThread(reinterpret_cast<RecordThread*>(thread.get()));
status = newPatch.createConnections(this);
if (status != NO_ERROR) {
goto exit;
@@ -369,6 +364,12 @@
return status;
}
+AudioFlinger::PatchPanel::Patch::~Patch()
+{
+ ALOGE_IF(isSoftware(), "Software patch connections leaked %d %d",
+ mRecord.handle(), mPlayback.handle());
+}
+
status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel)
{
// create patch from source device to record thread input
@@ -377,32 +378,32 @@
subPatch.sources[0] = mAudioPatch.sources[0];
subPatch.num_sinks = 1;
- mRecordThread->getAudioPortConfig(&subPatch.sinks[0]);
+ mRecord.thread()->getAudioPortConfig(&subPatch.sinks[0]);
subPatch.sinks[0].ext.mix.usecase.source = AUDIO_SOURCE_MIC;
- status_t status = panel->createAudioPatch(&subPatch, &mRecordPatchHandle);
+ status_t status = panel->createAudioPatch(&subPatch, mRecord.handlePtr());
if (status != NO_ERROR) {
- mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE;
+ *mRecord.handlePtr() = AUDIO_PATCH_HANDLE_NONE;
return status;
}
// create patch from playback thread output to sink device
if (mAudioPatch.num_sinks != 0) {
- mPlaybackThread->getAudioPortConfig(&subPatch.sources[0]);
+ mPlayback.thread()->getAudioPortConfig(&subPatch.sources[0]);
subPatch.sinks[0] = mAudioPatch.sinks[0];
- status = panel->createAudioPatch(&subPatch, &mPlaybackPatchHandle);
+ status = panel->createAudioPatch(&subPatch, mPlayback.handlePtr());
if (status != NO_ERROR) {
- mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE;
+ *mPlayback.handlePtr() = AUDIO_PATCH_HANDLE_NONE;
return status;
}
} else {
- mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE;
+ *mPlayback.handlePtr() = AUDIO_PATCH_HANDLE_NONE;
}
// use a pseudo LCM between input and output framecount
- size_t playbackFrameCount = mPlaybackThread->frameCount();
+ size_t playbackFrameCount = mPlayback.thread()->frameCount();
int playbackShift = __builtin_ctz(playbackFrameCount);
- size_t recordFramecount = mRecordThread->frameCount();
+ size_t recordFramecount = mRecord.thread()->frameCount();
int shift = __builtin_ctz(recordFramecount);
if (playbackShift < shift) {
shift = playbackShift;
@@ -412,14 +413,14 @@
__func__, playbackFrameCount, recordFramecount, frameCount);
// create a special record track to capture from record thread
- uint32_t channelCount = mPlaybackThread->channelCount();
+ uint32_t channelCount = mPlayback.thread()->channelCount();
audio_channel_mask_t inChannelMask = audio_channel_in_mask_from_count(channelCount);
- audio_channel_mask_t outChannelMask = mPlaybackThread->channelMask();
- uint32_t sampleRate = mPlaybackThread->sampleRate();
- audio_format_t format = mPlaybackThread->format();
+ audio_channel_mask_t outChannelMask = mPlayback.thread()->channelMask();
+ uint32_t sampleRate = mPlayback.thread()->sampleRate();
+ audio_format_t format = mPlayback.thread()->format();
- mPatchRecord = new RecordThread::PatchRecord(
- mRecordThread.get(),
+ sp<RecordThread::PatchRecord> tempRecordTrack = new (std::nothrow) RecordThread::PatchRecord(
+ mRecord.thread().get(),
sampleRate,
inChannelMask,
format,
@@ -427,91 +428,47 @@
NULL,
(size_t)0 /* bufferSize */,
AUDIO_INPUT_FLAG_NONE);
- if (mPatchRecord == 0) {
- return NO_MEMORY;
- }
- status = mPatchRecord->initCheck();
+ status = mRecord.checkTrack(tempRecordTrack.get());
if (status != NO_ERROR) {
return status;
}
- mRecordThread->addPatchRecord(mPatchRecord);
// create a special playback track to render to playback thread.
// this track is given the same buffer as the PatchRecord buffer
- mPatchTrack = new PlaybackThread::PatchTrack(
- mPlaybackThread.get(),
+ sp<PlaybackThread::PatchTrack> tempPatchTrack = new (std::nothrow) PlaybackThread::PatchTrack(
+ mPlayback.thread().get(),
mAudioPatch.sources[1].ext.mix.usecase.stream,
sampleRate,
outChannelMask,
format,
frameCount,
- mPatchRecord->buffer(),
- mPatchRecord->bufferSize(),
+ tempRecordTrack->buffer(),
+ tempRecordTrack->bufferSize(),
AUDIO_OUTPUT_FLAG_NONE);
- status = mPatchTrack->initCheck();
+ status = mPlayback.checkTrack(tempPatchTrack.get());
if (status != NO_ERROR) {
return status;
}
- mPlaybackThread->addPatchTrack(mPatchTrack);
// tie playback and record tracks together
- mPatchRecord->setPeerProxy(mPatchTrack.get());
- mPatchTrack->setPeerProxy(mPatchRecord.get());
+ mRecord.setTrackAndPeer(tempRecordTrack, tempPatchTrack.get());
+ mPlayback.setTrackAndPeer(tempPatchTrack, tempRecordTrack.get());
// start capture and playback
- mPatchRecord->start(AudioSystem::SYNC_EVENT_NONE, AUDIO_SESSION_NONE);
- mPatchTrack->start();
+ mRecord.track()->start(AudioSystem::SYNC_EVENT_NONE, AUDIO_SESSION_NONE);
+ mPlayback.track()->start();
return status;
}
void AudioFlinger::PatchPanel::Patch::clearConnections(PatchPanel *panel)
{
- ALOGV("%s() mRecordPatchHandle %d mPlaybackPatchHandle %d",
- __func__, mRecordPatchHandle, mPlaybackPatchHandle);
-
- if (mPatchRecord != 0) {
- mPatchRecord->stop();
- }
- if (mPatchTrack != 0) {
- mPatchTrack->stop();
- }
- if (mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE) {
- panel->releaseAudioPatch(mRecordPatchHandle);
- mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE;
- }
- if (mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) {
- panel->releaseAudioPatch(mPlaybackPatchHandle);
- mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE;
- }
- if (mRecordThread != 0) {
- if (mPatchRecord != 0) {
- mRecordThread->deletePatchRecord(mPatchRecord);
- }
- panel->mAudioFlinger.closeInputInternal_l(mRecordThread);
- }
- if (mPlaybackThread != 0) {
- if (mPatchTrack != 0) {
- mPlaybackThread->deletePatchTrack(mPatchTrack);
- }
- // if num sources == 2 we are reusing an existing playback thread so we do not close it
- if (mAudioPatch.num_sources != 2) {
- panel->mAudioFlinger.closeOutputInternal_l(mPlaybackThread);
- }
- }
- if (mRecordThread != 0) {
- if (mPatchRecord != 0) {
- mPatchRecord.clear();
- }
- mRecordThread.clear();
- }
- if (mPlaybackThread != 0) {
- if (mPatchTrack != 0) {
- mPatchTrack.clear();
- }
- mPlaybackThread.clear();
- }
-
+ ALOGV("%s() mRecord.handle %d mPlayback.handle %d",
+ __func__, mRecord.handle(), mPlayback.handle());
+ mRecord.stopTrack();
+ mPlayback.stopTrack();
+ mRecord.closeConnections(panel);
+ mPlayback.closeConnections(panel);
}
/* Disconnect a patch */
@@ -527,18 +484,17 @@
Patch &removedPatch = iter->second;
const struct audio_patch &patch = removedPatch.mAudioPatch;
- switch (patch.sources[0].type) {
+ const struct audio_port_config &src = patch.sources[0];
+ switch (src.type) {
case AUDIO_PORT_TYPE_DEVICE: {
- audio_module_handle_t srcModule = patch.sources[0].ext.device.hw_module;
- ssize_t index = mAudioFlinger.mAudioHwDevs.indexOfKey(srcModule);
- if (index < 0) {
- ALOGW("%s() bad src hw module %d", __func__, srcModule);
+ sp<DeviceHalInterface> hwDevice = findHwDeviceByModule(src.ext.device.hw_module);
+ if (hwDevice == 0) {
+ ALOGW("%s() bad src hw module %d", __func__, src.ext.device.hw_module);
status = BAD_VALUE;
break;
}
- if (removedPatch.mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE ||
- removedPatch.mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) {
+ if (removedPatch.isSoftware()) {
removedPatch.clearConnections(this);
break;
}
@@ -556,20 +512,16 @@
}
status = thread->sendReleaseAudioPatchConfigEvent(removedPatch.mHalHandle);
} else {
- AudioHwDevice *audioHwDevice = mAudioFlinger.mAudioHwDevs.valueAt(index);
- sp<DeviceHalInterface> hwDevice = audioHwDevice->hwDevice();
status = hwDevice->releaseAudioPatch(removedPatch.mHalHandle);
}
} break;
case AUDIO_PORT_TYPE_MIX: {
- audio_module_handle_t srcModule = patch.sources[0].ext.mix.hw_module;
- ssize_t index = mAudioFlinger.mAudioHwDevs.indexOfKey(srcModule);
- if (index < 0) {
- ALOGW("%s() bad src hw module %d", __func__, srcModule);
+ if (findHwDeviceByModule(src.ext.mix.hw_module) == 0) {
+ ALOGW("%s() bad src hw module %d", __func__, src.ext.mix.hw_module);
status = BAD_VALUE;
break;
}
- audio_io_handle_t ioHandle = patch.sources[0].ext.mix.handle;
+ audio_io_handle_t ioHandle = src.ext.mix.handle;
sp<ThreadBase> thread = mAudioFlinger.checkPlaybackThread_l(ioHandle);
if (thread == 0) {
thread = mAudioFlinger.checkMmapThread_l(ioHandle);
@@ -597,4 +549,14 @@
return NO_ERROR;
}
+sp<DeviceHalInterface> AudioFlinger::PatchPanel::findHwDeviceByModule(audio_module_handle_t module)
+{
+ if (module == AUDIO_MODULE_HANDLE_NONE) return nullptr;
+ ssize_t index = mAudioFlinger.mAudioHwDevs.indexOfKey(module);
+ if (index < 0) {
+ return nullptr;
+ }
+ return mAudioFlinger.mAudioHwDevs.valueAt(index)->hwDevice();
+}
+
} // namespace android
diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h
index c2cb7ac..5a68960 100644
--- a/services/audioflinger/PatchPanel.h
+++ b/services/audioflinger/PatchPanel.h
@@ -43,12 +43,61 @@
struct audio_patch *patches);
private:
+ template<typename ThreadType, typename TrackType>
+ class Endpoint {
+ public:
+ status_t checkTrack(TrackType *trackOrNull) const {
+ if (trackOrNull == nullptr) return NO_MEMORY;
+ return trackOrNull->initCheck();
+ }
+ audio_patch_handle_t handle() const { return mHandle; }
+ sp<ThreadType> thread() { return mThread; }
+ sp<TrackType> track() { return mTrack; }
+
+ void closeConnections(PatchPanel *panel) {
+ if (mHandle != AUDIO_PATCH_HANDLE_NONE) {
+ panel->releaseAudioPatch(mHandle);
+ mHandle = AUDIO_PATCH_HANDLE_NONE;
+ }
+ if (mThread != 0) {
+ if (mTrack != 0) {
+ mThread->deletePatchTrack(mTrack);
+ }
+ if (mCloseThread) {
+ panel->mAudioFlinger.closeThreadInternal_l(mThread);
+ }
+ }
+ }
+ audio_patch_handle_t* handlePtr() { return &mHandle; }
+ void setThread(const sp<ThreadType>& thread, bool closeThread = true) {
+ mThread = thread;
+ mCloseThread = closeThread;
+ }
+ void setTrackAndPeer(const sp<TrackType>& track,
+ ThreadBase::PatchProxyBufferProvider *peer) {
+ mTrack = track;
+ mThread->addPatchTrack(mTrack);
+ mTrack->setPeerProxy(peer);
+ }
+ void stopTrack() { if (mTrack) mTrack->stop(); }
+
+ private:
+ sp<ThreadType> mThread;
+ bool mCloseThread = true;
+ audio_patch_handle_t mHandle = AUDIO_PATCH_HANDLE_NONE;
+ sp<TrackType> mTrack;
+ };
+
class Patch {
public:
explicit Patch(const struct audio_patch &patch) : mAudioPatch(patch) {}
+ ~Patch();
status_t createConnections(PatchPanel *panel);
void clearConnections(PatchPanel *panel);
+ bool isSoftware() const {
+ return mRecord.handle() != AUDIO_PATCH_HANDLE_NONE ||
+ mPlayback.handle() != AUDIO_PATCH_HANDLE_NONE; }
// Note that audio_patch::id is only unique within a HAL module
struct audio_patch mAudioPatch;
@@ -58,17 +107,14 @@
// given audio HW module to a sink device on an other audio HW module.
// the objects are created by createConnections() and released by clearConnections()
// playback thread is created if no existing playback thread can be used
- sp<PlaybackThread> mPlaybackThread;
- sp<PlaybackThread::PatchTrack> mPatchTrack;
- sp<RecordThread> mRecordThread;
- sp<RecordThread::PatchRecord> mPatchRecord;
- // handle for audio patch connecting source device to record thread input.
- audio_patch_handle_t mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE;
- // handle for audio patch connecting playback thread output to sink device
- audio_patch_handle_t mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE;
-
+ // connects playback thread output to sink device
+ Endpoint<PlaybackThread, PlaybackThread::PatchTrack> mPlayback;
+ // connects source device to record thread input
+ Endpoint<RecordThread, RecordThread::PatchRecord> mRecord;
};
+ sp<DeviceHalInterface> findHwDeviceByModule(audio_module_handle_t module);
+
AudioFlinger &mAudioFlinger;
std::map<audio_patch_handle_t, Patch> mPatches;
};
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index c47aa01..998cf4e 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -7835,13 +7835,13 @@
return status;
}
-void AudioFlinger::RecordThread::addPatchRecord(const sp<PatchRecord>& record)
+void AudioFlinger::RecordThread::addPatchTrack(const sp<PatchRecord>& record)
{
Mutex::Autolock _l(mLock);
mTracks.add(record);
}
-void AudioFlinger::RecordThread::deletePatchRecord(const sp<PatchRecord>& record)
+void AudioFlinger::RecordThread::deletePatchTrack(const sp<PatchRecord>& record)
{
Mutex::Autolock _l(mLock);
destroyTrack_l(record);
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 28d4482..c490fb5 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1437,8 +1437,8 @@
audio_patch_handle_t *handle);
virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle);
- void addPatchRecord(const sp<PatchRecord>& record);
- void deletePatchRecord(const sp<PatchRecord>& record);
+ void addPatchTrack(const sp<PatchRecord>& record);
+ void deletePatchTrack(const sp<PatchRecord>& record);
void readInputParameters_l();
virtual uint32_t getInputFramesLost();