| Kevin Rocard | 97b5448 | 2017-06-23 16:11:03 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright (C) 2017 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 "EffectsFactoryConfigLoader" | 
 | 18 | //#define LOG_NDEBUG 0 | 
 | 19 |  | 
 | 20 | #include <dlfcn.h> | 
 | 21 | #include <stdlib.h> | 
 | 22 |  | 
 | 23 | #include <cutils/config_utils.h> | 
 | 24 | #include <cutils/misc.h> | 
 | 25 | #include <log/log.h> | 
 | 26 |  | 
 | 27 | #include <system/audio_effects/audio_effects_conf.h> | 
 | 28 |  | 
 | 29 | #include "EffectsConfigLoader.h" | 
 | 30 | #include "EffectsFactoryState.h" | 
 | 31 |  | 
 | 32 | ///////////////////////////////////////////////// | 
 | 33 | //      Local functions prototypes | 
 | 34 | ///////////////////////////////////////////////// | 
 | 35 |  | 
 | 36 | static int loadEffectConfigFile(const char *path); | 
 | 37 | static int loadLibraries(cnode *root); | 
 | 38 | static int loadLibrary(cnode *root, const char *name); | 
 | 39 | static int loadEffects(cnode *root); | 
 | 40 | static int loadEffect(cnode *node); | 
 | 41 | // To get and add the effect pointed by the passed node to the gSubEffectList | 
 | 42 | static int addSubEffect(cnode *root); | 
 | 43 | static lib_entry_t *getLibrary(const char *path); | 
 | 44 |  | 
 | 45 | static lib_entry_t *gCachedLibrary;  // last library accessed by getLibrary() | 
 | 46 |  | 
