| /* |
| ** |
| ** 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 "PatchCommandThread.h" |
| |
| #include <utils/Log.h> |
| |
| namespace android { |
| |
| constexpr char kPatchCommandThreadName[] = "AudioFlinger_PatchCommandThread"; |
| |
| PatchCommandThread::~PatchCommandThread() { |
| exit(); |
| |
| audio_utils::lock_guard _l(mutex()); |
| mCommands.clear(); |
| } |
| |
| void PatchCommandThread::onFirstRef() { |
| run(kPatchCommandThreadName, ANDROID_PRIORITY_AUDIO); |
| } |
| |
| void PatchCommandThread::addListener(const sp<PatchCommandListener>& listener) { |
| ALOGV("%s add listener %p", __func__, static_cast<void*>(listener.get())); |
| audio_utils::lock_guard _l(listenerMutex()); |
| mListeners.emplace_back(listener); |
| } |
| |
| void PatchCommandThread::createAudioPatch(audio_patch_handle_t handle, |
| const IAfPatchPanel::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 PatchCommandThread::releaseAudioPatch(audio_patch_handle_t handle) { |
| ALOGV("%s", __func__); |
| releaseAudioPatchCommand(handle); |
| } |
| |
| void PatchCommandThread::updateAudioPatch(audio_patch_handle_t oldHandle, |
| audio_patch_handle_t newHandle, const IAfPatchPanel::Patch& patch) { |
| ALOGV("%s handle %d mHalHandle %d num sinks %d device sink %08x", |
| __func__, oldHandle, patch.mHalHandle, |
| patch.mAudioPatch.num_sinks, |
| patch.mAudioPatch.num_sinks > 0 ? patch.mAudioPatch.sinks[0].ext.device.type : 0); |
| |
| updateAudioPatchCommand(oldHandle, newHandle, patch); |
| } |
| |
| bool PatchCommandThread::threadLoop() |
| { |
| audio_utils::unique_lock _l(mutex()); |
| |
| while (!exitPending()) { |
| while (!mCommands.empty() && !exitPending()) { |
| const sp<Command> command = mCommands.front(); |
| mCommands.pop_front(); |
| _l.unlock(); |
| |
| std::vector<wp<PatchCommandListener>> listenersCopy; |
| { |
| audio_utils::lock_guard _ll(listenerMutex()); |
| 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; |
| case UPDATE_AUDIO_PATCH: { |
| const auto data = (UpdateAudioPatchData*) command->mData.get(); |
| ALOGV("%s processing update audio patch old handle %d new handle %d", |
| __func__, |
| data->mOldHandle, data->mNewHandle); |
| |
| for (const auto& listener : listenersCopy) { |
| auto spListener = listener.promote(); |
| if (spListener) { |
| spListener->onUpdateAudioPatch(data->mOldHandle, |
| data->mNewHandle, data->mPatch); |
| } |
| } |
| } |
| 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 PatchCommandThread::sendCommand(const sp<Command>& command) { |
| audio_utils::lock_guard _l(mutex()); |
| mCommands.emplace_back(command); |
| mWaitWorkCV.notify_one(); |
| } |
| |
| void PatchCommandThread::createAudioPatchCommand( |
| audio_patch_handle_t handle, const IAfPatchPanel::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 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 PatchCommandThread::updateAudioPatchCommand( |
| audio_patch_handle_t oldHandle, audio_patch_handle_t newHandle, |
| const IAfPatchPanel::Patch& patch) { |
| sp<Command> command = sp<Command>::make(UPDATE_AUDIO_PATCH, |
| new UpdateAudioPatchData(oldHandle, newHandle, patch)); |
| ALOGV("%s adding update patch old handle %d new handle %d mHalHandle %d.", |
| __func__, oldHandle, newHandle, patch.mHalHandle); |
| sendCommand(command); |
| } |
| |
| void PatchCommandThread::exit() { |
| ALOGV("%s", __func__); |
| { |
| audio_utils::lock_guard _l(mutex()); |
| 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 |