/*
 * Copyright (C) 2010 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 "EffectsFactory"
//#define LOG_NDEBUG 0

#include "EffectsFactory.h"
#include <string.h>
#include <stdlib.h>
#include <dlfcn.h>

#include <cutils/misc.h>
#include <cutils/config_utils.h>
#include <cutils/properties.h>
#include <audio_effects/audio_effects_conf.h>

static list_elem_t *gEffectList; // list of effect_entry_t: all currently created effects
static list_elem_t *gLibraryList; // list of lib_entry_t: all currently loaded libraries
static list_elem_t *gSkippedEffects; // list of effects skipped because of duplicate uuid
// list of effect_descriptor and list of sub effects : all currently loaded
// It does not contain effects without sub effects.
static list_sub_elem_t *gSubEffectList;
static pthread_mutex_t gLibLock = PTHREAD_MUTEX_INITIALIZER; // controls access to gLibraryList
static uint32_t gNumEffects;         // total number number of effects
static list_elem_t *gCurLib;    // current library in enumeration process
static list_elem_t *gCurEffect; // current effect in enumeration process
static uint32_t gCurEffectIdx;       // current effect index in enumeration process
static lib_entry_t *gCachedLibrary;  // last library accessed by getLibrary()

static int gInitDone; // true is global initialization has been preformed
static int gCanQueryEffect; // indicates that call to EffectQueryEffect() is valid, i.e. that the list of effects
                          // was not modified since last call to EffectQueryNumberEffects()


/////////////////////////////////////////////////
//      Local functions prototypes
/////////////////////////////////////////////////

static int init();
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 void resetEffectEnumeration();
static uint32_t updateNumEffects();
static int findEffect(const effect_uuid_t *type,
               const effect_uuid_t *uuid,
               lib_entry_t **lib,
               effect_descriptor_t **desc);
// To search a subeffect in the gSubEffectList
static int findSubEffect(const effect_uuid_t *uuid,
               lib_entry_t **lib,
               effect_descriptor_t **desc);
static void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len, int indent);
static int stringToUuid(const char *str, effect_uuid_t *uuid);
static int uuidToString(const effect_uuid_t *uuid, char *str, size_t maxLen);

/////////////////////////////////////////////////
//      Effect Control Interface functions
/////////////////////////////////////////////////

int Effect_Process(effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
{
    int ret = init();
    if (ret < 0) {
        return ret;
    }
    effect_entry_t *fx = (effect_entry_t *)self;
    pthread_mutex_lock(&gLibLock);
    if (fx->lib == NULL) {
        pthread_mutex_unlock(&gLibLock);
        return -EPIPE;
    }
    pthread_mutex_lock(&fx->lib->lock);
    pthread_mutex_unlock(&gLibLock);

    ret = (*fx->subItfe)->process(fx->subItfe, inBuffer, outBuffer);
    pthread_mutex_unlock(&fx->lib->lock);
    return ret;
}

int Effect_Command(effect_handle_t self,
                   uint32_t cmdCode,
                   uint32_t cmdSize,
                   void *pCmdData,
                   uint32_t *replySize,
                   void *pReplyData)
{
    int ret = init();
    if (ret < 0) {
        return ret;
    }
    effect_entry_t *fx = (effect_entry_t *)self;
    pthread_mutex_lock(&gLibLock);
    if (fx->lib == NULL) {
        pthread_mutex_unlock(&gLibLock);
        return -EPIPE;
    }
    pthread_mutex_lock(&fx->lib->lock);
    pthread_mutex_unlock(&gLibLock);

    ret = (*fx->subItfe)->command(fx->subItfe, cmdCode, cmdSize, pCmdData, replySize, pReplyData);
    pthread_mutex_unlock(&fx->lib->lock);
    return ret;
}

int Effect_GetDescriptor(effect_handle_t self,
                         effect_descriptor_t *desc)
{
    int ret = init();
    if (ret < 0) {
        return ret;
    }
    effect_entry_t *fx = (effect_entry_t *)self;
    pthread_mutex_lock(&gLibLock);
    if (fx->lib == NULL) {
        pthread_mutex_unlock(&gLibLock);
        return -EPIPE;
    }
    pthread_mutex_lock(&fx->lib->lock);
    pthread_mutex_unlock(&gLibLock);

    ret = (*fx->subItfe)->get_descriptor(fx->subItfe, desc);
    pthread_mutex_unlock(&fx->lib->lock);
    return ret;
}

int Effect_ProcessReverse(effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
{
    int ret = init();
    if (ret < 0) {
        return ret;
    }
    effect_entry_t *fx = (effect_entry_t *)self;
    pthread_mutex_lock(&gLibLock);
    if (fx->lib == NULL) {
        pthread_mutex_unlock(&gLibLock);
        return -EPIPE;
    }
    pthread_mutex_lock(&fx->lib->lock);
    pthread_mutex_unlock(&gLibLock);

    if ((*fx->subItfe)->process_reverse != NULL) {
        ret = (*fx->subItfe)->process_reverse(fx->subItfe, inBuffer, outBuffer);
    } else {
        ret = -ENOSYS;
    }
    pthread_mutex_unlock(&fx->lib->lock);
    return ret;
}


const struct effect_interface_s gInterface = {
        Effect_Process,
        Effect_Command,
        Effect_GetDescriptor,
        NULL
};

const struct effect_interface_s gInterfaceWithReverse = {
        Effect_Process,
        Effect_Command,
        Effect_GetDescriptor,
        Effect_ProcessReverse
};

/////////////////////////////////////////////////
//      Effect Factory Interface functions
/////////////////////////////////////////////////

int EffectQueryNumberEffects(uint32_t *pNumEffects)
{
    int ret = init();
    if (ret < 0) {
        return ret;
    }
    if (pNumEffects == NULL) {
        return -EINVAL;
    }

    pthread_mutex_lock(&gLibLock);
    *pNumEffects = gNumEffects;
    gCanQueryEffect = 1;
    pthread_mutex_unlock(&gLibLock);
    ALOGV("EffectQueryNumberEffects(): %d", *pNumEffects);
    return ret;
}

int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor)
{
    int ret = init();
    if (ret < 0) {
        return ret;
    }
    if (pDescriptor == NULL ||
        index >= gNumEffects) {
        return -EINVAL;
    }
    if (gCanQueryEffect == 0) {
        return -ENOSYS;
    }

    pthread_mutex_lock(&gLibLock);
    ret = -ENOENT;
    if (index < gCurEffectIdx) {
        resetEffectEnumeration();
    }
    while (gCurLib) {
        if (gCurEffect) {
            if (index == gCurEffectIdx) {
                *pDescriptor = *(effect_descriptor_t *)gCurEffect->object;
                ret = 0;
                break;
            } else {
                gCurEffect = gCurEffect->next;
                gCurEffectIdx++;
            }
        } else {
            gCurLib = gCurLib->next;
            gCurEffect = ((lib_entry_t *)gCurLib->object)->effects;
        }
    }

#if (LOG_NDEBUG == 0)
    char str[512];
    dumpEffectDescriptor(pDescriptor, str, sizeof(str), 0 /* indent */);
    ALOGV("EffectQueryEffect() desc:%s", str);
