/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "EffectsFactoryConfigLoader" //#define LOG_NDEBUG 0 #include #include #include #include #include #include #include #include "EffectsConfigLoader.h" #include "EffectsFactoryState.h" ///////////////////////////////////////////////// // Local functions prototypes ///////////////////////////////////////////////// static int loadEffectConfigFile(const char *path); static int loadLibraries(cnode *root); static int loadLibrary(cnode *root, const char *name); static int loadEffects(cnode *root); static int loadEffect(cnode *node); // To get and add the effect pointed by the passed node to the gSubEffectList static int addSubEffect(cnode *root); static lib_entry_t *getLibrary(const char *path); static lib_entry_t *gCachedLibrary; // last library accessed by getLibrary() int EffectLoadEffectConfig() { if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) { return loadEffectConfigFile(AUDIO_EFFECT_VENDOR_CONFIG_FILE); } else if (access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) { return loadEffectConfigFile(AUDIO_EFFECT_DEFAULT_CONFIG_FILE); } return 0; } int loadEffectConfigFile(const char *path) { cnode *root; char *data; data = load_file(path, NULL); if (data == NULL) { return -ENODEV; } root = config_node("", ""); config_load(root, data); loadLibraries(root); loadEffects(root); config_free(root); free(root); free(data); return 0; } int loadLibraries(cnode *root) { cnode *node; node = config_find(root, LIBRARIES_TAG); if (node == NULL) { return -ENOENT; } node = node->first_child; while (node) { loadLibrary(node, node->name); node = node->next; } return 0; } #ifdef __LP64__ // audio_effects.conf always specifies 32 bit lib path: convert to 64 bit path if needed static const char *kLibraryPathRoot[] = {"/odm/lib64/soundfx", "/vendor/lib64/soundfx", "/system/lib64/soundfx"}; #else static const char *kLibraryPathRoot[] = {"/odm/lib/soundfx", "/vendor/lib/soundfx", "/system/lib/soundfx"}; #endif static const int kLibraryPathRootSize = (sizeof(kLibraryPathRoot) / sizeof(kLibraryPathRoot[0])); // Checks if the library path passed as lib_path_in can be opened and if not // tries in standard effect library directories with just the library name and returns correct path // in lib_path_out int checkLibraryPath(const char *lib_path_in, char *lib_path_out) { char *str; const char *lib_name; size_t len; if (lib_path_in == NULL || lib_path_out == NULL) { return -EINVAL; } strlcpy(lib_path_out, lib_path_in, PATH_MAX); // Try exact path first str = strstr(lib_path_out, "/lib/soundfx/"); if (str == NULL) { return -EINVAL; } // Extract library name from input path len = str - lib_path_out; lib_name = lib_path_in + len + strlen("/lib/soundfx/"); // Then try with library name and standard path names in order of preference for (int i = 0; i < kLibraryPathRootSize; i++) { char path[PATH_MAX]; snprintf(path, PATH_MAX, "%s/%s", kLibraryPathRoot[i], lib_name); if (F_OK == access(path, 0)) { strcpy(lib_path_out, path); ALOGW_IF(strncmp(lib_path_out, lib_path_in, PATH_MAX) != 0, "checkLibraryPath() corrected library path %s to %s", lib_path_in, lib_path_out); return 0; } } return -EINVAL; } int loadLibrary(cnode *root, const char *name) { cnode *node; void *hdl = NULL; audio_effect_library_t *desc; list_elem_t *e; lib_entry_t *l; char path[PATH_MAX]; node = config_find(root, PATH_TAG); if (node == NULL) { return -EINVAL; } if (checkLibraryPath((const char *)node->value, path) != 0) { ALOGW("loadLibrary() could not find library %s", path); goto error; } hdl = dlopen(path, RTLD_NOW); if (hdl == NULL) { ALOGW("loadLibrary() failed to open %s", path); goto error; } desc = (audio_effect_library_t *)dlsym(hdl, AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR); if (desc == NULL) { ALOGW("loadLibrary() could not find symbol %s", AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR); goto error; } if (AUDIO_EFFECT_LIBRARY_TAG != desc->tag) { ALOGW("getLibrary() bad tag %08x in lib info struct", desc->tag); goto error; } if (EFFECT_API_VERSION_MAJOR(desc->version) != EFFECT_API_VERSION_MAJOR(EFFECT_LIBRARY_API_VERSION)) { ALOGW("loadLibrary() bad lib version %08x", desc->version); goto error; } // add entry for library in gLibraryList l = malloc(sizeof(lib_entry_t)); l->name = strndup(name, PATH_MAX); l->path = strndup(path, PATH_MAX); l->handle = hdl; l->desc = desc; l->effects = NULL; pthread_mutex_init(&l->lock, NULL); e = malloc(sizeof(list_elem_t)); e->object = l; pthread_mutex_lock(&gLibLock); e->next = gLibraryList; gLibraryList = e; pthread_mutex_unlock(&gLibLock); ALOGV("getLibrary() linked library %p for path %s", l, path); return 0; error: if (hdl != NULL) { dlclose(hdl); } //add entry for library errors in gLibraryFailedList lib_failed_entry_t *fl = malloc(sizeof(lib_failed_entry_t)); fl->name = strndup(name, PATH_MAX); fl->path = strndup(path, PATH_MAX); list_elem_t *fe = malloc(sizeof(list_elem_t)); fe->object = fl; fe->next = gLibraryFailedList; gLibraryFailedList = fe; ALOGV("getLibrary() linked error in library %p for path %s", fl, path); return -EINVAL; } // This will find the library and UUID tags of the sub effect pointed by the // node, gets the effect descriptor and lib_entry_t and adds the subeffect - // sub_entry_t to the gSubEffectList int addSubEffect(cnode *root) { ALOGV("addSubEffect"); cnode *node; effect_uuid_t uuid; effect_descriptor_t *d; lib_entry_t *l; list_elem_t *e; node = config_find(root, LIBRARY_TAG); if (node == NULL) { return -EINVAL; } l = getLibrary(node->value); if (l == NULL) { ALOGW("addSubEffect() could not get library %s", node->value); return -EINVAL; } node = config_find(root, UUID_TAG); if (node == NULL) { return -EINVAL; } if (stringToUuid(node->value, &uuid) != 0) { ALOGW("addSubEffect() invalid uuid %s", node->value); return -EINVAL; } d = malloc(sizeof(effect_descriptor_t)); if (l->desc->get_descriptor(&uuid, d) != 0) { char s[40]; uuidToString(&uuid, s, 40); ALOGW("Error querying effect %s on lib %s", s, l->name); free(d); return -EINVAL; } #if (LOG_NDEBUG==0) char s[512]; dumpEffectDescriptor(d, s, sizeof(s), 0 /* indent */); ALOGV("addSubEffect() read descriptor %p:%s",d, s); #endif if (EFFECT_API_VERSION_MAJOR(d->apiVersion) != EFFECT_API_VERSION_MAJOR(EFFECT_CONTROL_API_VERSION)) { ALOGW("Bad API version %08x on lib %s", d->apiVersion, l->name); free(d); return -EINVAL; } sub_effect_entry_t *sub_effect = malloc(sizeof(sub_effect_entry_t)); sub_effect->object = d; // lib_entry_t is stored since the sub effects are not linked to the library sub_effect->lib = l; e = malloc(sizeof(list_elem_t)); e->object = sub_effect; e->next = gSubEffectList->sub_elem; gSubEffectList->sub_elem = e; ALOGV("addSubEffect end"); return 0; } int loadEffects(cnode *root) { cnode *node; node = config_find(root, EFFECTS_TAG); if (node == NULL) { return -ENOENT; } node = node->first_child; while (node) { loadEffect(node); node = node->next; } return 0; } int loadEffect(cnode *root) { cnode *node; effect_uuid_t uuid; lib_entry_t *l; effect_descriptor_t *d; list_elem_t *e; node = config_find(root, LIBRARY_TAG); if (node == NULL) { return -EINVAL; } l = getLibrary(node->value); if (l == NULL) { ALOGW("loadEffect() could not get library %s", node->value); return -EINVAL; } node = config_find(root, UUID_TAG); if (node == NULL) { return -EINVAL; } if (stringToUuid(node->value, &uuid) != 0) { ALOGW("loadEffect() invalid uuid %s", node->value); return -EINVAL; } lib_entry_t *tmp; bool skip = false; if (findEffect(NULL, &uuid, &tmp, NULL) == 0) { ALOGW("skipping duplicate uuid %s %s", node->value, node->next ? "and its sub-effects" : ""); skip = true; } d = malloc(sizeof(effect_descriptor_t)); if (l->desc->get_descriptor(&uuid, d) != 0) { char s[40]; uuidToString(&uuid, s, 40); ALOGW("Error querying effect %s on lib %s", s, l->name); free(d); return -EINVAL; } #if (LOG_NDEBUG==0) char s[512]; dumpEffectDescriptor(d, s, sizeof(s), 0 /* indent */); ALOGV("loadEffect() read descriptor %p:%s",d, s); #endif if (EFFECT_API_VERSION_MAJOR(d->apiVersion) != EFFECT_API_VERSION_MAJOR(EFFECT_CONTROL_API_VERSION)) { ALOGW("Bad API version %08x on lib %s", d->apiVersion, l->name); free(d); return -EINVAL; } e = malloc(sizeof(list_elem_t)); e->object = d; if (skip) { e->next = gSkippedEffects; gSkippedEffects = e; return -EINVAL; } else { e->next = l->effects; l->effects = e; } // After the UUID node in the config_tree, if node->next is valid, // that would be sub effect node. // Find the sub effects and add them to the gSubEffectList node = node->next; int count = 2; bool hwSubefx = false, swSubefx = false; list_sub_elem_t *sube = NULL; if (node != NULL) { ALOGV("Adding the effect to gEffectSubList as there are sub effects"); sube = malloc(sizeof(list_sub_elem_t)); sube->object = d; sube->sub_elem = NULL; sube->next = gSubEffectList; gSubEffectList = sube; } while (node != NULL && count) { if (addSubEffect(node)) { ALOGW("loadEffect() could not add subEffect %s", node->value); // Change the gSubEffectList to point to older list; gSubEffectList = sube->next; free(sube->sub_elem);// Free an already added sub effect sube->sub_elem = NULL; free(sube); return -ENOENT; } sub_effect_entry_t *subEntry = (sub_effect_entry_t*)gSubEffectList->sub_elem->object; effect_descriptor_t *subEffectDesc = (effect_descriptor_t*)(subEntry->object); // Since we return a stub descriptor for the proxy during // get_descriptor call,we replace it with the correspoding // sw effect descriptor, but with Proxy UUID // check for Sw desc if (!((subEffectDesc->flags & EFFECT_FLAG_HW_ACC_MASK) == EFFECT_FLAG_HW_ACC_TUNNEL)) { swSubefx = true; *d = *subEffectDesc; d->uuid = uuid; ALOGV("loadEffect() Changed the Proxy desc"); } else hwSubefx = true; count--; node = node->next; } // 1 HW and 1 SW sub effect found. Set the offload flag in the Proxy desc if (hwSubefx && swSubefx) { d->flags |= EFFECT_FLAG_OFFLOAD_SUPPORTED; } return 0; } lib_entry_t *getLibrary(const char *name) { list_elem_t *e; if (gCachedLibrary && !strncmp(gCachedLibrary->name, name, PATH_MAX)) { return gCachedLibrary; } e = gLibraryList; while (e) { lib_entry_t *l = (lib_entry_t *)e->object; if (!strcmp(l->name, name)) { gCachedLibrary = l; return l; } e = e->next; } return NULL; }