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 <stdlib.h>
21#include <string.h>
22#include <unistd.h>
23
24#include <cutils/properties.h>
25#include <log/log.h>
26
27#include <media/EffectsFactoryApi.h>
28
29#include "EffectsConfigLoader.h"
30#include "EffectsFactoryState.h"
31#include "EffectsXmlConfigLoader.h"
32
33#include "EffectsFactory.h"
34
35static list_elem_t *gEffectList; // list of effect_entry_t: all currently created effects
36static uint32_t gNumEffects;         // total number number of effects
37static list_elem_t *gCurLib;    // current library in enumeration process
38static list_elem_t *gCurEffect; // current effect in enumeration process
39static uint32_t gCurEffectIdx;       // current effect index in enumeration process
40/** Number of elements skipped during the effects configuration loading.
41 *  -1 if the config loader failed
42 *  -2 if config load was skipped
43 */
44static ssize_t gConfigNbElemSkipped = -2;
45
46static int gInitDone; // true is global initialization has been preformed
47static int gCanQueryEffect; // indicates that call to EffectQueryEffect() is valid, i.e. that the list of effects
48                          // was not modified since last call to EffectQueryNumberEffects()
49/////////////////////////////////////////////////
50//      Local functions prototypes
51/////////////////////////////////////////////////
52
53static int init();
54static void resetEffectEnumeration();
55static uint32_t updateNumEffects();
56// To search a subeffect in the gSubEffectList
57static int findSubEffect(const effect_uuid_t *uuid,
58               lib_entry_t **lib,
59               effect_descriptor_t **desc);
60
61/////////////////////////////////////////////////
62//      Effect Control Interface functions
63/////////////////////////////////////////////////
64
65int Effect_Process(effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
66{
67    int ret = init();
68    if (ret < 0) {
69        return ret;
70    }
71    effect_entry_t *fx = (effect_entry_t *)self;
72    pthread_mutex_lock(&gLibLock);
73    if (fx->lib == NULL) {
74        pthread_mutex_unlock(&gLibLock);
75        return -EPIPE;
76    }
77    pthread_mutex_lock(&fx->lib->lock);
78    pthread_mutex_unlock(&gLibLock);
79
80    ret = (*fx->subItfe)->process(fx->subItfe, inBuffer, outBuffer);
81    pthread_mutex_unlock(&fx->lib->lock);
82    return ret;
83}
84
85int Effect_Command(effect_handle_t self,
86                   uint32_t cmdCode,
87                   uint32_t cmdSize,
88                   void *pCmdData,
89                   uint32_t *replySize,
90                   void *pReplyData)
91{
92    int ret = init();
93    if (ret < 0) {
94        return ret;
95    }
96    effect_entry_t *fx = (effect_entry_t *)self;
97    pthread_mutex_lock(&gLibLock);
98    if (fx->lib == NULL) {
99        pthread_mutex_unlock(&gLibLock);
100        return -EPIPE;
101    }
102    pthread_mutex_lock(&fx->lib->lock);
103    pthread_mutex_unlock(&gLibLock);
104
105    ret = (*fx->subItfe)->command(fx->subItfe, cmdCode, cmdSize, pCmdData, replySize, pReplyData);
106    pthread_mutex_unlock(&fx->lib->lock);
107    return ret;
108}
109
110int Effect_GetDescriptor(effect_handle_t self,
111                         effect_descriptor_t *desc)
112{
113    int ret = init();
114    if (ret < 0) {
115        return ret;
116    }
117    effect_entry_t *fx = (effect_entry_t *)self;
118    pthread_mutex_lock(&gLibLock);
119    if (fx->lib == NULL) {
120        pthread_mutex_unlock(&gLibLock);
121        return -EPIPE;
122    }
123    pthread_mutex_lock(&fx->lib->lock);
124    pthread_mutex_unlock(&gLibLock);
125
126    ret = (*fx->subItfe)->get_descriptor(fx->subItfe, desc);
127    pthread_mutex_unlock(&fx->lib->lock);
128    return ret;
129}
130
131int Effect_ProcessReverse(effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
132{
133    int ret = init();
134    if (ret < 0) {
135        return ret;
136    }
137    effect_entry_t *fx = (effect_entry_t *)self;
138    pthread_mutex_lock(&gLibLock);
139    if (fx->lib == NULL) {
140        pthread_mutex_unlock(&gLibLock);
141        return -EPIPE;
142    }
143    pthread_mutex_lock(&fx->lib->lock);
144    pthread_mutex_unlock(&gLibLock);
145
146    if ((*fx->subItfe)->process_reverse != NULL) {
147        ret = (*fx->subItfe)->process_reverse(fx->subItfe, inBuffer, outBuffer);
148    } else {
149        ret = -ENOSYS;
150    }
151    pthread_mutex_unlock(&fx->lib->lock);
152    return ret;
153}
154
155
156const struct effect_interface_s gInterface = {
157        Effect_Process,
158        Effect_Command,
159        Effect_GetDescriptor,
160        NULL
161};
162
163const struct effect_interface_s gInterfaceWithReverse = {
164        Effect_Process,
165        Effect_Command,
166        Effect_GetDescriptor,
167        Effect_ProcessReverse
168};
169
170/////////////////////////////////////////////////
171//      Effect Factory Interface functions
172/////////////////////////////////////////////////
173
174int EffectQueryNumberEffects(uint32_t *pNumEffects)
175{
176    int ret = init();
177    if (ret < 0) {
178        return ret;
179    }
180    if (pNumEffects == NULL) {
181        return -EINVAL;
182    }
183
184    pthread_mutex_lock(&gLibLock);
185    *pNumEffects = gNumEffects;
186    gCanQueryEffect = 1;
187    pthread_mutex_unlock(&gLibLock);
188    ALOGV("EffectQueryNumberEffects(): %d", *pNumEffects);
189    return ret;
190}
191
192int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor)
193{
194    int ret = init();
195    if (ret < 0) {
196        return ret;
197    }
198    if (pDescriptor == NULL ||
199        index >= gNumEffects) {
200        return -EINVAL;
201    }
202    if (gCanQueryEffect == 0) {
203        return -ENOSYS;
204    }
205
206    pthread_mutex_lock(&gLibLock);
207    ret = -ENOENT;
208    if (index < gCurEffectIdx) {
209        resetEffectEnumeration();
210    }
211    while (gCurLib) {
212        if (gCurEffect) {
213            if (index == gCurEffectIdx) {
214                *pDescriptor = *(effect_descriptor_t *)gCurEffect->object;
215                ret = 0;
216                break;
217            } else {
218                gCurEffect = gCurEffect->next;
219                gCurEffectIdx++;
220            }
221        } else {
222            gCurLib = gCurLib->next;
223            gCurEffect = ((lib_entry_t *)gCurLib->object)->effects;
224        }
225    }
226
227#if (LOG_NDEBUG == 0)
228    char str[512];
229    dumpEffectDescriptor(pDescriptor, str, sizeof(str), 0 /* indent */);
230    ALOGV("EffectQueryEffect() desc:%s", str);
231#endif
232    pthread_mutex_unlock(&gLibLock);
233    return ret;
234}
235
236int EffectGetDescriptor(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor)
237{
238    lib_entry_t *l = NULL;
239    effect_descriptor_t *d = NULL;
240
241    int ret = init();
242    if (ret < 0) {
243        return ret;
244    }
245    if (pDescriptor == NULL || uuid == NULL) {
246        return -EINVAL;
247    }
248    pthread_mutex_lock(&gLibLock);
249    ret = findEffect(NULL, uuid, &l, &d);
250    if (ret == 0) {
251        *pDescriptor = *d;
252    }
253    pthread_mutex_unlock(&gLibLock);
254    return ret;
255}
256
257int EffectCreate(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, effect_handle_t *pHandle)
258{
259    list_elem_t *e = gLibraryList;
260    lib_entry_t *l = NULL;
261    effect_descriptor_t *d = NULL;
262    effect_handle_t itfe;
263    effect_entry_t *fx;
264    int ret;
265
266    if (uuid == NULL || pHandle == NULL) {
267        return -EINVAL;
268    }
269
270    ALOGV("EffectCreate() UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
271            uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion,
272            uuid->clockSeq, uuid->node[0], uuid->node[1],uuid->node[2],
273            uuid->node[3],uuid->node[4],uuid->node[5]);
274
275    ret = init();
276
277    if (ret < 0) {
278        ALOGW("EffectCreate() init error: %d", ret);
279        return ret;
280    }
281
282    pthread_mutex_lock(&gLibLock);
283
284    ret = findEffect(NULL, uuid, &l, &d);
285    if (ret < 0){
286        // Sub effects are not associated with the library->effects,
287        // so, findEffect will fail. Search for the effect in gSubEffectList.
288        ret = findSubEffect(uuid, &l, &d);
289        if (ret < 0 ) {
290            goto exit;
291        }
292    }
293
294    // create effect in library
295    ret = l->desc->create_effect(uuid, sessionId, ioId, &itfe);
296    if (ret != 0) {
297        ALOGW("EffectCreate() library %s: could not create fx %s, error %d", l->name, d->name, ret);
298        goto exit;
299    }
300
301    // add entry to effect list
302    fx = (effect_entry_t *)malloc(sizeof(effect_entry_t));
303    fx->subItfe = itfe;
304    if ((*itfe)->process_reverse != NULL) {
305        fx->itfe = (struct effect_interface_s *)&gInterfaceWithReverse;
306        ALOGV("EffectCreate() gInterfaceWithReverse");
307    }   else {
308        fx->itfe = (struct effect_interface_s *)&gInterface;
309        ALOGV("EffectCreate() gInterface");
310    }
311    fx->lib = l;
312
313    e = (list_elem_t *)malloc(sizeof(list_elem_t));
314    e->object = fx;
315    e->next = gEffectList;
316    gEffectList = e;
317
318    *pHandle = (effect_handle_t)fx;
319
320    ALOGV("EffectCreate() created entry %p with sub itfe %p in library %s", *pHandle, itfe, l->name);
321
322exit:
323    pthread_mutex_unlock(&gLibLock);
324    return ret;
325}
326
327int EffectRelease(effect_handle_t handle)
328{
329    effect_entry_t *fx;
330    list_elem_t *e1;
331    list_elem_t *e2;
332
333    int ret = init();
334    if (ret < 0) {
335        return ret;
336    }
337
338    // remove effect from effect list
339    pthread_mutex_lock(&gLibLock);
340    e1 = gEffectList;
341    e2 = NULL;
342    while (e1) {
343        if (e1->object == handle) {
344            if (e2) {
345                e2->next = e1->next;
346            } else {
347                gEffectList = e1->next;
348            }
349            fx = (effect_entry_t *)e1->object;
350            free(e1);
351            break;
352        }
353        e2 = e1;
354        e1 = e1->next;
355    }
356    if (e1 == NULL) {
357        ret = -ENOENT;
358        goto exit;
359    }
360
361    // release effect in library
362    if (fx->lib == NULL) {
363        ALOGW("EffectRelease() fx %p library already unloaded", handle);
364    } else {
365        pthread_mutex_lock(&fx->lib->lock);
366        fx->lib->desc->release_effect(fx->subItfe);
367        pthread_mutex_unlock(&fx->lib->lock);
368    }
369    free(fx);
370
371exit:
372    pthread_mutex_unlock(&gLibLock);
373    return ret;
374}
375
376int EffectIsNullUuid(const effect_uuid_t *uuid)
377{
378    if (memcmp(uuid, EFFECT_UUID_NULL, sizeof(effect_uuid_t))) {
379        return 0;
380    }
381    return 1;
382}
383
384// Function to get the sub effect descriptors of the effect whose uuid
385// is pointed by the first argument. It searches the gSubEffectList for the
386// matching uuid and then copies the corresponding sub effect descriptors
387// to the inout param
388int EffectGetSubEffects(const effect_uuid_t *uuid, sub_effect_entry_t **pSube,
389                        size_t size)
390{
391   ALOGV("EffectGetSubEffects() UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X"
392          "%02X\n",uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion,
393          uuid->clockSeq, uuid->node[0], uuid->node[1],uuid->node[2],
394          uuid->node[3],uuid->node[4],uuid->node[5]);
395
396   // Check if the size of the desc buffer is large enough for 2 subeffects
397   if ((uuid == NULL) || (pSube == NULL) || (size < 2)) {
398       ALOGW("NULL pointer or insufficient memory. Cannot query subeffects");
399       return -EINVAL;
400   }
401   int ret = init();
402   if (ret < 0)
403      return ret;
404   list_sub_elem_t *e = gSubEffectList;
405   sub_effect_entry_t *subeffect;
406   effect_descriptor_t *d;
407   int count = 0;
408   while (e != NULL) {
409       d = (effect_descriptor_t*)e->object;
410       if (memcmp(uuid, &d->uuid, sizeof(effect_uuid_t)) == 0) {
411           ALOGV("EffectGetSubEffects: effect found in the list");
412           list_elem_t *subefx = e->sub_elem;
413           while (subefx != NULL) {
414               subeffect = (sub_effect_entry_t*)subefx->object;
415               pSube[count++] = subeffect;
416               subefx = subefx->next;
417           }
418           ALOGV("EffectGetSubEffects end - copied the sub effect structures");
419           return count;
420       }
421       e = e->next;
422   }
423   return -ENOENT;
424}
425/////////////////////////////////////////////////
426//      Local functions
427/////////////////////////////////////////////////
428
429int init() {
430    if (gInitDone) {
431        return 0;
432    }
433
434    // ignore effects or not?
435    const bool ignoreFxConfFiles = property_get_bool(PROPERTY_IGNORE_EFFECTS, false);
436
437    pthread_mutex_init(&gLibLock, NULL);
438
439    if (ignoreFxConfFiles) {
440        ALOGI("Audio effects in configuration files will be ignored");
441    } else {
442        gConfigNbElemSkipped = EffectLoadXmlEffectConfig(NULL);
443        if (gConfigNbElemSkipped < 0) {
444            ALOGW("Failed to load XML effect configuration, fallback to .conf");
445            EffectLoadEffectConfig();
446        } else if (gConfigNbElemSkipped > 0) {
447            ALOGE("Effect config is partially invalid, skipped %zd elements", gConfigNbElemSkipped);
448        }
449    }
450
451    updateNumEffects();
452    gInitDone = 1;
453    ALOGV("init() done");
454    return 0;
455}
456
457// Searches the sub effect matching to the specified uuid
458// in the gSubEffectList. It gets the lib_entry_t for
459// the matched sub_effect . Used in EffectCreate of sub effects
460int findSubEffect(const effect_uuid_t *uuid,
461               lib_entry_t **lib,
462               effect_descriptor_t **desc)
463{
464    list_sub_elem_t *e = gSubEffectList;
465    list_elem_t *subefx;
466    sub_effect_entry_t *effect;
467    lib_entry_t *l = NULL;
468    effect_descriptor_t *d = NULL;
469    int found = 0;
470    int ret = 0;
471
472    if (uuid == NULL)
473        return -EINVAL;
474
475    while (e != NULL && !found) {
476        subefx = (list_elem_t*)(e->sub_elem);
477        while (subefx != NULL) {
478            effect = (sub_effect_entry_t*)subefx->object;
479            l = (lib_entry_t *)effect->lib;
480            d = (effect_descriptor_t *)effect->object;
481            if (memcmp(&d->uuid, uuid, sizeof(effect_uuid_t)) == 0) {
482                ALOGV("uuid matched");
483                found = 1;
484                break;
485            }
486            subefx = subefx->next;
487        }
488        e = e->next;
489    }
490    if (!found) {
491        ALOGV("findSubEffect() effect not found");
492        ret = -ENOENT;
493    } else {
494        ALOGV("findSubEffect() found effect: %s in lib %s", d->name, l->name);
495        *lib = l;
496        if (desc != NULL) {
497            *desc = d;
498        }
499    }
500    return ret;
501}
502
503void resetEffectEnumeration()
504{
505    gCurLib = gLibraryList;
506    gCurEffect = NULL;
507    if (gCurLib) {
508        gCurEffect = ((lib_entry_t *)gCurLib->object)->effects;
509    }
510    gCurEffectIdx = 0;
511}
512
513uint32_t updateNumEffects() {
514    list_elem_t *e;
515    uint32_t cnt = 0;
516
517    resetEffectEnumeration();
518
519    e = gLibraryList;
520    while (e) {
521        lib_entry_t *l = (lib_entry_t *)e->object;
522        list_elem_t *efx = l->effects;
523        while (efx) {
524            cnt++;
525            efx = efx->next;
526        }
527        e = e->next;
528    }
529    gNumEffects = cnt;
530    gCanQueryEffect = 0;
531    return cnt;
532}
533
534int EffectDumpEffects(int fd) {
535    char s[512];
536
537    list_elem_t *fe = gLibraryFailedList;
538    lib_failed_entry_t *fl = NULL;
539
540    dprintf(fd, "Libraries NOT loaded:\n");
541
542    while (fe) {
543        fl = (lib_failed_entry_t *)fe->object;
544        dprintf(fd, " Library %s\n", fl->name);
545        dprintf(fd, "  path: %s\n", fl->path);
546        fe = fe->next;
547    }
548
549    list_elem_t *e = gLibraryList;
550    lib_entry_t *l = NULL;
551    effect_descriptor_t *d = NULL;
552    int ret = 0;
553
554    dprintf(fd, "Libraries loaded:\n");
555    while (e) {
556        l = (lib_entry_t *)e->object;
557        list_elem_t *efx = l->effects;
558        dprintf(fd, " Library %s\n", l->name);
559        dprintf(fd, "  path: %s\n", l->path);
560        if (!efx) {
561            dprintf(fd, "  (no effects)\n");
562        }
563        while (efx) {
564            d = (effect_descriptor_t *)efx->object;
565            dumpEffectDescriptor(d, s, sizeof(s), 2);
566            dprintf(fd, "%s", s);
567            efx = efx->next;
568        }
569        e = e->next;
570    }
571
572    e = gSkippedEffects;
573    if (e) {
574        dprintf(fd, "Skipped effects\n");
575        while(e) {
576            d = (effect_descriptor_t *)e->object;
577            dumpEffectDescriptor(d, s, sizeof(s), 2 /* indent */);
578            dprintf(fd, "%s", s);
579            e = e->next;
580        }
581    }
582    switch (gConfigNbElemSkipped) {
583    case -2:
584        dprintf(fd, "Effect configuration loading skipped.\n");
585        break;
586    case -1:
587        dprintf(fd, "XML effect configuration failed to load.\n");
588        break;
589    case 0:
590        dprintf(fd, "XML effect configuration loaded successfully.\n");
591        break;
592    default:
593        dprintf(fd, "XML effect configuration partially loaded, skipped %zd elements.\n",
594                gConfigNbElemSkipped);
595    }
596    return ret;
597}
598
599