Refactor the PatchCommandThread for reuse.
Extract the CommandThread into PatchCommandThread and make it reusable
for other components interested in PatchPanel changes.
Test: manual
Bug: 252776298
Change-Id: I16db5341543328b4a84be238aa7c1b5804dfe7c9
diff --git a/services/audioflinger/Android.bp b/services/audioflinger/Android.bp
index 87c67bd..2549f7c 100644
--- a/services/audioflinger/Android.bp
+++ b/services/audioflinger/Android.bp
@@ -44,6 +44,7 @@
"FastThreadDumpState.cpp",
"FastThreadState.cpp",
"NBAIO_Tee.cpp",
+ "PatchCommandThread.cpp",
"PatchPanel.cpp",
"PropertyUtils.cpp",
"SpdifStreamOut.cpp",
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 12cf70d..f2dd600 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -323,7 +323,8 @@
mClientSharedHeapSize(kMinimumClientSharedHeapSizeBytes),
mGlobalEffectEnableTime(0),
mPatchPanel(this),
- mDeviceEffectManager(this),
+ mPatchCommandThread(sp<PatchCommandThread>::make()),
+ mDeviceEffectManager(sp<DeviceEffectManager>::make(*this)),
mSystemReady(false)
{
// Move the audio session unique ID generator start base as time passes to limit risk of
@@ -873,7 +874,7 @@
mPatchPanel.dump(fd);
- mDeviceEffectManager.dump(fd);
+ mDeviceEffectManager->dump(fd);
// dump external setParameters
auto dumpLogger = [fd](SimpleLog& logger, const char* name) {
@@ -4069,7 +4070,7 @@
if (sessionId == AUDIO_SESSION_DEVICE) {
sp<Client> client = registerPid(currentPid);
ALOGV("%s device type %#x address %s", __func__, device.mType, device.getAddress());
- handle = mDeviceEffectManager.createEffect_l(
+ handle = mDeviceEffectManager->createEffect_l(
&descOut, device, client, effectClient, mPatchPanel.patches_l(),
&enabledOut, &lStatus, probe, request.notifyFramesProcessed);
if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index fcf19c9..00ca355 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -634,6 +634,8 @@
#include "Effects.h"
+#include "PatchCommandThread.h"
+
#include "DeviceEffectManager.h"
// Find io handle by session id.
@@ -1012,7 +1014,8 @@
PatchPanel mPatchPanel;
sp<EffectsFactoryHalInterface> mEffectsFactoryHal;
- DeviceEffectManager mDeviceEffectManager;
+ const sp<PatchCommandThread> mPatchCommandThread;
+ sp<DeviceEffectManager> mDeviceEffectManager;
bool mSystemReady;
std::atomic_bool mAudioPolicyReady{};
diff --git a/services/audioflinger/DeviceEffectManager.cpp b/services/audioflinger/DeviceEffectManager.cpp
index 4f3ed0a..a614736 100644
--- a/services/audioflinger/DeviceEffectManager.cpp
+++ b/services/audioflinger/DeviceEffectManager.cpp
@@ -32,16 +32,6 @@
using media::IEffectClient;
-void AudioFlinger::DeviceEffectManager::createAudioPatch(audio_patch_handle_t handle,
- const PatchPanel::Patch& patch) {
- ALOGV("%s handle %d mHalHandle %d num sinks %d device sink %08x",
- __func__, handle, patch.mHalHandle,
- patch.mAudioPatch.num_sinks,
- patch.mAudioPatch.num_sinks > 0 ? patch.mAudioPatch.sinks[0].ext.device.type : 0);
-
- mCommandThread->createAudioPatchCommand(handle, patch);
-}
-
void AudioFlinger::DeviceEffectManager::onCreateAudioPatch(audio_patch_handle_t handle,
const PatchPanel::Patch& patch) {
ALOGV("%s handle %d mHalHandle %d device sink %08x",
@@ -55,11 +45,6 @@
}
}
-void AudioFlinger::DeviceEffectManager::releaseAudioPatch(audio_patch_handle_t handle) {
- ALOGV("%s", __func__);
- mCommandThread->releaseAudioPatchCommand(handle);
-}
-
void AudioFlinger::DeviceEffectManager::onReleaseAudioPatch(audio_patch_handle_t handle) {
ALOGV("%s", __func__);
Mutex::Autolock _l(mLock);
@@ -116,7 +101,7 @@
}
}
}
- if (enabled != NULL) {
+ if (enabled != nullptr) {
*enabled = (int)effect->isEnabled();
}
*status = lStatus;
@@ -208,91 +193,4 @@
return true;
}
-// ----------- DeviceEffectManager::CommandThread implementation ----------
-
-
-AudioFlinger::DeviceEffectManager::CommandThread::~CommandThread()
-{
- Mutex::Autolock _l(mLock);
- mCommands.clear();
-}
-
-void AudioFlinger::DeviceEffectManager::CommandThread::onFirstRef()
-{
- run("DeviceEffectManage_CommandThread", ANDROID_PRIORITY_AUDIO);
-}
-
-bool AudioFlinger::DeviceEffectManager::CommandThread::threadLoop()
-{
- mLock.lock();
- while (!exitPending())
- {
- while (!mCommands.empty() && !exitPending()) {
- sp<Command> command = mCommands.front();
- mCommands.pop_front();
- mLock.unlock();
-
- switch (command->mCommand) {
- case CREATE_AUDIO_PATCH: {
- CreateAudioPatchData *data = (CreateAudioPatchData *)command->mData.get();
- ALOGV("CommandThread() processing create audio patch handle %d", data->mHandle);
- mManager.onCreateAudioPatch(data->mHandle, data->mPatch);
- } break;
- case RELEASE_AUDIO_PATCH: {
- ReleaseAudioPatchData *data = (ReleaseAudioPatchData *)command->mData.get();
- ALOGV("CommandThread() processing release audio patch handle %d", data->mHandle);
- mManager.onReleaseAudioPatch(data->mHandle);
- } break;
- default:
- ALOGW("CommandThread() unknown command %d", command->mCommand);
- }
- mLock.lock();
- }
-
- // At this stage we have either an empty command queue or the first command in the queue
- // has a finite delay. So unless we are exiting it is safe to wait.
- if (!exitPending()) {
- ALOGV("CommandThread() going to sleep");
- mWaitWorkCV.wait(mLock);
- }
- }
- mLock.unlock();
- return false;
-}
-
-void AudioFlinger::DeviceEffectManager::CommandThread::sendCommand(sp<Command> command) {
- Mutex::Autolock _l(mLock);
- mCommands.push_back(command);
- mWaitWorkCV.signal();
-}
-
-void AudioFlinger::DeviceEffectManager::CommandThread::createAudioPatchCommand(
- audio_patch_handle_t handle, const PatchPanel::Patch& patch)
-{
- sp<Command> command = new Command(CREATE_AUDIO_PATCH, new CreateAudioPatchData(handle, patch));
- ALOGV("CommandThread() adding create patch handle %d mHalHandle %d.", handle, patch.mHalHandle);
- sendCommand(command);
-}
-
-void AudioFlinger::DeviceEffectManager::CommandThread::releaseAudioPatchCommand(
- audio_patch_handle_t handle)
-{
- sp<Command> command = new Command(RELEASE_AUDIO_PATCH, new ReleaseAudioPatchData(handle));
- ALOGV("CommandThread() adding release patch");
- sendCommand(command);
-}
-
-void AudioFlinger::DeviceEffectManager::CommandThread::exit()
-{
- ALOGV("CommandThread::exit");
- {
- AutoMutex _l(mLock);
- requestExit();
- mWaitWorkCV.signal();
- }
- // Note that we can call it from the thread loop if all other references have been released
- // but it will safely return WOULD_BLOCK in this case
- requestExitAndWait();
-}
-
} // namespace android
diff --git a/services/audioflinger/DeviceEffectManager.h b/services/audioflinger/DeviceEffectManager.h
index d2faa70..7602f12 100644
--- a/services/audioflinger/DeviceEffectManager.h
+++ b/services/audioflinger/DeviceEffectManager.h
@@ -20,15 +20,15 @@
#endif
// DeviceEffectManager is concealed within AudioFlinger, their lifetimes are the same.
-class DeviceEffectManager {
+class DeviceEffectManager : public PatchCommandThread::PatchCommandListener {
public:
- explicit DeviceEffectManager(AudioFlinger* audioFlinger)
- : mCommandThread(new CommandThread(*this)), mAudioFlinger(*audioFlinger),
- mMyCallback(new DeviceEffectManagerCallback(this)) {}
+ explicit DeviceEffectManager(AudioFlinger& audioFlinger)
+ : mAudioFlinger(audioFlinger),
+ mMyCallback(new DeviceEffectManagerCallback(*this)) {}
- ~DeviceEffectManager() {
- mCommandThread->exit();
- }
+ void onFirstRef() override {
+ mAudioFlinger.mPatchCommandThread->addListener(this);
+ }
sp<EffectHandle> createEffect_l(effect_descriptor_t *descriptor,
const AudioDeviceTypeAddr& device,
@@ -39,8 +39,6 @@
status_t *status,
bool probe,
bool notifyFramesProcessed);
- void createAudioPatch(audio_patch_handle_t handle, const PatchPanel::Patch& patch);
- void releaseAudioPatch(audio_patch_handle_t handle);
size_t removeEffect(const sp<DeviceEffectProxy>& effect);
status_t createEffectHal(const effect_uuid_t *pEffectUuid,
@@ -59,94 +57,25 @@
void dump(int fd);
+ // PatchCommandThread::PatchCommandListener implementation
+
+ void onCreateAudioPatch(audio_patch_handle_t handle,
+ const PatchPanel::Patch& patch) override;
+ void onReleaseAudioPatch(audio_patch_handle_t handle) override;
+
private:
-
- // Thread to execute create and release patch commands asynchronously. This is needed because
- // PatchPanel::createAudioPatch and releaseAudioPatch are executed from audio policy service
- // with mutex locked and effect management requires to call back into audio policy service
- class Command;
- class CommandThread : public Thread {
- public:
-
- enum {
- CREATE_AUDIO_PATCH,
- RELEASE_AUDIO_PATCH,
- };
-
- CommandThread(DeviceEffectManager& manager)
- : Thread(false), mManager(manager) {}
- ~CommandThread() override;
-
- // Thread virtuals
- void onFirstRef() override;
- bool threadLoop() override;
-
- void exit();
-
- void createAudioPatchCommand(audio_patch_handle_t handle,
- const PatchPanel::Patch& patch);
- void releaseAudioPatchCommand(audio_patch_handle_t handle);
-
- private:
- class CommandData;
-
- // descriptor for requested tone playback event
- class Command: public RefBase {
- public:
- Command() = default;
- Command(int command, sp<CommandData> data)
- : mCommand(command), mData(data) {}
-
- int mCommand = -1;
- sp<CommandData> mData;
- };
-
- class CommandData: public RefBase {
- public:
- virtual ~CommandData() = default;
- };
-
- class CreateAudioPatchData : public CommandData {
- public:
- CreateAudioPatchData(audio_patch_handle_t handle, const PatchPanel::Patch& patch)
- : mHandle(handle), mPatch(patch) {}
-
- audio_patch_handle_t mHandle;
- const PatchPanel::Patch mPatch;
- };
-
- class ReleaseAudioPatchData : public CommandData {
- public:
- ReleaseAudioPatchData(audio_patch_handle_t handle)
- : mHandle(handle) {}
-
- audio_patch_handle_t mHandle;
- };
-
- void sendCommand(sp<Command> command);
-
- Mutex mLock;
- Condition mWaitWorkCV;
- std::deque <sp<Command>> mCommands; // list of pending commands
- DeviceEffectManager& mManager;
- };
-
- void onCreateAudioPatch(audio_patch_handle_t handle, const PatchPanel::Patch& patch);
- void onReleaseAudioPatch(audio_patch_handle_t handle);
-
status_t checkEffectCompatibility(const effect_descriptor_t *desc);
Mutex mLock;
- sp<CommandThread> mCommandThread;
AudioFlinger &mAudioFlinger;
const sp<DeviceEffectManagerCallback> mMyCallback;
std::map<AudioDeviceTypeAddr, sp<DeviceEffectProxy>> mDeviceEffects;
};
-class DeviceEffectManagerCallback : public EffectCallbackInterface {
+class DeviceEffectManagerCallback : public EffectCallbackInterface {
public:
- DeviceEffectManagerCallback(DeviceEffectManager *manager)
- : mManager(*manager) {}
+ DeviceEffectManagerCallback(DeviceEffectManager& manager)
+ : mManager(manager) {}
status_t createEffectHal(const effect_uuid_t *pEffectUuid,
int32_t sessionId, int32_t deviceId,
diff --git a/services/audioflinger/PatchCommandThread.cpp b/services/audioflinger/PatchCommandThread.cpp
new file mode 100644
index 0000000..c3cb7e7
--- /dev/null
+++ b/services/audioflinger/PatchCommandThread.cpp
@@ -0,0 +1,156 @@
+/*
+**
+** Copyright 2022, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "AudioFlinger::PatchCommandThread"
+//#define LOG_NDEBUG 0
+
+#include "AudioFlinger.h"
+
+namespace android {
+
+constexpr char kPatchCommandThreadName[] = "AudioFlinger_PatchCommandThread";
+
+AudioFlinger::PatchCommandThread::~PatchCommandThread() {
+ exit();
+
+ std::lock_guard _l(mLock);
+ mCommands.clear();
+}
+
+void AudioFlinger::PatchCommandThread::onFirstRef() {
+ run(kPatchCommandThreadName, ANDROID_PRIORITY_AUDIO);
+}
+
+void AudioFlinger::PatchCommandThread::addListener(const sp<PatchCommandListener>& listener) {
+ ALOGV("%s add listener %p", __func__, static_cast<void*>(listener.get()));
+ std::lock_guard _l(mListenerLock);
+ mListeners.emplace_back(listener);
+}
+
+void AudioFlinger::PatchCommandThread::createAudioPatch(audio_patch_handle_t handle,
+ const PatchPanel::Patch& patch) {
+ ALOGV("%s handle %d mHalHandle %d num sinks %d device sink %08x",
+ __func__, handle, patch.mHalHandle,
+ patch.mAudioPatch.num_sinks,
+ patch.mAudioPatch.num_sinks > 0 ? patch.mAudioPatch.sinks[0].ext.device.type : 0);
+
+ createAudioPatchCommand(handle, patch);
+}
+
+void AudioFlinger::PatchCommandThread::releaseAudioPatch(audio_patch_handle_t handle) {
+ ALOGV("%s", __func__);
+ releaseAudioPatchCommand(handle);
+}
+
+bool AudioFlinger::PatchCommandThread::threadLoop() {
+ std::unique_lock _l(mLock);
+
+ while (!exitPending()) {
+ while (!mCommands.empty() && !exitPending()) {
+ const sp<Command> command = mCommands.front();
+ mCommands.pop_front();
+ _l.unlock();
+
+ std::vector<wp<PatchCommandListener>> listenersCopy;
+ {
+ std::lock_guard _ll(mListenerLock);
+ listenersCopy = mListeners;
+ }
+
+ switch (command->mCommand) {
+ case CREATE_AUDIO_PATCH: {
+ const auto data = (CreateAudioPatchData*) command->mData.get();
+ ALOGV("%s processing create audio patch handle %d",
+ __func__,
+ data->mHandle);
+
+ for (const auto& listener : listenersCopy) {
+ auto spListener = listener.promote();
+ if (spListener) {
+ spListener->onCreateAudioPatch(data->mHandle, data->mPatch);
+ }
+ }
+ }
+ break;
+ case RELEASE_AUDIO_PATCH: {
+ const auto data = (ReleaseAudioPatchData*) command->mData.get();
+ ALOGV("%s processing release audio patch handle %d",
+ __func__,
+ data->mHandle);
+
+ for (const auto& listener : listenersCopy) {
+ auto spListener = listener.promote();
+ if (spListener) {
+ spListener->onReleaseAudioPatch(data->mHandle);
+ }
+ }
+ }
+ break;
+ default:
+ ALOGW("%s unknown command %d", __func__, command->mCommand);
+ break;
+ }
+ _l.lock();
+ }
+
+ // At this stage we have either an empty command queue or the first command in the queue
+ // has a finite delay. So unless we are exiting it is safe to wait.
+ if (!exitPending()) {
+ ALOGV("%s going to sleep", __func__);
+ mWaitWorkCV.wait(_l);
+ }
+ }
+ return false;
+}
+
+void AudioFlinger::PatchCommandThread::sendCommand(const sp<Command>& command) {
+ std::lock_guard _l(mLock);
+ mCommands.emplace_back(command);
+ mWaitWorkCV.notify_one();
+}
+
+void AudioFlinger::PatchCommandThread::createAudioPatchCommand(
+ audio_patch_handle_t handle, const PatchPanel::Patch& patch) {
+ auto command = sp<Command>::make(CREATE_AUDIO_PATCH,
+ new CreateAudioPatchData(handle, patch));
+ ALOGV("%s adding create patch handle %d mHalHandle %d.",
+ __func__,
+ handle,
+ patch.mHalHandle);
+ sendCommand(command);
+}
+
+void AudioFlinger::PatchCommandThread::releaseAudioPatchCommand(audio_patch_handle_t handle) {
+ sp<Command> command =
+ sp<Command>::make(RELEASE_AUDIO_PATCH, new ReleaseAudioPatchData(handle));
+ ALOGV("%s adding release patch", __func__);
+ sendCommand(command);
+}
+
+void AudioFlinger::PatchCommandThread::exit() {
+ ALOGV("%s", __func__);
+ {
+ std::lock_guard _l(mLock);
+ requestExit();
+ mWaitWorkCV.notify_one();
+ }
+ // Note that we can call it from the thread loop if all other references have been released
+ // but it will safely return WOULD_BLOCK in this case
+ requestExitAndWait();
+}
+
+} // namespace android
diff --git a/services/audioflinger/PatchCommandThread.h b/services/audioflinger/PatchCommandThread.h
new file mode 100644
index 0000000..b7853f0
--- /dev/null
+++ b/services/audioflinger/PatchCommandThread.h
@@ -0,0 +1,102 @@
+/*
+**
+** Copyright 2022, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef INCLUDING_FROM_AUDIOFLINGER_H
+ #error This header file should only be included from AudioFlinger.h
+#endif
+
+class Command;
+
+// Thread to execute create and release patch commands asynchronously. This is needed because
+// PatchPanel::createAudioPatch and releaseAudioPatch are executed from audio policy service
+// with mutex locked and effect management requires to call back into audio policy service
+class PatchCommandThread : public Thread {
+public:
+
+ enum {
+ CREATE_AUDIO_PATCH,
+ RELEASE_AUDIO_PATCH,
+ };
+
+ class PatchCommandListener : public virtual RefBase {
+ public:
+ virtual void onCreateAudioPatch(audio_patch_handle_t handle,
+ const PatchPanel::Patch& patch) = 0;
+ virtual void onReleaseAudioPatch(audio_patch_handle_t handle) = 0;
+ };
+
+ PatchCommandThread() : Thread(false /* canCallJava */) {}
+ ~PatchCommandThread() override;
+
+ void addListener(const sp<PatchCommandListener>& listener);
+
+ void createAudioPatch(audio_patch_handle_t handle, const PatchPanel::Patch& patch);
+ void releaseAudioPatch(audio_patch_handle_t handle);
+
+ // Thread virtuals
+ void onFirstRef() override;
+ bool threadLoop() override;
+
+ void exit();
+
+ void createAudioPatchCommand(audio_patch_handle_t handle,
+ const PatchPanel::Patch& patch);
+ void releaseAudioPatchCommand(audio_patch_handle_t handle);
+
+private:
+ class CommandData;
+
+ // Command type received from the PatchPanel
+ class Command: public RefBase {
+ public:
+ Command() = default;
+ Command(int command, const sp<CommandData>& data)
+ : mCommand(command), mData(data) {}
+
+ const int mCommand = -1;
+ const sp<CommandData> mData;
+ };
+
+ class CommandData: public RefBase {};
+
+ class CreateAudioPatchData : public CommandData {
+ public:
+ CreateAudioPatchData(audio_patch_handle_t handle, const PatchPanel::Patch& patch)
+ : mHandle(handle), mPatch(patch) {}
+
+ const audio_patch_handle_t mHandle;
+ const PatchPanel::Patch mPatch;
+ };
+
+ class ReleaseAudioPatchData : public CommandData {
+ public:
+ ReleaseAudioPatchData(audio_patch_handle_t handle)
+ : mHandle(handle) {}
+
+ audio_patch_handle_t mHandle;
+ };
+
+ void sendCommand(const sp<Command>& command);
+
+ std::string mThreadName;
+ std::mutex mLock;
+ std::condition_variable mWaitWorkCV;
+ std::deque<sp<Command>> mCommands GUARDED_BY(mLock); // list of pending commands
+
+ std::mutex mListenerLock;
+ std::vector<wp<PatchCommandListener>> mListeners GUARDED_BY(mListenerLock);
+};
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index b54b41f..3b428bb 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -449,7 +449,7 @@
if (status == NO_ERROR) {
*handle = (audio_patch_handle_t) mAudioFlinger.nextUniqueId(AUDIO_UNIQUE_ID_USE_PATCH);
newPatch.mHalHandle = halHandle;
- mAudioFlinger.mDeviceEffectManager.createAudioPatch(*handle, newPatch);
+ mAudioFlinger.mPatchCommandThread->createAudioPatch(*handle, newPatch);
if (insertedModule != AUDIO_MODULE_HANDLE_NONE) {
addSoftwarePatchToInsertedModules(insertedModule, *handle, &newPatch.mAudioPatch);
}
@@ -800,7 +800,7 @@
void AudioFlinger::PatchPanel::erasePatch(audio_patch_handle_t handle) {
mPatches.erase(handle);
removeSoftwarePatchFromInsertedModules(handle);
- mAudioFlinger.mDeviceEffectManager.releaseAudioPatch(handle);
+ mAudioFlinger.mPatchCommandThread->releaseAudioPatch(handle);
}
/* List connected audio ports and they attributes */