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 <cstddef>
19 #include <iterator>
20 #include <memory>
21 #define LOG_TAG "EffectProxy"
22 // #define LOG_NDEBUG 0
23
24 #include <fmq/AidlMessageQueue.h>
25 #include <system/audio_aidl_utils.h>
26 #include <system/audio_effects/aidl_effects_utils.h>
27 #include <utils/Log.h>
28
29 #include "EffectProxy.h"
30
31 using ::aidl::android::hardware::audio::effect::Capability;
32 using ::aidl::android::hardware::audio::effect::CommandId;
33 using ::aidl::android::hardware::audio::effect::Descriptor;
34 using ::aidl::android::hardware::audio::effect::Flags;
35 using ::aidl::android::hardware::audio::effect::IEffect;
36 using ::aidl::android::hardware::audio::effect::IFactory;
37 using ::aidl::android::hardware::audio::effect::Parameter;
38 using ::aidl::android::hardware::audio::effect::State;
39 using ::aidl::android::media::audio::common::AudioUuid;
40
41 namespace android::effect {
42
EffectProxy(const AudioUuid & uuid,const std::vector<Descriptor> & descriptors,const std::shared_ptr<IFactory> & factory)43 EffectProxy::EffectProxy(const AudioUuid& uuid, const std::vector<Descriptor>& descriptors,
44 const std::shared_ptr<IFactory>& factory)
45 : mSharedCapability(buildDescriptorCapability(descriptors)),
46 mDescriptorCommon(buildDescriptorCommon(uuid, descriptors)),
47 mSubEffects(
48 [](const std::vector<Descriptor>& descs, const std::shared_ptr<IFactory>& factory) {
49 std::vector<SubEffect> subEffects;
50 ALOG_ASSERT(factory, "invalid EffectFactory handle");
51 ndk::ScopedAStatus status = ndk::ScopedAStatus::ok();
52 for (const auto& desc : descs) {
53 SubEffect sub({.descriptor = desc});
54 status = factory->createEffect(desc.common.id.uuid, &sub.handle);
55 if (!status.isOk() || !sub.handle) {
56 sub.handle = nullptr;
57 ALOGW("%s create sub-effect %s failed", __func__,
58 ::android::audio::utils::toString(desc.common.id.uuid).c_str());
59 }
60 subEffects.emplace_back(sub);
61 }
62 return subEffects;
63 }(descriptors, factory)),
64 mFactory(factory) {}
65
~EffectProxy()66 EffectProxy::~EffectProxy() {
67 close();
68 destroy();
69 mSubEffects.clear();
70 }
71
destroy()72 ndk::ScopedAStatus EffectProxy::destroy() {
73 ALOGV("%s: %s", __func__,
74 ::android::audio::utils::toString(mDescriptorCommon.id.type).c_str());
75 return runWithAllSubEffects([&](std::shared_ptr<IEffect>& effect) {
76 ndk::ScopedAStatus status = mFactory->destroyEffect(effect);
77 if (status.isOk()) {
78 effect.reset();
79 }
80 return status;
81 });
82 }
83
setOffloadParam(const effect_offload_param_t * offload)84 ndk::ScopedAStatus EffectProxy::setOffloadParam(const effect_offload_param_t* offload) {
85 const auto& itor = std::find_if(mSubEffects.begin(), mSubEffects.end(), [&](const auto& sub) {
86 const auto& desc = sub.descriptor;
87 return offload->isOffload == desc.common.flags.offloadIndication;
88 });
89 if (itor == mSubEffects.end()) {
90 ALOGE("%s no %soffload sub-effect found", __func__, offload->isOffload ? "" : "non-");
91 mActiveSubIdx = 0;
92 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER,
93 "noActiveEffctFound");
94 }
95
96 mActiveSubIdx = std::distance(mSubEffects.begin(), itor);
97 ALOGI("%s: active %soffload sub-effect %zu: %s", __func__,
98 offload->isOffload ? "" : "non-", mActiveSubIdx,
99 ::android::audio::utils::toString(mSubEffects[mActiveSubIdx].descriptor.common.id.uuid)
100 .c_str());
101 return runWithAllSubEffects([&](std::shared_ptr<IEffect>& effect) {
102 return effect->setParameter(Parameter::make<Parameter::offload>(offload->isOffload));
103 });
104 }
105
106 // EffectProxy go over sub-effects and call IEffect interfaces
open(const Parameter::Common & common,const std::optional<Parameter::Specific> & specific,IEffect::OpenEffectReturn * ret __unused)107 ndk::ScopedAStatus EffectProxy::open(const Parameter::Common& common,
108 const std::optional<Parameter::Specific>& specific,
109 IEffect::OpenEffectReturn* ret __unused) {
110 ndk::ScopedAStatus status =
111 ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE, "nullEffectHandle");
112 for (auto& sub : mSubEffects) {
113 IEffect::OpenEffectReturn openReturn;
114 if (!sub.handle || !(status = sub.handle->open(common, specific, &openReturn)).isOk()) {
115 ALOGE("%s: failed to open %p UUID %s", __func__, sub.handle.get(),
116 ::android::audio::utils::toString(sub.descriptor.common.id.uuid).c_str());
117 break;
118 }
119 sub.effectMq.statusQ = std::make_shared<StatusMQ>(openReturn.statusMQ);
120 sub.effectMq.inputQ = std::make_shared<DataMQ>(openReturn.inputDataMQ);
121 sub.effectMq.outputQ = std::make_shared<DataMQ>(openReturn.outputDataMQ);
122 }
123
124 // close all opened effects if failure
125 if (!status.isOk()) {
126 ALOGE("%s: closing all sub-effects with error %s", __func__,
127 status.getDescription().c_str());
128 close();
129 }
130
131 return status;
132 }
133
reopen(OpenEffectReturn * ret __unused)134 ndk::ScopedAStatus EffectProxy::reopen(OpenEffectReturn* ret __unused) {
135 ndk::ScopedAStatus status =
136 ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE, "nullEffectHandle");
137 for (auto& sub : mSubEffects) {
138 IEffect::OpenEffectReturn openReturn;
139 if (!sub.handle || !(status = sub.handle->reopen(&openReturn)).isOk()) {
140 ALOGE("%s: failed to open %p UUID %s", __func__, sub.handle.get(),
141 ::android::audio::utils::toString(sub.descriptor.common.id.uuid).c_str());
142 break;
143 }
144 sub.effectMq.statusQ = std::make_shared<StatusMQ>(openReturn.statusMQ);
145 sub.effectMq.inputQ = std::make_shared<DataMQ>(openReturn.inputDataMQ);
146 sub.effectMq.outputQ = std::make_shared<DataMQ>(openReturn.outputDataMQ);
147 }
148
149 // close all opened effects if failure
150 if (!status.isOk()) {
151 ALOGW("%s: closing all sub-effects with error %s", __func__,
152 status.getDescription().c_str());
153 close();
154 }
155
156 return status;
157 }
158
close()159 ndk::ScopedAStatus EffectProxy::close() {
160 command(CommandId::STOP);
161 return runWithAllSubEffects([&](std::shared_ptr<IEffect>& effect) {
162 return effect->close();
163 });
164 }
165
getDescriptor(Descriptor * desc)166 ndk::ScopedAStatus EffectProxy::getDescriptor(Descriptor* desc) {
167 *desc = mSubEffects[mActiveSubIdx].descriptor;
168 desc->capability = mSharedCapability;
169 desc->common = mDescriptorCommon;
170 return ndk::ScopedAStatus::ok();
171 }
172
buildDescriptor(const AudioUuid & uuid,const std::vector<Descriptor> & subEffectDescs,Descriptor * desc)173 ndk::ScopedAStatus EffectProxy::buildDescriptor(const AudioUuid& uuid,
174 const std::vector<Descriptor>& subEffectDescs,
175 Descriptor* desc) {
176 if (!desc) {
177 ALOGE("%s: null descriptor pointer", __func__);
178 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER, "nullptr");
179 }
180
181 if (subEffectDescs.size() < 2) {
182 ALOGE("%s: proxy need at least 2 sub-effects, got %zu", __func__, subEffectDescs.size());
183 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
184 "needMoreSubEffects");
185 }
186
187 desc->common = buildDescriptorCommon(uuid, subEffectDescs);
188 desc->capability = buildDescriptorCapability(subEffectDescs);
189 return ndk::ScopedAStatus::ok();
190 }
191
192 // Sub-effects are required to have identical features, so here we return the SW sub-effect
193 // descriptor, with the implementation UUID replaced with proxy UUID, and flags setting respect all
194 // sub-effects.
buildDescriptorCommon(const AudioUuid & uuid,const std::vector<Descriptor> & subEffectDescs)195 Descriptor::Common EffectProxy::buildDescriptorCommon(
196 const AudioUuid& uuid, const std::vector<Descriptor>& subEffectDescs) {
197 Descriptor::Common swCommon;
198 const Flags& firstFlag = subEffectDescs[0].common.flags;
199 bool offloadExist = false;
200 for (const auto& desc : subEffectDescs) {
201 if (desc.common.flags.offloadIndication) {
202 offloadExist = true;
203 } else {
204 swCommon = desc.common;
205 }
206 if (desc.common.flags.audioModeIndication != firstFlag.audioModeIndication ||
207 desc.common.flags.audioSourceIndication != firstFlag.audioSourceIndication ||
208 desc.common.flags.sinkMetadataIndication != firstFlag.sinkMetadataIndication ||
209 desc.common.flags.sourceMetadataIndication != firstFlag.sourceMetadataIndication ||
210 desc.common.flags.deviceIndication != firstFlag.deviceIndication) {
211 ALOGW("Inconsistent flags %s vs %s", desc.common.flags.toString().c_str(),
212 firstFlag.toString().c_str());
213 }
214 }
215
216 swCommon.flags.offloadIndication = offloadExist;
217 // replace implementation UUID with proxy UUID.
218 swCommon.id.uuid = uuid;
219 swCommon.id.proxy = std::nullopt;
220 return swCommon;
221 }
222
223 // Build a shared Descriptor capability with all sub-effects.
buildDescriptorCapability(const std::vector<Descriptor> & subEffectDescs)224 Capability EffectProxy::buildDescriptorCapability(const std::vector<Descriptor>& subEffectDescs) {
225 std::optional<Capability> cap = subEffectDescs[0].capability;
226 for (size_t i = 1; i < subEffectDescs.size(); i++) {
227 cap = findSharedCapability(cap.value(), subEffectDescs[i].capability);
228 if (!cap) {
229 ALOGE("%s failed to find the shared capability at %zu", __func__, i);
230 return subEffectDescs[0].capability;
231 }
232 }
233
234 return cap.value();
235 }
236
237 // Handle with active sub-effect first, only send to other sub-effects when success
command(CommandId id)238 ndk::ScopedAStatus EffectProxy::command(CommandId id) {
239 return runWithActiveSubEffectThenOthers(
240 [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus {
241 return effect->command(id);
242 });
243 }
244
245 // Return the active sub-effect state
getState(State * state)246 ndk::ScopedAStatus EffectProxy::getState(State* state) {
247 return runWithActiveSubEffect(
248 [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus {
249 return effect->getState(state);
250 });
251 }
252
253 // Handle with active sub-effect first, only send to other sub-effects when success
setParameter(const Parameter & param)254 ndk::ScopedAStatus EffectProxy::setParameter(const Parameter& param) {
255 return runWithActiveSubEffectThenOthers(
256 [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus {
257 return effect->setParameter(param);
258 });
259 }
260
261 // Return the active sub-effect parameter
getParameter(const Parameter::Id & id,Parameter * param)262 ndk::ScopedAStatus EffectProxy::getParameter(const Parameter::Id& id, Parameter* param) {
263 return runWithActiveSubEffect(
264 [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus {
265 return effect->getParameter(id, param);
266 });
267 }
268
runWithActiveSubEffectThenOthers(std::function<ndk::ScopedAStatus (const std::shared_ptr<IEffect> &)> const & func)269 ndk::ScopedAStatus EffectProxy::runWithActiveSubEffectThenOthers(
270 std::function<ndk::ScopedAStatus(const std::shared_ptr<IEffect>&)> const& func) {
271 ndk::ScopedAStatus status = runWithActiveSubEffect(func);
272 if (!status.isOk()) {
273 ALOGW("%s active sub-effect return error %s", __func__, status.getDescription().c_str());
274 }
275
276 // proceed with others
277 for (size_t i = 0; i < mSubEffects.size(); i++) {
278 if (i == mActiveSubIdx) {
279 continue;
280 }
281 if (!mSubEffects[i].handle) {
282 ALOGW("%s null sub-effect interface for %s", __func__,
283 mSubEffects[i].descriptor.common.id.uuid.toString().c_str());
284 continue;
285 }
286 func(mSubEffects[i].handle);
287 }
288 return status;
289 }
290
runWithActiveSubEffect(std::function<ndk::ScopedAStatus (const std::shared_ptr<IEffect> &)> const & func)291 ndk::ScopedAStatus EffectProxy::runWithActiveSubEffect(
292 std::function<ndk::ScopedAStatus(const std::shared_ptr<IEffect>&)> const& func) {
293 if (!mSubEffects[mActiveSubIdx].handle) {
294 ALOGE("%s null active sub-effect interface, active %s", __func__,
295 mSubEffects[mActiveSubIdx].descriptor.toString().c_str());
296 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER,
297 "activeSubEffectNull");
298 }
299 return func(mSubEffects[mActiveSubIdx].handle);
300 }
301
runWithAllSubEffects(std::function<ndk::ScopedAStatus (std::shared_ptr<IEffect> &)> const & func)302 ndk::ScopedAStatus EffectProxy::runWithAllSubEffects(
303 std::function<ndk::ScopedAStatus(std::shared_ptr<IEffect>&)> const& func) {
304 ndk::ScopedAStatus status = ndk::ScopedAStatus::ok();
305 // proceed with others if active sub-effect success
306 for (auto& sub : mSubEffects) {
307 if (!sub.handle) {
308 ALOGW("%s null sub-effect interface %s", __func__, sub.descriptor.toString().c_str());
309 continue;
310 }
311 ndk::ScopedAStatus temp = func(sub.handle);
312 if (!temp.isOk()) {
313 status = ndk::ScopedAStatus::fromStatus(temp.getStatus());
314 }
315 }
316 return status;
317 }
318
isBypassing() const319 bool EffectProxy::isBypassing() const {
320 return mSubEffects[mActiveSubIdx].descriptor.common.flags.bypass;
321 }
322
isTunnel() const323 bool EffectProxy::isTunnel() const {
324 return mSubEffects[mActiveSubIdx].descriptor.common.flags.hwAcceleratorMode ==
325 Flags::HardwareAccelerator::TUNNEL;
326 }
327
dump(int fd,const char ** args,uint32_t numArgs)328 binder_status_t EffectProxy::dump(int fd, const char** args, uint32_t numArgs) {
329 const std::string dumpString = toString();
330 write(fd, dumpString.c_str(), dumpString.size());
331
332 return runWithAllSubEffects([&](std::shared_ptr<IEffect>& effect) {
333 return ndk::ScopedAStatus::fromStatus(effect->dump(fd, args, numArgs));
334 })
335 .getStatus();
336 }
337
toString(size_t level) const338 std::string EffectProxy::toString(size_t level) const {
339 std::string prefixSpace(level, ' ');
340 std::string ss = prefixSpace + "EffectProxy:\n";
341 prefixSpace += " ";
342 base::StringAppendF(&ss, "%sDescriptorCommon: %s\n", prefixSpace.c_str(),
343 mDescriptorCommon.toString().c_str());
344 base::StringAppendF(&ss, "%sDescriptorCapability: %s\n", prefixSpace.c_str(),
345 mSharedCapability.toString().c_str());
346 base::StringAppendF(&ss, "%sActiveSubIdx: %zu\n", prefixSpace.c_str(), mActiveSubIdx);
347 base::StringAppendF(&ss, "%sAllSubEffects:\n", prefixSpace.c_str());
348 for (size_t i = 0; i < mSubEffects.size(); i++) {
349 base::StringAppendF(&ss, "%s[%zu] - Handle: %p, %s\n", prefixSpace.c_str(), i,
350 mSubEffects[i].handle.get(),
351 mSubEffects[i].descriptor.toString().c_str());
352 }
353 return ss;
354 }
355
356 } // namespace android::effect
357