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