| /* |
| * 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 |