blob: b61532dbb42a04c3b208450e56d49cd2100b10ba [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>
23#include <utils/Log.h>
24
25#include "EffectProxy.h"
26
27using ::aidl::android::hardware::audio::effect::CommandId;
28using ::aidl::android::hardware::audio::effect::Descriptor;
29using ::aidl::android::hardware::audio::effect::Flags;
30using ::aidl::android::hardware::audio::effect::IEffect;
31using ::aidl::android::hardware::audio::effect::IFactory;
32using ::aidl::android::hardware::audio::effect::Parameter;
33using ::aidl::android::hardware::audio::effect::State;
34using ::aidl::android::media::audio::common::AudioUuid;
35
36namespace android {
37namespace effect {
38
39EffectProxy::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
49EffectProxy::~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.
56ndk::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
94ndk::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
115ndk::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
126const IEffect::OpenEffectReturn* EffectProxy::getEffectReturnParam() {
127 return &std::get<SubEffectTupleIndex::RETURN>(mSubEffects[mActiveSub]);
128}
129
130ndk::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
152ndk::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);
Shunkai Yao8b5c6742023-04-07 22:35:10 +0000161 if (!effect || !(status = effect->open(common, specific, &openRet)).isOk()) {
Shunkai Yao5c718342023-02-23 23:49:51 +0000162 ALOGE("%s: failed to open UUID %s", __func__, sub.first.uuid.toString().c_str());
163 break;
164 }
165 }
166
167 // close all opened effects if failure
168 if (!status.isOk()) {
169 close();
170 }
171
172 return status;
173}
174
175ndk::ScopedAStatus EffectProxy::close() {
176 ALOGV("%s: %s", __func__, mIdentity.type.toString().c_str());
177 return runWithAllSubEffects([&](std::shared_ptr<IEffect>& effect) {
178 return effect->close();
179 });
180}
181
182ndk::ScopedAStatus EffectProxy::getDescriptor(Descriptor* desc) {
183 if (!desc) {
184 ALOGE("%s: nuull descriptor pointer", __func__);
185 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER, "nullptr");
186 }
187
188 auto& activeSubEffect = std::get<SubEffectTupleIndex::HANDLE>(mSubEffects[mActiveSub]);
189 // return initial descriptor if no active sub-effect exist
190 if (!activeSubEffect) {
191 desc->common.id = mIdentity;
192 desc->common.flags = mSubFlags;
193 desc->common.name = "Proxy";
194 desc->common.implementor = "AOSP";
195 } else {
196 *desc = std::get<SubEffectTupleIndex::DESCRIPTOR>(mSubEffects[mActiveSub]);
197 desc->common.id = mIdentity;
198 }
199
200 ALOGI("%s with %s", __func__, desc->toString().c_str());
201 return ndk::ScopedAStatus::ok();
202}
203
204// Handle with active sub-effect first, only send to other sub-effects when success
205ndk::ScopedAStatus EffectProxy::command(CommandId id) {
206 ALOGV("%s: %s, command %s", __func__, mIdentity.type.toString().c_str(),
207 android::internal::ToString(id).c_str());
208 return runWithActiveSubEffectThenOthers(
209 [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus {
210 return effect->command(id);
211 });
212}
213
214// Return the active sub-effect state
215ndk::ScopedAStatus EffectProxy::getState(State* state) {
216 return runWithActiveSubEffect(
217 [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus {
218 return effect->getState(state);
219 });
220}
221
222// Handle with active sub-effect first, only send to other sub-effects when success
223ndk::ScopedAStatus EffectProxy::setParameter(const Parameter& param) {
224 return runWithActiveSubEffectThenOthers(
225 [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus {
226 return effect->setParameter(param);
227 });
228}
229
230// Return the active sub-effect parameter
231ndk::ScopedAStatus EffectProxy::getParameter(const Parameter::Id& id, Parameter* param) {
232 return runWithActiveSubEffect(
233 [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus {
234 return effect->getParameter(id, param);
235 });
236}
237
238ndk::ScopedAStatus EffectProxy::runWithActiveSubEffectThenOthers(
239 std::function<ndk::ScopedAStatus(const std::shared_ptr<IEffect>&)> const& func) {
240 ndk::ScopedAStatus status = runWithActiveSubEffect(func);
241 if (!status.isOk()) {
242 return status;
243 }
244
245 // proceed with others if active sub-effect success
246 for (const auto& sub : mSubEffects) {
247 auto& effect = std::get<SubEffectTupleIndex::HANDLE>(sub.second);
248 if (sub.first != mActiveSub) {
249 if (!effect) {
250 ALOGE("%s null sub-effect interface for %s", __func__,
251 sub.first.toString().c_str());
252 continue;
253 }
254 func(effect);
255 }
256 }
257 return status;
258}
259
260ndk::ScopedAStatus EffectProxy::runWithActiveSubEffect(
261 std::function<ndk::ScopedAStatus(const std::shared_ptr<IEffect>&)> const& func) {
262 auto& effect = std::get<SubEffectTupleIndex::HANDLE>(mSubEffects[mActiveSub]);
263 if (!effect) {
264 ALOGE("%s null active sub-effect interface, active %s", __func__,
265 mActiveSub.toString().c_str());
266 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER,
267 "activeSubEffectNull");
268 }
269 return func(effect);
270}
271
272ndk::ScopedAStatus EffectProxy::runWithAllSubEffects(
273 std::function<ndk::ScopedAStatus(std::shared_ptr<IEffect>&)> const& func) {
274 ndk::ScopedAStatus status = ndk::ScopedAStatus::ok();
275 // proceed with others if active sub-effect success
276 for (auto& sub : mSubEffects) {
277 auto& effect = std::get<SubEffectTupleIndex::HANDLE>(sub.second);
278 if (!effect) {
279 ALOGW("%s null sub-effect interface for %s", __func__, sub.first.toString().c_str());
280 continue;
281 }
282 ndk::ScopedAStatus temp = func(effect);
283 if (!temp.isOk()) {
284 status = ndk::ScopedAStatus::fromStatus(temp.getStatus());
285 }
286 }
287 return status;
288}
289
290} // namespace effect
291} // namespace android