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 "AudioGain.h"
22 #include "IOProfile.h"
23 #include <system/audio.h>
24 #include <media/AudioParameter.h>
25 #include <media/TypeConverter.h>
26 #include <utils/Log.h>
27 #include <cutils/misc.h>
28
29 namespace android {
30
31 // --- audio_policy.conf file parsing
32
33 //static
loadAudioPortGain(cnode * root,AudioPort & audioPort,int index)34 void 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
loadAudioPortGains(cnode * root,AudioPort & audioPort)80 void 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
loadDeviceDescriptorGains(cnode * root,sp<DeviceDescriptor> & deviceDesc)92 void 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
loadHwModuleDevice(cnode * root,DeviceVector & devices)101 status_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 deviceFromString(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
loadHwModuleProfile(cnode * root,sp<HwModule> & module,audio_port_role_t role)150 status_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
loadHwModule(cnode * root,sp<HwModule> & module,AudioPolicyConfig & config)221 status_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
loadHwModules(cnode * root,HwModuleCollection & hwModules,AudioPolicyConfig & config)268 void 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
loadDevicesFromTag(const char * tag,DeviceVector & devices,const DeviceVector & declaredDevices)288 void ConfigParsingUtils::loadDevicesFromTag(const char *tag, DeviceVector &devices,
289 const DeviceVector &declaredDevices)
290 {
291 char *tagLiteral = strndup(tag, strlen(tag));
292 char *devTag = strtok(tagLiteral, AudioParameter::valueListSeparator);
293 while (devTag != NULL) {
294 if (strlen(devTag) != 0) {
295 audio_devices_t type;
296 if (deviceFromString(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, AudioParameter::valueListSeparator);
315 }
316 free(tagLiteral);
317 }
318
319 //static
loadModuleGlobalConfig(cnode * root,const sp<HwModule> & module,AudioPolicyConfig & config)320 void 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 deviceFromString(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(major, minor);
360 ALOGV("loadGlobalConfig() mHalVersion = major %u minor %u", major, minor);
361 }
362 node = node->next;
363 }
364 }
365
366 //static
loadGlobalConfig(cnode * root,AudioPolicyConfig & config,const sp<HwModule> & primaryModule)367 void ConfigParsingUtils::loadGlobalConfig(cnode *root, AudioPolicyConfig &config,
368 const sp<HwModule>& primaryModule)
369 {
370 cnode *node = config_find(root, GLOBAL_CONFIG_TAG);
371
372 if (node == NULL) {
373 return;
374 }
375 node = node->first_child;
376 while (node) {
377 if (strcmp(SPEAKER_DRC_ENABLED_TAG, node->name) == 0) {
378 bool speakerDrcEnabled;
379 if (utilities::convertTo<std::string, bool>(node->value, speakerDrcEnabled)) {
380 ALOGV("loadGlobalConfig() mSpeakerDrcEnabled = %d", speakerDrcEnabled);
381 config.setSpeakerDrcEnabled(speakerDrcEnabled);
382 }
383 }
384 node = node->next;
385 }
386 loadModuleGlobalConfig(root, primaryModule, config);
387 }
388
389 //static
loadConfig(const char * path,AudioPolicyConfig & config)390 status_t ConfigParsingUtils::loadConfig(const char *path, AudioPolicyConfig &config)
391 {
392 cnode *root;
393 char *data;
394
395 data = (char *)load_file(path, NULL);
396 if (data == NULL) {
397 return -ENODEV;
398 }
399 root = config_node("", "");
400 config_load(root, data);
401
402 HwModuleCollection hwModules;
403 loadHwModules(root, hwModules, config);
404
405 // legacy audio_policy.conf files have one global_configuration section, attached to primary.
406 loadGlobalConfig(root, config, hwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY));
407
408 config.setHwModules(hwModules);
409
410 config_free(root);
411 free(root);
412 free(data);
413
414 ALOGI("loadAudioPolicyConfig() loaded %s\n", path);
415
416 return NO_ERROR;
417 }
418
419 }; // namespace android
420