blob: acad70f52dede866a49d2d13765434bb27e76174 [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 Naganovdf5adfd2021-11-11 22:09:22 +000021#include <android-base/logging.h>
Shunkai Yao39bf2c32022-12-06 03:25:59 +000022#include <android/binder_ibinder_platform.h>
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000023
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000024#include <Utils.h>
25#include <aidl/android/media/audio/common/AudioInputFlags.h>
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000026#include <aidl/android/media/audio/common/AudioOutputFlags.h>
27
Mikhail Naganov10c6fe22022-09-30 23:49:17 +000028#include "core-impl/Bluetooth.h"
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000029#include "core-impl/Module.h"
Vlad Popa943b7e22022-12-08 14:24:12 +010030#include "core-impl/SoundDose.h"
Mikhail Naganovf429c032023-01-07 00:24:50 +000031#include "core-impl/StreamStub.h"
Mikhail Naganov3b125b72022-10-05 02:12:39 +000032#include "core-impl/Telephony.h"
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000033#include "core-impl/utils.h"
34
35using aidl::android::hardware::audio::common::SinkMetadata;
36using aidl::android::hardware::audio::common::SourceMetadata;
Vlad Popa2afbd1e2022-12-28 17:04:58 +010037using aidl::android::hardware::audio::core::sounddose::ISoundDose;
Mikhail Naganov6a4872d2022-06-15 21:39:04 +000038using aidl::android::media::audio::common::AudioChannelLayout;
Mikhail Naganovef6bc742022-10-06 00:14:19 +000039using aidl::android::media::audio::common::AudioDevice;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000040using aidl::android::media::audio::common::AudioFormatDescription;
Mikhail Naganov6a4872d2022-06-15 21:39:04 +000041using aidl::android::media::audio::common::AudioFormatType;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000042using aidl::android::media::audio::common::AudioInputFlags;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000043using aidl::android::media::audio::common::AudioIoFlags;
44using aidl::android::media::audio::common::AudioOffloadInfo;
45using aidl::android::media::audio::common::AudioOutputFlags;
46using aidl::android::media::audio::common::AudioPort;
47using aidl::android::media::audio::common::AudioPortConfig;
48using aidl::android::media::audio::common::AudioPortExt;
49using aidl::android::media::audio::common::AudioProfile;
Mikhail Naganov20047bc2023-01-05 20:16:07 +000050using aidl::android::media::audio::common::Boolean;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000051using aidl::android::media::audio::common::Int;
Mikhail Naganov6a4872d2022-06-15 21:39:04 +000052using aidl::android::media::audio::common::PcmType;
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +000053using android::hardware::audio::common::getFrameSizeInBytes;
Mikhail Naganova2c5ddf2022-09-12 22:57:14 +000054using android::hardware::audio::common::isBitPositionFlagSet;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +000055
56namespace aidl::android::hardware::audio::core {
57
58namespace {
59
60bool generateDefaultPortConfig(const AudioPort& port, AudioPortConfig* config) {
61 *config = {};
62 config->portId = port.id;
63 if (port.profiles.empty()) {
64 LOG(ERROR) << __func__ << ": port " << port.id << " has no profiles";
65 return false;
66 }
67 const auto& profile = port.profiles.begin();
68 config->format = profile->format;
69 if (profile->channelMasks.empty()) {
70 LOG(ERROR) << __func__ << ": the first profile in port " << port.id
71 << " has no channel masks";
72 return false;
73 }
74 config->channelMask = *profile->channelMasks.begin();
75 if (profile->sampleRates.empty()) {
76 LOG(ERROR) << __func__ << ": the first profile in port " << port.id
77 << " has no sample rates";
78 return false;
79 }
80 Int sampleRate;
81 sampleRate.value = *profile->sampleRates.begin();
82 config->sampleRate = sampleRate;
83 config->flags = port.flags;
84 config->ext = port.ext;
85 return true;
86}
87
88bool findAudioProfile(const AudioPort& port, const AudioFormatDescription& format,
89 AudioProfile* profile) {
90 if (auto profilesIt =
91 find_if(port.profiles.begin(), port.profiles.end(),
92 [&format](const auto& profile) { return profile.format == format; });
93 profilesIt != port.profiles.end()) {
94 *profile = *profilesIt;
95 return true;
96 }
97 return false;
98}
Mikhail Naganov00603d12022-05-02 22:52:13 +000099
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000100} // namespace
101
102void Module::cleanUpPatch(int32_t patchId) {
103 erase_all_values(mPatches, std::set<int32_t>{patchId});
104}
105
Mikhail Naganov8651b362023-01-06 23:15:27 +0000106ndk::ScopedAStatus Module::createStreamContext(
107 int32_t in_portConfigId, int64_t in_bufferSizeFrames,
108 std::shared_ptr<IStreamCallback> asyncCallback,
109 std::shared_ptr<IStreamOutEventCallback> outEventCallback, StreamContext* out_context) {
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000110 if (in_bufferSizeFrames <= 0) {
111 LOG(ERROR) << __func__ << ": non-positive buffer size " << in_bufferSizeFrames;
112 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
113 }
114 if (in_bufferSizeFrames < kMinimumStreamBufferSizeFrames) {
115 LOG(ERROR) << __func__ << ": insufficient buffer size " << in_bufferSizeFrames
116 << ", must be at least " << kMinimumStreamBufferSizeFrames;
117 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
118 }
119 auto& configs = getConfig().portConfigs;
120 auto portConfigIt = findById<AudioPortConfig>(configs, in_portConfigId);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000121 // Since this is a private method, it is assumed that
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000122 // validity of the portConfigId has already been checked.
123 const size_t frameSize =
124 getFrameSizeInBytes(portConfigIt->format.value(), portConfigIt->channelMask.value());
125 if (frameSize == 0) {
126 LOG(ERROR) << __func__ << ": could not calculate frame size for port config "
127 << portConfigIt->toString();
128 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
129 }
130 LOG(DEBUG) << __func__ << ": frame size " << frameSize << " bytes";
131 if (frameSize > kMaximumStreamBufferSizeBytes / in_bufferSizeFrames) {
132 LOG(ERROR) << __func__ << ": buffer size " << in_bufferSizeFrames
133 << " frames is too large, maximum size is "
134 << kMaximumStreamBufferSizeBytes / frameSize;
135 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
136 }
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000137 const auto& flags = portConfigIt->flags.value();
138 if ((flags.getTag() == AudioIoFlags::Tag::input &&
Mikhail Naganova2c5ddf2022-09-12 22:57:14 +0000139 !isBitPositionFlagSet(flags.get<AudioIoFlags::Tag::input>(),
140 AudioInputFlags::MMAP_NOIRQ)) ||
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000141 (flags.getTag() == AudioIoFlags::Tag::output &&
Mikhail Naganova2c5ddf2022-09-12 22:57:14 +0000142 !isBitPositionFlagSet(flags.get<AudioIoFlags::Tag::output>(),
143 AudioOutputFlags::MMAP_NOIRQ))) {
Mikhail Naganov20047bc2023-01-05 20:16:07 +0000144 StreamContext::DebugParameters params{mDebug.streamTransientStateDelayMs,
Mikhail Naganov194daaa2023-01-05 22:34:20 +0000145 mVendorDebug.forceTransientBurst,
146 mVendorDebug.forceSynchronousDrain};
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000147 StreamContext temp(
148 std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
149 std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000150 portConfigIt->format.value(), portConfigIt->channelMask.value(),
151 std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames),
Mikhail Naganov8651b362023-01-06 23:15:27 +0000152 asyncCallback, outEventCallback, params);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000153 if (temp.isValid()) {
154 *out_context = std::move(temp);
155 } else {
156 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
157 }
158 } else {
159 // TODO: Implement simulation of MMAP buffer allocation
160 }
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000161 return ndk::ScopedAStatus::ok();
162}
163
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000164std::vector<AudioDevice> Module::findConnectedDevices(int32_t portConfigId) {
165 std::vector<AudioDevice> result;
166 auto& ports = getConfig().ports;
167 auto portIds = portIdsFromPortConfigIds(findConnectedPortConfigIds(portConfigId));
168 for (auto it = portIds.begin(); it != portIds.end(); ++it) {
169 auto portIt = findById<AudioPort>(ports, *it);
170 if (portIt != ports.end() && portIt->ext.getTag() == AudioPortExt::Tag::device) {
171 result.push_back(portIt->ext.template get<AudioPortExt::Tag::device>().device);
172 }
173 }
174 return result;
175}
176
177std::set<int32_t> Module::findConnectedPortConfigIds(int32_t portConfigId) {
178 std::set<int32_t> result;
179 auto patchIdsRange = mPatches.equal_range(portConfigId);
180 auto& patches = getConfig().patches;
181 for (auto it = patchIdsRange.first; it != patchIdsRange.second; ++it) {
182 auto patchIt = findById<AudioPatch>(patches, it->second);
183 if (patchIt == patches.end()) {
184 LOG(FATAL) << __func__ << ": patch with id " << it->second << " taken from mPatches "
185 << "not found in the configuration";
186 }
187 if (std::find(patchIt->sourcePortConfigIds.begin(), patchIt->sourcePortConfigIds.end(),
188 portConfigId) != patchIt->sourcePortConfigIds.end()) {
189 result.insert(patchIt->sinkPortConfigIds.begin(), patchIt->sinkPortConfigIds.end());
190 } else {
191 result.insert(patchIt->sourcePortConfigIds.begin(), patchIt->sourcePortConfigIds.end());
192 }
193 }
194 return result;
195}
196
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000197ndk::ScopedAStatus Module::findPortIdForNewStream(int32_t in_portConfigId, AudioPort** port) {
198 auto& configs = getConfig().portConfigs;
199 auto portConfigIt = findById<AudioPortConfig>(configs, in_portConfigId);
200 if (portConfigIt == configs.end()) {
201 LOG(ERROR) << __func__ << ": existing port config id " << in_portConfigId << " not found";
202 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
203 }
204 const int32_t portId = portConfigIt->portId;
205 // In our implementation, configs of mix ports always have unique IDs.
206 CHECK(portId != in_portConfigId);
207 auto& ports = getConfig().ports;
208 auto portIt = findById<AudioPort>(ports, portId);
209 if (portIt == ports.end()) {
210 LOG(ERROR) << __func__ << ": port id " << portId << " used by port config id "
211 << in_portConfigId << " not found";
212 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
213 }
214 if (mStreams.count(in_portConfigId) != 0) {
215 LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
216 << " already has a stream opened on it";
217 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
218 }
219 if (portIt->ext.getTag() != AudioPortExt::Tag::mix) {
220 LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
221 << " does not correspond to a mix port";
222 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
223 }
224 const int32_t maxOpenStreamCount = portIt->ext.get<AudioPortExt::Tag::mix>().maxOpenStreamCount;
225 if (maxOpenStreamCount != 0 && mStreams.count(portId) >= maxOpenStreamCount) {
226 LOG(ERROR) << __func__ << ": port id " << portId
227 << " has already reached maximum allowed opened stream count: "
228 << maxOpenStreamCount;
229 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
230 }
231 *port = &(*portIt);
232 return ndk::ScopedAStatus::ok();
233}
234
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000235template <typename C>
236std::set<int32_t> Module::portIdsFromPortConfigIds(C portConfigIds) {
237 std::set<int32_t> result;
238 auto& portConfigs = getConfig().portConfigs;
239 for (auto it = portConfigIds.begin(); it != portConfigIds.end(); ++it) {
240 auto portConfigIt = findById<AudioPortConfig>(portConfigs, *it);
241 if (portConfigIt != portConfigs.end()) {
242 result.insert(portConfigIt->portId);
243 }
244 }
245 return result;
246}
247
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000248internal::Configuration& Module::getConfig() {
249 if (!mConfig) {
Mikhail Naganovc8e43122022-12-09 00:33:47 +0000250 switch (mType) {
251 case Type::DEFAULT:
252 mConfig = std::move(internal::getPrimaryConfiguration());
253 break;
254 case Type::R_SUBMIX:
255 mConfig = std::move(internal::getRSubmixConfiguration());
256 break;
257 }
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000258 }
259 return *mConfig;
260}
261
262void Module::registerPatch(const AudioPatch& patch) {
263 auto& configs = getConfig().portConfigs;
264 auto do_insert = [&](const std::vector<int32_t>& portConfigIds) {
265 for (auto portConfigId : portConfigIds) {
266 auto configIt = findById<AudioPortConfig>(configs, portConfigId);
267 if (configIt != configs.end()) {
268 mPatches.insert(std::pair{portConfigId, patch.id});
269 if (configIt->portId != portConfigId) {
270 mPatches.insert(std::pair{configIt->portId, patch.id});
271 }
272 }
273 };
274 };
275 do_insert(patch.sourcePortConfigIds);
276 do_insert(patch.sinkPortConfigIds);
277}
278
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000279void Module::updateStreamsConnectedState(const AudioPatch& oldPatch, const AudioPatch& newPatch) {
280 // Streams from the old patch need to be disconnected, streams from the new
281 // patch need to be connected. If the stream belongs to both patches, no need
282 // to update it.
283 std::set<int32_t> idsToDisconnect, idsToConnect;
284 idsToDisconnect.insert(oldPatch.sourcePortConfigIds.begin(),
285 oldPatch.sourcePortConfigIds.end());
286 idsToDisconnect.insert(oldPatch.sinkPortConfigIds.begin(), oldPatch.sinkPortConfigIds.end());
287 idsToConnect.insert(newPatch.sourcePortConfigIds.begin(), newPatch.sourcePortConfigIds.end());
288 idsToConnect.insert(newPatch.sinkPortConfigIds.begin(), newPatch.sinkPortConfigIds.end());
289 std::for_each(idsToDisconnect.begin(), idsToDisconnect.end(), [&](const auto& portConfigId) {
290 if (idsToConnect.count(portConfigId) == 0) {
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000291 LOG(DEBUG) << "The stream on port config id " << portConfigId << " is not connected";
292 mStreams.setStreamIsConnected(portConfigId, {});
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000293 }
294 });
295 std::for_each(idsToConnect.begin(), idsToConnect.end(), [&](const auto& portConfigId) {
296 if (idsToDisconnect.count(portConfigId) == 0) {
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000297 const auto connectedDevices = findConnectedDevices(portConfigId);
298 LOG(DEBUG) << "The stream on port config id " << portConfigId
299 << " is connected to: " << ::android::internal::ToString(connectedDevices);
300 mStreams.setStreamIsConnected(portConfigId, connectedDevices);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000301 }
302 });
303}
304
Mikhail Naganov00603d12022-05-02 22:52:13 +0000305ndk::ScopedAStatus Module::setModuleDebug(
306 const ::aidl::android::hardware::audio::core::ModuleDebug& in_debug) {
307 LOG(DEBUG) << __func__ << ": old flags:" << mDebug.toString()
308 << ", new flags: " << in_debug.toString();
309 if (mDebug.simulateDeviceConnections != in_debug.simulateDeviceConnections &&
310 !mConnectedDevicePorts.empty()) {
311 LOG(ERROR) << __func__ << ": attempting to change device connections simulation "
312 << "while having external devices connected";
313 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
314 }
Mikhail Naganovbd483c02022-11-17 20:33:39 +0000315 if (in_debug.streamTransientStateDelayMs < 0) {
316 LOG(ERROR) << __func__ << ": streamTransientStateDelayMs is negative: "
317 << in_debug.streamTransientStateDelayMs;
318 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
319 }
Mikhail Naganov00603d12022-05-02 22:52:13 +0000320 mDebug = in_debug;
321 return ndk::ScopedAStatus::ok();
322}
323
Mikhail Naganov3b125b72022-10-05 02:12:39 +0000324ndk::ScopedAStatus Module::getTelephony(std::shared_ptr<ITelephony>* _aidl_return) {
325 if (mTelephony == nullptr) {
326 mTelephony = ndk::SharedRefBase::make<Telephony>();
Mikhail Naganovdf5feba2022-12-15 00:11:14 +0000327 mTelephonyBinder = mTelephony->asBinder();
328 AIBinder_setMinSchedulerPolicy(mTelephonyBinder.get(), SCHED_NORMAL,
Shunkai Yao39bf2c32022-12-06 03:25:59 +0000329 ANDROID_PRIORITY_AUDIO);
Mikhail Naganov3b125b72022-10-05 02:12:39 +0000330 }
331 *_aidl_return = mTelephony;
332 LOG(DEBUG) << __func__ << ": returning instance of ITelephony: " << _aidl_return->get();
333 return ndk::ScopedAStatus::ok();
334}
335
Mikhail Naganov10c6fe22022-09-30 23:49:17 +0000336ndk::ScopedAStatus Module::getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) {
337 if (mBluetooth == nullptr) {
338 mBluetooth = ndk::SharedRefBase::make<Bluetooth>();
339 mBluetoothBinder = mBluetooth->asBinder();
340 AIBinder_setMinSchedulerPolicy(mBluetoothBinder.get(), SCHED_NORMAL,
341 ANDROID_PRIORITY_AUDIO);
342 }
343 *_aidl_return = mBluetooth;
344 LOG(DEBUG) << __func__ << ": returning instance of IBluetooth: " << _aidl_return->get();
345 return ndk::ScopedAStatus::ok();
346}
347
Mikhail Naganov00603d12022-05-02 22:52:13 +0000348ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdAndAdditionalData,
349 AudioPort* _aidl_return) {
350 const int32_t templateId = in_templateIdAndAdditionalData.id;
351 auto& ports = getConfig().ports;
352 AudioPort connectedPort;
353 { // Scope the template port so that we don't accidentally modify it.
354 auto templateIt = findById<AudioPort>(ports, templateId);
355 if (templateIt == ports.end()) {
356 LOG(ERROR) << __func__ << ": port id " << templateId << " not found";
357 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
358 }
359 if (templateIt->ext.getTag() != AudioPortExt::Tag::device) {
360 LOG(ERROR) << __func__ << ": port id " << templateId << " is not a device port";
361 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
362 }
363 if (!templateIt->profiles.empty()) {
364 LOG(ERROR) << __func__ << ": port id " << templateId
365 << " does not have dynamic profiles";
366 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
367 }
368 auto& templateDevicePort = templateIt->ext.get<AudioPortExt::Tag::device>();
369 if (templateDevicePort.device.type.connection.empty()) {
370 LOG(ERROR) << __func__ << ": port id " << templateId << " is permanently attached";
371 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
372 }
373 // Postpone id allocation until we ensure that there are no client errors.
374 connectedPort = *templateIt;
375 connectedPort.extraAudioDescriptors = in_templateIdAndAdditionalData.extraAudioDescriptors;
376 const auto& inputDevicePort =
377 in_templateIdAndAdditionalData.ext.get<AudioPortExt::Tag::device>();
378 auto& connectedDevicePort = connectedPort.ext.get<AudioPortExt::Tag::device>();
379 connectedDevicePort.device.address = inputDevicePort.device.address;
380 LOG(DEBUG) << __func__ << ": device port " << connectedPort.id << " device set to "
381 << connectedDevicePort.device.toString();
382 // Check if there is already a connected port with for the same external device.
383 for (auto connectedPortId : mConnectedDevicePorts) {
384 auto connectedPortIt = findById<AudioPort>(ports, connectedPortId);
385 if (connectedPortIt->ext.get<AudioPortExt::Tag::device>().device ==
386 connectedDevicePort.device) {
387 LOG(ERROR) << __func__ << ": device " << connectedDevicePort.device.toString()
388 << " is already connected at the device port id " << connectedPortId;
389 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
390 }
391 }
392 }
393
394 if (!mDebug.simulateDeviceConnections) {
395 // In a real HAL here we would attempt querying the profiles from the device.
396 LOG(ERROR) << __func__ << ": failed to query supported device profiles";
397 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
398 }
399
400 connectedPort.id = ++getConfig().nextPortId;
401 mConnectedDevicePorts.insert(connectedPort.id);
402 LOG(DEBUG) << __func__ << ": template port " << templateId << " external device connected, "
403 << "connected port ID " << connectedPort.id;
404 auto& connectedProfiles = getConfig().connectedProfiles;
405 if (auto connectedProfilesIt = connectedProfiles.find(templateId);
406 connectedProfilesIt != connectedProfiles.end()) {
407 connectedPort.profiles = connectedProfilesIt->second;
408 }
409 ports.push_back(connectedPort);
410 *_aidl_return = std::move(connectedPort);
411
412 std::vector<AudioRoute> newRoutes;
413 auto& routes = getConfig().routes;
414 for (auto& r : routes) {
415 if (r.sinkPortId == templateId) {
416 AudioRoute newRoute;
417 newRoute.sourcePortIds = r.sourcePortIds;
418 newRoute.sinkPortId = connectedPort.id;
419 newRoute.isExclusive = r.isExclusive;
420 newRoutes.push_back(std::move(newRoute));
421 } else {
422 auto& srcs = r.sourcePortIds;
423 if (std::find(srcs.begin(), srcs.end(), templateId) != srcs.end()) {
424 srcs.push_back(connectedPort.id);
425 }
426 }
427 }
428 routes.insert(routes.end(), newRoutes.begin(), newRoutes.end());
429
430 return ndk::ScopedAStatus::ok();
431}
432
433ndk::ScopedAStatus Module::disconnectExternalDevice(int32_t in_portId) {
434 auto& ports = getConfig().ports;
435 auto portIt = findById<AudioPort>(ports, in_portId);
436 if (portIt == ports.end()) {
437 LOG(ERROR) << __func__ << ": port id " << in_portId << " not found";
438 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
439 }
440 if (portIt->ext.getTag() != AudioPortExt::Tag::device) {
441 LOG(ERROR) << __func__ << ": port id " << in_portId << " is not a device port";
442 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
443 }
444 if (mConnectedDevicePorts.count(in_portId) == 0) {
445 LOG(ERROR) << __func__ << ": port id " << in_portId << " is not a connected device port";
446 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
447 }
448 auto& configs = getConfig().portConfigs;
449 auto& initials = getConfig().initialConfigs;
450 auto configIt = std::find_if(configs.begin(), configs.end(), [&](const auto& config) {
451 if (config.portId == in_portId) {
452 // Check if the configuration was provided by the client.
453 const auto& initialIt = findById<AudioPortConfig>(initials, config.id);
454 return initialIt == initials.end() || config != *initialIt;
455 }
456 return false;
457 });
458 if (configIt != configs.end()) {
459 LOG(ERROR) << __func__ << ": port id " << in_portId << " has a non-default config with id "
460 << configIt->id;
461 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
462 }
463 ports.erase(portIt);
464 mConnectedDevicePorts.erase(in_portId);
465 LOG(DEBUG) << __func__ << ": connected device port " << in_portId << " released";
466
467 auto& routes = getConfig().routes;
468 for (auto routesIt = routes.begin(); routesIt != routes.end();) {
469 if (routesIt->sinkPortId == in_portId) {
470 routesIt = routes.erase(routesIt);
471 } else {
472 // Note: the list of sourcePortIds can't become empty because there must
473 // be the id of the template port in the route.
474 erase_if(routesIt->sourcePortIds, [in_portId](auto src) { return src == in_portId; });
475 ++routesIt;
476 }
477 }
478
479 return ndk::ScopedAStatus::ok();
480}
481
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000482ndk::ScopedAStatus Module::getAudioPatches(std::vector<AudioPatch>* _aidl_return) {
483 *_aidl_return = getConfig().patches;
484 LOG(DEBUG) << __func__ << ": returning " << _aidl_return->size() << " patches";
485 return ndk::ScopedAStatus::ok();
486}
487
488ndk::ScopedAStatus Module::getAudioPort(int32_t in_portId, AudioPort* _aidl_return) {
489 auto& ports = getConfig().ports;
490 auto portIt = findById<AudioPort>(ports, in_portId);
491 if (portIt != ports.end()) {
492 *_aidl_return = *portIt;
493 LOG(DEBUG) << __func__ << ": returning port by id " << in_portId;
494 return ndk::ScopedAStatus::ok();
495 }
496 LOG(ERROR) << __func__ << ": port id " << in_portId << " not found";
497 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
498}
499
500ndk::ScopedAStatus Module::getAudioPortConfigs(std::vector<AudioPortConfig>* _aidl_return) {
501 *_aidl_return = getConfig().portConfigs;
502 LOG(DEBUG) << __func__ << ": returning " << _aidl_return->size() << " port configs";
503 return ndk::ScopedAStatus::ok();
504}
505
506ndk::ScopedAStatus Module::getAudioPorts(std::vector<AudioPort>* _aidl_return) {
507 *_aidl_return = getConfig().ports;
508 LOG(DEBUG) << __func__ << ": returning " << _aidl_return->size() << " ports";
509 return ndk::ScopedAStatus::ok();
510}
511
512ndk::ScopedAStatus Module::getAudioRoutes(std::vector<AudioRoute>* _aidl_return) {
513 *_aidl_return = getConfig().routes;
514 LOG(DEBUG) << __func__ << ": returning " << _aidl_return->size() << " routes";
515 return ndk::ScopedAStatus::ok();
516}
517
Mikhail Naganov00603d12022-05-02 22:52:13 +0000518ndk::ScopedAStatus Module::getAudioRoutesForAudioPort(int32_t in_portId,
519 std::vector<AudioRoute>* _aidl_return) {
520 auto& ports = getConfig().ports;
521 if (auto portIt = findById<AudioPort>(ports, in_portId); portIt == ports.end()) {
522 LOG(ERROR) << __func__ << ": port id " << in_portId << " not found";
523 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
524 }
525 auto& routes = getConfig().routes;
526 std::copy_if(routes.begin(), routes.end(), std::back_inserter(*_aidl_return),
527 [&](const auto& r) {
528 const auto& srcs = r.sourcePortIds;
529 return r.sinkPortId == in_portId ||
530 std::find(srcs.begin(), srcs.end(), in_portId) != srcs.end();
531 });
532 return ndk::ScopedAStatus::ok();
533}
534
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000535ndk::ScopedAStatus Module::openInputStream(const OpenInputStreamArguments& in_args,
536 OpenInputStreamReturn* _aidl_return) {
537 LOG(DEBUG) << __func__ << ": port config id " << in_args.portConfigId << ", buffer size "
538 << in_args.bufferSizeFrames << " frames";
539 AudioPort* port = nullptr;
540 if (auto status = findPortIdForNewStream(in_args.portConfigId, &port); !status.isOk()) {
541 return status;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000542 }
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000543 if (port->flags.getTag() != AudioIoFlags::Tag::input) {
544 LOG(ERROR) << __func__ << ": port config id " << in_args.portConfigId
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000545 << " does not correspond to an input mix port";
546 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
547 }
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000548 StreamContext context;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000549 if (auto status = createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames, nullptr,
Mikhail Naganov8651b362023-01-06 23:15:27 +0000550 nullptr, &context);
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000551 !status.isOk()) {
552 return status;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000553 }
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000554 context.fillDescriptor(&_aidl_return->desc);
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000555 std::shared_ptr<StreamIn> stream;
Mikhail Naganovf429c032023-01-07 00:24:50 +0000556 // TODO: Add a mapping from module instance names to a corresponding 'createInstance'.
557 if (auto status = StreamInStub::createInstance(in_args.sinkMetadata, std::move(context),
558 mConfig->microphones, &stream);
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000559 !status.isOk()) {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000560 return status;
561 }
562 StreamWrapper streamWrapper(stream);
Mikhail Naganovdf5feba2022-12-15 00:11:14 +0000563 AIBinder_setMinSchedulerPolicy(streamWrapper.getBinder().get(), SCHED_NORMAL,
564 ANDROID_PRIORITY_AUDIO);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000565 auto patchIt = mPatches.find(in_args.portConfigId);
566 if (patchIt != mPatches.end()) {
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000567 streamWrapper.setStreamIsConnected(findConnectedDevices(in_args.portConfigId));
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000568 }
569 mStreams.insert(port->id, in_args.portConfigId, std::move(streamWrapper));
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000570 _aidl_return->stream = std::move(stream);
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000571 return ndk::ScopedAStatus::ok();
572}
573
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000574ndk::ScopedAStatus Module::openOutputStream(const OpenOutputStreamArguments& in_args,
575 OpenOutputStreamReturn* _aidl_return) {
576 LOG(DEBUG) << __func__ << ": port config id " << in_args.portConfigId << ", has offload info? "
577 << (in_args.offloadInfo.has_value()) << ", buffer size " << in_args.bufferSizeFrames
578 << " frames";
579 AudioPort* port = nullptr;
580 if (auto status = findPortIdForNewStream(in_args.portConfigId, &port); !status.isOk()) {
581 return status;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000582 }
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000583 if (port->flags.getTag() != AudioIoFlags::Tag::output) {
584 LOG(ERROR) << __func__ << ": port config id " << in_args.portConfigId
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000585 << " does not correspond to an output mix port";
586 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
587 }
Mikhail Naganova2c5ddf2022-09-12 22:57:14 +0000588 const bool isOffload = isBitPositionFlagSet(port->flags.get<AudioIoFlags::Tag::output>(),
589 AudioOutputFlags::COMPRESS_OFFLOAD);
590 if (isOffload && !in_args.offloadInfo.has_value()) {
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000591 LOG(ERROR) << __func__ << ": port id " << port->id
Mikhail Naganov111e0ce2022-06-17 21:41:19 +0000592 << " has COMPRESS_OFFLOAD flag set, requires offload info";
593 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
594 }
Mikhail Naganov30301a42022-09-13 01:20:45 +0000595 const bool isNonBlocking = isBitPositionFlagSet(port->flags.get<AudioIoFlags::Tag::output>(),
596 AudioOutputFlags::NON_BLOCKING);
597 if (isNonBlocking && in_args.callback == nullptr) {
598 LOG(ERROR) << __func__ << ": port id " << port->id
599 << " has NON_BLOCKING flag set, requires async callback";
600 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
601 }
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000602 StreamContext context;
Mikhail Naganov30301a42022-09-13 01:20:45 +0000603 if (auto status = createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames,
Mikhail Naganov8651b362023-01-06 23:15:27 +0000604 isNonBlocking ? in_args.callback : nullptr,
605 in_args.eventCallback, &context);
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000606 !status.isOk()) {
607 return status;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000608 }
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000609 context.fillDescriptor(&_aidl_return->desc);
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000610 std::shared_ptr<StreamOut> stream;
Mikhail Naganovf429c032023-01-07 00:24:50 +0000611 // TODO: Add a mapping from module instance names to a corresponding 'createInstance'.
612 if (auto status = StreamOutStub::createInstance(in_args.sourceMetadata, std::move(context),
613 in_args.offloadInfo, &stream);
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000614 !status.isOk()) {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000615 return status;
616 }
617 StreamWrapper streamWrapper(stream);
Mikhail Naganovdf5feba2022-12-15 00:11:14 +0000618 AIBinder_setMinSchedulerPolicy(streamWrapper.getBinder().get(), SCHED_NORMAL,
619 ANDROID_PRIORITY_AUDIO);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000620 auto patchIt = mPatches.find(in_args.portConfigId);
621 if (patchIt != mPatches.end()) {
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000622 streamWrapper.setStreamIsConnected(findConnectedDevices(in_args.portConfigId));
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000623 }
624 mStreams.insert(port->id, in_args.portConfigId, std::move(streamWrapper));
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000625 _aidl_return->stream = std::move(stream);
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000626 return ndk::ScopedAStatus::ok();
627}
628
Mikhail Naganov74927202022-12-19 16:37:14 +0000629ndk::ScopedAStatus Module::getSupportedPlaybackRateFactors(
630 SupportedPlaybackRateFactors* _aidl_return) {
631 LOG(DEBUG) << __func__;
632 (void)_aidl_return;
633 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
634}
635
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000636ndk::ScopedAStatus Module::setAudioPatch(const AudioPatch& in_requested, AudioPatch* _aidl_return) {
Mikhail Naganov16db9b72022-06-17 21:36:18 +0000637 LOG(DEBUG) << __func__ << ": requested patch " << in_requested.toString();
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000638 if (in_requested.sourcePortConfigIds.empty()) {
639 LOG(ERROR) << __func__ << ": requested patch has empty sources list";
640 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
641 }
642 if (!all_unique<int32_t>(in_requested.sourcePortConfigIds)) {
643 LOG(ERROR) << __func__ << ": requested patch has duplicate ids in the sources list";
644 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
645 }
646 if (in_requested.sinkPortConfigIds.empty()) {
647 LOG(ERROR) << __func__ << ": requested patch has empty sinks list";
648 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
649 }
650 if (!all_unique<int32_t>(in_requested.sinkPortConfigIds)) {
651 LOG(ERROR) << __func__ << ": requested patch has duplicate ids in the sinks list";
652 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
653 }
654
655 auto& configs = getConfig().portConfigs;
656 std::vector<int32_t> missingIds;
657 auto sources =
658 selectByIds<AudioPortConfig>(configs, in_requested.sourcePortConfigIds, &missingIds);
659 if (!missingIds.empty()) {
660 LOG(ERROR) << __func__ << ": following source port config ids not found: "
661 << ::android::internal::ToString(missingIds);
662 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
663 }
664 auto sinks = selectByIds<AudioPortConfig>(configs, in_requested.sinkPortConfigIds, &missingIds);
665 if (!missingIds.empty()) {
666 LOG(ERROR) << __func__ << ": following sink port config ids not found: "
667 << ::android::internal::ToString(missingIds);
668 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
669 }
670 // bool indicates whether a non-exclusive route is available.
671 // If only an exclusive route is available, that means the patch can not be
672 // established if there is any other patch which currently uses the sink port.
673 std::map<int32_t, bool> allowedSinkPorts;
674 auto& routes = getConfig().routes;
675 for (auto src : sources) {
676 for (const auto& r : routes) {
677 const auto& srcs = r.sourcePortIds;
678 if (std::find(srcs.begin(), srcs.end(), src->portId) != srcs.end()) {
679 if (!allowedSinkPorts[r.sinkPortId]) { // prefer non-exclusive
680 allowedSinkPorts[r.sinkPortId] = !r.isExclusive;
681 }
682 }
683 }
684 }
685 for (auto sink : sinks) {
686 if (allowedSinkPorts.count(sink->portId) == 0) {
687 LOG(ERROR) << __func__ << ": there is no route to the sink port id " << sink->portId;
688 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
689 }
690 }
691
692 auto& patches = getConfig().patches;
693 auto existing = patches.end();
694 std::optional<decltype(mPatches)> patchesBackup;
695 if (in_requested.id != 0) {
696 existing = findById<AudioPatch>(patches, in_requested.id);
697 if (existing != patches.end()) {
698 patchesBackup = mPatches;
699 cleanUpPatch(existing->id);
700 } else {
701 LOG(ERROR) << __func__ << ": not found existing patch id " << in_requested.id;
702 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
703 }
704 }
705 // Validate the requested patch.
706 for (const auto& [sinkPortId, nonExclusive] : allowedSinkPorts) {
707 if (!nonExclusive && mPatches.count(sinkPortId) != 0) {
708 LOG(ERROR) << __func__ << ": sink port id " << sinkPortId
709 << "is exclusive and is already used by some other patch";
710 if (patchesBackup.has_value()) {
711 mPatches = std::move(*patchesBackup);
712 }
713 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
714 }
715 }
716 *_aidl_return = in_requested;
Mikhail Naganov6a4872d2022-06-15 21:39:04 +0000717 _aidl_return->minimumStreamBufferSizeFrames = kMinimumStreamBufferSizeFrames;
718 _aidl_return->latenciesMs.clear();
719 _aidl_return->latenciesMs.insert(_aidl_return->latenciesMs.end(),
720 _aidl_return->sinkPortConfigIds.size(), kLatencyMs);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000721 AudioPatch oldPatch{};
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000722 if (existing == patches.end()) {
723 _aidl_return->id = getConfig().nextPatchId++;
724 patches.push_back(*_aidl_return);
725 existing = patches.begin() + (patches.size() - 1);
726 } else {
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000727 oldPatch = *existing;
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000728 *existing = *_aidl_return;
729 }
730 registerPatch(*existing);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000731 updateStreamsConnectedState(oldPatch, *_aidl_return);
732
733 LOG(DEBUG) << __func__ << ": " << (oldPatch.id == 0 ? "created" : "updated") << " patch "
734 << _aidl_return->toString();
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000735 return ndk::ScopedAStatus::ok();
736}
737
738ndk::ScopedAStatus Module::setAudioPortConfig(const AudioPortConfig& in_requested,
739 AudioPortConfig* out_suggested, bool* _aidl_return) {
740 LOG(DEBUG) << __func__ << ": requested " << in_requested.toString();
741 auto& configs = getConfig().portConfigs;
742 auto existing = configs.end();
743 if (in_requested.id != 0) {
744 if (existing = findById<AudioPortConfig>(configs, in_requested.id);
745 existing == configs.end()) {
746 LOG(ERROR) << __func__ << ": existing port config id " << in_requested.id
747 << " not found";
748 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
749 }
750 }
751
752 const int portId = existing != configs.end() ? existing->portId : in_requested.portId;
753 if (portId == 0) {
754 LOG(ERROR) << __func__ << ": input port config does not specify portId";
755 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
756 }
757 auto& ports = getConfig().ports;
758 auto portIt = findById<AudioPort>(ports, portId);
759 if (portIt == ports.end()) {
760 LOG(ERROR) << __func__ << ": input port config points to non-existent portId " << portId;
761 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
762 }
763 if (existing != configs.end()) {
764 *out_suggested = *existing;
765 } else {
766 AudioPortConfig newConfig;
767 if (generateDefaultPortConfig(*portIt, &newConfig)) {
768 *out_suggested = newConfig;
769 } else {
770 LOG(ERROR) << __func__ << ": unable generate a default config for port " << portId;
771 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
772 }
773 }
774 // From this moment, 'out_suggested' is either an existing port config,
775 // or a new generated config. Now attempt to update it according to the specified
776 // fields of 'in_requested'.
777
778 bool requestedIsValid = true, requestedIsFullySpecified = true;
779
780 AudioIoFlags portFlags = portIt->flags;
781 if (in_requested.flags.has_value()) {
782 if (in_requested.flags.value() != portFlags) {
783 LOG(WARNING) << __func__ << ": requested flags "
784 << in_requested.flags.value().toString() << " do not match port's "
785 << portId << " flags " << portFlags.toString();
786 requestedIsValid = false;
787 }
788 } else {
789 requestedIsFullySpecified = false;
790 }
791
792 AudioProfile portProfile;
793 if (in_requested.format.has_value()) {
794 const auto& format = in_requested.format.value();
795 if (findAudioProfile(*portIt, format, &portProfile)) {
796 out_suggested->format = format;
797 } else {
798 LOG(WARNING) << __func__ << ": requested format " << format.toString()
799 << " is not found in port's " << portId << " profiles";
800 requestedIsValid = false;
801 }
802 } else {
803 requestedIsFullySpecified = false;
804 }
805 if (!findAudioProfile(*portIt, out_suggested->format.value(), &portProfile)) {
806 LOG(ERROR) << __func__ << ": port " << portId << " does not support format "
807 << out_suggested->format.value().toString() << " anymore";
808 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
809 }
810
811 if (in_requested.channelMask.has_value()) {
812 const auto& channelMask = in_requested.channelMask.value();
813 if (find(portProfile.channelMasks.begin(), portProfile.channelMasks.end(), channelMask) !=
814 portProfile.channelMasks.end()) {
815 out_suggested->channelMask = channelMask;
816 } else {
817 LOG(WARNING) << __func__ << ": requested channel mask " << channelMask.toString()
818 << " is not supported for the format " << portProfile.format.toString()
819 << " by the port " << portId;
820 requestedIsValid = false;
821 }
822 } else {
823 requestedIsFullySpecified = false;
824 }
825
826 if (in_requested.sampleRate.has_value()) {
827 const auto& sampleRate = in_requested.sampleRate.value();
828 if (find(portProfile.sampleRates.begin(), portProfile.sampleRates.end(),
829 sampleRate.value) != portProfile.sampleRates.end()) {
830 out_suggested->sampleRate = sampleRate;
831 } else {
832 LOG(WARNING) << __func__ << ": requested sample rate " << sampleRate.value
833 << " is not supported for the format " << portProfile.format.toString()
834 << " by the port " << portId;
835 requestedIsValid = false;
836 }
837 } else {
838 requestedIsFullySpecified = false;
839 }
840
841 if (in_requested.gain.has_value()) {
842 // Let's pretend that gain can always be applied.
843 out_suggested->gain = in_requested.gain.value();
844 }
845
846 if (existing == configs.end() && requestedIsValid && requestedIsFullySpecified) {
847 out_suggested->id = getConfig().nextPortId++;
848 configs.push_back(*out_suggested);
849 *_aidl_return = true;
850 LOG(DEBUG) << __func__ << ": created new port config " << out_suggested->toString();
851 } else if (existing != configs.end() && requestedIsValid) {
852 *existing = *out_suggested;
853 *_aidl_return = true;
854 LOG(DEBUG) << __func__ << ": updated port config " << out_suggested->toString();
855 } else {
856 LOG(DEBUG) << __func__ << ": not applied; existing config ? " << (existing != configs.end())
857 << "; requested is valid? " << requestedIsValid << ", fully specified? "
858 << requestedIsFullySpecified;
859 *_aidl_return = false;
860 }
861 return ndk::ScopedAStatus::ok();
862}
863
864ndk::ScopedAStatus Module::resetAudioPatch(int32_t in_patchId) {
865 auto& patches = getConfig().patches;
866 auto patchIt = findById<AudioPatch>(patches, in_patchId);
867 if (patchIt != patches.end()) {
868 cleanUpPatch(patchIt->id);
Mikhail Naganov4f5d3f12022-07-22 23:23:25 +0000869 updateStreamsConnectedState(*patchIt, AudioPatch{});
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +0000870 patches.erase(patchIt);
871 LOG(DEBUG) << __func__ << ": erased patch " << in_patchId;
872 return ndk::ScopedAStatus::ok();
873 }
874 LOG(ERROR) << __func__ << ": patch id " << in_patchId << " not found";
875 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
876}
877
878ndk::ScopedAStatus Module::resetAudioPortConfig(int32_t in_portConfigId) {
879 auto& configs = getConfig().portConfigs;
880 auto configIt = findById<AudioPortConfig>(configs, in_portConfigId);
881 if (configIt != configs.end()) {
882 if (mStreams.count(in_portConfigId) != 0) {
883 LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
884 << " has a stream opened on it";
885 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
886 }
887 auto patchIt = mPatches.find(in_portConfigId);
888 if (patchIt != mPatches.end()) {
889 LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
890 << " is used by the patch with id " << patchIt->second;
891 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
892 }
893 auto& initials = getConfig().initialConfigs;
894 auto initialIt = findById<AudioPortConfig>(initials, in_portConfigId);
895 if (initialIt == initials.end()) {
896 configs.erase(configIt);
897 LOG(DEBUG) << __func__ << ": erased port config " << in_portConfigId;
898 } else if (*configIt != *initialIt) {
899 *configIt = *initialIt;
900 LOG(DEBUG) << __func__ << ": reset port config " << in_portConfigId;
901 }
902 return ndk::ScopedAStatus::ok();
903 }
904 LOG(ERROR) << __func__ << ": port config id " << in_portConfigId << " not found";
905 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
906}
907
Mikhail Naganov3b125b72022-10-05 02:12:39 +0000908ndk::ScopedAStatus Module::getMasterMute(bool* _aidl_return) {
909 *_aidl_return = mMasterMute;
910 LOG(DEBUG) << __func__ << ": returning " << *_aidl_return;
911 return ndk::ScopedAStatus::ok();
912}
913
914ndk::ScopedAStatus Module::setMasterMute(bool in_mute) {
915 LOG(DEBUG) << __func__ << ": " << in_mute;
916 mMasterMute = in_mute;
917 return ndk::ScopedAStatus::ok();
918}
919
920ndk::ScopedAStatus Module::getMasterVolume(float* _aidl_return) {
921 *_aidl_return = mMasterVolume;
922 LOG(DEBUG) << __func__ << ": returning " << *_aidl_return;
923 return ndk::ScopedAStatus::ok();
924}
925
926ndk::ScopedAStatus Module::setMasterVolume(float in_volume) {
927 LOG(DEBUG) << __func__ << ": " << in_volume;
928 if (in_volume >= 0.0f && in_volume <= 1.0f) {
929 mMasterVolume = in_volume;
930 return ndk::ScopedAStatus::ok();
931 }
932 LOG(ERROR) << __func__ << ": invalid master volume value: " << in_volume;
933 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
934}
935
936ndk::ScopedAStatus Module::getMicMute(bool* _aidl_return) {
937 *_aidl_return = mMicMute;
938 LOG(DEBUG) << __func__ << ": returning " << *_aidl_return;
939 return ndk::ScopedAStatus::ok();
940}
941
942ndk::ScopedAStatus Module::setMicMute(bool in_mute) {
943 LOG(DEBUG) << __func__ << ": " << in_mute;
944 mMicMute = in_mute;
945 return ndk::ScopedAStatus::ok();
946}
947
Mikhail Naganovef6bc742022-10-06 00:14:19 +0000948ndk::ScopedAStatus Module::getMicrophones(std::vector<MicrophoneInfo>* _aidl_return) {
949 *_aidl_return = mConfig->microphones;
950 LOG(DEBUG) << __func__ << ": returning " << ::android::internal::ToString(*_aidl_return);
951 return ndk::ScopedAStatus::ok();
952}
953
Mikhail Naganov3b125b72022-10-05 02:12:39 +0000954ndk::ScopedAStatus Module::updateAudioMode(AudioMode in_mode) {
955 // No checks for supported audio modes here, it's an informative notification.
956 LOG(DEBUG) << __func__ << ": " << toString(in_mode);
957 return ndk::ScopedAStatus::ok();
958}
959
960ndk::ScopedAStatus Module::updateScreenRotation(ScreenRotation in_rotation) {
961 LOG(DEBUG) << __func__ << ": " << toString(in_rotation);
962 return ndk::ScopedAStatus::ok();
963}
964
965ndk::ScopedAStatus Module::updateScreenState(bool in_isTurnedOn) {
966 LOG(DEBUG) << __func__ << ": " << in_isTurnedOn;
967 return ndk::ScopedAStatus::ok();
968}
969
Vlad Popa83a6d822022-11-07 13:53:57 +0100970ndk::ScopedAStatus Module::getSoundDose(std::shared_ptr<ISoundDose>* _aidl_return) {
Vlad Popa943b7e22022-12-08 14:24:12 +0100971 if (mSoundDose == nullptr) {
Vlad Popa2afbd1e2022-12-28 17:04:58 +0100972 mSoundDose = ndk::SharedRefBase::make<sounddose::SoundDose>();
Mikhail Naganovdf5feba2022-12-15 00:11:14 +0000973 mSoundDoseBinder = mSoundDose->asBinder();
974 AIBinder_setMinSchedulerPolicy(mSoundDoseBinder.get(), SCHED_NORMAL,
975 ANDROID_PRIORITY_AUDIO);
Vlad Popa943b7e22022-12-08 14:24:12 +0100976 }
977 *_aidl_return = mSoundDose;
978 LOG(DEBUG) << __func__ << ": returning instance of ISoundDose: " << _aidl_return->get();
Vlad Popa83a6d822022-11-07 13:53:57 +0100979 return ndk::ScopedAStatus::ok();
980}
981
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000982ndk::ScopedAStatus Module::generateHwAvSyncId(int32_t* _aidl_return) {
983 LOG(DEBUG) << __func__;
984 (void)_aidl_return;
985 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
986}
987
Mikhail Naganov20047bc2023-01-05 20:16:07 +0000988const std::string Module::VendorDebug::kForceTransientBurstName = "aosp.forceTransientBurst";
Mikhail Naganov194daaa2023-01-05 22:34:20 +0000989const std::string Module::VendorDebug::kForceSynchronousDrainName = "aosp.forceSynchronousDrain";
Mikhail Naganov20047bc2023-01-05 20:16:07 +0000990
Mikhail Naganove9f10fc2022-10-14 23:31:52 +0000991ndk::ScopedAStatus Module::getVendorParameters(const std::vector<std::string>& in_ids,
992 std::vector<VendorParameter>* _aidl_return) {
993 LOG(DEBUG) << __func__ << ": id count: " << in_ids.size();
Mikhail Naganov20047bc2023-01-05 20:16:07 +0000994 bool allParametersKnown = true;
995 for (const auto& id : in_ids) {
996 if (id == VendorDebug::kForceTransientBurstName) {
997 VendorParameter forceTransientBurst{.id = id};
998 forceTransientBurst.ext.setParcelable(Boolean{mVendorDebug.forceTransientBurst});
999 _aidl_return->push_back(std::move(forceTransientBurst));
Mikhail Naganov194daaa2023-01-05 22:34:20 +00001000 } else if (id == VendorDebug::kForceSynchronousDrainName) {
1001 VendorParameter forceSynchronousDrain{.id = id};
1002 forceSynchronousDrain.ext.setParcelable(Boolean{mVendorDebug.forceSynchronousDrain});
1003 _aidl_return->push_back(std::move(forceSynchronousDrain));
Mikhail Naganov20047bc2023-01-05 20:16:07 +00001004 } else {
1005 allParametersKnown = false;
1006 LOG(ERROR) << __func__ << ": unrecognized parameter \"" << id << "\"";
1007 }
1008 }
1009 if (allParametersKnown) return ndk::ScopedAStatus::ok();
1010 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
Mikhail Naganove9f10fc2022-10-14 23:31:52 +00001011}
1012
Mikhail Naganov194daaa2023-01-05 22:34:20 +00001013namespace {
1014
1015template <typename W>
1016bool extractParameter(const VendorParameter& p, decltype(W::value)* v) {
1017 std::optional<W> value;
1018 binder_status_t result = p.ext.getParcelable(&value);
1019 if (result == STATUS_OK && value.has_value()) {
1020 *v = value.value().value;
1021 return true;
1022 }
1023 LOG(ERROR) << __func__ << ": failed to read the value of the parameter \"" << p.id
1024 << "\": " << result;
1025 return false;
1026}
1027
1028} // namespace
1029
Mikhail Naganove9f10fc2022-10-14 23:31:52 +00001030ndk::ScopedAStatus Module::setVendorParameters(const std::vector<VendorParameter>& in_parameters,
1031 bool in_async) {
1032 LOG(DEBUG) << __func__ << ": parameter count " << in_parameters.size()
1033 << ", async: " << in_async;
Mikhail Naganov20047bc2023-01-05 20:16:07 +00001034 bool allParametersKnown = true;
1035 for (const auto& p : in_parameters) {
1036 if (p.id == VendorDebug::kForceTransientBurstName) {
Mikhail Naganov194daaa2023-01-05 22:34:20 +00001037 if (!extractParameter<Boolean>(p, &mVendorDebug.forceTransientBurst)) {
1038 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1039 }
1040 } else if (p.id == VendorDebug::kForceSynchronousDrainName) {
1041 if (!extractParameter<Boolean>(p, &mVendorDebug.forceSynchronousDrain)) {
Mikhail Naganov20047bc2023-01-05 20:16:07 +00001042 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
1043 }
1044 } else {
1045 allParametersKnown = false;
1046 LOG(ERROR) << __func__ << ": unrecognized parameter \"" << p.id << "\"";
1047 }
1048 }
1049 if (allParametersKnown) return ndk::ScopedAStatus::ok();
1050 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
Mikhail Naganove9f10fc2022-10-14 23:31:52 +00001051}
1052
Mikhail Naganovfb1acde2022-12-12 18:57:36 +00001053ndk::ScopedAStatus Module::addDeviceEffect(
1054 int32_t in_portConfigId,
1055 const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) {
1056 if (in_effect == nullptr) {
1057 LOG(DEBUG) << __func__ << ": port id " << in_portConfigId << ", null effect";
1058 } else {
1059 LOG(DEBUG) << __func__ << ": port id " << in_portConfigId << ", effect Binder "
1060 << in_effect->asBinder().get();
1061 }
1062 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
1063}
1064
1065ndk::ScopedAStatus Module::removeDeviceEffect(
1066 int32_t in_portConfigId,
1067 const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) {
1068 if (in_effect == nullptr) {
1069 LOG(DEBUG) << __func__ << ": port id " << in_portConfigId << ", null effect";
1070 } else {
1071 LOG(DEBUG) << __func__ << ": port id " << in_portConfigId << ", effect Binder "
1072 << in_effect->asBinder().get();
1073 }
1074 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
1075}
1076
Mikhail Naganovdf5adfd2021-11-11 22:09:22 +00001077} // namespace aidl::android::hardware::audio::core