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