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 <hardware/audio.h>
23 #include <utils/Log.h>
24 #include <cutils/misc.h>
25 
26 namespace android {
27 
28 //static
stringToEnum(const struct StringToEnum * table,size_t size,const char * name)29 uint32_t ConfigParsingUtils::stringToEnum(const struct StringToEnum *table,
30                                               size_t size,
31                                               const char *name)
32 {
33     for (size_t i = 0; i < size; i++) {
34         if (strcmp(table[i].name, name) == 0) {
35             ALOGV("stringToEnum() found %s", table[i].name);
36             return table[i].value;
37         }
38     }
39     return 0;
40 }
41 
42 //static
enumToString(const struct StringToEnum * table,size_t size,uint32_t value)43 const char *ConfigParsingUtils::enumToString(const struct StringToEnum *table,
44                                               size_t size,
45                                               uint32_t value)
46 {
47     for (size_t i = 0; i < size; i++) {
48         if (table[i].value == value) {
49             return table[i].name;
50         }
51     }
52     return "";
53 }
54 
55 //static
stringToBool(const char * value)56 bool ConfigParsingUtils::stringToBool(const char *value)
57 {
58     return ((strcasecmp("true", value) == 0) || (strcmp("1", value) == 0));
59 }
60 
61 
62 // --- audio_policy.conf file parsing
63 //static
parseOutputFlagNames(char * name)64 uint32_t ConfigParsingUtils::parseOutputFlagNames(char *name)
65 {
66     uint32_t flag = 0;
67 
68     // it is OK to cast name to non const here as we are not going to use it after
69     // strtok() modifies it
70     char *flagName = strtok(name, "|");
71     while (flagName != NULL) {
72         if (strlen(flagName) != 0) {
73             flag |= ConfigParsingUtils::stringToEnum(sOutputFlagNameToEnumTable,
74                                ARRAY_SIZE(sOutputFlagNameToEnumTable),
75                                flagName);
76         }
77         flagName = strtok(NULL, "|");
78     }
79     //force direct flag if offload flag is set: offloading implies a direct output stream
80     // and all common behaviors are driven by checking only the direct flag
81     // this should normally be set appropriately in the policy configuration file
82     if ((flag & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
83         flag |= AUDIO_OUTPUT_FLAG_DIRECT;
84     }
85 
86     return flag;
87 }
88 
89 //static
parseInputFlagNames(char * name)90 uint32_t ConfigParsingUtils::parseInputFlagNames(char *name)
91 {
92     uint32_t flag = 0;
93 
94     // it is OK to cast name to non const here as we are not going to use it after
95     // strtok() modifies it
96     char *flagName = strtok(name, "|");
97     while (flagName != NULL) {
98         if (strlen(flagName) != 0) {
99             flag |= stringToEnum(sInputFlagNameToEnumTable,
100                                ARRAY_SIZE(sInputFlagNameToEnumTable),
101                                flagName);
102         }
103         flagName = strtok(NULL, "|");
104     }
105     return flag;
106 }
107 
108 //static
parseDeviceNames(char * name)109 audio_devices_t ConfigParsingUtils::parseDeviceNames(char *name)
110 {
111     uint32_t device = 0;
112 
113     char *devName = strtok(name, "|");
114     while (devName != NULL) {
115         if (strlen(devName) != 0) {
116             device |= stringToEnum(sDeviceTypeToEnumTable,
117                                  ARRAY_SIZE(sDeviceTypeToEnumTable),
118                                  devName);
119          }
120         devName = strtok(NULL, "|");
121      }
122     return device;
123 }
124 
125 //static
loadHwModule(cnode * root,HwModuleCollection & hwModules,DeviceVector & availableInputDevices,DeviceVector & availableOutputDevices,sp<DeviceDescriptor> & defaultOutputDevices,bool & isSpeakerDrcEnable)126 void ConfigParsingUtils::loadHwModule(cnode *root, HwModuleCollection &hwModules,
127                                       DeviceVector &availableInputDevices,
128                                       DeviceVector &availableOutputDevices,
129                                       sp<DeviceDescriptor> &defaultOutputDevices,
130                                       bool &isSpeakerDrcEnable)
131 {
132     status_t status = NAME_NOT_FOUND;
133     cnode *node;
134     sp<HwModule> module = new HwModule(root->name);
135 
136     node = config_find(root, DEVICES_TAG);
137     if (node != NULL) {
138         node = node->first_child;
139         while (node) {
140             ALOGV("loadHwModule() loading device %s", node->name);
141             status_t tmpStatus = module->loadDevice(node);
142             if (status == NAME_NOT_FOUND || status == NO_ERROR) {
143                 status = tmpStatus;
144             }
145             node = node->next;
146         }
147     }
148     node = config_find(root, OUTPUTS_TAG);
149     if (node != NULL) {
150         node = node->first_child;
151         while (node) {
152             ALOGV("loadHwModule() loading output %s", node->name);
153             status_t tmpStatus = module->loadOutput(node);
154             if (status == NAME_NOT_FOUND || status == NO_ERROR) {
155                 status = tmpStatus;
156             }
157             node = node->next;
158         }
159     }
160     node = config_find(root, INPUTS_TAG);
161     if (node != NULL) {
162         node = node->first_child;
163         while (node) {
164             ALOGV("loadHwModule() loading input %s", node->name);
165             status_t tmpStatus = module->loadInput(node);
166             if (status == NAME_NOT_FOUND || status == NO_ERROR) {
167                 status = tmpStatus;
168             }
169             node = node->next;
170         }
171     }
172     loadGlobalConfig(root, module, availableInputDevices, availableOutputDevices,
173                      defaultOutputDevices, isSpeakerDrcEnable);
174 
175     if (status == NO_ERROR) {
176         hwModules.add(module);
177     }
178 }
179 
180 //static
loadHwModules(cnode * root,HwModuleCollection & hwModules,DeviceVector & availableInputDevices,DeviceVector & availableOutputDevices,sp<DeviceDescriptor> & defaultOutputDevices,bool & isSpeakerDrcEnabled)181 void ConfigParsingUtils::loadHwModules(cnode *root, HwModuleCollection &hwModules,
182                                        DeviceVector &availableInputDevices,
183                                        DeviceVector &availableOutputDevices,
184                                        sp<DeviceDescriptor> &defaultOutputDevices,
185                                        bool &isSpeakerDrcEnabled)
186 {
187     cnode *node = config_find(root, AUDIO_HW_MODULE_TAG);
188     if (node == NULL) {
189         return;
190     }
191 
192     node = node->first_child;
193     while (node) {
194         ALOGV("loadHwModules() loading module %s", node->name);
195         loadHwModule(node, hwModules, availableInputDevices, availableOutputDevices,
196                      defaultOutputDevices, isSpeakerDrcEnabled);
197         node = node->next;
198     }
199 }
200 
201 //static
loadGlobalConfig(cnode * root,const sp<HwModule> & module,DeviceVector & availableInputDevices,DeviceVector & availableOutputDevices,sp<DeviceDescriptor> & defaultOutputDevice,bool & speakerDrcEnabled)202 void ConfigParsingUtils::loadGlobalConfig(cnode *root, const sp<HwModule>& module,
203                                           DeviceVector &availableInputDevices,
204                                           DeviceVector &availableOutputDevices,
205                                           sp<DeviceDescriptor> &defaultOutputDevice,
206                                           bool &speakerDrcEnabled)
207 {
208     cnode *node = config_find(root, GLOBAL_CONFIG_TAG);
209 
210     if (node == NULL) {
211         return;
212     }
213     DeviceVector declaredDevices;
214     if (module != NULL) {
215         declaredDevices = module->mDeclaredDevices;
216     }
217 
218     node = node->first_child;
219     while (node) {
220         if (strcmp(ATTACHED_OUTPUT_DEVICES_TAG, node->name) == 0) {
221             availableOutputDevices.loadDevicesFromTag((char *)node->value,
222                                                         declaredDevices);
223             ALOGV("loadGlobalConfig() Attached Output Devices %08x",
224                   availableOutputDevices.types());
225         } else if (strcmp(DEFAULT_OUTPUT_DEVICE_TAG, node->name) == 0) {
226             audio_devices_t device = (audio_devices_t)stringToEnum(
227                     sDeviceTypeToEnumTable,
228                     ARRAY_SIZE(sDeviceTypeToEnumTable),
229                     (char *)node->value);
230             if (device != AUDIO_DEVICE_NONE) {
231                 defaultOutputDevice = new DeviceDescriptor(device);
232             } else {
233                 ALOGW("loadGlobalConfig() default device not specified");
234             }
235             ALOGV("loadGlobalConfig() mDefaultOutputDevice %08x", defaultOutputDevice->type());
236         } else if (strcmp(ATTACHED_INPUT_DEVICES_TAG, node->name) == 0) {
237             availableInputDevices.loadDevicesFromTag((char *)node->value,
238                                                        declaredDevices);
239             ALOGV("loadGlobalConfig() Available InputDevices %08x", availableInputDevices.types());
240         } else if (strcmp(SPEAKER_DRC_ENABLED_TAG, node->name) == 0) {
241             speakerDrcEnabled = stringToBool((char *)node->value);
242             ALOGV("loadGlobalConfig() mSpeakerDrcEnabled = %d", speakerDrcEnabled);
243         } else if (strcmp(AUDIO_HAL_VERSION_TAG, node->name) == 0) {
244             uint32_t major, minor;
245             sscanf((char *)node->value, "%u.%u", &major, &minor);
246             module->mHalVersion = HARDWARE_DEVICE_API_VERSION(major, minor);
247             ALOGV("loadGlobalConfig() mHalVersion = %04x major %u minor %u",
248                   module->mHalVersion, major, minor);
249         }
250         node = node->next;
251     }
252 }
253 
254 //static
loadAudioPolicyConfig(const char * path,HwModuleCollection & hwModules,DeviceVector & availableInputDevices,DeviceVector & availableOutputDevices,sp<DeviceDescriptor> & defaultOutputDevices,bool & isSpeakerDrcEnabled)255 status_t ConfigParsingUtils::loadAudioPolicyConfig(const char *path,
256                                                    HwModuleCollection &hwModules,
257                                                    DeviceVector &availableInputDevices,
258                                                    DeviceVector &availableOutputDevices,
259                                                    sp<DeviceDescriptor> &defaultOutputDevices,
260                                                    bool &isSpeakerDrcEnabled)
261 {
262     cnode *root;
263     char *data;
264 
265     data = (char *)load_file(path, NULL);
266     if (data == NULL) {
267         return -ENODEV;
268     }
269     root = config_node("", "");
270     config_load(root, data);
271 
272     loadHwModules(root, hwModules,
273                   availableInputDevices, availableOutputDevices,
274                   defaultOutputDevices, isSpeakerDrcEnabled);
275     // legacy audio_policy.conf files have one global_configuration section
276     loadGlobalConfig(root, hwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY),
277                      availableInputDevices, availableOutputDevices,
278                      defaultOutputDevices, isSpeakerDrcEnabled);
279     config_free(root);
280     free(root);
281     free(data);
282 
283     ALOGI("loadAudioPolicyConfig() loaded %s\n", path);
284 
285     return NO_ERROR;
286 }
287 
288 }; // namespace android
289