blob: 7835dd9c335b606cdeb15bf54324c6dd2d3dfce1 [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 Yao849ba4c2023-07-11 18:10:18 +000018#include <cstddef>
Shunkai Yao79f98742023-05-03 23:54:43 +000019#include <iterator>
Shunkai Yao5c718342023-02-23 23:49:51 +000020#include <memory>
21#define LOG_TAG "EffectProxy"
Shunkai Yao79f98742023-05-03 23:54:43 +000022// #define LOG_NDEBUG 0
Shunkai Yao5c718342023-02-23 23:49:51 +000023
24#include <fmq/AidlMessageQueue.h>
Shunkai Yao007caf02023-05-17 00:52:54 +000025#include <system/audio_aidl_utils.h>
Shunkai Yao5c718342023-02-23 23:49:51 +000026#include <utils/Log.h>
27
28#include "EffectProxy.h"
29
Shunkai Yao79f98742023-05-03 23:54:43 +000030using ::aidl::android::hardware::audio::effect::Capability;
Shunkai Yao5c718342023-02-23 23:49:51 +000031using ::aidl::android::hardware::audio::effect::CommandId;
32using ::aidl::android::hardware::audio::effect::Descriptor;
33using ::aidl::android::hardware::audio::effect::Flags;
34using ::aidl::android::hardware::audio::effect::IEffect;
35using ::aidl::android::hardware::audio::effect::IFactory;
36using ::aidl::android::hardware::audio::effect::Parameter;
37using ::aidl::android::hardware::audio::effect::State;
38using ::aidl::android::media::audio::common::AudioUuid;
39
Shunkai Yao79f98742023-05-03 23:54:43 +000040namespace android::effect {
Shunkai Yao5c718342023-02-23 23:49:51 +000041
Shunkai Yao79f98742023-05-03 23:54:43 +000042EffectProxy::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)),
Shunkai Yao5c718342023-02-23 23:49:51 +000062 mFactory(factory) {}
63
64EffectProxy::~EffectProxy() {
65 close();
66 destroy();
67 mSubEffects.clear();
68}
69
Shunkai Yao5c718342023-02-23 23:49:51 +000070ndk::ScopedAStatus EffectProxy::destroy() {
Shunkai Yao79f98742023-05-03 23:54:43 +000071 ALOGV("%s: %s", __func__,
72 ::android::audio::utils::toString(mDescriptorCommon.id.type).c_str());
Shunkai Yao5c718342023-02-23 23:49:51 +000073 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
Shunkai Yao5c718342023-02-23 23:49:51 +000082ndk::ScopedAStatus EffectProxy::setOffloadParam(const effect_offload_param_t* offload) {
83 const auto& itor = std::find_if(mSubEffects.begin(), mSubEffects.end(), [&](const auto& sub) {
Shunkai Yao79f98742023-05-03 23:54:43 +000084 const auto& desc = sub.descriptor;
Shunkai Yao1b7efb02024-04-11 23:37:00 +000085 return offload->isOffload == desc.common.flags.offloadIndication;
Shunkai Yao5c718342023-02-23 23:49:51 +000086 });
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 Yao4fe66442023-07-25 21:51:24 +000094 mActiveSubIdx = std::distance(mSubEffects.begin(), itor);
Shunkai Yao1b7efb02024-04-11 23:37:00 +000095 ALOGI("%s: active %soffload sub-effect %zu: %s", __func__,
Shunkai Yao79f98742023-05-03 23:54:43 +000096 offload->isOffload ? "" : "non-", mActiveSubIdx,
97 ::android::audio::utils::toString(mSubEffects[mActiveSubIdx].descriptor.common.id.uuid)
98 .c_str());
Shunkai Yao849ba4c2023-07-11 18:10:18 +000099 return runWithAllSubEffects([&](std::shared_ptr<IEffect>& effect) {
100 return effect->setParameter(Parameter::make<Parameter::offload>(offload->isOffload));
101 });
Shunkai Yao5c718342023-02-23 23:49:51 +0000102}
103
104// EffectProxy go over sub-effects and call IEffect interfaces
105ndk::ScopedAStatus EffectProxy::open(const Parameter::Common& common,
106 const std::optional<Parameter::Specific>& specific,
107 IEffect::OpenEffectReturn* ret __unused) {
Shunkai Yaoe67f8722024-01-02 20:24:54 +0000108 ndk::ScopedAStatus status =
109 ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE, "nullEffectHandle");
Shunkai Yao5c718342023-02-23 23:49:51 +0000110 for (auto& sub : mSubEffects) {
Shunkai Yao79f98742023-05-03 23:54:43 +0000111 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());
Shunkai Yao5c718342023-02-23 23:49:51 +0000115 break;
116 }
Shunkai Yao79f98742023-05-03 23:54:43 +0000117 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);
Shunkai Yao5c718342023-02-23 23:49:51 +0000120 }
121
122 // close all opened effects if failure
123 if (!status.isOk()) {
Shunkai Yao79f98742023-05-03 23:54:43 +0000124 ALOGE("%s: closing all sub-effects with error %s", __func__,
125 status.getDescription().c_str());
Shunkai Yao5c718342023-02-23 23:49:51 +0000126 close();
127 }
128
129 return status;
130}
131
Shunkai Yaoe67f8722024-01-02 20:24:54 +0000132ndk::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()) {
Shunkai Yaoac61ee92024-03-14 21:57:46 +0000149 ALOGW("%s: closing all sub-effects with error %s", __func__,
Shunkai Yaoe67f8722024-01-02 20:24:54 +0000150 status.getDescription().c_str());
151 close();
152 }
153
154 return status;
155}
156
Shunkai Yao5c718342023-02-23 23:49:51 +0000157ndk::ScopedAStatus EffectProxy::close() {
Shunkai Yao5c718342023-02-23 23:49:51 +0000158 return runWithAllSubEffects([&](std::shared_ptr<IEffect>& effect) {
159 return effect->close();
160 });
161}
162
163ndk::ScopedAStatus EffectProxy::getDescriptor(Descriptor* desc) {
Shunkai Yaodf7bd8b2023-07-13 23:25:38 +0000164 *desc = mSubEffects[mActiveSubIdx].descriptor;
Shunkai Yao1b7efb02024-04-11 23:37:00 +0000165 desc->common = mDescriptorCommon;
Shunkai Yao79f98742023-05-03 23:54:43 +0000166 return ndk::ScopedAStatus::ok();
167}
168
169ndk::ScopedAStatus EffectProxy::buildDescriptor(const AudioUuid& uuid,
170 const std::vector<Descriptor>& subEffectDescs,
171 Descriptor* desc) {
Shunkai Yao5c718342023-02-23 23:49:51 +0000172 if (!desc) {
Shunkai Yao79f98742023-05-03 23:54:43 +0000173 ALOGE("%s: null descriptor pointer", __func__);
Shunkai Yao5c718342023-02-23 23:49:51 +0000174 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER, "nullptr");
175 }
176
Shunkai Yao79f98742023-05-03 23:54:43 +0000177 if (subEffectDescs.size() < 2) {
178 ALOGE("%s: proxy need at least 2 sub-effects, got %zu", __func__, subEffectDescs.size());
179 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
180 "needMoreSubEffects");
Shunkai Yao5c718342023-02-23 23:49:51 +0000181 }
182
Shunkai Yao79f98742023-05-03 23:54:43 +0000183 desc->common = buildDescriptorCommon(uuid, subEffectDescs);
Shunkai Yao5c718342023-02-23 23:49:51 +0000184 return ndk::ScopedAStatus::ok();
185}
186
Shunkai Yao1b7efb02024-04-11 23:37:00 +0000187// Sub-effects are required to have identical features, so here we return the SW sub-effect
188// descriptor, with the implementation UUID replaced with proxy UUID, and flags setting respect all
189// sub-effects.
Shunkai Yao79f98742023-05-03 23:54:43 +0000190Descriptor::Common EffectProxy::buildDescriptorCommon(
191 const AudioUuid& uuid, const std::vector<Descriptor>& subEffectDescs) {
Shunkai Yao1b7efb02024-04-11 23:37:00 +0000192 Descriptor::Common swCommon;
193 const Flags& firstFlag = subEffectDescs[0].common.flags;
194 bool offloadExist = false;
Shunkai Yao79f98742023-05-03 23:54:43 +0000195 for (const auto& desc : subEffectDescs) {
Shunkai Yao1b7efb02024-04-11 23:37:00 +0000196 if (desc.common.flags.offloadIndication) {
197 offloadExist = true;
198 } else {
199 swCommon = desc.common;
Shunkai Yao79f98742023-05-03 23:54:43 +0000200 }
Shunkai Yao1b7efb02024-04-11 23:37:00 +0000201 if (desc.common.flags.audioModeIndication != firstFlag.audioModeIndication ||
202 desc.common.flags.audioSourceIndication != firstFlag.audioSourceIndication ||
203 desc.common.flags.sinkMetadataIndication != firstFlag.sinkMetadataIndication ||
204 desc.common.flags.sourceMetadataIndication != firstFlag.sourceMetadataIndication ||
205 desc.common.flags.deviceIndication != firstFlag.deviceIndication) {
206 ALOGW("Inconsistent flags %s vs %s", desc.common.flags.toString().c_str(),
207 firstFlag.toString().c_str());
Shunkai Yaoff5eb3c2023-11-10 23:50:57 +0000208 }
Jaideep Sharma2dcfadd2024-03-05 13:22:18 +0530209 // set to AUXILIARY if any sub-effect is of AUXILIARY type
210 if (desc.common.flags.type == Flags::Type::AUXILIARY) {
211 common.flags.type = Flags::Type::AUXILIARY;
212 }
Shunkai Yao79f98742023-05-03 23:54:43 +0000213 }
214
Shunkai Yao1b7efb02024-04-11 23:37:00 +0000215 swCommon.flags.offloadIndication = offloadExist;
Shunkai Yao79f98742023-05-03 23:54:43 +0000216 // replace implementation UUID with proxy UUID.
Shunkai Yao1b7efb02024-04-11 23:37:00 +0000217 swCommon.id.uuid = uuid;
218 swCommon.id.proxy = std::nullopt;
219 return swCommon;
Shunkai Yao79f98742023-05-03 23:54:43 +0000220}
221
Shunkai Yao5c718342023-02-23 23:49:51 +0000222// Handle with active sub-effect first, only send to other sub-effects when success
223ndk::ScopedAStatus EffectProxy::command(CommandId id) {
Shunkai Yao5c718342023-02-23 23:49:51 +0000224 return runWithActiveSubEffectThenOthers(
225 [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus {
226 return effect->command(id);
227 });
228}
229
230// Return the active sub-effect state
231ndk::ScopedAStatus EffectProxy::getState(State* state) {
232 return runWithActiveSubEffect(
233 [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus {
234 return effect->getState(state);
235 });
236}
237
238// Handle with active sub-effect first, only send to other sub-effects when success
239ndk::ScopedAStatus EffectProxy::setParameter(const Parameter& param) {
240 return runWithActiveSubEffectThenOthers(
241 [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus {
242 return effect->setParameter(param);
243 });
244}
245
246// Return the active sub-effect parameter
247ndk::ScopedAStatus EffectProxy::getParameter(const Parameter::Id& id, Parameter* param) {
248 return runWithActiveSubEffect(
249 [&](const std::shared_ptr<IEffect>& effect) -> ndk::ScopedAStatus {
250 return effect->getParameter(id, param);
251 });
252}
253
254ndk::ScopedAStatus EffectProxy::runWithActiveSubEffectThenOthers(
255 std::function<ndk::ScopedAStatus(const std::shared_ptr<IEffect>&)> const& func) {
256 ndk::ScopedAStatus status = runWithActiveSubEffect(func);
257 if (!status.isOk()) {
Shunkai Yaoac61ee92024-03-14 21:57:46 +0000258 ALOGW("%s active sub-effect return error %s", __func__, status.getDescription().c_str());
Shunkai Yao5c718342023-02-23 23:49:51 +0000259 }
260
Shunkai Yao79f98742023-05-03 23:54:43 +0000261 // proceed with others
Jaideep Sharmad8231042023-08-14 13:03:18 +0530262 for (size_t i = 0; i < mSubEffects.size(); i++) {
263 if (i == mActiveSubIdx) {
264 continue;
265 }
Shunkai Yao79f98742023-05-03 23:54:43 +0000266 if (!mSubEffects[i].handle) {
Shunkai Yaoac61ee92024-03-14 21:57:46 +0000267 ALOGW("%s null sub-effect interface for %s", __func__,
Shunkai Yao79f98742023-05-03 23:54:43 +0000268 mSubEffects[i].descriptor.common.id.uuid.toString().c_str());
269 continue;
Shunkai Yao5c718342023-02-23 23:49:51 +0000270 }
Shunkai Yao79f98742023-05-03 23:54:43 +0000271 func(mSubEffects[i].handle);
Shunkai Yao5c718342023-02-23 23:49:51 +0000272 }
273 return status;
274}
275
276ndk::ScopedAStatus EffectProxy::runWithActiveSubEffect(
277 std::function<ndk::ScopedAStatus(const std::shared_ptr<IEffect>&)> const& func) {
Shunkai Yao79f98742023-05-03 23:54:43 +0000278 if (!mSubEffects[mActiveSubIdx].handle) {
Shunkai Yao5c718342023-02-23 23:49:51 +0000279 ALOGE("%s null active sub-effect interface, active %s", __func__,
Shunkai Yao79f98742023-05-03 23:54:43 +0000280 mSubEffects[mActiveSubIdx].descriptor.toString().c_str());
Shunkai Yao5c718342023-02-23 23:49:51 +0000281 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER,
282 "activeSubEffectNull");
283 }
Shunkai Yao79f98742023-05-03 23:54:43 +0000284 return func(mSubEffects[mActiveSubIdx].handle);
Shunkai Yao5c718342023-02-23 23:49:51 +0000285}
286
287ndk::ScopedAStatus EffectProxy::runWithAllSubEffects(
288 std::function<ndk::ScopedAStatus(std::shared_ptr<IEffect>&)> const& func) {
289 ndk::ScopedAStatus status = ndk::ScopedAStatus::ok();
290 // proceed with others if active sub-effect success
291 for (auto& sub : mSubEffects) {
Shunkai Yao79f98742023-05-03 23:54:43 +0000292 if (!sub.handle) {
293 ALOGW("%s null sub-effect interface %s", __func__, sub.descriptor.toString().c_str());
Shunkai Yao5c718342023-02-23 23:49:51 +0000294 continue;
295 }
Shunkai Yao79f98742023-05-03 23:54:43 +0000296 ndk::ScopedAStatus temp = func(sub.handle);
Shunkai Yao5c718342023-02-23 23:49:51 +0000297 if (!temp.isOk()) {
298 status = ndk::ScopedAStatus::fromStatus(temp.getStatus());
299 }
300 }
301 return status;
302}
303
Shunkai Yao79f98742023-05-03 23:54:43 +0000304bool EffectProxy::isBypassing() const {
305 return mSubEffects[mActiveSubIdx].descriptor.common.flags.bypass;
306}
307
Shunkai Yao13993232023-09-14 22:49:41 +0000308bool EffectProxy::isTunnel() const {
309 return mSubEffects[mActiveSubIdx].descriptor.common.flags.hwAcceleratorMode ==
310 Flags::HardwareAccelerator::TUNNEL;
Shunkai Yao0a047982023-09-13 16:55:26 +0000311}
312
Shunkai Yao79f98742023-05-03 23:54:43 +0000313binder_status_t EffectProxy::dump(int fd, const char** args, uint32_t numArgs) {
314 const std::string dumpString = toString();
315 write(fd, dumpString.c_str(), dumpString.size());
316
317 return runWithAllSubEffects([&](std::shared_ptr<IEffect>& effect) {
318 return ndk::ScopedAStatus::fromStatus(effect->dump(fd, args, numArgs));
319 })
320 .getStatus();
321}
322
323std::string EffectProxy::toString(size_t level) const {
324 std::string prefixSpace(level, ' ');
325 std::string ss = prefixSpace + "EffectProxy:\n";
326 prefixSpace += " ";
327 base::StringAppendF(&ss, "%sDescriptorCommon: %s\n", prefixSpace.c_str(),
328 mDescriptorCommon.toString().c_str());
329 base::StringAppendF(&ss, "%sActiveSubIdx: %zu\n", prefixSpace.c_str(), mActiveSubIdx);
330 base::StringAppendF(&ss, "%sAllSubEffects:\n", prefixSpace.c_str());
331 for (size_t i = 0; i < mSubEffects.size(); i++) {
332 base::StringAppendF(&ss, "%s[%zu] - Handle: %p, %s\n", prefixSpace.c_str(), i,
333 mSubEffects[i].handle.get(),
334 mSubEffects[i].descriptor.toString().c_str());
335 }
336 return ss;
337}
338
339} // namespace android::effect