blob: 4edd11f93531c558946c751c256fd3469a8fca78 [file] [log] [blame]
/*
* Copyright (C) 2009 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.
*/
#define LOG_TAG "APM_Config"
#include <AudioPolicyConfig.h>
#include <IOProfile.h>
#include <Serializer.h>
#include <hardware/audio.h>
#include <media/AidlConversion.h>
#include <media/AidlConversionUtil.h>
#include <media/AudioProfile.h>
#include <system/audio.h>
#include <system/audio_config.h>
#include <utils/Log.h>
namespace android {
using media::audio::common::AudioDeviceAddress;
using media::audio::common::AudioDeviceType;
using media::audio::common::AudioIoFlags;
using media::audio::common::AudioPortDeviceExt;
using media::audio::common::AudioPortExt;
namespace {
ConversionResult<sp<PolicyAudioPort>>
aidl2legacy_portId_PolicyAudioPort(int32_t portId,
const std::unordered_map<int32_t, sp<PolicyAudioPort>>& ports) {
if (auto it = ports.find(portId); it != ports.end()) {
return it->second;
}
return base::unexpected(BAD_VALUE);
}
ConversionResult<sp<AudioRoute>>
aidl2legacy_AudioRoute(const media::AudioRoute& aidl,
const std::unordered_map<int32_t, sp<PolicyAudioPort>>& ports) {
auto legacy = sp<AudioRoute>::make(aidl.isExclusive ? AUDIO_ROUTE_MUX : AUDIO_ROUTE_MIX);
auto legacySink = VALUE_OR_RETURN(aidl2legacy_portId_PolicyAudioPort(aidl.sinkPortId, ports));
legacy->setSink(legacySink);
PolicyAudioPortVector legacySources;
for (int32_t portId : aidl.sourcePortIds) {
sp<PolicyAudioPort> legacyPort = VALUE_OR_RETURN(
aidl2legacy_portId_PolicyAudioPort(portId, ports));
legacySources.add(legacyPort);
}
legacy->setSources(legacySources);
legacySink->addRoute(legacy);
for (const auto& legacySource : legacySources) {
legacySource->addRoute(legacy);
}
return legacy;
}
status_t aidl2legacy_AudioHwModule_HwModule(const media::AudioHwModule& aidl,
sp<HwModule>* legacy,
DeviceVector* attachedInputDevices, DeviceVector* attachedOutputDevices,
sp<DeviceDescriptor>* defaultOutputDevice) {
*legacy = sp<HwModule>::make(aidl.name.c_str(), AUDIO_DEVICE_API_VERSION_CURRENT);
audio_module_handle_t legacyHandle = VALUE_OR_RETURN_STATUS(
aidl2legacy_int32_t_audio_module_handle_t(aidl.handle));
(*legacy)->setHandle(legacyHandle);
IOProfileCollection mixPorts;
DeviceVector devicePorts;
const int defaultDeviceFlag = 1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE;
std::unordered_map<int32_t, sp<PolicyAudioPort>> ports;
for (const auto& aidlPort : aidl.ports) {
const bool isInput = aidlPort.flags.getTag() == AudioIoFlags::input;
audio_port_v7 legacyPort = VALUE_OR_RETURN_STATUS(
aidl2legacy_AudioPort_audio_port_v7(aidlPort, isInput));
// This conversion fills out both 'hal' and 'sys' parts.
media::AudioPortFw fwPort = VALUE_OR_RETURN_STATUS(
legacy2aidl_audio_port_v7_AudioPortFw(legacyPort));
// Since audio_port_v7 lacks some fields, for example, 'maxOpen/ActiveCount',
// replace the converted data with the actual data from the HAL.
fwPort.hal = aidlPort;
if (aidlPort.ext.getTag() == AudioPortExt::mix) {
auto mixPort = sp<IOProfile>::make("", AUDIO_PORT_ROLE_NONE);
RETURN_STATUS_IF_ERROR(mixPort->readFromParcelable(fwPort));
auto& profiles = mixPort->getAudioProfiles();
if (profiles.empty()) {
profiles.add(AudioProfile::createFullDynamic(gDynamicFormat));
} else {
sortAudioProfiles(mixPort->getAudioProfiles());
}
mixPorts.add(mixPort);
ports.emplace(aidlPort.id, mixPort);
} else if (aidlPort.ext.getTag() == AudioPortExt::device) {
// In the legacy XML, device ports use 'tagName' instead of 'AudioPort.name'.
auto devicePort =
sp<DeviceDescriptor>::make(AUDIO_DEVICE_NONE, aidlPort.name);
RETURN_STATUS_IF_ERROR(devicePort->readFromParcelable(fwPort));
devicePort->setName("");
auto& profiles = devicePort->getAudioProfiles();
if (profiles.empty()) {
profiles.add(AudioProfile::createFullDynamic(gDynamicFormat));
} else {
sortAudioProfiles(profiles);
}
devicePorts.add(devicePort);
ports.emplace(aidlPort.id, devicePort);
if (const auto& deviceExt = aidlPort.ext.get<AudioPortExt::device>();
deviceExt.device.type.connection.empty() ||
// DeviceHalAidl connects remote submix input with an address.
(deviceExt.device.type.type == AudioDeviceType::IN_SUBMIX &&
deviceExt.device.address != AudioDeviceAddress())) {
// Attached device.
if (isInput) {
attachedInputDevices->add(devicePort);
} else {
attachedOutputDevices->add(devicePort);
if (*defaultOutputDevice == nullptr &&
(deviceExt.flags & defaultDeviceFlag) != 0) {
*defaultOutputDevice = devicePort;
}
}
}
} else {
return BAD_VALUE;
}
}
(*legacy)->setProfiles(mixPorts);
(*legacy)->setDeclaredDevices(devicePorts);
AudioRouteVector routes;
for (const auto& aidlRoute : aidl.routes) {
sp<AudioRoute> legacy = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioRoute(aidlRoute, ports));
routes.add(legacy);
}
(*legacy)->setRoutes(routes);
return OK;
}
status_t aidl2legacy_AudioHwModules_HwModuleCollection(
const std::vector<media::AudioHwModule>& aidl,
HwModuleCollection* legacyModules, DeviceVector* attachedInputDevices,
DeviceVector* attachedOutputDevices, sp<DeviceDescriptor>* defaultOutputDevice) {
for (const auto& aidlModule : aidl) {
sp<HwModule> legacy;
RETURN_STATUS_IF_ERROR(aidl2legacy_AudioHwModule_HwModule(aidlModule, &legacy,
attachedInputDevices, attachedOutputDevices, defaultOutputDevice));
legacyModules->add(legacy);
}
return OK;
}
using SurroundFormatFamily = AudioPolicyConfig::SurroundFormats::value_type;
ConversionResult<SurroundFormatFamily>
aidl2legacy_SurroundFormatFamily(const media::SurroundSoundConfig::SurroundFormatFamily& aidl) {
audio_format_t legacyPrimary = VALUE_OR_RETURN(
aidl2legacy_AudioFormatDescription_audio_format_t(aidl.primaryFormat));
std::unordered_set<audio_format_t> legacySubs = VALUE_OR_RETURN(
convertContainer<std::unordered_set<audio_format_t>>(
aidl.subFormats, aidl2legacy_AudioFormatDescription_audio_format_t));
return std::make_pair(legacyPrimary, legacySubs);
}
ConversionResult<AudioPolicyConfig::SurroundFormats>
aidl2legacy_SurroundSoundConfig_SurroundFormats(const media::SurroundSoundConfig& aidl) {
return convertContainer<AudioPolicyConfig::SurroundFormats>(aidl.formatFamilies,
aidl2legacy_SurroundFormatFamily);
};
} // namespace
// static
sp<const AudioPolicyConfig> AudioPolicyConfig::createDefault() {
auto config = sp<AudioPolicyConfig>::make();
config->setDefault();
return config;
}
// static
sp<const AudioPolicyConfig> AudioPolicyConfig::loadFromApmAidlConfigWithFallback(
const media::AudioPolicyConfig& aidl) {
auto config = sp<AudioPolicyConfig>::make();
if (status_t status = config->loadFromAidl(aidl); status == NO_ERROR) {
return config;
}
return createDefault();
}
// static
sp<const AudioPolicyConfig> AudioPolicyConfig::loadFromApmXmlConfigWithFallback(
const std::string& xmlFilePath) {
const std::string filePath =
xmlFilePath.empty() ? audio_get_audio_policy_config_file() : xmlFilePath;
auto config = sp<AudioPolicyConfig>::make();
if (status_t status = config->loadFromXml(filePath, false /*forVts*/); status == NO_ERROR) {
return config;
}
return createDefault();
}
// static
sp<AudioPolicyConfig> AudioPolicyConfig::createWritableForTests() {
return sp<AudioPolicyConfig>::make();
}
// static
error::Result<sp<AudioPolicyConfig>> AudioPolicyConfig::loadFromCustomXmlConfigForTests(
const std::string& xmlFilePath) {
auto config = sp<AudioPolicyConfig>::make();
if (status_t status = config->loadFromXml(xmlFilePath, false /*forVts*/); status == NO_ERROR) {
return config;
} else {
return base::unexpected(status);
}
}
// static
error::Result<sp<AudioPolicyConfig>> AudioPolicyConfig::loadFromCustomXmlConfigForVtsTests(
const std::string& configPath, const std::string& xmlFileName) {
auto filePath = configPath;
if (filePath.empty()) {
for (const auto& location : audio_get_configuration_paths()) {
std::string path = location + '/' + xmlFileName;
if (access(path.c_str(), F_OK) == 0) {
filePath = location;
break;
}
}
}
if (filePath.empty()) {
ALOGE("Did not find a config file \"%s\" among known config paths", xmlFileName.c_str());
return base::unexpected(BAD_VALUE);
}
auto config = sp<AudioPolicyConfig>::make();
if (status_t status = config->loadFromXml(filePath + "/" + xmlFileName, true /*forVts*/);
status == NO_ERROR) {
return config;
} else {
return base::unexpected(status);
}
}
void AudioPolicyConfig::augmentData() {
// If microphones address is empty, set it according to device type
for (size_t i = 0; i < mInputDevices.size(); i++) {
if (mInputDevices[i]->address().empty()) {
if (mInputDevices[i]->type() == AUDIO_DEVICE_IN_BUILTIN_MIC) {
mInputDevices[i]->setAddress(AUDIO_BOTTOM_MICROPHONE_ADDRESS);
} else if (mInputDevices[i]->type() == AUDIO_DEVICE_IN_BACK_MIC) {
mInputDevices[i]->setAddress(AUDIO_BACK_MICROPHONE_ADDRESS);
}
}
}
}
status_t AudioPolicyConfig::loadFromAidl(const media::AudioPolicyConfig& aidl) {
RETURN_STATUS_IF_ERROR(aidl2legacy_AudioHwModules_HwModuleCollection(aidl.modules,
&mHwModules, &mInputDevices, &mOutputDevices, &mDefaultOutputDevice));
mIsCallScreenModeSupported = std::find(aidl.supportedModes.begin(), aidl.supportedModes.end(),
media::audio::common::AudioMode::CALL_SCREEN) != aidl.supportedModes.end();
mSurroundFormats = VALUE_OR_RETURN_STATUS(
aidl2legacy_SurroundSoundConfig_SurroundFormats(aidl.surroundSoundConfig));
mSource = kAidlConfigSource;
// No need to augmentData() as AIDL HAL must provide correct mic addresses.
return NO_ERROR;
}
status_t AudioPolicyConfig::loadFromXml(const std::string& xmlFilePath, bool forVts) {
if (xmlFilePath.empty()) {
ALOGE("Audio policy configuration file name is empty");
return BAD_VALUE;
}
status_t status = forVts ? deserializeAudioPolicyFileForVts(xmlFilePath.c_str(), this)
: deserializeAudioPolicyFile(xmlFilePath.c_str(), this);
if (status == NO_ERROR) {
mSource = xmlFilePath;
augmentData();
} else {
ALOGE("Could not load audio policy from the configuration file \"%s\": %d",
xmlFilePath.c_str(), status);
}
return status;
}
void AudioPolicyConfig::setDefault() {
mSource = kDefaultConfigSource;
mEngineLibraryNameSuffix = kDefaultEngineLibraryNameSuffix;
mDefaultOutputDevice = new DeviceDescriptor(AUDIO_DEVICE_OUT_SPEAKER);
mDefaultOutputDevice->addAudioProfile(AudioProfile::createFullDynamic(gDynamicFormat));
sp<DeviceDescriptor> defaultInputDevice = new DeviceDescriptor(AUDIO_DEVICE_IN_BUILTIN_MIC);
defaultInputDevice->addAudioProfile(AudioProfile::createFullDynamic(gDynamicFormat));
sp<AudioProfile> micProfile = new AudioProfile(
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_MONO, 8000);
defaultInputDevice->addAudioProfile(micProfile);
mOutputDevices.add(mDefaultOutputDevice);
mInputDevices.add(defaultInputDevice);
sp<HwModule> module = new HwModule(
AUDIO_HARDWARE_MODULE_ID_PRIMARY, AUDIO_DEVICE_API_VERSION_2_0);
mHwModules.add(module);
sp<OutputProfile> outProfile = new OutputProfile("primary");
outProfile->addAudioProfile(
new AudioProfile(AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 44100));
outProfile->addSupportedDevice(mDefaultOutputDevice);
outProfile->setFlags(AUDIO_OUTPUT_FLAG_PRIMARY);
module->addOutputProfile(outProfile);
sp<InputProfile> inProfile = new InputProfile("primary");
inProfile->addAudioProfile(micProfile);
inProfile->addSupportedDevice(defaultInputDevice);
module->addInputProfile(inProfile);
setDefaultSurroundFormats();
augmentData();
}
void AudioPolicyConfig::setDefaultSurroundFormats() {
mSurroundFormats = {
{AUDIO_FORMAT_AC3, {}},
{AUDIO_FORMAT_E_AC3, {}},
{AUDIO_FORMAT_DTS, {}},
{AUDIO_FORMAT_DTS_HD, {}},
{AUDIO_FORMAT_DTS_HD_MA, {}},
{AUDIO_FORMAT_DTS_UHD, {}},
{AUDIO_FORMAT_DTS_UHD_P2, {}},
{AUDIO_FORMAT_AAC_LC, {
AUDIO_FORMAT_AAC_HE_V1, AUDIO_FORMAT_AAC_HE_V2, AUDIO_FORMAT_AAC_ELD,
AUDIO_FORMAT_AAC_XHE}},
{AUDIO_FORMAT_DOLBY_TRUEHD, {}},
{AUDIO_FORMAT_E_AC3_JOC, {}},
{AUDIO_FORMAT_AC4, {}}};
}
} // namespace android