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