xref: /aosp_15_r20/frameworks/av/media/libaudiohal/impl/EffectProxy.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
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