blob: 543efd1b3e5eff4d6976b9498e27cd75eed52c2b [file] [log] [blame]
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001/*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <algorithm>
18#include <set>
19
20#define LOG_TAG "AHAL_Module"
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000021#include <aidl/android/media/audio/common/AudioInputFlags.h>
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000022#include <aidl/android/media/audio/common/AudioOutputFlags.h>
Mikhail Naganov26dc9ad2023-06-23 13:55:37 -070023#include <android-base/logging.h>
24#include <android/binder_ibinder_platform.h>
25#include <error/expected_utils.h>
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000026
Lorena Torres-Huerta394e2522022-12-20 02:21:41 +000027#include "core-impl/Configuration.h"
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000028#include "core-impl/Module.h"
Mikhail Naganovb03b5c42023-07-26 13:13:35 -070029#include "core-impl/ModuleBluetooth.h"
Mikhail Naganov521fc492023-07-11 17:24:08 -070030#include "core-impl/ModulePrimary.h"
Shraddha Basantwani6bb69632023-04-25 15:26:38 +053031#include "core-impl/ModuleRemoteSubmix.h"
Mikhail Naganov521fc492023-07-11 17:24:08 -070032#include "core-impl/ModuleStub.h"
jiabin253bd322023-01-25 23:57:31 +000033#include "core-impl/ModuleUsb.h"
Vlad Popa943b7e22022-12-08 14:24:12 +010034#include "core-impl/SoundDose.h"
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000035#include "core-impl/utils.h"
36
Mikhail Naganov55045b52023-10-24 17:03:50 -070037using aidl::android::hardware::audio::common::frameCountFromDurationMs;
Mikhail Naganov872d4a62023-03-09 18:19:01 -080038using aidl::android::hardware::audio::common::getFrameSizeInBytes;
Kuowei Li53a8d4d2024-06-24 14:35:07 +080039using aidl::android::hardware::audio::common::hasMmapFlag;
Mikhail Naganov872d4a62023-03-09 18:19:01 -080040using aidl::android::hardware::audio::common::isBitPositionFlagSet;
41using aidl::android::hardware::audio::common::isValidAudioMode;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000042using aidl::android::hardware::audio::common::SinkMetadata;
43using aidl::android::hardware::audio::common::SourceMetadata;
Vlad Popa2afbd1e2022-12-28 17:04:58 +010044using aidl::android::hardware::audio::core::sounddose::ISoundDose;
Mikhail Naganov6a4872d2022-06-15 21:39:04 +000045using aidl::android::media::audio::common::AudioChannelLayout;
Mikhail Naganovef6bc742022-10-06 00:14:19 +000046using aidl::android::media::audio::common::AudioDevice;
Lorena Torres-Huerta533cc782023-01-18 00:11:48 +000047using aidl::android::media::audio::common::AudioDeviceType;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000048using aidl::android::media::audio::common::AudioFormatDescription;
Mikhail Naganov6a4872d2022-06-15 21:39:04 +000049using aidl::android::media::audio::common::AudioFormatType;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000050using aidl::android::media::audio::common::AudioInputFlags;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000051using aidl::android::media::audio::common::AudioIoFlags;
jiabin9a8e6862023-01-12 23:06:37 +000052using aidl::android::media::audio::common::AudioMMapPolicy;
53using aidl::android::media::audio::common::AudioMMapPolicyInfo;
54using aidl::android::media::audio::common::AudioMMapPolicyType;
Mikhail Naganov04ae8222023-01-11 15:48:10 -080055using aidl::android::media::audio::common::AudioMode;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000056using aidl::android::media::audio::common::AudioOffloadInfo;
57using aidl::android::media::audio::common::AudioOutputFlags;
58using aidl::android::media::audio::common::AudioPort;
59using aidl::android::media::audio::common::AudioPortConfig;
60using aidl::android::media::audio::common::AudioPortExt;
61using aidl::android::media::audio::common::AudioProfile;
Mikhail Naganov20047bc2023-01-05 20:16:07 +000062using aidl::android::media::audio::common::Boolean;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000063using aidl::android::media::audio::common::Int;
Mikhail Naganov6725ef52023-02-09 17:52:50 -080064using aidl::android::media::audio::common::MicrophoneInfo;
Mikhail Naganov6a4872d2022-06-15 21:39:04 +000065using aidl::android::media::audio::common::PcmType;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000066
67namespace aidl::android::hardware::audio::core {
68
69namespace {
70
Mikhail Naganov84bcc042023-10-05 17:36:57 -070071inline bool hasDynamicChannelMasks(const std::vector<AudioChannelLayout>& channelMasks) {
72 return channelMasks.empty() ||
73 std::all_of(channelMasks.begin(), channelMasks.end(),
74 [](const auto& channelMask) { return channelMask == AudioChannelLayout{}; });
75}
76
77inline bool hasDynamicFormat(const AudioFormatDescription& format) {
78 return format == AudioFormatDescription{};
79}
80
81inline bool hasDynamicSampleRates(const std::vector<int32_t>& sampleRates) {
82 return sampleRates.empty() ||
83 std::all_of(sampleRates.begin(), sampleRates.end(),
84 [](const auto& sampleRate) { return sampleRate == 0; });
85}
86
87inline bool isDynamicProfile(const AudioProfile& profile) {
88 return hasDynamicFormat(profile.format) || hasDynamicChannelMasks(profile.channelMasks) ||
89 hasDynamicSampleRates(profile.sampleRates);
90}
91
92bool hasDynamicProfilesOnly(const std::vector<AudioProfile>& profiles) {
93 if (profiles.empty()) return true;
94 return std::all_of(profiles.begin(), profiles.end(), isDynamicProfile);
95}
96
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000097bool findAudioProfile(const AudioPort& port, const AudioFormatDescription& format,
98 AudioProfile* profile) {
99 if (auto profilesIt =
100 find_if(port.profiles.begin(), port.profiles.end(),
101 [&format](const auto& profile) { return profile.format == format; });
102 profilesIt != port.profiles.end()) {
103 *profile = *profilesIt;
104 return true;
105 }
106 return false;
107}
Mikhail Naganov00603d12022-05-02 22:52:13 +0000108
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000109} // namespace
110
jiabin253bd322023-01-25 23:57:31 +0000111// static
Lorena Torres-Huerta394e2522022-12-20 02:21:41 +0000112std::shared_ptr<Module> Module::createInstance(Type type, std::unique_ptr<Configuration>&& config) {
jiabin253bd322023-01-25 23:57:31 +0000113 switch (type) {
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530114 case Type::DEFAULT:
Lorena Torres-Huerta394e2522022-12-20 02:21:41 +0000115 return ndk::SharedRefBase::make<ModulePrimary>(std::move(config));
Mikhail Naganov521fc492023-07-11 17:24:08 -0700116 case Type::R_SUBMIX:
Lorena Torres-Huerta394e2522022-12-20 02:21:41 +0000117 return ndk::SharedRefBase::make<ModuleRemoteSubmix>(std::move(config));
Mikhail Naganov521fc492023-07-11 17:24:08 -0700118 case Type::STUB:
Lorena Torres-Huerta394e2522022-12-20 02:21:41 +0000119 return ndk::SharedRefBase::make<ModuleStub>(std::move(config));
Mikhail Naganov521fc492023-07-11 17:24:08 -0700120 case Type::USB:
Lorena Torres-Huerta394e2522022-12-20 02:21:41 +0000121 return ndk::SharedRefBase::make<ModuleUsb>(std::move(config));
Mikhail Naganovb03b5c42023-07-26 13:13:35 -0700122 case Type::BLUETOOTH:
Lorena Torres-Huerta394e2522022-12-20 02:21:41 +0000123 return ndk::SharedRefBase::make<ModuleBluetooth>(std::move(config));
jiabin253bd322023-01-25 23:57:31 +0000124 }
125}
126
Lorena Torres-Huerta394e2522022-12-20 02:21:41 +0000127// static
128std::optional<Module::Type> Module::typeFromString(const std::string& type) {
129 if (type == "default")
130 return Module::Type::DEFAULT;
131 else if (type == "r_submix")
132 return Module::Type::R_SUBMIX;
133 else if (type == "stub")
134 return Module::Type::STUB;
135 else if (type == "usb")
136 return Module::Type::USB;
137 else if (type == "bluetooth")
138 return Module::Type::BLUETOOTH;
139 return {};
140}
141
Mikhail Naganovd5536d92023-03-24 18:27:58 -0700142std::ostream& operator<<(std::ostream& os, Module::Type t) {
143 switch (t) {
144 case Module::Type::DEFAULT:
145 os << "default";
146 break;
147 case Module::Type::R_SUBMIX:
148 os << "r_submix";
149 break;
Mikhail Naganov521fc492023-07-11 17:24:08 -0700150 case Module::Type::STUB:
151 os << "stub";
152 break;
Mikhail Naganovd5536d92023-03-24 18:27:58 -0700153 case Module::Type::USB:
154 os << "usb";
155 break;
Mikhail Naganovb03b5c42023-07-26 13:13:35 -0700156 case Module::Type::BLUETOOTH:
157 os << "bluetooth";
158 break;
Mikhail Naganovd5536d92023-03-24 18:27:58 -0700159 }
160 return os;
161}
162
Lorena Torres-Huertaf7492512023-01-14 02:49:41 +0000163Module::Module(Type type, std::unique_ptr<Configuration>&& config)
164 : mType(type), mConfig(std::move(config)) {
165 populateConnectedProfiles();
166}
167
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000168void Module::cleanUpPatch(int32_t patchId) {
169 erase_all_values(mPatches, std::set<int32_t>{patchId});
170}
171
Mikhail Naganov8651b362023-01-06 23:15:27 +0000172ndk::ScopedAStatus Module::createStreamContext(
173 int32_t in_portConfigId, int64_t in_bufferSizeFrames,
174 std::shared_ptr<IStreamCallback> asyncCallback,
175 std::shared_ptr<IStreamOutEventCallback> outEventCallback, StreamContext* out_context) {
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000176 if (in_bufferSizeFrames <= 0) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530177 LOG(ERROR) << __func__ << ": " << mType << ": non-positive buffer size "
178 << in_bufferSizeFrames;
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000179 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
180 }
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000181 auto& configs = getConfig().portConfigs;
182 auto portConfigIt = findById<AudioPortConfig>(configs, in_portConfigId);
Mikhail Naganova92039a2023-12-20 14:27:22 -0800183 const int32_t nominalLatencyMs = getNominalLatencyMs(*portConfigIt);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000184 // Since this is a private method, it is assumed that
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000185 // validity of the portConfigId has already been checked.
Mikhail Naganova92039a2023-12-20 14:27:22 -0800186 const int32_t minimumStreamBufferSizeFrames =
187 calculateBufferSizeFrames(nominalLatencyMs, portConfigIt->sampleRate.value().value);
Mikhail Naganov13501872023-10-18 16:15:46 -0700188 if (in_bufferSizeFrames < minimumStreamBufferSizeFrames) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530189 LOG(ERROR) << __func__ << ": " << mType << ": insufficient buffer size "
190 << in_bufferSizeFrames << ", must be at least " << minimumStreamBufferSizeFrames;
Mikhail Naganov13501872023-10-18 16:15:46 -0700191 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
192 }
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000193 const size_t frameSize =
194 getFrameSizeInBytes(portConfigIt->format.value(), portConfigIt->channelMask.value());
195 if (frameSize == 0) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530196 LOG(ERROR) << __func__ << ": " << mType
197 << ": could not calculate frame size for port config "
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000198 << portConfigIt->toString();
199 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
200 }
Jaideep Sharma559a4912024-03-07 10:05:51 +0530201 LOG(DEBUG) << __func__ << ": " << mType << ": frame size " << frameSize << " bytes";
Mikhail Naganovb511b8a2023-05-15 14:35:24 -0700202 if (frameSize > static_cast<size_t>(kMaximumStreamBufferSizeBytes / in_bufferSizeFrames)) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530203 LOG(ERROR) << __func__ << ": " << mType << ": buffer size " << in_bufferSizeFrames
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000204 << " frames is too large, maximum size is "
205 << kMaximumStreamBufferSizeBytes / frameSize;
206 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
207 }
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000208 const auto& flags = portConfigIt->flags.value();
Kuowei Li53a8d4d2024-06-24 14:35:07 +0800209 StreamContext::DebugParameters params{mDebug.streamTransientStateDelayMs,
210 mVendorDebug.forceTransientBurst,
211 mVendorDebug.forceSynchronousDrain};
212 std::unique_ptr<StreamContext::DataMQ> dataMQ = nullptr;
213 std::shared_ptr<IStreamCallback> streamAsyncCallback = nullptr;
214 std::shared_ptr<ISoundDose> soundDose;
215 if (!getSoundDose(&soundDose).isOk()) {
216 LOG(ERROR) << __func__ << ": could not create sound dose instance";
217 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
218 }
219 if (!hasMmapFlag(flags)) {
220 dataMQ = std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames);
221 streamAsyncCallback = asyncCallback;
222 }
223 StreamContext temp(
224 std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
225 std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
226 portConfigIt->format.value(), portConfigIt->channelMask.value(),
227 portConfigIt->sampleRate.value().value, flags, nominalLatencyMs,
228 portConfigIt->ext.get<AudioPortExt::mix>().handle, std::move(dataMQ),
229 streamAsyncCallback, outEventCallback, mSoundDose.getInstance(), params);
230 if (temp.isValid()) {
231 *out_context = std::move(temp);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000232 } else {
Kuowei Li53a8d4d2024-06-24 14:35:07 +0800233 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000234 }
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000235 return ndk::ScopedAStatus::ok();
236}
237
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000238std::vector<AudioDevice> Module::findConnectedDevices(int32_t portConfigId) {
239 std::vector<AudioDevice> result;
240 auto& ports = getConfig().ports;
241 auto portIds = portIdsFromPortConfigIds(findConnectedPortConfigIds(portConfigId));
242 for (auto it = portIds.begin(); it != portIds.end(); ++it) {
243 auto portIt = findById<AudioPort>(ports, *it);
244 if (portIt != ports.end() && portIt->ext.getTag() == AudioPortExt::Tag::device) {
245 result.push_back(portIt->ext.template get<AudioPortExt::Tag::device>().device);
246 }
247 }
248 return result;
249}
250
251std::set<int32_t> Module::findConnectedPortConfigIds(int32_t portConfigId) {
252 std::set<int32_t> result;
253 auto patchIdsRange = mPatches.equal_range(portConfigId);
254 auto& patches = getConfig().patches;
255 for (auto it = patchIdsRange.first; it != patchIdsRange.second; ++it) {
256 auto patchIt = findById<AudioPatch>(patches, it->second);
257 if (patchIt == patches.end()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530258 LOG(FATAL) << __func__ << ": " << mType << ": patch with id " << it->second
259 << " taken from mPatches "
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000260 << "not found in the configuration";
261 }
262 if (std::find(patchIt->sourcePortConfigIds.begin(), patchIt->sourcePortConfigIds.end(),
263 portConfigId) != patchIt->sourcePortConfigIds.end()) {
264 result.insert(patchIt->sinkPortConfigIds.begin(), patchIt->sinkPortConfigIds.end());
265 } else {
266 result.insert(patchIt->sourcePortConfigIds.begin(), patchIt->sourcePortConfigIds.end());
267 }
268 }
269 return result;
270}
271
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000272ndk::ScopedAStatus Module::findPortIdForNewStream(int32_t in_portConfigId, AudioPort** port) {
273 auto& configs = getConfig().portConfigs;
274 auto portConfigIt = findById<AudioPortConfig>(configs, in_portConfigId);
275 if (portConfigIt == configs.end()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530276 LOG(ERROR) << __func__ << ": " << mType << ": existing port config id " << in_portConfigId
277 << " not found";
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000278 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
279 }
280 const int32_t portId = portConfigIt->portId;
281 // In our implementation, configs of mix ports always have unique IDs.
282 CHECK(portId != in_portConfigId);
283 auto& ports = getConfig().ports;
284 auto portIt = findById<AudioPort>(ports, portId);
285 if (portIt == ports.end()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530286 LOG(ERROR) << __func__ << ": " << mType << ": port id " << portId
287 << " used by port config id " << in_portConfigId << " not found";
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000288 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
289 }
290 if (mStreams.count(in_portConfigId) != 0) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530291 LOG(ERROR) << __func__ << ": " << mType << ": port config id " << in_portConfigId
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000292 << " already has a stream opened on it";
293 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
294 }
295 if (portIt->ext.getTag() != AudioPortExt::Tag::mix) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530296 LOG(ERROR) << __func__ << ": " << mType << ": port config id " << in_portConfigId
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000297 << " does not correspond to a mix port";
298 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
299 }
Mikhail Naganovb511b8a2023-05-15 14:35:24 -0700300 const size_t maxOpenStreamCount = portIt->ext.get<AudioPortExt::Tag::mix>().maxOpenStreamCount;
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000301 if (maxOpenStreamCount != 0 && mStreams.count(portId) >= maxOpenStreamCount) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530302 LOG(ERROR) << __func__ << ": " << mType << ": port id " << portId
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000303 << " has already reached maximum allowed opened stream count: "
304 << maxOpenStreamCount;
305 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
306 }
307 *port = &(*portIt);
308 return ndk::ScopedAStatus::ok();
309}
310
Mikhail Naganova92039a2023-12-20 14:27:22 -0800311bool Module::generateDefaultPortConfig(const AudioPort& port, AudioPortConfig* config) {
312 const bool allowDynamicConfig = port.ext.getTag() == AudioPortExt::device;
313 for (const auto& profile : port.profiles) {
314 if (isDynamicProfile(profile)) continue;
315 config->format = profile.format;
316 config->channelMask = *profile.channelMasks.begin();
317 config->sampleRate = Int{.value = *profile.sampleRates.begin()};
318 config->flags = port.flags;
319 config->ext = port.ext;
320 return true;
321 }
322 if (allowDynamicConfig) {
323 config->format = AudioFormatDescription{};
324 config->channelMask = AudioChannelLayout{};
325 config->sampleRate = Int{.value = 0};
326 config->flags = port.flags;
327 config->ext = port.ext;
328 return true;
329 }
Jaideep Sharma559a4912024-03-07 10:05:51 +0530330 LOG(ERROR) << __func__ << ": " << mType << ": port " << port.id << " only has dynamic profiles";
Mikhail Naganova92039a2023-12-20 14:27:22 -0800331 return false;
332}
333
Lorena Torres-Huertaf7492512023-01-14 02:49:41 +0000334void Module::populateConnectedProfiles() {
335 Configuration& config = getConfig();
336 for (const AudioPort& port : config.ports) {
337 if (port.ext.getTag() == AudioPortExt::device) {
338 if (auto devicePort = port.ext.get<AudioPortExt::device>();
339 !devicePort.device.type.connection.empty() && port.profiles.empty()) {
340 if (auto connIt = config.connectedProfiles.find(port.id);
341 connIt == config.connectedProfiles.end()) {
342 config.connectedProfiles.emplace(
343 port.id, internal::getStandard16And24BitPcmAudioProfiles());
344 }
345 }
346 }
347 }
348}
349
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000350template <typename C>
351std::set<int32_t> Module::portIdsFromPortConfigIds(C portConfigIds) {
352 std::set<int32_t> result;
353 auto& portConfigs = getConfig().portConfigs;
354 for (auto it = portConfigIds.begin(); it != portConfigIds.end(); ++it) {
355 auto portConfigIt = findById<AudioPortConfig>(portConfigs, *it);
356 if (portConfigIt != portConfigs.end()) {
357 result.insert(portConfigIt->portId);
358 }
359 }
360 return result;
361}
362
Lorena Torres-Huerta394e2522022-12-20 02:21:41 +0000363std::unique_ptr<Module::Configuration> Module::initializeConfig() {
364 return internal::getConfiguration(getType());
Peter Yoon918a6a52023-07-13 17:04:37 +0900365}
366
Mikhail Naganov13501872023-10-18 16:15:46 -0700367int32_t Module::getNominalLatencyMs(const AudioPortConfig&) {
368 // Arbitrary value. Implementations must override this method to provide their actual latency.
369 static constexpr int32_t kLatencyMs = 5;
370 return kLatencyMs;
371}
372
Kuowei Li53a8d4d2024-06-24 14:35:07 +0800373ndk::ScopedAStatus Module::createMmapBuffer(
374 const ::aidl::android::hardware::audio::core::StreamContext& context __unused,
375 ::aidl::android::hardware::audio::core::StreamDescriptor* desc __unused) {
376 LOG(ERROR) << __func__ << ": " << mType << ": is not implemented";
377 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
378}
379
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700380std::vector<AudioRoute*> Module::getAudioRoutesForAudioPortImpl(int32_t portId) {
381 std::vector<AudioRoute*> result;
382 auto& routes = getConfig().routes;
383 for (auto& r : routes) {
384 const auto& srcs = r.sourcePortIds;
385 if (r.sinkPortId == portId || std::find(srcs.begin(), srcs.end(), portId) != srcs.end()) {
386 result.push_back(&r);
387 }
388 }
389 return result;
390}
391
Lorena Torres-Huerta394e2522022-12-20 02:21:41 +0000392Module::Configuration& Module::getConfig() {
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000393 if (!mConfig) {
Peter Yoon918a6a52023-07-13 17:04:37 +0900394 mConfig = std::move(initializeConfig());
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000395 }
396 return *mConfig;
397}
398
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700399std::set<int32_t> Module::getRoutableAudioPortIds(int32_t portId,
400 std::vector<AudioRoute*>* routes) {
401 std::vector<AudioRoute*> routesStorage;
402 if (routes == nullptr) {
403 routesStorage = getAudioRoutesForAudioPortImpl(portId);
404 routes = &routesStorage;
405 }
406 std::set<int32_t> result;
407 for (AudioRoute* r : *routes) {
408 if (r->sinkPortId == portId) {
409 result.insert(r->sourcePortIds.begin(), r->sourcePortIds.end());
410 } else {
411 result.insert(r->sinkPortId);
412 }
413 }
414 return result;
415}
416
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000417void Module::registerPatch(const AudioPatch& patch) {
418 auto& configs = getConfig().portConfigs;
419 auto do_insert = [&](const std::vector<int32_t>& portConfigIds) {
420 for (auto portConfigId : portConfigIds) {
421 auto configIt = findById<AudioPortConfig>(configs, portConfigId);
422 if (configIt != configs.end()) {
423 mPatches.insert(std::pair{portConfigId, patch.id});
424 if (configIt->portId != portConfigId) {
425 mPatches.insert(std::pair{configIt->portId, patch.id});
426 }
427 }
428 };
429 };
430 do_insert(patch.sourcePortConfigIds);
431 do_insert(patch.sinkPortConfigIds);
432}
433
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700434ndk::ScopedAStatus Module::updateStreamsConnectedState(const AudioPatch& oldPatch,
435 const AudioPatch& newPatch) {
Mikhail Naganov89a8ea92023-09-29 17:02:12 -0700436 // Notify streams about the new set of devices they are connected to.
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700437 auto maybeFailure = ndk::ScopedAStatus::ok();
Mikhail Naganov89a8ea92023-09-29 17:02:12 -0700438 using Connections =
439 std::map<int32_t /*mixPortConfigId*/, std::set<int32_t /*devicePortConfigId*/>>;
440 Connections oldConnections, newConnections;
441 auto fillConnectionsHelper = [&](Connections& connections,
442 const std::vector<int32_t>& mixPortCfgIds,
443 const std::vector<int32_t>& devicePortCfgIds) {
444 for (int32_t mixPortCfgId : mixPortCfgIds) {
445 connections[mixPortCfgId].insert(devicePortCfgIds.begin(), devicePortCfgIds.end());
446 }
447 };
448 auto fillConnections = [&](Connections& connections, const AudioPatch& patch) {
449 if (std::find_if(patch.sourcePortConfigIds.begin(), patch.sourcePortConfigIds.end(),
450 [&](int32_t portConfigId) { return mStreams.count(portConfigId) > 0; }) !=
451 patch.sourcePortConfigIds.end()) {
452 // Sources are mix ports.
453 fillConnectionsHelper(connections, patch.sourcePortConfigIds, patch.sinkPortConfigIds);
454 } else if (std::find_if(patch.sinkPortConfigIds.begin(), patch.sinkPortConfigIds.end(),
455 [&](int32_t portConfigId) {
456 return mStreams.count(portConfigId) > 0;
457 }) != patch.sinkPortConfigIds.end()) {
458 // Sources are device ports.
459 fillConnectionsHelper(connections, patch.sinkPortConfigIds, patch.sourcePortConfigIds);
460 } // Otherwise, there are no streams to notify.
461 };
462 fillConnections(oldConnections, oldPatch);
463 fillConnections(newConnections, newPatch);
464
465 std::for_each(oldConnections.begin(), oldConnections.end(), [&](const auto& connectionPair) {
466 const int32_t mixPortConfigId = connectionPair.first;
467 if (auto it = newConnections.find(mixPortConfigId);
468 it == newConnections.end() || it->second != connectionPair.second) {
469 if (auto status = mStreams.setStreamConnectedDevices(mixPortConfigId, {});
470 status.isOk()) {
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700471 LOG(DEBUG) << "updateStreamsConnectedState: The stream on port config id "
Mikhail Naganov89a8ea92023-09-29 17:02:12 -0700472 << mixPortConfigId << " has been disconnected";
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700473 } else {
474 // Disconnection is tricky to roll back, just register a failure.
475 maybeFailure = std::move(status);
476 }
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000477 }
478 });
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700479 if (!maybeFailure.isOk()) return maybeFailure;
Mikhail Naganov89a8ea92023-09-29 17:02:12 -0700480 std::set<int32_t> idsToDisconnectOnFailure;
481 std::for_each(newConnections.begin(), newConnections.end(), [&](const auto& connectionPair) {
482 const int32_t mixPortConfigId = connectionPair.first;
483 if (auto it = oldConnections.find(mixPortConfigId);
484 it == oldConnections.end() || it->second != connectionPair.second) {
485 const auto connectedDevices = findConnectedDevices(mixPortConfigId);
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700486 if (connectedDevices.empty()) {
487 // This is important as workers use the vector size to derive the connection status.
488 LOG(FATAL) << "updateStreamsConnectedState: No connected devices found for port "
489 "config id "
Mikhail Naganov89a8ea92023-09-29 17:02:12 -0700490 << mixPortConfigId;
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700491 }
Mikhail Naganov89a8ea92023-09-29 17:02:12 -0700492 if (auto status = mStreams.setStreamConnectedDevices(mixPortConfigId, connectedDevices);
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700493 status.isOk()) {
494 LOG(DEBUG) << "updateStreamsConnectedState: The stream on port config id "
Mikhail Naganov89a8ea92023-09-29 17:02:12 -0700495 << mixPortConfigId << " has been connected to: "
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700496 << ::android::internal::ToString(connectedDevices);
497 } else {
498 maybeFailure = std::move(status);
Mikhail Naganov89a8ea92023-09-29 17:02:12 -0700499 idsToDisconnectOnFailure.insert(mixPortConfigId);
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700500 }
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000501 }
502 });
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700503 if (!maybeFailure.isOk()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530504 LOG(WARNING) << __func__ << ": " << mType
505 << ": Due to a failure, disconnecting streams on port config ids "
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700506 << ::android::internal::ToString(idsToDisconnectOnFailure);
507 std::for_each(idsToDisconnectOnFailure.begin(), idsToDisconnectOnFailure.end(),
508 [&](const auto& portConfigId) {
509 auto status = mStreams.setStreamConnectedDevices(portConfigId, {});
510 (void)status.isOk(); // Can't do much about a failure here.
511 });
512 return maybeFailure;
513 }
514 return ndk::ScopedAStatus::ok();
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000515}
516
Mikhail Naganov00603d12022-05-02 22:52:13 +0000517ndk::ScopedAStatus Module::setModuleDebug(
518 const ::aidl::android::hardware::audio::core::ModuleDebug& in_debug) {
Mikhail Naganovd5536d92023-03-24 18:27:58 -0700519 LOG(DEBUG) << __func__ << ": " << mType << ": old flags:" << mDebug.toString()
Mikhail Naganov00603d12022-05-02 22:52:13 +0000520 << ", new flags: " << in_debug.toString();
521 if (mDebug.simulateDeviceConnections != in_debug.simulateDeviceConnections &&
522 !mConnectedDevicePorts.empty()) {
Mikhail Naganovd5536d92023-03-24 18:27:58 -0700523 LOG(ERROR) << __func__ << ": " << mType
Jaideep Sharma559a4912024-03-07 10:05:51 +0530524 << ": attempting to change device connections simulation while "
525 "having external "
Mikhail Naganovd5536d92023-03-24 18:27:58 -0700526 << "devices connected";
Mikhail Naganov00603d12022-05-02 22:52:13 +0000527 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
528 }
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000529 if (in_debug.streamTransientStateDelayMs < 0) {
Mikhail Naganovd5536d92023-03-24 18:27:58 -0700530 LOG(ERROR) << __func__ << ": " << mType << ": streamTransientStateDelayMs is negative: "
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000531 << in_debug.streamTransientStateDelayMs;
532 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
533 }
Mikhail Naganov00603d12022-05-02 22:52:13 +0000534 mDebug = in_debug;
535 return ndk::ScopedAStatus::ok();
536}
537
Mikhail Naganov3b125b72022-10-05 02:12:39 +0000538ndk::ScopedAStatus Module::getTelephony(std::shared_ptr<ITelephony>* _aidl_return) {
Mikhail Naganov521fc492023-07-11 17:24:08 -0700539 *_aidl_return = nullptr;
Jaideep Sharma559a4912024-03-07 10:05:51 +0530540 LOG(DEBUG) << __func__ << ": " << mType << ": returning null";
Mikhail Naganov3b125b72022-10-05 02:12:39 +0000541 return ndk::ScopedAStatus::ok();
542}
543
Mikhail Naganov10c6fe22022-09-30 23:49:17 +0000544ndk::ScopedAStatus Module::getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) {
Mikhail Naganov521fc492023-07-11 17:24:08 -0700545 *_aidl_return = nullptr;
Jaideep Sharma559a4912024-03-07 10:05:51 +0530546 LOG(DEBUG) << __func__ << ": " << mType << ": returning null";
Mikhail Naganov10c6fe22022-09-30 23:49:17 +0000547 return ndk::ScopedAStatus::ok();
548}
549
Mikhail Naganov7499a002023-02-27 18:51:44 -0800550ndk::ScopedAStatus Module::getBluetoothA2dp(std::shared_ptr<IBluetoothA2dp>* _aidl_return) {
Mikhail Naganov521fc492023-07-11 17:24:08 -0700551 *_aidl_return = nullptr;
Jaideep Sharma559a4912024-03-07 10:05:51 +0530552 LOG(DEBUG) << __func__ << ": " << mType << ": returning null";
Mikhail Naganov7499a002023-02-27 18:51:44 -0800553 return ndk::ScopedAStatus::ok();
554}
555
Mikhail Naganovb5647da2023-03-06 14:37:38 -0800556ndk::ScopedAStatus Module::getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) {
Mikhail Naganov521fc492023-07-11 17:24:08 -0700557 *_aidl_return = nullptr;
Jaideep Sharma559a4912024-03-07 10:05:51 +0530558 LOG(DEBUG) << __func__ << ": " << mType << ": returning null";
Mikhail Naganovb5647da2023-03-06 14:37:38 -0800559 return ndk::ScopedAStatus::ok();
560}
561
Mikhail Naganov00603d12022-05-02 22:52:13 +0000562ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdAndAdditionalData,
563 AudioPort* _aidl_return) {
564 const int32_t templateId = in_templateIdAndAdditionalData.id;
565 auto& ports = getConfig().ports;
566 AudioPort connectedPort;
567 { // Scope the template port so that we don't accidentally modify it.
568 auto templateIt = findById<AudioPort>(ports, templateId);
569 if (templateIt == ports.end()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530570 LOG(ERROR) << __func__ << ": " << mType << ": port id " << templateId << " not found";
Mikhail Naganov00603d12022-05-02 22:52:13 +0000571 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
572 }
573 if (templateIt->ext.getTag() != AudioPortExt::Tag::device) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530574 LOG(ERROR) << __func__ << ": " << mType << ": port id " << templateId
575 << " is not a device port";
Mikhail Naganov00603d12022-05-02 22:52:13 +0000576 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
577 }
Mikhail Naganov00603d12022-05-02 22:52:13 +0000578 auto& templateDevicePort = templateIt->ext.get<AudioPortExt::Tag::device>();
579 if (templateDevicePort.device.type.connection.empty()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530580 LOG(ERROR) << __func__ << ": " << mType << ": port id " << templateId
581 << " is permanently attached";
Mikhail Naganov00603d12022-05-02 22:52:13 +0000582 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
583 }
Mikhail Naganovfcf980e2023-09-07 16:30:11 -0700584 if (mConnectedDevicePorts.find(templateId) != mConnectedDevicePorts.end()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530585 LOG(ERROR) << __func__ << ": " << mType << ": port id " << templateId
586 << " is a connected device port";
Mikhail Naganovfcf980e2023-09-07 16:30:11 -0700587 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
588 }
Mikhail Naganov00603d12022-05-02 22:52:13 +0000589 // Postpone id allocation until we ensure that there are no client errors.
590 connectedPort = *templateIt;
591 connectedPort.extraAudioDescriptors = in_templateIdAndAdditionalData.extraAudioDescriptors;
592 const auto& inputDevicePort =
593 in_templateIdAndAdditionalData.ext.get<AudioPortExt::Tag::device>();
594 auto& connectedDevicePort = connectedPort.ext.get<AudioPortExt::Tag::device>();
595 connectedDevicePort.device.address = inputDevicePort.device.address;
Jaideep Sharma559a4912024-03-07 10:05:51 +0530596 LOG(DEBUG) << __func__ << ": " << mType << ": device port " << connectedPort.id
597 << " device set to " << connectedDevicePort.device.toString();
Mikhail Naganov00603d12022-05-02 22:52:13 +0000598 // Check if there is already a connected port with for the same external device.
Jaideep Sharma559a4912024-03-07 10:05:51 +0530599
Mikhail Naganov7b2d12b2023-03-24 18:29:14 -0700600 for (auto connectedPortPair : mConnectedDevicePorts) {
601 auto connectedPortIt = findById<AudioPort>(ports, connectedPortPair.first);
Mikhail Naganov00603d12022-05-02 22:52:13 +0000602 if (connectedPortIt->ext.get<AudioPortExt::Tag::device>().device ==
603 connectedDevicePort.device) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530604 LOG(ERROR) << __func__ << ": " << mType << ": device "
605 << connectedDevicePort.device.toString()
Mikhail Naganov7b2d12b2023-03-24 18:29:14 -0700606 << " is already connected at the device port id "
607 << connectedPortPair.first;
Mikhail Naganov00603d12022-05-02 22:52:13 +0000608 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
609 }
610 }
611 }
612
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700613 // Two main cases are considered with regard to the profiles of the connected device port:
614 //
615 // 1. If the template device port has dynamic profiles, and at least one routable mix
616 // port also has dynamic profiles, it means that after connecting the device, the
617 // connected device port must have profiles populated with actual capabilities of
618 // the connected device, and dynamic of routable mix ports will be filled
619 // according to these capabilities. An example of this case is connection of an
620 // HDMI or USB device. For USB handled by ADSP, there can be mix ports with static
621 // profiles, and one dedicated mix port for "hi-fi" playback. The latter is left with
622 // dynamic profiles so that they can be populated with actual capabilities of
623 // the connected device.
624 //
625 // 2. If the template device port has dynamic profiles, while all routable mix ports
626 // have static profiles, it means that after connecting the device, the connected
627 // device port can be left with dynamic profiles, and profiles of mix ports are
628 // left untouched. An example of this case is connection of an analog wired
629 // headset, it should be treated in the same way as a speaker.
630 //
631 // Yet another possible case is when both the template device port and all routable
632 // mix ports have static profiles. This is allowed and handled correctly, however, it
633 // is not very practical, since these profiles are likely duplicates of each other.
634
635 std::vector<AudioRoute*> routesToMixPorts = getAudioRoutesForAudioPortImpl(templateId);
636 std::set<int32_t> routableMixPortIds = getRoutableAudioPortIds(templateId, &routesToMixPorts);
Mikhail Naganova92039a2023-12-20 14:27:22 -0800637 const int32_t nextPortId = getConfig().nextPortId++;
Mikhail Naganov55045b52023-10-24 17:03:50 -0700638 if (!mDebug.simulateDeviceConnections) {
639 // Even if the device port has static profiles, the HAL module might need to update
640 // them, or abort the connection process.
Mikhail Naganova92039a2023-12-20 14:27:22 -0800641 RETURN_STATUS_IF_ERROR(populateConnectedDevicePort(&connectedPort, nextPortId));
Mikhail Naganov55045b52023-10-24 17:03:50 -0700642 } else if (hasDynamicProfilesOnly(connectedPort.profiles)) {
643 auto& connectedProfiles = getConfig().connectedProfiles;
644 if (auto connectedProfilesIt = connectedProfiles.find(templateId);
645 connectedProfilesIt != connectedProfiles.end()) {
646 connectedPort.profiles = connectedProfilesIt->second;
Mikhail Naganovfcf980e2023-09-07 16:30:11 -0700647 }
Mikhail Naganov55045b52023-10-24 17:03:50 -0700648 }
649 if (hasDynamicProfilesOnly(connectedPort.profiles)) {
650 // Possible case 2. Check if all routable mix ports have static profiles.
651 if (auto dynamicMixPortIt = std::find_if(ports.begin(), ports.end(),
652 [&routableMixPortIds](const auto& p) {
653 return routableMixPortIds.count(p.id) > 0 &&
654 hasDynamicProfilesOnly(p.profiles);
655 });
656 dynamicMixPortIt != ports.end()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530657 LOG(ERROR) << __func__ << ": " << mType
658 << ": connected port only has dynamic profiles after connecting "
Mikhail Naganov55045b52023-10-24 17:03:50 -0700659 << "external device " << connectedPort.toString() << ", and there exist "
660 << "a routable mix port with dynamic profiles: "
661 << dynamicMixPortIt->toString();
662 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
Shraddha Basantwani6bb69632023-04-25 15:26:38 +0530663 }
664 }
665
Mikhail Naganova92039a2023-12-20 14:27:22 -0800666 connectedPort.id = nextPortId;
Mikhail Naganov7b2d12b2023-03-24 18:29:14 -0700667 auto [connectedPortsIt, _] =
Mikhail Naganov0e128dd2023-09-13 18:01:18 -0700668 mConnectedDevicePorts.insert(std::pair(connectedPort.id, std::set<int32_t>()));
Jaideep Sharma559a4912024-03-07 10:05:51 +0530669 LOG(DEBUG) << __func__ << ": " << mType << ": template port " << templateId
670 << " external device connected, "
Mikhail Naganov00603d12022-05-02 22:52:13 +0000671 << "connected port ID " << connectedPort.id;
Mikhail Naganov00603d12022-05-02 22:52:13 +0000672 ports.push_back(connectedPort);
jiabin783c48b2023-02-28 18:28:06 +0000673 onExternalDeviceConnectionChanged(connectedPort, true /*connected*/);
Mikhail Naganov00603d12022-05-02 22:52:13 +0000674
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700675 // For routes where the template port is a source, add the connected port to sources,
676 // otherwise, create a new route by copying from the route for the template port.
Mikhail Naganov00603d12022-05-02 22:52:13 +0000677 std::vector<AudioRoute> newRoutes;
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700678 for (AudioRoute* r : routesToMixPorts) {
679 if (r->sinkPortId == templateId) {
680 newRoutes.push_back(AudioRoute{.sourcePortIds = r->sourcePortIds,
681 .sinkPortId = connectedPort.id,
682 .isExclusive = r->isExclusive});
Mikhail Naganov00603d12022-05-02 22:52:13 +0000683 } else {
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700684 r->sourcePortIds.push_back(connectedPort.id);
Mikhail Naganov00603d12022-05-02 22:52:13 +0000685 }
686 }
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700687 auto& routes = getConfig().routes;
Mikhail Naganov00603d12022-05-02 22:52:13 +0000688 routes.insert(routes.end(), newRoutes.begin(), newRoutes.end());
689
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700690 if (!hasDynamicProfilesOnly(connectedPort.profiles) && !routableMixPortIds.empty()) {
691 // Note: this is a simplistic approach assuming that a mix port can only be populated
692 // from a single device port. Implementing support for stuffing dynamic profiles with
693 // a superset of all profiles from all routable dynamic device ports would be more involved.
694 for (auto& port : ports) {
695 if (routableMixPortIds.count(port.id) == 0) continue;
696 if (hasDynamicProfilesOnly(port.profiles)) {
697 port.profiles = connectedPort.profiles;
698 connectedPortsIt->second.insert(port.id);
Mikhail Naganov0e128dd2023-09-13 18:01:18 -0700699 } else {
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700700 // Check if profiles are not all dynamic because they were populated by
701 // a previous connection. Otherwise, it means that they are actually static.
702 for (const auto& cp : mConnectedDevicePorts) {
703 if (cp.second.count(port.id) > 0) {
704 connectedPortsIt->second.insert(port.id);
Mikhail Naganov0e128dd2023-09-13 18:01:18 -0700705 break;
706 }
707 }
708 }
Mikhail Naganov7b2d12b2023-03-24 18:29:14 -0700709 }
710 }
711 *_aidl_return = std::move(connectedPort);
712
Mikhail Naganov00603d12022-05-02 22:52:13 +0000713 return ndk::ScopedAStatus::ok();
714}
715
716ndk::ScopedAStatus Module::disconnectExternalDevice(int32_t in_portId) {
717 auto& ports = getConfig().ports;
718 auto portIt = findById<AudioPort>(ports, in_portId);
719 if (portIt == ports.end()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530720 LOG(ERROR) << __func__ << ": " << mType << ": port id " << in_portId << " not found";
Mikhail Naganov00603d12022-05-02 22:52:13 +0000721 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
722 }
723 if (portIt->ext.getTag() != AudioPortExt::Tag::device) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530724 LOG(ERROR) << __func__ << ": " << mType << ": port id " << in_portId
725 << " is not a device port";
Mikhail Naganov00603d12022-05-02 22:52:13 +0000726 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
727 }
Mikhail Naganov7b2d12b2023-03-24 18:29:14 -0700728 auto connectedPortsIt = mConnectedDevicePorts.find(in_portId);
729 if (connectedPortsIt == mConnectedDevicePorts.end()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530730 LOG(ERROR) << __func__ << ": " << mType << ": port id " << in_portId
731 << " is not a connected device port";
Mikhail Naganov00603d12022-05-02 22:52:13 +0000732 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
733 }
734 auto& configs = getConfig().portConfigs;
735 auto& initials = getConfig().initialConfigs;
736 auto configIt = std::find_if(configs.begin(), configs.end(), [&](const auto& config) {
737 if (config.portId == in_portId) {
738 // Check if the configuration was provided by the client.
739 const auto& initialIt = findById<AudioPortConfig>(initials, config.id);
740 return initialIt == initials.end() || config != *initialIt;
741 }
742 return false;
743 });
744 if (configIt != configs.end()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530745 LOG(ERROR) << __func__ << ": " << mType << ": port id " << in_portId
746 << " has a non-default config with id " << configIt->id;
Mikhail Naganov00603d12022-05-02 22:52:13 +0000747 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
748 }
jiabin783c48b2023-02-28 18:28:06 +0000749 onExternalDeviceConnectionChanged(*portIt, false /*connected*/);
Mikhail Naganov00603d12022-05-02 22:52:13 +0000750 ports.erase(portIt);
Jaideep Sharma559a4912024-03-07 10:05:51 +0530751 LOG(DEBUG) << __func__ << ": " << mType << ": connected device port " << in_portId
752 << " released";
Mikhail Naganov00603d12022-05-02 22:52:13 +0000753
754 auto& routes = getConfig().routes;
755 for (auto routesIt = routes.begin(); routesIt != routes.end();) {
756 if (routesIt->sinkPortId == in_portId) {
757 routesIt = routes.erase(routesIt);
758 } else {
759 // Note: the list of sourcePortIds can't become empty because there must
760 // be the id of the template port in the route.
761 erase_if(routesIt->sourcePortIds, [in_portId](auto src) { return src == in_portId; });
762 ++routesIt;
763 }
764 }
765
Mikhail Naganov0e128dd2023-09-13 18:01:18 -0700766 // Clear profiles for mix ports that are not connected to any other ports.
767 std::set<int32_t> mixPortsToClear = std::move(connectedPortsIt->second);
768 mConnectedDevicePorts.erase(connectedPortsIt);
769 for (const auto& connectedPort : mConnectedDevicePorts) {
770 for (int32_t mixPortId : connectedPort.second) {
771 mixPortsToClear.erase(mixPortId);
772 }
773 }
774 for (int32_t mixPortId : mixPortsToClear) {
Mikhail Naganov7b2d12b2023-03-24 18:29:14 -0700775 auto mixPortIt = findById<AudioPort>(ports, mixPortId);
776 if (mixPortIt != ports.end()) {
777 mixPortIt->profiles = {};
778 }
779 }
Mikhail Naganov7b2d12b2023-03-24 18:29:14 -0700780
Mikhail Naganov00603d12022-05-02 22:52:13 +0000781 return ndk::ScopedAStatus::ok();
782}
783
jiabindd23b0e2023-12-11 19:10:05 +0000784ndk::ScopedAStatus Module::prepareToDisconnectExternalDevice(int32_t in_portId) {
785 auto& ports = getConfig().ports;
786 auto portIt = findById<AudioPort>(ports, in_portId);
787 if (portIt == ports.end()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530788 LOG(ERROR) << __func__ << ": " << mType << ": port id " << in_portId << " not found";
jiabindd23b0e2023-12-11 19:10:05 +0000789 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
790 }
791 if (portIt->ext.getTag() != AudioPortExt::Tag::device) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530792 LOG(ERROR) << __func__ << ": " << mType << ": port id " << in_portId
793 << " is not a device port";
jiabindd23b0e2023-12-11 19:10:05 +0000794 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
795 }
796 auto connectedPortsIt = mConnectedDevicePorts.find(in_portId);
797 if (connectedPortsIt == mConnectedDevicePorts.end()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530798 LOG(ERROR) << __func__ << ": " << mType << ": port id " << in_portId
799 << " is not a connected device port";
jiabindd23b0e2023-12-11 19:10:05 +0000800 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
801 }
802
803 onPrepareToDisconnectExternalDevice(*portIt);
804
805 return ndk::ScopedAStatus::ok();
806}
807
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000808ndk::ScopedAStatus Module::getAudioPatches(std::vector<AudioPatch>* _aidl_return) {
809 *_aidl_return = getConfig().patches;
Jaideep Sharma559a4912024-03-07 10:05:51 +0530810 LOG(DEBUG) << __func__ << ": " << mType << ": returning " << _aidl_return->size() << " patches";
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000811 return ndk::ScopedAStatus::ok();
812}
813
814ndk::ScopedAStatus Module::getAudioPort(int32_t in_portId, AudioPort* _aidl_return) {
815 auto& ports = getConfig().ports;
816 auto portIt = findById<AudioPort>(ports, in_portId);
817 if (portIt != ports.end()) {
818 *_aidl_return = *portIt;
Jaideep Sharma559a4912024-03-07 10:05:51 +0530819 LOG(DEBUG) << __func__ << ": " << mType << ": returning port by id " << in_portId;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000820 return ndk::ScopedAStatus::ok();
821 }
Jaideep Sharma559a4912024-03-07 10:05:51 +0530822 LOG(ERROR) << __func__ << ": " << mType << ": port id " << in_portId << " not found";
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000823 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
824}
825
826ndk::ScopedAStatus Module::getAudioPortConfigs(std::vector<AudioPortConfig>* _aidl_return) {
827 *_aidl_return = getConfig().portConfigs;
Jaideep Sharma559a4912024-03-07 10:05:51 +0530828 LOG(DEBUG) << __func__ << ": " << mType << ": returning " << _aidl_return->size()
829 << " port configs";
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000830 return ndk::ScopedAStatus::ok();
831}
832
833ndk::ScopedAStatus Module::getAudioPorts(std::vector<AudioPort>* _aidl_return) {
834 *_aidl_return = getConfig().ports;
Jaideep Sharma559a4912024-03-07 10:05:51 +0530835 LOG(DEBUG) << __func__ << ": " << mType << ": returning " << _aidl_return->size() << " ports";
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000836 return ndk::ScopedAStatus::ok();
837}
838
839ndk::ScopedAStatus Module::getAudioRoutes(std::vector<AudioRoute>* _aidl_return) {
840 *_aidl_return = getConfig().routes;
Jaideep Sharma559a4912024-03-07 10:05:51 +0530841 LOG(DEBUG) << __func__ << ": " << mType << ": returning " << _aidl_return->size() << " routes";
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000842 return ndk::ScopedAStatus::ok();
843}
844
Mikhail Naganov00603d12022-05-02 22:52:13 +0000845ndk::ScopedAStatus Module::getAudioRoutesForAudioPort(int32_t in_portId,
846 std::vector<AudioRoute>* _aidl_return) {
847 auto& ports = getConfig().ports;
848 if (auto portIt = findById<AudioPort>(ports, in_portId); portIt == ports.end()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530849 LOG(ERROR) << __func__ << ": " << mType << ": port id " << in_portId << " not found";
Mikhail Naganov00603d12022-05-02 22:52:13 +0000850 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
851 }
Mikhail Naganov84bcc042023-10-05 17:36:57 -0700852 std::vector<AudioRoute*> routes = getAudioRoutesForAudioPortImpl(in_portId);
853 std::transform(routes.begin(), routes.end(), std::back_inserter(*_aidl_return),
854 [](auto rptr) { return *rptr; });
Mikhail Naganov00603d12022-05-02 22:52:13 +0000855 return ndk::ScopedAStatus::ok();
856}
857
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000858ndk::ScopedAStatus Module::openInputStream(const OpenInputStreamArguments& in_args,
859 OpenInputStreamReturn* _aidl_return) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530860 LOG(DEBUG) << __func__ << ": " << mType << ": port config id " << in_args.portConfigId
861 << ", buffer size " << in_args.bufferSizeFrames << " frames";
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000862 AudioPort* port = nullptr;
Mikhail Naganov26dc9ad2023-06-23 13:55:37 -0700863 RETURN_STATUS_IF_ERROR(findPortIdForNewStream(in_args.portConfigId, &port));
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000864 if (port->flags.getTag() != AudioIoFlags::Tag::input) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530865 LOG(ERROR) << __func__ << ": " << mType << ": port config id " << in_args.portConfigId
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000866 << " does not correspond to an input mix port";
867 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
868 }
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000869 StreamContext context;
Mikhail Naganov26dc9ad2023-06-23 13:55:37 -0700870 RETURN_STATUS_IF_ERROR(createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames,
871 nullptr, nullptr, &context));
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000872 context.fillDescriptor(&_aidl_return->desc);
Kuowei Li53a8d4d2024-06-24 14:35:07 +0800873 if (hasMmapFlag(context.getFlags())) {
874 RETURN_STATUS_IF_ERROR(createMmapBuffer(context, &_aidl_return->desc));
875 }
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000876 std::shared_ptr<StreamIn> stream;
Mikhail Naganov6ddefdb2023-07-19 17:30:06 -0700877 RETURN_STATUS_IF_ERROR(createInputStream(std::move(context), in_args.sinkMetadata,
Lorena Torres-Huerta533cc782023-01-18 00:11:48 +0000878 getMicrophoneInfos(), &stream));
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000879 StreamWrapper streamWrapper(stream);
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700880 if (auto patchIt = mPatches.find(in_args.portConfigId); patchIt != mPatches.end()) {
881 RETURN_STATUS_IF_ERROR(
882 streamWrapper.setConnectedDevices(findConnectedDevices(in_args.portConfigId)));
883 }
Mikhail Naganovefb45bc2024-03-27 16:20:33 +0000884 auto streamBinder = streamWrapper.getBinder();
885 AIBinder_setMinSchedulerPolicy(streamBinder.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
886 AIBinder_setInheritRt(streamBinder.get(), true);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000887 mStreams.insert(port->id, in_args.portConfigId, std::move(streamWrapper));
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000888 _aidl_return->stream = std::move(stream);
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000889 return ndk::ScopedAStatus::ok();
890}
891
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000892ndk::ScopedAStatus Module::openOutputStream(const OpenOutputStreamArguments& in_args,
893 OpenOutputStreamReturn* _aidl_return) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530894 LOG(DEBUG) << __func__ << ": " << mType << ": port config id " << in_args.portConfigId
895 << ", has offload info? " << (in_args.offloadInfo.has_value()) << ", buffer size "
896 << in_args.bufferSizeFrames << " frames";
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000897 AudioPort* port = nullptr;
Mikhail Naganov26dc9ad2023-06-23 13:55:37 -0700898 RETURN_STATUS_IF_ERROR(findPortIdForNewStream(in_args.portConfigId, &port));
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000899 if (port->flags.getTag() != AudioIoFlags::Tag::output) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530900 LOG(ERROR) << __func__ << ": " << mType << ": port config id " << in_args.portConfigId
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000901 << " does not correspond to an output mix port";
902 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
903 }
Mikhail Naganova2c5ddf2022-09-12 22:57:14 +0000904 const bool isOffload = isBitPositionFlagSet(port->flags.get<AudioIoFlags::Tag::output>(),
905 AudioOutputFlags::COMPRESS_OFFLOAD);
906 if (isOffload && !in_args.offloadInfo.has_value()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530907 LOG(ERROR) << __func__ << ": " << mType << ": port id " << port->id
Mikhail Naganov111e0ce2022-06-17 21:41:19 +0000908 << " has COMPRESS_OFFLOAD flag set, requires offload info";
909 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
910 }
Mikhail Naganov30301a42022-09-13 01:20:45 +0000911 const bool isNonBlocking = isBitPositionFlagSet(port->flags.get<AudioIoFlags::Tag::output>(),
912 AudioOutputFlags::NON_BLOCKING);
913 if (isNonBlocking && in_args.callback == nullptr) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530914 LOG(ERROR) << __func__ << ": " << mType << ": port id " << port->id
Mikhail Naganov30301a42022-09-13 01:20:45 +0000915 << " has NON_BLOCKING flag set, requires async callback";
916 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
917 }
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000918 StreamContext context;
Mikhail Naganov26dc9ad2023-06-23 13:55:37 -0700919 RETURN_STATUS_IF_ERROR(createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames,
920 isNonBlocking ? in_args.callback : nullptr,
921 in_args.eventCallback, &context));
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000922 context.fillDescriptor(&_aidl_return->desc);
Kuowei Li53a8d4d2024-06-24 14:35:07 +0800923 if (hasMmapFlag(context.getFlags())) {
924 RETURN_STATUS_IF_ERROR(createMmapBuffer(context, &_aidl_return->desc));
925 }
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000926 std::shared_ptr<StreamOut> stream;
Mikhail Naganov6ddefdb2023-07-19 17:30:06 -0700927 RETURN_STATUS_IF_ERROR(createOutputStream(std::move(context), in_args.sourceMetadata,
Mikhail Naganov9d16a6a2023-06-26 17:21:04 -0700928 in_args.offloadInfo, &stream));
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000929 StreamWrapper streamWrapper(stream);
Mikhail Naganov75b59df2023-06-23 13:39:40 -0700930 if (auto patchIt = mPatches.find(in_args.portConfigId); patchIt != mPatches.end()) {
931 RETURN_STATUS_IF_ERROR(
932 streamWrapper.setConnectedDevices(findConnectedDevices(in_args.portConfigId)));
933 }
Mikhail Naganovefb45bc2024-03-27 16:20:33 +0000934 auto streamBinder = streamWrapper.getBinder();
935 AIBinder_setMinSchedulerPolicy(streamBinder.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
936 AIBinder_setInheritRt(streamBinder.get(), true);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000937 mStreams.insert(port->id, in_args.portConfigId, std::move(streamWrapper));
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000938 _aidl_return->stream = std::move(stream);
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000939 return ndk::ScopedAStatus::ok();
940}
941
Mikhail Naganov74927202022-12-19 16:37:14 +0000942ndk::ScopedAStatus Module::getSupportedPlaybackRateFactors(
943 SupportedPlaybackRateFactors* _aidl_return) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530944 LOG(DEBUG) << __func__ << ": " << mType;
Mikhail Naganov74927202022-12-19 16:37:14 +0000945 (void)_aidl_return;
946 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
947}
948
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000949ndk::ScopedAStatus Module::setAudioPatch(const AudioPatch& in_requested, AudioPatch* _aidl_return) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530950 LOG(DEBUG) << __func__ << ": " << mType << ": requested patch " << in_requested.toString();
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000951 if (in_requested.sourcePortConfigIds.empty()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530952 LOG(ERROR) << __func__ << ": " << mType << ": requested patch has empty sources list";
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000953 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
954 }
955 if (!all_unique<int32_t>(in_requested.sourcePortConfigIds)) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530956 LOG(ERROR) << __func__ << ": " << mType
957 << ": requested patch has duplicate ids in the sources list";
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000958 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
959 }
960 if (in_requested.sinkPortConfigIds.empty()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530961 LOG(ERROR) << __func__ << ": " << mType << ": requested patch has empty sinks list";
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000962 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
963 }
964 if (!all_unique<int32_t>(in_requested.sinkPortConfigIds)) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530965 LOG(ERROR) << __func__ << ": " << mType
966 << ": requested patch has duplicate ids in the sinks list";
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000967 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
968 }
969
970 auto& configs = getConfig().portConfigs;
971 std::vector<int32_t> missingIds;
972 auto sources =
973 selectByIds<AudioPortConfig>(configs, in_requested.sourcePortConfigIds, &missingIds);
974 if (!missingIds.empty()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530975 LOG(ERROR) << __func__ << ": " << mType << ": following source port config ids not found: "
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000976 << ::android::internal::ToString(missingIds);
977 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
978 }
979 auto sinks = selectByIds<AudioPortConfig>(configs, in_requested.sinkPortConfigIds, &missingIds);
980 if (!missingIds.empty()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +0530981 LOG(ERROR) << __func__ << ": " << mType << ": following sink port config ids not found: "
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000982 << ::android::internal::ToString(missingIds);
983 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
984 }
985 // bool indicates whether a non-exclusive route is available.
986 // If only an exclusive route is available, that means the patch can not be
987 // established if there is any other patch which currently uses the sink port.
988 std::map<int32_t, bool> allowedSinkPorts;
989 auto& routes = getConfig().routes;
990 for (auto src : sources) {
991 for (const auto& r : routes) {
992 const auto& srcs = r.sourcePortIds;
993 if (std::find(srcs.begin(), srcs.end(), src->portId) != srcs.end()) {
994 if (!allowedSinkPorts[r.sinkPortId]) { // prefer non-exclusive
995 allowedSinkPorts[r.sinkPortId] = !r.isExclusive;
996 }
997 }
998 }
999 }
1000 for (auto sink : sinks) {
1001 if (allowedSinkPorts.count(sink->portId) == 0) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301002 LOG(ERROR) << __func__ << ": " << mType << ": there is no route to the sink port id "
1003 << sink->portId;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001004 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1005 }
1006 }
Mikhail Naganov26dc9ad2023-06-23 13:55:37 -07001007 RETURN_STATUS_IF_ERROR(checkAudioPatchEndpointsMatch(sources, sinks));
jiabin253bd322023-01-25 23:57:31 +00001008
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001009 auto& patches = getConfig().patches;
1010 auto existing = patches.end();
1011 std::optional<decltype(mPatches)> patchesBackup;
1012 if (in_requested.id != 0) {
1013 existing = findById<AudioPatch>(patches, in_requested.id);
1014 if (existing != patches.end()) {
1015 patchesBackup = mPatches;
1016 cleanUpPatch(existing->id);
1017 } else {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301018 LOG(ERROR) << __func__ << ": " << mType << ": not found existing patch id "
1019 << in_requested.id;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001020 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1021 }
1022 }
1023 // Validate the requested patch.
1024 for (const auto& [sinkPortId, nonExclusive] : allowedSinkPorts) {
1025 if (!nonExclusive && mPatches.count(sinkPortId) != 0) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301026 LOG(ERROR) << __func__ << ": " << mType << ": sink port id " << sinkPortId
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001027 << "is exclusive and is already used by some other patch";
1028 if (patchesBackup.has_value()) {
1029 mPatches = std::move(*patchesBackup);
1030 }
1031 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
1032 }
1033 }
Mikhail Naganov13501872023-10-18 16:15:46 -07001034 // Find the highest sample rate among mix port configs.
1035 std::map<int32_t, AudioPortConfig*> sampleRates;
1036 std::vector<AudioPortConfig*>& mixPortConfigs =
1037 sources[0]->ext.getTag() == AudioPortExt::mix ? sources : sinks;
1038 for (auto mix : mixPortConfigs) {
1039 sampleRates.emplace(mix->sampleRate.value().value, mix);
1040 }
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001041 *_aidl_return = in_requested;
Mikhail Naganov13501872023-10-18 16:15:46 -07001042 auto maxSampleRateIt = std::max_element(sampleRates.begin(), sampleRates.end());
1043 const int32_t latencyMs = getNominalLatencyMs(*(maxSampleRateIt->second));
1044 _aidl_return->minimumStreamBufferSizeFrames =
1045 calculateBufferSizeFrames(latencyMs, maxSampleRateIt->first);
Mikhail Naganov6a4872d2022-06-15 21:39:04 +00001046 _aidl_return->latenciesMs.clear();
1047 _aidl_return->latenciesMs.insert(_aidl_return->latenciesMs.end(),
Mikhail Naganov13501872023-10-18 16:15:46 -07001048 _aidl_return->sinkPortConfigIds.size(), latencyMs);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +00001049 AudioPatch oldPatch{};
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001050 if (existing == patches.end()) {
1051 _aidl_return->id = getConfig().nextPatchId++;
1052 patches.push_back(*_aidl_return);
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001053 } else {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +00001054 oldPatch = *existing;
Mikhail Naganovdc417732023-09-29 15:49:35 -07001055 *existing = *_aidl_return;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001056 }
Mikhail Naganov75b59df2023-06-23 13:39:40 -07001057 patchesBackup = mPatches;
1058 registerPatch(*_aidl_return);
1059 if (auto status = updateStreamsConnectedState(oldPatch, *_aidl_return); !status.isOk()) {
1060 mPatches = std::move(*patchesBackup);
1061 if (existing == patches.end()) {
1062 patches.pop_back();
1063 } else {
1064 *existing = oldPatch;
1065 }
1066 return status;
1067 }
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +00001068
Jaideep Sharma559a4912024-03-07 10:05:51 +05301069 LOG(DEBUG) << __func__ << ": " << mType << ": " << (oldPatch.id == 0 ? "created" : "updated")
1070 << " patch " << _aidl_return->toString();
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001071 return ndk::ScopedAStatus::ok();
1072}
1073
1074ndk::ScopedAStatus Module::setAudioPortConfig(const AudioPortConfig& in_requested,
1075 AudioPortConfig* out_suggested, bool* _aidl_return) {
Mikhail Naganova92039a2023-12-20 14:27:22 -08001076 auto generate = [this](const AudioPort& port, AudioPortConfig* config) {
1077 return generateDefaultPortConfig(port, config);
1078 };
1079 return setAudioPortConfigImpl(in_requested, generate, out_suggested, _aidl_return);
1080}
1081
1082ndk::ScopedAStatus Module::setAudioPortConfigImpl(
1083 const AudioPortConfig& in_requested,
1084 const std::function<bool(const ::aidl::android::media::audio::common::AudioPort& port,
1085 ::aidl::android::media::audio::common::AudioPortConfig* config)>&
1086 fillPortConfig,
1087 AudioPortConfig* out_suggested, bool* applied) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301088 LOG(DEBUG) << __func__ << ": " << mType << ": requested " << in_requested.toString();
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001089 auto& configs = getConfig().portConfigs;
1090 auto existing = configs.end();
1091 if (in_requested.id != 0) {
1092 if (existing = findById<AudioPortConfig>(configs, in_requested.id);
1093 existing == configs.end()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301094 LOG(ERROR) << __func__ << ": " << mType << ": existing port config id "
1095 << in_requested.id << " not found";
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001096 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1097 }
1098 }
1099
1100 const int portId = existing != configs.end() ? existing->portId : in_requested.portId;
1101 if (portId == 0) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301102 LOG(ERROR) << __func__ << ": " << mType
1103 << ": requested port config does not specify portId";
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001104 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1105 }
1106 auto& ports = getConfig().ports;
1107 auto portIt = findById<AudioPort>(ports, portId);
1108 if (portIt == ports.end()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301109 LOG(ERROR) << __func__ << ": " << mType
1110 << ": requested port config points to non-existent portId " << portId;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001111 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1112 }
1113 if (existing != configs.end()) {
1114 *out_suggested = *existing;
1115 } else {
1116 AudioPortConfig newConfig;
Mikhail Naganova92039a2023-12-20 14:27:22 -08001117 newConfig.portId = portIt->id;
1118 if (fillPortConfig(*portIt, &newConfig)) {
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001119 *out_suggested = newConfig;
1120 } else {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301121 LOG(ERROR) << __func__ << ": " << mType
1122 << ": unable generate a default config for port " << portId;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001123 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1124 }
1125 }
1126 // From this moment, 'out_suggested' is either an existing port config,
1127 // or a new generated config. Now attempt to update it according to the specified
1128 // fields of 'in_requested'.
1129
Mikhail Naganov84bcc042023-10-05 17:36:57 -07001130 // Device ports with only dynamic profiles are used for devices that are connected via ADSP,
1131 // which takes care of their actual configuration automatically.
1132 const bool allowDynamicConfig = portIt->ext.getTag() == AudioPortExt::device &&
1133 hasDynamicProfilesOnly(portIt->profiles);
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001134 bool requestedIsValid = true, requestedIsFullySpecified = true;
1135
1136 AudioIoFlags portFlags = portIt->flags;
1137 if (in_requested.flags.has_value()) {
1138 if (in_requested.flags.value() != portFlags) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301139 LOG(WARNING) << __func__ << ": " << mType << ": requested flags "
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001140 << in_requested.flags.value().toString() << " do not match port's "
1141 << portId << " flags " << portFlags.toString();
1142 requestedIsValid = false;
1143 }
1144 } else {
1145 requestedIsFullySpecified = false;
1146 }
1147
1148 AudioProfile portProfile;
1149 if (in_requested.format.has_value()) {
1150 const auto& format = in_requested.format.value();
Mikhail Naganov84bcc042023-10-05 17:36:57 -07001151 if ((format == AudioFormatDescription{} && allowDynamicConfig) ||
1152 findAudioProfile(*portIt, format, &portProfile)) {
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001153 out_suggested->format = format;
1154 } else {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301155 LOG(WARNING) << __func__ << ": " << mType << ": requested format " << format.toString()
Mikhail Naganov84bcc042023-10-05 17:36:57 -07001156 << " is not found in the profiles of port " << portId;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001157 requestedIsValid = false;
1158 }
1159 } else {
1160 requestedIsFullySpecified = false;
1161 }
Mikhail Naganov84bcc042023-10-05 17:36:57 -07001162 if (!(out_suggested->format.value() == AudioFormatDescription{} && allowDynamicConfig) &&
1163 !findAudioProfile(*portIt, out_suggested->format.value(), &portProfile)) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301164 LOG(ERROR) << __func__ << ": " << mType << ": port " << portId
1165 << " does not support format " << out_suggested->format.value().toString()
1166 << " anymore";
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001167 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1168 }
1169
1170 if (in_requested.channelMask.has_value()) {
1171 const auto& channelMask = in_requested.channelMask.value();
Mikhail Naganov84bcc042023-10-05 17:36:57 -07001172 if ((channelMask == AudioChannelLayout{} && allowDynamicConfig) ||
1173 find(portProfile.channelMasks.begin(), portProfile.channelMasks.end(), channelMask) !=
1174 portProfile.channelMasks.end()) {
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001175 out_suggested->channelMask = channelMask;
1176 } else {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301177 LOG(WARNING) << __func__ << ": " << mType << ": requested channel mask "
1178 << channelMask.toString() << " is not supported for the format "
1179 << portProfile.format.toString() << " by the port " << portId;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001180 requestedIsValid = false;
1181 }
1182 } else {
1183 requestedIsFullySpecified = false;
1184 }
1185
1186 if (in_requested.sampleRate.has_value()) {
1187 const auto& sampleRate = in_requested.sampleRate.value();
Mikhail Naganov84bcc042023-10-05 17:36:57 -07001188 if ((sampleRate.value == 0 && allowDynamicConfig) ||
1189 find(portProfile.sampleRates.begin(), portProfile.sampleRates.end(),
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001190 sampleRate.value) != portProfile.sampleRates.end()) {
1191 out_suggested->sampleRate = sampleRate;
1192 } else {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301193 LOG(WARNING) << __func__ << ": " << mType << ": requested sample rate "
1194 << sampleRate.value << " is not supported for the format "
1195 << portProfile.format.toString() << " by the port " << portId;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001196 requestedIsValid = false;
1197 }
1198 } else {
1199 requestedIsFullySpecified = false;
1200 }
1201
1202 if (in_requested.gain.has_value()) {
1203 // Let's pretend that gain can always be applied.
1204 out_suggested->gain = in_requested.gain.value();
1205 }
1206
Mikhail Naganov248e9502023-02-21 16:32:40 -08001207 if (in_requested.ext.getTag() != AudioPortExt::Tag::unspecified) {
1208 if (in_requested.ext.getTag() == out_suggested->ext.getTag()) {
1209 if (out_suggested->ext.getTag() == AudioPortExt::Tag::mix) {
Mikhail Naganovb06a4922024-03-06 16:39:50 -08001210 // 'AudioMixPortExt.handle' and '.usecase' are set by the client,
1211 // copy from in_requested.
1212 const auto& src = in_requested.ext.get<AudioPortExt::Tag::mix>();
1213 auto& dst = out_suggested->ext.get<AudioPortExt::Tag::mix>();
1214 dst.handle = src.handle;
1215 dst.usecase = src.usecase;
Mikhail Naganov248e9502023-02-21 16:32:40 -08001216 }
1217 } else {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301218 LOG(WARNING) << __func__ << ": " << mType << ": requested ext tag "
Mikhail Naganov248e9502023-02-21 16:32:40 -08001219 << toString(in_requested.ext.getTag()) << " do not match port's tag "
1220 << toString(out_suggested->ext.getTag());
1221 requestedIsValid = false;
1222 }
1223 }
1224
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001225 if (existing == configs.end() && requestedIsValid && requestedIsFullySpecified) {
1226 out_suggested->id = getConfig().nextPortId++;
1227 configs.push_back(*out_suggested);
Mikhail Naganova92039a2023-12-20 14:27:22 -08001228 *applied = true;
Jaideep Sharma559a4912024-03-07 10:05:51 +05301229 LOG(DEBUG) << __func__ << ": " << mType << ": created new port config "
1230 << out_suggested->toString();
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001231 } else if (existing != configs.end() && requestedIsValid) {
1232 *existing = *out_suggested;
Mikhail Naganova92039a2023-12-20 14:27:22 -08001233 *applied = true;
Jaideep Sharma559a4912024-03-07 10:05:51 +05301234 LOG(DEBUG) << __func__ << ": " << mType << ": updated port config "
1235 << out_suggested->toString();
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001236 } else {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301237 LOG(DEBUG) << __func__ << ": " << mType << ": not applied; existing config ? "
1238 << (existing != configs.end()) << "; requested is valid? " << requestedIsValid
1239 << ", fully specified? " << requestedIsFullySpecified;
Mikhail Naganova92039a2023-12-20 14:27:22 -08001240 *applied = false;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001241 }
1242 return ndk::ScopedAStatus::ok();
1243}
1244
1245ndk::ScopedAStatus Module::resetAudioPatch(int32_t in_patchId) {
1246 auto& patches = getConfig().patches;
1247 auto patchIt = findById<AudioPatch>(patches, in_patchId);
1248 if (patchIt != patches.end()) {
Mikhail Naganov75b59df2023-06-23 13:39:40 -07001249 auto patchesBackup = mPatches;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001250 cleanUpPatch(patchIt->id);
Mikhail Naganov75b59df2023-06-23 13:39:40 -07001251 if (auto status = updateStreamsConnectedState(*patchIt, AudioPatch{}); !status.isOk()) {
1252 mPatches = std::move(patchesBackup);
1253 return status;
1254 }
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001255 patches.erase(patchIt);
Jaideep Sharma559a4912024-03-07 10:05:51 +05301256 LOG(DEBUG) << __func__ << ": " << mType << ": erased patch " << in_patchId;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001257 return ndk::ScopedAStatus::ok();
1258 }
Jaideep Sharma559a4912024-03-07 10:05:51 +05301259 LOG(ERROR) << __func__ << ": " << mType << ": patch id " << in_patchId << " not found";
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001260 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1261}
1262
1263ndk::ScopedAStatus Module::resetAudioPortConfig(int32_t in_portConfigId) {
1264 auto& configs = getConfig().portConfigs;
1265 auto configIt = findById<AudioPortConfig>(configs, in_portConfigId);
1266 if (configIt != configs.end()) {
1267 if (mStreams.count(in_portConfigId) != 0) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301268 LOG(ERROR) << __func__ << ": " << mType << ": port config id " << in_portConfigId
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001269 << " has a stream opened on it";
1270 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
1271 }
1272 auto patchIt = mPatches.find(in_portConfigId);
1273 if (patchIt != mPatches.end()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301274 LOG(ERROR) << __func__ << ": " << mType << ": port config id " << in_portConfigId
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001275 << " is used by the patch with id " << patchIt->second;
1276 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
1277 }
1278 auto& initials = getConfig().initialConfigs;
1279 auto initialIt = findById<AudioPortConfig>(initials, in_portConfigId);
1280 if (initialIt == initials.end()) {
1281 configs.erase(configIt);
Jaideep Sharma559a4912024-03-07 10:05:51 +05301282 LOG(DEBUG) << __func__ << ": " << mType << ": erased port config " << in_portConfigId;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001283 } else if (*configIt != *initialIt) {
1284 *configIt = *initialIt;
Jaideep Sharma559a4912024-03-07 10:05:51 +05301285 LOG(DEBUG) << __func__ << ": " << mType << ": reset port config " << in_portConfigId;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001286 }
1287 return ndk::ScopedAStatus::ok();
1288 }
Jaideep Sharma559a4912024-03-07 10:05:51 +05301289 LOG(ERROR) << __func__ << ": " << mType << ": port config id " << in_portConfigId
1290 << " not found";
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001291 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1292}
1293
Mikhail Naganov3b125b72022-10-05 02:12:39 +00001294ndk::ScopedAStatus Module::getMasterMute(bool* _aidl_return) {
1295 *_aidl_return = mMasterMute;
Jaideep Sharma559a4912024-03-07 10:05:51 +05301296 LOG(DEBUG) << __func__ << ": " << mType << ": returning " << *_aidl_return;
Mikhail Naganov3b125b72022-10-05 02:12:39 +00001297 return ndk::ScopedAStatus::ok();
1298}
1299
1300ndk::ScopedAStatus Module::setMasterMute(bool in_mute) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301301 LOG(DEBUG) << __func__ << ": " << mType << ": " << in_mute;
jiabin783c48b2023-02-28 18:28:06 +00001302 auto result = mDebug.simulateDeviceConnections ? ndk::ScopedAStatus::ok()
1303 : onMasterMuteChanged(in_mute);
1304 if (result.isOk()) {
1305 mMasterMute = in_mute;
1306 } else {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301307 LOG(ERROR) << __func__ << ": " << mType << ": failed calling onMasterMuteChanged("
1308 << in_mute << "), error=" << result;
jiabin783c48b2023-02-28 18:28:06 +00001309 // Reset master mute if it failed.
1310 onMasterMuteChanged(mMasterMute);
1311 }
Mikhail Naganov55045b52023-10-24 17:03:50 -07001312 return result;
Mikhail Naganov3b125b72022-10-05 02:12:39 +00001313}
1314
1315ndk::ScopedAStatus Module::getMasterVolume(float* _aidl_return) {
1316 *_aidl_return = mMasterVolume;
Jaideep Sharma559a4912024-03-07 10:05:51 +05301317 LOG(DEBUG) << __func__ << ": " << mType << ": returning " << *_aidl_return;
Mikhail Naganov3b125b72022-10-05 02:12:39 +00001318 return ndk::ScopedAStatus::ok();
1319}
1320
1321ndk::ScopedAStatus Module::setMasterVolume(float in_volume) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301322 LOG(DEBUG) << __func__ << ": " << mType << ": " << in_volume;
Mikhail Naganov3b125b72022-10-05 02:12:39 +00001323 if (in_volume >= 0.0f && in_volume <= 1.0f) {
jiabin783c48b2023-02-28 18:28:06 +00001324 auto result = mDebug.simulateDeviceConnections ? ndk::ScopedAStatus::ok()
1325 : onMasterVolumeChanged(in_volume);
1326 if (result.isOk()) {
1327 mMasterVolume = in_volume;
1328 } else {
1329 // Reset master volume if it failed.
Jaideep Sharma559a4912024-03-07 10:05:51 +05301330 LOG(ERROR) << __func__ << ": " << mType << ": failed calling onMasterVolumeChanged("
1331 << in_volume << "), error=" << result;
jiabin783c48b2023-02-28 18:28:06 +00001332 onMasterVolumeChanged(mMasterVolume);
1333 }
Mikhail Naganov55045b52023-10-24 17:03:50 -07001334 return result;
Mikhail Naganov3b125b72022-10-05 02:12:39 +00001335 }
Jaideep Sharma559a4912024-03-07 10:05:51 +05301336 LOG(ERROR) << __func__ << ": " << mType << ": invalid master volume value: " << in_volume;
Mikhail Naganov3b125b72022-10-05 02:12:39 +00001337 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1338}
1339
1340ndk::ScopedAStatus Module::getMicMute(bool* _aidl_return) {
1341 *_aidl_return = mMicMute;
Jaideep Sharma559a4912024-03-07 10:05:51 +05301342 LOG(DEBUG) << __func__ << ": " << mType << ": returning " << *_aidl_return;
Mikhail Naganov3b125b72022-10-05 02:12:39 +00001343 return ndk::ScopedAStatus::ok();
1344}
1345
1346ndk::ScopedAStatus Module::setMicMute(bool in_mute) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301347 LOG(DEBUG) << __func__ << ": " << mType << ": " << in_mute;
Mikhail Naganov3b125b72022-10-05 02:12:39 +00001348 mMicMute = in_mute;
1349 return ndk::ScopedAStatus::ok();
1350}
1351
Mikhail Naganovef6bc742022-10-06 00:14:19 +00001352ndk::ScopedAStatus Module::getMicrophones(std::vector<MicrophoneInfo>* _aidl_return) {
Lorena Torres-Huerta533cc782023-01-18 00:11:48 +00001353 *_aidl_return = getMicrophoneInfos();
Jaideep Sharma559a4912024-03-07 10:05:51 +05301354 LOG(DEBUG) << __func__ << ": " << mType << ": returning "
1355 << ::android::internal::ToString(*_aidl_return);
Mikhail Naganovef6bc742022-10-06 00:14:19 +00001356 return ndk::ScopedAStatus::ok();
1357}
1358
Mikhail Naganov3b125b72022-10-05 02:12:39 +00001359ndk::ScopedAStatus Module::updateAudioMode(AudioMode in_mode) {
Mikhail Naganov04ae8222023-01-11 15:48:10 -08001360 if (!isValidAudioMode(in_mode)) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301361 LOG(ERROR) << __func__ << ": " << mType << ": invalid mode " << toString(in_mode);
Mikhail Naganov04ae8222023-01-11 15:48:10 -08001362 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1363 }
Mikhail Naganov3b125b72022-10-05 02:12:39 +00001364 // No checks for supported audio modes here, it's an informative notification.
Jaideep Sharma559a4912024-03-07 10:05:51 +05301365 LOG(DEBUG) << __func__ << ": " << mType << ": " << toString(in_mode);
Mikhail Naganov3b125b72022-10-05 02:12:39 +00001366 return ndk::ScopedAStatus::ok();
1367}
1368
1369ndk::ScopedAStatus Module::updateScreenRotation(ScreenRotation in_rotation) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301370 LOG(DEBUG) << __func__ << ": " << mType << ": " << toString(in_rotation);
Mikhail Naganov3b125b72022-10-05 02:12:39 +00001371 return ndk::ScopedAStatus::ok();
1372}
1373
1374ndk::ScopedAStatus Module::updateScreenState(bool in_isTurnedOn) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301375 LOG(DEBUG) << __func__ << ": " << mType << ": " << in_isTurnedOn;
Mikhail Naganov3b125b72022-10-05 02:12:39 +00001376 return ndk::ScopedAStatus::ok();
1377}
1378
Vlad Popa83a6d822022-11-07 13:53:57 +01001379ndk::ScopedAStatus Module::getSoundDose(std::shared_ptr<ISoundDose>* _aidl_return) {
Mikhail Naganov7499a002023-02-27 18:51:44 -08001380 if (!mSoundDose) {
Vlad Popa2afbd1e2022-12-28 17:04:58 +01001381 mSoundDose = ndk::SharedRefBase::make<sounddose::SoundDose>();
Vlad Popa943b7e22022-12-08 14:24:12 +01001382 }
Mikhail Naganov780fefb2023-07-21 17:01:38 -07001383 *_aidl_return = mSoundDose.getInstance();
Jaideep Sharma559a4912024-03-07 10:05:51 +05301384 LOG(DEBUG) << __func__ << ": " << mType
1385 << ": returning instance of ISoundDose: " << _aidl_return->get();
Vlad Popa83a6d822022-11-07 13:53:57 +01001386 return ndk::ScopedAStatus::ok();
1387}
1388
Mikhail Naganove9f10fc2022-10-14 23:31:52 +00001389ndk::ScopedAStatus Module::generateHwAvSyncId(int32_t* _aidl_return) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301390 LOG(DEBUG) << __func__ << ": " << mType;
Mikhail Naganove9f10fc2022-10-14 23:31:52 +00001391 (void)_aidl_return;
1392 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
1393}
1394
Mikhail Naganov20047bc2023-01-05 20:16:07 +00001395const std::string Module::VendorDebug::kForceTransientBurstName = "aosp.forceTransientBurst";
Mikhail Naganov194daaa2023-01-05 22:34:20 +00001396const std::string Module::VendorDebug::kForceSynchronousDrainName = "aosp.forceSynchronousDrain";
Mikhail Naganov20047bc2023-01-05 20:16:07 +00001397
Mikhail Naganove9f10fc2022-10-14 23:31:52 +00001398ndk::ScopedAStatus Module::getVendorParameters(const std::vector<std::string>& in_ids,
1399 std::vector<VendorParameter>* _aidl_return) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301400 LOG(VERBOSE) << __func__ << ": " << mType << ": id count: " << in_ids.size();
Mikhail Naganov20047bc2023-01-05 20:16:07 +00001401 bool allParametersKnown = true;
1402 for (const auto& id : in_ids) {
1403 if (id == VendorDebug::kForceTransientBurstName) {
1404 VendorParameter forceTransientBurst{.id = id};
1405 forceTransientBurst.ext.setParcelable(Boolean{mVendorDebug.forceTransientBurst});
1406 _aidl_return->push_back(std::move(forceTransientBurst));
Mikhail Naganov194daaa2023-01-05 22:34:20 +00001407 } else if (id == VendorDebug::kForceSynchronousDrainName) {
1408 VendorParameter forceSynchronousDrain{.id = id};
1409 forceSynchronousDrain.ext.setParcelable(Boolean{mVendorDebug.forceSynchronousDrain});
1410 _aidl_return->push_back(std::move(forceSynchronousDrain));
Mikhail Naganov20047bc2023-01-05 20:16:07 +00001411 } else {
1412 allParametersKnown = false;
Jaideep Sharma559a4912024-03-07 10:05:51 +05301413 LOG(VERBOSE) << __func__ << ": " << mType << ": unrecognized parameter \"" << id << "\"";
Mikhail Naganov20047bc2023-01-05 20:16:07 +00001414 }
1415 }
1416 if (allParametersKnown) return ndk::ScopedAStatus::ok();
1417 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
Mikhail Naganove9f10fc2022-10-14 23:31:52 +00001418}
1419
Mikhail Naganov194daaa2023-01-05 22:34:20 +00001420namespace {
1421
1422template <typename W>
1423bool extractParameter(const VendorParameter& p, decltype(W::value)* v) {
1424 std::optional<W> value;
1425 binder_status_t result = p.ext.getParcelable(&value);
1426 if (result == STATUS_OK && value.has_value()) {
1427 *v = value.value().value;
1428 return true;
1429 }
1430 LOG(ERROR) << __func__ << ": failed to read the value of the parameter \"" << p.id
1431 << "\": " << result;
1432 return false;
1433}
1434
1435} // namespace
1436
Mikhail Naganove9f10fc2022-10-14 23:31:52 +00001437ndk::ScopedAStatus Module::setVendorParameters(const std::vector<VendorParameter>& in_parameters,
1438 bool in_async) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301439 LOG(VERBOSE) << __func__ << ": " << mType << ": parameter count " << in_parameters.size()
1440 << ", async: " << in_async;
Mikhail Naganov20047bc2023-01-05 20:16:07 +00001441 bool allParametersKnown = true;
1442 for (const auto& p : in_parameters) {
1443 if (p.id == VendorDebug::kForceTransientBurstName) {
Mikhail Naganov194daaa2023-01-05 22:34:20 +00001444 if (!extractParameter<Boolean>(p, &mVendorDebug.forceTransientBurst)) {
1445 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1446 }
1447 } else if (p.id == VendorDebug::kForceSynchronousDrainName) {
1448 if (!extractParameter<Boolean>(p, &mVendorDebug.forceSynchronousDrain)) {
Mikhail Naganov20047bc2023-01-05 20:16:07 +00001449 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1450 }
1451 } else {
1452 allParametersKnown = false;
Jaideep Sharma559a4912024-03-07 10:05:51 +05301453 LOG(VERBOSE) << __func__ << ": " << mType << ": unrecognized parameter \"" << p.id
1454 << "\"";
Mikhail Naganov20047bc2023-01-05 20:16:07 +00001455 }
1456 }
1457 if (allParametersKnown) return ndk::ScopedAStatus::ok();
1458 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
Mikhail Naganove9f10fc2022-10-14 23:31:52 +00001459}
1460
Mikhail Naganovfb1acde2022-12-12 18:57:36 +00001461ndk::ScopedAStatus Module::addDeviceEffect(
1462 int32_t in_portConfigId,
1463 const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) {
1464 if (in_effect == nullptr) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301465 LOG(DEBUG) << __func__ << ": " << mType << ": port id " << in_portConfigId
1466 << ", null effect";
Mikhail Naganovfb1acde2022-12-12 18:57:36 +00001467 } else {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301468 LOG(DEBUG) << __func__ << ": " << mType << ": port id " << in_portConfigId
1469 << ", effect Binder " << in_effect->asBinder().get();
Mikhail Naganovfb1acde2022-12-12 18:57:36 +00001470 }
1471 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
1472}
1473
1474ndk::ScopedAStatus Module::removeDeviceEffect(
1475 int32_t in_portConfigId,
1476 const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) {
1477 if (in_effect == nullptr) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301478 LOG(DEBUG) << __func__ << ": " << mType << ": port id " << in_portConfigId
1479 << ", null effect";
Mikhail Naganovfb1acde2022-12-12 18:57:36 +00001480 } else {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301481 LOG(DEBUG) << __func__ << ": " << mType << ": port id " << in_portConfigId
1482 << ", effect Binder " << in_effect->asBinder().get();
Mikhail Naganovfb1acde2022-12-12 18:57:36 +00001483 }
1484 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
1485}
1486
jiabin9a8e6862023-01-12 23:06:37 +00001487ndk::ScopedAStatus Module::getMmapPolicyInfos(AudioMMapPolicyType mmapPolicyType,
1488 std::vector<AudioMMapPolicyInfo>* _aidl_return) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301489 LOG(DEBUG) << __func__ << ": " << mType << ": mmap policy type " << toString(mmapPolicyType);
jiabin9a8e6862023-01-12 23:06:37 +00001490 std::set<int32_t> mmapSinks;
1491 std::set<int32_t> mmapSources;
1492 auto& ports = getConfig().ports;
1493 for (const auto& port : ports) {
1494 if (port.flags.getTag() == AudioIoFlags::Tag::input &&
1495 isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::input>(),
1496 AudioInputFlags::MMAP_NOIRQ)) {
1497 mmapSinks.insert(port.id);
1498 } else if (port.flags.getTag() == AudioIoFlags::Tag::output &&
1499 isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
1500 AudioOutputFlags::MMAP_NOIRQ)) {
1501 mmapSources.insert(port.id);
1502 }
1503 }
Mikhail Naganov85064912023-09-26 17:10:08 -07001504 if (mmapSources.empty() && mmapSinks.empty()) {
1505 AudioMMapPolicyInfo never;
1506 never.mmapPolicy = AudioMMapPolicy::NEVER;
1507 _aidl_return->push_back(never);
1508 return ndk::ScopedAStatus::ok();
1509 }
jiabin9a8e6862023-01-12 23:06:37 +00001510 for (const auto& route : getConfig().routes) {
1511 if (mmapSinks.count(route.sinkPortId) != 0) {
1512 // The sink is a mix port, add the sources if they are device ports.
1513 for (int sourcePortId : route.sourcePortIds) {
1514 auto sourcePortIt = findById<AudioPort>(ports, sourcePortId);
1515 if (sourcePortIt == ports.end()) {
1516 // This must not happen
Jaideep Sharma559a4912024-03-07 10:05:51 +05301517 LOG(ERROR) << __func__ << ": " << mType << ": port id " << sourcePortId
1518 << " cannot be found";
jiabin9a8e6862023-01-12 23:06:37 +00001519 continue;
1520 }
1521 if (sourcePortIt->ext.getTag() != AudioPortExt::Tag::device) {
1522 // The source is not a device port, skip
1523 continue;
1524 }
1525 AudioMMapPolicyInfo policyInfo;
1526 policyInfo.device = sourcePortIt->ext.get<AudioPortExt::Tag::device>().device;
1527 // Always return AudioMMapPolicy.AUTO if the device supports mmap for
1528 // default implementation.
1529 policyInfo.mmapPolicy = AudioMMapPolicy::AUTO;
1530 _aidl_return->push_back(policyInfo);
1531 }
1532 } else {
1533 auto sinkPortIt = findById<AudioPort>(ports, route.sinkPortId);
1534 if (sinkPortIt == ports.end()) {
1535 // This must not happen
Jaideep Sharma559a4912024-03-07 10:05:51 +05301536 LOG(ERROR) << __func__ << ": " << mType << ": port id " << route.sinkPortId
1537 << " cannot be found";
jiabin9a8e6862023-01-12 23:06:37 +00001538 continue;
1539 }
1540 if (sinkPortIt->ext.getTag() != AudioPortExt::Tag::device) {
1541 // The sink is not a device port, skip
1542 continue;
1543 }
1544 if (count_any(mmapSources, route.sourcePortIds)) {
1545 AudioMMapPolicyInfo policyInfo;
1546 policyInfo.device = sinkPortIt->ext.get<AudioPortExt::Tag::device>().device;
1547 // Always return AudioMMapPolicy.AUTO if the device supports mmap for
1548 // default implementation.
1549 policyInfo.mmapPolicy = AudioMMapPolicy::AUTO;
1550 _aidl_return->push_back(policyInfo);
1551 }
1552 }
1553 }
1554 return ndk::ScopedAStatus::ok();
1555}
1556
Eric Laurente2432ea2023-01-12 17:47:31 +01001557ndk::ScopedAStatus Module::supportsVariableLatency(bool* _aidl_return) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301558 LOG(DEBUG) << __func__ << ": " << mType;
Eric Laurente2432ea2023-01-12 17:47:31 +01001559 *_aidl_return = false;
1560 return ndk::ScopedAStatus::ok();
1561}
1562
jiabinb76981e2023-01-18 00:58:30 +00001563ndk::ScopedAStatus Module::getAAudioMixerBurstCount(int32_t* _aidl_return) {
1564 if (!isMmapSupported()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301565 LOG(DEBUG) << __func__ << ": " << mType << ": mmap is not supported ";
jiabinb76981e2023-01-18 00:58:30 +00001566 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
1567 }
1568 *_aidl_return = DEFAULT_AAUDIO_MIXER_BURST_COUNT;
Jaideep Sharma559a4912024-03-07 10:05:51 +05301569 LOG(DEBUG) << __func__ << ": " << mType << ": returning " << *_aidl_return;
jiabinb76981e2023-01-18 00:58:30 +00001570 return ndk::ScopedAStatus::ok();
1571}
1572
1573ndk::ScopedAStatus Module::getAAudioHardwareBurstMinUsec(int32_t* _aidl_return) {
1574 if (!isMmapSupported()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301575 LOG(DEBUG) << __func__ << ": " << mType << ": mmap is not supported ";
jiabinb76981e2023-01-18 00:58:30 +00001576 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
1577 }
1578 *_aidl_return = DEFAULT_AAUDIO_HARDWARE_BURST_MIN_DURATION_US;
Jaideep Sharma559a4912024-03-07 10:05:51 +05301579 LOG(DEBUG) << __func__ << ": " << mType << ": returning " << *_aidl_return;
jiabinb76981e2023-01-18 00:58:30 +00001580 return ndk::ScopedAStatus::ok();
1581}
1582
1583bool Module::isMmapSupported() {
1584 if (mIsMmapSupported.has_value()) {
1585 return mIsMmapSupported.value();
1586 }
1587 std::vector<AudioMMapPolicyInfo> mmapPolicyInfos;
1588 if (!getMmapPolicyInfos(AudioMMapPolicyType::DEFAULT, &mmapPolicyInfos).isOk()) {
1589 mIsMmapSupported = false;
1590 } else {
1591 mIsMmapSupported =
1592 std::find_if(mmapPolicyInfos.begin(), mmapPolicyInfos.end(), [](const auto& info) {
1593 return info.mmapPolicy == AudioMMapPolicy::AUTO ||
1594 info.mmapPolicy == AudioMMapPolicy::ALWAYS;
1595 }) != mmapPolicyInfos.end();
1596 }
1597 return mIsMmapSupported.value();
1598}
1599
Mikhail Naganova92039a2023-12-20 14:27:22 -08001600ndk::ScopedAStatus Module::populateConnectedDevicePort(AudioPort* audioPort, int32_t) {
Mikhail Naganov84bcc042023-10-05 17:36:57 -07001601 if (audioPort->ext.getTag() != AudioPortExt::device) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301602 LOG(ERROR) << __func__ << ": " << mType << ": not a device port: " << audioPort->toString();
Mikhail Naganov84bcc042023-10-05 17:36:57 -07001603 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1604 }
1605 const auto& devicePort = audioPort->ext.get<AudioPortExt::device>();
1606 if (!devicePort.device.type.connection.empty()) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301607 LOG(ERROR) << __func__ << ": " << mType << ": module implementation must override "
1608 "'populateConnectedDevicePort' "
Mikhail Naganov84bcc042023-10-05 17:36:57 -07001609 << "to handle connection of external devices.";
1610 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
1611 }
Jaideep Sharma559a4912024-03-07 10:05:51 +05301612 LOG(VERBOSE) << __func__ << ": " << mType << ": do nothing and return ok";
jiabin253bd322023-01-25 23:57:31 +00001613 return ndk::ScopedAStatus::ok();
1614}
1615
1616ndk::ScopedAStatus Module::checkAudioPatchEndpointsMatch(
1617 const std::vector<AudioPortConfig*>& sources __unused,
1618 const std::vector<AudioPortConfig*>& sinks __unused) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301619 LOG(VERBOSE) << __func__ << ": " << mType << ": do nothing and return ok";
jiabin253bd322023-01-25 23:57:31 +00001620 return ndk::ScopedAStatus::ok();
1621}
1622
jiabin783c48b2023-02-28 18:28:06 +00001623void Module::onExternalDeviceConnectionChanged(
1624 const ::aidl::android::media::audio::common::AudioPort& audioPort __unused,
1625 bool connected __unused) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301626 LOG(DEBUG) << __func__ << ": " << mType << ": do nothing and return";
jiabin783c48b2023-02-28 18:28:06 +00001627}
1628
jiabindd23b0e2023-12-11 19:10:05 +00001629void Module::onPrepareToDisconnectExternalDevice(
1630 const ::aidl::android::media::audio::common::AudioPort& audioPort __unused) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301631 LOG(DEBUG) << __func__ << ": " << mType << ": do nothing and return";
jiabindd23b0e2023-12-11 19:10:05 +00001632}
1633
jiabin783c48b2023-02-28 18:28:06 +00001634ndk::ScopedAStatus Module::onMasterMuteChanged(bool mute __unused) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301635 LOG(VERBOSE) << __func__ << ": " << mType << ": do nothing and return ok";
jiabin783c48b2023-02-28 18:28:06 +00001636 return ndk::ScopedAStatus::ok();
1637}
1638
1639ndk::ScopedAStatus Module::onMasterVolumeChanged(float volume __unused) {
Jaideep Sharma559a4912024-03-07 10:05:51 +05301640 LOG(VERBOSE) << __func__ << ": " << mType << ": do nothing and return ok";
jiabin783c48b2023-02-28 18:28:06 +00001641 return ndk::ScopedAStatus::ok();
1642}
1643
Lorena Torres-Huerta533cc782023-01-18 00:11:48 +00001644std::vector<MicrophoneInfo> Module::getMicrophoneInfos() {
1645 std::vector<MicrophoneInfo> result;
1646 Configuration& config = getConfig();
1647 for (const AudioPort& port : config.ports) {
1648 if (port.ext.getTag() == AudioPortExt::Tag::device) {
1649 const AudioDeviceType deviceType =
1650 port.ext.get<AudioPortExt::Tag::device>().device.type.type;
1651 if (deviceType == AudioDeviceType::IN_MICROPHONE ||
1652 deviceType == AudioDeviceType::IN_MICROPHONE_BACK) {
1653 // Placeholder values. Vendor implementations must populate MicrophoneInfo
1654 // accordingly based on their physical microphone parameters.
1655 result.push_back(MicrophoneInfo{
1656 .id = port.name,
1657 .device = port.ext.get<AudioPortExt::Tag::device>().device,
1658 .group = 0,
1659 .indexInTheGroup = 0,
1660 });
1661 }
1662 }
1663 }
1664 return result;
1665}
1666
Ram Mohan18f0d512023-07-01 00:47:09 +05301667ndk::ScopedAStatus Module::bluetoothParametersUpdated() {
1668 return mStreams.bluetoothParametersUpdated();
1669}
1670
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001671} // namespace aidl::android::hardware::audio::core