#include #include #define LOG_TAG "AHAL_Config" #include #include #include #include #include #include #include "core-impl/XmlConverter.h" #include "core-impl/XsdcConversion.h" using aidl::android::media::audio::common::AudioChannelLayout; using aidl::android::media::audio::common::AudioDevice; using aidl::android::media::audio::common::AudioDeviceAddress; using aidl::android::media::audio::common::AudioDeviceDescription; using aidl::android::media::audio::common::AudioDeviceType; using aidl::android::media::audio::common::AudioFormatDescription; using aidl::android::media::audio::common::AudioFormatType; using aidl::android::media::audio::common::AudioGain; using aidl::android::media::audio::common::AudioHalCapCriterion; using aidl::android::media::audio::common::AudioHalCapCriterionType; using aidl::android::media::audio::common::AudioHalVolumeCurve; using aidl::android::media::audio::common::AudioIoFlags; using aidl::android::media::audio::common::AudioPort; using aidl::android::media::audio::common::AudioPortConfig; using aidl::android::media::audio::common::AudioPortDeviceExt; using aidl::android::media::audio::common::AudioPortExt; using aidl::android::media::audio::common::AudioPortMixExt; using aidl::android::media::audio::common::AudioProfile; using ::android::BAD_VALUE; using ::android::base::unexpected; namespace ap_xsd = android::audio::policy::configuration; namespace eng_xsd = android::audio::policy::engine::configuration; namespace aidl::android::hardware::audio::core::internal { inline ConversionResult assertNonEmpty(const std::string& s) { if (s.empty()) { LOG(ERROR) << __func__ << " Review Audio Policy config: " << " empty string is not valid."; return unexpected(BAD_VALUE); } return s; } #define NON_EMPTY_STRING_OR_FATAL(s) VALUE_OR_FATAL(assertNonEmpty(s)) ConversionResult convertAudioFormatToAidl(const std::string& xsdcFormat) { audio_format_t legacyFormat = ::android::formatFromString(xsdcFormat, AUDIO_FORMAT_DEFAULT); ConversionResult result = legacy2aidl_audio_format_t_AudioFormatDescription(legacyFormat); if ((legacyFormat == AUDIO_FORMAT_DEFAULT && xsdcFormat.compare("AUDIO_FORMAT_DEFAULT") != 0) || !result.ok()) { LOG(ERROR) << __func__ << " Review Audio Policy config: " << xsdcFormat << " is not a valid audio format."; return unexpected(BAD_VALUE); } return result; } std::unordered_set getAttachedDevices(const ap_xsd::Modules::Module& moduleConfig) { std::unordered_set attachedDeviceSet; if (moduleConfig.hasAttachedDevices()) { for (const ap_xsd::AttachedDevices& attachedDevices : moduleConfig.getAttachedDevices()) { if (attachedDevices.hasItem()) { attachedDeviceSet.insert(attachedDevices.getItem().begin(), attachedDevices.getItem().end()); } } } return attachedDeviceSet; } ConversionResult convertDeviceTypeToAidl(const std::string& xType) { audio_devices_t legacyDeviceType = AUDIO_DEVICE_NONE; ::android::DeviceConverter::fromString(xType, legacyDeviceType); ConversionResult result = legacy2aidl_audio_devices_t_AudioDeviceDescription(legacyDeviceType); if ((legacyDeviceType == AUDIO_DEVICE_NONE) || !result.ok()) { LOG(ERROR) << __func__ << " Review Audio Policy config: " << xType << " is not a valid device type."; return unexpected(BAD_VALUE); } return result; } ConversionResult createAudioDevice( const ap_xsd::DevicePorts::DevicePort& xDevicePort) { AudioDevice device = { .type = VALUE_OR_FATAL(convertDeviceTypeToAidl(xDevicePort.getType())), .address = xDevicePort.hasAddress() ? AudioDeviceAddress::make( xDevicePort.getAddress()) : AudioDeviceAddress{}}; if (device.type.type == AudioDeviceType::IN_MICROPHONE && device.type.connection.empty()) { device.address = "bottom"; } else if (device.type.type == AudioDeviceType::IN_MICROPHONE_BACK && device.type.connection.empty()) { device.address = "back"; } return device; } ConversionResult createAudioPortExt( const ap_xsd::DevicePorts::DevicePort& xDevicePort, const std::string& xDefaultOutputDevice) { AudioPortDeviceExt deviceExt = { .device = VALUE_OR_FATAL(createAudioDevice(xDevicePort)), .flags = (xDevicePort.getTagName() == xDefaultOutputDevice) ? 1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE : 0, .encodedFormats = xDevicePort.hasEncodedFormats() ? VALUE_OR_FATAL( (convertCollectionToAidl( xDevicePort.getEncodedFormats(), &convertAudioFormatToAidl))) : std::vector{}, }; return AudioPortExt::make(deviceExt); } ConversionResult createAudioPortExt(const ap_xsd::MixPorts::MixPort& xMixPort) { AudioPortMixExt mixExt = { .maxOpenStreamCount = xMixPort.hasMaxOpenCount() ? static_cast(xMixPort.getMaxOpenCount()) : 0, .maxActiveStreamCount = xMixPort.hasMaxActiveCount() ? static_cast(xMixPort.getMaxActiveCount()) : 1, .recommendedMuteDurationMs = xMixPort.hasRecommendedMuteDurationMs() ? static_cast(xMixPort.getRecommendedMuteDurationMs()) : 0}; return AudioPortExt::make(mixExt); } ConversionResult convertGainModeToAidl(const std::vector& gainModeVec) { int gainModeMask = 0; for (const ap_xsd::AudioGainMode& gainMode : gainModeVec) { audio_gain_mode_t legacyGainMode; if (::android::GainModeConverter::fromString(ap_xsd::toString(gainMode), legacyGainMode)) { gainModeMask |= static_cast(legacyGainMode); } } return gainModeMask; } ConversionResult convertChannelMaskToAidl( const ap_xsd::AudioChannelMask& xChannelMask) { std::string xChannelMaskLiteral = ap_xsd::toString(xChannelMask); audio_channel_mask_t legacyChannelMask = ::android::channelMaskFromString(xChannelMaskLiteral); ConversionResult result = legacy2aidl_audio_channel_mask_t_AudioChannelLayout( legacyChannelMask, /* isInput= */ xChannelMaskLiteral.find("AUDIO_CHANNEL_IN_") == 0); if ((legacyChannelMask == AUDIO_CHANNEL_INVALID) || !result.ok()) { LOG(ERROR) << __func__ << " Review Audio Policy config: " << xChannelMaskLiteral << " is not a valid audio channel mask."; return unexpected(BAD_VALUE); } return result; } ConversionResult convertGainToAidl(const ap_xsd::Gains::Gain& xGain) { return AudioGain{ .mode = VALUE_OR_FATAL(convertGainModeToAidl(xGain.getMode())), .channelMask = xGain.hasChannel_mask() ? VALUE_OR_FATAL(convertChannelMaskToAidl(xGain.getChannel_mask())) : AudioChannelLayout{}, .minValue = xGain.hasMinValueMB() ? xGain.getMinValueMB() : 0, .maxValue = xGain.hasMaxValueMB() ? xGain.getMaxValueMB() : 0, .defaultValue = xGain.hasDefaultValueMB() ? xGain.getDefaultValueMB() : 0, .stepValue = xGain.hasStepValueMB() ? xGain.getStepValueMB() : 0, .minRampMs = xGain.hasMinRampMs() ? xGain.getMinRampMs() : 0, .maxRampMs = xGain.hasMaxRampMs() ? xGain.getMaxRampMs() : 0, .useForVolume = xGain.hasUseForVolume() ? xGain.getUseForVolume() : false, }; } ConversionResult convertAudioProfileToAidl(const ap_xsd::Profile& xProfile) { return AudioProfile{ .format = xProfile.hasFormat() ? VALUE_OR_FATAL(convertAudioFormatToAidl(xProfile.getFormat())) : AudioFormatDescription{}, .channelMasks = xProfile.hasChannelMasks() ? VALUE_OR_FATAL((convertCollectionToAidl( xProfile.getChannelMasks(), &convertChannelMaskToAidl))) : std::vector{}, .sampleRates = xProfile.hasSamplingRates() ? VALUE_OR_FATAL((convertCollectionToAidl( xProfile.getSamplingRates(), [](const int64_t x) -> int { return x; }))) : std::vector{}}; } ConversionResult convertIoFlagsToAidl( const std::vector& flags, const ap_xsd::Role role, bool flagsForMixPort) { int legacyFlagMask = 0; if ((role == ap_xsd::Role::sink && flagsForMixPort) || (role == ap_xsd::Role::source && !flagsForMixPort)) { for (const ap_xsd::AudioInOutFlag& flag : flags) { audio_input_flags_t legacyFlag; if (::android::InputFlagConverter::fromString(ap_xsd::toString(flag), legacyFlag)) { legacyFlagMask |= static_cast(legacyFlag); } } return AudioIoFlags::make( VALUE_OR_FATAL(legacy2aidl_audio_input_flags_t_int32_t_mask( static_cast(legacyFlagMask)))); } else { for (const ap_xsd::AudioInOutFlag& flag : flags) { audio_output_flags_t legacyFlag; if (::android::OutputFlagConverter::fromString(ap_xsd::toString(flag), legacyFlag)) { legacyFlagMask |= static_cast(legacyFlag); } } return AudioIoFlags::make( VALUE_OR_FATAL(legacy2aidl_audio_output_flags_t_int32_t_mask( static_cast(legacyFlagMask)))); } } ConversionResult convertDevicePortToAidl( const ap_xsd::DevicePorts::DevicePort& xDevicePort, const std::string& xDefaultOutputDevice, int32_t& nextPortId) { return AudioPort{ .id = nextPortId++, .name = NON_EMPTY_STRING_OR_FATAL(xDevicePort.getTagName()), .profiles = VALUE_OR_FATAL((convertCollectionToAidl( xDevicePort.getProfile(), convertAudioProfileToAidl))), .flags = VALUE_OR_FATAL(convertIoFlagsToAidl({}, xDevicePort.getRole(), false)), .gains = VALUE_OR_FATAL( (convertWrappedCollectionToAidl( xDevicePort.getGains(), &ap_xsd::Gains::getGain, convertGainToAidl))), .ext = VALUE_OR_FATAL(createAudioPortExt(xDevicePort, xDefaultOutputDevice))}; } ConversionResult> convertDevicePortsInModuleToAidl( const ap_xsd::Modules::Module& xModuleConfig, int32_t& nextPortId) { std::vector audioPortVec; std::vector xDevicePortsVec = xModuleConfig.getDevicePorts(); if (xDevicePortsVec.size() > 1) { LOG(ERROR) << __func__ << "Having multiple '' elements is not allowed, found: " << xDevicePortsVec.size(); return unexpected(BAD_VALUE); } if (!xDevicePortsVec.empty()) { const std::string xDefaultOutputDevice = xModuleConfig.hasDefaultOutputDevice() ? xModuleConfig.getDefaultOutputDevice() : ""; audioPortVec.reserve(xDevicePortsVec[0].getDevicePort().size()); for (const ap_xsd::DevicePorts& xDevicePortsType : xDevicePortsVec) { for (const ap_xsd::DevicePorts::DevicePort& xDevicePort : xDevicePortsType.getDevicePort()) { audioPortVec.push_back(VALUE_OR_FATAL( convertDevicePortToAidl(xDevicePort, xDefaultOutputDevice, nextPortId))); } } } const std::unordered_set xAttachedDeviceSet = getAttachedDevices(xModuleConfig); for (const auto& port : audioPortVec) { const auto& devicePort = port.ext.get(); if (xAttachedDeviceSet.count(port.name) != devicePort.device.type.connection.empty()) { LOG(ERROR) << __func__ << ": Review Audio Policy config: " << "list is incorrect or devicePort \"" << port.name << "\" type= " << devicePort.device.type.toString() << " is incorrect."; return unexpected(BAD_VALUE); } } return audioPortVec; } ConversionResult convertMixPortToAidl(const ap_xsd::MixPorts::MixPort& xMixPort, int32_t& nextPortId) { return AudioPort{ .id = nextPortId++, .name = NON_EMPTY_STRING_OR_FATAL(xMixPort.getName()), .profiles = VALUE_OR_FATAL((convertCollectionToAidl( xMixPort.getProfile(), convertAudioProfileToAidl))), .flags = xMixPort.hasFlags() ? VALUE_OR_FATAL(convertIoFlagsToAidl(xMixPort.getFlags(), xMixPort.getRole(), true)) : VALUE_OR_FATAL(convertIoFlagsToAidl({}, xMixPort.getRole(), true)), .gains = VALUE_OR_FATAL( (convertWrappedCollectionToAidl( xMixPort.getGains(), &ap_xsd::Gains::getGain, &convertGainToAidl))), .ext = VALUE_OR_FATAL(createAudioPortExt(xMixPort)), }; } ConversionResult> convertMixPortsInModuleToAidl( const ap_xsd::Modules::Module& xModuleConfig, int32_t& nextPortId) { std::vector audioPortVec; std::vector xMixPortsVec = xModuleConfig.getMixPorts(); if (xMixPortsVec.size() > 1) { LOG(ERROR) << __func__ << "Having multiple '' elements is not allowed, found: " << xMixPortsVec.size(); return unexpected(BAD_VALUE); } if (!xMixPortsVec.empty()) { audioPortVec.reserve(xMixPortsVec[0].getMixPort().size()); for (const ap_xsd::MixPorts& xMixPortsType : xMixPortsVec) { for (const ap_xsd::MixPorts::MixPort& xMixPort : xMixPortsType.getMixPort()) { audioPortVec.push_back(VALUE_OR_FATAL(convertMixPortToAidl(xMixPort, nextPortId))); } } } return audioPortVec; } ConversionResult getSinkPortId(const ap_xsd::Routes::Route& xRoute, const std::unordered_map& portMap) { auto portMapIter = portMap.find(xRoute.getSink()); if (portMapIter == portMap.end()) { LOG(ERROR) << __func__ << " Review Audio Policy config: audio route" << "has sink: " << xRoute.getSink() << " which is neither a device port nor mix port."; return unexpected(BAD_VALUE); } return portMapIter->second; } ConversionResult> getSourcePortIds( const ap_xsd::Routes::Route& xRoute, const std::unordered_map& portMap) { std::vector sourcePortIds; for (const std::string& rawSource : ::android::base::Split(xRoute.getSources(), ",")) { const std::string source = ::android::base::Trim(rawSource); auto portMapIter = portMap.find(source); if (portMapIter == portMap.end()) { LOG(ERROR) << __func__ << " Review Audio Policy config: audio route" << "has source \"" << source << "\" which is neither a device port nor mix port."; return unexpected(BAD_VALUE); } sourcePortIds.push_back(portMapIter->second); } return sourcePortIds; } ConversionResult convertRouteToAidl(const ap_xsd::Routes::Route& xRoute, const std::vector& aidlAudioPorts) { std::unordered_map portMap; for (const AudioPort& port : aidlAudioPorts) { portMap.insert({port.name, port.id}); } return AudioRoute{.sourcePortIds = VALUE_OR_FATAL(getSourcePortIds(xRoute, portMap)), .sinkPortId = VALUE_OR_FATAL(getSinkPortId(xRoute, portMap)), .isExclusive = (xRoute.getType() == ap_xsd::MixType::mux)}; } ConversionResult> convertRoutesInModuleToAidl( const ap_xsd::Modules::Module& xModuleConfig, const std::vector& aidlAudioPorts) { std::vector audioRouteVec; std::vector xRoutesVec = xModuleConfig.getRoutes(); if (!xRoutesVec.empty()) { /* * xRoutesVec likely only contains one element; that is, it's * likely that all ap_xsd::Routes::MixPort types that we need to convert * are inside of xRoutesVec[0]. */ audioRouteVec.reserve(xRoutesVec[0].getRoute().size()); for (const ap_xsd::Routes& xRoutesType : xRoutesVec) { for (const ap_xsd::Routes::Route& xRoute : xRoutesType.getRoute()) { audioRouteVec.push_back(VALUE_OR_FATAL(convertRouteToAidl(xRoute, aidlAudioPorts))); } } } return audioRouteVec; } ConversionResult> convertModuleConfigToAidl( const ap_xsd::Modules::Module& xModuleConfig) { auto result = std::make_unique(); auto& aidlModuleConfig = *result; std::vector devicePorts = VALUE_OR_FATAL( convertDevicePortsInModuleToAidl(xModuleConfig, aidlModuleConfig.nextPortId)); // The XML config does not specify the default input device. // Assign the first attached input device as the default. for (auto& port : devicePorts) { if (port.flags.getTag() != AudioIoFlags::input) continue; auto& deviceExt = port.ext.get(); if (!deviceExt.device.type.connection.empty()) continue; deviceExt.flags |= 1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE; break; } std::vector mixPorts = VALUE_OR_FATAL( convertMixPortsInModuleToAidl(xModuleConfig, aidlModuleConfig.nextPortId)); aidlModuleConfig.ports.reserve(devicePorts.size() + mixPorts.size()); aidlModuleConfig.ports.insert(aidlModuleConfig.ports.end(), devicePorts.begin(), devicePorts.end()); aidlModuleConfig.ports.insert(aidlModuleConfig.ports.end(), mixPorts.begin(), mixPorts.end()); aidlModuleConfig.routes = VALUE_OR_FATAL(convertRoutesInModuleToAidl(xModuleConfig, aidlModuleConfig.ports)); return result; } ConversionResult convertCapCriterionToAidl( const eng_xsd::CriterionType& xsdcCriterion) { AudioHalCapCriterion aidlCapCriterion; aidlCapCriterion.name = xsdcCriterion.getName(); aidlCapCriterion.criterionTypeName = xsdcCriterion.getType(); aidlCapCriterion.defaultLiteralValue = xsdcCriterion.get_default(); return aidlCapCriterion; } ConversionResult convertCriterionTypeValueToAidl( const eng_xsd::ValueType& xsdcCriterionTypeValue) { return xsdcCriterionTypeValue.getLiteral(); } ConversionResult convertCapCriterionTypeToAidl( const eng_xsd::CriterionTypeType& xsdcCriterionType) { AudioHalCapCriterionType aidlCapCriterionType; aidlCapCriterionType.name = xsdcCriterionType.getName(); aidlCapCriterionType.isInclusive = !(static_cast(xsdcCriterionType.getType())); aidlCapCriterionType.values = VALUE_OR_RETURN( (convertWrappedCollectionToAidl( xsdcCriterionType.getValues(), &eng_xsd::ValuesType::getValue, &convertCriterionTypeValueToAidl))); return aidlCapCriterionType; } ConversionResult convertCurvePointToAidl( const std::string& xsdcCurvePoint) { AudioHalVolumeCurve::CurvePoint aidlCurvePoint{}; if ((sscanf(xsdcCurvePoint.c_str(), "%" SCNd8 ",%d", &aidlCurvePoint.index, &aidlCurvePoint.attenuationMb) != 2) || (aidlCurvePoint.index < AudioHalVolumeCurve::CurvePoint::MIN_INDEX) || (aidlCurvePoint.index > AudioHalVolumeCurve::CurvePoint::MAX_INDEX)) { LOG(ERROR) << __func__ << " Review Audio Policy config: volume curve point:" << "\"" << xsdcCurvePoint << "\" is invalid"; return unexpected(BAD_VALUE); } return aidlCurvePoint; } } // namespace aidl::android::hardware::audio::core::internal