blob: 3bb045b454599f8aaf11f2ca846f3ec6e88089be [file] [log] [blame]
Shunkai Yao5c718342023-02-23 23:49:51 +00001/*
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 Yao007caf02023-05-17 00:52:54 +000023#include <system/audio_aidl_utils.h>
Shunkai Yao5c718342023-02-23 23:49:51 +000024#include <utils/Log.h>
25
26#include "EffectProxy.h"
27
28using ::aidl::android::hardware::audio::effect::CommandId;
29using ::aidl::android::hardware::audio::effect::Descriptor;
30using ::aidl::android::hardware::audio::effect::Flags;
31using ::aidl::android::hardware::audio::effect::IEffect;
32using ::aidl::android::hardware::audio::effect::IFactory;
33using ::aidl::android::hardware::audio::effect::Parameter;
34using ::aidl::android::hardware::audio::effect::State;
35using ::aidl::android::media::audio::common::AudioUuid;
Shunkai Yao007caf02023-05-17 00:52:54 +000036using ::android::audio::utils::toString;
Shunkai Yao5c718342023-02-23 23:49:51 +000037
38namespace android {
39namespace effect {
40
41EffectProxy::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
51EffectProxy::~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.
58ndk::ScopedAStatus EffectProxy::addSubEffect(const Descriptor& sub) {
Shunkai Yao007caf02023-05-17 00:52:54 +000059 ALOGV("%s: %s", __func__, toString(mIdentity.type).c_str());
Shunkai Yao5c718342023-02-23 23:49:51 +000060 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
96ndk::ScopedAStatus EffectProxy::create() {
Shunkai Yao007caf02023-05-17 00:52:54 +000097 ALOGV("%s: %s", __func__, toString(mIdentity.type).c_str());
Shunkai Yao5c718342023-02-23 23:49:51 +000098 ndk::ScopedAStatus status = ndk::ScopedAStatus::ok();
99
100 for (auto& sub : mSubEffects) {
101 auto& effectHandle = std::get<SubEffectTupleIndex::HANDLE>(sub.second);
Shunkai Yao007caf02023-05-17 00:52:54 +0000102 ALOGI("%s sub-effect %s", __func__, toString(sub.first.uuid).c_str());
Shunkai Yao5c718342023-02-23 23:49:51 +0000103 status = mFactory->createEffect(sub.first.uuid, &effectHandle);
104 if (!status.isOk() || !effectHandle) {
Shunkai Yao007caf02023-05-17 00:52:54 +0000105 ALOGE("%s sub-effect failed %s", __func__, toString(sub.first.uuid).c_str());
Shunkai Yao5c718342023-02-23 23:49:51 +0000106 break;
107 }
108 }
109
110 // destroy all created effects if failure
111 if (!status.isOk()) {
112 destroy();
113 }
114 return status;
115}
116
117ndk::ScopedAStatus EffectProxy::destroy() {
Shunkai Yao007caf02023-05-17 00:52:54 +0000118 ALOGV("%s: %s", __func__, toString(mIdentity.type).c_str());
Shunkai Yao5c718342023-02-23 23:49:51 +0000119 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
128const IEffect::OpenEffectReturn* EffectProxy::getEffectReturnParam() {
129 return &std::get<SubEffectTupleIndex::RETURN>(mSubEffects[mActiveSub]);
130}
131
132ndk::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 Yao007caf02023-05-17 00:52:54 +0000136 toString(desc.common.id.uuid).c_str(), desc.common.flags.toString().c_str());
Shunkai Yao5c718342023-02-23 23:49:51 +0000137 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 Yao007caf02023-05-17 00:52:54 +0000148 offload->isOffload ? "" : "non-", toString(mActiveSub.uuid).c_str(),
Shunkai Yao5c718342023-02-23 23:49:51 +0000149 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
154ndk::ScopedAStatus EffectProxy::open(const Parameter::Common& common,
155 const std::optional<Parameter::Specific>& specific,
156 IEffect::OpenEffectReturn* ret __unused) {
Shunkai Yao007caf02023-05-17 00:52:54 +0000157 ALOGV("%s: %s", __func__, toString(mIdentity.type).c_str());
Shunkai Yao5c718342023-02-23 23:49:51 +0000158 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 Yao8b5c6742023-04-07 22:35:10 +0000163 if (!effect || !(status = effect->open(common, specific, &openRet)).isOk()) {
Shunkai Yao007caf02023-05-17 00:52:54 +0000164 ALOGE("%s: failed to open UUID %s", __func__, toString(sub.first.uuid).c_str());
Shunkai Yao5c718342023-02-23 23:49:51 +0000165 break;
166 }
167 }
168
169 // close all opened effects if failure
170 if (!status.isOk()) {
171 close();
172 }
173
174 return status;
175}
176
177ndk::ScopedAStatus EffectProxy::close() {
Shunkai Yao007caf02023-05-17 00:52:54 +0000178 ALOGV("%s: %s", __func__, toString(mIdentity.type).c_str());
Shunkai Yao5c718342023-02-23 23:49:51 +0000179 return runWithAllSubEffects([&](std::shared_ptr<IEffect>& effect) {
180 return effect->close();
181 });
182}
183
184ndk::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
207ndk::ScopedAStatus EffectProxy::command(CommandId id) {
Shunkai Yao007caf02023-05-17 00:52:54 +0000208 ALOGV("%s: %s, command %s", __func__, toString(mIdentity.type).c_str(),
Shunkai Yao5c718342023-02-23 23:49:51 +0000209 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
217ndk::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
225ndk::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
233ndk::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
240ndk::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
262ndk::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
274ndk::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