#endif
    pthread_mutex_unlock(&gLibLock);
    return ret;
}

int EffectGetDescriptor(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor)
{
    lib_entry_t *l = NULL;
    effect_descriptor_t *d = NULL;

    int ret = init();
    if (ret < 0) {
        return ret;
    }
    if (pDescriptor == NULL || uuid == NULL) {
        return -EINVAL;
    }
    pthread_mutex_lock(&gLibLock);
    ret = findEffect(NULL, uuid, &l, &d);
    if (ret == 0) {
        *pDescriptor = *d;
    }
    pthread_mutex_unlock(&gLibLock);
    return ret;
}

int EffectCreate(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, effect_handle_t *pHandle)
{
    list_elem_t *e = gLibraryList;
    lib_entry_t *l = NULL;
    effect_descriptor_t *d = NULL;
    effect_handle_t itfe;
    effect_entry_t *fx;
    int found = 0;
    int ret;

    if (uuid == NULL || pHandle == NULL) {
        return -EINVAL;
    }

    ALOGV("EffectCreate() UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
            uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion,
            uuid->clockSeq, uuid->node[0], uuid->node[1],uuid->node[2],
            uuid->node[3],uuid->node[4],uuid->node[5]);

    ret = init();

    if (ret < 0) {
        ALOGW("EffectCreate() init error: %d", ret);
        return ret;
    }

    pthread_mutex_lock(&gLibLock);

    ret = findEffect(NULL, uuid, &l, &d);
    if (ret < 0){
        // Sub effects are not associated with the library->effects,
        // so, findEffect will fail. Search for the effect in gSubEffectList.
        ret = findSubEffect(uuid, &l, &d);
        if (ret < 0 ) {
            goto exit;
        }
    }

    // create effect in library
    ret = l->desc->create_effect(uuid, sessionId, ioId, &itfe);
    if (ret != 0) {
        ALOGW("EffectCreate() library %s: could not create fx %s, error %d", l->name, d->name, ret);
        goto exit;
    }

    // add entry to effect list
    fx = (effect_entry_t *)malloc(sizeof(effect_entry_t));
    fx->subItfe = itfe;
    if ((*itfe)->process_reverse != NULL) {
        fx->itfe = (struct effect_interface_s *)&gInterfaceWithReverse;
        ALOGV("EffectCreate() gInterfaceWithReverse");
    }   else {
        fx->itfe = (struct effect_interface_s *)&gInterface;
        ALOGV("EffectCreate() gInterface");
    }
    fx->lib = l;

    e = (list_elem_t *)malloc(sizeof(list_elem_t));
    e->object = fx;
    e->next = gEffectList;
    gEffectList = e;

    *pHandle = (effect_handle_t)fx;

    ALOGV("EffectCreate() created entry %p with sub itfe %p in library %s", *pHandle, itfe, l->name);

exit:
    pthread_mutex_unlock(&gLibLock);
    return ret;
}

