blob: 453f9e2113e400ad4431ac66ce2b66ce3716982f [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;
Mikhail Naganovca92a5c2023-12-07 14:00:48 -080033using aidl::android::media::audio::common::AudioConfigBase;
Mikhail Naganovac9d4e72023-10-23 12:00:09 -070034using aidl::android::media::audio::common::AudioDevice;
35using aidl::android::media::audio::common::AudioDeviceAddress;
36using aidl::android::media::audio::common::AudioDeviceDescription;
37using aidl::android::media::audio::common::AudioDeviceType;
38using aidl::android::media::audio::common::AudioFormatDescription;
Mikhail Naganovca92a5c2023-12-07 14:00:48 -080039using aidl::android::media::audio::common::AudioFormatType;
Mikhail Naganovac9d4e72023-10-23 12:00:09 -070040using aidl::android::media::audio::common::AudioInputFlags;
41using aidl::android::media::audio::common::AudioIoFlags;
42using aidl::android::media::audio::common::AudioOutputFlags;
43using aidl::android::media::audio::common::AudioPort;
44using aidl::android::media::audio::common::AudioPortConfig;
45using aidl::android::media::audio::common::AudioPortDeviceExt;
46using aidl::android::media::audio::common::AudioPortExt;
47using aidl::android::media::audio::common::AudioPortMixExt;
48using aidl::android::media::audio::common::AudioPortMixExtUseCase;
49using aidl::android::media::audio::common::AudioProfile;
50using aidl::android::media::audio::common::AudioSource;
51using aidl::android::media::audio::common::Int;
52using aidl::android::hardware::audio::common::isBitPositionFlagSet;
53using aidl::android::hardware::audio::common::isDefaultAudioFormat;
54using aidl::android::hardware::audio::common::makeBitPositionFlagMask;
55using aidl::android::hardware::audio::core::AudioPatch;
56using aidl::android::hardware::audio::core::AudioRoute;
57using aidl::android::hardware::audio::core::IModule;
58
59namespace android {
60
61namespace {
62
63bool isConfigEqualToPortConfig(const AudioConfig& config, const AudioPortConfig& portConfig) {
64 return portConfig.sampleRate.value().value == config.base.sampleRate &&
65 portConfig.channelMask.value() == config.base.channelMask &&
66 portConfig.format.value() == config.base.format;
67}
68
Mikhail Naganovca92a5c2023-12-07 14:00:48 -080069AudioConfig* setConfigFromPortConfig(AudioConfig* config, const AudioPortConfig& portConfig) {
Mikhail Naganovac9d4e72023-10-23 12:00:09 -070070 config->base.sampleRate = portConfig.sampleRate.value().value;
71 config->base.channelMask = portConfig.channelMask.value();
72 config->base.format = portConfig.format.value();
Mikhail Naganovca92a5c2023-12-07 14:00:48 -080073 return config;
Mikhail Naganovac9d4e72023-10-23 12:00:09 -070074}
75
76void setPortConfigFromConfig(AudioPortConfig* portConfig, const AudioConfig& config) {
77 if (config.base.sampleRate != 0) {
78 portConfig->sampleRate = Int{ .value = config.base.sampleRate };
79 }
80 if (config.base.channelMask != AudioChannelLayout{}) {
81 portConfig->channelMask = config.base.channelMask;
82 }
83 if (config.base.format != AudioFormatDescription{}) {
84 portConfig->format = config.base.format;
85 }
86}
87
jiabin80797002023-12-01 22:02:41 +000088bool containHapticChannel(AudioChannelLayout channel) {
89 return channel.getTag() == AudioChannelLayout::Tag::layoutMask &&
90 ((channel.get<AudioChannelLayout::Tag::layoutMask>()
91 & AudioChannelLayout::CHANNEL_HAPTIC_A)
92 == AudioChannelLayout::CHANNEL_HAPTIC_A ||
93 (channel.get<AudioChannelLayout::Tag::layoutMask>()
94 & AudioChannelLayout::CHANNEL_HAPTIC_B)
95 == AudioChannelLayout::CHANNEL_HAPTIC_B);
96}
97
Mikhail Naganovac9d4e72023-10-23 12:00:09 -070098} // namespace
99
100Hal2AidlMapper::Hal2AidlMapper(const std::string& instance, const std::shared_ptr<IModule>& module)
101 : mInstance(instance), mModule(module) {
102}
103
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800104void Hal2AidlMapper::addStream(
Mikhail Naganova317a802024-03-15 18:03:10 +0000105 const sp<StreamHalInterface>& stream, int32_t mixPortConfigId, int32_t patchId) {
106 mStreams.insert(std::pair(stream, std::pair(mixPortConfigId, patchId)));
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700107}
108
109bool Hal2AidlMapper::audioDeviceMatches(const AudioDevice& device, const AudioPort& p) {
110 if (p.ext.getTag() != AudioPortExt::Tag::device) return false;
111 return p.ext.get<AudioPortExt::Tag::device>().device == device;
112}
113
114bool Hal2AidlMapper::audioDeviceMatches(const AudioDevice& device, const AudioPortConfig& p) {
115 if (p.ext.getTag() != AudioPortExt::Tag::device) return false;
116 if (device.type.type == AudioDeviceType::IN_DEFAULT) {
117 return p.portId == mDefaultInputPortId;
118 } else if (device.type.type == AudioDeviceType::OUT_DEFAULT) {
119 return p.portId == mDefaultOutputPortId;
120 }
121 return p.ext.get<AudioPortExt::Tag::device>().device == device;
122}
123
124status_t Hal2AidlMapper::createOrUpdatePatch(
125 const std::vector<AudioPortConfig>& sources,
126 const std::vector<AudioPortConfig>& sinks,
127 int32_t* patchId, Cleanups* cleanups) {
128 auto existingPatchIt = *patchId != 0 ? mPatches.find(*patchId): mPatches.end();
129 AudioPatch patch;
130 if (existingPatchIt != mPatches.end()) {
131 patch = existingPatchIt->second;
132 patch.sourcePortConfigIds.clear();
133 patch.sinkPortConfigIds.clear();
134 }
135 // The IDs will be found by 'fillPortConfigs', however the original 'sources' and
136 // 'sinks' will not be updated because 'setAudioPatch' only needs IDs. Here we log
137 // the source arguments, where only the audio configuration and device specifications
138 // are relevant.
139 ALOGD("%s: [disregard IDs] sources: %s, sinks: %s",
140 __func__, ::android::internal::ToString(sources).c_str(),
141 ::android::internal::ToString(sinks).c_str());
142 auto fillPortConfigs = [&](
143 const std::vector<AudioPortConfig>& configs,
144 const std::set<int32_t>& destinationPortIds,
145 std::vector<int32_t>* ids, std::set<int32_t>* portIds) -> status_t {
146 for (const auto& s : configs) {
147 AudioPortConfig portConfig;
Mikhail Naganov38220af2023-12-07 14:00:48 -0800148 if (status_t status = setPortConfig(
149 s, destinationPortIds, &portConfig, cleanups); status != OK) {
150 if (s.ext.getTag() == AudioPortExt::mix) {
151 // See b/315528763. Despite that the framework knows the actual format of
152 // the mix port, it still uses the original format. Luckily, there is
153 // the I/O handle which can be used to find the mix port.
154 ALOGI("fillPortConfigs: retrying to find a mix port config with default "
155 "configuration");
156 if (auto it = findPortConfig(std::nullopt, s.flags,
157 s.ext.get<AudioPortExt::mix>().handle);
158 it != mPortConfigs.end()) {
159 portConfig = it->second;
160 } else {
161 const std::string flags = s.flags.has_value() ?
162 s.flags->toString() : "<unspecified>";
163 ALOGE("fillPortConfigs: existing port config for flags %s, handle %d "
164 "not found in module %s", flags.c_str(),
165 s.ext.get<AudioPortExt::mix>().handle, mInstance.c_str());
166 return BAD_VALUE;
167 }
168 } else {
169 return status;
170 }
171 }
Mikhail Naganovca92a5c2023-12-07 14:00:48 -0800172 LOG_ALWAYS_FATAL_IF(portConfig.id == 0,
173 "fillPortConfigs: initial config: %s, port config: %s",
174 s.toString().c_str(), portConfig.toString().c_str());
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700175 ids->push_back(portConfig.id);
176 if (portIds != nullptr) {
177 portIds->insert(portConfig.portId);
178 }
179 }
180 return OK;
181 };
182 // When looking up port configs, the destinationPortId is only used for mix ports.
183 // Thus, we process device port configs first, and look up the destination port ID from them.
Mikhail Naganov03b0a002024-05-03 20:22:58 +0000184 const bool sourceIsDevice = std::any_of(sources.begin(), sources.end(),
185 [](const auto& config) { return config.ext.getTag() == AudioPortExt::device; });
186 const bool sinkIsDevice = std::any_of(sinks.begin(), sinks.end(),
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700187 [](const auto& config) { return config.ext.getTag() == AudioPortExt::device; });
188 const std::vector<AudioPortConfig>& devicePortConfigs =
189 sourceIsDevice ? sources : sinks;
190 std::vector<int32_t>* devicePortConfigIds =
191 sourceIsDevice ? &patch.sourcePortConfigIds : &patch.sinkPortConfigIds;
192 const std::vector<AudioPortConfig>& mixPortConfigs =
193 sourceIsDevice ? sinks : sources;
194 std::vector<int32_t>* mixPortConfigIds =
195 sourceIsDevice ? &patch.sinkPortConfigIds : &patch.sourcePortConfigIds;
196 std::set<int32_t> devicePortIds;
197 RETURN_STATUS_IF_ERROR(fillPortConfigs(
198 devicePortConfigs, std::set<int32_t>(), devicePortConfigIds, &devicePortIds));
199 RETURN_STATUS_IF_ERROR(fillPortConfigs(
200 mixPortConfigs, devicePortIds, mixPortConfigIds, nullptr));
201 if (existingPatchIt != mPatches.end()) {
202 RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
203 mModule->setAudioPatch(patch, &patch)));
204 existingPatchIt->second = patch;
205 } else {
206 bool created = false;
Mikhail Naganov03b0a002024-05-03 20:22:58 +0000207 // When the framework does not specify a patch ID, only the mix port config
208 // is used for finding an existing patch. That's because the framework assumes
209 // that there can only be one patch for an I/O thread.
210 PatchMatch match = sourceIsDevice && sinkIsDevice ?
211 MATCH_BOTH : (sourceIsDevice ? MATCH_SINKS : MATCH_SOURCES);
212 RETURN_STATUS_IF_ERROR(findOrCreatePatch(patch, match,
213 &patch, &created));
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800214 // No cleanup of the patch is needed, it is managed by the framework.
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700215 *patchId = patch.id;
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800216 if (!created) {
217 // The framework might have "created" a patch which already existed due to
218 // stream creation. Need to release the ownership from the stream.
219 for (auto& s : mStreams) {
220 if (s.second.second == patch.id) s.second.second = -1;
221 }
222 }
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700223 }
224 return OK;
225}
226
227status_t Hal2AidlMapper::createOrUpdatePortConfig(
Mikhail Naganovca92a5c2023-12-07 14:00:48 -0800228 const AudioPortConfig& requestedPortConfig, AudioPortConfig* result, bool* created) {
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700229 bool applied = false;
230 RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->setAudioPortConfig(
Mikhail Naganovca92a5c2023-12-07 14:00:48 -0800231 requestedPortConfig, result, &applied)));
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700232 if (!applied) {
Mikhail Naganovca92a5c2023-12-07 14:00:48 -0800233 result->id = 0;
234 *created = false;
235 return OK;
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700236 }
237
Mikhail Naganovca92a5c2023-12-07 14:00:48 -0800238 int32_t id = result->id;
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700239 if (requestedPortConfig.id != 0 && requestedPortConfig.id != id) {
240 LOG_ALWAYS_FATAL("%s: requested port config id %d changed to %d", __func__,
241 requestedPortConfig.id, id);
242 }
243
Mikhail Naganovca92a5c2023-12-07 14:00:48 -0800244 auto [_, inserted] = mPortConfigs.insert_or_assign(id, *result);
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700245 *created = inserted;
246 return OK;
247}
248
David Lifef5d0c2024-01-11 16:57:17 +0000249status_t Hal2AidlMapper::createOrUpdatePortConfigRetry(
250 const AudioPortConfig& requestedPortConfig, AudioPortConfig* result, bool* created) {
251 AudioPortConfig suggestedOrAppliedPortConfig;
252 RETURN_STATUS_IF_ERROR(createOrUpdatePortConfig(requestedPortConfig,
253 &suggestedOrAppliedPortConfig, created));
254 if (suggestedOrAppliedPortConfig.id == 0) {
255 // Try again with the suggested config
256 suggestedOrAppliedPortConfig.id = requestedPortConfig.id;
257 AudioPortConfig appliedPortConfig;
258 RETURN_STATUS_IF_ERROR(createOrUpdatePortConfig(suggestedOrAppliedPortConfig,
259 &appliedPortConfig, created));
260 if (appliedPortConfig.id == 0) {
261 ALOGE("%s: module %s did not apply suggested config %s", __func__,
262 mInstance.c_str(), suggestedOrAppliedPortConfig.toString().c_str());
263 return NO_INIT;
264 }
265 *result = appliedPortConfig;
266 } else {
267 *result = suggestedOrAppliedPortConfig;
268 }
269 return OK;
270}
271
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700272void Hal2AidlMapper::eraseConnectedPort(int32_t portId) {
273 mPorts.erase(portId);
274 mConnectedPorts.erase(portId);
275 if (mDisconnectedPortReplacement.first == portId) {
276 const auto& port = mDisconnectedPortReplacement.second;
277 mPorts.insert(std::make_pair(port.id, port));
278 ALOGD("%s: disconnected port replacement: %s", __func__, port.toString().c_str());
279 mDisconnectedPortReplacement = std::pair<int32_t, AudioPort>();
280 }
jiabin255ff7f2024-01-11 00:24:47 +0000281 updateDynamicMixPorts();
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700282}
283
284status_t Hal2AidlMapper::findOrCreatePatch(
Mikhail Naganov03b0a002024-05-03 20:22:58 +0000285 const AudioPatch& requestedPatch, PatchMatch match, AudioPatch* patch, bool* created) {
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700286 std::set<int32_t> sourcePortConfigIds(requestedPatch.sourcePortConfigIds.begin(),
287 requestedPatch.sourcePortConfigIds.end());
288 std::set<int32_t> sinkPortConfigIds(requestedPatch.sinkPortConfigIds.begin(),
289 requestedPatch.sinkPortConfigIds.end());
Mikhail Naganov03b0a002024-05-03 20:22:58 +0000290 return findOrCreatePatch(sourcePortConfigIds, sinkPortConfigIds, match, patch, created);
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700291}
292
293status_t Hal2AidlMapper::findOrCreatePatch(
294 const std::set<int32_t>& sourcePortConfigIds, const std::set<int32_t>& sinkPortConfigIds,
Mikhail Naganov03b0a002024-05-03 20:22:58 +0000295 PatchMatch match, AudioPatch* patch, bool* created) {
296 auto patchIt = findPatch(sourcePortConfigIds, sinkPortConfigIds, match);
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700297 if (patchIt == mPatches.end()) {
298 AudioPatch requestedPatch, appliedPatch;
299 requestedPatch.sourcePortConfigIds.insert(requestedPatch.sourcePortConfigIds.end(),
300 sourcePortConfigIds.begin(), sourcePortConfigIds.end());
301 requestedPatch.sinkPortConfigIds.insert(requestedPatch.sinkPortConfigIds.end(),
302 sinkPortConfigIds.begin(), sinkPortConfigIds.end());
303 RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->setAudioPatch(
304 requestedPatch, &appliedPatch)));
305 patchIt = mPatches.insert(mPatches.end(), std::make_pair(appliedPatch.id, appliedPatch));
306 *created = true;
307 } else {
308 *created = false;
309 }
310 *patch = patchIt->second;
311 return OK;
312}
313
Mikhail Naganovca92a5c2023-12-07 14:00:48 -0800314status_t Hal2AidlMapper::findOrCreateDevicePortConfig(
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700315 const AudioDevice& device, const AudioConfig* config, AudioPortConfig* portConfig,
316 bool* created) {
Mikhail Naganovca92a5c2023-12-07 14:00:48 -0800317 if (auto portConfigIt = findPortConfig(device); portConfigIt == mPortConfigs.end()) {
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700318 auto portsIt = findPort(device);
319 if (portsIt == mPorts.end()) {
320 ALOGE("%s: device port for device %s is not found in the module %s",
321 __func__, device.toString().c_str(), mInstance.c_str());
322 return BAD_VALUE;
323 }
324 AudioPortConfig requestedPortConfig;
325 requestedPortConfig.portId = portsIt->first;
326 if (config != nullptr) {
327 setPortConfigFromConfig(&requestedPortConfig, *config);
328 }
David Lifef5d0c2024-01-11 16:57:17 +0000329 return createOrUpdatePortConfigRetry(requestedPortConfig, portConfig, created);
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700330 } else {
David Lifef5d0c2024-01-11 16:57:17 +0000331 AudioPortConfig requestedPortConfig = portConfigIt->second;
332 if (config != nullptr) {
333 setPortConfigFromConfig(&requestedPortConfig, *config);
334 }
335
336 if (requestedPortConfig != portConfigIt->second) {
337 return createOrUpdatePortConfigRetry(requestedPortConfig, portConfig, created);
338 } else {
339 *portConfig = portConfigIt->second;
340 *created = false;
341 }
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700342 }
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700343 return OK;
344}
345
Mikhail Naganovca92a5c2023-12-07 14:00:48 -0800346status_t Hal2AidlMapper::findOrCreateMixPortConfig(
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700347 const AudioConfig& config, const std::optional<AudioIoFlags>& flags, int32_t ioHandle,
348 AudioSource source, const std::set<int32_t>& destinationPortIds,
349 AudioPortConfig* portConfig, bool* created) {
350 // These flags get removed one by one in this order when retrying port finding.
351 static const std::vector<AudioInputFlags> kOptionalInputFlags{
352 AudioInputFlags::FAST, AudioInputFlags::RAW, AudioInputFlags::VOIP_TX };
Mikhail Naganovca92a5c2023-12-07 14:00:48 -0800353 if (auto portConfigIt = findPortConfig(config, flags, ioHandle);
354 portConfigIt == mPortConfigs.end() && flags.has_value()) {
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700355 auto optionalInputFlagsIt = kOptionalInputFlags.begin();
356 AudioIoFlags matchFlags = flags.value();
357 auto portsIt = findPort(config, matchFlags, destinationPortIds);
358 while (portsIt == mPorts.end() && matchFlags.getTag() == AudioIoFlags::Tag::input
359 && optionalInputFlagsIt != kOptionalInputFlags.end()) {
360 if (!isBitPositionFlagSet(
361 matchFlags.get<AudioIoFlags::Tag::input>(), *optionalInputFlagsIt)) {
362 ++optionalInputFlagsIt;
363 continue;
364 }
365 matchFlags.set<AudioIoFlags::Tag::input>(matchFlags.get<AudioIoFlags::Tag::input>() &
366 ~makeBitPositionFlagMask(*optionalInputFlagsIt++));
367 portsIt = findPort(config, matchFlags, destinationPortIds);
368 ALOGI("%s: mix port for config %s, flags %s was not found in the module %s, "
369 "retried with flags %s", __func__, config.toString().c_str(),
370 flags.value().toString().c_str(), mInstance.c_str(),
371 matchFlags.toString().c_str());
372 }
373 if (portsIt == mPorts.end()) {
374 ALOGE("%s: mix port for config %s, flags %s is not found in the module %s",
375 __func__, config.toString().c_str(), matchFlags.toString().c_str(),
376 mInstance.c_str());
377 return BAD_VALUE;
378 }
379 AudioPortConfig requestedPortConfig;
380 requestedPortConfig.portId = portsIt->first;
381 setPortConfigFromConfig(&requestedPortConfig, config);
Mikhail Naganovca92a5c2023-12-07 14:00:48 -0800382 requestedPortConfig.flags = portsIt->second.flags;
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700383 requestedPortConfig.ext = AudioPortMixExt{ .handle = ioHandle };
384 if (matchFlags.getTag() == AudioIoFlags::Tag::input
385 && source != AudioSource::SYS_RESERVED_INVALID) {
386 requestedPortConfig.ext.get<AudioPortExt::Tag::mix>().usecase =
387 AudioPortMixExtUseCase::make<AudioPortMixExtUseCase::Tag::source>(source);
388 }
Mikhail Naganovca92a5c2023-12-07 14:00:48 -0800389 return createOrUpdatePortConfig(requestedPortConfig, portConfig, created);
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700390 } else if (portConfigIt == mPortConfigs.end() && !flags.has_value()) {
391 ALOGW("%s: mix port config for %s, handle %d not found in the module %s, "
392 "and was not created as flags are not specified",
393 __func__, config.toString().c_str(), ioHandle, mInstance.c_str());
394 return BAD_VALUE;
395 } else {
396 AudioPortConfig requestedPortConfig = portConfigIt->second;
David Lifef5d0c2024-01-11 16:57:17 +0000397 setPortConfigFromConfig(&requestedPortConfig, config);
398
399 AudioPortMixExt& mixExt = requestedPortConfig.ext.get<AudioPortExt::Tag::mix>();
400 if (mixExt.usecase.getTag() == AudioPortMixExtUseCase::Tag::source &&
401 source != AudioSource::SYS_RESERVED_INVALID) {
402 mixExt.usecase.get<AudioPortMixExtUseCase::Tag::source>() = source;
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700403 }
404
405 if (requestedPortConfig != portConfigIt->second) {
Mikhail Naganovca92a5c2023-12-07 14:00:48 -0800406 return createOrUpdatePortConfig(requestedPortConfig, portConfig, created);
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700407 } else {
Mikhail Naganovca92a5c2023-12-07 14:00:48 -0800408 *portConfig = portConfigIt->second;
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700409 *created = false;
410 }
411 }
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700412 return OK;
413}
414
415status_t Hal2AidlMapper::findOrCreatePortConfig(
416 const AudioPortConfig& requestedPortConfig, const std::set<int32_t>& destinationPortIds,
417 AudioPortConfig* portConfig, bool* created) {
418 using Tag = AudioPortExt::Tag;
419 if (requestedPortConfig.ext.getTag() == Tag::mix) {
420 if (const auto& p = requestedPortConfig;
421 !p.sampleRate.has_value() || !p.channelMask.has_value() ||
422 !p.format.has_value()) {
423 ALOGW("%s: provided mix port config is not fully specified: %s",
424 __func__, p.toString().c_str());
425 return BAD_VALUE;
426 }
427 AudioConfig config;
428 setConfigFromPortConfig(&config, requestedPortConfig);
429 AudioSource source = requestedPortConfig.ext.get<Tag::mix>().usecase.getTag() ==
430 AudioPortMixExtUseCase::Tag::source ?
431 requestedPortConfig.ext.get<Tag::mix>().usecase.
432 get<AudioPortMixExtUseCase::Tag::source>() : AudioSource::SYS_RESERVED_INVALID;
Mikhail Naganovca92a5c2023-12-07 14:00:48 -0800433 return findOrCreateMixPortConfig(config, requestedPortConfig.flags,
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700434 requestedPortConfig.ext.get<Tag::mix>().handle, source, destinationPortIds,
435 portConfig, created);
436 } else if (requestedPortConfig.ext.getTag() == Tag::device) {
David Lifef5d0c2024-01-11 16:57:17 +0000437 if (const auto& p = requestedPortConfig;
438 p.sampleRate.has_value() && p.channelMask.has_value() &&
439 p.format.has_value()) {
440 AudioConfig config;
441 setConfigFromPortConfig(&config, requestedPortConfig);
442 return findOrCreateDevicePortConfig(
443 requestedPortConfig.ext.get<Tag::device>().device, &config,
444 portConfig, created);
445 } else {
446 return findOrCreateDevicePortConfig(
447 requestedPortConfig.ext.get<Tag::device>().device, nullptr /*config*/,
448 portConfig, created);
449 }
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700450 }
451 ALOGW("%s: unsupported audio port config: %s",
452 __func__, requestedPortConfig.toString().c_str());
453 return BAD_VALUE;
454}
455
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700456status_t Hal2AidlMapper::findPortConfig(const AudioDevice& device, AudioPortConfig* portConfig) {
457 if (auto it = findPortConfig(device); it != mPortConfigs.end()) {
458 *portConfig = it->second;
459 return OK;
460 }
Mikhail Naganov67c2f6d2024-03-18 09:48:27 -0700461 ALOGE("%s: could not find a device port config for device %s",
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700462 __func__, device.toString().c_str());
463 return BAD_VALUE;
464}
465
466Hal2AidlMapper::Patches::iterator Hal2AidlMapper::findPatch(
Mikhail Naganov03b0a002024-05-03 20:22:58 +0000467 const std::set<int32_t>& sourcePortConfigIds, const std::set<int32_t>& sinkPortConfigIds,
468 PatchMatch match) {
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700469 return std::find_if(mPatches.begin(), mPatches.end(),
470 [&](const auto& pair) {
471 const auto& p = pair.second;
472 std::set<int32_t> patchSrcs(
473 p.sourcePortConfigIds.begin(), p.sourcePortConfigIds.end());
474 std::set<int32_t> patchSinks(
475 p.sinkPortConfigIds.begin(), p.sinkPortConfigIds.end());
Mikhail Naganov03b0a002024-05-03 20:22:58 +0000476 switch (match) {
477 case MATCH_SOURCES:
478 return sourcePortConfigIds == patchSrcs;
479 case MATCH_SINKS:
480 return sinkPortConfigIds == patchSinks;
481 case MATCH_BOTH:
482 return sourcePortConfigIds == patchSrcs && sinkPortConfigIds == patchSinks;
483 }
484 });
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700485}
486
487Hal2AidlMapper::Ports::iterator Hal2AidlMapper::findPort(const AudioDevice& device) {
488 if (device.type.type == AudioDeviceType::IN_DEFAULT) {
489 return mPorts.find(mDefaultInputPortId);
490 } else if (device.type.type == AudioDeviceType::OUT_DEFAULT) {
491 return mPorts.find(mDefaultOutputPortId);
492 }
493 if (device.address.getTag() != AudioDeviceAddress::id ||
494 !device.address.get<AudioDeviceAddress::id>().empty()) {
495 return std::find_if(mPorts.begin(), mPorts.end(),
496 [&](const auto& pair) { return audioDeviceMatches(device, pair.second); });
497 }
498 // For connection w/o an address, two ports can be found: the template port,
499 // and a connected port (if exists). Make sure we return the connected port.
500 Hal2AidlMapper::Ports::iterator portIt = mPorts.end();
501 for (auto it = mPorts.begin(); it != mPorts.end(); ++it) {
502 if (audioDeviceMatches(device, it->second)) {
503 if (mConnectedPorts.find(it->first) != mConnectedPorts.end()) {
504 return it;
505 } else {
506 // Will return 'it' if there is no connected port.
507 portIt = it;
508 }
509 }
510 }
511 return portIt;
512}
513
514Hal2AidlMapper::Ports::iterator Hal2AidlMapper::findPort(
515 const AudioConfig& config, const AudioIoFlags& flags,
516 const std::set<int32_t>& destinationPortIds) {
jiabin80797002023-12-01 22:02:41 +0000517 auto channelMaskMatches = [](const std::vector<AudioChannelLayout>& channelMasks,
518 const AudioChannelLayout& channelMask) {
519 // Return true when 1) the channel mask is none and none of the channel mask from the
520 // collection contains haptic channel mask, or 2) the channel mask collection contains
521 // the queried channel mask.
522 return (channelMask.getTag() == AudioChannelLayout::none &&
523 std::none_of(channelMasks.begin(), channelMasks.end(),
524 containHapticChannel)) ||
525 std::find(channelMasks.begin(), channelMasks.end(), channelMask)
526 != channelMasks.end();
527 };
528 auto belongsToProfile = [&config, &channelMaskMatches](const AudioProfile& prof) {
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700529 return (isDefaultAudioFormat(config.base.format) || prof.format == config.base.format) &&
jiabin80797002023-12-01 22:02:41 +0000530 channelMaskMatches(prof.channelMasks, config.base.channelMask) &&
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700531 (config.base.sampleRate == 0 ||
532 std::find(prof.sampleRates.begin(), prof.sampleRates.end(),
533 config.base.sampleRate) != prof.sampleRates.end());
534 };
535 static const std::vector<AudioOutputFlags> kOptionalOutputFlags{AudioOutputFlags::BIT_PERFECT};
536 int optionalFlags = 0;
537 auto flagMatches = [&flags, &optionalFlags](const AudioIoFlags& portFlags) {
538 // Ports should be able to match if the optional flags are not requested.
539 return portFlags == flags ||
540 (portFlags.getTag() == AudioIoFlags::Tag::output &&
541 AudioIoFlags::make<AudioIoFlags::Tag::output>(
542 portFlags.get<AudioIoFlags::Tag::output>() &
543 ~optionalFlags) == flags);
544 };
545 auto matcher = [&](const auto& pair) {
546 const auto& p = pair.second;
547 return p.ext.getTag() == AudioPortExt::Tag::mix &&
548 flagMatches(p.flags) &&
549 (destinationPortIds.empty() ||
550 std::any_of(destinationPortIds.begin(), destinationPortIds.end(),
551 [&](const int32_t destId) { return mRoutingMatrix.count(
552 std::make_pair(p.id, destId)) != 0; })) &&
553 (p.profiles.empty() ||
554 std::find_if(p.profiles.begin(), p.profiles.end(), belongsToProfile) !=
555 p.profiles.end()); };
556 auto result = std::find_if(mPorts.begin(), mPorts.end(), matcher);
557 if (result == mPorts.end() && flags.getTag() == AudioIoFlags::Tag::output) {
558 auto optionalOutputFlagsIt = kOptionalOutputFlags.begin();
559 while (result == mPorts.end() && optionalOutputFlagsIt != kOptionalOutputFlags.end()) {
560 if (isBitPositionFlagSet(
561 flags.get<AudioIoFlags::Tag::output>(), *optionalOutputFlagsIt)) {
562 // If the flag is set by the request, it must be matched.
563 ++optionalOutputFlagsIt;
564 continue;
565 }
566 optionalFlags |= makeBitPositionFlagMask(*optionalOutputFlagsIt++);
567 result = std::find_if(mPorts.begin(), mPorts.end(), matcher);
568 ALOGI("%s: port for config %s, flags %s was not found in the module %s, "
569 "retried with excluding optional flags %#x", __func__, config.toString().c_str(),
570 flags.toString().c_str(), mInstance.c_str(), optionalFlags);
571 }
572 }
573 return result;
574}
575
576Hal2AidlMapper::PortConfigs::iterator Hal2AidlMapper::findPortConfig(const AudioDevice& device) {
577 return std::find_if(mPortConfigs.begin(), mPortConfigs.end(),
578 [&](const auto& pair) { return audioDeviceMatches(device, pair.second); });
579}
580
581Hal2AidlMapper::PortConfigs::iterator Hal2AidlMapper::findPortConfig(
582 const std::optional<AudioConfig>& config,
583 const std::optional<AudioIoFlags>& flags,
584 int32_t ioHandle) {
585 using Tag = AudioPortExt::Tag;
586 return std::find_if(mPortConfigs.begin(), mPortConfigs.end(),
587 [&](const auto& pair) {
588 const auto& p = pair.second;
589 LOG_ALWAYS_FATAL_IF(p.ext.getTag() == Tag::mix &&
590 (!p.sampleRate.has_value() || !p.channelMask.has_value() ||
591 !p.format.has_value() || !p.flags.has_value()),
592 "%s: stored mix port config is not fully specified: %s",
593 __func__, p.toString().c_str());
594 return p.ext.getTag() == Tag::mix &&
595 (!config.has_value() ||
596 isConfigEqualToPortConfig(config.value(), p)) &&
597 (!flags.has_value() || p.flags.value() == flags.value()) &&
598 p.ext.template get<Tag::mix>().handle == ioHandle; });
599}
600
601status_t Hal2AidlMapper::getAudioMixPort(int32_t ioHandle, AudioPort* port) {
602 auto it = findPortConfig(std::nullopt /*config*/, std::nullopt /*flags*/, ioHandle);
603 if (it == mPortConfigs.end()) {
604 ALOGE("%s, cannot find mix port config for handle %u", __func__, ioHandle);
605 return BAD_VALUE;
606 }
607 return updateAudioPort(it->second.portId, port);
608}
609
610status_t Hal2AidlMapper::getAudioPortCached(
611 const ::aidl::android::media::audio::common::AudioDevice& device,
612 ::aidl::android::media::audio::common::AudioPort* port) {
613
614 if (auto portsIt = findPort(device); portsIt != mPorts.end()) {
615 *port = portsIt->second;
616 return OK;
617 }
618 ALOGE("%s: device port for device %s is not found in the module %s",
619 __func__, device.toString().c_str(), mInstance.c_str());
620 return BAD_VALUE;
621}
622
623status_t Hal2AidlMapper::initialize() {
624 std::vector<AudioPort> ports;
625 RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->getAudioPorts(&ports)));
626 ALOGW_IF(ports.empty(), "%s: module %s returned an empty list of audio ports",
627 __func__, mInstance.c_str());
628 mDefaultInputPortId = mDefaultOutputPortId = -1;
629 const int defaultDeviceFlag = 1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE;
630 for (auto it = ports.begin(); it != ports.end(); ) {
631 const auto& port = *it;
632 if (port.ext.getTag() != AudioPortExt::Tag::device) {
633 ++it;
634 continue;
635 }
636 const AudioPortDeviceExt& deviceExt = port.ext.get<AudioPortExt::Tag::device>();
637 if ((deviceExt.flags & defaultDeviceFlag) != 0) {
638 if (port.flags.getTag() == AudioIoFlags::Tag::input) {
639 mDefaultInputPortId = port.id;
640 } else if (port.flags.getTag() == AudioIoFlags::Tag::output) {
641 mDefaultOutputPortId = port.id;
642 }
643 }
644 // For compatibility with HIDL, hide "template" remote submix ports from ports list.
645 if (const auto& devDesc = deviceExt.device;
646 (devDesc.type.type == AudioDeviceType::IN_SUBMIX ||
647 devDesc.type.type == AudioDeviceType::OUT_SUBMIX) &&
648 devDesc.type.connection == AudioDeviceDescription::CONNECTION_VIRTUAL) {
649 if (devDesc.type.type == AudioDeviceType::IN_SUBMIX) {
650 mRemoteSubmixIn = port;
651 } else {
652 mRemoteSubmixOut = port;
653 }
654 it = ports.erase(it);
655 } else {
656 ++it;
657 }
658 }
659 if (mRemoteSubmixIn.has_value() != mRemoteSubmixOut.has_value()) {
660 ALOGE("%s: The configuration only has input or output remote submix device, must have both",
661 __func__);
662 mRemoteSubmixIn.reset();
663 mRemoteSubmixOut.reset();
664 }
665 if (mRemoteSubmixIn.has_value()) {
666 AudioPort connectedRSubmixIn = *mRemoteSubmixIn;
667 connectedRSubmixIn.ext.get<AudioPortExt::Tag::device>().device.address =
668 AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS;
669 ALOGD("%s: connecting remote submix input", __func__);
670 RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->connectExternalDevice(
671 connectedRSubmixIn, &connectedRSubmixIn)));
672 // The template port for the remote submix input couldn't be "default" because it is not
673 // attached. The connected port can now be made default because we never disconnect it.
674 if (mDefaultInputPortId == -1) {
675 mDefaultInputPortId = connectedRSubmixIn.id;
676 }
677 ports.push_back(std::move(connectedRSubmixIn));
678
679 // Remote submix output must not be connected until the framework actually starts
680 // using it, however for legacy compatibility we need to provide an "augmented template"
681 // port with an address and profiles. It is obtained by connecting the output and then
682 // immediately disconnecting it. This is a cheap operation as we don't open any streams.
683 AudioPort tempConnectedRSubmixOut = *mRemoteSubmixOut;
684 tempConnectedRSubmixOut.ext.get<AudioPortExt::Tag::device>().device.address =
685 AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS;
686 ALOGD("%s: temporarily connecting and disconnecting remote submix output", __func__);
687 RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->connectExternalDevice(
688 tempConnectedRSubmixOut, &tempConnectedRSubmixOut)));
689 RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->disconnectExternalDevice(
690 tempConnectedRSubmixOut.id)));
691 tempConnectedRSubmixOut.id = mRemoteSubmixOut->id;
692 ports.push_back(std::move(tempConnectedRSubmixOut));
693 }
694
695 ALOGI("%s: module %s default port ids: input %d, output %d",
696 __func__, mInstance.c_str(), mDefaultInputPortId, mDefaultOutputPortId);
697 std::transform(ports.begin(), ports.end(), std::inserter(mPorts, mPorts.end()),
698 [](const auto& p) { return std::make_pair(p.id, p); });
699 RETURN_STATUS_IF_ERROR(updateRoutes());
700 std::vector<AudioPortConfig> portConfigs;
701 RETURN_STATUS_IF_ERROR(
702 statusTFromBinderStatus(mModule->getAudioPortConfigs(&portConfigs))); // OK if empty
703 std::transform(portConfigs.begin(), portConfigs.end(),
704 std::inserter(mPortConfigs, mPortConfigs.end()),
705 [](const auto& p) { return std::make_pair(p.id, p); });
706 std::transform(mPortConfigs.begin(), mPortConfigs.end(),
707 std::inserter(mInitialPortConfigIds, mInitialPortConfigIds.end()),
708 [](const auto& pcPair) { return pcPair.first; });
709 std::vector<AudioPatch> patches;
710 RETURN_STATUS_IF_ERROR(
711 statusTFromBinderStatus(mModule->getAudioPatches(&patches))); // OK if empty
712 std::transform(patches.begin(), patches.end(),
713 std::inserter(mPatches, mPatches.end()),
714 [](const auto& p) { return std::make_pair(p.id, p); });
715 return OK;
716}
717
Mikhail Naganova317a802024-03-15 18:03:10 +0000718std::set<int32_t> Hal2AidlMapper::getPatchIdsByPortId(int32_t portId) {
719 std::set<int32_t> result;
720 for (const auto& [patchId, patch] : mPatches) {
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800721 for (int32_t id : patch.sourcePortConfigIds) {
Mikhail Naganova317a802024-03-15 18:03:10 +0000722 if (portConfigBelongsToPort(id, portId)) {
723 result.insert(patchId);
724 break;
725 }
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700726 }
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800727 for (int32_t id : patch.sinkPortConfigIds) {
Mikhail Naganova317a802024-03-15 18:03:10 +0000728 if (portConfigBelongsToPort(id, portId)) {
729 result.insert(patchId);
730 break;
731 }
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700732 }
733 }
Mikhail Naganova317a802024-03-15 18:03:10 +0000734 return result;
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700735}
736
jiabin62750c22023-12-21 22:06:07 +0000737status_t Hal2AidlMapper::prepareToDisconnectExternalDevice(const AudioPort& devicePort) {
738 auto portsIt = findPort(devicePort.ext.get<AudioPortExt::device>().device);
739 if (portsIt == mPorts.end()) {
740 return BAD_VALUE;
741 }
742 return statusTFromBinderStatus(mModule->prepareToDisconnectExternalDevice(portsIt->second.id));
743}
744
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700745status_t Hal2AidlMapper::prepareToOpenStream(
746 int32_t ioHandle, const AudioDevice& device, const AudioIoFlags& flags,
747 AudioSource source, Cleanups* cleanups, AudioConfig* config,
748 AudioPortConfig* mixPortConfig, AudioPatch* patch) {
749 ALOGD("%p %s: handle %d, device %s, flags %s, source %s, config %s, mix port config %s",
750 this, __func__, ioHandle, device.toString().c_str(),
751 flags.toString().c_str(), toString(source).c_str(),
752 config->toString().c_str(), mixPortConfig->toString().c_str());
Mikhail Naganova317a802024-03-15 18:03:10 +0000753 resetUnusedPatchesAndPortConfigs();
Mikhail Naganovca92a5c2023-12-07 14:00:48 -0800754 const AudioConfig initialConfig = *config;
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700755 // Find / create AudioPortConfigs for the device port and the mix port,
756 // then find / create a patch between them, and open a stream on the mix port.
757 AudioPortConfig devicePortConfig;
758 bool created = false;
Mikhail Naganovca92a5c2023-12-07 14:00:48 -0800759 RETURN_STATUS_IF_ERROR(findOrCreateDevicePortConfig(device, config,
760 &devicePortConfig, &created));
761 LOG_ALWAYS_FATAL_IF(devicePortConfig.id == 0);
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700762 if (created) {
763 cleanups->add(&Hal2AidlMapper::resetPortConfig, devicePortConfig.id);
764 }
Mikhail Naganov38220af2023-12-07 14:00:48 -0800765 status_t status = prepareToOpenStreamHelper(ioHandle, devicePortConfig.portId,
766 devicePortConfig.id, flags, source, initialConfig, cleanups, config,
767 mixPortConfig, patch);
768 if (status != OK) {
769 // If using the client-provided config did not work out for establishing a mix port config
770 // or patching, try with the device port config. Note that in general device port config and
771 // mix port config are not required to be the same, however they must match if the HAL
772 // module can't perform audio stream conversions.
773 AudioConfig deviceConfig = initialConfig;
774 if (setConfigFromPortConfig(&deviceConfig, devicePortConfig)->base != initialConfig.base) {
775 ALOGD("%s: retrying with device port config: %s", __func__,
776 devicePortConfig.toString().c_str());
777 status = prepareToOpenStreamHelper(ioHandle, devicePortConfig.portId,
778 devicePortConfig.id, flags, source, initialConfig, cleanups,
779 &deviceConfig, mixPortConfig, patch);
780 if (status == OK) {
781 *config = deviceConfig;
782 }
783 }
784 }
785 return status;
786}
787
788status_t Hal2AidlMapper::prepareToOpenStreamHelper(
789 int32_t ioHandle, int32_t devicePortId, int32_t devicePortConfigId,
790 const AudioIoFlags& flags, AudioSource source, const AudioConfig& initialConfig,
791 Cleanups* cleanups, AudioConfig* config, AudioPortConfig* mixPortConfig,
792 AudioPatch* patch) {
793 const bool isInput = flags.getTag() == AudioIoFlags::Tag::input;
794 bool created = false;
Mikhail Naganovca92a5c2023-12-07 14:00:48 -0800795 RETURN_STATUS_IF_ERROR(findOrCreateMixPortConfig(*config, flags, ioHandle, source,
Mikhail Naganov38220af2023-12-07 14:00:48 -0800796 std::set<int32_t>{devicePortId}, mixPortConfig, &created));
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700797 if (created) {
798 cleanups->add(&Hal2AidlMapper::resetPortConfig, mixPortConfig->id);
799 }
800 setConfigFromPortConfig(config, *mixPortConfig);
Mikhail Naganovca92a5c2023-12-07 14:00:48 -0800801 bool retryWithSuggestedConfig = false; // By default, let the framework to retry.
802 if (mixPortConfig->id == 0 && config->base == AudioConfigBase{}) {
803 // The HAL proposes a default config, can retry here.
804 retryWithSuggestedConfig = true;
805 } else if (isInput && config->base != initialConfig.base) {
806 // If the resulting config is different, we must stop and provide the config to the
807 // framework so that it can retry.
808 mixPortConfig->id = 0;
809 } else if (!isInput && mixPortConfig->id == 0 &&
810 (initialConfig.base.format.type == AudioFormatType::PCM ||
811 !isBitPositionFlagSet(flags.get<AudioIoFlags::output>(),
812 AudioOutputFlags::DIRECT) ||
813 isBitPositionFlagSet(flags.get<AudioIoFlags::output>(),
814 AudioOutputFlags::COMPRESS_OFFLOAD))) {
815 // The framework does not retry opening non-direct PCM and IEC61937 outputs, need to retry
816 // here (see 'AudioHwDevice::openOutputStream').
817 retryWithSuggestedConfig = true;
818 }
819 if (mixPortConfig->id == 0 && retryWithSuggestedConfig) {
820 ALOGD("%s: retrying to find/create a mix port config using config %s", __func__,
821 config->toString().c_str());
822 RETURN_STATUS_IF_ERROR(findOrCreateMixPortConfig(*config, flags, ioHandle, source,
Mikhail Naganov38220af2023-12-07 14:00:48 -0800823 std::set<int32_t>{devicePortId}, mixPortConfig, &created));
Mikhail Naganovca92a5c2023-12-07 14:00:48 -0800824 if (created) {
825 cleanups->add(&Hal2AidlMapper::resetPortConfig, mixPortConfig->id);
826 }
827 setConfigFromPortConfig(config, *mixPortConfig);
828 }
829 if (mixPortConfig->id == 0) {
830 ALOGD("%p %s: returning suggested config for the stream: %s", this, __func__,
831 config->toString().c_str());
832 return OK;
833 }
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700834 if (isInput) {
835 RETURN_STATUS_IF_ERROR(findOrCreatePatch(
Mikhail Naganov03b0a002024-05-03 20:22:58 +0000836 {devicePortConfigId}, {mixPortConfig->id}, MATCH_BOTH, patch, &created));
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700837 } else {
838 RETURN_STATUS_IF_ERROR(findOrCreatePatch(
Mikhail Naganov03b0a002024-05-03 20:22:58 +0000839 {mixPortConfig->id}, {devicePortConfigId}, MATCH_BOTH, patch, &created));
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700840 }
841 if (created) {
842 cleanups->add(&Hal2AidlMapper::resetPatch, patch->id);
843 }
844 if (config->frameCount <= 0) {
845 config->frameCount = patch->minimumStreamBufferSizeFrames;
846 }
847 return OK;
848}
849
Mikhail Naganovca92a5c2023-12-07 14:00:48 -0800850status_t Hal2AidlMapper::setPortConfig(
851 const AudioPortConfig& requestedPortConfig, const std::set<int32_t>& destinationPortIds,
852 AudioPortConfig* portConfig, Cleanups* cleanups) {
853 bool created = false;
854 RETURN_STATUS_IF_ERROR(findOrCreatePortConfig(
855 requestedPortConfig, destinationPortIds, portConfig, &created));
856 if (created && cleanups != nullptr) {
857 cleanups->add(&Hal2AidlMapper::resetPortConfig, portConfig->id);
858 }
859 return OK;
860}
861
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700862status_t Hal2AidlMapper::releaseAudioPatch(int32_t patchId) {
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800863 return releaseAudioPatches({patchId});
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700864}
865
Mikhail Naganova317a802024-03-15 18:03:10 +0000866// Note: does not reset port configs.
867status_t Hal2AidlMapper::releaseAudioPatch(Patches::iterator it) {
868 const int32_t patchId = it->first;
869 if (ndk::ScopedAStatus status = mModule->resetAudioPatch(patchId); !status.isOk()) {
870 ALOGE("%s: error while resetting patch %d: %s",
871 __func__, patchId, status.getDescription().c_str());
872 return statusTFromBinderStatus(status);
873 }
874 mPatches.erase(it);
875 for (auto it = mFwkPatches.begin(); it != mFwkPatches.end(); ++it) {
876 if (it->second == patchId) {
877 mFwkPatches.erase(it);
878 break;
879 }
880 }
881 return OK;
882}
883
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800884status_t Hal2AidlMapper::releaseAudioPatches(const std::set<int32_t>& patchIds) {
885 status_t result = OK;
886 for (const auto patchId : patchIds) {
887 if (auto it = mPatches.find(patchId); it != mPatches.end()) {
Mikhail Naganova317a802024-03-15 18:03:10 +0000888 releaseAudioPatch(it);
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800889 } else {
890 ALOGE("%s: patch id %d not found", __func__, patchId);
891 result = BAD_VALUE;
892 }
893 }
Mikhail Naganova317a802024-03-15 18:03:10 +0000894 resetUnusedPortConfigs();
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800895 return result;
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700896}
897
898void Hal2AidlMapper::resetPortConfig(int32_t portConfigId) {
899 if (auto it = mPortConfigs.find(portConfigId); it != mPortConfigs.end()) {
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700900 if (ndk::ScopedAStatus status = mModule->resetAudioPortConfig(portConfigId);
901 !status.isOk()) {
902 ALOGE("%s: error while resetting port config %d: %s",
903 __func__, portConfigId, status.getDescription().c_str());
904 }
Mikhail Naganova317a802024-03-15 18:03:10 +0000905 mPortConfigs.erase(it);
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700906 return;
907 }
908 ALOGE("%s: port config id %d not found", __func__, portConfigId);
909}
910
Mikhail Naganova317a802024-03-15 18:03:10 +0000911void Hal2AidlMapper::resetUnusedPatchesAndPortConfigs() {
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800912 // Since patches can be created independently of streams via 'createOrUpdatePatch',
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700913 // here we only clean up patches for released streams.
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800914 std::set<int32_t> patchesToRelease;
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700915 for (auto it = mStreams.begin(); it != mStreams.end(); ) {
916 if (auto streamSp = it->first.promote(); streamSp) {
917 ++it;
918 } else {
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800919 if (const int32_t patchId = it->second.second; patchId != -1) {
920 patchesToRelease.insert(patchId);
921 }
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700922 it = mStreams.erase(it);
923 }
924 }
Mikhail Naganova317a802024-03-15 18:03:10 +0000925 // 'releaseAudioPatches' also resets unused port configs.
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800926 releaseAudioPatches(patchesToRelease);
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700927}
928
Mikhail Naganova317a802024-03-15 18:03:10 +0000929void Hal2AidlMapper::resetUnusedPortConfigs() {
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700930 // The assumption is that port configs are used to create patches
931 // (or to open streams, but that involves creation of patches, too). Thus,
932 // orphaned port configs can and should be reset.
Mikhail Naganova317a802024-03-15 18:03:10 +0000933 std::set<int32_t> portConfigIdsToReset;
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700934 std::transform(mPortConfigs.begin(), mPortConfigs.end(),
Mikhail Naganova317a802024-03-15 18:03:10 +0000935 std::inserter(portConfigIdsToReset, portConfigIdsToReset.end()),
936 [](const auto& pcPair) { return pcPair.first; });
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700937 for (const auto& p : mPatches) {
Mikhail Naganova317a802024-03-15 18:03:10 +0000938 for (int32_t id : p.second.sourcePortConfigIds) portConfigIdsToReset.erase(id);
939 for (int32_t id : p.second.sinkPortConfigIds) portConfigIdsToReset.erase(id);
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700940 }
941 for (int32_t id : mInitialPortConfigIds) {
Mikhail Naganova317a802024-03-15 18:03:10 +0000942 portConfigIdsToReset.erase(id);
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700943 }
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800944 for (const auto& s : mStreams) {
Mikhail Naganova317a802024-03-15 18:03:10 +0000945 portConfigIdsToReset.erase(s.second.first);
Mikhail Naganov78f7f9a2023-11-16 15:49:23 -0800946 }
Mikhail Naganova317a802024-03-15 18:03:10 +0000947 for (const auto& portConfigId : portConfigIdsToReset) {
948 resetPortConfig(portConfigId);
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700949 }
950}
951
952status_t Hal2AidlMapper::setDevicePortConnectedState(const AudioPort& devicePort, bool connected) {
Mikhail Naganova317a802024-03-15 18:03:10 +0000953 resetUnusedPatchesAndPortConfigs();
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700954 if (connected) {
955 AudioDevice matchDevice = devicePort.ext.get<AudioPortExt::device>().device;
956 std::optional<AudioPort> templatePort;
957 auto erasePortAfterConnectionIt = mPorts.end();
958 // Connection of remote submix out with address "0" is a special case. Since there is
959 // already an "augmented template" port with this address in mPorts, we need to replace
960 // it with a connected port.
961 // Connection of remote submix outs with any other address is done as usual except that
962 // the template port is in `mRemoteSubmixOut`.
963 if (mRemoteSubmixOut.has_value() && matchDevice.type.type == AudioDeviceType::OUT_SUBMIX) {
964 if (matchDevice.address == AudioDeviceAddress::make<AudioDeviceAddress::id>(
965 AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS)) {
966 erasePortAfterConnectionIt = findPort(matchDevice);
967 }
968 templatePort = mRemoteSubmixOut;
969 } else if (mRemoteSubmixIn.has_value() &&
970 matchDevice.type.type == AudioDeviceType::IN_SUBMIX) {
971 templatePort = mRemoteSubmixIn;
972 } else {
973 // Reset the device address to find the "template" port.
974 matchDevice.address = AudioDeviceAddress::make<AudioDeviceAddress::id>();
975 }
976 if (!templatePort.has_value()) {
977 auto portsIt = findPort(matchDevice);
978 if (portsIt == mPorts.end()) {
979 // Since 'setConnectedState' is called for all modules, it is normal when the device
980 // port not found in every one of them.
981 return BAD_VALUE;
982 } else {
983 ALOGD("%s: device port for device %s found in the module %s",
984 __func__, matchDevice.toString().c_str(), mInstance.c_str());
985 }
986 templatePort = portsIt->second;
987 }
Mikhail Naganovac9d4e72023-10-23 12:00:09 -0700988
989 // Use the ID of the "template" port, use all the information from the provided port.
990 AudioPort connectedPort = devicePort;
991 connectedPort.id = templatePort->id;
992 RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->connectExternalDevice(
993 connectedPort, &connectedPort)));
994 const auto [it, inserted] = mPorts.insert(std::make_pair(connectedPort.id, connectedPort));
995 LOG_ALWAYS_FATAL_IF(!inserted,
996 "%s: module %s, duplicate port ID received from HAL: %s, existing port: %s",
997 __func__, mInstance.c_str(), connectedPort.toString().c_str(),
998 it->second.toString().c_str());
Mikhail Naganova317a802024-03-15 18:03:10 +0000999 mConnectedPorts.insert(connectedPort.id);
Mikhail Naganovac9d4e72023-10-23 12:00:09 -07001000 if (erasePortAfterConnectionIt != mPorts.end()) {
1001 mPorts.erase(erasePortAfterConnectionIt);
1002 }
1003 } else { // !connected
1004 AudioDevice matchDevice = devicePort.ext.get<AudioPortExt::device>().device;
1005 auto portsIt = findPort(matchDevice);
1006 if (portsIt == mPorts.end()) {
1007 // Since 'setConnectedState' is called for all modules, it is normal when the device
1008 // port not found in every one of them.
1009 return BAD_VALUE;
1010 } else {
1011 ALOGD("%s: device port for device %s found in the module %s",
1012 __func__, matchDevice.toString().c_str(), mInstance.c_str());
1013 }
Mikhail Naganovac9d4e72023-10-23 12:00:09 -07001014
1015 // Disconnection of remote submix out with address "0" is a special case. We need to replace
1016 // the connected port entry with the "augmented template".
1017 const int32_t portId = portsIt->second.id;
1018 if (mRemoteSubmixOut.has_value() && matchDevice.type.type == AudioDeviceType::OUT_SUBMIX &&
1019 matchDevice.address == AudioDeviceAddress::make<AudioDeviceAddress::id>(
1020 AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS)) {
1021 mDisconnectedPortReplacement = std::make_pair(portId, *mRemoteSubmixOut);
1022 auto& port = mDisconnectedPortReplacement.second;
1023 port.ext.get<AudioPortExt::Tag::device>().device = matchDevice;
1024 port.profiles = portsIt->second.profiles;
1025 }
Mikhail Naganova317a802024-03-15 18:03:10 +00001026
1027 // Patches may still exist, the framework may reset or update them later.
1028 // For disconnection to succeed, need to release these patches first.
1029 if (std::set<int32_t> patchIdsToRelease = getPatchIdsByPortId(portId);
1030 !patchIdsToRelease.empty()) {
1031 FwkPatches releasedPatches;
1032 status_t status = OK;
1033 for (int32_t patchId : patchIdsToRelease) {
1034 if (auto it = mPatches.find(patchId); it != mPatches.end()) {
1035 if (status = releaseAudioPatch(it); status != OK) break;
1036 releasedPatches.insert(std::make_pair(patchId, patchId));
1037 }
1038 }
1039 resetUnusedPortConfigs();
Mikhail Naganov67c2f6d2024-03-18 09:48:27 -07001040 // Patches created by Hal2AidlMapper during stream creation and not "claimed"
1041 // by the framework must not be surfaced to it.
1042 for (auto& s : mStreams) {
1043 if (auto it = releasedPatches.find(s.second.second); it != releasedPatches.end()) {
1044 releasedPatches.erase(it);
1045 }
1046 }
Mikhail Naganova317a802024-03-15 18:03:10 +00001047 mFwkPatches.merge(releasedPatches);
1048 LOG_ALWAYS_FATAL_IF(!releasedPatches.empty(),
1049 "mFwkPatches already contains some of released patches");
1050 if (status != OK) return status;
Mikhail Naganovac9d4e72023-10-23 12:00:09 -07001051 }
Mikhail Naganova317a802024-03-15 18:03:10 +00001052 RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->disconnectExternalDevice(portId)));
1053 eraseConnectedPort(portId);
Mikhail Naganovac9d4e72023-10-23 12:00:09 -07001054 }
1055 return updateRoutes();
1056}
1057
1058status_t Hal2AidlMapper::updateAudioPort(int32_t portId, AudioPort* port) {
1059 const status_t status = statusTFromBinderStatus(mModule->getAudioPort(portId, port));
1060 if (status == OK) {
1061 auto portIt = mPorts.find(portId);
1062 if (portIt != mPorts.end()) {
jiabin255ff7f2024-01-11 00:24:47 +00001063 if (port->ext.getTag() == AudioPortExt::Tag::mix && portIt->second != *port) {
1064 mDynamicMixPortIds.insert(portId);
1065 }
Mikhail Naganovac9d4e72023-10-23 12:00:09 -07001066 portIt->second = *port;
1067 } else {
1068 ALOGW("%s, port(%d) returned successfully from the HAL but not it is not cached",
1069 __func__, portId);
1070 }
1071 }
1072 return status;
1073}
1074
1075status_t Hal2AidlMapper::updateRoutes() {
1076 RETURN_STATUS_IF_ERROR(
1077 statusTFromBinderStatus(mModule->getAudioRoutes(&mRoutes)));
1078 ALOGW_IF(mRoutes.empty(), "%s: module %s returned an empty list of audio routes",
1079 __func__, mInstance.c_str());
1080 if (mRemoteSubmixIn.has_value()) {
1081 // Remove mentions of the template remote submix input from routes.
1082 int32_t rSubmixInId = mRemoteSubmixIn->id;
1083 // Remove mentions of the template remote submix out only if it is not in mPorts
1084 // (that means there is a connected port in mPorts).
1085 int32_t rSubmixOutId = mPorts.find(mRemoteSubmixOut->id) == mPorts.end() ?
1086 mRemoteSubmixOut->id : -1;
1087 for (auto it = mRoutes.begin(); it != mRoutes.end();) {
1088 auto& route = *it;
1089 if (route.sinkPortId == rSubmixOutId) {
1090 it = mRoutes.erase(it);
1091 continue;
1092 }
1093 if (auto routeIt = std::find(route.sourcePortIds.begin(), route.sourcePortIds.end(),
1094 rSubmixInId); routeIt != route.sourcePortIds.end()) {
1095 route.sourcePortIds.erase(routeIt);
1096 if (route.sourcePortIds.empty()) {
1097 it = mRoutes.erase(it);
1098 continue;
1099 }
1100 }
1101 ++it;
1102 }
1103 }
1104 mRoutingMatrix.clear();
1105 for (const auto& r : mRoutes) {
1106 for (auto portId : r.sourcePortIds) {
1107 mRoutingMatrix.emplace(r.sinkPortId, portId);
1108 mRoutingMatrix.emplace(portId, r.sinkPortId);
1109 }
1110 }
1111 return OK;
1112}
1113
jiabin255ff7f2024-01-11 00:24:47 +00001114void Hal2AidlMapper::updateDynamicMixPorts() {
1115 for (int32_t portId : mDynamicMixPortIds) {
1116 if (auto it = mPorts.find(portId); it != mPorts.end()) {
1117 updateAudioPort(portId, &it->second);
1118 } else {
1119 // This must not happen
1120 ALOGE("%s, cannot find port for id=%d", __func__, portId);
1121 }
1122 }
1123}
1124
Mikhail Naganovac9d4e72023-10-23 12:00:09 -07001125} // namespace android