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 <utils/Log.h>
27
28 #include "EffectProxy.h"
29
30 using ::aidl::android::hardware::audio::effect::Capability;
31 using ::aidl::android::hardware::audio::effect::CommandId;
32 using ::aidl::android::hardware::audio::effect::Descriptor;
33 using ::aidl::android::hardware::audio::effect::Flags;
34 using ::aidl::android::hardware::audio::effect::IEffect;
35 using ::aidl::android::hardware::audio::effect::IFactory;
36 using ::aidl::android::hardware::audio::effect::Parameter;
37 using ::aidl::android::hardware::audio::effect::State;
38 using ::aidl::android::media::audio::common::AudioUuid;
39
40 namespace android::effect {
41
EffectProxy(const AudioUuid & uuid,const std::vector<Descriptor> & descriptors,const std::shared_ptr<IFactory> & factory)42 EffectProxy::EffectProxy(const AudioUuid& uuid, const std::vector<Descriptor>& descriptors,
43 const std::shared_ptr<IFactory>& factory)
44 : mDescriptorCommon(buildDescriptorCommon(uuid, descriptors)),
45 mSubEffects(
46 [](const std::vector<Descriptor>& descs, const std::shared_ptr<IFactory>& factory) {
47 std::vector<SubEffect> subEffects;
48 ALOG_ASSERT(factory, "invalid EffectFactory handle");
49 ndk::ScopedAStatus status = ndk::ScopedAStatus::ok();
50 for (const auto& desc : descs) {
51 SubEffect sub({.descriptor = desc});
52 status = factory->createEffect(desc.common.id.uuid, &sub.handle);
53 if (!status.isOk() || !sub.handle) {
54 ALOGW("%s create sub-effect %s failed", __func__,
55 ::android::audio::utils::toString(desc.common.id.uuid).c_str());
56 }
57 subEffects.emplace_back(sub);
58 }
59 return subEffects;
60 }(descriptors, factory)),
61 mFactory(factory) {}
62
~EffectProxy()63 EffectProxy::~EffectProxy() {
64 close();
65 destroy();
66 mSubEffects.clear();
67 }
68
destroy()69 ndk::ScopedAStatus EffectProxy::destroy() {
70 ALOGV("%s: %s", __func__,
71 ::android::audio::utils::toString(mDescriptorCommon.id.type).c_str());
72 return runWithAllSubEffects([&](std::shared_ptr<IEffect>& effect) {
73 ndk::ScopedAStatus status = mFactory->destroyEffect(effect);
74 if (status.isOk()) {
75 effect.reset();
76 }
77 return status;
78 });
79 }
80
setOffloadParam(const effect_offload_param_t * offload)81 ndk::ScopedAStatus EffectProxy::setOffloadParam(const effect_offload_param_t* offload) {
82 const auto& itor = std::find_if(mSubEffects.begin(), mSubEffects.end(), [&](const auto& sub) {
83 const auto& desc = sub.descriptor;
84 return offload->isOffload ==
85 (desc.common.flags.hwAcceleratorMode == Flags::HardwareAccelerator::TUNNEL);
86 });
87 if (itor == mSubEffects.end()) {
88 ALOGE("%s no %soffload sub-effect found", __func__, offload->isOffload ? "" : "non-");
89 mActiveSubIdx = 0;
90 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER,
91 "noActiveEffctFound");
92 }
93
94 const size_t newIndex = std::distance(mSubEffects.begin(), itor);
95 mActiveSubIdx = newIndex;
96
97 ALOGI("%s: active %soffload sub-effect %zu descriptor: %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 = ndk::ScopedAStatus::fromExceptionCodeWithMessage(
111 EX_ILLEGAL_ARGUMENT, "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
close()134 ndk::ScopedAStatus EffectProxy::close() {
135 return runWithAllSubEffects([&](std::shared_ptr<IEffect>& effect) {
136 return effect->close();
137 });
138 }
139
getDescriptor(Descriptor * desc)140 ndk::ScopedAStatus EffectProxy::getDescriptor(Descriptor* desc) {
141 *desc = mSubEffects[mActiveSubIdx].descriptor;
142 desc->common.id.uuid = desc->common.id.proxy.value();
143 return ndk::ScopedAStatus::ok();
144 }
145
buildDescriptor(const AudioUuid & uuid,const std::vector<Descriptor> & subEffectDescs,Descriptor * desc)146 ndk::ScopedAStatus EffectProxy::buildDescriptor(const AudioUuid& uuid,
147 const std::vector<Descriptor>& subEffectDescs,
148 Descriptor* desc) {
149 if (!desc) {
150 ALOGE("%s: null descriptor pointer", __func__);
151 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER, "nullptr");
152 }
153
154 if (subEffectDescs.size() < 2) {
155 ALOGE("%s: proxy need at least 2 sub-effects, got %zu", __func__, subEffectDescs.size());
156 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
157 "needMoreSubEffects");
158 }
159
160 desc->common = buildDescriptorCommon(uuid, subEffectDescs);
161 return ndk::ScopedAStatus::ok();
162 }
163
buildDescriptorCommon(const AudioUuid & uuid,const std::vector<Descriptor> & subEffectDescs)164 Descriptor::Common EffectProxy::buildDescriptorCommon(
165 const AudioUuid& uuid, const std::vector<Descriptor>& subEffectDescs) {
166 Descriptor::Common common;
167 for (const auto& desc : subEffectDescs) {
168 if (desc.common.flags.hwAcceleratorMode == Flags::HardwareAccelerator::TUNNEL) {
169 common.flags.hwAcceleratorMode = Flags::HardwareAccelerator::TUNNEL;
170 }
171
172 // set indication if any sub-effect indication was set
173 common.flags.offloadIndication |= desc.common.flags.offloadIndication;
174 common.flags.deviceIndication |= desc.common.flags.deviceIndication;
175 common.flags.audioModeIndication |= desc.common.flags.audioModeIndication;
176 common.flags.audioSourceIndication |= desc.common.flags.audioSourceIndication;
177 }
178
179 // copy type UUID from any of sub-effects, all sub-effects should have same type
180 common.id.type = subEffectDescs[0].common.id.type;
181 // replace implementation UUID with proxy UUID.
182 common.id.uuid = uuid;
183 common.id.proxy = std::nullopt;
184 common.name = "Proxy";
185 common.implementor = "AOSP";
186 return common;
187 }
188
189 // Handle with active sub-effect first, only send to other sub-effects when success
command(CommandId id)190 ndk::ScopedAStatus EffectProxy::command(CommandId id) {
191 return runWithActiveSubEffectThenOthers(
192 [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus {
193 return effect->command(id);
194 });
195 }
196
197 // Return the active sub-effect state
getState(State * state)198 ndk::ScopedAStatus EffectProxy::getState(State* state) {
199 return runWithActiveSubEffect(
200 [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus {
201 return effect->getState(state);
202 });
203 }
204
205 // Handle with active sub-effect first, only send to other sub-effects when success
setParameter(const Parameter & param)206 ndk::ScopedAStatus EffectProxy::setParameter(const Parameter& param) {
207 return runWithActiveSubEffectThenOthers(
208 [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus {
209 return effect->setParameter(param);
210 });
211 }
212
213 // Return the active sub-effect parameter
getParameter(const Parameter::Id & id,Parameter * param)214 ndk::ScopedAStatus EffectProxy::getParameter(const Parameter::Id& id, Parameter* param) {
215 return runWithActiveSubEffect(
216 [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus {
217 return effect->getParameter(id, param);
218 });
219 }
220
runWithActiveSubEffectThenOthers(std::function<ndk::ScopedAStatus (const std::shared_ptr<IEffect> &)> const & func)221 ndk::ScopedAStatus EffectProxy::runWithActiveSubEffectThenOthers(
222 std::function<ndk::ScopedAStatus(const std::shared_ptr<IEffect>&)> const& func) {
223 ndk::ScopedAStatus status = runWithActiveSubEffect(func);
224 if (!status.isOk()) {
225 ALOGE("%s active sub-effect return error %s", __func__, status.getDescription().c_str());
226 }
227
228 // proceed with others
229 for (size_t i = 0; i < mSubEffects.size(); i++) {
230 if (i == mActiveSubIdx) {
231 continue;
232 }
233 if (!mSubEffects[i].handle) {
234 ALOGE("%s null sub-effect interface for %s", __func__,
235 mSubEffects[i].descriptor.common.id.uuid.toString().c_str());
236 continue;
237 }
238 func(mSubEffects[i].handle);
239 }
240 return status;
241 }
242
runWithActiveSubEffect(std::function<ndk::ScopedAStatus (const std::shared_ptr<IEffect> &)> const & func)243 ndk::ScopedAStatus EffectProxy::runWithActiveSubEffect(
244 std::function<ndk::ScopedAStatus(const std::shared_ptr<IEffect>&)> const& func) {
245 if (!mSubEffects[mActiveSubIdx].handle) {
246 ALOGE("%s null active sub-effect interface, active %s", __func__,
247 mSubEffects[mActiveSubIdx].descriptor.toString().c_str());
248 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER,
249 "activeSubEffectNull");
250 }
251 return func(mSubEffects[mActiveSubIdx].handle);
252 }
253
runWithAllSubEffects(std::function<ndk::ScopedAStatus (std::shared_ptr<IEffect> &)> const & func)254 ndk::ScopedAStatus EffectProxy::runWithAllSubEffects(
255 std::function<ndk::ScopedAStatus(std::shared_ptr<IEffect>&)> const& func) {
256 ndk::ScopedAStatus status = ndk::ScopedAStatus::ok();
257 // proceed with others if active sub-effect success
258 for (auto& sub : mSubEffects) {
259 if (!sub.handle) {
260 ALOGW("%s null sub-effect interface %s", __func__, sub.descriptor.toString().c_str());
261 continue;
262 }
263 ndk::ScopedAStatus temp = func(sub.handle);
264 if (!temp.isOk()) {
265 status = ndk::ScopedAStatus::fromStatus(temp.getStatus());
266 }
267 }
268 return status;
269 }
270
isBypassing() const271 bool EffectProxy::isBypassing() const {
272 return mSubEffects[mActiveSubIdx].descriptor.common.flags.bypass;
273 }
274
dump(int fd,const char ** args,uint32_t numArgs)275 binder_status_t EffectProxy::dump(int fd, const char** args, uint32_t numArgs) {
276 const std::string dumpString = toString();
277 write(fd, dumpString.c_str(), dumpString.size());
278
279 return runWithAllSubEffects([&](std::shared_ptr<IEffect>& effect) {
280 return ndk::ScopedAStatus::fromStatus(effect->dump(fd, args, numArgs));
281 })
282 .getStatus();
283 }
284
toString(size_t level) const285 std::string EffectProxy::toString(size_t level) const {
286 std::string prefixSpace(level, ' ');
287 std::string ss = prefixSpace + "EffectProxy:\n";
288 prefixSpace += " ";
289 base::StringAppendF(&ss, "%sDescriptorCommon: %s\n", prefixSpace.c_str(),
290 mDescriptorCommon.toString().c_str());
291 base::StringAppendF(&ss, "%sActiveSubIdx: %zu\n", prefixSpace.c_str(), mActiveSubIdx);
292 base::StringAppendF(&ss, "%sAllSubEffects:\n", prefixSpace.c_str());
293 for (size_t i = 0; i < mSubEffects.size(); i++) {
294 base::StringAppendF(&ss, "%s[%zu] - Handle: %p, %s\n", prefixSpace.c_str(), i,
295 mSubEffects[i].handle ? mSubEffects[i].handle.get() : nullptr,
296 mSubEffects[i].descriptor.toString().c_str());
297 }
298 return ss;
299 }
300
301 } // namespace android::effect
302