Shunkai Yao | 5c71834 | 2023-02-23 23:49:51 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2023 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include <algorithm> |
| 18 | #include <memory> |
| 19 | #define LOG_TAG "EffectProxy" |
| 20 | //#define LOG_NDEBUG 0 |
| 21 | |
| 22 | #include <fmq/AidlMessageQueue.h> |
Shunkai Yao | 007caf0 | 2023-05-17 00:52:54 +0000 | [diff] [blame] | 23 | #include <system/audio_aidl_utils.h> |
Shunkai Yao | 5c71834 | 2023-02-23 23:49:51 +0000 | [diff] [blame] | 24 | #include <utils/Log.h> |
| 25 | |
| 26 | #include "EffectProxy.h" |
| 27 | |
| 28 | using ::aidl::android::hardware::audio::effect::CommandId; |
| 29 | using ::aidl::android::hardware::audio::effect::Descriptor; |
| 30 | using ::aidl::android::hardware::audio::effect::Flags; |
| 31 | using ::aidl::android::hardware::audio::effect::IEffect; |
| 32 | using ::aidl::android::hardware::audio::effect::IFactory; |
| 33 | using ::aidl::android::hardware::audio::effect::Parameter; |
| 34 | using ::aidl::android::hardware::audio::effect::State; |
| 35 | using ::aidl::android::media::audio::common::AudioUuid; |
Shunkai Yao | 007caf0 | 2023-05-17 00:52:54 +0000 | [diff] [blame] | 36 | using ::android::audio::utils::toString; |
Shunkai Yao | 5c71834 | 2023-02-23 23:49:51 +0000 | [diff] [blame] | 37 | |
| 38 | namespace android { |
| 39 | namespace effect { |
| 40 | |
| 41 | EffectProxy::EffectProxy(const Descriptor::Identity& id, const std::shared_ptr<IFactory>& factory) |
| 42 | : mIdentity([](const Descriptor::Identity& subId) { |
| 43 | // update EffectProxy implementation UUID to the sub-effect proxy UUID |
| 44 | ALOG_ASSERT(subId.proxy.has_value(), "Sub-effect Identity must have valid proxy UUID"); |
| 45 | Descriptor::Identity tempId = subId; |
| 46 | tempId.uuid = subId.proxy.value(); |
| 47 | return tempId; |
| 48 | }(id)), |
| 49 | mFactory(factory) {} |
| 50 | |
| 51 | EffectProxy::~EffectProxy() { |
| 52 | close(); |
| 53 | destroy(); |
| 54 | mSubEffects.clear(); |
| 55 | } |
| 56 | |
| 57 | // sub effect must have same proxy UUID as EffectProxy, and the type UUID must match. |
| 58 | ndk::ScopedAStatus EffectProxy::addSubEffect(const Descriptor& sub) { |
Shunkai Yao | 007caf0 | 2023-05-17 00:52:54 +0000 | [diff] [blame] | 59 | ALOGV("%s: %s", __func__, toString(mIdentity.type).c_str()); |
Shunkai Yao | 5c71834 | 2023-02-23 23:49:51 +0000 | [diff] [blame] | 60 | if (0 != mSubEffects.count(sub.common.id) || !sub.common.id.proxy.has_value() || |
| 61 | sub.common.id.proxy.value() != mIdentity.uuid) { |
| 62 | ALOGE("%s sub effect already exist or mismatch %s", __func__, sub.toString().c_str()); |
| 63 | return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, |
| 64 | "illegalSubEffect"); |
| 65 | } |
| 66 | |
| 67 | // not create sub-effect yet |
| 68 | std::get<SubEffectTupleIndex::HANDLE>(mSubEffects[sub.common.id]) = nullptr; |
| 69 | std::get<SubEffectTupleIndex::DESCRIPTOR>(mSubEffects[sub.common.id]) = sub; |
| 70 | // set the last added sub-effect to active before setOffloadParam() |
| 71 | mActiveSub = sub.common.id; |
| 72 | ALOGI("%s add %s to proxy %s flag %s", __func__, mActiveSub.toString().c_str(), |
| 73 | mIdentity.toString().c_str(), sub.common.flags.toString().c_str()); |
| 74 | |
| 75 | if (sub.common.flags.hwAcceleratorMode == Flags::HardwareAccelerator::TUNNEL) { |
| 76 | mSubFlags.hwAcceleratorMode = Flags::HardwareAccelerator::TUNNEL; |
| 77 | } |
| 78 | |
| 79 | // initial flag values before we know which sub-effect to active (with setOffloadParam) |
| 80 | // same as HIDL EffectProxy flags |
| 81 | mSubFlags.type = Flags::Type::INSERT; |
| 82 | mSubFlags.insert = Flags::Insert::LAST; |
| 83 | mSubFlags.volume = Flags::Volume::CTRL; |
| 84 | |
| 85 | // set indication if any sub-effect indication was set |
| 86 | mSubFlags.offloadIndication |= sub.common.flags.offloadIndication; |
| 87 | mSubFlags.deviceIndication |= sub.common.flags.deviceIndication; |
| 88 | mSubFlags.audioModeIndication |= sub.common.flags.audioModeIndication; |
| 89 | mSubFlags.audioSourceIndication |= sub.common.flags.audioSourceIndication; |
| 90 | |
| 91 | // set bypass when all sub-effects are bypassing |
| 92 | mSubFlags.bypass &= sub.common.flags.bypass; |
| 93 | return ndk::ScopedAStatus::ok(); |
| 94 | } |
| 95 | |
| 96 | ndk::ScopedAStatus EffectProxy::create() { |
Shunkai Yao | 007caf0 | 2023-05-17 00:52:54 +0000 | [diff] [blame] | 97 | ALOGV("%s: %s", __func__, toString(mIdentity.type).c_str()); |
Shunkai Yao | 5c71834 | 2023-02-23 23:49:51 +0000 | [diff] [blame] | 98 | ndk::ScopedAStatus status = ndk::ScopedAStatus::ok(); |
| 99 | |
| 100 | for (auto& sub : mSubEffects) { |
| 101 | auto& effectHandle = std::get<SubEffectTupleIndex::HANDLE>(sub.second); |
Shunkai Yao | 007caf0 | 2023-05-17 00:52:54 +0000 | [diff] [blame] | 102 | ALOGI("%s sub-effect %s", __func__, toString(sub.first.uuid).c_str()); |
Shunkai Yao | 5c71834 | 2023-02-23 23:49:51 +0000 | [diff] [blame] | 103 | status = mFactory->createEffect(sub.first.uuid, &effectHandle); |
| 104 | if (!status.isOk() || !effectHandle) { |
Shunkai Yao | 007caf0 | 2023-05-17 00:52:54 +0000 | [diff] [blame] | 105 | ALOGE("%s sub-effect failed %s", __func__, toString(sub.first.uuid).c_str()); |
Shunkai Yao | 5c71834 | 2023-02-23 23:49:51 +0000 | [diff] [blame] | 106 | break; |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | // destroy all created effects if failure |
| 111 | if (!status.isOk()) { |
| 112 | destroy(); |
| 113 | } |
| 114 | return status; |
| 115 | } |
| 116 | |
| 117 | ndk::ScopedAStatus EffectProxy::destroy() { |
Shunkai Yao | 007caf0 | 2023-05-17 00:52:54 +0000 | [diff] [blame] | 118 | ALOGV("%s: %s", __func__, toString(mIdentity.type).c_str()); |
Shunkai Yao | 5c71834 | 2023-02-23 23:49:51 +0000 | [diff] [blame] | 119 | return runWithAllSubEffects([&](std::shared_ptr<IEffect>& effect) { |
| 120 | ndk::ScopedAStatus status = mFactory->destroyEffect(effect); |
| 121 | if (status.isOk()) { |
| 122 | effect.reset(); |
| 123 | } |
| 124 | return status; |
| 125 | }); |
| 126 | } |
| 127 | |
| 128 | const IEffect::OpenEffectReturn* EffectProxy::getEffectReturnParam() { |
| 129 | return &std::get<SubEffectTupleIndex::RETURN>(mSubEffects[mActiveSub]); |
| 130 | } |
| 131 | |
| 132 | ndk::ScopedAStatus EffectProxy::setOffloadParam(const effect_offload_param_t* offload) { |
| 133 | const auto& itor = std::find_if(mSubEffects.begin(), mSubEffects.end(), [&](const auto& sub) { |
| 134 | const auto& desc = std::get<SubEffectTupleIndex::DESCRIPTOR>(sub.second); |
| 135 | ALOGI("%s: isOffload %d sub-effect: %s, flags %s", __func__, offload->isOffload, |
Shunkai Yao | 007caf0 | 2023-05-17 00:52:54 +0000 | [diff] [blame] | 136 | toString(desc.common.id.uuid).c_str(), desc.common.flags.toString().c_str()); |
Shunkai Yao | 5c71834 | 2023-02-23 23:49:51 +0000 | [diff] [blame] | 137 | return offload->isOffload == |
| 138 | (desc.common.flags.hwAcceleratorMode == Flags::HardwareAccelerator::TUNNEL); |
| 139 | }); |
| 140 | if (itor == mSubEffects.end()) { |
| 141 | ALOGE("%s no %soffload sub-effect found", __func__, offload->isOffload ? "" : "non-"); |
| 142 | return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER, |
| 143 | "noActiveEffctFound"); |
| 144 | } |
| 145 | |
| 146 | mActiveSub = itor->first; |
| 147 | ALOGI("%s: active %soffload sub-effect: %s, flags %s", __func__, |
Shunkai Yao | 007caf0 | 2023-05-17 00:52:54 +0000 | [diff] [blame] | 148 | offload->isOffload ? "" : "non-", toString(mActiveSub.uuid).c_str(), |
Shunkai Yao | 5c71834 | 2023-02-23 23:49:51 +0000 | [diff] [blame] | 149 | std::get<SubEffectTupleIndex::DESCRIPTOR>(itor->second).common.flags.toString().c_str()); |
| 150 | return ndk::ScopedAStatus::ok(); |
| 151 | } |
| 152 | |
| 153 | // EffectProxy go over sub-effects and call IEffect interfaces |
| 154 | ndk::ScopedAStatus EffectProxy::open(const Parameter::Common& common, |
| 155 | const std::optional<Parameter::Specific>& specific, |
| 156 | IEffect::OpenEffectReturn* ret __unused) { |
Shunkai Yao | 007caf0 | 2023-05-17 00:52:54 +0000 | [diff] [blame] | 157 | ALOGV("%s: %s", __func__, toString(mIdentity.type).c_str()); |
Shunkai Yao | 5c71834 | 2023-02-23 23:49:51 +0000 | [diff] [blame] | 158 | ndk::ScopedAStatus status = ndk::ScopedAStatus::fromExceptionCodeWithMessage( |
| 159 | EX_ILLEGAL_ARGUMENT, "nullEffectHandle"); |
| 160 | for (auto& sub : mSubEffects) { |
| 161 | auto& effect = std::get<SubEffectTupleIndex::HANDLE>(sub.second); |
| 162 | auto& openRet = std::get<SubEffectTupleIndex::RETURN>(sub.second); |
Shunkai Yao | 8b5c674 | 2023-04-07 22:35:10 +0000 | [diff] [blame] | 163 | if (!effect || !(status = effect->open(common, specific, &openRet)).isOk()) { |
Shunkai Yao | 007caf0 | 2023-05-17 00:52:54 +0000 | [diff] [blame] | 164 | ALOGE("%s: failed to open UUID %s", __func__, toString(sub.first.uuid).c_str()); |
Shunkai Yao | 5c71834 | 2023-02-23 23:49:51 +0000 | [diff] [blame] | 165 | break; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // close all opened effects if failure |
| 170 | if (!status.isOk()) { |
| 171 | close(); |
| 172 | } |
| 173 | |
| 174 | return status; |
| 175 | } |
| 176 | |
| 177 | ndk::ScopedAStatus EffectProxy::close() { |
Shunkai Yao | 007caf0 | 2023-05-17 00:52:54 +0000 | [diff] [blame] | 178 | ALOGV("%s: %s", __func__, toString(mIdentity.type).c_str()); |
Shunkai Yao | 5c71834 | 2023-02-23 23:49:51 +0000 | [diff] [blame] | 179 | return runWithAllSubEffects([&](std::shared_ptr<IEffect>& effect) { |
| 180 | return effect->close(); |
| 181 | }); |
| 182 | } |
| 183 | |
| 184 | ndk::ScopedAStatus EffectProxy::getDescriptor(Descriptor* desc) { |
| 185 | if (!desc) { |
| 186 | ALOGE("%s: nuull descriptor pointer", __func__); |
| 187 | return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER, "nullptr"); |
| 188 | } |
| 189 | |
| 190 | auto& activeSubEffect = std::get<SubEffectTupleIndex::HANDLE>(mSubEffects[mActiveSub]); |
| 191 | // return initial descriptor if no active sub-effect exist |
| 192 | if (!activeSubEffect) { |
| 193 | desc->common.id = mIdentity; |
| 194 | desc->common.flags = mSubFlags; |
| 195 | desc->common.name = "Proxy"; |
| 196 | desc->common.implementor = "AOSP"; |
| 197 | } else { |
| 198 | *desc = std::get<SubEffectTupleIndex::DESCRIPTOR>(mSubEffects[mActiveSub]); |
| 199 | desc->common.id = mIdentity; |
| 200 | } |
| 201 | |
| 202 | ALOGI("%s with %s", __func__, desc->toString().c_str()); |
| 203 | return ndk::ScopedAStatus::ok(); |
| 204 | } |
| 205 | |
| 206 | // Handle with active sub-effect first, only send to other sub-effects when success |
| 207 | ndk::ScopedAStatus EffectProxy::command(CommandId id) { |
Shunkai Yao | 007caf0 | 2023-05-17 00:52:54 +0000 | [diff] [blame] | 208 | ALOGV("%s: %s, command %s", __func__, toString(mIdentity.type).c_str(), |
Shunkai Yao | 5c71834 | 2023-02-23 23:49:51 +0000 | [diff] [blame] | 209 | android::internal::ToString(id).c_str()); |
| 210 | return runWithActiveSubEffectThenOthers( |
| 211 | [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus { |
| 212 | return effect->command(id); |
| 213 | }); |
| 214 | } |
| 215 | |
| 216 | // Return the active sub-effect state |
| 217 | ndk::ScopedAStatus EffectProxy::getState(State* state) { |
| 218 | return runWithActiveSubEffect( |
| 219 | [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus { |
| 220 | return effect->getState(state); |
| 221 | }); |
| 222 | } |
| 223 | |
| 224 | // Handle with active sub-effect first, only send to other sub-effects when success |
| 225 | ndk::ScopedAStatus EffectProxy::setParameter(const Parameter& param) { |
| 226 | return runWithActiveSubEffectThenOthers( |
| 227 | [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus { |
| 228 | return effect->setParameter(param); |
| 229 | }); |
| 230 | } |
| 231 | |
| 232 | // Return the active sub-effect parameter |
| 233 | ndk::ScopedAStatus EffectProxy::getParameter(const Parameter::Id& id, Parameter* param) { |
| 234 | return runWithActiveSubEffect( |
| 235 | [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus { |
| 236 | return effect->getParameter(id, param); |
| 237 | }); |
| 238 | } |
| 239 | |
| 240 | ndk::ScopedAStatus EffectProxy::runWithActiveSubEffectThenOthers( |
| 241 | std::function<ndk::ScopedAStatus(const std::shared_ptr<IEffect>&)> const& func) { |
| 242 | ndk::ScopedAStatus status = runWithActiveSubEffect(func); |
| 243 | if (!status.isOk()) { |
| 244 | return status; |
| 245 | } |
| 246 | |
| 247 | // proceed with others if active sub-effect success |
| 248 | for (const auto& sub : mSubEffects) { |
| 249 | auto& effect = std::get<SubEffectTupleIndex::HANDLE>(sub.second); |
| 250 | if (sub.first != mActiveSub) { |
| 251 | if (!effect) { |
| 252 | ALOGE("%s null sub-effect interface for %s", __func__, |
| 253 | sub.first.toString().c_str()); |
| 254 | continue; |
| 255 | } |
| 256 | func(effect); |
| 257 | } |
| 258 | } |
| 259 | return status; |
| 260 | } |
| 261 | |
| 262 | ndk::ScopedAStatus EffectProxy::runWithActiveSubEffect( |
| 263 | std::function<ndk::ScopedAStatus(const std::shared_ptr<IEffect>&)> const& func) { |
| 264 | auto& effect = std::get<SubEffectTupleIndex::HANDLE>(mSubEffects[mActiveSub]); |
| 265 | if (!effect) { |
| 266 | ALOGE("%s null active sub-effect interface, active %s", __func__, |
| 267 | mActiveSub.toString().c_str()); |
| 268 | return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER, |
| 269 | "activeSubEffectNull"); |
| 270 | } |
| 271 | return func(effect); |
| 272 | } |
| 273 | |
| 274 | ndk::ScopedAStatus EffectProxy::runWithAllSubEffects( |
| 275 | std::function<ndk::ScopedAStatus(std::shared_ptr<IEffect>&)> const& func) { |
| 276 | ndk::ScopedAStatus status = ndk::ScopedAStatus::ok(); |
| 277 | // proceed with others if active sub-effect success |
| 278 | for (auto& sub : mSubEffects) { |
| 279 | auto& effect = std::get<SubEffectTupleIndex::HANDLE>(sub.second); |
| 280 | if (!effect) { |
| 281 | ALOGW("%s null sub-effect interface for %s", __func__, sub.first.toString().c_str()); |
| 282 | continue; |
| 283 | } |
| 284 | ndk::ScopedAStatus temp = func(effect); |
| 285 | if (!temp.isOk()) { |
| 286 | status = ndk::ScopedAStatus::fromStatus(temp.getStatus()); |
| 287 | } |
| 288 | } |
| 289 | return status; |
| 290 | } |
| 291 | |
| 292 | } // namespace effect |
| 293 | } // namespace android |