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