int EffectRelease(effect_handle_t handle)
{
    effect_entry_t *fx;
    list_elem_t *e1;
    list_elem_t *e2;

    int ret = init();
    if (ret < 0) {
        return ret;
    }

    // remove effect from effect list
    pthread_mutex_lock(&gLibLock);
    e1 = gEffectList;
    e2 = NULL;
    while (e1) {
        if (e1->object == handle) {
            if (e2) {
                e2->next = e1->next;
            } else {
                gEffectList = e1->next;
            }
            fx = (effect_entry_t *)e1->object;
            free(e1);
            break;
        }
        e2 = e1;
        e1 = e1->next;
    }
    if (e1 == NULL) {
        ret = -ENOENT;
        goto exit;
    }

    // release effect in library
    if (fx->lib == NULL) {
        ALOGW("EffectRelease() fx %p library already unloaded", handle);
    } else {
        pthread_mutex_lock(&fx->lib->lock);
        fx->lib->desc->release_effect(fx->subItfe);
        pthread_mutex_unlock(&fx->lib->lock);
    }
    free(fx);

exit:
    pthread_mutex_unlock(&gLibLock);
    return ret;
}

int EffectIsNullUuid(const effect_uuid_t *uuid)
{
    if (memcmp(uuid, EFFECT_UUID_NULL, sizeof(effect_uuid_t))) {
        return 0;
    }
    return 1;
}

// Function to get the sub effect descriptors of the effect whose uuid
// is pointed by the first argument. It searches the gSubEffectList for the
// matching uuid and then copies the corresponding sub effect descriptors
// to the inout param
int EffectGetSubEffects(const effect_uuid_t *uuid, sub_effect_entry_t **pSube,
                        size_t size)
{
   ALOGV("EffectGetSubEffects() UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X"
          "%02X\n",uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion,
          uuid->clockSeq, uuid->node[0], uuid->node[1],uuid->node[2],
          uuid->node[3],uuid->node[4],uuid->node[5]);

   // Check if the size of the desc buffer is large enough for 2 subeffects
   if ((uuid == NULL) || (pSube == NULL) || (size < 2)) {
       ALOGW("NULL pointer or insufficient memory. Cannot query subeffects");
       return -EINVAL;
   }
   int ret = init();
   if (ret < 0)
      return ret;
   list_sub_elem_t *e = gSubEffectList;
   sub_effect_entry_t *subeffect;
   effect_descriptor_t *d;
   int count = 0;
   while (e != NULL) {
       d = (effect_descriptor_t*)e->object;
       if (memcmp(uuid, &d->uuid, sizeof(effect_uuid_t)) == 0) {
           ALOGV("EffectGetSubEffects: effect found in the list");
           list_elem_t *subefx = e->sub_elem;
           while (subefx != NULL) {
               subeffect = (sub_effect_entry_t*)subefx->object;
               pSube[count++] = subeffect;
               subefx = subefx->next;
           }
           ALOGV("EffectGetSubEffects end - copied the sub effect structures");
           return count;
       }
       e = e->next;
   }
   return -ENOENT;
}
/////////////////////////////////////////////////
//      Local functions
/////////////////////////////////////////////////

