| /* |
| * 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 <dlfcn.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| |
| #include <cutils/config_utils.h> |
| #include <cutils/misc.h> |
| #include <log/log.h> |
| |
| #include <system/audio_effects/audio_effects_conf.h> |
| |
| #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)) { |
| strlcpy(lib_path_out, path, PATH_MAX); |
| 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; |
| } |