blob: 89ef0458e30e521fe7e9ee5d4e67c53ce063f8c9 [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 <hardware/audio.h>
#include <utils/Log.h>
#include <cutils/misc.h>
namespace android {
//static
uint32_t ConfigParsingUtils::stringToEnum(const struct StringToEnum *table,
size_t size,
const char *name)
{
for (size_t i = 0; i < size; i++) {
if (strcmp(table[i].name, name) == 0) {
ALOGV("stringToEnum() found %s", table[i].name);
return table[i].value;
}
}
return 0;
}
//static
const char *ConfigParsingUtils::enumToString(const struct StringToEnum *table,
size_t size,
uint32_t value)
{
for (size_t i = 0; i < size; i++) {
if (table[i].value == value) {
return table[i].name;
}
}
return "";
}
//static
bool ConfigParsingUtils::stringToBool(const char *value)
{
return ((strcasecmp("true", value) == 0) || (strcmp("1", value) == 0));
}
// --- audio_policy.conf file parsing
//static
uint32_t ConfigParsingUtils::parseOutputFlagNames(char *name)
{
uint32_t flag = 0;
// it is OK to cast name to non const here as we are not going to use it after
// strtok() modifies it
char *flagName = strtok(name, "|");
while (flagName != NULL) {
if (strlen(flagName) != 0) {
flag |= ConfigParsingUtils::stringToEnum(sOutputFlagNameToEnumTable,
ARRAY_SIZE(sOutputFlagNameToEnumTable),
flagName);
}
flagName = strtok(NULL, "|");
}
//force direct flag if offload flag is set: offloading implies a direct output stream
// and all common behaviors are driven by checking only the direct flag
// this should normally be set appropriately in the policy configuration file
if ((flag & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
flag |= AUDIO_OUTPUT_FLAG_DIRECT;
}
return flag;
}
//static
uint32_t ConfigParsingUtils::parseInputFlagNames(char *name)
{
uint32_t flag = 0;
// it is OK to cast name to non const here as we are not going to use it after
// strtok() modifies it
char *flagName = strtok(name, "|");
while (flagName != NULL) {
if (strlen(flagName) != 0) {
flag |= stringToEnum(sInputFlagNameToEnumTable,
ARRAY_SIZE(sInputFlagNameToEnumTable),
flagName);
}
flagName = strtok(NULL, "|");
}
return flag;
}
//static
audio_devices_t ConfigParsingUtils::parseDeviceNames(char *name)
{
uint32_t device = 0;
char *devName = strtok(name, "|");
while (devName != NULL) {
if (strlen(devName) != 0) {
device |= stringToEnum(sDeviceTypeToEnumTable,
ARRAY_SIZE(sDeviceTypeToEnumTable),
devName);
}
devName = strtok(NULL, "|");
}
return device;
}
//static
void ConfigParsingUtils::loadHwModule(cnode *root, HwModuleCollection &hwModules,
DeviceVector &availableInputDevices,
DeviceVector &availableOutputDevices,
sp<DeviceDescriptor> &defaultOutputDevices,
bool &isSpeakerDrcEnable)
{
status_t status = NAME_NOT_FOUND;
cnode *node;
sp<HwModule> module = new HwModule(root->name);
node = config_find(root, DEVICES_TAG);
if (node != NULL) {
node = node->first_child;
while (node) {
ALOGV("loadHwModule() loading device %s", node->name);
status_t tmpStatus = module->loadDevice(node);
if (status == NAME_NOT_FOUND || status == NO_ERROR) {
status = tmpStatus;
}
node = node->next;
}
}
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 = module->loadOutput(node);
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 = module->loadInput(node);
if (status == NAME_NOT_FOUND || status == NO_ERROR) {
status = tmpStatus;
}
node = node->next;
}
}
loadGlobalConfig(root, module, availableInputDevices, availableOutputDevices,
defaultOutputDevices, isSpeakerDrcEnable);
if (status == NO_ERROR) {
hwModules.add(module);
}
}
//static
void ConfigParsingUtils::loadHwModules(cnode *root, HwModuleCollection &hwModules,
DeviceVector &availableInputDevices,
DeviceVector &availableOutputDevices,
sp<DeviceDescriptor> &defaultOutputDevices,
bool &isSpeakerDrcEnabled)
{
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);
loadHwModule(node, hwModules, availableInputDevices, availableOutputDevices,
defaultOutputDevices, isSpeakerDrcEnabled);
node = node->next;
}
}
//static
void ConfigParsingUtils::loadGlobalConfig(cnode *root, const sp<HwModule>& module,
DeviceVector &availableInputDevices,
DeviceVector &availableOutputDevices,
sp<DeviceDescriptor> &defaultOutputDevice,
bool &speakerDrcEnabled)
{
cnode *node = config_find(root, GLOBAL_CONFIG_TAG);
if (node == NULL) {
return;
}
DeviceVector declaredDevices;
if (module != NULL) {
declaredDevices = module->mDeclaredDevices;
}
node = node->first_child;
while (node) {
if (strcmp(ATTACHED_OUTPUT_DEVICES_TAG, node->name) == 0) {
availableOutputDevices.loadDevicesFromTag((char *)node->value,
declaredDevices);
ALOGV("loadGlobalConfig() Attached Output Devices %08x",
availableOutputDevices.types());
} else if (strcmp(DEFAULT_OUTPUT_DEVICE_TAG, node->name) == 0) {
audio_devices_t device = (audio_devices_t)stringToEnum(
sDeviceTypeToEnumTable,
ARRAY_SIZE(sDeviceTypeToEnumTable),
(char *)node->value);
if (device != AUDIO_DEVICE_NONE) {
defaultOutputDevice = new DeviceDescriptor(device);
} else {
ALOGW("loadGlobalConfig() default device not specified");
}
ALOGV("loadGlobalConfig() mDefaultOutputDevice %08x", defaultOutputDevice->type());
} else if (strcmp(ATTACHED_INPUT_DEVICES_TAG, node->name) == 0) {
availableInputDevices.loadDevicesFromTag((char *)node->value,
declaredDevices);
ALOGV("loadGlobalConfig() Available InputDevices %08x", availableInputDevices.types());
} else if (strcmp(SPEAKER_DRC_ENABLED_TAG, node->name) == 0) {
speakerDrcEnabled = stringToBool((char *)node->value);
ALOGV("loadGlobalConfig() mSpeakerDrcEnabled = %d", speakerDrcEnabled);
} else if (strcmp(AUDIO_HAL_VERSION_TAG, node->name) == 0) {
uint32_t major, minor;
sscanf((char *)node->value, "%u.%u", &major, &minor);
module->mHalVersion = HARDWARE_DEVICE_API_VERSION(major, minor);
ALOGV("loadGlobalConfig() mHalVersion = %04x major %u minor %u",
module->mHalVersion, major, minor);
}
node = node->next;
}
}
//static
status_t ConfigParsingUtils::loadAudioPolicyConfig(const char *path,
HwModuleCollection &hwModules,
DeviceVector &availableInputDevices,
DeviceVector &availableOutputDevices,
sp<DeviceDescriptor> &defaultOutputDevices,
bool &isSpeakerDrcEnabled)
{
cnode *root;
char *data;
data = (char *)load_file(path, NULL);
if (data == NULL) {
return -ENODEV;
}
root = config_node("", "");
config_load(root, data);
loadHwModules(root, hwModules,
availableInputDevices, availableOutputDevices,
defaultOutputDevices, isSpeakerDrcEnabled);
// legacy audio_policy.conf files have one global_configuration section
loadGlobalConfig(root, hwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY),
availableInputDevices, availableOutputDevices,
defaultOutputDevices, isSpeakerDrcEnabled);
config_free(root);
free(root);
free(data);
ALOGI("loadAudioPolicyConfig() loaded %s\n", path);
return NO_ERROR;
}
}; // namespace android