blob: e5888e2f19d60be0ba008f45dab044dfbb31cc3b [file] [log] [blame]
/*
* Copyright (C) 2015 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::ConfigParsingUtils"
//#define LOG_NDEBUG 0
#include "ConfigParsingUtils.h"
#include "AudioGain.h"
#include "IOProfile.h"
#include <system/audio.h>
#include <media/AudioParameter.h>
#include <media/TypeConverter.h>
#include <utils/Log.h>
#include <cutils/misc.h>
namespace android {
// --- audio_policy.conf file parsing
//static
void ConfigParsingUtils::loadAudioPortGain(cnode *root, AudioPort &audioPort, int index)
{
cnode *node = root->first_child;
sp<AudioGain> gain = new AudioGain(index, audioPort.useInputChannelMask());
while (node) {
if (strcmp(node->name, GAIN_MODE) == 0) {
gain->setMode(GainModeConverter::maskFromString(node->value));
} else if (strcmp(node->name, GAIN_CHANNELS) == 0) {
audio_channel_mask_t mask;
if (audioPort.useInputChannelMask()) {
if (InputChannelConverter::fromString(node->value, mask)) {
gain->setChannelMask(mask);
}
} else {
if (OutputChannelConverter::fromString(node->value, mask)) {
gain->setChannelMask(mask);
}
}
} else if (strcmp(node->name, GAIN_MIN_VALUE) == 0) {
gain->setMinValueInMb(atoi(node->value));
} else if (strcmp(node->name, GAIN_MAX_VALUE) == 0) {
gain->setMaxValueInMb(atoi(node->value));
} else if (strcmp(node->name, GAIN_DEFAULT_VALUE) == 0) {
gain->setDefaultValueInMb(atoi(node->value));
} else if (strcmp(node->name, GAIN_STEP_VALUE) == 0) {
gain->setStepValueInMb(atoi(node->value));
} else if (strcmp(node->name, GAIN_MIN_RAMP_MS) == 0) {
gain->setMinRampInMs(atoi(node->value));
} else if (strcmp(node->name, GAIN_MAX_RAMP_MS) == 0) {
gain->setMaxRampInMs(atoi(node->value));
}
node = node->next;
}
ALOGV("loadGain() adding new gain mode %08x channel mask %08x min mB %d max mB %d",
gain->getMode(), gain->getChannelMask(), gain->getMinValueInMb(),
gain->getMaxValueInMb());
if (gain->getMode() == 0) {
return;
}
audioPort.mGains.add(gain);
}
void ConfigParsingUtils::loadAudioPortGains(cnode *root, AudioPort &audioPort)
{
cnode *node = root->first_child;
int index = 0;
while (node) {
ALOGV("loadGains() loading gain %s", node->name);
loadAudioPortGain(node, audioPort, index++);
node = node->next;
}
}
//static
void ConfigParsingUtils::loadDeviceDescriptorGains(cnode *root, sp<DeviceDescriptor> &deviceDesc)
{
loadAudioPortGains(root, *deviceDesc);
if (deviceDesc->mGains.size() > 0) {
deviceDesc->mGains[0]->getDefaultConfig(&deviceDesc->mGain);
}
}
//static
status_t ConfigParsingUtils::loadHwModuleDevice(cnode *root, DeviceVector &devices)
{
cnode *node = root->first_child;
audio_devices_t type = AUDIO_DEVICE_NONE;
while (node) {
if (strcmp(node->name, APM_DEVICE_TYPE) == 0) {
deviceFromString(node->value, type);
break;
}
node = node->next;
}
if (type == AUDIO_DEVICE_NONE ||
(!audio_is_input_device(type) && !audio_is_output_device(type))) {
ALOGW("loadDevice() bad type %08x", type);
return BAD_VALUE;
}
sp<DeviceDescriptor> deviceDesc = new DeviceDescriptor(type, String8(root->name));
node = root->first_child;
while (node) {
if (strcmp(node->name, APM_DEVICE_ADDRESS) == 0) {
deviceDesc->mAddress = String8((char *)node->value);
} else if (strcmp(node->name, CHANNELS_TAG) == 0) {
if (audio_is_input_device(type)) {
deviceDesc->addAudioProfile(
new AudioProfile(gDynamicFormat,
inputChannelMasksFromString(node->value),
SampleRateVector()));
} else {
deviceDesc->addAudioProfile(
new AudioProfile(gDynamicFormat,
outputChannelMasksFromString(node->value),
SampleRateVector()));
}
} else if (strcmp(node->name, GAINS_TAG) == 0) {
loadDeviceDescriptorGains(node, deviceDesc);
}
node = node->next;
}
ALOGV("loadDevice() adding device tag (literal type) %s type %08x address %s",
deviceDesc->getTagName().string(), type, deviceDesc->mAddress.string());
devices.add(deviceDesc);
return NO_ERROR;
}
//static
status_t ConfigParsingUtils::loadHwModuleProfile(cnode *root, sp<HwModule> &module,
audio_port_role_t role)
{
cnode *node = root->first_child;
sp<IOProfile> profile = new IOProfile(String8(root->name), role);
AudioProfileVector audioProfiles;
SampleRateVector sampleRates;
ChannelsVector channels;
FormatVector formats;
while (node) {
if (strcmp(node->name, FORMATS_TAG) == 0 &&
strcmp(node->value, DYNAMIC_VALUE_TAG) != 0) {
formats = formatsFromString(node->value);
} else if (strcmp(node->name, SAMPLING_RATES_TAG) == 0 &&
strcmp(node->value, DYNAMIC_VALUE_TAG) != 0) {
collectionFromString<SampleRateTraits>(node->value, sampleRates);
} else if (strcmp(node->name, CHANNELS_TAG) == 0 &&
strcmp(node->value, DYNAMIC_VALUE_TAG) != 0) {
if (role == AUDIO_PORT_ROLE_SINK) {
channels = inputChannelMasksFromString(node->value);
} else {
channels = outputChannelMasksFromString(node->value);
}
} else if (strcmp(node->name, DEVICES_TAG) == 0) {
DeviceVector devices;
loadDevicesFromTag(node->value, devices, module->getDeclaredDevices());
profile->setSupportedDevices(devices);
} else if (strcmp(node->name, FLAGS_TAG) == 0) {
if (role == AUDIO_PORT_ROLE_SINK) {
profile->setFlags(InputFlagConverter::maskFromString(node->value));
} else {
profile->setFlags(OutputFlagConverter::maskFromString(node->value));
}
} else if (strcmp(node->name, GAINS_TAG) == 0) {
loadAudioPortGains(node, *profile);
}
node = node->next;
}
if (formats.isEmpty()) {
sp<AudioProfile> profileToAdd = new AudioProfile(gDynamicFormat, channels, sampleRates);
profileToAdd->setDynamicFormat(true);
profileToAdd->setDynamicChannels(channels.isEmpty());
profileToAdd->setDynamicRate(sampleRates.isEmpty());
audioProfiles.add(profileToAdd);
} else {
for (size_t i = 0; i < formats.size(); i++) {
// For compatibility reason, for each format, creates a profile with the same
// collection of rate and channels.
sp<AudioProfile> profileToAdd = new AudioProfile(formats[i], channels, sampleRates);
profileToAdd->setDynamicFormat(formats[i] == gDynamicFormat);
profileToAdd->setDynamicChannels(channels.isEmpty());
profileToAdd->setDynamicRate(sampleRates.isEmpty());
audioProfiles.add(profileToAdd);
}
}
profile->setAudioProfiles(audioProfiles);
ALOGW_IF(!profile->hasSupportedDevices(), "load%s() invalid supported devices",
role == AUDIO_PORT_ROLE_SINK ? "Input" : "Output");
if (profile->hasSupportedDevices()) {
ALOGV("load%s() adding Supported Devices %04x, mFlags %04x",
role == AUDIO_PORT_ROLE_SINK ? "Input" : "Output",
profile->getSupportedDevicesType(), profile->getFlags());
return module->addProfile(profile);
}
return BAD_VALUE;
}
//static
status_t ConfigParsingUtils::loadHwModule(cnode *root, sp<HwModule> &module,
AudioPolicyConfig &config)
{
status_t status = NAME_NOT_FOUND;
cnode *node = config_find(root, DEVICES_TAG);
if (node != NULL) {
node = node->first_child;
DeviceVector devices;
while (node) {
ALOGV("loadHwModule() loading device %s", node->name);
status_t tmpStatus = loadHwModuleDevice(node, devices);
if (status == NAME_NOT_FOUND || status == NO_ERROR) {
status = tmpStatus;
}
node = node->next;
}
module->setDeclaredDevices(devices);
}
node = config_find(root, OUTPUTS_TAG);
if (node != NULL) {
node = node->first_child;
while (node) {
ALOGV("loadHwModule() loading output %s", node->name);
status_t tmpStatus = loadHwModuleProfile(node, module, AUDIO_PORT_ROLE_SOURCE);
if (status == NAME_NOT_FOUND || status == NO_ERROR) {
status = tmpStatus;
}
node = node->next;
}
}
node = config_find(root, INPUTS_TAG);
if (node != NULL) {
node = node->first_child;
while (node) {
ALOGV("loadHwModule() loading input %s", node->name);
status_t tmpStatus = loadHwModuleProfile(node, module, AUDIO_PORT_ROLE_SINK);
if (status == NAME_NOT_FOUND || status == NO_ERROR) {
status = tmpStatus;
}
node = node->next;
}
}
loadModuleGlobalConfig(root, module, config);
return status;
}
//static
void ConfigParsingUtils::loadHwModules(cnode *root, HwModuleCollection &hwModules,
AudioPolicyConfig &config)
{
cnode *node = config_find(root, AUDIO_HW_MODULE_TAG);
if (node == NULL) {
return;
}
node = node->first_child;
while (node) {
ALOGV("loadHwModules() loading module %s", node->name);
sp<HwModule> module = new HwModule(node->name);
if (loadHwModule(node, module, config) == NO_ERROR) {
hwModules.add(module);
}
node = node->next;
}
}
//static
void ConfigParsingUtils::loadDevicesFromTag(const char *tag, DeviceVector &devices,
const DeviceVector &declaredDevices)
{
char *tagLiteral = strndup(tag, strlen(tag));
char *devTag = strtok(tagLiteral, AudioParameter::valueListSeparator);
while (devTag != NULL) {
if (strlen(devTag) != 0) {
audio_devices_t type;
if (deviceFromString(devTag, type)) {
uint32_t inBit = type & AUDIO_DEVICE_BIT_IN;
type &= ~AUDIO_DEVICE_BIT_IN;
while (type) {
audio_devices_t singleType =
inBit | (1 << (31 - __builtin_clz(type)));
type &= ~singleType;
sp<DeviceDescriptor> dev = new DeviceDescriptor(singleType);
devices.add(dev);
}
} else {
sp<DeviceDescriptor> deviceDesc =
declaredDevices.getDeviceFromTagName(String8(devTag));
if (deviceDesc != 0) {
devices.add(deviceDesc);
}
}
}
devTag = strtok(NULL, AudioParameter::valueListSeparator);
}
free(tagLiteral);
}
//static
void ConfigParsingUtils::loadModuleGlobalConfig(cnode *root, const sp<HwModule> &module,
AudioPolicyConfig &config)
{
cnode *node = config_find(root, GLOBAL_CONFIG_TAG);
if (node == NULL) {
return;
}
DeviceVector declaredDevices;
if (module != NULL) {
declaredDevices = module->getDeclaredDevices();
}
node = node->first_child;
while (node) {
if (strcmp(ATTACHED_OUTPUT_DEVICES_TAG, node->name) == 0) {
DeviceVector availableOutputDevices;
loadDevicesFromTag(node->value, availableOutputDevices, declaredDevices);
ALOGV("loadGlobalConfig() Attached Output Devices %08x",
availableOutputDevices.types());
config.addAvailableOutputDevices(availableOutputDevices);
} else if (strcmp(DEFAULT_OUTPUT_DEVICE_TAG, node->name) == 0) {
audio_devices_t device = AUDIO_DEVICE_NONE;
deviceFromString(node->value, device);
if (device != AUDIO_DEVICE_NONE) {
sp<DeviceDescriptor> defaultOutputDevice = new DeviceDescriptor(device);
config.setDefaultOutputDevice(defaultOutputDevice);
ALOGV("loadGlobalConfig() mDefaultOutputDevice %08x", defaultOutputDevice->type());
} else {
ALOGW("loadGlobalConfig() default device not specified");
}
} else if (strcmp(ATTACHED_INPUT_DEVICES_TAG, node->name) == 0) {
DeviceVector availableInputDevices;
loadDevicesFromTag(node->value, availableInputDevices, declaredDevices);
ALOGV("loadGlobalConfig() Available InputDevices %08x", availableInputDevices.types());
config.addAvailableInputDevices(availableInputDevices);
} else if (strcmp(AUDIO_HAL_VERSION_TAG, node->name) == 0) {
uint32_t major, minor;
sscanf((char *)node->value, "%u.%u", &major, &minor);
module->setHalVersion(major, minor);
ALOGV("loadGlobalConfig() mHalVersion = major %u minor %u", major, minor);
}
node = node->next;
}
}
//static
void ConfigParsingUtils::loadGlobalConfig(cnode *root, AudioPolicyConfig &config,
const sp<HwModule>& primaryModule)
{
cnode *node = config_find(root, GLOBAL_CONFIG_TAG);
if (node == NULL) {
return;
}
node = node->first_child;
while (node) {
if (strcmp(SPEAKER_DRC_ENABLED_TAG, node->name) == 0) {
bool speakerDrcEnabled;
if (utilities::convertTo<std::string, bool>(node->value, speakerDrcEnabled)) {
ALOGV("loadGlobalConfig() mSpeakerDrcEnabled = %d", speakerDrcEnabled);
config.setSpeakerDrcEnabled(speakerDrcEnabled);
}
}
node = node->next;
}
loadModuleGlobalConfig(root, primaryModule, config);
}
//static
status_t ConfigParsingUtils::loadConfig(const char *path, AudioPolicyConfig &config)
{
cnode *root;
char *data;
data = (char *)load_file(path, NULL);
if (data == NULL) {
return -ENODEV;
}
root = config_node("", "");
config_load(root, data);
HwModuleCollection hwModules;
loadHwModules(root, hwModules, config);
// legacy audio_policy.conf files have one global_configuration section, attached to primary.
loadGlobalConfig(root, config, hwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY));
config.setHwModules(hwModules);
config_free(root);
free(root);
free(data);
ALOGI("loadAudioPolicyConfig() loaded %s\n", path);
return NO_ERROR;
}
}; // namespace android