| Kevin Rocard | 41af42b | 2017-06-23 12:10:07 -0700 | [diff] [blame^] | 47 | int EffectLoadEffectConfig() | 
| Kevin Rocard | 97b5448 | 2017-06-23 16:11:03 -0700 | [diff] [blame] | 48 | { | 
 | 49 |     if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) { | 
 | 50 |         return loadEffectConfigFile(AUDIO_EFFECT_VENDOR_CONFIG_FILE); | 
 | 51 |     } else if (access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) { | 
 | 52 |         return loadEffectConfigFile(AUDIO_EFFECT_DEFAULT_CONFIG_FILE); | 
 | 53 |     } | 
 | 54 |     return 0; | 
 | 55 | } | 
 | 56 |  | 
 | 57 | int loadEffectConfigFile(const char *path) | 
 | 58 | { | 
 | 59 |     cnode *root; | 
 | 60 |     char *data; | 
 | 61 |  | 
 | 62 |     data = load_file(path, NULL); | 
 | 63 |     if (data == NULL) { | 
 | 64 |         return -ENODEV; | 
 | 65 |     } | 
 | 66 |     root = config_node("", ""); | 
 | 67 |     config_load(root, data); | 
 | 68 |     loadLibraries(root); | 
 | 69 |     loadEffects(root); | 
 | 70 |     config_free(root); | 
 | 71 |     free(root); | 
 | 72 |     free(data); | 
 | 73 |  | 
 | 74 |     return 0; | 
 | 75 | } | 
 | 76 |  | 
 | 77 | int loadLibraries(cnode *root) | 
 | 78 | { | 
 | 79 |     cnode *node; | 
 | 80 |  | 
 | 81 |     node = config_find(root, LIBRARIES_TAG); | 
 | 82 |     if (node == NULL) { | 
 | 83 |         return -ENOENT; | 
 | 84 |     } | 
 | 85 |     node = node->first_child; | 
 | 86 |     while (node) { | 
 | 87 |         loadLibrary(node, node->name); | 
 | 88 |         node = node->next; | 
 | 89 |     } | 
 | 90 |     return 0; | 
 | 91 | } | 
 | 92 |  | 
 | 93 | #ifdef __LP64__ | 
 | 94 | // audio_effects.conf always specifies 32 bit lib path: convert to 64 bit path if needed | 
 | 95 | static const char *kLibraryPathRoot[] = | 
 | 96 |         {"/odm/lib64/soundfx", "/vendor/lib64/soundfx", "/system/lib64/soundfx"}; | 
 | 97 | #else | 
 | 98 | static const char *kLibraryPathRoot[] = | 
 | 99 |         {"/odm/lib/soundfx", "/vendor/lib/soundfx", "/system/lib/soundfx"}; | 
 | 100 | #endif | 
 | 101 |  | 
 | 102 | static const int kLibraryPathRootSize = | 
 | 103 |         (sizeof(kLibraryPathRoot) / sizeof(kLibraryPathRoot[0])); | 
 | 104 |  | 
 | 105 | // Checks if the library path passed as lib_path_in can be opened and if not | 
 | 106 | // tries in standard effect library directories with just the library name and returns correct path | 
 | 107 | // in lib_path_out | 
 | 108 | int checkLibraryPath(const char *lib_path_in, char *lib_path_out) { | 
 | 109 |     char *str; | 
 | 110 |     const char *lib_name; | 
 | 111 |     size_t len; | 
 | 112 |  | 
 | 113 |     if (lib_path_in == NULL || lib_path_out == NULL) { | 
 | 114 |         return -EINVAL; | 
 | 115 |     } | 
 | 116 |  | 
 | 117 |     strlcpy(lib_path_out, lib_path_in, PATH_MAX); | 
 | 118 |  | 
 | 119 |     // Try exact path first | 
 | 120 |     str = strstr(lib_path_out, "/lib/soundfx/"); | 
 | 121 |     if (str == NULL) { | 
 | 122 |         return -EINVAL; | 
 | 123 |     } | 
 | 124 |  | 
 | 125 |     // Extract library name from input path | 
 | 126 |     len = str - lib_path_out; | 
 | 127 |     lib_name = lib_path_in + len + strlen("/lib/soundfx/"); | 
 | 128 |  | 
 | 129 |     // Then try with library name and standard path names in order of preference | 
 | 130 |     for (int i = 0; i < kLibraryPathRootSize; i++) { | 
 | 131 |         char path[PATH_MAX]; | 
 | 132 |  | 
 | 133 |         snprintf(path, | 
 | 134 |                  PATH_MAX, | 
 | 135 |                  "%s/%s", | 
 | 136 |                  kLibraryPathRoot[i], | 
 | 137 |                  lib_name); | 
 | 138 |         if (F_OK == access(path, 0)) { | 
 | 139 |             strcpy(lib_path_out, path); | 
 | 140 |             ALOGW_IF(strncmp(lib_path_out, lib_path_in, PATH_MAX) != 0, | 
 | 141 |                 "checkLibraryPath() corrected library path %s to %s", lib_path_in, lib_path_out); | 
 | 142 |             return 0; | 
 | 143 |         } | 
 | 144 |     } | 
 | 145 |     return -EINVAL; | 
 | 146 | } | 
 | 147 |  | 
 | 148 |  | 
 | 149 |  | 
 | 150 | int loadLibrary(cnode *root, const char *name) | 
 | 151 | { | 
 | 152 |     cnode *node; | 
 | 153 |     void *hdl = NULL; | 
 | 154 |     audio_effect_library_t *desc; | 
 | 155 |     list_elem_t *e; | 
 | 156 |     lib_entry_t *l; | 
 | 157 |     char path[PATH_MAX]; | 
 | 158 |  | 
 | 159 |     node = config_find(root, PATH_TAG); | 
 | 160 |     if (node == NULL) { | 
 | 161 |         return -EINVAL; | 
 | 162 |     } | 
 | 163 |  | 
 | 164 |     if (checkLibraryPath((const char *)node->value, path) != 0) { | 
 | 165 |         ALOGW("loadLibrary() could not find library %s", path); | 
 | 166 |         goto error; | 
 | 167 |     } | 
 | 168 |  | 
 | 169 |     hdl = dlopen(path, RTLD_NOW); | 
 | 170 |     if (hdl == NULL) { | 
 | 171 |         ALOGW("loadLibrary() failed to open %s", path); | 
 | 172 |         goto error; | 
 | 173 |     } | 
 | 174 |  | 
 | 175 |     desc = (audio_effect_library_t *)dlsym(hdl, AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR); | 
 | 176 |     if (desc == NULL) { | 
 | 177 |         ALOGW("loadLibrary() could not find symbol %s", AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR); | 
 | 178 |         goto error; | 
 | 179 |     } | 
 | 180 |  | 
 | 181 |     if (AUDIO_EFFECT_LIBRARY_TAG != desc->tag) { | 
 | 182 |         ALOGW("getLibrary() bad tag %08x in lib info struct", desc->tag); | 
 | 183 |         goto error; | 
 | 184 |     } | 
 | 185 |  | 
 | 186 |     if (EFFECT_API_VERSION_MAJOR(desc->version) != | 
 | 187 |             EFFECT_API_VERSION_MAJOR(EFFECT_LIBRARY_API_VERSION)) { | 
 | 188 |         ALOGW("loadLibrary() bad lib version %08x", desc->version); | 
 | 189 |         goto error; | 
 | 190 |     } | 
 | 191 |  | 
 | 192 |     // add entry for library in gLibraryList | 
 | 193 |     l = malloc(sizeof(lib_entry_t)); | 
 | 194 |     l->name = strndup(name, PATH_MAX); | 
 | 195 |     l->path = strndup(path, PATH_MAX); | 
 | 196 |     l->handle = hdl; | 
 | 197 |     l->desc = desc; | 
 | 198 |     l->effects = NULL; | 
 | 199 |     pthread_mutex_init(&l->lock, NULL); | 
 | 200 |  | 
 | 201 |     e = malloc(sizeof(list_elem_t)); | 
 | 202 |     e->object = l; | 
 | 203 |     pthread_mutex_lock(&gLibLock); | 
 | 204 |     e->next = gLibraryList; | 
 | 205 |     gLibraryList = e; | 
 | 206 |     pthread_mutex_unlock(&gLibLock); | 
 | 207 |     ALOGV("getLibrary() linked library %p for path %s", l, path); | 
 | 208 |  | 
 | 209 |     return 0; | 
 | 210 |  | 
 | 211 | error: | 
 | 212 |     if (hdl != NULL) { | 
 | 213 |         dlclose(hdl); | 
 | 214 |     } | 
 | 215 |     //add entry for library errors in gLibraryFailedList | 
 | 216 |     lib_failed_entry_t *fl = malloc(sizeof(lib_failed_entry_t)); | 
 | 217 |     fl->name = strndup(name, PATH_MAX); | 
 | 218 |     fl->path = strndup(path, PATH_MAX); | 
 | 219 |  | 
 | 220 |     list_elem_t *fe = malloc(sizeof(list_elem_t)); | 
 | 221 |     fe->object = fl; | 
 | 222 |     fe->next = gLibraryFailedList; | 
 | 223 |     gLibraryFailedList = fe; | 
 | 224 |     ALOGV("getLibrary() linked error in library %p for path %s", fl, path); | 
 | 225 |  | 
 | 226 |     return -EINVAL; | 
 | 227 | } | 
 | 228 |  | 
 | 229 | // This will find the library and UUID tags of the sub effect pointed by the | 
 | 230 | // node, gets the effect descriptor and lib_entry_t and adds the subeffect - | 
 | 231 | // sub_entry_t to the gSubEffectList | 
 | 232 | int addSubEffect(cnode *root) | 
 | 233 | { | 
 | 234 |     ALOGV("addSubEffect"); | 
 | 235 |     cnode *node; | 
 | 236 |     effect_uuid_t uuid; | 
 | 237 |     effect_descriptor_t *d; | 
 | 238 |     lib_entry_t *l; | 
 | 239 |     list_elem_t *e; | 
 | 240 |     node = config_find(root, LIBRARY_TAG); | 
 | 241 |     if (node == NULL) { | 
 | 242 |         return -EINVAL; | 
 | 243 |     } | 
 | 244 |     l = getLibrary(node->value); | 
 | 245 |     if (l == NULL) { | 
 | 246 |         ALOGW("addSubEffect() could not get library %s", node->value); | 
 | 247 |         return -EINVAL; | 
 | 248 |     } | 
 | 249 |     node = config_find(root, UUID_TAG); | 
 | 250 |     if (node == NULL) { | 
 | 251 |         return -EINVAL; | 
 | 252 |     } | 
 | 253 |     if (stringToUuid(node->value, &uuid) != 0) { | 
 | 254 |         ALOGW("addSubEffect() invalid uuid %s", node->value); | 
 | 255 |         return -EINVAL; | 
 | 256 |     } | 
 | 257 |     d = malloc(sizeof(effect_descriptor_t)); | 
 | 258 |     if (l->desc->get_descriptor(&uuid, d) != 0) { | 
 | 259 |         char s[40]; | 
 | 260 |         uuidToString(&uuid, s, 40); | 
 | 261 |         ALOGW("Error querying effect %s on lib %s", s, l->name); | 
 | 262 |         free(d); | 
 | 263 |         return -EINVAL; | 
 | 264 |     } | 
 | 265 | #if (LOG_NDEBUG==0) | 
 | 266 |     char s[512]; | 
 | 267 |     dumpEffectDescriptor(d, s, sizeof(s), 0 /* indent */); | 
 | 268 |     ALOGV("addSubEffect() read descriptor %p:%s",d, s); | 
 | 269 | #endif | 
 | 270 |     if (EFFECT_API_VERSION_MAJOR(d->apiVersion) != | 
 | 271 |             EFFECT_API_VERSION_MAJOR(EFFECT_CONTROL_API_VERSION)) { | 
 | 272 |         ALOGW("Bad API version %08x on lib %s", d->apiVersion, l->name); | 
 | 273 |         free(d); | 
 | 274 |         return -EINVAL; | 
 | 275 |     } | 
 | 276 |     sub_effect_entry_t *sub_effect = malloc(sizeof(sub_effect_entry_t)); | 
 | 277 |     sub_effect->object = d; | 
 | 278 |     // lib_entry_t is stored since the sub effects are not linked to the library | 
 | 279 |     sub_effect->lib = l; | 
 | 280 |     e = malloc(sizeof(list_elem_t)); | 
 | 281 |     e->object = sub_effect; | 
 | 282 |     e->next = gSubEffectList->sub_elem; | 
 | 283 |     gSubEffectList->sub_elem = e; | 
 | 284 |     ALOGV("addSubEffect end"); | 
 | 285 |     return 0; | 
 | 286 | } | 
 | 287 |  | 
 | 288 | int loadEffects(cnode *root) | 
 | 289 | { | 
 | 290 |     cnode *node; | 
 | 291 |  | 
 | 292 |     node = config_find(root, EFFECTS_TAG); | 
 | 293 |     if (node == NULL) { | 
 | 294 |         return -ENOENT; | 
 | 295 |     } | 
 | 296 |     node = node->first_child; | 
 | 297 |     while (node) { | 
 | 298 |         loadEffect(node); | 
 | 299 |         node = node->next; | 
 | 300 |     } | 
 | 301 |     return 0; | 
 | 302 | } | 
 | 303 |  | 
 | 304 | int loadEffect(cnode *root) | 
 | 305 | { | 
 | 306 |     cnode *node; | 
 | 307 |     effect_uuid_t uuid; | 
 | 308 |     lib_entry_t *l; | 
 | 309 |     effect_descriptor_t *d; | 
 | 310 |     list_elem_t *e; | 
 | 311 |  | 
 | 312 |     node = config_find(root, LIBRARY_TAG); | 
 | 313 |     if (node == NULL) { | 
 | 314 |         return -EINVAL; | 
 | 315 |     } | 
 | 316 |  | 
 | 317 |     l = getLibrary(node->value); | 
 | 318 |     if (l == NULL) { | 
 | 319 |         ALOGW("loadEffect() could not get library %s", node->value); | 
 | 320 |         return -EINVAL; | 
 | 321 |     } | 
 | 322 |  | 
 | 323 |     node = config_find(root, UUID_TAG); | 
 | 324 |     if (node == NULL) { | 
 | 325 |         return -EINVAL; | 
 | 326 |     } | 
 | 327 |     if (stringToUuid(node->value, &uuid) != 0) { | 
 | 328 |         ALOGW("loadEffect() invalid uuid %s", node->value); | 
 | 329 |         return -EINVAL; | 
 | 330 |     } | 
 | 331 |     lib_entry_t *tmp; | 
 | 332 |     bool skip = false; | 
 | 333 |     if (findEffect(NULL, &uuid, &tmp, NULL) == 0) { | 
 | 334 |         ALOGW("skipping duplicate uuid %s %s", node->value, | 
 | 335 |                 node->next ? "and its sub-effects" : ""); | 
 | 336 |         skip = true; | 
 | 337 |     } | 
 | 338 |  | 
 | 339 |     d = malloc(sizeof(effect_descriptor_t)); | 
 | 340 |     if (l->desc->get_descriptor(&uuid, d) != 0) { | 
 | 341 |         char s[40]; | 
 | 342 |         uuidToString(&uuid, s, 40); | 
 | 343 |         ALOGW("Error querying effect %s on lib %s", s, l->name); | 
 | 344 |         free(d); | 
 | 345 |         return -EINVAL; | 
 | 346 |     } | 
 | 347 | #if (LOG_NDEBUG==0) | 
 | 348 |     char s[512]; | 
 | 349 |     dumpEffectDescriptor(d, s, sizeof(s), 0 /* indent */); | 
 | 350 |     ALOGV("loadEffect() read descriptor %p:%s",d, s); | 
 | 351 | #endif | 
 | 352 |     if (EFFECT_API_VERSION_MAJOR(d->apiVersion) != | 
 | 353 |             EFFECT_API_VERSION_MAJOR(EFFECT_CONTROL_API_VERSION)) { | 
 | 354 |         ALOGW("Bad API version %08x on lib %s", d->apiVersion, l->name); | 
 | 355 |         free(d); | 
 | 356 |         return -EINVAL; | 
 | 357 |     } | 
 | 358 |     e = malloc(sizeof(list_elem_t)); | 
 | 359 |     e->object = d; | 
 | 360 |     if (skip) { | 
 | 361 |         e->next = gSkippedEffects; | 
 | 362 |         gSkippedEffects = e; | 
 | 363 |         return -EINVAL; | 
 | 364 |     } else { | 
 | 365 |         e->next = l->effects; | 
 | 366 |         l->effects = e; | 
 | 367 |     } | 
 | 368 |  | 
 | 369 |     // After the UUID node in the config_tree, if node->next is valid, | 
 | 370 |     // that would be sub effect node. | 
 | 371 |     // Find the sub effects and add them to the gSubEffectList | 
 | 372 |     node = node->next; | 
 | 373 |     int count = 2; | 
 | 374 |     bool hwSubefx = false, swSubefx = false; | 
 | 375 |     list_sub_elem_t *sube = NULL; | 
 | 376 |     if (node != NULL) { | 
 | 377 |         ALOGV("Adding the effect to gEffectSubList as there are sub effects"); | 
 | 378 |         sube = malloc(sizeof(list_sub_elem_t)); | 
 | 379 |         sube->object = d; | 
 | 380 |         sube->sub_elem = NULL; | 
 | 381 |         sube->next = gSubEffectList; | 
 | 382 |         gSubEffectList = sube; | 
 | 383 |     } | 
 | 384 |     while (node != NULL && count) { | 
 | 385 |        if (addSubEffect(node)) { | 
 | 386 |            ALOGW("loadEffect() could not add subEffect %s", node->value); | 
 | 387 |            // Change the gSubEffectList to point to older list; | 
 | 388 |            gSubEffectList = sube->next; | 
 | 389 |            free(sube->sub_elem);// Free an already added sub effect | 
 | 390 |            sube->sub_elem = NULL; | 
 | 391 |            free(sube); | 
 | 392 |            return -ENOENT; | 
 | 393 |        } | 
 | 394 |        sub_effect_entry_t *subEntry = (sub_effect_entry_t*)gSubEffectList->sub_elem->object; | 
 | 395 |        effect_descriptor_t *subEffectDesc = (effect_descriptor_t*)(subEntry->object); | 
 | 396 |        // Since we return a dummy descriptor for the proxy during | 
 | 397 |        // get_descriptor call,we replace it with the correspoding | 
 | 398 |        // sw effect descriptor, but with Proxy UUID | 
 | 399 |        // check for Sw desc | 
 | 400 |         if (!((subEffectDesc->flags & EFFECT_FLAG_HW_ACC_MASK) == | 
 | 401 |                                            EFFECT_FLAG_HW_ACC_TUNNEL)) { | 
 | 402 |              swSubefx = true; | 
 | 403 |              *d = *subEffectDesc; | 
 | 404 |              d->uuid = uuid; | 
 | 405 |              ALOGV("loadEffect() Changed the Proxy desc"); | 
 | 406 |        } else | 
 | 407 |            hwSubefx = true; | 
 | 408 |        count--; | 
 | 409 |        node = node->next; | 
 | 410 |     } | 
 | 411 |     // 1 HW and 1 SW sub effect found. Set the offload flag in the Proxy desc | 
 | 412 |     if (hwSubefx && swSubefx) { | 
 | 413 |         d->flags |= EFFECT_FLAG_OFFLOAD_SUPPORTED; | 
 | 414 |     } | 
 | 415 |     return 0; | 
 | 416 | } | 
 | 417 |  | 
 | 418 | lib_entry_t *getLibrary(const char *name) | 
 | 419 | { | 
 | 420 |     list_elem_t *e; | 
 | 421 |  | 
 | 422 |     if (gCachedLibrary && | 
 | 423 |             !strncmp(gCachedLibrary->name, name, PATH_MAX)) { | 
 | 424 |         return gCachedLibrary; | 
 | 425 |     } | 
 | 426 |  | 
 | 427 |     e = gLibraryList; | 
 | 428 |     while (e) { | 
 | 429 |         lib_entry_t *l = (lib_entry_t *)e->object; | 
 | 430 |         if (!strcmp(l->name, name)) { | 
 | 431 |             gCachedLibrary = l; | 
 | 432 |             return l; | 
 | 433 |         } | 
 | 434 |         e = e->next; | 
 | 435 |     } | 
 | 436 |  | 
 | 437 |     return NULL; | 
 | 438 | } |