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/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