blob: a1318792c6b4d15655ae6d678f4ef92ad7e9a854 [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>
Shunkai Yao79f98742023-05-03 23:54:43 +000018#include <iterator>
Shunkai Yao5c718342023-02-23 23:49:51 +000019#include <memory>
20#define LOG_TAG "EffectProxy"
Shunkai Yao79f98742023-05-03 23:54:43 +000021// #define LOG_NDEBUG 0
Shunkai Yao5c718342023-02-23 23:49:51 +000022
23#include <fmq/AidlMessageQueue.h>
Shunkai Yao007caf02023-05-17 00:52:54 +000024#include <system/audio_aidl_utils.h>
Shunkai Yao5c718342023-02-23 23:49:51 +000025#include <utils/Log.h>
26
27#include "EffectProxy.h"
28
Shunkai Yao79f98742023-05-03 23:54:43 +000029using ::aidl::android::hardware::audio::effect::Capability;
Shunkai Yao5c718342023-02-23 23:49:51 +000030using ::aidl::android::hardware::audio::effect::CommandId;
31using ::aidl::android::hardware::audio::effect::Descriptor;
32using ::aidl::android::hardware::audio::effect::Flags;
33using ::aidl::android::hardware::audio::effect::IEffect;
34using ::aidl::android::hardware::audio::effect::IFactory;
35using ::aidl::android::hardware::audio::effect::Parameter;
36using ::aidl::android::hardware::audio::effect::State;
37using ::aidl::android::media::audio::common::AudioUuid;
38
Shunkai Yao79f98742023-05-03 23:54:43 +000039namespace android::effect {
Shunkai Yao5c718342023-02-23 23:49:51 +000040
Shunkai Yao79f98742023-05-03 23:54:43 +000041EffectProxy::EffectProxy(const AudioUuid& uuid, const std::vector<Descriptor>& descriptors,
42 const std::shared_ptr<IFactory>& factory)
43 : mDescriptorCommon(buildDescriptorCommon(uuid, descriptors)),
44 mSubEffects(
45 [](const std::vector<Descriptor>& descs, const std::shared_ptr<IFactory>& factory) {
46 std::vector<SubEffect> subEffects;
47 ALOG_ASSERT(factory, "invalid EffectFactory handle");
48 ndk::ScopedAStatus status = ndk::ScopedAStatus::ok();
49 for (const auto& desc : descs) {
50 SubEffect sub({.descriptor = desc});
51 status = factory->createEffect(desc.common.id.uuid, &sub.handle);
52 if (!status.isOk() || !sub.handle) {
53 sub.handle = nullptr;
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)),
Shunkai Yao5c718342023-02-23 23:49:51 +000061 mFactory(factory) {}
62
63EffectProxy::~EffectProxy() {
64 close();
65 destroy();
66 mSubEffects.clear();
67}
68
Shunkai Yao5c718342023-02-23 23:49:51 +000069ndk::ScopedAStatus EffectProxy::destroy() {
Shunkai Yao79f98742023-05-03 23:54:43 +000070 ALOGV("%s: %s", __func__,
71 ::android::audio::utils::toString(mDescriptorCommon.id.type).c_str());
Shunkai Yao5c718342023-02-23 23:49:51 +000072 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
Shunkai Yao5c718342023-02-23 23:49:51 +000081ndk::ScopedAStatus EffectProxy::setOffloadParam(const effect_offload_param_t* offload) {
82 const auto& itor = std::find_if(mSubEffects.begin(), mSubEffects.end(), [&](const auto& sub) {
Shunkai Yao79f98742023-05-03 23:54:43 +000083 const auto& desc = sub.descriptor;
Shunkai Yao5c718342023-02-23 23:49:51 +000084 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-");
Shunkai Yao79f98742023-05-03 23:54:43 +000089 mActiveSubIdx = 0;
Shunkai Yao5c718342023-02-23 23:49:51 +000090 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER,
91 "noActiveEffctFound");
92 }
93
Shunkai Yao79f98742023-05-03 23:54:43 +000094 mActiveSubIdx = std::distance(mSubEffects.begin(), itor);
95 ALOGV("%s: active %soffload sub-effect %zu descriptor: %s", __func__,
96 offload->isOffload ? "" : "non-", mActiveSubIdx,
97 ::android::audio::utils::toString(mSubEffects[mActiveSubIdx].descriptor.common.id.uuid)
98 .c_str());
Shunkai Yao5c718342023-02-23 23:49:51 +000099 return ndk::ScopedAStatus::ok();
100}
101
102// EffectProxy go over sub-effects and call IEffect interfaces
103ndk::ScopedAStatus EffectProxy::open(const Parameter::Common& common,
104 const std::optional<Parameter::Specific>& specific,
105 IEffect::OpenEffectReturn* ret __unused) {
Shunkai Yao5c718342023-02-23 23:49:51 +0000106 ndk::ScopedAStatus status = ndk::ScopedAStatus::fromExceptionCodeWithMessage(
107 EX_ILLEGAL_ARGUMENT, "nullEffectHandle");
108 for (auto& sub : mSubEffects) {
Shunkai Yao79f98742023-05-03 23:54:43 +0000109 IEffect::OpenEffectReturn openReturn;
110 if (!sub.handle || !(status = sub.handle->open(common, specific, &openReturn)).isOk()) {
111 ALOGE("%s: failed to open %p UUID %s", __func__, sub.handle.get(),
112 ::android::audio::utils::toString(sub.descriptor.common.id.uuid).c_str());
Shunkai Yao5c718342023-02-23 23:49:51 +0000113 break;
114 }
Shunkai Yao79f98742023-05-03 23:54:43 +0000115 sub.effectMq.statusQ = std::make_shared<StatusMQ>(openReturn.statusMQ);
116 sub.effectMq.inputQ = std::make_shared<DataMQ>(openReturn.inputDataMQ);
117 sub.effectMq.outputQ = std::make_shared<DataMQ>(openReturn.outputDataMQ);
Shunkai Yao5c718342023-02-23 23:49:51 +0000118 }
119
120 // close all opened effects if failure
121 if (!status.isOk()) {
Shunkai Yao79f98742023-05-03 23:54:43 +0000122 ALOGE("%s: closing all sub-effects with error %s", __func__,
123 status.getDescription().c_str());
Shunkai Yao5c718342023-02-23 23:49:51 +0000124 close();
125 }
126
127 return status;
128}
129
130ndk::ScopedAStatus EffectProxy::close() {
Shunkai Yao5c718342023-02-23 23:49:51 +0000131 return runWithAllSubEffects([&](std::shared_ptr<IEffect>& effect) {
132 return effect->close();
133 });
134}
135
136ndk::ScopedAStatus EffectProxy::getDescriptor(Descriptor* desc) {
Shunkai Yaodf7bd8b2023-07-13 23:25:38 +0000137 *desc = mSubEffects[mActiveSubIdx].descriptor;
138 desc->common.id.uuid = desc->common.id.proxy.value();
Shunkai Yao79f98742023-05-03 23:54:43 +0000139 return ndk::ScopedAStatus::ok();
140}
141
142ndk::ScopedAStatus EffectProxy::buildDescriptor(const AudioUuid& uuid,
143 const std::vector<Descriptor>& subEffectDescs,
144 Descriptor* desc) {
Shunkai Yao5c718342023-02-23 23:49:51 +0000145 if (!desc) {
Shunkai Yao79f98742023-05-03 23:54:43 +0000146 ALOGE("%s: null descriptor pointer", __func__);
Shunkai Yao5c718342023-02-23 23:49:51 +0000147 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER, "nullptr");
148 }
149
Shunkai Yao79f98742023-05-03 23:54:43 +0000150 if (subEffectDescs.size() < 2) {
151 ALOGE("%s: proxy need at least 2 sub-effects, got %zu", __func__, subEffectDescs.size());
152 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
153 "needMoreSubEffects");
Shunkai Yao5c718342023-02-23 23:49:51 +0000154 }
155
Shunkai Yao79f98742023-05-03 23:54:43 +0000156 desc->common = buildDescriptorCommon(uuid, subEffectDescs);
Shunkai Yao5c718342023-02-23 23:49:51 +0000157 return ndk::ScopedAStatus::ok();
158}
159
Shunkai Yao79f98742023-05-03 23:54:43 +0000160Descriptor::Common EffectProxy::buildDescriptorCommon(
161 const AudioUuid& uuid, const std::vector<Descriptor>& subEffectDescs) {
162 Descriptor::Common common;
163
164 for (const auto& desc : subEffectDescs) {
165 if (desc.common.flags.hwAcceleratorMode == Flags::HardwareAccelerator::TUNNEL) {
166 common.flags.hwAcceleratorMode = Flags::HardwareAccelerator::TUNNEL;
167 }
168
Shunkai Yaodf7bd8b2023-07-13 23:25:38 +0000169 // initial flag values before we know which sub-effect to active (with setOffloadParam)
170 // same as HIDL EffectProxy flags
171 common.flags.type = Flags::Type::INSERT;
172 common.flags.insert = Flags::Insert::LAST;
173 common.flags.volume = Flags::Volume::NONE;
174
Shunkai Yao79f98742023-05-03 23:54:43 +0000175 // set indication if any sub-effect indication was set
176 common.flags.offloadIndication |= desc.common.flags.offloadIndication;
177 common.flags.deviceIndication |= desc.common.flags.deviceIndication;
178 common.flags.audioModeIndication |= desc.common.flags.audioModeIndication;
179 common.flags.audioSourceIndication |= desc.common.flags.audioSourceIndication;
180 }
181
182 // initial flag values before we know which sub-effect to active (with setOffloadParam)
183 // same as HIDL EffectProxy flags
184 common.flags.type = Flags::Type::INSERT;
185 common.flags.insert = Flags::Insert::LAST;
186 common.flags.volume = Flags::Volume::CTRL;
187
188 // copy type UUID from any of sub-effects, all sub-effects should have same type
189 common.id.type = subEffectDescs[0].common.id.type;
190 // replace implementation UUID with proxy UUID.
191 common.id.uuid = uuid;
192 common.id.proxy = std::nullopt;
193 common.name = "Proxy";
194 common.implementor = "AOSP";
195 return common;
196}
197
Shunkai Yao5c718342023-02-23 23:49:51 +0000198// Handle with active sub-effect first, only send to other sub-effects when success
199ndk::ScopedAStatus EffectProxy::command(CommandId id) {
Shunkai Yao5c718342023-02-23 23:49:51 +0000200 return runWithActiveSubEffectThenOthers(
201 [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus {
202 return effect->command(id);
203 });
204}
205
206// Return the active sub-effect state
207ndk::ScopedAStatus EffectProxy::getState(State* state) {
208 return runWithActiveSubEffect(
209 [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus {
210 return effect->getState(state);
211 });
212}
213
214// Handle with active sub-effect first, only send to other sub-effects when success
215ndk::ScopedAStatus EffectProxy::setParameter(const Parameter& param) {
216 return runWithActiveSubEffectThenOthers(
217 [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus {
218 return effect->setParameter(param);
219 });
220}
221
222// Return the active sub-effect parameter
223ndk::ScopedAStatus EffectProxy::getParameter(const Parameter::Id& id, Parameter* param) {
224 return runWithActiveSubEffect(
225 [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus {
226 return effect->getParameter(id, param);
227 });
228}
229
230ndk::ScopedAStatus EffectProxy::runWithActiveSubEffectThenOthers(
231 std::function<ndk::ScopedAStatus(const std::shared_ptr<IEffect>&)> const& func) {
232 ndk::ScopedAStatus status = runWithActiveSubEffect(func);
233 if (!status.isOk()) {
Shunkai Yao79f98742023-05-03 23:54:43 +0000234 ALOGE("%s active sub-effect return error %s", __func__, status.getDescription().c_str());
Shunkai Yao5c718342023-02-23 23:49:51 +0000235 }
236
Shunkai Yao79f98742023-05-03 23:54:43 +0000237 // proceed with others
238 for (size_t i = 0; i < mSubEffects.size() && i != mActiveSubIdx; i++) {
239 if (!mSubEffects[i].handle) {
240 ALOGE("%s null sub-effect interface for %s", __func__,
241 mSubEffects[i].descriptor.common.id.uuid.toString().c_str());
242 continue;
Shunkai Yao5c718342023-02-23 23:49:51 +0000243 }
Shunkai Yao79f98742023-05-03 23:54:43 +0000244 func(mSubEffects[i].handle);
Shunkai Yao5c718342023-02-23 23:49:51 +0000245 }
246 return status;
247}
248
249ndk::ScopedAStatus EffectProxy::runWithActiveSubEffect(
250 std::function<ndk::ScopedAStatus(const std::shared_ptr<IEffect>&)> const& func) {
Shunkai Yao79f98742023-05-03 23:54:43 +0000251 if (!mSubEffects[mActiveSubIdx].handle) {
Shunkai Yao5c718342023-02-23 23:49:51 +0000252 ALOGE("%s null active sub-effect interface, active %s", __func__,
Shunkai Yao79f98742023-05-03 23:54:43 +0000253 mSubEffects[mActiveSubIdx].descriptor.toString().c_str());
Shunkai Yao5c718342023-02-23 23:49:51 +0000254 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER,
255 "activeSubEffectNull");
256 }
Shunkai Yao79f98742023-05-03 23:54:43 +0000257 return func(mSubEffects[mActiveSubIdx].handle);
Shunkai Yao5c718342023-02-23 23:49:51 +0000258}
259
260ndk::ScopedAStatus EffectProxy::runWithAllSubEffects(
261 std::function<ndk::ScopedAStatus(std::shared_ptr<IEffect>&)> const& func) {
262 ndk::ScopedAStatus status = ndk::ScopedAStatus::ok();
263 // proceed with others if active sub-effect success
264 for (auto& sub : mSubEffects) {
Shunkai Yao79f98742023-05-03 23:54:43 +0000265 if (!sub.handle) {
266 ALOGW("%s null sub-effect interface %s", __func__, sub.descriptor.toString().c_str());
Shunkai Yao5c718342023-02-23 23:49:51 +0000267 continue;
268 }
Shunkai Yao79f98742023-05-03 23:54:43 +0000269 ndk::ScopedAStatus temp = func(sub.handle);
Shunkai Yao5c718342023-02-23 23:49:51 +0000270 if (!temp.isOk()) {
271 status = ndk::ScopedAStatus::fromStatus(temp.getStatus());
272 }
273 }
274 return status;
275}
276
Shunkai Yao79f98742023-05-03 23:54:43 +0000277bool EffectProxy::isBypassing() const {
278 return mSubEffects[mActiveSubIdx].descriptor.common.flags.bypass;
279}
280
281binder_status_t EffectProxy::dump(int fd, const char** args, uint32_t numArgs) {
282 const std::string dumpString = toString();
283 write(fd, dumpString.c_str(), dumpString.size());
284
285 return runWithAllSubEffects([&](std::shared_ptr<IEffect>& effect) {
286 return ndk::ScopedAStatus::fromStatus(effect->dump(fd, args, numArgs));
287 })
288 .getStatus();
289}
290
291std::string EffectProxy::toString(size_t level) const {
292 std::string prefixSpace(level, ' ');
293 std::string ss = prefixSpace + "EffectProxy:\n";
294 prefixSpace += " ";
295 base::StringAppendF(&ss, "%sDescriptorCommon: %s\n", prefixSpace.c_str(),
296 mDescriptorCommon.toString().c_str());
297 base::StringAppendF(&ss, "%sActiveSubIdx: %zu\n", prefixSpace.c_str(), mActiveSubIdx);
298 base::StringAppendF(&ss, "%sAllSubEffects:\n", prefixSpace.c_str());
299 for (size_t i = 0; i < mSubEffects.size(); i++) {
300 base::StringAppendF(&ss, "%s[%zu] - Handle: %p, %s\n", prefixSpace.c_str(), i,
301 mSubEffects[i].handle.get(),
302 mSubEffects[i].descriptor.toString().c_str());
303 }
304 return ss;
305}
306
307} // namespace android::effect