| Eric Laurent | b82e6b7 | 2019-11-22 17:25:04 -0800 | [diff] [blame] | 1 | /* | 
 | 2 | ** | 
 | 3 | ** Copyright 2019, The Android Open Source Project | 
 | 4 | ** | 
 | 5 | ** Licensed under the Apache License, Version 2.0 (the "License"); | 
 | 6 | ** you may not use this file except in compliance with the License. | 
 | 7 | ** You may obtain a copy of the License at | 
 | 8 | ** | 
 | 9 | **     http://www.apache.org/licenses/LICENSE-2.0 | 
 | 10 | ** | 
 | 11 | ** Unless required by applicable law or agreed to in writing, software | 
 | 12 | ** distributed under the License is distributed on an "AS IS" BASIS, | 
 | 13 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | 14 | ** See the License for the specific language governing permissions and | 
 | 15 | ** limitations under the License. | 
 | 16 | */ | 
 | 17 |  | 
 | 18 |  | 
 | 19 | #define LOG_TAG "AudioFlinger::DeviceEffectManager" | 
 | 20 | //#define LOG_NDEBUG 0 | 
 | 21 |  | 
 | 22 | #include <utils/Log.h> | 
 | 23 | #include <audio_utils/primitives.h> | 
 | 24 |  | 
 | 25 | #include "AudioFlinger.h" | 
 | 26 | #include <media/audiohal/EffectsFactoryHalInterface.h> | 
 | 27 |  | 
 | 28 | // ---------------------------------------------------------------------------- | 
 | 29 |  | 
 | 30 |  | 
 | 31 | namespace android { | 
 | 32 |  | 
| Ytai Ben-Tsvi | 9cd8981 | 2020-07-01 17:12:06 -0700 | [diff] [blame] | 33 | using media::IEffectClient; | 
 | 34 |  | 
| Eric Laurent | b82e6b7 | 2019-11-22 17:25:04 -0800 | [diff] [blame] | 35 | void AudioFlinger::DeviceEffectManager::createAudioPatch(audio_patch_handle_t handle, | 
 | 36 |         const PatchPanel::Patch& patch) { | 
 | 37 |     ALOGV("%s handle %d mHalHandle %d num sinks %d device sink %08x", | 
 | 38 |             __func__, handle, patch.mHalHandle, | 
 | 39 |             patch.mAudioPatch.num_sinks, | 
 | 40 |             patch.mAudioPatch.num_sinks > 0 ? patch.mAudioPatch.sinks[0].ext.device.type : 0); | 
 | 41 |  | 
 | 42 |     mCommandThread->createAudioPatchCommand(handle, patch); | 
 | 43 | } | 
 | 44 |  | 
 | 45 | void AudioFlinger::DeviceEffectManager::onCreateAudioPatch(audio_patch_handle_t handle, | 
 | 46 |         const PatchPanel::Patch& patch) { | 
 | 47 |     ALOGV("%s handle %d mHalHandle %d device sink %08x", | 
 | 48 |             __func__, handle, patch.mHalHandle, | 
 | 49 |             patch.mAudioPatch.num_sinks > 0 ? patch.mAudioPatch.sinks[0].ext.device.type : 0); | 
 | 50 |     Mutex::Autolock _l(mLock); | 
 | 51 |     for (auto& effect : mDeviceEffects) { | 
 | 52 |         status_t status = effect.second->onCreatePatch(handle, patch); | 
 | 53 |         ALOGV("%s Effect onCreatePatch status %d", __func__, status); | 
 | 54 |         ALOGW_IF(status == BAD_VALUE, "%s onCreatePatch error %d", __func__, status); | 
 | 55 |     } | 
 | 56 | } | 
 | 57 |  | 
 | 58 | void AudioFlinger::DeviceEffectManager::releaseAudioPatch(audio_patch_handle_t handle) { | 
 | 59 |     ALOGV("%s", __func__); | 
 | 60 |     mCommandThread->releaseAudioPatchCommand(handle); | 
 | 61 | } | 
 | 62 |  | 
 | 63 | void AudioFlinger::DeviceEffectManager::onReleaseAudioPatch(audio_patch_handle_t handle) { | 
 | 64 |     ALOGV("%s", __func__); | 
 | 65 |     Mutex::Autolock _l(mLock); | 
 | 66 |     for (auto& effect : mDeviceEffects) { | 
 | 67 |         effect.second->onReleasePatch(handle); | 
 | 68 |     } | 
 | 69 | } | 
 | 70 |  | 
 | 71 | // DeviceEffectManager::createEffect_l() must be called with AudioFlinger::mLock held | 
 | 72 | sp<AudioFlinger::EffectHandle> AudioFlinger::DeviceEffectManager::createEffect_l( | 
 | 73 |         effect_descriptor_t *descriptor, | 
 | 74 |         const AudioDeviceTypeAddr& device, | 
 | 75 |         const sp<AudioFlinger::Client>& client, | 
 | 76 |         const sp<IEffectClient>& effectClient, | 
 | 77 |         const std::map<audio_patch_handle_t, PatchPanel::Patch>& patches, | 
 | 78 |         int *enabled, | 
| Eric Laurent | 2fe0acd | 2020-03-13 14:30:46 -0700 | [diff] [blame] | 79 |         status_t *status, | 
 | 80 |         bool probe) { | 
| Eric Laurent | b82e6b7 | 2019-11-22 17:25:04 -0800 | [diff] [blame] | 81 |     sp<DeviceEffectProxy> effect; | 
 | 82 |     sp<EffectHandle> handle; | 
 | 83 |     status_t lStatus; | 
 | 84 |  | 
 | 85 |     lStatus = checkEffectCompatibility(descriptor); | 
| Eric Laurent | 2fe0acd | 2020-03-13 14:30:46 -0700 | [diff] [blame] | 86 |     if (probe || lStatus != NO_ERROR) { | 
| Eric Laurent | b82e6b7 | 2019-11-22 17:25:04 -0800 | [diff] [blame] | 87 |        *status = lStatus; | 
 | 88 |        return handle; | 
 | 89 |     } | 
 | 90 |  | 
 | 91 |     { | 
 | 92 |         Mutex::Autolock _l(mLock); | 
 | 93 |         auto iter = mDeviceEffects.find(device); | 
 | 94 |         if (iter != mDeviceEffects.end()) { | 
 | 95 |             effect = iter->second; | 
 | 96 |         } else { | 
 | 97 |             effect = new DeviceEffectProxy(device, mMyCallback, | 
 | 98 |                     descriptor, mAudioFlinger.nextUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT)); | 
 | 99 |         } | 
 | 100 |         // create effect handle and connect it to effect module | 
 | 101 |         handle = new EffectHandle(effect, client, effectClient, 0 /*priority*/); | 
 | 102 |         lStatus = handle->initCheck(); | 
 | 103 |         if (lStatus == NO_ERROR) { | 
 | 104 |             lStatus = effect->addHandle(handle.get()); | 
 | 105 |             if (lStatus == NO_ERROR) { | 
 | 106 |                 effect->init(patches); | 
 | 107 |                 mDeviceEffects.emplace(device, effect); | 
 | 108 |             } | 
 | 109 |         } | 
 | 110 |     } | 
 | 111 |     if (enabled != NULL) { | 
 | 112 |         *enabled = (int)effect->isEnabled(); | 
 | 113 |     } | 
 | 114 |     *status = lStatus; | 
 | 115 |     return handle; | 
 | 116 | } | 
 | 117 |  | 
 | 118 | status_t AudioFlinger::DeviceEffectManager::checkEffectCompatibility( | 
 | 119 |         const effect_descriptor_t *desc) { | 
 | 120 |  | 
 | 121 |     if ((desc->flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_PRE_PROC | 
 | 122 |         && (desc->flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_POST_PROC) { | 
 | 123 |         ALOGW("%s() non pre/post processing device effect %s", __func__, desc->name); | 
 | 124 |         return BAD_VALUE; | 
 | 125 |     } | 
 | 126 |  | 
 | 127 |     return NO_ERROR; | 
 | 128 | } | 
 | 129 |  | 
 | 130 | status_t AudioFlinger::DeviceEffectManager::createEffectHal( | 
 | 131 |         const effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t deviceId, | 
 | 132 |         sp<EffectHalInterface> *effect) { | 
 | 133 |     status_t status = NO_INIT; | 
 | 134 |     sp<EffectsFactoryHalInterface> effectsFactory = mAudioFlinger.getEffectsFactory(); | 
 | 135 |     if (effectsFactory != 0) { | 
 | 136 |         status = effectsFactory->createEffect( | 
 | 137 |                 pEffectUuid, sessionId, AUDIO_IO_HANDLE_NONE, deviceId, effect); | 
 | 138 |     } | 
 | 139 |     return status; | 
 | 140 | } | 
 | 141 |  | 
 | 142 | void AudioFlinger::DeviceEffectManager::dump(int fd) { | 
 | 143 |     const bool locked = dumpTryLock(mLock); | 
 | 144 |     if (!locked) { | 
 | 145 |         String8 result("DeviceEffectManager may be deadlocked\n"); | 
 | 146 |         write(fd, result.string(), result.size()); | 
 | 147 |     } | 
 | 148 |  | 
| Phil Burk | 651d0a5 | 2020-05-08 14:00:58 -0700 | [diff] [blame] | 149 |     String8 heading("\nDevice Effects:\n"); | 
 | 150 |     write(fd, heading.string(), heading.size()); | 
| Eric Laurent | b82e6b7 | 2019-11-22 17:25:04 -0800 | [diff] [blame] | 151 |     for (const auto& iter : mDeviceEffects) { | 
 | 152 |         String8 outStr; | 
 | 153 |         outStr.appendFormat("%*sEffect for device %s address %s:\n", 2, "", | 
 | 154 |                 ::android::toString(iter.first.mType).c_str(), iter.first.getAddress()); | 
 | 155 |         write(fd, outStr.string(), outStr.size()); | 
 | 156 |         iter.second->dump(fd, 4); | 
 | 157 |     } | 
 | 158 |  | 
 | 159 |     if (locked) { | 
 | 160 |         mLock.unlock(); | 
 | 161 |     } | 
 | 162 | } | 
 | 163 |  | 
 | 164 |  | 
 | 165 | size_t AudioFlinger::DeviceEffectManager::removeEffect(const sp<DeviceEffectProxy>& effect) | 
 | 166 | { | 
 | 167 |     Mutex::Autolock _l(mLock); | 
 | 168 |     mDeviceEffects.erase(effect->device()); | 
 | 169 |     return mDeviceEffects.size(); | 
 | 170 | } | 
 | 171 |  | 
 | 172 | bool AudioFlinger::DeviceEffectManagerCallback::disconnectEffectHandle( | 
 | 173 |         EffectHandle *handle, bool unpinIfLast) { | 
 | 174 |     sp<EffectBase> effectBase = handle->effect().promote(); | 
 | 175 |     if (effectBase == nullptr) { | 
 | 176 |         return false; | 
 | 177 |     } | 
 | 178 |  | 
 | 179 |     sp<DeviceEffectProxy> effect = effectBase->asDeviceEffectProxy(); | 
 | 180 |     if (effect == nullptr) { | 
 | 181 |         return false; | 
 | 182 |     } | 
 | 183 |     // restore suspended effects if the disconnected handle was enabled and the last one. | 
 | 184 |     bool remove = (effect->removeHandle(handle) == 0) && (!effect->isPinned() || unpinIfLast); | 
 | 185 |     if (remove) { | 
 | 186 |         mManager.removeEffect(effect); | 
 | 187 |         if (handle->enabled()) { | 
 | 188 |             effectBase->checkSuspendOnEffectEnabled(false, false /*threadLocked*/); | 
 | 189 |         } | 
 | 190 |     } | 
 | 191 |     return true; | 
 | 192 | } | 
 | 193 |  | 
 | 194 | // -----------  DeviceEffectManager::CommandThread implementation ---------- | 
 | 195 |  | 
 | 196 |  | 
 | 197 | AudioFlinger::DeviceEffectManager::CommandThread::~CommandThread() | 
 | 198 | { | 
 | 199 |     Mutex::Autolock _l(mLock); | 
 | 200 |     mCommands.clear(); | 
 | 201 | } | 
 | 202 |  | 
 | 203 | void AudioFlinger::DeviceEffectManager::CommandThread::onFirstRef() | 
 | 204 | { | 
 | 205 |     run("DeviceEffectManage_CommandThread", ANDROID_PRIORITY_AUDIO); | 
 | 206 | } | 
 | 207 |  | 
 | 208 | bool AudioFlinger::DeviceEffectManager::CommandThread::threadLoop() | 
 | 209 | { | 
 | 210 |     mLock.lock(); | 
 | 211 |     while (!exitPending()) | 
 | 212 |     { | 
 | 213 |         while (!mCommands.empty() && !exitPending()) { | 
 | 214 |             sp<Command> command = mCommands.front(); | 
 | 215 |             mCommands.pop_front(); | 
 | 216 |             mLock.unlock(); | 
 | 217 |  | 
 | 218 |             switch (command->mCommand) { | 
 | 219 |             case CREATE_AUDIO_PATCH: { | 
 | 220 |                 CreateAudioPatchData *data = (CreateAudioPatchData *)command->mData.get(); | 
 | 221 |                 ALOGV("CommandThread() processing create audio patch handle %d", data->mHandle); | 
 | 222 |                 mManager.onCreateAudioPatch(data->mHandle, data->mPatch); | 
 | 223 |                 } break; | 
 | 224 |             case RELEASE_AUDIO_PATCH: { | 
 | 225 |                 ReleaseAudioPatchData *data = (ReleaseAudioPatchData *)command->mData.get(); | 
 | 226 |                 ALOGV("CommandThread() processing release audio patch handle %d", data->mHandle); | 
 | 227 |                 mManager.onReleaseAudioPatch(data->mHandle); | 
 | 228 |                 } break; | 
 | 229 |             default: | 
 | 230 |                 ALOGW("CommandThread() unknown command %d", command->mCommand); | 
 | 231 |             } | 
 | 232 |             mLock.lock(); | 
 | 233 |         } | 
 | 234 |  | 
 | 235 |         // At this stage we have either an empty command queue or the first command in the queue | 
 | 236 |         // has a finite delay. So unless we are exiting it is safe to wait. | 
 | 237 |         if (!exitPending()) { | 
 | 238 |             ALOGV("CommandThread() going to sleep"); | 
 | 239 |             mWaitWorkCV.wait(mLock); | 
 | 240 |         } | 
 | 241 |     } | 
 | 242 |     mLock.unlock(); | 
 | 243 |     return false; | 
 | 244 | } | 
 | 245 |  | 
 | 246 | void AudioFlinger::DeviceEffectManager::CommandThread::sendCommand(sp<Command> command) { | 
 | 247 |     Mutex::Autolock _l(mLock); | 
 | 248 |     mCommands.push_back(command); | 
 | 249 |     mWaitWorkCV.signal(); | 
 | 250 | } | 
 | 251 |  | 
 | 252 | void AudioFlinger::DeviceEffectManager::CommandThread::createAudioPatchCommand( | 
 | 253 |         audio_patch_handle_t handle, const PatchPanel::Patch& patch) | 
 | 254 | { | 
 | 255 |     sp<Command> command = new Command(CREATE_AUDIO_PATCH, new CreateAudioPatchData(handle, patch)); | 
 | 256 |     ALOGV("CommandThread() adding create patch handle %d mHalHandle %d.", handle, patch.mHalHandle); | 
 | 257 |     sendCommand(command); | 
 | 258 | } | 
 | 259 |  | 
 | 260 | void AudioFlinger::DeviceEffectManager::CommandThread::releaseAudioPatchCommand( | 
 | 261 |         audio_patch_handle_t handle) | 
 | 262 | { | 
 | 263 |     sp<Command> command = new Command(RELEASE_AUDIO_PATCH, new ReleaseAudioPatchData(handle)); | 
 | 264 |     ALOGV("CommandThread() adding release patch"); | 
 | 265 |     sendCommand(command); | 
 | 266 | } | 
 | 267 |  | 
 | 268 | void AudioFlinger::DeviceEffectManager::CommandThread::exit() | 
 | 269 | { | 
 | 270 |     ALOGV("CommandThread::exit"); | 
 | 271 |     { | 
 | 272 |         AutoMutex _l(mLock); | 
 | 273 |         requestExit(); | 
 | 274 |         mWaitWorkCV.signal(); | 
 | 275 |     } | 
 | 276 |     // Note that we can call it from the thread loop if all other references have been released | 
 | 277 |     // but it will safely return WOULD_BLOCK in this case | 
 | 278 |     requestExitAndWait(); | 
 | 279 | } | 
 | 280 |  | 
 | 281 | } // namespace android |