ConfigParsingUtils.cpp revision e0db30868d9734f95a02df516dd0dc6a7226474b
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "APM::ConfigParsingUtils"
18//#define LOG_NDEBUG 0
19
20#include "ConfigParsingUtils.h"
21#include <convert/convert.h>
22#include "AudioGain.h"
23#include "IOProfile.h"
24#include "TypeConverter.h"
25#include <hardware/audio.h>
26#include <utils/Log.h>
27#include <cutils/misc.h>
28
29namespace android {
30
31// --- audio_policy.conf file parsing
32
33//static
34void ConfigParsingUtils::loadAudioPortGain(cnode *root, AudioPort &audioPort, int index)
35{
36    cnode *node = root->first_child;
37
38    sp<AudioGain> gain = new AudioGain(index, audioPort.useInputChannelMask());
39
40    while (node) {
41        if (strcmp(node->name, GAIN_MODE) == 0) {
42            gain->setMode(GainModeConverter::maskFromString(node->value));
43        } else if (strcmp(node->name, GAIN_CHANNELS) == 0) {
44            audio_channel_mask_t mask;
45            if (audioPort.useInputChannelMask()) {
46                if (InputChannelConverter::fromString(node->value, mask)) {
47                    gain->setChannelMask(mask);
48                }
49            } else {
50                if (OutputChannelConverter::fromString(node->value, mask)) {
51                    gain->setChannelMask(mask);
52                }
53            }
54        } else if (strcmp(node->name, GAIN_MIN_VALUE) == 0) {
55            gain->setMinValueInMb(atoi(node->value));
56        } else if (strcmp(node->name, GAIN_MAX_VALUE) == 0) {
57            gain->setMaxValueInMb(atoi(node->value));
58        } else if (strcmp(node->name, GAIN_DEFAULT_VALUE) == 0) {
59            gain->setDefaultValueInMb(atoi(node->value));
60        } else if (strcmp(node->name, GAIN_STEP_VALUE) == 0) {
61            gain->setStepValueInMb(atoi(node->value));
62        } else if (strcmp(node->name, GAIN_MIN_RAMP_MS) == 0) {
63            gain->setMinRampInMs(atoi(node->value));
64        } else if (strcmp(node->name, GAIN_MAX_RAMP_MS) == 0) {
65            gain->setMaxRampInMs(atoi(node->value));
66        }
67        node = node->next;
68    }
69
70    ALOGV("loadGain() adding new gain mode %08x channel mask %08x min mB %d max mB %d",
71          gain->getMode(), gain->getChannelMask(), gain->getMinValueInMb(),
72          gain->getMaxValueInMb());
73
74    if (gain->getMode() == 0) {
75        return;
76    }
77    audioPort.mGains.add(gain);
78}
79
80void ConfigParsingUtils::loadAudioPortGains(cnode *root, AudioPort &audioPort)
81{
82    cnode *node = root->first_child;
83    int index = 0;
84    while (node) {
85        ALOGV("loadGains() loading gain %s", node->name);
86        loadAudioPortGain(node, audioPort, index++);
87        node = node->next;
88    }
89}
90
91//static
92void ConfigParsingUtils::loadDeviceDescriptorGains(cnode *root, sp<DeviceDescriptor> &deviceDesc)
93{
94    loadAudioPortGains(root, *deviceDesc);
95    if (deviceDesc->mGains.size() > 0) {
96        deviceDesc->mGains[0]->getDefaultConfig(&deviceDesc->mGain);
97    }
98}
99
100//static
101status_t ConfigParsingUtils::loadHwModuleDevice(cnode *root, DeviceVector &devices)
102{
103    cnode *node = root->first_child;
104
105    audio_devices_t type = AUDIO_DEVICE_NONE;
106    while (node) {
107        if (strcmp(node->name, APM_DEVICE_TYPE) == 0) {
108            DeviceConverter::fromString(node->value, type);
109            break;
110        }
111        node = node->next;
112    }
113    if (type == AUDIO_DEVICE_NONE ||
114            (!audio_is_input_device(type) && !audio_is_output_device(type))) {
115        ALOGW("loadDevice() bad type %08x", type);
116        return BAD_VALUE;
117    }
118    sp<DeviceDescriptor> deviceDesc = new DeviceDescriptor(type, String8(root->name));
119
120    node = root->first_child;
121    while (node) {
122        if (strcmp(node->name, APM_DEVICE_ADDRESS) == 0) {
123            deviceDesc->mAddress = String8((char *)node->value);
124        } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
125            if (audio_is_input_device(type)) {
126                deviceDesc->addAudioProfile(
127                        new AudioProfile(gDynamicFormat,
128                                         inputChannelMasksFromString(node->value),
129                                         SampleRateVector()));
130            } else {
131                deviceDesc->addAudioProfile(
132                        new AudioProfile(gDynamicFormat,
133                                         outputChannelMasksFromString(node->value),
134                                         SampleRateVector()));
135            }
136        } else if (strcmp(node->name, GAINS_TAG) == 0) {
137            loadDeviceDescriptorGains(node, deviceDesc);
138        }
139        node = node->next;
140    }
141
142    ALOGV("loadDevice() adding device tag (literal type) %s type %08x address %s",
143          deviceDesc->getTagName().string(), type, deviceDesc->mAddress.string());
144
145    devices.add(deviceDesc);
146    return NO_ERROR;
147}
148
149//static
150status_t ConfigParsingUtils::loadHwModuleProfile(cnode *root, sp<HwModule> &module,
151                                                 audio_port_role_t role)
152{
153    cnode *node = root->first_child;
154
155    sp<IOProfile> profile = new IOProfile(String8(root->name), role);
156
157    AudioProfileVector audioProfiles;
158    SampleRateVector sampleRates;
159    ChannelsVector channels;
160    FormatVector formats;
161
162    while (node) {
163        if (strcmp(node->name, FORMATS_TAG) == 0 &&
164                strcmp(node->value, DYNAMIC_VALUE_TAG) != 0) {
165            formats = formatsFromString(node->value);
166        } else if (strcmp(node->name, SAMPLING_RATES_TAG) == 0 &&
167                  strcmp(node->value, DYNAMIC_VALUE_TAG) != 0) {
168            collectionFromString<SampleRateTraits>(node->value, sampleRates);
169        } else if (strcmp(node->name, CHANNELS_TAG) == 0 &&
170                   strcmp(node->value, DYNAMIC_VALUE_TAG) != 0) {
171            if (role == AUDIO_PORT_ROLE_SINK) {
172                channels = inputChannelMasksFromString(node->value);
173            } else {
174                channels = outputChannelMasksFromString(node->value);
175            }
176        } else if (strcmp(node->name, DEVICES_TAG) == 0) {
177            DeviceVector devices;
178            loadDevicesFromTag(node->value, devices, module->getDeclaredDevices());
179            profile->setSupportedDevices(devices);
180        } else if (strcmp(node->name, FLAGS_TAG) == 0) {
181            if (role == AUDIO_PORT_ROLE_SINK) {
182                profile->setFlags(InputFlagConverter::maskFromString(node->value));
183            } else {
184                profile->setFlags(OutputFlagConverter::maskFromString(node->value));
185            }
186        } else if (strcmp(node->name, GAINS_TAG) == 0) {
187            loadAudioPortGains(node, *profile);
188        }
189        node = node->next;
190    }
191    if (formats.isEmpty()) {
192        sp<AudioProfile> profileToAdd = new AudioProfile(gDynamicFormat, channels, sampleRates);
193        profileToAdd->setDynamicFormat(true);
194        profileToAdd->setDynamicChannels(channels.isEmpty());
195        profileToAdd->setDynamicRate(sampleRates.isEmpty());
196        audioProfiles.add(profileToAdd);
197    } else {
198        for (size_t i = 0; i < formats.size(); i++) {
199            // For compatibility reason, for each format, creates a profile with the same
200            // collection of rate and channels.
201            sp<AudioProfile> profileToAdd = new AudioProfile(formats[i], channels, sampleRates);
202            profileToAdd->setDynamicFormat(formats[i] == gDynamicFormat);
203            profileToAdd->setDynamicChannels(channels.isEmpty());
204            profileToAdd->setDynamicRate(sampleRates.isEmpty());
205            audioProfiles.add(profileToAdd);
206        }
207    }
208    profile->setAudioProfiles(audioProfiles);
209    ALOGW_IF(!profile->hasSupportedDevices(), "load%s() invalid supported devices",
210             role == AUDIO_PORT_ROLE_SINK ? "Input" : "Output");
211    if (profile->hasSupportedDevices()) {
212        ALOGV("load%s() adding Supported Devices %04x, mFlags %04x",
213              role == AUDIO_PORT_ROLE_SINK ? "Input" : "Output",
214              profile->getSupportedDevicesType(), profile->getFlags());
215        return module->addProfile(profile);
216    }
217    return BAD_VALUE;
218}
219
220//static
221status_t ConfigParsingUtils::loadHwModule(cnode *root, sp<HwModule> &module,
222                                          AudioPolicyConfig &config)
223{
224    status_t status = NAME_NOT_FOUND;
225    cnode *node = config_find(root, DEVICES_TAG);
226    if (node != NULL) {
227        node = node->first_child;
228        DeviceVector devices;
229        while (node) {
230            ALOGV("loadHwModule() loading device %s", node->name);
231            status_t tmpStatus = loadHwModuleDevice(node, devices);
232            if (status == NAME_NOT_FOUND || status == NO_ERROR) {
233                status = tmpStatus;
234            }
235            node = node->next;
236        }
237        module->setDeclaredDevices(devices);
238    }
239    node = config_find(root, OUTPUTS_TAG);
240    if (node != NULL) {
241        node = node->first_child;
242        while (node) {
243            ALOGV("loadHwModule() loading output %s", node->name);
244            status_t tmpStatus = loadHwModuleProfile(node, module, AUDIO_PORT_ROLE_SOURCE);
245            if (status == NAME_NOT_FOUND || status == NO_ERROR) {
246                status = tmpStatus;
247            }
248            node = node->next;
249        }
250    }
251    node = config_find(root, INPUTS_TAG);
252    if (node != NULL) {
253        node = node->first_child;
254        while (node) {
255            ALOGV("loadHwModule() loading input %s", node->name);
256            status_t tmpStatus = loadHwModuleProfile(node, module, AUDIO_PORT_ROLE_SINK);
257            if (status == NAME_NOT_FOUND || status == NO_ERROR) {
258                status = tmpStatus;
259            }
260            node = node->next;
261        }
262    }
263    loadModuleGlobalConfig(root, module, config);
264    return status;
265}
266
267//static
268void ConfigParsingUtils::loadHwModules(cnode *root, HwModuleCollection &hwModules,
269                                       AudioPolicyConfig &config)
270{
271    cnode *node = config_find(root, AUDIO_HW_MODULE_TAG);
272    if (node == NULL) {
273        return;
274    }
275
276    node = node->first_child;
277    while (node) {
278        ALOGV("loadHwModules() loading module %s", node->name);
279        sp<HwModule> module = new HwModule(node->name);
280        if (loadHwModule(node, module, config) == NO_ERROR) {
281            hwModules.add(module);
282        }
283        node = node->next;
284    }
285}
286
287//static
288void ConfigParsingUtils::loadDevicesFromTag(const char *tag, DeviceVector &devices,
289                                            const DeviceVector &declaredDevices)
290{
291    char *tagLiteral = strndup(tag, strlen(tag));
292    char *devTag = strtok(tagLiteral, "|");
293    while (devTag != NULL) {
294        if (strlen(devTag) != 0) {
295            audio_devices_t type;
296            if (DeviceConverter::fromString(devTag, type)) {
297                uint32_t inBit = type & AUDIO_DEVICE_BIT_IN;
298                type &= ~AUDIO_DEVICE_BIT_IN;
299                while (type) {
300                  audio_devices_t singleType =
301                        inBit | (1 << (31 - __builtin_clz(type)));
302                    type &= ~singleType;
303                    sp<DeviceDescriptor> dev = new DeviceDescriptor(singleType);
304                    devices.add(dev);
305                }
306            } else {
307                sp<DeviceDescriptor> deviceDesc =
308                        declaredDevices.getDeviceFromTagName(String8(devTag));
309                if (deviceDesc != 0) {
310                    devices.add(deviceDesc);
311                }
312            }
313        }
314        devTag = strtok(NULL, "|");
315    }
316    free(tagLiteral);
317}
318
319//static
320void ConfigParsingUtils::loadModuleGlobalConfig(cnode *root, const sp<HwModule> &module,
321                                                AudioPolicyConfig &config)
322{
323    cnode *node = config_find(root, GLOBAL_CONFIG_TAG);
324
325    if (node == NULL) {
326        return;
327    }
328    DeviceVector declaredDevices;
329    if (module != NULL) {
330        declaredDevices = module->getDeclaredDevices();
331    }
332
333    node = node->first_child;
334    while (node) {
335        if (strcmp(ATTACHED_OUTPUT_DEVICES_TAG, node->name) == 0) {
336            DeviceVector availableOutputDevices;
337            loadDevicesFromTag(node->value, availableOutputDevices, declaredDevices);
338            ALOGV("loadGlobalConfig() Attached Output Devices %08x",
339                  availableOutputDevices.types());
340            config.addAvailableOutputDevices(availableOutputDevices);
341        } else if (strcmp(DEFAULT_OUTPUT_DEVICE_TAG, node->name) == 0) {
342            audio_devices_t device = AUDIO_DEVICE_NONE;
343            DeviceConverter::fromString(node->value, device);
344            if (device != AUDIO_DEVICE_NONE) {
345                sp<DeviceDescriptor> defaultOutputDevice = new DeviceDescriptor(device);
346                config.setDefaultOutputDevice(defaultOutputDevice);
347                ALOGV("loadGlobalConfig() mDefaultOutputDevice %08x", defaultOutputDevice->type());
348            } else {
349                ALOGW("loadGlobalConfig() default device not specified");
350            }
351        } else if (strcmp(ATTACHED_INPUT_DEVICES_TAG, node->name) == 0) {
352            DeviceVector availableInputDevices;
353            loadDevicesFromTag(node->value, availableInputDevices, declaredDevices);
354            ALOGV("loadGlobalConfig() Available InputDevices %08x", availableInputDevices.types());
355            config.addAvailableInputDevices(availableInputDevices);
356        } else if (strcmp(AUDIO_HAL_VERSION_TAG, node->name) == 0) {
357            uint32_t major, minor;
358            sscanf((char *)node->value, "%u.%u", &major, &minor);
359            module->setHalVersion(HARDWARE_DEVICE_API_VERSION(major, minor));
360            ALOGV("loadGlobalConfig() mHalVersion = %04x major %u minor %u",
361                  module->getHalVersion(), major, minor);
362        }
363        node = node->next;
364    }
365}
366
367//static
368void ConfigParsingUtils::loadGlobalConfig(cnode *root, AudioPolicyConfig &config,
369                                          const sp<HwModule>& primaryModule)
370{
371    cnode *node = config_find(root, GLOBAL_CONFIG_TAG);
372
373    if (node == NULL) {
374        return;
375    }
376    node = node->first_child;
377    while (node) {
378        if (strcmp(SPEAKER_DRC_ENABLED_TAG, node->name) == 0) {
379            bool speakerDrcEnabled;
380            if (utilities::convertTo<std::string, bool>(node->value, speakerDrcEnabled)) {
381                ALOGV("loadGlobalConfig() mSpeakerDrcEnabled = %d", speakerDrcEnabled);
382                config.setSpeakerDrcEnabled(speakerDrcEnabled);
383            }
384        }
385        node = node->next;
386    }
387    loadModuleGlobalConfig(root, primaryModule, config);
388}
389
390//static
391status_t ConfigParsingUtils::loadConfig(const char *path, AudioPolicyConfig &config)
392{
393    cnode *root;
394    char *data;
395
396    data = (char *)load_file(path, NULL);
397    if (data == NULL) {
398        return -ENODEV;
399    }
400    root = config_node("", "");
401    config_load(root, data);
402
403    HwModuleCollection hwModules;
404    loadHwModules(root, hwModules, config);
405
406    // legacy audio_policy.conf files have one global_configuration section, attached to primary.
407    loadGlobalConfig(root, config, hwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY));
408
409    config.setHwModules(hwModules);
410
411    config_free(root);
412    free(root);
413    free(data);
414
415    ALOGI("loadAudioPolicyConfig() loaded %s\n", path);
416
417    return NO_ERROR;
418}
419
420}; // namespace android
421