int init() {
    int hdl;

    if (gInitDone) {
        return 0;
    }

    // ignore effects or not?
    const bool ignoreFxConfFiles = property_get_bool(PROPERTY_IGNORE_EFFECTS, false);

    pthread_mutex_init(&gLibLock, NULL);

    if (ignoreFxConfFiles) {
        ALOGI("Audio effects in configuration files will be ignored");
    } else {
        if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) {
            loadEffectConfigFile(AUDIO_EFFECT_VENDOR_CONFIG_FILE);
        } else if (access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) {
            loadEffectConfigFile(AUDIO_EFFECT_DEFAULT_CONFIG_FILE);
        }
    }

    updateNumEffects();
    gInitDone = 1;
    ALOGV("init() done");
    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;
}

int loadLibrary(cnode *root, const char *name)
{
    cnode *node;
    void *hdl;
    audio_effect_library_t *desc;
    list_elem_t *e;
    lib_entry_t *l;
    char path[PATH_MAX];
    char *str;
    size_t len;

    node = config_find(root, PATH_TAG);
    if (node == NULL) {
        return -EINVAL;
    }
    // audio_effects.conf always specifies 32 bit lib path: convert to 64 bit path if needed
    strlcpy(path, node->value, PATH_MAX);
#ifdef __LP64__
    str = strstr(path, "/lib/");
    if (str == NULL)
        return -EINVAL;
    len = str - path;
    path[len] = '\0';
    strlcat(path, "/lib64/", PATH_MAX);
    strlcat(path, node->value + len + strlen("/lib/"), PATH_MAX);
#endif
    if (strlen(path) >= PATH_MAX - 1)
        return -EINVAL;

    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);
    }
    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 dummy 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;
}

// Searches the sub effect matching to the specified uuid
// in the gSubEffectList. It gets the lib_entry_t for
// the matched sub_effect . Used in EffectCreate of sub effects
int findSubEffect(const effect_uuid_t *uuid,
               lib_entry_t **lib,
               effect_descriptor_t **desc)
{
    list_sub_elem_t *e = gSubEffectList;
    list_elem_t *subefx;
    sub_effect_entry_t *effect;
    lib_entry_t *l = NULL;
    effect_descriptor_t *d = NULL;
    int found = 0;
    int ret = 0;

    if (uuid == NULL)
        return -EINVAL;

    while (e != NULL && !found) {
        subefx = (list_elem_t*)(e->sub_elem);
        while (subefx != NULL) {
            effect = (sub_effect_entry_t*)subefx->object;
            l = (lib_entry_t *)effect->lib;
            d = (effect_descriptor_t *)effect->object;
            if (memcmp(&d->uuid, uuid, sizeof(effect_uuid_t)) == 0) {
                ALOGV("uuid matched");
                found = 1;
                break;
            }
            subefx = subefx->next;
        }
        e = e->next;
    }
    if (!found) {
        ALOGV("findSubEffect() effect not found");
        ret = -ENOENT;
    } else {
        ALOGV("findSubEffect() found effect: %s in lib %s", d->name, l->name);
        *lib = l;
        if (desc != NULL) {
            *desc = d;
        }
    }
    return ret;
}

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;
}


void resetEffectEnumeration()
{
    gCurLib = gLibraryList;
    gCurEffect = NULL;
    if (gCurLib) {
        gCurEffect = ((lib_entry_t *)gCurLib->object)->effects;
    }
    gCurEffectIdx = 0;
}

uint32_t updateNumEffects() {
    list_elem_t *e;
    uint32_t cnt = 0;

    resetEffectEnumeration();

    e = gLibraryList;
    while (e) {
        lib_entry_t *l = (lib_entry_t *)e->object;
        list_elem_t *efx = l->effects;
        while (efx) {
            cnt++;
            efx = efx->next;
        }
        e = e->next;
    }
    gNumEffects = cnt;
    gCanQueryEffect = 0;
    return cnt;
}

