blob: d721b32d839f852f7c00aa2dd4d749752471adbd [file] [log] [blame]
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001/*
2 * Copyright (C) 2022 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 <set>
19
20#define LOG_TAG "AHAL_Module"
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000021#include <Utils.h>
22#include <aidl/android/media/audio/common/AudioInputFlags.h>
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000023#include <aidl/android/media/audio/common/AudioOutputFlags.h>
Mikhail Naganov26dc9ad2023-06-23 13:55:37 -070024#include <android-base/logging.h>
25#include <android/binder_ibinder_platform.h>
26#include <error/expected_utils.h>
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000027
28#include "core-impl/Module.h"
Mikhail Naganovb03b5c42023-07-26 13:13:35 -070029#include "core-impl/ModuleBluetooth.h"
Mikhail Naganov521fc492023-07-11 17:24:08 -070030#include "core-impl/ModulePrimary.h"
Shraddha Basantwani6bb69632023-04-25 15:26:38 +053031#include "core-impl/ModuleRemoteSubmix.h"
Mikhail Naganov521fc492023-07-11 17:24:08 -070032#include "core-impl/ModuleStub.h"
jiabin253bd322023-01-25 23:57:31 +000033#include "core-impl/ModuleUsb.h"
Vlad Popa943b7e22022-12-08 14:24:12 +010034#include "core-impl/SoundDose.h"
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000035#include "core-impl/utils.h"
36
Mikhail Naganov872d4a62023-03-09 18:19:01 -080037using aidl::android::hardware::audio::common::getFrameSizeInBytes;
38using aidl::android::hardware::audio::common::isBitPositionFlagSet;
39using aidl::android::hardware::audio::common::isValidAudioMode;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000040using aidl::android::hardware::audio::common::SinkMetadata;
41using aidl::android::hardware::audio::common::SourceMetadata;
Vlad Popa2afbd1e2022-12-28 17:04:58 +010042using aidl::android::hardware::audio::core::sounddose::ISoundDose;
Mikhail Naganov6a4872d2022-06-15 21:39:04 +000043using aidl::android::media::audio::common::AudioChannelLayout;
Mikhail Naganovef6bc742022-10-06 00:14:19 +000044using aidl::android::media::audio::common::AudioDevice;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000045using aidl::android::media::audio::common::AudioFormatDescription;
Mikhail Naganov6a4872d2022-06-15 21:39:04 +000046using aidl::android::media::audio::common::AudioFormatType;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000047using aidl::android::media::audio::common::AudioInputFlags;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000048using aidl::android::media::audio::common::AudioIoFlags;
jiabin9a8e6862023-01-12 23:06:37 +000049using aidl::android::media::audio::common::AudioMMapPolicy;
50using aidl::android::media::audio::common::AudioMMapPolicyInfo;
51using aidl::android::media::audio::common::AudioMMapPolicyType;
Mikhail Naganov04ae8222023-01-11 15:48:10 -080052using aidl::android::media::audio::common::AudioMode;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000053using aidl::android::media::audio::common::AudioOffloadInfo;
54using aidl::android::media::audio::common::AudioOutputFlags;
55using aidl::android::media::audio::common::AudioPort;
56using aidl::android::media::audio::common::AudioPortConfig;
57using aidl::android::media::audio::common::AudioPortExt;
58using aidl::android::media::audio::common::AudioProfile;
Mikhail Naganov20047bc2023-01-05 20:16:07 +000059using aidl::android::media::audio::common::Boolean;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000060using aidl::android::media::audio::common::Int;
Mikhail Naganov6725ef52023-02-09 17:52:50 -080061using aidl::android::media::audio::common::MicrophoneInfo;
Mikhail Naganov6a4872d2022-06-15 21:39:04 +000062using aidl::android::media::audio::common::PcmType;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000063
64namespace aidl::android::hardware::audio::core {
65
66namespace {
67
Mikhail Naganov84bcc042023-10-05 17:36:57 -070068inline bool hasDynamicChannelMasks(const std::vector<AudioChannelLayout>& channelMasks) {
69 return channelMasks.empty() ||
70 std::all_of(channelMasks.begin(), channelMasks.end(),
71 [](const auto& channelMask) { return channelMask == AudioChannelLayout{}; });
72}
73
74inline bool hasDynamicFormat(const AudioFormatDescription& format) {
75 return format == AudioFormatDescription{};
76}
77
78inline bool hasDynamicSampleRates(const std::vector<int32_t>& sampleRates) {
79 return sampleRates.empty() ||
80 std::all_of(sampleRates.begin(), sampleRates.end(),
81 [](const auto& sampleRate) { return sampleRate == 0; });
82}
83
84inline bool isDynamicProfile(const AudioProfile& profile) {
85 return hasDynamicFormat(profile.format) || hasDynamicChannelMasks(profile.channelMasks) ||
86 hasDynamicSampleRates(profile.sampleRates);
87}
88
89bool hasDynamicProfilesOnly(const std::vector<AudioProfile>& profiles) {
90 if (profiles.empty()) return true;
91 return std::all_of(profiles.begin(), profiles.end(), isDynamicProfile);
92}
93
94// Note: does not assign an ID to the config.
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000095bool generateDefaultPortConfig(const AudioPort& port, AudioPortConfig* config) {
Mikhail Naganov84bcc042023-10-05 17:36:57 -070096 const bool allowDynamicConfig = port.ext.getTag() == AudioPortExt::device;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000097 *config = {};
98 config->portId = port.id;
Mikhail Naganov84bcc042023-10-05 17:36:57 -070099 for (const auto& profile : port.profiles) {
100 if (isDynamicProfile(profile)) continue;
101 config->format = profile.format;
102 config->channelMask = *profile.channelMasks.begin();
103 config->sampleRate = Int{.value = *profile.sampleRates.begin()};
104 config->flags = port.flags;
105 config->ext = port.ext;
106 return true;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000107 }
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700108 if (allowDynamicConfig) {
109 config->format = AudioFormatDescription{};
110 config->channelMask = AudioChannelLayout{};
111 config->sampleRate = Int{.value = 0};
112 config->flags = port.flags;
113 config->ext = port.ext;
114 return true;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000115 }
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700116 LOG(ERROR) << __func__ << ": port " << port.id << " only has dynamic profiles";
117 return false;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000118}
119
120bool findAudioProfile(const AudioPort& port, const AudioFormatDescription& format,
121 AudioProfile* profile) {
122 if (auto profilesIt =
123 find_if(port.profiles.begin(), port.profiles.end(),
124 [&format](const auto& profile) { return profile.format == format; });
125 profilesIt != port.profiles.end()) {
126 *profile = *profilesIt;
127 return true;
128 }
129 return false;
130}
Mikhail Naganov00603d12022-05-02 22:52:13 +0000131
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000132} // namespace
133
jiabin253bd322023-01-25 23:57:31 +0000134// static
Mikhail Naganovaf75a672023-10-13 23:38:25 +0000135std::shared_ptr<Module> Module::createInstance(Type type) {
jiabin253bd322023-01-25 23:57:31 +0000136 switch (type) {
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530137 case Type::DEFAULT:
Mikhail Naganovaf75a672023-10-13 23:38:25 +0000138 return ndk::SharedRefBase::make<ModulePrimary>();
Mikhail Naganov521fc492023-07-11 17:24:08 -0700139 case Type::R_SUBMIX:
Mikhail Naganovaf75a672023-10-13 23:38:25 +0000140 return ndk::SharedRefBase::make<ModuleRemoteSubmix>();
Mikhail Naganov521fc492023-07-11 17:24:08 -0700141 case Type::STUB:
Mikhail Naganovaf75a672023-10-13 23:38:25 +0000142 return ndk::SharedRefBase::make<ModuleStub>();
Mikhail Naganov521fc492023-07-11 17:24:08 -0700143 case Type::USB:
Mikhail Naganovaf75a672023-10-13 23:38:25 +0000144 return ndk::SharedRefBase::make<ModuleUsb>();
Mikhail Naganovb03b5c42023-07-26 13:13:35 -0700145 case Type::BLUETOOTH:
Mikhail Naganovaf75a672023-10-13 23:38:25 +0000146 return ndk::SharedRefBase::make<ModuleBluetooth>();
jiabin253bd322023-01-25 23:57:31 +0000147 }
148}
149
Mikhail Naganovd5536d92023-03-24 18:27:58 -0700150std::ostream& operator<<(std::ostream& os, Module::Type t) {
151 switch (t) {
152 case Module::Type::DEFAULT:
153 os << "default";
154 break;
155 case Module::Type::R_SUBMIX:
156 os << "r_submix";
157 break;
Mikhail Naganov521fc492023-07-11 17:24:08 -0700158 case Module::Type::STUB:
159 os << "stub";
160 break;
Mikhail Naganovd5536d92023-03-24 18:27:58 -0700161 case Module::Type::USB:
162 os << "usb";
163 break;
Mikhail Naganovb03b5c42023-07-26 13:13:35 -0700164 case Module::Type::BLUETOOTH:
165 os << "bluetooth";
166 break;
Mikhail Naganovd5536d92023-03-24 18:27:58 -0700167 }
168 return os;
169}
170
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000171void Module::cleanUpPatch(int32_t patchId) {
172 erase_all_values(mPatches, std::set<int32_t>{patchId});
173}
174
Mikhail Naganov8651b362023-01-06 23:15:27 +0000175ndk::ScopedAStatus Module::createStreamContext(
176 int32_t in_portConfigId, int64_t in_bufferSizeFrames,
177 std::shared_ptr<IStreamCallback> asyncCallback,
178 std::shared_ptr<IStreamOutEventCallback> outEventCallback, StreamContext* out_context) {
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000179 if (in_bufferSizeFrames <= 0) {
180 LOG(ERROR) << __func__ << ": non-positive buffer size " << in_bufferSizeFrames;
181 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
182 }
183 if (in_bufferSizeFrames < kMinimumStreamBufferSizeFrames) {
184 LOG(ERROR) << __func__ << ": insufficient buffer size " << in_bufferSizeFrames
185 << ", must be at least " << kMinimumStreamBufferSizeFrames;
186 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
187 }
188 auto& configs = getConfig().portConfigs;
189 auto portConfigIt = findById<AudioPortConfig>(configs, in_portConfigId);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000190 // Since this is a private method, it is assumed that
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000191 // validity of the portConfigId has already been checked.
192 const size_t frameSize =
193 getFrameSizeInBytes(portConfigIt->format.value(), portConfigIt->channelMask.value());
194 if (frameSize == 0) {
195 LOG(ERROR) << __func__ << ": could not calculate frame size for port config "
196 << portConfigIt->toString();
197 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
198 }
199 LOG(DEBUG) << __func__ << ": frame size " << frameSize << " bytes";
Mikhail Naganovb511b8a2023-05-15 14:35:24 -0700200 if (frameSize > static_cast<size_t>(kMaximumStreamBufferSizeBytes / in_bufferSizeFrames)) {
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000201 LOG(ERROR) << __func__ << ": buffer size " << in_bufferSizeFrames
202 << " frames is too large, maximum size is "
203 << kMaximumStreamBufferSizeBytes / frameSize;
204 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
205 }
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000206 const auto& flags = portConfigIt->flags.value();
207 if ((flags.getTag() == AudioIoFlags::Tag::input &&
Mikhail Naganova2c5ddf2022-09-12 22:57:14 +0000208 !isBitPositionFlagSet(flags.get<AudioIoFlags::Tag::input>(),
209 AudioInputFlags::MMAP_NOIRQ)) ||
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000210 (flags.getTag() == AudioIoFlags::Tag::output &&
Mikhail Naganova2c5ddf2022-09-12 22:57:14 +0000211 !isBitPositionFlagSet(flags.get<AudioIoFlags::Tag::output>(),
212 AudioOutputFlags::MMAP_NOIRQ))) {
Mikhail Naganov20047bc2023-01-05 20:16:07 +0000213 StreamContext::DebugParameters params{mDebug.streamTransientStateDelayMs,
Mikhail Naganov194daaa2023-01-05 22:34:20 +0000214 mVendorDebug.forceTransientBurst,
215 mVendorDebug.forceSynchronousDrain};
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000216 StreamContext temp(
217 std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
218 std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530219 portConfigIt->portId, portConfigIt->format.value(),
220 portConfigIt->channelMask.value(), portConfigIt->sampleRate.value().value, flags,
Mikhail Naganovb42a69e2023-06-16 12:38:25 -0700221 portConfigIt->ext.get<AudioPortExt::mix>().handle,
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000222 std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames),
Mikhail Naganov8651b362023-01-06 23:15:27 +0000223 asyncCallback, outEventCallback, params);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000224 if (temp.isValid()) {
225 *out_context = std::move(temp);
226 } else {
227 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
228 }
229 } else {
230 // TODO: Implement simulation of MMAP buffer allocation
231 }
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000232 return ndk::ScopedAStatus::ok();
233}
234
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000235std::vector<AudioDevice> Module::findConnectedDevices(int32_t portConfigId) {
236 std::vector<AudioDevice> result;
237 auto& ports = getConfig().ports;
238 auto portIds = portIdsFromPortConfigIds(findConnectedPortConfigIds(portConfigId));
239 for (auto it = portIds.begin(); it != portIds.end(); ++it) {
240 auto portIt = findById<AudioPort>(ports, *it);
241 if (portIt != ports.end() && portIt->ext.getTag() == AudioPortExt::Tag::device) {
242 result.push_back(portIt->ext.template get<AudioPortExt::Tag::device>().device);
243 }
244 }
245 return result;
246}
247
248std::set<int32_t> Module::findConnectedPortConfigIds(int32_t portConfigId) {
249 std::set<int32_t> result;
250 auto patchIdsRange = mPatches.equal_range(portConfigId);
251 auto& patches = getConfig().patches;
252 for (auto it = patchIdsRange.first; it != patchIdsRange.second; ++it) {
253 auto patchIt = findById<AudioPatch>(patches, it->second);
254 if (patchIt == patches.end()) {
255 LOG(FATAL) << __func__ << ": patch with id " << it->second << " taken from mPatches "
256 << "not found in the configuration";
257 }
258 if (std::find(patchIt->sourcePortConfigIds.begin(), patchIt->sourcePortConfigIds.end(),
259 portConfigId) != patchIt->sourcePortConfigIds.end()) {
260 result.insert(patchIt->sinkPortConfigIds.begin(), patchIt->sinkPortConfigIds.end());
261 } else {
262 result.insert(patchIt->sourcePortConfigIds.begin(), patchIt->sourcePortConfigIds.end());
263 }
264 }
265 return result;
266}
267
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000268ndk::ScopedAStatus Module::findPortIdForNewStream(int32_t in_portConfigId, AudioPort** port) {
269 auto& configs = getConfig().portConfigs;
270 auto portConfigIt = findById<AudioPortConfig>(configs, in_portConfigId);
271 if (portConfigIt == configs.end()) {
272 LOG(ERROR) << __func__ << ": existing port config id " << in_portConfigId << " not found";
273 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
274 }
275 const int32_t portId = portConfigIt->portId;
276 // In our implementation, configs of mix ports always have unique IDs.
277 CHECK(portId != in_portConfigId);
278 auto& ports = getConfig().ports;
279 auto portIt = findById<AudioPort>(ports, portId);
280 if (portIt == ports.end()) {
281 LOG(ERROR) << __func__ << ": port id " << portId << " used by port config id "
282 << in_portConfigId << " not found";
283 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
284 }
285 if (mStreams.count(in_portConfigId) != 0) {
286 LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
287 << " already has a stream opened on it";
288 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
289 }
290 if (portIt->ext.getTag() != AudioPortExt::Tag::mix) {
291 LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
292 << " does not correspond to a mix port";
293 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
294 }
Mikhail Naganovb511b8a2023-05-15 14:35:24 -0700295 const size_t maxOpenStreamCount = portIt->ext.get<AudioPortExt::Tag::mix>().maxOpenStreamCount;
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000296 if (maxOpenStreamCount != 0 && mStreams.count(portId) >= maxOpenStreamCount) {
297 LOG(ERROR) << __func__ << ": port id " << portId
298 << " has already reached maximum allowed opened stream count: "
299 << maxOpenStreamCount;
300 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
301 }
302 *port = &(*portIt);
303 return ndk::ScopedAStatus::ok();
304}
305
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000306template <typename C>
307std::set<int32_t> Module::portIdsFromPortConfigIds(C portConfigIds) {
308 std::set<int32_t> result;
309 auto& portConfigs = getConfig().portConfigs;
310 for (auto it = portConfigIds.begin(); it != portConfigIds.end(); ++it) {
311 auto portConfigIt = findById<AudioPortConfig>(portConfigs, *it);
312 if (portConfigIt != portConfigs.end()) {
313 result.insert(portConfigIt->portId);
314 }
315 }
316 return result;
317}
318
Mikhail Naganovaf75a672023-10-13 23:38:25 +0000319std::unique_ptr<internal::Configuration> Module::initializeConfig() {
320 std::unique_ptr<internal::Configuration> config;
321 switch (getType()) {
322 case Type::DEFAULT:
323 config = std::move(internal::getPrimaryConfiguration());
324 break;
325 case Type::R_SUBMIX:
326 config = std::move(internal::getRSubmixConfiguration());
327 break;
328 case Type::STUB:
329 config = std::move(internal::getStubConfiguration());
330 break;
331 case Type::USB:
332 config = std::move(internal::getUsbConfiguration());
333 break;
334 case Type::BLUETOOTH:
335 config = std::move(internal::getBluetoothConfiguration());
336 break;
337 }
338 return config;
Peter Yoon918a6a52023-07-13 17:04:37 +0900339}
340
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700341std::vector<AudioRoute*> Module::getAudioRoutesForAudioPortImpl(int32_t portId) {
342 std::vector<AudioRoute*> result;
343 auto& routes = getConfig().routes;
344 for (auto& r : routes) {
345 const auto& srcs = r.sourcePortIds;
346 if (r.sinkPortId == portId || std::find(srcs.begin(), srcs.end(), portId) != srcs.end()) {
347 result.push_back(&r);
348 }
349 }
350 return result;
351}
352
Mikhail Naganovaf75a672023-10-13 23:38:25 +0000353internal::Configuration& Module::getConfig() {
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000354 if (!mConfig) {
Peter Yoon918a6a52023-07-13 17:04:37 +0900355 mConfig = std::move(initializeConfig());
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000356 }
357 return *mConfig;
358}
359
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700360std::set<int32_t> Module::getRoutableAudioPortIds(int32_t portId,
361 std::vector<AudioRoute*>* routes) {
362 std::vector<AudioRoute*> routesStorage;
363 if (routes == nullptr) {
364 routesStorage = getAudioRoutesForAudioPortImpl(portId);
365 routes = &routesStorage;
366 }
367 std::set<int32_t> result;
368 for (AudioRoute* r : *routes) {
369 if (r->sinkPortId == portId) {
370 result.insert(r->sourcePortIds.begin(), r->sourcePortIds.end());
371 } else {
372 result.insert(r->sinkPortId);
373 }
374 }
375 return result;
376}
377
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000378void Module::registerPatch(const AudioPatch& patch) {
379 auto& configs = getConfig().portConfigs;
380 auto do_insert = [&](const std::vector<int32_t>& portConfigIds) {
381 for (auto portConfigId : portConfigIds) {
382 auto configIt = findById<AudioPortConfig>(configs, portConfigId);
383 if (configIt != configs.end()) {
384 mPatches.insert(std::pair{portConfigId, patch.id});
385 if (configIt->portId != portConfigId) {
386 mPatches.insert(std::pair{configIt->portId, patch.id});
387 }
388 }
389 };
390 };
391 do_insert(patch.sourcePortConfigIds);
392 do_insert(patch.sinkPortConfigIds);
393}
394
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700395ndk::ScopedAStatus Module::updateStreamsConnectedState(const AudioPatch& oldPatch,
396 const AudioPatch& newPatch) {
Mikhail Naganov89a8ea92023-09-29 17:02:12 -0700397 // Notify streams about the new set of devices they are connected to.
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700398 auto maybeFailure = ndk::ScopedAStatus::ok();
Mikhail Naganov89a8ea92023-09-29 17:02:12 -0700399 using Connections =
400 std::map<int32_t /*mixPortConfigId*/, std::set<int32_t /*devicePortConfigId*/>>;
401 Connections oldConnections, newConnections;
402 auto fillConnectionsHelper = [&](Connections& connections,
403 const std::vector<int32_t>& mixPortCfgIds,
404 const std::vector<int32_t>& devicePortCfgIds) {
405 for (int32_t mixPortCfgId : mixPortCfgIds) {
406 connections[mixPortCfgId].insert(devicePortCfgIds.begin(), devicePortCfgIds.end());
407 }
408 };
409 auto fillConnections = [&](Connections& connections, const AudioPatch& patch) {
410 if (std::find_if(patch.sourcePortConfigIds.begin(), patch.sourcePortConfigIds.end(),
411 [&](int32_t portConfigId) { return mStreams.count(portConfigId) > 0; }) !=
412 patch.sourcePortConfigIds.end()) {
413 // Sources are mix ports.
414 fillConnectionsHelper(connections, patch.sourcePortConfigIds, patch.sinkPortConfigIds);
415 } else if (std::find_if(patch.sinkPortConfigIds.begin(), patch.sinkPortConfigIds.end(),
416 [&](int32_t portConfigId) {
417 return mStreams.count(portConfigId) > 0;
418 }) != patch.sinkPortConfigIds.end()) {
419 // Sources are device ports.
420 fillConnectionsHelper(connections, patch.sinkPortConfigIds, patch.sourcePortConfigIds);
421 } // Otherwise, there are no streams to notify.
422 };
423 fillConnections(oldConnections, oldPatch);
424 fillConnections(newConnections, newPatch);
425
426 std::for_each(oldConnections.begin(), oldConnections.end(), [&](const auto& connectionPair) {
427 const int32_t mixPortConfigId = connectionPair.first;
428 if (auto it = newConnections.find(mixPortConfigId);
429 it == newConnections.end() || it->second != connectionPair.second) {
430 if (auto status = mStreams.setStreamConnectedDevices(mixPortConfigId, {});
431 status.isOk()) {
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700432 LOG(DEBUG) << "updateStreamsConnectedState: The stream on port config id "
Mikhail Naganov89a8ea92023-09-29 17:02:12 -0700433 << mixPortConfigId << " has been disconnected";
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700434 } else {
435 // Disconnection is tricky to roll back, just register a failure.
436 maybeFailure = std::move(status);
437 }
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000438 }
439 });
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700440 if (!maybeFailure.isOk()) return maybeFailure;
Mikhail Naganov89a8ea92023-09-29 17:02:12 -0700441 std::set<int32_t> idsToDisconnectOnFailure;
442 std::for_each(newConnections.begin(), newConnections.end(), [&](const auto& connectionPair) {
443 const int32_t mixPortConfigId = connectionPair.first;
444 if (auto it = oldConnections.find(mixPortConfigId);
445 it == oldConnections.end() || it->second != connectionPair.second) {
446 const auto connectedDevices = findConnectedDevices(mixPortConfigId);
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700447 if (connectedDevices.empty()) {
448 // This is important as workers use the vector size to derive the connection status.
449 LOG(FATAL) << "updateStreamsConnectedState: No connected devices found for port "
450 "config id "
Mikhail Naganov89a8ea92023-09-29 17:02:12 -0700451 << mixPortConfigId;
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700452 }
Mikhail Naganov89a8ea92023-09-29 17:02:12 -0700453 if (auto status = mStreams.setStreamConnectedDevices(mixPortConfigId, connectedDevices);
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700454 status.isOk()) {
455 LOG(DEBUG) << "updateStreamsConnectedState: The stream on port config id "
Mikhail Naganov89a8ea92023-09-29 17:02:12 -0700456 << mixPortConfigId << " has been connected to: "
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700457 << ::android::internal::ToString(connectedDevices);
458 } else {
459 maybeFailure = std::move(status);
Mikhail Naganov89a8ea92023-09-29 17:02:12 -0700460 idsToDisconnectOnFailure.insert(mixPortConfigId);
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700461 }
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000462 }
463 });
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700464 if (!maybeFailure.isOk()) {
465 LOG(WARNING) << __func__ << ": Due to a failure, disconnecting streams on port config ids "
466 << ::android::internal::ToString(idsToDisconnectOnFailure);
467 std::for_each(idsToDisconnectOnFailure.begin(), idsToDisconnectOnFailure.end(),
468 [&](const auto& portConfigId) {
469 auto status = mStreams.setStreamConnectedDevices(portConfigId, {});
470 (void)status.isOk(); // Can't do much about a failure here.
471 });
472 return maybeFailure;
473 }
474 return ndk::ScopedAStatus::ok();
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000475}
476
Mikhail Naganov00603d12022-05-02 22:52:13 +0000477ndk::ScopedAStatus Module::setModuleDebug(
478 const ::aidl::android::hardware::audio::core::ModuleDebug& in_debug) {
Mikhail Naganovd5536d92023-03-24 18:27:58 -0700479 LOG(DEBUG) << __func__ << ": " << mType << ": old flags:" << mDebug.toString()
Mikhail Naganov00603d12022-05-02 22:52:13 +0000480 << ", new flags: " << in_debug.toString();
481 if (mDebug.simulateDeviceConnections != in_debug.simulateDeviceConnections &&
482 !mConnectedDevicePorts.empty()) {
Mikhail Naganovd5536d92023-03-24 18:27:58 -0700483 LOG(ERROR) << __func__ << ": " << mType
484 << ": attempting to change device connections simulation while having external "
485 << "devices connected";
Mikhail Naganov00603d12022-05-02 22:52:13 +0000486 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
487 }
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000488 if (in_debug.streamTransientStateDelayMs < 0) {
Mikhail Naganovd5536d92023-03-24 18:27:58 -0700489 LOG(ERROR) << __func__ << ": " << mType << ": streamTransientStateDelayMs is negative: "
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000490 << in_debug.streamTransientStateDelayMs;
491 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
492 }
Mikhail Naganov00603d12022-05-02 22:52:13 +0000493 mDebug = in_debug;
494 return ndk::ScopedAStatus::ok();
495}
496
Mikhail Naganov3b125b72022-10-05 02:12:39 +0000497ndk::ScopedAStatus Module::getTelephony(std::shared_ptr<ITelephony>* _aidl_return) {
Mikhail Naganov521fc492023-07-11 17:24:08 -0700498 *_aidl_return = nullptr;
499 LOG(DEBUG) << __func__ << ": returning null";
Mikhail Naganov3b125b72022-10-05 02:12:39 +0000500 return ndk::ScopedAStatus::ok();
501}
502
Mikhail Naganov10c6fe22022-09-30 23:49:17 +0000503ndk::ScopedAStatus Module::getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) {
Mikhail Naganov521fc492023-07-11 17:24:08 -0700504 *_aidl_return = nullptr;
505 LOG(DEBUG) << __func__ << ": returning null";
Mikhail Naganov10c6fe22022-09-30 23:49:17 +0000506 return ndk::ScopedAStatus::ok();
507}
508
Mikhail Naganov7499a002023-02-27 18:51:44 -0800509ndk::ScopedAStatus Module::getBluetoothA2dp(std::shared_ptr<IBluetoothA2dp>* _aidl_return) {
Mikhail Naganov521fc492023-07-11 17:24:08 -0700510 *_aidl_return = nullptr;
511 LOG(DEBUG) << __func__ << ": returning null";
Mikhail Naganov7499a002023-02-27 18:51:44 -0800512 return ndk::ScopedAStatus::ok();
513}
514
Mikhail Naganovb5647da2023-03-06 14:37:38 -0800515ndk::ScopedAStatus Module::getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) {
Mikhail Naganov521fc492023-07-11 17:24:08 -0700516 *_aidl_return = nullptr;
517 LOG(DEBUG) << __func__ << ": returning null";
Mikhail Naganovb5647da2023-03-06 14:37:38 -0800518 return ndk::ScopedAStatus::ok();
519}
520
Mikhail Naganov00603d12022-05-02 22:52:13 +0000521ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdAndAdditionalData,
522 AudioPort* _aidl_return) {
523 const int32_t templateId = in_templateIdAndAdditionalData.id;
524 auto& ports = getConfig().ports;
525 AudioPort connectedPort;
526 { // Scope the template port so that we don't accidentally modify it.
527 auto templateIt = findById<AudioPort>(ports, templateId);
528 if (templateIt == ports.end()) {
529 LOG(ERROR) << __func__ << ": port id " << templateId << " not found";
530 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
531 }
532 if (templateIt->ext.getTag() != AudioPortExt::Tag::device) {
533 LOG(ERROR) << __func__ << ": port id " << templateId << " is not a device port";
534 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
535 }
Mikhail Naganov00603d12022-05-02 22:52:13 +0000536 auto& templateDevicePort = templateIt->ext.get<AudioPortExt::Tag::device>();
537 if (templateDevicePort.device.type.connection.empty()) {
538 LOG(ERROR) << __func__ << ": port id " << templateId << " is permanently attached";
539 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
540 }
Mikhail Naganovfcf980e2023-09-07 16:30:11 -0700541 if (mConnectedDevicePorts.find(templateId) != mConnectedDevicePorts.end()) {
542 LOG(ERROR) << __func__ << ": port id " << templateId << " is a connected device port";
543 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
544 }
Mikhail Naganov00603d12022-05-02 22:52:13 +0000545 // Postpone id allocation until we ensure that there are no client errors.
546 connectedPort = *templateIt;
547 connectedPort.extraAudioDescriptors = in_templateIdAndAdditionalData.extraAudioDescriptors;
548 const auto& inputDevicePort =
549 in_templateIdAndAdditionalData.ext.get<AudioPortExt::Tag::device>();
550 auto& connectedDevicePort = connectedPort.ext.get<AudioPortExt::Tag::device>();
551 connectedDevicePort.device.address = inputDevicePort.device.address;
552 LOG(DEBUG) << __func__ << ": device port " << connectedPort.id << " device set to "
553 << connectedDevicePort.device.toString();
554 // Check if there is already a connected port with for the same external device.
Mikhail Naganov7b2d12b2023-03-24 18:29:14 -0700555 for (auto connectedPortPair : mConnectedDevicePorts) {
556 auto connectedPortIt = findById<AudioPort>(ports, connectedPortPair.first);
Mikhail Naganov00603d12022-05-02 22:52:13 +0000557 if (connectedPortIt->ext.get<AudioPortExt::Tag::device>().device ==
558 connectedDevicePort.device) {
559 LOG(ERROR) << __func__ << ": device " << connectedDevicePort.device.toString()
Mikhail Naganov7b2d12b2023-03-24 18:29:14 -0700560 << " is already connected at the device port id "
561 << connectedPortPair.first;
Mikhail Naganov00603d12022-05-02 22:52:13 +0000562 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
563 }
564 }
565 }
566
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700567 // Two main cases are considered with regard to the profiles of the connected device port:
568 //
569 // 1. If the template device port has dynamic profiles, and at least one routable mix
570 // port also has dynamic profiles, it means that after connecting the device, the
571 // connected device port must have profiles populated with actual capabilities of
572 // the connected device, and dynamic of routable mix ports will be filled
573 // according to these capabilities. An example of this case is connection of an
574 // HDMI or USB device. For USB handled by ADSP, there can be mix ports with static
575 // profiles, and one dedicated mix port for "hi-fi" playback. The latter is left with
576 // dynamic profiles so that they can be populated with actual capabilities of
577 // the connected device.
578 //
579 // 2. If the template device port has dynamic profiles, while all routable mix ports
580 // have static profiles, it means that after connecting the device, the connected
581 // device port can be left with dynamic profiles, and profiles of mix ports are
582 // left untouched. An example of this case is connection of an analog wired
583 // headset, it should be treated in the same way as a speaker.
584 //
585 // Yet another possible case is when both the template device port and all routable
586 // mix ports have static profiles. This is allowed and handled correctly, however, it
587 // is not very practical, since these profiles are likely duplicates of each other.
588
589 std::vector<AudioRoute*> routesToMixPorts = getAudioRoutesForAudioPortImpl(templateId);
590 std::set<int32_t> routableMixPortIds = getRoutableAudioPortIds(templateId, &routesToMixPorts);
591 if (hasDynamicProfilesOnly(connectedPort.profiles)) {
Mikhail Naganovfcf980e2023-09-07 16:30:11 -0700592 if (!mDebug.simulateDeviceConnections) {
593 RETURN_STATUS_IF_ERROR(populateConnectedDevicePort(&connectedPort));
594 } else {
595 auto& connectedProfiles = getConfig().connectedProfiles;
596 if (auto connectedProfilesIt = connectedProfiles.find(templateId);
597 connectedProfilesIt != connectedProfiles.end()) {
598 connectedPort.profiles = connectedProfilesIt->second;
599 }
600 }
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700601 if (hasDynamicProfilesOnly(connectedPort.profiles)) {
602 // Possible case 2. Check if all routable mix ports have static profiles.
603 if (auto dynamicMixPortIt = std::find_if(ports.begin(), ports.end(),
604 [&routableMixPortIds](const auto& p) {
605 return routableMixPortIds.count(p.id) >
606 0 &&
607 hasDynamicProfilesOnly(p.profiles);
608 });
609 dynamicMixPortIt != ports.end()) {
610 LOG(ERROR) << __func__
611 << ": connected port only has dynamic profiles after connecting "
612 << "external device " << connectedPort.toString() << ", and there exist "
613 << "a routable mix port with dynamic profiles: "
614 << dynamicMixPortIt->toString();
615 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
616 }
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530617 }
618 }
619
Mikhail Naganovdc417732023-09-29 15:49:35 -0700620 connectedPort.id = getConfig().nextPortId++;
Mikhail Naganov7b2d12b2023-03-24 18:29:14 -0700621 auto [connectedPortsIt, _] =
Mikhail Naganov0e128dd2023-09-13 18:01:18 -0700622 mConnectedDevicePorts.insert(std::pair(connectedPort.id, std::set<int32_t>()));
Mikhail Naganov00603d12022-05-02 22:52:13 +0000623 LOG(DEBUG) << __func__ << ": template port " << templateId << " external device connected, "
624 << "connected port ID " << connectedPort.id;
Mikhail Naganov00603d12022-05-02 22:52:13 +0000625 ports.push_back(connectedPort);
jiabin783c48b2023-02-28 18:28:06 +0000626 onExternalDeviceConnectionChanged(connectedPort, true /*connected*/);
Mikhail Naganov00603d12022-05-02 22:52:13 +0000627
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700628 // For routes where the template port is a source, add the connected port to sources,
629 // otherwise, create a new route by copying from the route for the template port.
Mikhail Naganov00603d12022-05-02 22:52:13 +0000630 std::vector<AudioRoute> newRoutes;
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700631 for (AudioRoute* r : routesToMixPorts) {
632 if (r->sinkPortId == templateId) {
633 newRoutes.push_back(AudioRoute{.sourcePortIds = r->sourcePortIds,
634 .sinkPortId = connectedPort.id,
635 .isExclusive = r->isExclusive});
Mikhail Naganov00603d12022-05-02 22:52:13 +0000636 } else {
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700637 r->sourcePortIds.push_back(connectedPort.id);
Mikhail Naganov00603d12022-05-02 22:52:13 +0000638 }
639 }
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700640 auto& routes = getConfig().routes;
Mikhail Naganov00603d12022-05-02 22:52:13 +0000641 routes.insert(routes.end(), newRoutes.begin(), newRoutes.end());
642
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700643 if (!hasDynamicProfilesOnly(connectedPort.profiles) && !routableMixPortIds.empty()) {
644 // Note: this is a simplistic approach assuming that a mix port can only be populated
645 // from a single device port. Implementing support for stuffing dynamic profiles with
646 // a superset of all profiles from all routable dynamic device ports would be more involved.
647 for (auto& port : ports) {
648 if (routableMixPortIds.count(port.id) == 0) continue;
649 if (hasDynamicProfilesOnly(port.profiles)) {
650 port.profiles = connectedPort.profiles;
651 connectedPortsIt->second.insert(port.id);
Mikhail Naganov0e128dd2023-09-13 18:01:18 -0700652 } else {
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700653 // Check if profiles are not all dynamic because they were populated by
654 // a previous connection. Otherwise, it means that they are actually static.
655 for (const auto& cp : mConnectedDevicePorts) {
656 if (cp.second.count(port.id) > 0) {
657 connectedPortsIt->second.insert(port.id);
Mikhail Naganov0e128dd2023-09-13 18:01:18 -0700658 break;
659 }
660 }
661 }
Mikhail Naganov7b2d12b2023-03-24 18:29:14 -0700662 }
663 }
664 *_aidl_return = std::move(connectedPort);
665
Mikhail Naganov00603d12022-05-02 22:52:13 +0000666 return ndk::ScopedAStatus::ok();
667}
668
669ndk::ScopedAStatus Module::disconnectExternalDevice(int32_t in_portId) {
670 auto& ports = getConfig().ports;
671 auto portIt = findById<AudioPort>(ports, in_portId);
672 if (portIt == ports.end()) {
673 LOG(ERROR) << __func__ << ": port id " << in_portId << " not found";
674 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
675 }
676 if (portIt->ext.getTag() != AudioPortExt::Tag::device) {
677 LOG(ERROR) << __func__ << ": port id " << in_portId << " is not a device port";
678 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
679 }
Mikhail Naganov7b2d12b2023-03-24 18:29:14 -0700680 auto connectedPortsIt = mConnectedDevicePorts.find(in_portId);
681 if (connectedPortsIt == mConnectedDevicePorts.end()) {
Mikhail Naganov00603d12022-05-02 22:52:13 +0000682 LOG(ERROR) << __func__ << ": port id " << in_portId << " is not a connected device port";
683 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
684 }
685 auto& configs = getConfig().portConfigs;
686 auto& initials = getConfig().initialConfigs;
687 auto configIt = std::find_if(configs.begin(), configs.end(), [&](const auto& config) {
688 if (config.portId == in_portId) {
689 // Check if the configuration was provided by the client.
690 const auto& initialIt = findById<AudioPortConfig>(initials, config.id);
691 return initialIt == initials.end() || config != *initialIt;
692 }
693 return false;
694 });
695 if (configIt != configs.end()) {
696 LOG(ERROR) << __func__ << ": port id " << in_portId << " has a non-default config with id "
697 << configIt->id;
698 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
699 }
jiabin783c48b2023-02-28 18:28:06 +0000700 onExternalDeviceConnectionChanged(*portIt, false /*connected*/);
Mikhail Naganov00603d12022-05-02 22:52:13 +0000701 ports.erase(portIt);
Mikhail Naganov00603d12022-05-02 22:52:13 +0000702 LOG(DEBUG) << __func__ << ": connected device port " << in_portId << " released";
703
704 auto& routes = getConfig().routes;
705 for (auto routesIt = routes.begin(); routesIt != routes.end();) {
706 if (routesIt->sinkPortId == in_portId) {
707 routesIt = routes.erase(routesIt);
708 } else {
709 // Note: the list of sourcePortIds can't become empty because there must
710 // be the id of the template port in the route.
711 erase_if(routesIt->sourcePortIds, [in_portId](auto src) { return src == in_portId; });
712 ++routesIt;
713 }
714 }
715
Mikhail Naganov0e128dd2023-09-13 18:01:18 -0700716 // Clear profiles for mix ports that are not connected to any other ports.
717 std::set<int32_t> mixPortsToClear = std::move(connectedPortsIt->second);
718 mConnectedDevicePorts.erase(connectedPortsIt);
719 for (const auto& connectedPort : mConnectedDevicePorts) {
720 for (int32_t mixPortId : connectedPort.second) {
721 mixPortsToClear.erase(mixPortId);
722 }
723 }
724 for (int32_t mixPortId : mixPortsToClear) {
Mikhail Naganov7b2d12b2023-03-24 18:29:14 -0700725 auto mixPortIt = findById<AudioPort>(ports, mixPortId);
726 if (mixPortIt != ports.end()) {
727 mixPortIt->profiles = {};
728 }
729 }
Mikhail Naganov7b2d12b2023-03-24 18:29:14 -0700730
Mikhail Naganov00603d12022-05-02 22:52:13 +0000731 return ndk::ScopedAStatus::ok();
732}
733
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000734ndk::ScopedAStatus Module::getAudioPatches(std::vector<AudioPatch>* _aidl_return) {
735 *_aidl_return = getConfig().patches;
736 LOG(DEBUG) << __func__ << ": returning " << _aidl_return->size() << " patches";
737 return ndk::ScopedAStatus::ok();
738}
739
740ndk::ScopedAStatus Module::getAudioPort(int32_t in_portId, AudioPort* _aidl_return) {
741 auto& ports = getConfig().ports;
742 auto portIt = findById<AudioPort>(ports, in_portId);
743 if (portIt != ports.end()) {
744 *_aidl_return = *portIt;
745 LOG(DEBUG) << __func__ << ": returning port by id " << in_portId;
746 return ndk::ScopedAStatus::ok();
747 }
748 LOG(ERROR) << __func__ << ": port id " << in_portId << " not found";
749 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
750}
751
752ndk::ScopedAStatus Module::getAudioPortConfigs(std::vector<AudioPortConfig>* _aidl_return) {
753 *_aidl_return = getConfig().portConfigs;
754 LOG(DEBUG) << __func__ << ": returning " << _aidl_return->size() << " port configs";
755 return ndk::ScopedAStatus::ok();
756}
757
758ndk::ScopedAStatus Module::getAudioPorts(std::vector<AudioPort>* _aidl_return) {
759 *_aidl_return = getConfig().ports;
760 LOG(DEBUG) << __func__ << ": returning " << _aidl_return->size() << " ports";
761 return ndk::ScopedAStatus::ok();
762}
763
764ndk::ScopedAStatus Module::getAudioRoutes(std::vector<AudioRoute>* _aidl_return) {
765 *_aidl_return = getConfig().routes;
766 LOG(DEBUG) << __func__ << ": returning " << _aidl_return->size() << " routes";
767 return ndk::ScopedAStatus::ok();
768}
769
Mikhail Naganov00603d12022-05-02 22:52:13 +0000770ndk::ScopedAStatus Module::getAudioRoutesForAudioPort(int32_t in_portId,
771 std::vector<AudioRoute>* _aidl_return) {
772 auto& ports = getConfig().ports;
773 if (auto portIt = findById<AudioPort>(ports, in_portId); portIt == ports.end()) {
774 LOG(ERROR) << __func__ << ": port id " << in_portId << " not found";
775 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
776 }
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700777 std::vector<AudioRoute*> routes = getAudioRoutesForAudioPortImpl(in_portId);
778 std::transform(routes.begin(), routes.end(), std::back_inserter(*_aidl_return),
779 [](auto rptr) { return *rptr; });
Mikhail Naganov00603d12022-05-02 22:52:13 +0000780 return ndk::ScopedAStatus::ok();
781}
782
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000783ndk::ScopedAStatus Module::openInputStream(const OpenInputStreamArguments& in_args,
784 OpenInputStreamReturn* _aidl_return) {
785 LOG(DEBUG) << __func__ << ": port config id " << in_args.portConfigId << ", buffer size "
786 << in_args.bufferSizeFrames << " frames";
787 AudioPort* port = nullptr;
Mikhail Naganov26dc9ad2023-06-23 13:55:37 -0700788 RETURN_STATUS_IF_ERROR(findPortIdForNewStream(in_args.portConfigId, &port));
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000789 if (port->flags.getTag() != AudioIoFlags::Tag::input) {
790 LOG(ERROR) << __func__ << ": port config id " << in_args.portConfigId
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000791 << " does not correspond to an input mix port";
792 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
793 }
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000794 StreamContext context;
Mikhail Naganov26dc9ad2023-06-23 13:55:37 -0700795 RETURN_STATUS_IF_ERROR(createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames,
796 nullptr, nullptr, &context));
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000797 context.fillDescriptor(&_aidl_return->desc);
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000798 std::shared_ptr<StreamIn> stream;
Mikhail Naganov6ddefdb2023-07-19 17:30:06 -0700799 RETURN_STATUS_IF_ERROR(createInputStream(std::move(context), in_args.sinkMetadata,
Mikhail Naganovaf75a672023-10-13 23:38:25 +0000800 mConfig->microphones, &stream));
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000801 StreamWrapper streamWrapper(stream);
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700802 if (auto patchIt = mPatches.find(in_args.portConfigId); patchIt != mPatches.end()) {
803 RETURN_STATUS_IF_ERROR(
804 streamWrapper.setConnectedDevices(findConnectedDevices(in_args.portConfigId)));
805 }
Mikhail Naganovdf5feba2022-12-15 00:11:14 +0000806 AIBinder_setMinSchedulerPolicy(streamWrapper.getBinder().get(), SCHED_NORMAL,
807 ANDROID_PRIORITY_AUDIO);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000808 mStreams.insert(port->id, in_args.portConfigId, std::move(streamWrapper));
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000809 _aidl_return->stream = std::move(stream);
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000810 return ndk::ScopedAStatus::ok();
811}
812
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000813ndk::ScopedAStatus Module::openOutputStream(const OpenOutputStreamArguments& in_args,
814 OpenOutputStreamReturn* _aidl_return) {
815 LOG(DEBUG) << __func__ << ": port config id " << in_args.portConfigId << ", has offload info? "
816 << (in_args.offloadInfo.has_value()) << ", buffer size " << in_args.bufferSizeFrames
817 << " frames";
818 AudioPort* port = nullptr;
Mikhail Naganov26dc9ad2023-06-23 13:55:37 -0700819 RETURN_STATUS_IF_ERROR(findPortIdForNewStream(in_args.portConfigId, &port));
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000820 if (port->flags.getTag() != AudioIoFlags::Tag::output) {
821 LOG(ERROR) << __func__ << ": port config id " << in_args.portConfigId
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000822 << " does not correspond to an output mix port";
823 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
824 }
Mikhail Naganova2c5ddf2022-09-12 22:57:14 +0000825 const bool isOffload = isBitPositionFlagSet(port->flags.get<AudioIoFlags::Tag::output>(),
826 AudioOutputFlags::COMPRESS_OFFLOAD);
827 if (isOffload && !in_args.offloadInfo.has_value()) {
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000828 LOG(ERROR) << __func__ << ": port id " << port->id
Mikhail Naganov111e0ce2022-06-17 21:41:19 +0000829 << " has COMPRESS_OFFLOAD flag set, requires offload info";
830 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
831 }
Mikhail Naganov30301a42022-09-13 01:20:45 +0000832 const bool isNonBlocking = isBitPositionFlagSet(port->flags.get<AudioIoFlags::Tag::output>(),
833 AudioOutputFlags::NON_BLOCKING);
834 if (isNonBlocking && in_args.callback == nullptr) {
835 LOG(ERROR) << __func__ << ": port id " << port->id
836 << " has NON_BLOCKING flag set, requires async callback";
837 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
838 }
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000839 StreamContext context;
Mikhail Naganov26dc9ad2023-06-23 13:55:37 -0700840 RETURN_STATUS_IF_ERROR(createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames,
841 isNonBlocking ? in_args.callback : nullptr,
842 in_args.eventCallback, &context));
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000843 context.fillDescriptor(&_aidl_return->desc);
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000844 std::shared_ptr<StreamOut> stream;
Mikhail Naganov6ddefdb2023-07-19 17:30:06 -0700845 RETURN_STATUS_IF_ERROR(createOutputStream(std::move(context), in_args.sourceMetadata,
Mikhail Naganov9d16a6a2023-06-26 17:21:04 -0700846 in_args.offloadInfo, &stream));
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000847 StreamWrapper streamWrapper(stream);
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700848 if (auto patchIt = mPatches.find(in_args.portConfigId); patchIt != mPatches.end()) {
849 RETURN_STATUS_IF_ERROR(
850 streamWrapper.setConnectedDevices(findConnectedDevices(in_args.portConfigId)));
851 }
Mikhail Naganovdf5feba2022-12-15 00:11:14 +0000852 AIBinder_setMinSchedulerPolicy(streamWrapper.getBinder().get(), SCHED_NORMAL,
853 ANDROID_PRIORITY_AUDIO);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000854 mStreams.insert(port->id, in_args.portConfigId, std::move(streamWrapper));
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000855 _aidl_return->stream = std::move(stream);
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000856 return ndk::ScopedAStatus::ok();
857}
858
Mikhail Naganov74927202022-12-19 16:37:14 +0000859ndk::ScopedAStatus Module::getSupportedPlaybackRateFactors(
860 SupportedPlaybackRateFactors* _aidl_return) {
861 LOG(DEBUG) << __func__;
862 (void)_aidl_return;
863 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
864}
865
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000866ndk::ScopedAStatus Module::setAudioPatch(const AudioPatch& in_requested, AudioPatch* _aidl_return) {
Mikhail Naganov16db9b72022-06-17 21:36:18 +0000867 LOG(DEBUG) << __func__ << ": requested patch " << in_requested.toString();
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000868 if (in_requested.sourcePortConfigIds.empty()) {
869 LOG(ERROR) << __func__ << ": requested patch has empty sources list";
870 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
871 }
872 if (!all_unique<int32_t>(in_requested.sourcePortConfigIds)) {
873 LOG(ERROR) << __func__ << ": requested patch has duplicate ids in the sources list";
874 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
875 }
876 if (in_requested.sinkPortConfigIds.empty()) {
877 LOG(ERROR) << __func__ << ": requested patch has empty sinks list";
878 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
879 }
880 if (!all_unique<int32_t>(in_requested.sinkPortConfigIds)) {
881 LOG(ERROR) << __func__ << ": requested patch has duplicate ids in the sinks list";
882 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
883 }
884
885 auto& configs = getConfig().portConfigs;
886 std::vector<int32_t> missingIds;
887 auto sources =
888 selectByIds<AudioPortConfig>(configs, in_requested.sourcePortConfigIds, &missingIds);
889 if (!missingIds.empty()) {
890 LOG(ERROR) << __func__ << ": following source port config ids not found: "
891 << ::android::internal::ToString(missingIds);
892 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
893 }
894 auto sinks = selectByIds<AudioPortConfig>(configs, in_requested.sinkPortConfigIds, &missingIds);
895 if (!missingIds.empty()) {
896 LOG(ERROR) << __func__ << ": following sink port config ids not found: "
897 << ::android::internal::ToString(missingIds);
898 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
899 }
900 // bool indicates whether a non-exclusive route is available.
901 // If only an exclusive route is available, that means the patch can not be
902 // established if there is any other patch which currently uses the sink port.
903 std::map<int32_t, bool> allowedSinkPorts;
904 auto& routes = getConfig().routes;
905 for (auto src : sources) {
906 for (const auto& r : routes) {
907 const auto& srcs = r.sourcePortIds;
908 if (std::find(srcs.begin(), srcs.end(), src->portId) != srcs.end()) {
909 if (!allowedSinkPorts[r.sinkPortId]) { // prefer non-exclusive
910 allowedSinkPorts[r.sinkPortId] = !r.isExclusive;
911 }
912 }
913 }
914 }
915 for (auto sink : sinks) {
916 if (allowedSinkPorts.count(sink->portId) == 0) {
917 LOG(ERROR) << __func__ << ": there is no route to the sink port id " << sink->portId;
918 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
919 }
920 }
Mikhail Naganov26dc9ad2023-06-23 13:55:37 -0700921 RETURN_STATUS_IF_ERROR(checkAudioPatchEndpointsMatch(sources, sinks));
jiabin253bd322023-01-25 23:57:31 +0000922
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000923 auto& patches = getConfig().patches;
924 auto existing = patches.end();
925 std::optional<decltype(mPatches)> patchesBackup;
926 if (in_requested.id != 0) {
927 existing = findById<AudioPatch>(patches, in_requested.id);
928 if (existing != patches.end()) {
929 patchesBackup = mPatches;
930 cleanUpPatch(existing->id);
931 } else {
932 LOG(ERROR) << __func__ << ": not found existing patch id " << in_requested.id;
933 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
934 }
935 }
936 // Validate the requested patch.
937 for (const auto& [sinkPortId, nonExclusive] : allowedSinkPorts) {
938 if (!nonExclusive && mPatches.count(sinkPortId) != 0) {
939 LOG(ERROR) << __func__ << ": sink port id " << sinkPortId
940 << "is exclusive and is already used by some other patch";
941 if (patchesBackup.has_value()) {
942 mPatches = std::move(*patchesBackup);
943 }
944 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
945 }
946 }
947 *_aidl_return = in_requested;
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000948 _aidl_return->minimumStreamBufferSizeFrames = kMinimumStreamBufferSizeFrames;
949 _aidl_return->latenciesMs.clear();
950 _aidl_return->latenciesMs.insert(_aidl_return->latenciesMs.end(),
951 _aidl_return->sinkPortConfigIds.size(), kLatencyMs);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000952 AudioPatch oldPatch{};
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000953 if (existing == patches.end()) {
954 _aidl_return->id = getConfig().nextPatchId++;
955 patches.push_back(*_aidl_return);
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000956 } else {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000957 oldPatch = *existing;
Mikhail Naganovdc417732023-09-29 15:49:35 -0700958 *existing = *_aidl_return;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000959 }
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700960 patchesBackup = mPatches;
961 registerPatch(*_aidl_return);
962 if (auto status = updateStreamsConnectedState(oldPatch, *_aidl_return); !status.isOk()) {
963 mPatches = std::move(*patchesBackup);
964 if (existing == patches.end()) {
965 patches.pop_back();
966 } else {
967 *existing = oldPatch;
968 }
969 return status;
970 }
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000971
972 LOG(DEBUG) << __func__ << ": " << (oldPatch.id == 0 ? "created" : "updated") << " patch "
973 << _aidl_return->toString();
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000974 return ndk::ScopedAStatus::ok();
975}
976
977ndk::ScopedAStatus Module::setAudioPortConfig(const AudioPortConfig& in_requested,
978 AudioPortConfig* out_suggested, bool* _aidl_return) {
979 LOG(DEBUG) << __func__ << ": requested " << in_requested.toString();
980 auto& configs = getConfig().portConfigs;
981 auto existing = configs.end();
982 if (in_requested.id != 0) {
983 if (existing = findById<AudioPortConfig>(configs, in_requested.id);
984 existing == configs.end()) {
985 LOG(ERROR) << __func__ << ": existing port config id " << in_requested.id
986 << " not found";
987 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
988 }
989 }
990
991 const int portId = existing != configs.end() ? existing->portId : in_requested.portId;
992 if (portId == 0) {
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700993 LOG(ERROR) << __func__ << ": requested port config does not specify portId";
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000994 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
995 }
996 auto& ports = getConfig().ports;
997 auto portIt = findById<AudioPort>(ports, portId);
998 if (portIt == ports.end()) {
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700999 LOG(ERROR) << __func__ << ": requested port config points to non-existent portId "
1000 << portId;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001001 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1002 }
1003 if (existing != configs.end()) {
1004 *out_suggested = *existing;
1005 } else {
1006 AudioPortConfig newConfig;
1007 if (generateDefaultPortConfig(*portIt, &newConfig)) {
1008 *out_suggested = newConfig;
1009 } else {
1010 LOG(ERROR) << __func__ << ": unable generate a default config for port " << portId;
1011 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1012 }
1013 }
1014 // From this moment, 'out_suggested' is either an existing port config,
1015 // or a new generated config. Now attempt to update it according to the specified
1016 // fields of 'in_requested'.
1017
Mikhail Naganov84bcc042023-10-05 17:36:57 -07001018 // Device ports with only dynamic profiles are used for devices that are connected via ADSP,
1019 // which takes care of their actual configuration automatically.
1020 const bool allowDynamicConfig = portIt->ext.getTag() == AudioPortExt::device &&
1021 hasDynamicProfilesOnly(portIt->profiles);
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001022 bool requestedIsValid = true, requestedIsFullySpecified = true;
1023
1024 AudioIoFlags portFlags = portIt->flags;
1025 if (in_requested.flags.has_value()) {
1026 if (in_requested.flags.value() != portFlags) {
1027 LOG(WARNING) << __func__ << ": requested flags "
1028 << in_requested.flags.value().toString() << " do not match port's "
1029 << portId << " flags " << portFlags.toString();
1030 requestedIsValid = false;
1031 }
1032 } else {
1033 requestedIsFullySpecified = false;
1034 }
1035
1036 AudioProfile portProfile;
1037 if (in_requested.format.has_value()) {
1038 const auto& format = in_requested.format.value();
Mikhail Naganov84bcc042023-10-05 17:36:57 -07001039 if ((format == AudioFormatDescription{} && allowDynamicConfig) ||
1040 findAudioProfile(*portIt, format, &portProfile)) {
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001041 out_suggested->format = format;
1042 } else {
1043 LOG(WARNING) << __func__ << ": requested format " << format.toString()
Mikhail Naganov84bcc042023-10-05 17:36:57 -07001044 << " is not found in the profiles of port " << portId;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001045 requestedIsValid = false;
1046 }
1047 } else {
1048 requestedIsFullySpecified = false;
1049 }
Mikhail Naganov84bcc042023-10-05 17:36:57 -07001050 if (!(out_suggested->format.value() == AudioFormatDescription{} && allowDynamicConfig) &&
1051 !findAudioProfile(*portIt, out_suggested->format.value(), &portProfile)) {
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001052 LOG(ERROR) << __func__ << ": port " << portId << " does not support format "
1053 << out_suggested->format.value().toString() << " anymore";
1054 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1055 }
1056
1057 if (in_requested.channelMask.has_value()) {
1058 const auto& channelMask = in_requested.channelMask.value();
Mikhail Naganov84bcc042023-10-05 17:36:57 -07001059 if ((channelMask == AudioChannelLayout{} && allowDynamicConfig) ||
1060 find(portProfile.channelMasks.begin(), portProfile.channelMasks.end(), channelMask) !=
1061 portProfile.channelMasks.end()) {
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001062 out_suggested->channelMask = channelMask;
1063 } else {
1064 LOG(WARNING) << __func__ << ": requested channel mask " << channelMask.toString()
1065 << " is not supported for the format " << portProfile.format.toString()
1066 << " by the port " << portId;
1067 requestedIsValid = false;
1068 }
1069 } else {
1070 requestedIsFullySpecified = false;
1071 }
1072
1073 if (in_requested.sampleRate.has_value()) {
1074 const auto& sampleRate = in_requested.sampleRate.value();
Mikhail Naganov84bcc042023-10-05 17:36:57 -07001075 if ((sampleRate.value == 0 && allowDynamicConfig) ||
1076 find(portProfile.sampleRates.begin(), portProfile.sampleRates.end(),
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001077 sampleRate.value) != portProfile.sampleRates.end()) {
1078 out_suggested->sampleRate = sampleRate;
1079 } else {
1080 LOG(WARNING) << __func__ << ": requested sample rate " << sampleRate.value
1081 << " is not supported for the format " << portProfile.format.toString()
1082 << " by the port " << portId;
1083 requestedIsValid = false;
1084 }
1085 } else {
1086 requestedIsFullySpecified = false;
1087 }
1088
1089 if (in_requested.gain.has_value()) {
1090 // Let's pretend that gain can always be applied.
1091 out_suggested->gain = in_requested.gain.value();
1092 }
1093
Mikhail Naganov248e9502023-02-21 16:32:40 -08001094 if (in_requested.ext.getTag() != AudioPortExt::Tag::unspecified) {
1095 if (in_requested.ext.getTag() == out_suggested->ext.getTag()) {
1096 if (out_suggested->ext.getTag() == AudioPortExt::Tag::mix) {
1097 // 'AudioMixPortExt.handle' is set by the client, copy from in_requested
1098 out_suggested->ext.get<AudioPortExt::Tag::mix>().handle =
1099 in_requested.ext.get<AudioPortExt::Tag::mix>().handle;
1100 }
1101 } else {
1102 LOG(WARNING) << __func__ << ": requested ext tag "
1103 << toString(in_requested.ext.getTag()) << " do not match port's tag "
1104 << toString(out_suggested->ext.getTag());
1105 requestedIsValid = false;
1106 }
1107 }
1108
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001109 if (existing == configs.end() && requestedIsValid && requestedIsFullySpecified) {
1110 out_suggested->id = getConfig().nextPortId++;
1111 configs.push_back(*out_suggested);
1112 *_aidl_return = true;
1113 LOG(DEBUG) << __func__ << ": created new port config " << out_suggested->toString();
1114 } else if (existing != configs.end() && requestedIsValid) {
1115 *existing = *out_suggested;
1116 *_aidl_return = true;
1117 LOG(DEBUG) << __func__ << ": updated port config " << out_suggested->toString();
1118 } else {
1119 LOG(DEBUG) << __func__ << ": not applied; existing config ? " << (existing != configs.end())
1120 << "; requested is valid? " << requestedIsValid << ", fully specified? "
1121 << requestedIsFullySpecified;
1122 *_aidl_return = false;
1123 }
1124 return ndk::ScopedAStatus::ok();
1125}
1126
1127ndk::ScopedAStatus Module::resetAudioPatch(int32_t in_patchId) {
1128 auto& patches = getConfig().patches;
1129 auto patchIt = findById<AudioPatch>(patches, in_patchId);
1130 if (patchIt != patches.end()) {
Mikhail Naganov75b59df2023-06-23 13:39:40 -07001131 auto patchesBackup = mPatches;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001132 cleanUpPatch(patchIt->id);
Mikhail Naganov75b59df2023-06-23 13:39:40 -07001133 if (auto status = updateStreamsConnectedState(*patchIt, AudioPatch{}); !status.isOk()) {
1134 mPatches = std::move(patchesBackup);
1135 return status;
1136 }
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001137 patches.erase(patchIt);
1138 LOG(DEBUG) << __func__ << ": erased patch " << in_patchId;
1139 return ndk::ScopedAStatus::ok();
1140 }
1141 LOG(ERROR) << __func__ << ": patch id " << in_patchId << " not found";
1142 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1143}
1144
1145ndk::ScopedAStatus Module::resetAudioPortConfig(int32_t in_portConfigId) {
1146 auto& configs = getConfig().portConfigs;
1147 auto configIt = findById<AudioPortConfig>(configs, in_portConfigId);
1148 if (configIt != configs.end()) {
1149 if (mStreams.count(in_portConfigId) != 0) {
1150 LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
1151 << " has a stream opened on it";
1152 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
1153 }
1154 auto patchIt = mPatches.find(in_portConfigId);
1155 if (patchIt != mPatches.end()) {
1156 LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
1157 << " is used by the patch with id " << patchIt->second;
1158 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
1159 }
1160 auto& initials = getConfig().initialConfigs;
1161 auto initialIt = findById<AudioPortConfig>(initials, in_portConfigId);
1162 if (initialIt == initials.end()) {
1163 configs.erase(configIt);
1164 LOG(DEBUG) << __func__ << ": erased port config " << in_portConfigId;
1165 } else if (*configIt != *initialIt) {
1166 *configIt = *initialIt;
1167 LOG(DEBUG) << __func__ << ": reset port config " << in_portConfigId;
1168 }
1169 return ndk::ScopedAStatus::ok();
1170 }
1171 LOG(ERROR) << __func__ << ": port config id " << in_portConfigId << " not found";
1172 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1173}
1174
Mikhail Naganov3b125b72022-10-05 02:12:39 +00001175ndk::ScopedAStatus Module::getMasterMute(bool* _aidl_return) {
1176 *_aidl_return = mMasterMute;
1177 LOG(DEBUG) << __func__ << ": returning " << *_aidl_return;
1178 return ndk::ScopedAStatus::ok();
1179}
1180
1181ndk::ScopedAStatus Module::setMasterMute(bool in_mute) {
1182 LOG(DEBUG) << __func__ << ": " << in_mute;
jiabin783c48b2023-02-28 18:28:06 +00001183 auto result = mDebug.simulateDeviceConnections ? ndk::ScopedAStatus::ok()
1184 : onMasterMuteChanged(in_mute);
1185 if (result.isOk()) {
1186 mMasterMute = in_mute;
1187 } else {
1188 LOG(ERROR) << __func__ << ": failed calling onMasterMuteChanged(" << in_mute
1189 << "), error=" << result;
1190 // Reset master mute if it failed.
1191 onMasterMuteChanged(mMasterMute);
1192 }
1193 return std::move(result);
Mikhail Naganov3b125b72022-10-05 02:12:39 +00001194}
1195
1196ndk::ScopedAStatus Module::getMasterVolume(float* _aidl_return) {
1197 *_aidl_return = mMasterVolume;
1198 LOG(DEBUG) << __func__ << ": returning " << *_aidl_return;
1199 return ndk::ScopedAStatus::ok();
1200}
1201
1202ndk::ScopedAStatus Module::setMasterVolume(float in_volume) {
1203 LOG(DEBUG) << __func__ << ": " << in_volume;
1204 if (in_volume >= 0.0f && in_volume <= 1.0f) {
jiabin783c48b2023-02-28 18:28:06 +00001205 auto result = mDebug.simulateDeviceConnections ? ndk::ScopedAStatus::ok()
1206 : onMasterVolumeChanged(in_volume);
1207 if (result.isOk()) {
1208 mMasterVolume = in_volume;
1209 } else {
1210 // Reset master volume if it failed.
1211 LOG(ERROR) << __func__ << ": failed calling onMasterVolumeChanged(" << in_volume
1212 << "), error=" << result;
1213 onMasterVolumeChanged(mMasterVolume);
1214 }
1215 return std::move(result);
Mikhail Naganov3b125b72022-10-05 02:12:39 +00001216 }
1217 LOG(ERROR) << __func__ << ": invalid master volume value: " << in_volume;
1218 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1219}
1220
1221ndk::ScopedAStatus Module::getMicMute(bool* _aidl_return) {
1222 *_aidl_return = mMicMute;
1223 LOG(DEBUG) << __func__ << ": returning " << *_aidl_return;
1224 return ndk::ScopedAStatus::ok();
1225}
1226
1227ndk::ScopedAStatus Module::setMicMute(bool in_mute) {
1228 LOG(DEBUG) << __func__ << ": " << in_mute;
1229 mMicMute = in_mute;
1230 return ndk::ScopedAStatus::ok();
1231}
1232
Mikhail Naganovef6bc742022-10-06 00:14:19 +00001233ndk::ScopedAStatus Module::getMicrophones(std::vector<MicrophoneInfo>* _aidl_return) {
Mikhail Naganov5eea7fb2023-10-13 23:38:25 +00001234 *_aidl_return = getConfig().microphones;
Mikhail Naganovef6bc742022-10-06 00:14:19 +00001235 LOG(DEBUG) << __func__ << ": returning " << ::android::internal::ToString(*_aidl_return);
1236 return ndk::ScopedAStatus::ok();
1237}
1238
Mikhail Naganov3b125b72022-10-05 02:12:39 +00001239ndk::ScopedAStatus Module::updateAudioMode(AudioMode in_mode) {
Mikhail Naganov04ae8222023-01-11 15:48:10 -08001240 if (!isValidAudioMode(in_mode)) {
1241 LOG(ERROR) << __func__ << ": invalid mode " << toString(in_mode);
1242 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1243 }
Mikhail Naganov3b125b72022-10-05 02:12:39 +00001244 // No checks for supported audio modes here, it's an informative notification.
1245 LOG(DEBUG) << __func__ << ": " << toString(in_mode);
1246 return ndk::ScopedAStatus::ok();
1247}
1248
1249ndk::ScopedAStatus Module::updateScreenRotation(ScreenRotation in_rotation) {
1250 LOG(DEBUG) << __func__ << ": " << toString(in_rotation);
1251 return ndk::ScopedAStatus::ok();
1252}
1253
1254ndk::ScopedAStatus Module::updateScreenState(bool in_isTurnedOn) {
1255 LOG(DEBUG) << __func__ << ": " << in_isTurnedOn;
1256 return ndk::ScopedAStatus::ok();
1257}
1258
Vlad Popa83a6d822022-11-07 13:53:57 +01001259ndk::ScopedAStatus Module::getSoundDose(std::shared_ptr<ISoundDose>* _aidl_return) {
Mikhail Naganov7499a002023-02-27 18:51:44 -08001260 if (!mSoundDose) {
Vlad Popa2afbd1e2022-12-28 17:04:58 +01001261 mSoundDose = ndk::SharedRefBase::make<sounddose::SoundDose>();
Vlad Popa943b7e22022-12-08 14:24:12 +01001262 }
Mikhail Naganov780fefb2023-07-21 17:01:38 -07001263 *_aidl_return = mSoundDose.getInstance();
Vlad Popa943b7e22022-12-08 14:24:12 +01001264 LOG(DEBUG) << __func__ << ": returning instance of ISoundDose: " << _aidl_return->get();
Vlad Popa83a6d822022-11-07 13:53:57 +01001265 return ndk::ScopedAStatus::ok();
1266}
1267
Mikhail Naganove9f10fc2022-10-14 23:31:52 +00001268ndk::ScopedAStatus Module::generateHwAvSyncId(int32_t* _aidl_return) {
1269 LOG(DEBUG) << __func__;
1270 (void)_aidl_return;
1271 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
1272}
1273
Mikhail Naganov20047bc2023-01-05 20:16:07 +00001274const std::string Module::VendorDebug::kForceTransientBurstName = "aosp.forceTransientBurst";
Mikhail Naganov194daaa2023-01-05 22:34:20 +00001275const std::string Module::VendorDebug::kForceSynchronousDrainName = "aosp.forceSynchronousDrain";
Mikhail Naganov20047bc2023-01-05 20:16:07 +00001276
Mikhail Naganove9f10fc2022-10-14 23:31:52 +00001277ndk::ScopedAStatus Module::getVendorParameters(const std::vector<std::string>& in_ids,
1278 std::vector<VendorParameter>* _aidl_return) {
1279 LOG(DEBUG) << __func__ << ": id count: " << in_ids.size();
Mikhail Naganov20047bc2023-01-05 20:16:07 +00001280 bool allParametersKnown = true;
1281 for (const auto& id : in_ids) {
1282 if (id == VendorDebug::kForceTransientBurstName) {
1283 VendorParameter forceTransientBurst{.id = id};
1284 forceTransientBurst.ext.setParcelable(Boolean{mVendorDebug.forceTransientBurst});
1285 _aidl_return->push_back(std::move(forceTransientBurst));
Mikhail Naganov194daaa2023-01-05 22:34:20 +00001286 } else if (id == VendorDebug::kForceSynchronousDrainName) {
1287 VendorParameter forceSynchronousDrain{.id = id};
1288 forceSynchronousDrain.ext.setParcelable(Boolean{mVendorDebug.forceSynchronousDrain});
1289 _aidl_return->push_back(std::move(forceSynchronousDrain));
Mikhail Naganov20047bc2023-01-05 20:16:07 +00001290 } else {
1291 allParametersKnown = false;
1292 LOG(ERROR) << __func__ << ": unrecognized parameter \"" << id << "\"";
1293 }
1294 }
1295 if (allParametersKnown) return ndk::ScopedAStatus::ok();
1296 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
Mikhail Naganove9f10fc2022-10-14 23:31:52 +00001297}
1298
Mikhail Naganov194daaa2023-01-05 22:34:20 +00001299namespace {
1300
1301template <typename W>
1302bool extractParameter(const VendorParameter& p, decltype(W::value)* v) {
1303 std::optional<W> value;
1304 binder_status_t result = p.ext.getParcelable(&value);
1305 if (result == STATUS_OK && value.has_value()) {
1306 *v = value.value().value;
1307 return true;
1308 }
1309 LOG(ERROR) << __func__ << ": failed to read the value of the parameter \"" << p.id
1310 << "\": " << result;
1311 return false;
1312}
1313
1314} // namespace
1315
Mikhail Naganove9f10fc2022-10-14 23:31:52 +00001316ndk::ScopedAStatus Module::setVendorParameters(const std::vector<VendorParameter>& in_parameters,
1317 bool in_async) {
1318 LOG(DEBUG) << __func__ << ": parameter count " << in_parameters.size()
1319 << ", async: " << in_async;
Mikhail Naganov20047bc2023-01-05 20:16:07 +00001320 bool allParametersKnown = true;
1321 for (const auto& p : in_parameters) {
1322 if (p.id == VendorDebug::kForceTransientBurstName) {
Mikhail Naganov194daaa2023-01-05 22:34:20 +00001323 if (!extractParameter<Boolean>(p, &mVendorDebug.forceTransientBurst)) {
1324 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1325 }
1326 } else if (p.id == VendorDebug::kForceSynchronousDrainName) {
1327 if (!extractParameter<Boolean>(p, &mVendorDebug.forceSynchronousDrain)) {
Mikhail Naganov20047bc2023-01-05 20:16:07 +00001328 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1329 }
1330 } else {
1331 allParametersKnown = false;
1332 LOG(ERROR) << __func__ << ": unrecognized parameter \"" << p.id << "\"";
1333 }
1334 }
1335 if (allParametersKnown) return ndk::ScopedAStatus::ok();
1336 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
Mikhail Naganove9f10fc2022-10-14 23:31:52 +00001337}
1338
Mikhail Naganovfb1acde2022-12-12 18:57:36 +00001339ndk::ScopedAStatus Module::addDeviceEffect(
1340 int32_t in_portConfigId,
1341 const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) {
1342 if (in_effect == nullptr) {
1343 LOG(DEBUG) << __func__ << ": port id " << in_portConfigId << ", null effect";
1344 } else {
1345 LOG(DEBUG) << __func__ << ": port id " << in_portConfigId << ", effect Binder "
1346 << in_effect->asBinder().get();
1347 }
1348 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
1349}
1350
1351ndk::ScopedAStatus Module::removeDeviceEffect(
1352 int32_t in_portConfigId,
1353 const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) {
1354 if (in_effect == nullptr) {
1355 LOG(DEBUG) << __func__ << ": port id " << in_portConfigId << ", null effect";
1356 } else {
1357 LOG(DEBUG) << __func__ << ": port id " << in_portConfigId << ", effect Binder "
1358 << in_effect->asBinder().get();
1359 }
1360 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
1361}
1362
jiabin9a8e6862023-01-12 23:06:37 +00001363ndk::ScopedAStatus Module::getMmapPolicyInfos(AudioMMapPolicyType mmapPolicyType,
1364 std::vector<AudioMMapPolicyInfo>* _aidl_return) {
1365 LOG(DEBUG) << __func__ << ": mmap policy type " << toString(mmapPolicyType);
1366 std::set<int32_t> mmapSinks;
1367 std::set<int32_t> mmapSources;
1368 auto& ports = getConfig().ports;
1369 for (const auto& port : ports) {
1370 if (port.flags.getTag() == AudioIoFlags::Tag::input &&
1371 isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::input>(),
1372 AudioInputFlags::MMAP_NOIRQ)) {
1373 mmapSinks.insert(port.id);
1374 } else if (port.flags.getTag() == AudioIoFlags::Tag::output &&
1375 isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
1376 AudioOutputFlags::MMAP_NOIRQ)) {
1377 mmapSources.insert(port.id);
1378 }
1379 }
Mikhail Naganov85064912023-09-26 17:10:08 -07001380 if (mmapSources.empty() && mmapSinks.empty()) {
1381 AudioMMapPolicyInfo never;
1382 never.mmapPolicy = AudioMMapPolicy::NEVER;
1383 _aidl_return->push_back(never);
1384 return ndk::ScopedAStatus::ok();
1385 }
jiabin9a8e6862023-01-12 23:06:37 +00001386 for (const auto& route : getConfig().routes) {
1387 if (mmapSinks.count(route.sinkPortId) != 0) {
1388 // The sink is a mix port, add the sources if they are device ports.
1389 for (int sourcePortId : route.sourcePortIds) {
1390 auto sourcePortIt = findById<AudioPort>(ports, sourcePortId);
1391 if (sourcePortIt == ports.end()) {
1392 // This must not happen
1393 LOG(ERROR) << __func__ << ": port id " << sourcePortId << " cannot be found";
1394 continue;
1395 }
1396 if (sourcePortIt->ext.getTag() != AudioPortExt::Tag::device) {
1397 // The source is not a device port, skip
1398 continue;
1399 }
1400 AudioMMapPolicyInfo policyInfo;
1401 policyInfo.device = sourcePortIt->ext.get<AudioPortExt::Tag::device>().device;
1402 // Always return AudioMMapPolicy.AUTO if the device supports mmap for
1403 // default implementation.
1404 policyInfo.mmapPolicy = AudioMMapPolicy::AUTO;
1405 _aidl_return->push_back(policyInfo);
1406 }
1407 } else {
1408 auto sinkPortIt = findById<AudioPort>(ports, route.sinkPortId);
1409 if (sinkPortIt == ports.end()) {
1410 // This must not happen
1411 LOG(ERROR) << __func__ << ": port id " << route.sinkPortId << " cannot be found";
1412 continue;
1413 }
1414 if (sinkPortIt->ext.getTag() != AudioPortExt::Tag::device) {
1415 // The sink is not a device port, skip
1416 continue;
1417 }
1418 if (count_any(mmapSources, route.sourcePortIds)) {
1419 AudioMMapPolicyInfo policyInfo;
1420 policyInfo.device = sinkPortIt->ext.get<AudioPortExt::Tag::device>().device;
1421 // Always return AudioMMapPolicy.AUTO if the device supports mmap for
1422 // default implementation.
1423 policyInfo.mmapPolicy = AudioMMapPolicy::AUTO;
1424 _aidl_return->push_back(policyInfo);
1425 }
1426 }
1427 }
1428 return ndk::ScopedAStatus::ok();
1429}
1430
Eric Laurente2432ea2023-01-12 17:47:31 +01001431ndk::ScopedAStatus Module::supportsVariableLatency(bool* _aidl_return) {
1432 LOG(DEBUG) << __func__;
1433 *_aidl_return = false;
1434 return ndk::ScopedAStatus::ok();
1435}
1436
jiabinb76981e2023-01-18 00:58:30 +00001437ndk::ScopedAStatus Module::getAAudioMixerBurstCount(int32_t* _aidl_return) {
1438 if (!isMmapSupported()) {
1439 LOG(DEBUG) << __func__ << ": mmap is not supported ";
1440 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
1441 }
1442 *_aidl_return = DEFAULT_AAUDIO_MIXER_BURST_COUNT;
1443 LOG(DEBUG) << __func__ << ": returning " << *_aidl_return;
1444 return ndk::ScopedAStatus::ok();
1445}
1446
1447ndk::ScopedAStatus Module::getAAudioHardwareBurstMinUsec(int32_t* _aidl_return) {
1448 if (!isMmapSupported()) {
1449 LOG(DEBUG) << __func__ << ": mmap is not supported ";
1450 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
1451 }
1452 *_aidl_return = DEFAULT_AAUDIO_HARDWARE_BURST_MIN_DURATION_US;
1453 LOG(DEBUG) << __func__ << ": returning " << *_aidl_return;
1454 return ndk::ScopedAStatus::ok();
1455}
1456
1457bool Module::isMmapSupported() {
1458 if (mIsMmapSupported.has_value()) {
1459 return mIsMmapSupported.value();
1460 }
1461 std::vector<AudioMMapPolicyInfo> mmapPolicyInfos;
1462 if (!getMmapPolicyInfos(AudioMMapPolicyType::DEFAULT, &mmapPolicyInfos).isOk()) {
1463 mIsMmapSupported = false;
1464 } else {
1465 mIsMmapSupported =
1466 std::find_if(mmapPolicyInfos.begin(), mmapPolicyInfos.end(), [](const auto& info) {
1467 return info.mmapPolicy == AudioMMapPolicy::AUTO ||
1468 info.mmapPolicy == AudioMMapPolicy::ALWAYS;
1469 }) != mmapPolicyInfos.end();
1470 }
1471 return mIsMmapSupported.value();
1472}
1473
Mikhail Naganov84bcc042023-10-05 17:36:57 -07001474ndk::ScopedAStatus Module::populateConnectedDevicePort(AudioPort* audioPort) {
1475 if (audioPort->ext.getTag() != AudioPortExt::device) {
1476 LOG(ERROR) << __func__ << ": not a device port: " << audioPort->toString();
1477 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1478 }
1479 const auto& devicePort = audioPort->ext.get<AudioPortExt::device>();
1480 if (!devicePort.device.type.connection.empty()) {
1481 LOG(ERROR) << __func__
1482 << ": module implementation must override 'populateConnectedDevicePort' "
1483 << "to handle connection of external devices.";
1484 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
1485 }
jiabin116d8392023-03-01 22:52:57 +00001486 LOG(VERBOSE) << __func__ << ": do nothing and return ok";
jiabin253bd322023-01-25 23:57:31 +00001487 return ndk::ScopedAStatus::ok();
1488}
1489
1490ndk::ScopedAStatus Module::checkAudioPatchEndpointsMatch(
1491 const std::vector<AudioPortConfig*>& sources __unused,
1492 const std::vector<AudioPortConfig*>& sinks __unused) {
jiabin116d8392023-03-01 22:52:57 +00001493 LOG(VERBOSE) << __func__ << ": do nothing and return ok";
jiabin253bd322023-01-25 23:57:31 +00001494 return ndk::ScopedAStatus::ok();
1495}
1496
jiabin783c48b2023-02-28 18:28:06 +00001497void Module::onExternalDeviceConnectionChanged(
1498 const ::aidl::android::media::audio::common::AudioPort& audioPort __unused,
1499 bool connected __unused) {
1500 LOG(DEBUG) << __func__ << ": do nothing and return";
1501}
1502
1503ndk::ScopedAStatus Module::onMasterMuteChanged(bool mute __unused) {
1504 LOG(VERBOSE) << __func__ << ": do nothing and return ok";
1505 return ndk::ScopedAStatus::ok();
1506}
1507
1508ndk::ScopedAStatus Module::onMasterVolumeChanged(float volume __unused) {
1509 LOG(VERBOSE) << __func__ << ": do nothing and return ok";
1510 return ndk::ScopedAStatus::ok();
1511}
1512
Ram Mohan18f0d512023-07-01 00:47:09 +05301513Module::BtProfileHandles Module::getBtProfileManagerHandles() {
1514 return std::make_tuple(std::weak_ptr<IBluetooth>(), std::weak_ptr<IBluetoothA2dp>(),
1515 std::weak_ptr<IBluetoothLe>());
1516}
1517
1518ndk::ScopedAStatus Module::bluetoothParametersUpdated() {
1519 return mStreams.bluetoothParametersUpdated();
1520}
1521
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001522} // namespace aidl::android::hardware::audio::core