blob: f18705717473e7f8d69f90178061848f26b1f479 [file] [log] [blame]
Mikhail Naganovac9d4e72023-10-23 12:00:09 -07001/*
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#define LOG_TAG "Hal2AidlMapper"
18// #define LOG_NDEBUG 0
19
20#include <algorithm>
21
22#include <media/audiohal/StreamHalInterface.h>
23#include <error/expected_utils.h>
24#include <system/audio.h> // For AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS
25#include <Utils.h>
26#include <utils/Log.h>
27
28#include "Hal2AidlMapper.h"
29
30using aidl::android::aidl_utils::statusTFromBinderStatus;
31using aidl::android::media::audio::common::AudioChannelLayout;
32using aidl::android::media::audio::common::AudioConfig;
33using aidl::android::media::audio::common::AudioDevice;
34using aidl::android::media::audio::common::AudioDeviceAddress;
35using aidl::android::media::audio::common::AudioDeviceDescription;
36using aidl::android::media::audio::common::AudioDeviceType;
37using aidl::android::media::audio::common::AudioFormatDescription;
38using aidl::android::media::audio::common::AudioInputFlags;
39using aidl::android::media::audio::common::AudioIoFlags;
40using aidl::android::media::audio::common::AudioOutputFlags;
41using aidl::android::media::audio::common::AudioPort;
42using aidl::android::media::audio::common::AudioPortConfig;
43using aidl::android::media::audio::common::AudioPortDeviceExt;
44using aidl::android::media::audio::common::AudioPortExt;
45using aidl::android::media::audio::common::AudioPortMixExt;
46using aidl::android::media::audio::common::AudioPortMixExtUseCase;
47using aidl::android::media::audio::common::AudioProfile;
48using aidl::android::media::audio::common::AudioSource;
49using aidl::android::media::audio::common::Int;
50using aidl::android::hardware::audio::common::isBitPositionFlagSet;
51using aidl::android::hardware::audio::common::isDefaultAudioFormat;
52using aidl::android::hardware::audio::common::makeBitPositionFlagMask;
53using aidl::android::hardware::audio::core::AudioPatch;
54using aidl::android::hardware::audio::core::AudioRoute;
55using aidl::android::hardware::audio::core::IModule;
56
57namespace android {
58
59namespace {
60
61bool isConfigEqualToPortConfig(const AudioConfig& config, const AudioPortConfig& portConfig) {
62 return portConfig.sampleRate.value().value == config.base.sampleRate &&
63 portConfig.channelMask.value() == config.base.channelMask &&
64 portConfig.format.value() == config.base.format;
65}
66
67void setConfigFromPortConfig(AudioConfig* config, const AudioPortConfig& portConfig) {
68 config->base.sampleRate = portConfig.sampleRate.value().value;
69 config->base.channelMask = portConfig.channelMask.value();
70 config->base.format = portConfig.format.value();
71}
72
73void setPortConfigFromConfig(AudioPortConfig* portConfig, const AudioConfig& config) {
74 if (config.base.sampleRate != 0) {
75 portConfig->sampleRate = Int{ .value = config.base.sampleRate };
76 }
77 if (config.base.channelMask != AudioChannelLayout{}) {
78 portConfig->channelMask = config.base.channelMask;
79 }
80 if (config.base.format != AudioFormatDescription{}) {
81 portConfig->format = config.base.format;
82 }
83}
84
85} // namespace
86
87Hal2AidlMapper::Hal2AidlMapper(const std::string& instance, const std::shared_ptr<IModule>& module)
88 : mInstance(instance), mModule(module) {
89}
90
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -080091void Hal2AidlMapper::addStream(
92 const sp<StreamHalInterface>& stream, int32_t portConfigId, int32_t patchId) {
93 mStreams.insert(std::pair(stream, std::pair(portConfigId, patchId)));
Mikhail Naganovac9d4e72023-10-23 12:00:09 -070094}
95
96bool Hal2AidlMapper::audioDeviceMatches(const AudioDevice& device, const AudioPort& p) {
97 if (p.ext.getTag() != AudioPortExt::Tag::device) return false;
98 return p.ext.get<AudioPortExt::Tag::device>().device == device;
99}
100
101bool Hal2AidlMapper::audioDeviceMatches(const AudioDevice& device, const AudioPortConfig& p) {
102 if (p.ext.getTag() != AudioPortExt::Tag::device) return false;
103 if (device.type.type == AudioDeviceType::IN_DEFAULT) {
104 return p.portId == mDefaultInputPortId;
105 } else if (device.type.type == AudioDeviceType::OUT_DEFAULT) {
106 return p.portId == mDefaultOutputPortId;
107 }
108 return p.ext.get<AudioPortExt::Tag::device>().device == device;
109}
110
111status_t Hal2AidlMapper::createOrUpdatePatch(
112 const std::vector<AudioPortConfig>& sources,
113 const std::vector<AudioPortConfig>& sinks,
114 int32_t* patchId, Cleanups* cleanups) {
115 auto existingPatchIt = *patchId != 0 ? mPatches.find(*patchId): mPatches.end();
116 AudioPatch patch;
117 if (existingPatchIt != mPatches.end()) {
118 patch = existingPatchIt->second;
119 patch.sourcePortConfigIds.clear();
120 patch.sinkPortConfigIds.clear();
121 }
122 // The IDs will be found by 'fillPortConfigs', however the original 'sources' and
123 // 'sinks' will not be updated because 'setAudioPatch' only needs IDs. Here we log
124 // the source arguments, where only the audio configuration and device specifications
125 // are relevant.
126 ALOGD("%s: [disregard IDs] sources: %s, sinks: %s",
127 __func__, ::android::internal::ToString(sources).c_str(),
128 ::android::internal::ToString(sinks).c_str());
129 auto fillPortConfigs = [&](
130 const std::vector<AudioPortConfig>& configs,
131 const std::set<int32_t>& destinationPortIds,
132 std::vector<int32_t>* ids, std::set<int32_t>* portIds) -> status_t {
133 for (const auto& s : configs) {
134 AudioPortConfig portConfig;
135 RETURN_STATUS_IF_ERROR(findOrCreatePortConfig(
136 s, destinationPortIds, &portConfig, cleanups));
137 ids->push_back(portConfig.id);
138 if (portIds != nullptr) {
139 portIds->insert(portConfig.portId);
140 }
141 }
142 return OK;
143 };
144 // When looking up port configs, the destinationPortId is only used for mix ports.
145 // Thus, we process device port configs first, and look up the destination port ID from them.
146 bool sourceIsDevice = std::any_of(sources.begin(), sources.end(),
147 [](const auto& config) { return config.ext.getTag() == AudioPortExt::device; });
148 const std::vector<AudioPortConfig>& devicePortConfigs =
149 sourceIsDevice ? sources : sinks;
150 std::vector<int32_t>* devicePortConfigIds =
151 sourceIsDevice ? &patch.sourcePortConfigIds : &patch.sinkPortConfigIds;
152 const std::vector<AudioPortConfig>& mixPortConfigs =
153 sourceIsDevice ? sinks : sources;
154 std::vector<int32_t>* mixPortConfigIds =
155 sourceIsDevice ? &patch.sinkPortConfigIds : &patch.sourcePortConfigIds;
156 std::set<int32_t> devicePortIds;
157 RETURN_STATUS_IF_ERROR(fillPortConfigs(
158 devicePortConfigs, std::set<int32_t>(), devicePortConfigIds, &devicePortIds));
159 RETURN_STATUS_IF_ERROR(fillPortConfigs(
160 mixPortConfigs, devicePortIds, mixPortConfigIds, nullptr));
161 if (existingPatchIt != mPatches.end()) {
162 RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
163 mModule->setAudioPatch(patch, &patch)));
164 existingPatchIt->second = patch;
165 } else {
166 bool created = false;
167 RETURN_STATUS_IF_ERROR(findOrCreatePatch(patch, &patch, &created));
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800168 // No cleanup of the patch is needed, it is managed by the framework.
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700169 *patchId = patch.id;
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800170 if (!created) {
171 // The framework might have "created" a patch which already existed due to
172 // stream creation. Need to release the ownership from the stream.
173 for (auto& s : mStreams) {
174 if (s.second.second == patch.id) s.second.second = -1;
175 }
176 }
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700177 }
178 return OK;
179}
180
181status_t Hal2AidlMapper::createOrUpdatePortConfig(
182 const AudioPortConfig& requestedPortConfig, PortConfigs::iterator* result, bool* created) {
183 AudioPortConfig appliedPortConfig;
184 bool applied = false;
185 RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->setAudioPortConfig(
186 requestedPortConfig, &appliedPortConfig, &applied)));
187 if (!applied) {
188 RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->setAudioPortConfig(
189 appliedPortConfig, &appliedPortConfig, &applied)));
190 if (!applied) {
191 ALOGE("%s: module %s did not apply suggested config %s",
192 __func__, mInstance.c_str(), appliedPortConfig.toString().c_str());
193 return NO_INIT;
194 }
195 }
196
197 int32_t id = appliedPortConfig.id;
198 if (requestedPortConfig.id != 0 && requestedPortConfig.id != id) {
199 LOG_ALWAYS_FATAL("%s: requested port config id %d changed to %d", __func__,
200 requestedPortConfig.id, id);
201 }
202
203 auto [it, inserted] = mPortConfigs.insert_or_assign(std::move(id),
204 std::move(appliedPortConfig));
205 *result = it;
206 *created = inserted;
207 return OK;
208}
209
210void Hal2AidlMapper::eraseConnectedPort(int32_t portId) {
211 mPorts.erase(portId);
212 mConnectedPorts.erase(portId);
213 if (mDisconnectedPortReplacement.first == portId) {
214 const auto& port = mDisconnectedPortReplacement.second;
215 mPorts.insert(std::make_pair(port.id, port));
216 ALOGD("%s: disconnected port replacement: %s", __func__, port.toString().c_str());
217 mDisconnectedPortReplacement = std::pair<int32_t, AudioPort>();
218 }
219}
220
221status_t Hal2AidlMapper::findOrCreatePatch(
222 const AudioPatch& requestedPatch, AudioPatch* patch, bool* created) {
223 std::set<int32_t> sourcePortConfigIds(requestedPatch.sourcePortConfigIds.begin(),
224 requestedPatch.sourcePortConfigIds.end());
225 std::set<int32_t> sinkPortConfigIds(requestedPatch.sinkPortConfigIds.begin(),
226 requestedPatch.sinkPortConfigIds.end());
227 return findOrCreatePatch(sourcePortConfigIds, sinkPortConfigIds, patch, created);
228}
229
230status_t Hal2AidlMapper::findOrCreatePatch(
231 const std::set<int32_t>& sourcePortConfigIds, const std::set<int32_t>& sinkPortConfigIds,
232 AudioPatch* patch, bool* created) {
233 auto patchIt = findPatch(sourcePortConfigIds, sinkPortConfigIds);
234 if (patchIt == mPatches.end()) {
235 AudioPatch requestedPatch, appliedPatch;
236 requestedPatch.sourcePortConfigIds.insert(requestedPatch.sourcePortConfigIds.end(),
237 sourcePortConfigIds.begin(), sourcePortConfigIds.end());
238 requestedPatch.sinkPortConfigIds.insert(requestedPatch.sinkPortConfigIds.end(),
239 sinkPortConfigIds.begin(), sinkPortConfigIds.end());
240 RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->setAudioPatch(
241 requestedPatch, &appliedPatch)));
242 patchIt = mPatches.insert(mPatches.end(), std::make_pair(appliedPatch.id, appliedPatch));
243 *created = true;
244 } else {
245 *created = false;
246 }
247 *patch = patchIt->second;
248 return OK;
249}
250
251status_t Hal2AidlMapper::findOrCreatePortConfig(
252 const AudioDevice& device, const AudioConfig* config, AudioPortConfig* portConfig,
253 bool* created) {
254 auto portConfigIt = findPortConfig(device);
255 if (portConfigIt == mPortConfigs.end()) {
256 auto portsIt = findPort(device);
257 if (portsIt == mPorts.end()) {
258 ALOGE("%s: device port for device %s is not found in the module %s",
259 __func__, device.toString().c_str(), mInstance.c_str());
260 return BAD_VALUE;
261 }
262 AudioPortConfig requestedPortConfig;
263 requestedPortConfig.portId = portsIt->first;
264 if (config != nullptr) {
265 setPortConfigFromConfig(&requestedPortConfig, *config);
266 }
267 RETURN_STATUS_IF_ERROR(createOrUpdatePortConfig(requestedPortConfig, &portConfigIt,
268 created));
269 } else {
270 *created = false;
271 }
272 *portConfig = portConfigIt->second;
273 return OK;
274}
275
276status_t Hal2AidlMapper::findOrCreatePortConfig(
277 const AudioConfig& config, const std::optional<AudioIoFlags>& flags, int32_t ioHandle,
278 AudioSource source, const std::set<int32_t>& destinationPortIds,
279 AudioPortConfig* portConfig, bool* created) {
280 // These flags get removed one by one in this order when retrying port finding.
281 static const std::vector<AudioInputFlags> kOptionalInputFlags{
282 AudioInputFlags::FAST, AudioInputFlags::RAW, AudioInputFlags::VOIP_TX };
283 auto portConfigIt = findPortConfig(config, flags, ioHandle);
284 if (portConfigIt == mPortConfigs.end() && flags.has_value()) {
285 auto optionalInputFlagsIt = kOptionalInputFlags.begin();
286 AudioIoFlags matchFlags = flags.value();
287 auto portsIt = findPort(config, matchFlags, destinationPortIds);
288 while (portsIt == mPorts.end() && matchFlags.getTag() == AudioIoFlags::Tag::input
289 && optionalInputFlagsIt != kOptionalInputFlags.end()) {
290 if (!isBitPositionFlagSet(
291 matchFlags.get<AudioIoFlags::Tag::input>(), *optionalInputFlagsIt)) {
292 ++optionalInputFlagsIt;
293 continue;
294 }
295 matchFlags.set<AudioIoFlags::Tag::input>(matchFlags.get<AudioIoFlags::Tag::input>() &
296 ~makeBitPositionFlagMask(*optionalInputFlagsIt++));
297 portsIt = findPort(config, matchFlags, destinationPortIds);
298 ALOGI("%s: mix port for config %s, flags %s was not found in the module %s, "
299 "retried with flags %s", __func__, config.toString().c_str(),
300 flags.value().toString().c_str(), mInstance.c_str(),
301 matchFlags.toString().c_str());
302 }
303 if (portsIt == mPorts.end()) {
304 ALOGE("%s: mix port for config %s, flags %s is not found in the module %s",
305 __func__, config.toString().c_str(), matchFlags.toString().c_str(),
306 mInstance.c_str());
307 return BAD_VALUE;
308 }
309 AudioPortConfig requestedPortConfig;
310 requestedPortConfig.portId = portsIt->first;
311 setPortConfigFromConfig(&requestedPortConfig, config);
312 requestedPortConfig.ext = AudioPortMixExt{ .handle = ioHandle };
313 if (matchFlags.getTag() == AudioIoFlags::Tag::input
314 && source != AudioSource::SYS_RESERVED_INVALID) {
315 requestedPortConfig.ext.get<AudioPortExt::Tag::mix>().usecase =
316 AudioPortMixExtUseCase::make<AudioPortMixExtUseCase::Tag::source>(source);
317 }
318 RETURN_STATUS_IF_ERROR(createOrUpdatePortConfig(requestedPortConfig, &portConfigIt,
319 created));
320 } else if (portConfigIt == mPortConfigs.end() && !flags.has_value()) {
321 ALOGW("%s: mix port config for %s, handle %d not found in the module %s, "
322 "and was not created as flags are not specified",
323 __func__, config.toString().c_str(), ioHandle, mInstance.c_str());
324 return BAD_VALUE;
325 } else {
326 AudioPortConfig requestedPortConfig = portConfigIt->second;
327 if (requestedPortConfig.ext.getTag() == AudioPortExt::Tag::mix) {
328 AudioPortMixExt& mixExt = requestedPortConfig.ext.get<AudioPortExt::Tag::mix>();
329 if (mixExt.usecase.getTag() == AudioPortMixExtUseCase::Tag::source &&
330 source != AudioSource::SYS_RESERVED_INVALID) {
331 mixExt.usecase.get<AudioPortMixExtUseCase::Tag::source>() = source;
332 }
333 }
334
335 if (requestedPortConfig != portConfigIt->second) {
336 RETURN_STATUS_IF_ERROR(createOrUpdatePortConfig(requestedPortConfig, &portConfigIt,
337 created));
338 } else {
339 *created = false;
340 }
341 }
342 *portConfig = portConfigIt->second;
343 return OK;
344}
345
346status_t Hal2AidlMapper::findOrCreatePortConfig(
347 const AudioPortConfig& requestedPortConfig, const std::set<int32_t>& destinationPortIds,
348 AudioPortConfig* portConfig, bool* created) {
349 using Tag = AudioPortExt::Tag;
350 if (requestedPortConfig.ext.getTag() == Tag::mix) {
351 if (const auto& p = requestedPortConfig;
352 !p.sampleRate.has_value() || !p.channelMask.has_value() ||
353 !p.format.has_value()) {
354 ALOGW("%s: provided mix port config is not fully specified: %s",
355 __func__, p.toString().c_str());
356 return BAD_VALUE;
357 }
358 AudioConfig config;
359 setConfigFromPortConfig(&config, requestedPortConfig);
360 AudioSource source = requestedPortConfig.ext.get<Tag::mix>().usecase.getTag() ==
361 AudioPortMixExtUseCase::Tag::source ?
362 requestedPortConfig.ext.get<Tag::mix>().usecase.
363 get<AudioPortMixExtUseCase::Tag::source>() : AudioSource::SYS_RESERVED_INVALID;
364 return findOrCreatePortConfig(config, requestedPortConfig.flags,
365 requestedPortConfig.ext.get<Tag::mix>().handle, source, destinationPortIds,
366 portConfig, created);
367 } else if (requestedPortConfig.ext.getTag() == Tag::device) {
368 return findOrCreatePortConfig(
369 requestedPortConfig.ext.get<Tag::device>().device, nullptr /*config*/,
370 portConfig, created);
371 }
372 ALOGW("%s: unsupported audio port config: %s",
373 __func__, requestedPortConfig.toString().c_str());
374 return BAD_VALUE;
375}
376
377status_t Hal2AidlMapper::findOrCreatePortConfig(
378 const AudioPortConfig& requestedPortConfig, const std::set<int32_t>& destinationPortIds,
379 AudioPortConfig* portConfig, Cleanups* cleanups) {
380 bool created = false;
381 RETURN_STATUS_IF_ERROR(findOrCreatePortConfig(
382 requestedPortConfig, destinationPortIds, portConfig, &created));
383 if (created && cleanups != nullptr) {
384 cleanups->add(&Hal2AidlMapper::resetPortConfig, portConfig->id);
385 }
386 return OK;
387}
388
389status_t Hal2AidlMapper::findPortConfig(const AudioDevice& device, AudioPortConfig* portConfig) {
390 if (auto it = findPortConfig(device); it != mPortConfigs.end()) {
391 *portConfig = it->second;
392 return OK;
393 }
394 ALOGE("%s: could not find a configured device port for device %s",
395 __func__, device.toString().c_str());
396 return BAD_VALUE;
397}
398
399Hal2AidlMapper::Patches::iterator Hal2AidlMapper::findPatch(
400 const std::set<int32_t>& sourcePortConfigIds, const std::set<int32_t>& sinkPortConfigIds) {
401 return std::find_if(mPatches.begin(), mPatches.end(),
402 [&](const auto& pair) {
403 const auto& p = pair.second;
404 std::set<int32_t> patchSrcs(
405 p.sourcePortConfigIds.begin(), p.sourcePortConfigIds.end());
406 std::set<int32_t> patchSinks(
407 p.sinkPortConfigIds.begin(), p.sinkPortConfigIds.end());
408 return sourcePortConfigIds == patchSrcs && sinkPortConfigIds == patchSinks; });
409}
410
411Hal2AidlMapper::Ports::iterator Hal2AidlMapper::findPort(const AudioDevice& device) {
412 if (device.type.type == AudioDeviceType::IN_DEFAULT) {
413 return mPorts.find(mDefaultInputPortId);
414 } else if (device.type.type == AudioDeviceType::OUT_DEFAULT) {
415 return mPorts.find(mDefaultOutputPortId);
416 }
417 if (device.address.getTag() != AudioDeviceAddress::id ||
418 !device.address.get<AudioDeviceAddress::id>().empty()) {
419 return std::find_if(mPorts.begin(), mPorts.end(),
420 [&](const auto& pair) { return audioDeviceMatches(device, pair.second); });
421 }
422 // For connection w/o an address, two ports can be found: the template port,
423 // and a connected port (if exists). Make sure we return the connected port.
424 Hal2AidlMapper::Ports::iterator portIt = mPorts.end();
425 for (auto it = mPorts.begin(); it != mPorts.end(); ++it) {
426 if (audioDeviceMatches(device, it->second)) {
427 if (mConnectedPorts.find(it->first) != mConnectedPorts.end()) {
428 return it;
429 } else {
430 // Will return 'it' if there is no connected port.
431 portIt = it;
432 }
433 }
434 }
435 return portIt;
436}
437
438Hal2AidlMapper::Ports::iterator Hal2AidlMapper::findPort(
439 const AudioConfig& config, const AudioIoFlags& flags,
440 const std::set<int32_t>& destinationPortIds) {
441 auto belongsToProfile = [&config](const AudioProfile& prof) {
442 return (isDefaultAudioFormat(config.base.format) || prof.format == config.base.format) &&
443 (config.base.channelMask.getTag() == AudioChannelLayout::none ||
444 std::find(prof.channelMasks.begin(), prof.channelMasks.end(),
445 config.base.channelMask) != prof.channelMasks.end()) &&
446 (config.base.sampleRate == 0 ||
447 std::find(prof.sampleRates.begin(), prof.sampleRates.end(),
448 config.base.sampleRate) != prof.sampleRates.end());
449 };
450 static const std::vector<AudioOutputFlags> kOptionalOutputFlags{AudioOutputFlags::BIT_PERFECT};
451 int optionalFlags = 0;
452 auto flagMatches = [&flags, &optionalFlags](const AudioIoFlags& portFlags) {
453 // Ports should be able to match if the optional flags are not requested.
454 return portFlags == flags ||
455 (portFlags.getTag() == AudioIoFlags::Tag::output &&
456 AudioIoFlags::make<AudioIoFlags::Tag::output>(
457 portFlags.get<AudioIoFlags::Tag::output>() &
458 ~optionalFlags) == flags);
459 };
460 auto matcher = [&](const auto& pair) {
461 const auto& p = pair.second;
462 return p.ext.getTag() == AudioPortExt::Tag::mix &&
463 flagMatches(p.flags) &&
464 (destinationPortIds.empty() ||
465 std::any_of(destinationPortIds.begin(), destinationPortIds.end(),
466 [&](const int32_t destId) { return mRoutingMatrix.count(
467 std::make_pair(p.id, destId)) != 0; })) &&
468 (p.profiles.empty() ||
469 std::find_if(p.profiles.begin(), p.profiles.end(), belongsToProfile) !=
470 p.profiles.end()); };
471 auto result = std::find_if(mPorts.begin(), mPorts.end(), matcher);
472 if (result == mPorts.end() && flags.getTag() == AudioIoFlags::Tag::output) {
473 auto optionalOutputFlagsIt = kOptionalOutputFlags.begin();
474 while (result == mPorts.end() && optionalOutputFlagsIt != kOptionalOutputFlags.end()) {
475 if (isBitPositionFlagSet(
476 flags.get<AudioIoFlags::Tag::output>(), *optionalOutputFlagsIt)) {
477 // If the flag is set by the request, it must be matched.
478 ++optionalOutputFlagsIt;
479 continue;
480 }
481 optionalFlags |= makeBitPositionFlagMask(*optionalOutputFlagsIt++);
482 result = std::find_if(mPorts.begin(), mPorts.end(), matcher);
483 ALOGI("%s: port for config %s, flags %s was not found in the module %s, "
484 "retried with excluding optional flags %#x", __func__, config.toString().c_str(),
485 flags.toString().c_str(), mInstance.c_str(), optionalFlags);
486 }
487 }
488 return result;
489}
490
491Hal2AidlMapper::PortConfigs::iterator Hal2AidlMapper::findPortConfig(const AudioDevice& device) {
492 return std::find_if(mPortConfigs.begin(), mPortConfigs.end(),
493 [&](const auto& pair) { return audioDeviceMatches(device, pair.second); });
494}
495
496Hal2AidlMapper::PortConfigs::iterator Hal2AidlMapper::findPortConfig(
497 const std::optional<AudioConfig>& config,
498 const std::optional<AudioIoFlags>& flags,
499 int32_t ioHandle) {
500 using Tag = AudioPortExt::Tag;
501 return std::find_if(mPortConfigs.begin(), mPortConfigs.end(),
502 [&](const auto& pair) {
503 const auto& p = pair.second;
504 LOG_ALWAYS_FATAL_IF(p.ext.getTag() == Tag::mix &&
505 (!p.sampleRate.has_value() || !p.channelMask.has_value() ||
506 !p.format.has_value() || !p.flags.has_value()),
507 "%s: stored mix port config is not fully specified: %s",
508 __func__, p.toString().c_str());
509 return p.ext.getTag() == Tag::mix &&
510 (!config.has_value() ||
511 isConfigEqualToPortConfig(config.value(), p)) &&
512 (!flags.has_value() || p.flags.value() == flags.value()) &&
513 p.ext.template get<Tag::mix>().handle == ioHandle; });
514}
515
516status_t Hal2AidlMapper::getAudioMixPort(int32_t ioHandle, AudioPort* port) {
517 auto it = findPortConfig(std::nullopt /*config*/, std::nullopt /*flags*/, ioHandle);
518 if (it == mPortConfigs.end()) {
519 ALOGE("%s, cannot find mix port config for handle %u", __func__, ioHandle);
520 return BAD_VALUE;
521 }
522 return updateAudioPort(it->second.portId, port);
523}
524
525status_t Hal2AidlMapper::getAudioPortCached(
526 const ::aidl::android::media::audio::common::AudioDevice& device,
527 ::aidl::android::media::audio::common::AudioPort* port) {
528
529 if (auto portsIt = findPort(device); portsIt != mPorts.end()) {
530 *port = portsIt->second;
531 return OK;
532 }
533 ALOGE("%s: device port for device %s is not found in the module %s",
534 __func__, device.toString().c_str(), mInstance.c_str());
535 return BAD_VALUE;
536}
537
538status_t Hal2AidlMapper::initialize() {
539 std::vector<AudioPort> ports;
540 RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->getAudioPorts(&ports)));
541 ALOGW_IF(ports.empty(), "%s: module %s returned an empty list of audio ports",
542 __func__, mInstance.c_str());
543 mDefaultInputPortId = mDefaultOutputPortId = -1;
544 const int defaultDeviceFlag = 1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE;
545 for (auto it = ports.begin(); it != ports.end(); ) {
546 const auto& port = *it;
547 if (port.ext.getTag() != AudioPortExt::Tag::device) {
548 ++it;
549 continue;
550 }
551 const AudioPortDeviceExt& deviceExt = port.ext.get<AudioPortExt::Tag::device>();
552 if ((deviceExt.flags & defaultDeviceFlag) != 0) {
553 if (port.flags.getTag() == AudioIoFlags::Tag::input) {
554 mDefaultInputPortId = port.id;
555 } else if (port.flags.getTag() == AudioIoFlags::Tag::output) {
556 mDefaultOutputPortId = port.id;
557 }
558 }
559 // For compatibility with HIDL, hide "template" remote submix ports from ports list.
560 if (const auto& devDesc = deviceExt.device;
561 (devDesc.type.type == AudioDeviceType::IN_SUBMIX ||
562 devDesc.type.type == AudioDeviceType::OUT_SUBMIX) &&
563 devDesc.type.connection == AudioDeviceDescription::CONNECTION_VIRTUAL) {
564 if (devDesc.type.type == AudioDeviceType::IN_SUBMIX) {
565 mRemoteSubmixIn = port;
566 } else {
567 mRemoteSubmixOut = port;
568 }
569 it = ports.erase(it);
570 } else {
571 ++it;
572 }
573 }
574 if (mRemoteSubmixIn.has_value() != mRemoteSubmixOut.has_value()) {
575 ALOGE("%s: The configuration only has input or output remote submix device, must have both",
576 __func__);
577 mRemoteSubmixIn.reset();
578 mRemoteSubmixOut.reset();
579 }
580 if (mRemoteSubmixIn.has_value()) {
581 AudioPort connectedRSubmixIn = *mRemoteSubmixIn;
582 connectedRSubmixIn.ext.get<AudioPortExt::Tag::device>().device.address =
583 AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS;
584 ALOGD("%s: connecting remote submix input", __func__);
585 RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->connectExternalDevice(
586 connectedRSubmixIn, &connectedRSubmixIn)));
587 // The template port for the remote submix input couldn't be "default" because it is not
588 // attached. The connected port can now be made default because we never disconnect it.
589 if (mDefaultInputPortId == -1) {
590 mDefaultInputPortId = connectedRSubmixIn.id;
591 }
592 ports.push_back(std::move(connectedRSubmixIn));
593
594 // Remote submix output must not be connected until the framework actually starts
595 // using it, however for legacy compatibility we need to provide an "augmented template"
596 // port with an address and profiles. It is obtained by connecting the output and then
597 // immediately disconnecting it. This is a cheap operation as we don't open any streams.
598 AudioPort tempConnectedRSubmixOut = *mRemoteSubmixOut;
599 tempConnectedRSubmixOut.ext.get<AudioPortExt::Tag::device>().device.address =
600 AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS;
601 ALOGD("%s: temporarily connecting and disconnecting remote submix output", __func__);
602 RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->connectExternalDevice(
603 tempConnectedRSubmixOut, &tempConnectedRSubmixOut)));
604 RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->disconnectExternalDevice(
605 tempConnectedRSubmixOut.id)));
606 tempConnectedRSubmixOut.id = mRemoteSubmixOut->id;
607 ports.push_back(std::move(tempConnectedRSubmixOut));
608 }
609
610 ALOGI("%s: module %s default port ids: input %d, output %d",
611 __func__, mInstance.c_str(), mDefaultInputPortId, mDefaultOutputPortId);
612 std::transform(ports.begin(), ports.end(), std::inserter(mPorts, mPorts.end()),
613 [](const auto& p) { return std::make_pair(p.id, p); });
614 RETURN_STATUS_IF_ERROR(updateRoutes());
615 std::vector<AudioPortConfig> portConfigs;
616 RETURN_STATUS_IF_ERROR(
617 statusTFromBinderStatus(mModule->getAudioPortConfigs(&portConfigs))); // OK if empty
618 std::transform(portConfigs.begin(), portConfigs.end(),
619 std::inserter(mPortConfigs, mPortConfigs.end()),
620 [](const auto& p) { return std::make_pair(p.id, p); });
621 std::transform(mPortConfigs.begin(), mPortConfigs.end(),
622 std::inserter(mInitialPortConfigIds, mInitialPortConfigIds.end()),
623 [](const auto& pcPair) { return pcPair.first; });
624 std::vector<AudioPatch> patches;
625 RETURN_STATUS_IF_ERROR(
626 statusTFromBinderStatus(mModule->getAudioPatches(&patches))); // OK if empty
627 std::transform(patches.begin(), patches.end(),
628 std::inserter(mPatches, mPatches.end()),
629 [](const auto& p) { return std::make_pair(p.id, p); });
630 return OK;
631}
632
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800633bool Hal2AidlMapper::isPortBeingHeld(int32_t portId) {
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700634 // It is assumed that mStreams has already been cleaned up.
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800635 for (const auto& s : mStreams) {
636 if (portConfigBelongsToPort(s.second.first, portId)) return true;
637 }
638 for (const auto& [_, patch] : mPatches) {
639 for (int32_t id : patch.sourcePortConfigIds) {
640 if (portConfigBelongsToPort(id, portId)) return true;
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700641 }
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800642 for (int32_t id : patch.sinkPortConfigIds) {
643 if (portConfigBelongsToPort(id, portId)) return true;
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700644 }
645 }
646 return false;
647}
648
649status_t Hal2AidlMapper::prepareToOpenStream(
650 int32_t ioHandle, const AudioDevice& device, const AudioIoFlags& flags,
651 AudioSource source, Cleanups* cleanups, AudioConfig* config,
652 AudioPortConfig* mixPortConfig, AudioPatch* patch) {
653 ALOGD("%p %s: handle %d, device %s, flags %s, source %s, config %s, mix port config %s",
654 this, __func__, ioHandle, device.toString().c_str(),
655 flags.toString().c_str(), toString(source).c_str(),
656 config->toString().c_str(), mixPortConfig->toString().c_str());
657 resetUnusedPatchesAndPortConfigs();
658 const bool isInput = flags.getTag() == AudioIoFlags::Tag::input;
659 // Find / create AudioPortConfigs for the device port and the mix port,
660 // then find / create a patch between them, and open a stream on the mix port.
661 AudioPortConfig devicePortConfig;
662 bool created = false;
663 RETURN_STATUS_IF_ERROR(findOrCreatePortConfig(device, config,
664 &devicePortConfig, &created));
665 if (created) {
666 cleanups->add(&Hal2AidlMapper::resetPortConfig, devicePortConfig.id);
667 }
668 RETURN_STATUS_IF_ERROR(findOrCreatePortConfig(*config, flags, ioHandle, source,
669 std::set<int32_t>{devicePortConfig.portId}, mixPortConfig, &created));
670 if (created) {
671 cleanups->add(&Hal2AidlMapper::resetPortConfig, mixPortConfig->id);
672 }
673 setConfigFromPortConfig(config, *mixPortConfig);
674 if (isInput) {
675 RETURN_STATUS_IF_ERROR(findOrCreatePatch(
676 {devicePortConfig.id}, {mixPortConfig->id}, patch, &created));
677 } else {
678 RETURN_STATUS_IF_ERROR(findOrCreatePatch(
679 {mixPortConfig->id}, {devicePortConfig.id}, patch, &created));
680 }
681 if (created) {
682 cleanups->add(&Hal2AidlMapper::resetPatch, patch->id);
683 }
684 if (config->frameCount <= 0) {
685 config->frameCount = patch->minimumStreamBufferSizeFrames;
686 }
687 return OK;
688}
689
690status_t Hal2AidlMapper::releaseAudioPatch(int32_t patchId) {
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800691 return releaseAudioPatches({patchId});
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700692}
693
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800694status_t Hal2AidlMapper::releaseAudioPatches(const std::set<int32_t>& patchIds) {
695 status_t result = OK;
696 for (const auto patchId : patchIds) {
697 if (auto it = mPatches.find(patchId); it != mPatches.end()) {
698 mPatches.erase(it);
699 if (ndk::ScopedAStatus status = mModule->resetAudioPatch(patchId); !status.isOk()) {
700 ALOGE("%s: error while resetting patch %d: %s",
701 __func__, patchId, status.getDescription().c_str());
702 result = statusTFromBinderStatus(status);
703 }
704 } else {
705 ALOGE("%s: patch id %d not found", __func__, patchId);
706 result = BAD_VALUE;
707 }
708 }
709 resetUnusedPortConfigs();
710 return result;
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700711}
712
713void Hal2AidlMapper::resetPortConfig(int32_t portConfigId) {
714 if (auto it = mPortConfigs.find(portConfigId); it != mPortConfigs.end()) {
715 mPortConfigs.erase(it);
716 if (ndk::ScopedAStatus status = mModule->resetAudioPortConfig(portConfigId);
717 !status.isOk()) {
718 ALOGE("%s: error while resetting port config %d: %s",
719 __func__, portConfigId, status.getDescription().c_str());
720 }
721 return;
722 }
723 ALOGE("%s: port config id %d not found", __func__, portConfigId);
724}
725
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800726void Hal2AidlMapper::resetUnusedPatchesAndPortConfigs() {
727 // Since patches can be created independently of streams via 'createOrUpdatePatch',
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700728 // here we only clean up patches for released streams.
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800729 std::set<int32_t> patchesToRelease;
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700730 for (auto it = mStreams.begin(); it != mStreams.end(); ) {
731 if (auto streamSp = it->first.promote(); streamSp) {
732 ++it;
733 } else {
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800734 if (const int32_t patchId = it->second.second; patchId != -1) {
735 patchesToRelease.insert(patchId);
736 }
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700737 it = mStreams.erase(it);
738 }
739 }
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800740 // 'releaseAudioPatches' also resets unused port configs.
741 releaseAudioPatches(patchesToRelease);
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700742}
743
744void Hal2AidlMapper::resetUnusedPortConfigs() {
745 // The assumption is that port configs are used to create patches
746 // (or to open streams, but that involves creation of patches, too). Thus,
747 // orphaned port configs can and should be reset.
748 std::map<int32_t, int32_t /*portID*/> portConfigIds;
749 std::transform(mPortConfigs.begin(), mPortConfigs.end(),
750 std::inserter(portConfigIds, portConfigIds.end()),
751 [](const auto& pcPair) { return std::make_pair(pcPair.first, pcPair.second.portId); });
752 for (const auto& p : mPatches) {
753 for (int32_t id : p.second.sourcePortConfigIds) portConfigIds.erase(id);
754 for (int32_t id : p.second.sinkPortConfigIds) portConfigIds.erase(id);
755 }
756 for (int32_t id : mInitialPortConfigIds) {
757 portConfigIds.erase(id);
758 }
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800759 for (const auto& s : mStreams) {
760 portConfigIds.erase(s.second.first);
761 }
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700762 std::set<int32_t> retryDeviceDisconnection;
763 for (const auto& portConfigAndIdPair : portConfigIds) {
764 resetPortConfig(portConfigAndIdPair.first);
765 if (const auto it = mConnectedPorts.find(portConfigAndIdPair.second);
766 it != mConnectedPorts.end() && it->second) {
767 retryDeviceDisconnection.insert(portConfigAndIdPair.second);
768 }
769 }
770 for (int32_t portId : retryDeviceDisconnection) {
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800771 if (!isPortBeingHeld(portId)) {
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700772 if (auto status = mModule->disconnectExternalDevice(portId); status.isOk()) {
773 eraseConnectedPort(portId);
774 ALOGD("%s: executed postponed external device disconnection for port ID %d",
775 __func__, portId);
776 }
777 }
778 }
779 if (!retryDeviceDisconnection.empty()) {
780 updateRoutes();
781 }
782}
783
784status_t Hal2AidlMapper::setDevicePortConnectedState(const AudioPort& devicePort, bool connected) {
785 if (connected) {
786 AudioDevice matchDevice = devicePort.ext.get<AudioPortExt::device>().device;
787 std::optional<AudioPort> templatePort;
788 auto erasePortAfterConnectionIt = mPorts.end();
789 // Connection of remote submix out with address "0" is a special case. Since there is
790 // already an "augmented template" port with this address in mPorts, we need to replace
791 // it with a connected port.
792 // Connection of remote submix outs with any other address is done as usual except that
793 // the template port is in `mRemoteSubmixOut`.
794 if (mRemoteSubmixOut.has_value() && matchDevice.type.type == AudioDeviceType::OUT_SUBMIX) {
795 if (matchDevice.address == AudioDeviceAddress::make<AudioDeviceAddress::id>(
796 AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS)) {
797 erasePortAfterConnectionIt = findPort(matchDevice);
798 }
799 templatePort = mRemoteSubmixOut;
800 } else if (mRemoteSubmixIn.has_value() &&
801 matchDevice.type.type == AudioDeviceType::IN_SUBMIX) {
802 templatePort = mRemoteSubmixIn;
803 } else {
804 // Reset the device address to find the "template" port.
805 matchDevice.address = AudioDeviceAddress::make<AudioDeviceAddress::id>();
806 }
807 if (!templatePort.has_value()) {
808 auto portsIt = findPort(matchDevice);
809 if (portsIt == mPorts.end()) {
810 // Since 'setConnectedState' is called for all modules, it is normal when the device
811 // port not found in every one of them.
812 return BAD_VALUE;
813 } else {
814 ALOGD("%s: device port for device %s found in the module %s",
815 __func__, matchDevice.toString().c_str(), mInstance.c_str());
816 }
817 templatePort = portsIt->second;
818 }
819 resetUnusedPatchesAndPortConfigs();
820
821 // Use the ID of the "template" port, use all the information from the provided port.
822 AudioPort connectedPort = devicePort;
823 connectedPort.id = templatePort->id;
824 RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->connectExternalDevice(
825 connectedPort, &connectedPort)));
826 const auto [it, inserted] = mPorts.insert(std::make_pair(connectedPort.id, connectedPort));
827 LOG_ALWAYS_FATAL_IF(!inserted,
828 "%s: module %s, duplicate port ID received from HAL: %s, existing port: %s",
829 __func__, mInstance.c_str(), connectedPort.toString().c_str(),
830 it->second.toString().c_str());
831 mConnectedPorts[connectedPort.id] = false;
832 if (erasePortAfterConnectionIt != mPorts.end()) {
833 mPorts.erase(erasePortAfterConnectionIt);
834 }
835 } else { // !connected
836 AudioDevice matchDevice = devicePort.ext.get<AudioPortExt::device>().device;
837 auto portsIt = findPort(matchDevice);
838 if (portsIt == mPorts.end()) {
839 // Since 'setConnectedState' is called for all modules, it is normal when the device
840 // port not found in every one of them.
841 return BAD_VALUE;
842 } else {
843 ALOGD("%s: device port for device %s found in the module %s",
844 __func__, matchDevice.toString().c_str(), mInstance.c_str());
845 }
846 resetUnusedPatchesAndPortConfigs();
847
848 // Disconnection of remote submix out with address "0" is a special case. We need to replace
849 // the connected port entry with the "augmented template".
850 const int32_t portId = portsIt->second.id;
851 if (mRemoteSubmixOut.has_value() && matchDevice.type.type == AudioDeviceType::OUT_SUBMIX &&
852 matchDevice.address == AudioDeviceAddress::make<AudioDeviceAddress::id>(
853 AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS)) {
854 mDisconnectedPortReplacement = std::make_pair(portId, *mRemoteSubmixOut);
855 auto& port = mDisconnectedPortReplacement.second;
856 port.ext.get<AudioPortExt::Tag::device>().device = matchDevice;
857 port.profiles = portsIt->second.profiles;
858 }
859 // Streams are closed by AudioFlinger independently from device disconnections.
860 // It is possible that the stream has not been closed yet.
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800861 if (!isPortBeingHeld(portId)) {
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700862 RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
863 mModule->disconnectExternalDevice(portId)));
864 eraseConnectedPort(portId);
865 } else {
866 ALOGD("%s: since device port ID %d is used by a stream, "
867 "external device disconnection postponed", __func__, portId);
868 mConnectedPorts[portId] = true;
869 }
870 }
871 return updateRoutes();
872}
873
874status_t Hal2AidlMapper::updateAudioPort(int32_t portId, AudioPort* port) {
875 const status_t status = statusTFromBinderStatus(mModule->getAudioPort(portId, port));
876 if (status == OK) {
877 auto portIt = mPorts.find(portId);
878 if (portIt != mPorts.end()) {
879 portIt->second = *port;
880 } else {
881 ALOGW("%s, port(%d) returned successfully from the HAL but not it is not cached",
882 __func__, portId);
883 }
884 }
885 return status;
886}
887
888status_t Hal2AidlMapper::updateRoutes() {
889 RETURN_STATUS_IF_ERROR(
890 statusTFromBinderStatus(mModule->getAudioRoutes(&mRoutes)));
891 ALOGW_IF(mRoutes.empty(), "%s: module %s returned an empty list of audio routes",
892 __func__, mInstance.c_str());
893 if (mRemoteSubmixIn.has_value()) {
894 // Remove mentions of the template remote submix input from routes.
895 int32_t rSubmixInId = mRemoteSubmixIn->id;
896 // Remove mentions of the template remote submix out only if it is not in mPorts
897 // (that means there is a connected port in mPorts).
898 int32_t rSubmixOutId = mPorts.find(mRemoteSubmixOut->id) == mPorts.end() ?
899 mRemoteSubmixOut->id : -1;
900 for (auto it = mRoutes.begin(); it != mRoutes.end();) {
901 auto& route = *it;
902 if (route.sinkPortId == rSubmixOutId) {
903 it = mRoutes.erase(it);
904 continue;
905 }
906 if (auto routeIt = std::find(route.sourcePortIds.begin(), route.sourcePortIds.end(),
907 rSubmixInId); routeIt != route.sourcePortIds.end()) {
908 route.sourcePortIds.erase(routeIt);
909 if (route.sourcePortIds.empty()) {
910 it = mRoutes.erase(it);
911 continue;
912 }
913 }
914 ++it;
915 }
916 }
917 mRoutingMatrix.clear();
918 for (const auto& r : mRoutes) {
919 for (auto portId : r.sourcePortIds) {
920 mRoutingMatrix.emplace(r.sinkPortId, portId);
921 mRoutingMatrix.emplace(portId, r.sinkPortId);
922 }
923 }
924 return OK;
925}
926
927} // namespace android