int findEffect(const effect_uuid_t *type,
               const effect_uuid_t *uuid,
               lib_entry_t **lib,
               effect_descriptor_t **desc)
{
    list_elem_t *e = gLibraryList;
    lib_entry_t *l = NULL;
    effect_descriptor_t *d = NULL;
    int found = 0;
    int ret = 0;

    while (e && !found) {
        l = (lib_entry_t *)e->object;
        list_elem_t *efx = l->effects;
        while (efx) {
            d = (effect_descriptor_t *)efx->object;
            if (type != NULL && memcmp(&d->type, type, sizeof(effect_uuid_t)) == 0) {
                found = 1;
                break;
            }
            if (uuid != NULL && memcmp(&d->uuid, uuid, sizeof(effect_uuid_t)) == 0) {
                found = 1;
                break;
            }
            efx = efx->next;
        }
        e = e->next;
    }
    if (!found) {
        ALOGV("findEffect() effect not found");
        ret = -ENOENT;
    } else {
        ALOGV("findEffect() found effect: %s in lib %s", d->name, l->name);
        *lib = l;
        if (desc) {
            *desc = d;
        }
    }

    return ret;
}

void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len, int indent) {
    char s[256];
    char ss[256];
    char idt[indent + 1];

    memset(idt, ' ', indent);
    idt[indent] = 0;

    str[0] = 0;

    snprintf(s, sizeof(s), "%s%s / %s\n", idt, desc->name, desc->implementor);
    strlcat(str, s, len);

    uuidToString(&desc->uuid, s, sizeof(s));
    snprintf(ss, sizeof(ss), "%s  UUID: %s\n", idt, s);
    strlcat(str, ss, len);

    uuidToString(&desc->type, s, sizeof(s));
    snprintf(ss, sizeof(ss), "%s  TYPE: %s\n", idt, s);
    strlcat(str, ss, len);

    sprintf(s, "%s  apiVersion: %08X\n%s  flags: %08X\n", idt,
            desc->apiVersion, idt, desc->flags);
    strlcat(str, s, len);
}

int stringToUuid(const char *str, effect_uuid_t *uuid)
{
    int tmp[10];

    if (sscanf(str, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
            tmp, tmp+1, tmp+2, tmp+3, tmp+4, tmp+5, tmp+6, tmp+7, tmp+8, tmp+9) < 10) {
        return -EINVAL;
    }
    uuid->timeLow = (uint32_t)tmp[0];
    uuid->timeMid = (uint16_t)tmp[1];
    uuid->timeHiAndVersion = (uint16_t)tmp[2];
    uuid->clockSeq = (uint16_t)tmp[3];
    uuid->node[0] = (uint8_t)tmp[4];
    uuid->node[1] = (uint8_t)tmp[5];
    uuid->node[2] = (uint8_t)tmp[6];
    uuid->node[3] = (uint8_t)tmp[7];
    uuid->node[4] = (uint8_t)tmp[8];
    uuid->node[5] = (uint8_t)tmp[9];

    return 0;
}

int uuidToString(const effect_uuid_t *uuid, char *str, size_t maxLen)
{

    snprintf(str, maxLen, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
            uuid->timeLow,
            uuid->timeMid,
            uuid->timeHiAndVersion,
            uuid->clockSeq,
            uuid->node[0],
            uuid->node[1],
            uuid->node[2],
            uuid->node[3],
            uuid->node[4],
            uuid->node[5]);

    return 0;
}

int EffectDumpEffects(int fd) {
    char s[512];
    list_elem_t *e = gLibraryList;
    lib_entry_t *l = NULL;
    effect_descriptor_t *d = NULL;
    int found = 0;
    int ret = 0;

    while (e) {
        l = (lib_entry_t *)e->object;
        list_elem_t *efx = l->effects;
        dprintf(fd, "Library %s\n", l->name);
        if (!efx) {
            dprintf(fd, "  (no effects)\n");
        }
        while (efx) {
            d = (effect_descriptor_t *)efx->object;
            dumpEffectDescriptor(d, s, sizeof(s), 2);
            dprintf(fd, "%s", s);
            efx = efx->next;
        }
        e = e->next;
    }

    e = gSkippedEffects;
    if (e) {
        dprintf(fd, "Skipped effects\n");
        while(e) {
            d = (effect_descriptor_t *)e->object;
            dumpEffectDescriptor(d, s, sizeof(s), 2 /* indent */);
            dprintf(fd, "%s", s);
            e = e->next;
        }
    }
    return ret;
}