1/*
2 * Copyright (C) 2010 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 "EffectsFactory"
18//#define LOG_NDEBUG 0
19
20#include "EffectsFactory.h"
21#include <string.h>
22#include <stdlib.h>
23#include <dlfcn.h>
24
25#include <cutils/misc.h>
26#include <cutils/config_utils.h>
27#include <audio_effects/audio_effects_conf.h>
28
29static list_elem_t *gEffectList; // list of effect_entry_t: all currently created effects
30static list_elem_t *gLibraryList; // list of lib_entry_t: all currently loaded libraries
31static pthread_mutex_t gLibLock = PTHREAD_MUTEX_INITIALIZER; // controls access to gLibraryList
32static uint32_t gNumEffects;         // total number number of effects
33static list_elem_t *gCurLib;    // current library in enumeration process
34static list_elem_t *gCurEffect; // current effect in enumeration process
35static uint32_t gCurEffectIdx;       // current effect index in enumeration process
36static lib_entry_t *gCachedLibrary;  // last library accessed by getLibrary()
37
38static int gInitDone; // true is global initialization has been preformed
39static int gCanQueryEffect; // indicates that call to EffectQueryEffect() is valid, i.e. that the list of effects
40                          // was not modified since last call to EffectQueryNumberEffects()
41
42
43/////////////////////////////////////////////////
44//      Local functions prototypes
45/////////////////////////////////////////////////
46
47static int init();
48static int loadEffectConfigFile(const char *path);
49static int loadLibraries(cnode *root);
50static int loadLibrary(cnode *root, const char *name);
51static int loadEffects(cnode *root);
52static int loadEffect(cnode *node);
53static lib_entry_t *getLibrary(const char *path);
54static void resetEffectEnumeration();
55static uint32_t updateNumEffects();
56static int findEffect(const effect_uuid_t *type,
57               const effect_uuid_t *uuid,
58               lib_entry_t **lib,
59               effect_descriptor_t **desc);
60static void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len);
61static int stringToUuid(const char *str, effect_uuid_t *uuid);
62static int uuidToString(const effect_uuid_t *uuid, char *str, size_t maxLen);
63
64/////////////////////////////////////////////////
65//      Effect Control Interface functions
66/////////////////////////////////////////////////
67
68int Effect_Process(effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
69{
70    int ret = init();
71    if (ret < 0) {
72        return ret;
73    }
74    effect_entry_t *fx = (effect_entry_t *)self;
75    pthread_mutex_lock(&gLibLock);
76    if (fx->lib == NULL) {
77        pthread_mutex_unlock(&gLibLock);
78        return -EPIPE;
79    }
80    pthread_mutex_lock(&fx->lib->lock);
81    pthread_mutex_unlock(&gLibLock);
82
83    ret = (*fx->subItfe)->process(fx->subItfe, inBuffer, outBuffer);
84    pthread_mutex_unlock(&fx->lib->lock);
85    return ret;
86}
87
88int Effect_Command(effect_handle_t self,
89                   uint32_t cmdCode,
90                   uint32_t cmdSize,
91                   void *pCmdData,
92                   uint32_t *replySize,
93                   void *pReplyData)
94{
95    int ret = init();
96    if (ret < 0) {
97        return ret;
98    }
99    effect_entry_t *fx = (effect_entry_t *)self;
100    pthread_mutex_lock(&gLibLock);
101    if (fx->lib == NULL) {
102        pthread_mutex_unlock(&gLibLock);
103        return -EPIPE;
104    }
105    pthread_mutex_lock(&fx->lib->lock);
106    pthread_mutex_unlock(&gLibLock);
107
108    ret = (*fx->subItfe)->command(fx->subItfe, cmdCode, cmdSize, pCmdData, replySize, pReplyData);
109    pthread_mutex_unlock(&fx->lib->lock);
110    return ret;
111}
112
113int Effect_GetDescriptor(effect_handle_t self,
114                         effect_descriptor_t *desc)
115{
116    int ret = init();
117    if (ret < 0) {
118        return ret;
119    }
120    effect_entry_t *fx = (effect_entry_t *)self;
121    pthread_mutex_lock(&gLibLock);
122    if (fx->lib == NULL) {
123        pthread_mutex_unlock(&gLibLock);
124        return -EPIPE;
125    }
126    pthread_mutex_lock(&fx->lib->lock);
127    pthread_mutex_unlock(&gLibLock);
128
129    ret = (*fx->subItfe)->get_descriptor(fx->subItfe, desc);
130    pthread_mutex_unlock(&fx->lib->lock);
131    return ret;
132}
133
134int Effect_ProcessReverse(effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
135{
136    int ret = init();
137    if (ret < 0) {
138        return ret;
139    }
140    effect_entry_t *fx = (effect_entry_t *)self;
141    pthread_mutex_lock(&gLibLock);
142    if (fx->lib == NULL) {
143        pthread_mutex_unlock(&gLibLock);
144        return -EPIPE;
145    }
146    pthread_mutex_lock(&fx->lib->lock);
147    pthread_mutex_unlock(&gLibLock);
148
149    if ((*fx->subItfe)->process_reverse != NULL) {
150        ret = (*fx->subItfe)->process_reverse(fx->subItfe, inBuffer, outBuffer);
151    } else {
152        ret = -ENOSYS;
153    }
154    pthread_mutex_unlock(&fx->lib->lock);
155    return ret;
156}
157
158
159const struct effect_interface_s gInterface = {
160        Effect_Process,
161        Effect_Command,
162        Effect_GetDescriptor,
163        NULL
164};
165
166const struct effect_interface_s gInterfaceWithReverse = {
167        Effect_Process,
168        Effect_Command,
169        Effect_GetDescriptor,
170        Effect_ProcessReverse
171};
172
173/////////////////////////////////////////////////
174//      Effect Factory Interface functions
175/////////////////////////////////////////////////
176
177int EffectQueryNumberEffects(uint32_t *pNumEffects)
178{
179    int ret = init();
180    if (ret < 0) {
181        return ret;
182    }
183    if (pNumEffects == NULL) {
184        return -EINVAL;
185    }
186
187    pthread_mutex_lock(&gLibLock);
188    *pNumEffects = gNumEffects;
189    gCanQueryEffect = 1;
190    pthread_mutex_unlock(&gLibLock);
191    ALOGV("EffectQueryNumberEffects(): %d", *pNumEffects);
192    return ret;
193}
194
195int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor)
196{
197    int ret = init();
198    if (ret < 0) {
199        return ret;
200    }
201    if (pDescriptor == NULL ||
202        index >= gNumEffects) {
203        return -EINVAL;
204    }
205    if (gCanQueryEffect == 0) {
206        return -ENOSYS;
207    }
208
209    pthread_mutex_lock(&gLibLock);
210    ret = -ENOENT;
211    if (index < gCurEffectIdx) {
212        resetEffectEnumeration();
213    }
214    while (gCurLib) {
215        if (gCurEffect) {
216            if (index == gCurEffectIdx) {
217                *pDescriptor = *(effect_descriptor_t *)gCurEffect->object;
218                ret = 0;
219                break;
220            } else {
221                gCurEffect = gCurEffect->next;
222                gCurEffectIdx++;
223            }
224        } else {
225            gCurLib = gCurLib->next;
226            gCurEffect = ((lib_entry_t *)gCurLib->object)->effects;
227        }
228    }
229
230#if (LOG_NDEBUG == 0)
231    char str[256];
232    dumpEffectDescriptor(pDescriptor, str, 256);
233    ALOGV("EffectQueryEffect() desc:%s", str);
234#endif
235    pthread_mutex_unlock(&gLibLock);
236    return ret;
237}
238
239int EffectGetDescriptor(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor)
240{
241    lib_entry_t *l = NULL;
242    effect_descriptor_t *d = NULL;
243
244    int ret = init();
245    if (ret < 0) {
246        return ret;
247    }
248    if (pDescriptor == NULL || uuid == NULL) {
249        return -EINVAL;
250    }
251    pthread_mutex_lock(&gLibLock);
252    ret = findEffect(NULL, uuid, &l, &d);
253    if (ret == 0) {
254        *pDescriptor = *d;
255    }
256    pthread_mutex_unlock(&gLibLock);
257    return ret;
258}
259
260int EffectCreate(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, effect_handle_t *pHandle)
261{
262    list_elem_t *e = gLibraryList;
263    lib_entry_t *l = NULL;
264    effect_descriptor_t *d = NULL;
265    effect_handle_t itfe;
266    effect_entry_t *fx;
267    int found = 0;
268    int ret;
269
270    if (uuid == NULL || pHandle == NULL) {
271        return -EINVAL;
272    }
273
274    ALOGV("EffectCreate() UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
275            uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion,
276            uuid->clockSeq, uuid->node[0], uuid->node[1],uuid->node[2],
277            uuid->node[3],uuid->node[4],uuid->node[5]);
278
279    ret = init();
280
281    if (ret < 0) {
282        ALOGW("EffectCreate() init error: %d", ret);
283        return ret;
284    }
285
286    pthread_mutex_lock(&gLibLock);
287
288    ret = findEffect(NULL, uuid, &l, &d);
289    if (ret < 0){
290        goto exit;
291    }
292
293    // create effect in library
294    ret = l->desc->create_effect(uuid, sessionId, ioId, &itfe);
295    if (ret != 0) {
296        ALOGW("EffectCreate() library %s: could not create fx %s, error %d", l->name, d->name, ret);
297        goto exit;
298    }
299
300    // add entry to effect list
301    fx = (effect_entry_t *)malloc(sizeof(effect_entry_t));
302    fx->subItfe = itfe;
303    if ((*itfe)->process_reverse != NULL) {
304        fx->itfe = (struct effect_interface_s *)&gInterfaceWithReverse;
305        ALOGV("EffectCreate() gInterfaceWithReverse");
306    }   else {
307        fx->itfe = (struct effect_interface_s *)&gInterface;
308        ALOGV("EffectCreate() gInterface");
309    }
310    fx->lib = l;
311
312    e = (list_elem_t *)malloc(sizeof(list_elem_t));
313    e->object = fx;
314    e->next = gEffectList;
315    gEffectList = e;
316
317    *pHandle = (effect_handle_t)fx;
318
319    ALOGV("EffectCreate() created entry %p with sub itfe %p in library %s", *pHandle, itfe, l->name);
320
321exit:
322    pthread_mutex_unlock(&gLibLock);
323    return ret;
324}
325
326int EffectRelease(effect_handle_t handle)
327{
328    effect_entry_t *fx;
329    list_elem_t *e1;
330    list_elem_t *e2;
331
332    int ret = init();
333    if (ret < 0) {
334        return ret;
335    }
336
337    // remove effect from effect list
338    pthread_mutex_lock(&gLibLock);
339    e1 = gEffectList;
340    e2 = NULL;
341    while (e1) {
342        if (e1->object == handle) {
343            if (e2) {
344                e2->next = e1->next;
345            } else {
346                gEffectList = e1->next;
347            }
348            fx = (effect_entry_t *)e1->object;
349            free(e1);
350            break;
351        }
352        e2 = e1;
353        e1 = e1->next;
354    }
355    if (e1 == NULL) {
356        ret = -ENOENT;
357        goto exit;
358    }
359
360    // release effect in library
361    if (fx->lib == NULL) {
362        ALOGW("EffectRelease() fx %p library already unloaded", handle);
363    } else {
364        pthread_mutex_lock(&fx->lib->lock);
365        fx->lib->desc->release_effect(fx->subItfe);
366        pthread_mutex_unlock(&fx->lib->lock);
367    }
368    free(fx);
369
370exit:
371    pthread_mutex_unlock(&gLibLock);
372    return ret;
373}
374
375int EffectIsNullUuid(const effect_uuid_t *uuid)
376{
377    if (memcmp(uuid, EFFECT_UUID_NULL, sizeof(effect_uuid_t))) {
378        return 0;
379    }
380    return 1;
381}
382
383/////////////////////////////////////////////////
384//      Local functions
385/////////////////////////////////////////////////
386
387int init() {
388    int hdl;
389
390    if (gInitDone) {
391        return 0;
392    }
393
394    pthread_mutex_init(&gLibLock, NULL);
395
396    if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) {
397        loadEffectConfigFile(AUDIO_EFFECT_VENDOR_CONFIG_FILE);
398    } else if (access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) {
399        loadEffectConfigFile(AUDIO_EFFECT_DEFAULT_CONFIG_FILE);
400    }
401
402    updateNumEffects();
403    gInitDone = 1;
404    ALOGV("init() done");
405    return 0;
406}
407
408int loadEffectConfigFile(const char *path)
409{
410    cnode *root;
411    char *data;
412
413    data = load_file(path, NULL);
414    if (data == NULL) {
415        return -ENODEV;
416    }
417    root = config_node("", "");
418    config_load(root, data);
419    loadLibraries(root);
420    loadEffects(root);
421    config_free(root);
422    free(root);
423    free(data);
424
425    return 0;
426}
427
428int loadLibraries(cnode *root)
429{
430    cnode *node;
431
432    node = config_find(root, LIBRARIES_TAG);
433    if (node == NULL) {
434        return -ENOENT;
435    }
436    node = node->first_child;
437    while (node) {
438        loadLibrary(node, node->name);
439        node = node->next;
440    }
441    return 0;
442}
443
444int loadLibrary(cnode *root, const char *name)
445{
446    cnode *node;
447    void *hdl;
448    audio_effect_library_t *desc;
449    list_elem_t *e;
450    lib_entry_t *l;
451
452    node = config_find(root, PATH_TAG);
453    if (node == NULL) {
454        return -EINVAL;
455    }
456
457    hdl = dlopen(node->value, RTLD_NOW);
458    if (hdl == NULL) {
459        ALOGW("loadLibrary() failed to open %s", node->value);
460        goto error;
461    }
462
463    desc = (audio_effect_library_t *)dlsym(hdl, AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR);
464    if (desc == NULL) {
465        ALOGW("loadLibrary() could not find symbol %s", AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR);
466        goto error;
467    }
468
469    if (AUDIO_EFFECT_LIBRARY_TAG != desc->tag) {
470        ALOGW("getLibrary() bad tag %08x in lib info struct", desc->tag);
471        goto error;
472    }
473
474    if (EFFECT_API_VERSION_MAJOR(desc->version) !=
475            EFFECT_API_VERSION_MAJOR(EFFECT_LIBRARY_API_VERSION)) {
476        ALOGW("loadLibrary() bad lib version %08x", desc->version);
477        goto error;
478    }
479
480    // add entry for library in gLibraryList
481    l = malloc(sizeof(lib_entry_t));
482    l->name = strndup(name, PATH_MAX);
483    l->path = strndup(node->value, PATH_MAX);
484    l->handle = hdl;
485    l->desc = desc;
486    l->effects = NULL;
487    pthread_mutex_init(&l->lock, NULL);
488
489    e = malloc(sizeof(list_elem_t));
490    e->object = l;
491    pthread_mutex_lock(&gLibLock);
492    e->next = gLibraryList;
493    gLibraryList = e;
494    pthread_mutex_unlock(&gLibLock);
495    ALOGV("getLibrary() linked library %p for path %s", l, node->value);
496
497    return 0;
498
499error:
500    if (hdl != NULL) {
501        dlclose(hdl);
502    }
503    return -EINVAL;
504}
505
506int loadEffects(cnode *root)
507{
508    cnode *node;
509
510    node = config_find(root, EFFECTS_TAG);
511    if (node == NULL) {
512        return -ENOENT;
513    }
514    node = node->first_child;
515    while (node) {
516        loadEffect(node);
517        node = node->next;
518    }
519    return 0;
520}
521
522int loadEffect(cnode *root)
523{
524    cnode *node;
525    effect_uuid_t uuid;
526    lib_entry_t *l;
527    effect_descriptor_t *d;
528    list_elem_t *e;
529
530    node = config_find(root, LIBRARY_TAG);
531    if (node == NULL) {
532        return -EINVAL;
533    }
534
535    l = getLibrary(node->value);
536    if (l == NULL) {
537        ALOGW("loadEffect() could not get library %s", node->value);
538        return -EINVAL;
539    }
540
541    node = config_find(root, UUID_TAG);
542    if (node == NULL) {
543        return -EINVAL;
544    }
545    if (stringToUuid(node->value, &uuid) != 0) {
546        ALOGW("loadEffect() invalid uuid %s", node->value);
547        return -EINVAL;
548    }
549
550    d = malloc(sizeof(effect_descriptor_t));
551    if (l->desc->get_descriptor(&uuid, d) != 0) {
552        char s[40];
553        uuidToString(&uuid, s, 40);
554        ALOGW("Error querying effect %s on lib %s", s, l->name);
555        free(d);
556        return -EINVAL;
557    }
558#if (LOG_NDEBUG==0)
559    char s[256];
560    dumpEffectDescriptor(d, s, 256);
561    ALOGV("loadEffect() read descriptor %p:%s",d, s);
562#endif
563    if (EFFECT_API_VERSION_MAJOR(d->apiVersion) !=
564            EFFECT_API_VERSION_MAJOR(EFFECT_CONTROL_API_VERSION)) {
565        ALOGW("Bad API version %08x on lib %s", d->apiVersion, l->name);
566        free(d);
567        return -EINVAL;
568    }
569    e = malloc(sizeof(list_elem_t));
570    e->object = d;
571    e->next = l->effects;
572    l->effects = e;
573
574    return 0;
575}
576
577lib_entry_t *getLibrary(const char *name)
578{
579    list_elem_t *e;
580
581    if (gCachedLibrary &&
582            !strncmp(gCachedLibrary->name, name, PATH_MAX)) {
583        return gCachedLibrary;
584    }
585
586    e = gLibraryList;
587    while (e) {
588        lib_entry_t *l = (lib_entry_t *)e->object;
589        if (!strcmp(l->name, name)) {
590            gCachedLibrary = l;
591            return l;
592        }
593        e = e->next;
594    }
595
596    return NULL;
597}
598
599
600void resetEffectEnumeration()
601{
602    gCurLib = gLibraryList;
603    gCurEffect = NULL;
604    if (gCurLib) {
605        gCurEffect = ((lib_entry_t *)gCurLib->object)->effects;
606    }
607    gCurEffectIdx = 0;
608}
609
610uint32_t updateNumEffects() {
611    list_elem_t *e;
612    uint32_t cnt = 0;
613
614    resetEffectEnumeration();
615
616    e = gLibraryList;
617    while (e) {
618        lib_entry_t *l = (lib_entry_t *)e->object;
619        list_elem_t *efx = l->effects;
620        while (efx) {
621            cnt++;
622            efx = efx->next;
623        }
624        e = e->next;
625    }
626    gNumEffects = cnt;
627    gCanQueryEffect = 0;
628    return cnt;
629}
630
631int findEffect(const effect_uuid_t *type,
632               const effect_uuid_t *uuid,
633               lib_entry_t **lib,
634               effect_descriptor_t **desc)
635{
636    list_elem_t *e = gLibraryList;
637    lib_entry_t *l = NULL;
638    effect_descriptor_t *d = NULL;
639    int found = 0;
640    int ret = 0;
641
642    while (e && !found) {
643        l = (lib_entry_t *)e->object;
644        list_elem_t *efx = l->effects;
645        while (efx) {
646            d = (effect_descriptor_t *)efx->object;
647            if (type != NULL && memcmp(&d->type, type, sizeof(effect_uuid_t)) == 0) {
648                found = 1;
649                break;
650            }
651            if (uuid != NULL && memcmp(&d->uuid, uuid, sizeof(effect_uuid_t)) == 0) {
652                found = 1;
653                break;
654            }
655            efx = efx->next;
656        }
657        e = e->next;
658    }
659    if (!found) {
660        ALOGV("findEffect() effect not found");
661        ret = -ENOENT;
662    } else {
663        ALOGV("findEffect() found effect: %s in lib %s", d->name, l->name);
664        *lib = l;
665        if (desc) {
666            *desc = d;
667        }
668    }
669
670    return ret;
671}
672
673void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len) {
674    char s[256];
675
676    snprintf(str, len, "\nEffect Descriptor %p:\n", desc);
677    strncat(str, "- TYPE: ", len);
678    uuidToString(&desc->uuid, s, 256);
679    snprintf(str, len, "- UUID: %s\n", s);
680    uuidToString(&desc->type, s, 256);
681    snprintf(str, len, "- TYPE: %s\n", s);
682    sprintf(s, "- apiVersion: %08X\n- flags: %08X\n",
683            desc->apiVersion, desc->flags);
684    strncat(str, s, len);
685    sprintf(s, "- name: %s\n", desc->name);
686    strncat(str, s, len);
687    sprintf(s, "- implementor: %s\n", desc->implementor);
688    strncat(str, s, len);
689}
690
691int stringToUuid(const char *str, effect_uuid_t *uuid)
692{
693    int tmp[10];
694
695    if (sscanf(str, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
696            tmp, tmp+1, tmp+2, tmp+3, tmp+4, tmp+5, tmp+6, tmp+7, tmp+8, tmp+9) < 10) {
697        return -EINVAL;
698    }
699    uuid->timeLow = (uint32_t)tmp[0];
700    uuid->timeMid = (uint16_t)tmp[1];
701    uuid->timeHiAndVersion = (uint16_t)tmp[2];
702    uuid->clockSeq = (uint16_t)tmp[3];
703    uuid->node[0] = (uint8_t)tmp[4];
704    uuid->node[1] = (uint8_t)tmp[5];
705    uuid->node[2] = (uint8_t)tmp[6];
706    uuid->node[3] = (uint8_t)tmp[7];
707    uuid->node[4] = (uint8_t)tmp[8];
708    uuid->node[5] = (uint8_t)tmp[9];
709
710    return 0;
711}
712
713int uuidToString(const effect_uuid_t *uuid, char *str, size_t maxLen)
714{
715
716    snprintf(str, maxLen, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
717            uuid->timeLow,
718            uuid->timeMid,
719            uuid->timeHiAndVersion,
720            uuid->clockSeq,
721            uuid->node[0],
722            uuid->node[1],
723            uuid->node[2],
724            uuid->node[3],
725            uuid->node[4],
726            uuid->node[5]);
727
728    return 0;
729}
730
731