/* * 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 #include "AudioGain.h" #include "IOProfile.h" #include "TypeConverter.h" #include #include #include namespace android { // --- audio_policy.conf file parsing //static void ConfigParsingUtils::loadAudioPortGain(cnode *root, AudioPort &audioPort, int index) { cnode *node = root->first_child; sp 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 &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) { DeviceConverter::fromString(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 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 &module, audio_port_role_t role) { cnode *node = root->first_child; sp 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(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 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 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 &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 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, "|"); while (devTag != NULL) { if (strlen(devTag) != 0) { audio_devices_t type; if (DeviceConverter::fromString(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 dev = new DeviceDescriptor(singleType); devices.add(dev); } } else { sp deviceDesc = declaredDevices.getDeviceFromTagName(String8(devTag)); if (deviceDesc != 0) { devices.add(deviceDesc); } } } devTag = strtok(NULL, "|"); } free(tagLiteral); } //static void ConfigParsingUtils::loadModuleGlobalConfig(cnode *root, const sp &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; DeviceConverter::fromString(node->value, device); if (device != AUDIO_DEVICE_NONE) { sp 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(HARDWARE_DEVICE_API_VERSION(major, minor)); ALOGV("loadGlobalConfig() mHalVersion = %04x major %u minor %u", module->getHalVersion(), major, minor); } node = node->next; } } //static void ConfigParsingUtils::loadGlobalConfig(cnode *root, AudioPolicyConfig &config, const sp& 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(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