mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 16:50:18 +00:00
This is aligned with the HIDL implementation VTS. The echo reference device can't provide any input until certain preconditions are met, and modeling these preconditions in the test is not trivial. Also, add the information into the mix port into the trace scope for easier identification on test failure. Bug: 328010709 Test: atest VtsHalAudioCoreTargetTest Change-Id: I737479d8ef1961791ac3bd82aeb779453d2e49f4
596 lines
26 KiB
C++
596 lines
26 KiB
C++
/*
|
|
* Copyright (C) 2022 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <algorithm>
|
|
#include <chrono>
|
|
|
|
#define LOG_TAG "VtsHalAudio.ModuleConfig"
|
|
#include <android-base/logging.h>
|
|
|
|
#include <Utils.h>
|
|
#include <aidl/android/media/audio/common/AudioInputFlags.h>
|
|
#include <aidl/android/media/audio/common/AudioIoFlags.h>
|
|
#include <aidl/android/media/audio/common/AudioOutputFlags.h>
|
|
#include <error/expected_utils.h>
|
|
|
|
#include "ModuleConfig.h"
|
|
|
|
using namespace android;
|
|
using namespace std::chrono_literals;
|
|
|
|
using aidl::android::hardware::audio::common::isBitPositionFlagSet;
|
|
using aidl::android::hardware::audio::core::IModule;
|
|
using aidl::android::media::audio::common::AudioChannelLayout;
|
|
using aidl::android::media::audio::common::AudioDeviceDescription;
|
|
using aidl::android::media::audio::common::AudioDeviceType;
|
|
using aidl::android::media::audio::common::AudioEncapsulationMode;
|
|
using aidl::android::media::audio::common::AudioFormatDescription;
|
|
using aidl::android::media::audio::common::AudioFormatType;
|
|
using aidl::android::media::audio::common::AudioInputFlags;
|
|
using aidl::android::media::audio::common::AudioIoFlags;
|
|
using aidl::android::media::audio::common::AudioOffloadInfo;
|
|
using aidl::android::media::audio::common::AudioOutputFlags;
|
|
using aidl::android::media::audio::common::AudioPort;
|
|
using aidl::android::media::audio::common::AudioPortConfig;
|
|
using aidl::android::media::audio::common::AudioPortExt;
|
|
using aidl::android::media::audio::common::AudioProfile;
|
|
using aidl::android::media::audio::common::AudioUsage;
|
|
using aidl::android::media::audio::common::Int;
|
|
|
|
// static
|
|
std::optional<AudioOffloadInfo> ModuleConfig::generateOffloadInfoIfNeeded(
|
|
const AudioPortConfig& portConfig) {
|
|
if (portConfig.flags.has_value() &&
|
|
portConfig.flags.value().getTag() == AudioIoFlags::Tag::output &&
|
|
isBitPositionFlagSet(portConfig.flags.value().get<AudioIoFlags::Tag::output>(),
|
|
AudioOutputFlags::COMPRESS_OFFLOAD)) {
|
|
AudioOffloadInfo offloadInfo;
|
|
offloadInfo.base.sampleRate = portConfig.sampleRate.value().value;
|
|
offloadInfo.base.channelMask = portConfig.channelMask.value();
|
|
offloadInfo.base.format = portConfig.format.value();
|
|
offloadInfo.bitRatePerSecond = 256000; // Arbitrary value.
|
|
offloadInfo.durationUs = std::chrono::microseconds(1min).count(); // Arbitrary value.
|
|
offloadInfo.usage = AudioUsage::MEDIA;
|
|
offloadInfo.encapsulationMode = AudioEncapsulationMode::NONE;
|
|
return offloadInfo;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
// static
|
|
std::vector<aidl::android::media::audio::common::AudioPort>
|
|
ModuleConfig::getAudioPortsForDeviceTypes(
|
|
const std::vector<aidl::android::media::audio::common::AudioPort>& ports,
|
|
const std::vector<AudioDeviceType>& deviceTypes, const std::string& connection) {
|
|
std::vector<AudioPort> result;
|
|
for (const auto& port : ports) {
|
|
if (port.ext.getTag() != AudioPortExt::Tag::device) continue;
|
|
const auto type = port.ext.get<AudioPortExt::Tag::device>().device.type;
|
|
if (type.connection == connection) {
|
|
for (auto deviceType : deviceTypes) {
|
|
if (type.type == deviceType) {
|
|
result.push_back(port);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// static
|
|
std::vector<aidl::android::media::audio::common::AudioPort> ModuleConfig::getBuiltInMicPorts(
|
|
const std::vector<aidl::android::media::audio::common::AudioPort>& ports) {
|
|
return getAudioPortsForDeviceTypes(
|
|
ports, std::vector<AudioDeviceType>{AudioDeviceType::IN_MICROPHONE,
|
|
AudioDeviceType::IN_MICROPHONE_BACK});
|
|
}
|
|
|
|
template <typename T>
|
|
auto findById(const std::vector<T>& v, int32_t id) {
|
|
return std::find_if(v.begin(), v.end(), [&](const auto& p) { return p.id == id; });
|
|
}
|
|
|
|
ModuleConfig::ModuleConfig(IModule* module) {
|
|
mStatus = module->getAudioPorts(&mPorts);
|
|
if (!mStatus.isOk()) return;
|
|
for (const auto& port : mPorts) {
|
|
if (port.ext.getTag() != AudioPortExt::Tag::device) continue;
|
|
const auto& devicePort = port.ext.get<AudioPortExt::Tag::device>();
|
|
if (devicePort.device.type.connection.empty()) {
|
|
const bool isInput = port.flags.getTag() == AudioIoFlags::Tag::input;
|
|
// Permanently attached device.
|
|
if (isInput) {
|
|
mAttachedSourceDevicePorts.insert(port.id);
|
|
} else {
|
|
mAttachedSinkDevicePorts.insert(port.id);
|
|
}
|
|
} else {
|
|
mExternalDevicePorts.insert(port.id);
|
|
}
|
|
}
|
|
if (!mStatus.isOk()) return;
|
|
mStatus = module->getAudioRoutes(&mRoutes);
|
|
if (!mStatus.isOk()) return;
|
|
mStatus = module->getAudioPortConfigs(&mInitialConfigs);
|
|
}
|
|
|
|
std::vector<AudioPort> ModuleConfig::getAttachedDevicePorts() const {
|
|
std::vector<AudioPort> result;
|
|
std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) {
|
|
return mAttachedSinkDevicePorts.count(port.id) != 0 ||
|
|
mAttachedSourceDevicePorts.count(port.id) != 0;
|
|
});
|
|
return result;
|
|
}
|
|
|
|
std::vector<aidl::android::media::audio::common::AudioPort>
|
|
ModuleConfig::getAudioPortsForDeviceTypes(const std::vector<AudioDeviceType>& deviceTypes,
|
|
const std::string& connection) const {
|
|
return getAudioPortsForDeviceTypes(mPorts, deviceTypes, connection);
|
|
}
|
|
|
|
std::vector<AudioPort> ModuleConfig::getConnectedExternalDevicePorts() const {
|
|
std::vector<AudioPort> result;
|
|
std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) {
|
|
return mConnectedExternalSinkDevicePorts.count(port.id) != 0 ||
|
|
mConnectedExternalSourceDevicePorts.count(port.id) != 0;
|
|
});
|
|
return result;
|
|
}
|
|
|
|
std::set<int32_t> ModuleConfig::getConnectedSinkDevicePorts() const {
|
|
std::set<int32_t> result;
|
|
result.insert(mAttachedSinkDevicePorts.begin(), mAttachedSinkDevicePorts.end());
|
|
result.insert(mConnectedExternalSinkDevicePorts.begin(),
|
|
mConnectedExternalSinkDevicePorts.end());
|
|
return result;
|
|
}
|
|
|
|
std::set<int32_t> ModuleConfig::getConnectedSourceDevicePorts() const {
|
|
std::set<int32_t> result;
|
|
result.insert(mAttachedSourceDevicePorts.begin(), mAttachedSourceDevicePorts.end());
|
|
result.insert(mConnectedExternalSourceDevicePorts.begin(),
|
|
mConnectedExternalSourceDevicePorts.end());
|
|
return result;
|
|
}
|
|
|
|
std::vector<AudioPort> ModuleConfig::getExternalDevicePorts() const {
|
|
std::vector<AudioPort> result;
|
|
std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result),
|
|
[&](const auto& port) { return mExternalDevicePorts.count(port.id) != 0; });
|
|
return result;
|
|
}
|
|
|
|
std::vector<AudioPort> ModuleConfig::getInputMixPorts(bool connectedOnly) const {
|
|
std::vector<AudioPort> result;
|
|
std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) {
|
|
return port.ext.getTag() == AudioPortExt::Tag::mix &&
|
|
port.flags.getTag() == AudioIoFlags::Tag::input &&
|
|
(!connectedOnly || !getConnectedSourceDevicesPortsForMixPort(port).empty());
|
|
});
|
|
return result;
|
|
}
|
|
|
|
std::vector<AudioPort> ModuleConfig::getOutputMixPorts(bool connectedOnly) const {
|
|
std::vector<AudioPort> result;
|
|
std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) {
|
|
return port.ext.getTag() == AudioPortExt::Tag::mix &&
|
|
port.flags.getTag() == AudioIoFlags::Tag::output &&
|
|
(!connectedOnly || !getConnectedSinkDevicesPortsForMixPort(port).empty());
|
|
});
|
|
return result;
|
|
}
|
|
|
|
std::vector<AudioPort> ModuleConfig::getNonBlockingMixPorts(bool connectedOnly,
|
|
bool singlePort) const {
|
|
return findMixPorts(false /*isInput*/, connectedOnly, singlePort, [&](const AudioPort& port) {
|
|
return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
|
|
AudioOutputFlags::NON_BLOCKING);
|
|
});
|
|
}
|
|
|
|
std::vector<AudioPort> ModuleConfig::getOffloadMixPorts(bool connectedOnly, bool singlePort) const {
|
|
return findMixPorts(false /*isInput*/, connectedOnly, singlePort, [&](const AudioPort& port) {
|
|
return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
|
|
AudioOutputFlags::COMPRESS_OFFLOAD);
|
|
});
|
|
}
|
|
|
|
std::vector<AudioPort> ModuleConfig::getPrimaryMixPorts(bool connectedOnly, bool singlePort) const {
|
|
return findMixPorts(false /*isInput*/, connectedOnly, singlePort, [&](const AudioPort& port) {
|
|
return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
|
|
AudioOutputFlags::PRIMARY);
|
|
});
|
|
}
|
|
|
|
std::vector<AudioPort> ModuleConfig::getMmapOutMixPorts(bool connectedOnly, bool singlePort) const {
|
|
return findMixPorts(false /*isInput*/, connectedOnly, singlePort, [&](const AudioPort& port) {
|
|
return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
|
|
AudioOutputFlags::MMAP_NOIRQ);
|
|
});
|
|
}
|
|
|
|
std::vector<AudioPort> ModuleConfig::getMmapInMixPorts(bool connectedOnly, bool singlePort) const {
|
|
return findMixPorts(true /*isInput*/, connectedOnly, singlePort, [&](const AudioPort& port) {
|
|
return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::input>(),
|
|
AudioInputFlags::MMAP_NOIRQ);
|
|
});
|
|
}
|
|
|
|
std::vector<AudioPort> ModuleConfig::getRemoteSubmixPorts(bool isInput, bool singlePort) const {
|
|
AudioDeviceType deviceType = isInput ? AudioDeviceType::IN_SUBMIX : AudioDeviceType::OUT_SUBMIX;
|
|
auto ports = getAudioPortsForDeviceTypes(std::vector<AudioDeviceType>{deviceType},
|
|
AudioDeviceDescription::CONNECTION_VIRTUAL);
|
|
if (singlePort) {
|
|
if (!ports.empty()) ports.resize(1);
|
|
}
|
|
return ports;
|
|
}
|
|
|
|
std::vector<AudioPort> ModuleConfig::getConnectedDevicesPortsForMixPort(
|
|
bool isInput, const AudioPortConfig& mixPortConfig) const {
|
|
const auto mixPortIt = findById<AudioPort>(mPorts, mixPortConfig.portId);
|
|
if (mixPortIt != mPorts.end()) {
|
|
return getConnectedDevicesPortsForMixPort(isInput, *mixPortIt);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
std::vector<AudioPort> ModuleConfig::getConnectedSinkDevicesPortsForMixPort(
|
|
const AudioPort& mixPort) const {
|
|
std::vector<AudioPort> result;
|
|
std::set<int32_t> connectedSinkDevicePorts = getConnectedSinkDevicePorts();
|
|
for (const auto& route : mRoutes) {
|
|
if ((connectedSinkDevicePorts.count(route.sinkPortId) != 0) &&
|
|
std::find(route.sourcePortIds.begin(), route.sourcePortIds.end(), mixPort.id) !=
|
|
route.sourcePortIds.end()) {
|
|
const auto devicePortIt = findById<AudioPort>(mPorts, route.sinkPortId);
|
|
if (devicePortIt != mPorts.end()) result.push_back(*devicePortIt);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::vector<AudioPort> ModuleConfig::getConnectedSourceDevicesPortsForMixPort(
|
|
const AudioPort& mixPort) const {
|
|
std::vector<AudioPort> result;
|
|
std::set<int32_t> connectedSourceDevicePorts = getConnectedSourceDevicePorts();
|
|
for (const auto& route : mRoutes) {
|
|
if (route.sinkPortId == mixPort.id) {
|
|
for (const auto srcId : route.sourcePortIds) {
|
|
if (connectedSourceDevicePorts.count(srcId) != 0) {
|
|
const auto devicePortIt = findById<AudioPort>(mPorts, srcId);
|
|
if (devicePortIt != mPorts.end()) result.push_back(*devicePortIt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::optional<AudioPort> ModuleConfig::getSourceMixPortForConnectedDevice() const {
|
|
std::set<int32_t> connectedSinkDevicePorts = getConnectedSinkDevicePorts();
|
|
for (const auto& route : mRoutes) {
|
|
if (connectedSinkDevicePorts.count(route.sinkPortId) != 0) {
|
|
const auto mixPortIt = findById<AudioPort>(mPorts, route.sourcePortIds[0]);
|
|
if (mixPortIt != mPorts.end()) return *mixPortIt;
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
std::vector<AudioPort> ModuleConfig::getRoutableDevicePortsForMixPort(const AudioPort& port,
|
|
bool connectedOnly) const {
|
|
std::set<int32_t> portIds = findRoutablePortIds(port.id);
|
|
const bool isInput = port.flags.getTag() == AudioIoFlags::input;
|
|
std::set<int32_t> devicePortIds;
|
|
if (connectedOnly) {
|
|
devicePortIds = isInput ? getConnectedSourceDevicePorts() : getConnectedSinkDevicePorts();
|
|
} else {
|
|
devicePortIds = portIds;
|
|
}
|
|
std::vector<AudioPort> result;
|
|
std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) {
|
|
return port.ext.getTag() == AudioPortExt::Tag::device && portIds.count(port.id) > 0 &&
|
|
devicePortIds.count(port.id) > 0;
|
|
});
|
|
return result;
|
|
}
|
|
|
|
std::vector<AudioPort> ModuleConfig::getRoutableMixPortsForDevicePort(const AudioPort& port,
|
|
bool connectedOnly) const {
|
|
std::set<int32_t> portIds = findRoutablePortIds(port.id);
|
|
const bool isInput = port.flags.getTag() == AudioIoFlags::input;
|
|
return findMixPorts(isInput, connectedOnly, false /*singlePort*/,
|
|
[&portIds](const AudioPort& p) { return portIds.count(p.id) > 0; });
|
|
}
|
|
|
|
std::optional<ModuleConfig::SrcSinkPair> ModuleConfig::getNonRoutableSrcSinkPair(
|
|
bool isInput) const {
|
|
const auto mixPorts = getMixPorts(isInput, false /*connectedOnly*/);
|
|
std::set<std::pair<int32_t, int32_t>> allowedRoutes;
|
|
for (const auto& route : mRoutes) {
|
|
for (const auto srcPortId : route.sourcePortIds) {
|
|
allowedRoutes.emplace(std::make_pair(srcPortId, route.sinkPortId));
|
|
}
|
|
}
|
|
auto make_pair = [isInput](auto& device, auto& mix) {
|
|
return isInput ? std::make_pair(device, mix) : std::make_pair(mix, device);
|
|
};
|
|
for (const auto portId :
|
|
isInput ? getConnectedSourceDevicePorts() : getConnectedSinkDevicePorts()) {
|
|
const auto devicePortIt = findById<AudioPort>(mPorts, portId);
|
|
if (devicePortIt == mPorts.end()) continue;
|
|
auto devicePortConfig = getSingleConfigForDevicePort(*devicePortIt);
|
|
for (const auto& mixPort : mixPorts) {
|
|
if (std::find(allowedRoutes.begin(), allowedRoutes.end(),
|
|
make_pair(portId, mixPort.id)) == allowedRoutes.end()) {
|
|
auto mixPortConfig = getSingleConfigForMixPort(isInput, mixPort);
|
|
if (mixPortConfig.has_value()) {
|
|
return make_pair(devicePortConfig, mixPortConfig.value());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
std::optional<ModuleConfig::SrcSinkPair> ModuleConfig::getRoutableSrcSinkPair(bool isInput) const {
|
|
if (isInput) {
|
|
std::set<int32_t> connectedSourceDevicePorts = getConnectedSourceDevicePorts();
|
|
for (const auto& route : mRoutes) {
|
|
auto srcPortIdIt = std::find_if(
|
|
route.sourcePortIds.begin(), route.sourcePortIds.end(),
|
|
[&](const auto& portId) { return connectedSourceDevicePorts.count(portId); });
|
|
if (srcPortIdIt == route.sourcePortIds.end()) continue;
|
|
const auto devicePortIt = findById<AudioPort>(mPorts, *srcPortIdIt);
|
|
const auto mixPortIt = findById<AudioPort>(mPorts, route.sinkPortId);
|
|
if (devicePortIt == mPorts.end() || mixPortIt == mPorts.end()) continue;
|
|
auto devicePortConfig = getSingleConfigForDevicePort(*devicePortIt);
|
|
auto mixPortConfig = getSingleConfigForMixPort(isInput, *mixPortIt);
|
|
if (!mixPortConfig.has_value()) continue;
|
|
return std::make_pair(devicePortConfig, mixPortConfig.value());
|
|
}
|
|
} else {
|
|
std::set<int32_t> connectedSinkDevicePorts = getConnectedSinkDevicePorts();
|
|
for (const auto& route : mRoutes) {
|
|
if (connectedSinkDevicePorts.count(route.sinkPortId) == 0) continue;
|
|
const auto mixPortIt = findById<AudioPort>(mPorts, route.sourcePortIds[0]);
|
|
const auto devicePortIt = findById<AudioPort>(mPorts, route.sinkPortId);
|
|
if (devicePortIt == mPorts.end() || mixPortIt == mPorts.end()) continue;
|
|
auto mixPortConfig = getSingleConfigForMixPort(isInput, *mixPortIt);
|
|
auto devicePortConfig = getSingleConfigForDevicePort(*devicePortIt);
|
|
if (!mixPortConfig.has_value()) continue;
|
|
return std::make_pair(mixPortConfig.value(), devicePortConfig);
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
std::vector<ModuleConfig::SrcSinkGroup> ModuleConfig::getRoutableSrcSinkGroups(bool isInput) const {
|
|
std::vector<SrcSinkGroup> result;
|
|
if (isInput) {
|
|
std::set<int32_t> connectedSourceDevicePorts = getConnectedSourceDevicePorts();
|
|
for (const auto& route : mRoutes) {
|
|
std::vector<int32_t> srcPortIds;
|
|
std::copy_if(route.sourcePortIds.begin(), route.sourcePortIds.end(),
|
|
std::back_inserter(srcPortIds), [&](const auto& portId) {
|
|
return connectedSourceDevicePorts.count(portId);
|
|
});
|
|
if (srcPortIds.empty()) continue;
|
|
const auto mixPortIt = findById<AudioPort>(mPorts, route.sinkPortId);
|
|
if (mixPortIt == mPorts.end()) continue;
|
|
auto mixPortConfig = getSingleConfigForMixPort(isInput, *mixPortIt);
|
|
if (!mixPortConfig.has_value()) continue;
|
|
std::vector<SrcSinkPair> pairs;
|
|
for (const auto srcPortId : srcPortIds) {
|
|
const auto devicePortIt = findById<AudioPort>(mPorts, srcPortId);
|
|
if (devicePortIt == mPorts.end()) continue;
|
|
// Using all configs for every source would be too much.
|
|
auto devicePortConfig = getSingleConfigForDevicePort(*devicePortIt);
|
|
pairs.emplace_back(devicePortConfig, mixPortConfig.value());
|
|
}
|
|
if (!pairs.empty()) {
|
|
result.emplace_back(route, std::move(pairs));
|
|
}
|
|
}
|
|
} else {
|
|
std::set<int32_t> connectedSinkDevicePorts = getConnectedSinkDevicePorts();
|
|
for (const auto& route : mRoutes) {
|
|
if (connectedSinkDevicePorts.count(route.sinkPortId) == 0) continue;
|
|
const auto devicePortIt = findById<AudioPort>(mPorts, route.sinkPortId);
|
|
if (devicePortIt == mPorts.end()) continue;
|
|
auto devicePortConfig = getSingleConfigForDevicePort(*devicePortIt);
|
|
std::vector<SrcSinkPair> pairs;
|
|
for (const auto srcPortId : route.sourcePortIds) {
|
|
const auto mixPortIt = findById<AudioPort>(mPorts, srcPortId);
|
|
if (mixPortIt == mPorts.end()) continue;
|
|
// Using all configs for every source would be too much.
|
|
auto mixPortConfig = getSingleConfigForMixPort(isInput, *mixPortIt);
|
|
if (mixPortConfig.has_value()) {
|
|
pairs.emplace_back(mixPortConfig.value(), devicePortConfig);
|
|
}
|
|
}
|
|
if (!pairs.empty()) {
|
|
result.emplace_back(route, std::move(pairs));
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::string ModuleConfig::toString() const {
|
|
std::string result;
|
|
result.append("Ports: ");
|
|
result.append(android::internal::ToString(mPorts));
|
|
result.append("\nInitial configs: ");
|
|
result.append(android::internal::ToString(mInitialConfigs));
|
|
result.append("\nAttached sink device ports: ");
|
|
result.append(android::internal::ToString(mAttachedSinkDevicePorts));
|
|
result.append("\nAttached source device ports: ");
|
|
result.append(android::internal::ToString(mAttachedSourceDevicePorts));
|
|
result.append("\nExternal device ports: ");
|
|
result.append(android::internal::ToString(mExternalDevicePorts));
|
|
result.append("\nConnected external device ports: ");
|
|
result.append(android::internal::ToString(getConnectedExternalDevicePorts()));
|
|
result.append("\nRoutes: ");
|
|
result.append(android::internal::ToString(mRoutes));
|
|
return result;
|
|
}
|
|
|
|
static size_t combineAudioConfigs(const AudioPort& port, const AudioProfile& profile,
|
|
std::vector<AudioPortConfig>* result) {
|
|
const size_t newConfigCount = profile.channelMasks.size() * profile.sampleRates.size();
|
|
result->reserve(result->capacity() + newConfigCount);
|
|
for (auto channelMask : profile.channelMasks) {
|
|
for (auto sampleRate : profile.sampleRates) {
|
|
AudioPortConfig config{};
|
|
config.portId = port.id;
|
|
Int sr;
|
|
sr.value = sampleRate;
|
|
config.sampleRate = sr;
|
|
config.channelMask = channelMask;
|
|
config.format = profile.format;
|
|
config.flags = port.flags;
|
|
config.ext = port.ext;
|
|
result->push_back(std::move(config));
|
|
}
|
|
}
|
|
return newConfigCount;
|
|
}
|
|
|
|
static bool isDynamicProfile(const AudioProfile& profile) {
|
|
return (profile.format.type == AudioFormatType::DEFAULT && profile.format.encoding.empty()) ||
|
|
profile.sampleRates.empty() || profile.channelMasks.empty();
|
|
}
|
|
|
|
std::vector<AudioPort> ModuleConfig::findMixPorts(
|
|
bool isInput, bool connectedOnly, bool singlePort,
|
|
const std::function<bool(const AudioPort&)>& pred) const {
|
|
std::vector<AudioPort> result;
|
|
const auto mixPorts = getMixPorts(isInput, connectedOnly);
|
|
for (auto mixPortIt = mixPorts.begin(); mixPortIt != mixPorts.end();) {
|
|
mixPortIt = std::find_if(mixPortIt, mixPorts.end(), pred);
|
|
if (mixPortIt == mixPorts.end()) break;
|
|
result.push_back(*mixPortIt++);
|
|
if (singlePort) break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::set<int32_t> ModuleConfig::findRoutablePortIds(int32_t portId) const {
|
|
std::set<int32_t> portIds;
|
|
for (const auto& route : mRoutes) {
|
|
if (portId == route.sinkPortId) {
|
|
portIds.insert(route.sourcePortIds.begin(), route.sourcePortIds.end());
|
|
} else if (auto it = std::find(route.sourcePortIds.begin(), route.sourcePortIds.end(),
|
|
portId);
|
|
it != route.sourcePortIds.end()) {
|
|
portIds.insert(route.sinkPortId);
|
|
}
|
|
}
|
|
return portIds;
|
|
}
|
|
|
|
std::vector<AudioPortConfig> ModuleConfig::generateAudioMixPortConfigs(
|
|
const std::vector<AudioPort>& ports, bool isInput, bool singleProfile) const {
|
|
std::vector<AudioPortConfig> result;
|
|
for (const auto& mixPort : ports) {
|
|
if (getConnectedDevicesPortsForMixPort(isInput, mixPort).empty()) {
|
|
continue;
|
|
}
|
|
for (const auto& profile : mixPort.profiles) {
|
|
if (isDynamicProfile(profile)) continue;
|
|
combineAudioConfigs(mixPort, profile, &result);
|
|
if (singleProfile && !result.empty()) {
|
|
result.resize(1);
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::vector<AudioPortConfig> ModuleConfig::generateAudioDevicePortConfigs(
|
|
const std::vector<AudioPort>& ports, bool singleProfile) const {
|
|
std::vector<AudioPortConfig> result;
|
|
for (const auto& devicePort : ports) {
|
|
const size_t resultSizeBefore = result.size();
|
|
for (const auto& profile : devicePort.profiles) {
|
|
combineAudioConfigs(devicePort, profile, &result);
|
|
if (singleProfile && !result.empty()) {
|
|
result.resize(1);
|
|
return result;
|
|
}
|
|
}
|
|
if (resultSizeBefore == result.size()) {
|
|
std::copy_if(mInitialConfigs.begin(), mInitialConfigs.end(), std::back_inserter(result),
|
|
[&](const auto& config) { return config.portId == devicePort.id; });
|
|
if (resultSizeBefore == result.size()) {
|
|
AudioPortConfig empty;
|
|
empty.portId = devicePort.id;
|
|
empty.ext = devicePort.ext;
|
|
result.push_back(empty);
|
|
}
|
|
}
|
|
if (singleProfile) return result;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::optional<AudioPort> ModuleConfig::getPort(int32_t portId) {
|
|
auto portsIt = findById(mPorts, portId);
|
|
return portsIt != mPorts.end() ? std::optional<AudioPort>(*portsIt) : std::nullopt;
|
|
}
|
|
|
|
ndk::ScopedAStatus ModuleConfig::onExternalDeviceConnected(IModule* module, const AudioPort& port) {
|
|
RETURN_STATUS_IF_ERROR(module->getAudioPorts(&mPorts));
|
|
RETURN_STATUS_IF_ERROR(module->getAudioRoutes(&mRoutes));
|
|
|
|
// Validate port is present in module
|
|
if (std::find(mPorts.begin(), mPorts.end(), port) == mPorts.end()) {
|
|
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
|
}
|
|
|
|
if (port.flags.getTag() == aidl::android::media::audio::common::AudioIoFlags::Tag::input) {
|
|
mConnectedExternalSourceDevicePorts.insert(port.id);
|
|
} else {
|
|
mConnectedExternalSinkDevicePorts.insert(port.id);
|
|
}
|
|
return ndk::ScopedAStatus::ok();
|
|
}
|
|
|
|
ndk::ScopedAStatus ModuleConfig::onExternalDeviceDisconnected(IModule* module,
|
|
const AudioPort& port) {
|
|
RETURN_STATUS_IF_ERROR(module->getAudioPorts(&mPorts));
|
|
RETURN_STATUS_IF_ERROR(module->getAudioRoutes(&mRoutes));
|
|
|
|
if (port.flags.getTag() == aidl::android::media::audio::common::AudioIoFlags::Tag::input) {
|
|
mConnectedExternalSourceDevicePorts.erase(port.id);
|
|
} else {
|
|
mConnectedExternalSinkDevicePorts.erase(port.id);
|
|
}
|
|
return ndk::ScopedAStatus::ok();
|
|
}
|
|
|
|
bool ModuleConfig::isMmapSupported() const {
|
|
const std::vector<AudioPort> mmapOutMixPorts =
|
|
getMmapOutMixPorts(false /*connectedOnly*/, false /*singlePort*/);
|
|
const std::vector<AudioPort> mmapInMixPorts =
|
|
getMmapInMixPorts(false /*connectedOnly*/, false /*singlePort*/);
|
|
return !mmapOutMixPorts.empty() || !mmapInMixPorts.empty();
|
|
}
|