Merge "aaudio: command line argument parser"
diff --git a/drm/mediacas/plugins/mock/Android.mk b/drm/mediacas/plugins/mock/Android.mk
index a97fac6..a1d61da 100644
--- a/drm/mediacas/plugins/mock/Android.mk
+++ b/drm/mediacas/plugins/mock/Android.mk
@@ -28,6 +28,8 @@
 LOCAL_SHARED_LIBRARIES := \
     libutils liblog
 
+LOCAL_HEADER_LIBRARIES := media_plugin_headers
+
 LOCAL_C_INCLUDES += \
     $(TOP)/frameworks/av/include \
     $(TOP)/frameworks/native/include/media \
diff --git a/media/libeffects/Android.bp b/media/libeffects/Android.bp
index ccaa2b4..0dd3f17 100644
--- a/media/libeffects/Android.bp
+++ b/media/libeffects/Android.bp
@@ -1 +1 @@
-subdirs = ["factory"]
+subdirs = ["factory", "config"]
diff --git a/media/libeffects/config/Android.bp b/media/libeffects/config/Android.bp
new file mode 100644
index 0000000..5666d14
--- /dev/null
+++ b/media/libeffects/config/Android.bp
@@ -0,0 +1,14 @@
+// Effect configuration
+cc_library_shared {
+    name: "libeffectsconfig",
+    vendor_available: true,
+
+    srcs: ["src/EffectsConfig.cpp"],
+
+    shared_libs: [
+        "liblog",
+        "libtinyxml2",
+    ],
+
+    export_include_dirs: ["include"],
+}
diff --git a/media/libeffects/config/include/media/EffectsConfig.h b/media/libeffects/config/include/media/EffectsConfig.h
new file mode 100644
index 0000000..811730c
--- /dev/null
+++ b/media/libeffects/config/include/media/EffectsConfig.h
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+
+#ifndef ANDROID_MEDIA_EFFECTSCONFIG_H
+#define ANDROID_MEDIA_EFFECTSCONFIG_H
+
+/** @file Parses audio effects configuration file to C and C++ structure.
+ * @see audio_effects_conf_V2_0.xsd for documentation on each structure
+ */
+
+#include <system/audio_effect.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace effectsConfig {
+
+/** Default path of effect configuration file. */
+constexpr char DEFAULT_PATH[] = "/vendor/etc/audio_effects.xml";
+
+/** Directories where the effect libraries will be search for. */
+constexpr const char* LD_EFFECT_LIBRARY_PATH[] =
+#ifdef __LP64__
+        {"/odm/lib64/soundfx", "/vendor/lib64/soundfx", "/system/lib64/soundfx"};
+#else
+        {"/odm/lib/soundfx", "/vendor/lib/soundfx", "/system/lib/soundfx"};
+#endif
+
+struct Library {
+    std::string name;
+    std::string path;
+};
+using Libraries = std::vector<Library>;
+
+struct EffectImpl {
+    Library* library; //< Only valid as long as the associated library vector is unmodified
+    effect_uuid_t uuid;
+};
+
+struct Effect : public EffectImpl {
+    std::string name;
+    bool isProxy;
+    EffectImpl libSw; //< Only valid if isProxy
+    EffectImpl libHw; //< Only valid if isProxy
+};
+
+using Effects = std::vector<Effect>;
+
+template <class Type>
+struct Stream {
+    Type type;
+    std::vector<std::reference_wrapper<Effect>> effects;
+};
+using OutputStream = Stream<audio_stream_type_t>;
+using InputStream = Stream<audio_source_t>;
+
+/** Parsed configuration.
+ * Intended to be a transient structure only used for deserialization.
+ * Note: Everything is copied in the configuration from the xml dom.
+ *       If copies needed to be avoided due to performance issue,
+ *       consider keeping a private handle on the xml dom and replace all strings by dom pointers.
+ *       Or even better, use SAX parsing to avoid the allocations all together.
+ */
+struct Config {
+    float version;
+    Libraries libraries;
+    Effects effects;
+    std::vector<OutputStream> postprocess;
+    std::vector<InputStream> preprocess;
+};
+
+/** Result of `parse(const char*)` */
+struct ParsingResult {
+    /** Parsed config, nullptr if the xml lib could not load the file */
+    std::unique_ptr<Config> parsedConfig;
+    size_t nbSkippedElement; //< Number of skipped invalid library, effect or processing chain
+};
+
+/** Parses the provided effect configuration.
+ * Parsing do not stop of first invalid element, but continues to the next.
+ * @see ParsingResult::nbSkippedElement
+ */
+ParsingResult parse(const char* path = DEFAULT_PATH);
+
+} // namespace effectsConfig
+} // namespace android
+#endif  // ANDROID_MEDIA_EFFECTSCONFIG_H
diff --git a/media/libeffects/config/src/EffectsConfig.cpp b/media/libeffects/config/src/EffectsConfig.cpp
new file mode 100644
index 0000000..97462f8
--- /dev/null
+++ b/media/libeffects/config/src/EffectsConfig.cpp
@@ -0,0 +1,302 @@
+/*
+ * 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 "EffectsConfig"
+
+#include <algorithm>
+#include <cstdint>
+#include <functional>
+#include <string>
+
+#include <tinyxml2.h>
+#include <log/log.h>
+
+#include <media/EffectsConfig.h>
+
+using namespace tinyxml2;
+
+namespace android {
+namespace effectsConfig {
+
+/** All functions except `parse(const char*)` are static. */
+namespace {
+
+/** @return all `node`s children that are elements and match the tag if provided. */
+std::vector<std::reference_wrapper<const XMLElement>> getChildren(const XMLNode& node,
+                                                                  const char* childTag = nullptr) {
+    std::vector<std::reference_wrapper<const XMLElement>> children;
+    for (auto* child = node.FirstChildElement(childTag); child != nullptr;
+            child = child->NextSiblingElement(childTag)) {
+        children.emplace_back(*child);
+    }
+    return children;
+}
+
+/** @return xml dump of the provided element.
+ * By not providing a printer, it is implicitly created in the caller context.
+ * In such case the return pointer has the same lifetime as the expression containing dump().
+ */
+const char* dump(const XMLElement& element, XMLPrinter&& printer = {}) {
+    element.Accept(&printer);
+    return printer.CStr();
+}
+
+
+bool stringToUuid(const char *str, effect_uuid_t *uuid)
+{
+    uint32_t 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 false;
+    }
+    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 true;
+}
+
+/** Map the enum and string representation of a string type.
+ *  Intended to be specialized for each enum to deserialize.
+ *  The general template is disabled.
+ */
+template <class Enum>
+constexpr std::enable_if<false, Enum> STREAM_NAME_MAP;
+
+/** All output stream types which support effects.
+ * This need to be kept in sink with the xsd streamOutputType.
+ */
+template <>
+constexpr std::pair<audio_stream_type_t, const char*> STREAM_NAME_MAP<audio_stream_type_t>[] = {
+        {AUDIO_STREAM_VOICE_CALL, "voice_call"},
+        {AUDIO_STREAM_SYSTEM, "system"},
+        {AUDIO_STREAM_RING, "ring"},
+        {AUDIO_STREAM_MUSIC, "music"},
+        {AUDIO_STREAM_ALARM, "alarm"},
+        {AUDIO_STREAM_NOTIFICATION, "notification"},
+        {AUDIO_STREAM_BLUETOOTH_SCO, "bluetooth_sco"},
+        {AUDIO_STREAM_ENFORCED_AUDIBLE, "enforced_audible"},
+        {AUDIO_STREAM_DTMF, "dtmf"},
+        {AUDIO_STREAM_TTS, "tts"},
+};
+
+/** All input stream types which support effects.
+ * This need to be kept in sink with the xsd streamOutputType.
+ */
+template <>
+constexpr std::pair<audio_source_t, const char*> STREAM_NAME_MAP<audio_source_t>[] = {
+        {AUDIO_SOURCE_MIC, "mic"},
+        {AUDIO_SOURCE_VOICE_UPLINK, "voice_uplink"},
+        {AUDIO_SOURCE_VOICE_DOWNLINK, "voice_downlink"},
+        {AUDIO_SOURCE_VOICE_CALL, "voice_call"},
+        {AUDIO_SOURCE_CAMCORDER, "camcorder"},
+        {AUDIO_SOURCE_VOICE_RECOGNITION, "voice_recognition"},
+        {AUDIO_SOURCE_VOICE_COMMUNICATION, "voice_communication"},
+        {AUDIO_SOURCE_UNPROCESSED, "unprocessed"},
+};
+
+/** Find the stream type enum corresponding to the stream type name or return false */
+template <class Type>
+bool stringToStreamType(const char *streamName, Type* type)
+{
+    for (auto& streamNamePair : STREAM_NAME_MAP<Type>) {
+        if (strcmp(streamNamePair.second, streamName) == 0) {
+            *type = streamNamePair.first;
+            return true;
+        }
+    }
+    return false;
+}
+
+/** Parse a library xml note and push the result in libraries or return false on failure. */
+bool parseLibrary(const XMLElement& xmlLibrary, Libraries* libraries) {
+    const char* name = xmlLibrary.Attribute("name");
+    const char* path = xmlLibrary.Attribute("path");
+    if (name == nullptr || path == nullptr) {
+        ALOGE("library must have a name and a path: %s", dump(xmlLibrary));
+        return false;
+    }
+    libraries->push_back({name, path});
+    return true;
+}
+
+/** Find an element in a collection by its name.
+ * @return nullptr if not found, the ellements address if found.
+ */
+template <class T>
+T* findByName(const char* name, std::vector<T>& collection) {
+    auto it = find_if(begin(collection), end(collection),
+                         [name] (auto& item) { return item.name == name; });
+    return it != end(collection) ? &*it : nullptr;
+}
+
+/** Parse an effect from an xml element describing it.
+ * @return true and pushes the effect in effects on success,
+ *         false on failure. */
+bool parseEffect(const XMLElement& xmlEffect, Libraries& libraries, Effects* effects) {
+    Effect effect{};
+
+    const char* name = xmlEffect.Attribute("name");
+    if (name == nullptr) {
+        ALOGE("%s must have a name: %s", xmlEffect.Value(), dump(xmlEffect));
+        return false;
+    }
+    effect.name = name;
+
+    // Function to parse effect.library and effect.uuid from xml
+    auto parseImpl = [&libraries](const XMLElement& xmlImpl, EffectImpl& effect) {
+        // Retrieve library name and uuid from xml
+        const char* libraryName = xmlImpl.Attribute("library");
+        const char* uuid = xmlImpl.Attribute("uuid");
+        if (libraryName == nullptr || uuid == nullptr) {
+            ALOGE("effect must have a library name and a uuid: %s", dump(xmlImpl));
+            return false;
+        }
+
+        // Convert library name to a pointer to the previously loaded library
+        auto* library = findByName(libraryName, libraries);
+        if (library == nullptr) {
+            ALOGE("Could not find library referenced in: %s", dump(xmlImpl));
+            return false;
+        }
+        effect.library = library;
+
+        if (!stringToUuid(uuid, &effect.uuid)) {
+            ALOGE("Invalid uuid in: %s", dump(xmlImpl));
+            return false;
+        }
+        return true;
+    };
+
+    if (!parseImpl(xmlEffect, effect)) {
+        return false;
+    }
+
+    // Handle proxy effects
+    effect.isProxy = false;
+    if (std::strcmp(xmlEffect.Name(), "effectProxy") == 0) {
+        effect.isProxy = true;
+
+        // Function to parse libhw and libsw
+        auto parseProxy = [&xmlEffect, &parseImpl](const char* tag, EffectImpl& proxyLib) {
+            auto* xmlProxyLib = xmlEffect.FirstChildElement(tag);
+            if (xmlProxyLib == nullptr) {
+                ALOGE("effectProxy must contain a <%s>: %s", tag, dump(*xmlProxyLib));
+                return false;
+            }
+            return parseImpl(*xmlProxyLib, proxyLib);
+        };
+        if (!parseProxy("libhw", effect.libHw) || !parseProxy("libsw", effect.libSw)) {
+            return false;
+        }
+    }
+
+    effects->push_back(std::move(effect));
+    return true;
+}
+
+/** Parse an stream from an xml element describing it.
+ * @return true and pushes the stream in streams on success,
+ *         false on failure. */
+template <class Stream>
+bool parseStream(const XMLElement& xmlStream, Effects& effects, std::vector<Stream>* streams) {
+    const char* streamType = xmlStream.Attribute("type");
+    if (streamType == nullptr) {
+        ALOGE("stream must have a type: %s", dump(xmlStream));
+        return false;
+    }
+    Stream stream;
+    if (!stringToStreamType(streamType, &stream.type)) {
+        ALOGE("Invalid stream type %s: %s", streamType, dump(xmlStream));
+        return false;
+    }
+
+    for (auto& xmlApply : getChildren(xmlStream, "apply")) {
+        const char* effectName = xmlApply.get().Attribute("effect");
+        if (effectName == nullptr) {
+            ALOGE("stream/apply must have reference an effect: %s", dump(xmlApply));
+            return false;
+        }
+        auto* effect = findByName(effectName, effects);
+        if (effect == nullptr) {
+            ALOGE("Could not find effect referenced in: %s", dump(xmlApply));
+            return false;
+        }
+        stream.effects.emplace_back(*effect);
+    }
+    streams->push_back(std::move(stream));
+    return true;
+}
+
+}; // namespace
+
+ParsingResult parse(const char* path) {
+    XMLDocument doc;
+    doc.LoadFile(path);
+    if (doc.Error()) {
+        ALOGE("Failed to parse %s: Tinyxml2 error (%d): %s %s", path,
+              doc.ErrorID(), doc.GetErrorStr1(), doc.GetErrorStr2());
+        return {nullptr, 0};
+    }
+
+    auto config = std::make_unique<Config>();
+    size_t nbSkippedElements = 0;
+    auto registerFailure = [&nbSkippedElements](bool result) {
+        nbSkippedElements += result ? 0 : 1;
+    };
+    for (auto& xmlConfig : getChildren(doc, "audio_effects_conf")) {
+
+        // Parse library
+        for (auto& xmlLibraries : getChildren(xmlConfig, "libraries")) {
+            for (auto& xmlLibrary : getChildren(xmlLibraries, "library")) {
+                registerFailure(parseLibrary(xmlLibrary, &config->libraries));
+            }
+        }
+
+        // Parse effects
+        for (auto& xmlEffects : getChildren(xmlConfig, "effects")) {
+            for (auto& xmlEffect : getChildren(xmlEffects)) {
+                registerFailure(parseEffect(xmlEffect, config->libraries, &config->effects));
+            }
+        }
+
+        // Parse pre processing chains
+        for (auto& xmlPreprocess : getChildren(xmlConfig, "preprocess")) {
+            for (auto& xmlStream : getChildren(xmlPreprocess, "stream")) {
+                registerFailure(parseStream(xmlStream, config->effects, &config->preprocess));
+            }
+        }
+
+        // Parse post processing chains
+        for (auto& xmlPostprocess : getChildren(xmlConfig, "postprocess")) {
+            for (auto& xmlStream : getChildren(xmlPostprocess, "stream")) {
+                registerFailure(parseStream(xmlStream, config->effects, &config->postprocess));
+            }
+        }
+    }
+    return {std::move(config), nbSkippedElements};
+}
+
+} // namespace effectsConfig
+} // namespace android
diff --git a/media/libeffects/factory/Android.bp b/media/libeffects/factory/Android.bp
index 16680bd..21be587 100644
--- a/media/libeffects/factory/Android.bp
+++ b/media/libeffects/factory/Android.bp
@@ -10,13 +10,19 @@
 cc_library_shared {
     name: "libeffects",
     vendor: true,
-    srcs: ["EffectsFactory.c"],
+    srcs: ["EffectsFactory.c",
+           "EffectsConfigLoader.c",
+           "EffectsFactoryState.c",
+           "EffectsXmlConfigLoader.cpp",
+    ],
 
     shared_libs: [
         "libcutils",
         "liblog",
         "libdl",
+        "libeffectsconfig",
     ],
+    cflags: ["-fvisibility=hidden"],
 
     include_dirs: ["system/media/audio_effects/include"],
 
@@ -25,3 +31,24 @@
     header_libs: ["libeffects_headers"],
     export_header_lib_headers: ["libeffects_headers"],
 }
+
+cc_binary {
+    name: "dumpEffectConfigFile",
+    vendor: true,
+    srcs: ["test/DumpConfig.cpp"],
+
+    compile_multilib: "32",
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+
+
+    shared_libs: [
+        "libeffectsconfig",
+        "libeffects",
+    ],
+    local_include_dirs:[".", "include"],
+}
diff --git a/media/libeffects/factory/EffectsConfigLoader.c b/media/libeffects/factory/EffectsConfigLoader.c
new file mode 100644
index 0000000..5c973b4
--- /dev/null
+++ b/media/libeffects/factory/EffectsConfigLoader.c
@@ -0,0 +1,438 @@
+/*
+ * 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 <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)) {
+            strcpy(lib_path_out, path);
+            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 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;
+}
+
+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;
+}
diff --git a/media/libeffects/factory/EffectsConfigLoader.h b/media/libeffects/factory/EffectsConfigLoader.h
new file mode 100644
index 0000000..3f82609
--- /dev/null
+++ b/media/libeffects/factory/EffectsConfigLoader.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_EFFECTSCONFIGLOADER_H
+#define ANDROID_EFFECTSCONFIGLOADER_H
+
+#include <cutils/compiler.h>
+#include "EffectsFactoryState.h"
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+
+/** Parses the platform effect configuration
+ * and stores its content in the global EffectFactoryState. */
+ANDROID_API
+int EffectLoadEffectConfig();
+
+
+#ifdef  __cplusplus
+} // extern "C"
+#endif
+
+#endif  // ANDROID_EFFECTSCONFIGLOADER_H
diff --git a/media/libeffects/factory/EffectsFactory.c b/media/libeffects/factory/EffectsFactory.c
index 37c0bb7..cd0e765 100644
--- a/media/libeffects/factory/EffectsFactory.c
+++ b/media/libeffects/factory/EffectsFactory.c
@@ -17,65 +17,46 @@
 #define LOG_TAG "EffectsFactory"
 //#define LOG_NDEBUG 0
 
-#include "EffectsFactory.h"
-
-#include <dlfcn.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
-#include <cutils/config_utils.h>
-#include <cutils/misc.h>
 #include <cutils/properties.h>
 #include <log/log.h>
 
-#include <system/audio_effects/audio_effects_conf.h>
+#include <media/EffectsFactoryApi.h>
+
+#include "EffectsConfigLoader.h"
+#include "EffectsFactoryState.h"
+#include "EffectsXmlConfigLoader.h"
+
+#include "EffectsFactory.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()
+/** Number of elements skipped during the effects configuration loading.
+ *  -1 if the config loader failed
+ *  -2 if config load was skipped
+ */
+static ssize_t gConfigNbElemSkipped = -2;
 
 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()
-
-static list_elem_t *gLibraryFailedList;  //list of lib_failed_entry_t: libraries failed to load
-
 /////////////////////////////////////////////////
 //      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
@@ -461,10 +442,12 @@
     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);
+        gConfigNbElemSkipped = EffectLoadXmlEffectConfig(NULL);
+        if (gConfigNbElemSkipped < 0) {
+            ALOGW("Failed to load XML effect configuration, fallback to .conf");
+            EffectLoadEffectConfig();
+        } else if (gConfigNbElemSkipped > 0) {
+            ALOGE("Effect config is partially invalid, skipped %zd elements", gConfigNbElemSkipped);
         }
     }
 
@@ -474,367 +457,6 @@
     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)) {
-            strcpy(lib_path_out, path);
-            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 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
@@ -881,29 +503,6 @@
     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;
@@ -935,114 +534,6 @@
     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];
 
@@ -1092,6 +583,20 @@
             e = e->next;
         }
     }
+    switch (gConfigNbElemSkipped) {
+    case -2:
+        dprintf(fd, "Effect configuration loading skipped.\n");
+        break;
+    case -1:
+        dprintf(fd, "XML effect configuration failed to load.\n");
+        break;
+    case 0:
+        dprintf(fd, "XML effect configuration loaded successfully.\n");
+        break;
+    default:
+        dprintf(fd, "XML effect configuration partially loaded, skipped %zd elements.\n",
+                gConfigNbElemSkipped);
+    }
     return ret;
 }
 
diff --git a/media/libeffects/factory/EffectsFactory.h b/media/libeffects/factory/EffectsFactory.h
index 72e0931..cdba362 100644
--- a/media/libeffects/factory/EffectsFactory.h
+++ b/media/libeffects/factory/EffectsFactory.h
@@ -21,6 +21,7 @@
 #include <pthread.h>
 
 #include <android/log.h>
+#include <cutils/compiler.h>
 #include <hardware/audio_effect.h>
 
 #if __cplusplus
@@ -96,6 +97,7 @@
 //        *pDescriptor:     updated with the sub effect descriptors.
 //
 ////////////////////////////////////////////////////////////////////////////////
+ANDROID_API
 int EffectGetSubEffects(const effect_uuid_t *pEffectUuid,
                         sub_effect_entry_t **pSube,
                         size_t size);
diff --git a/media/libeffects/factory/EffectsFactoryState.c b/media/libeffects/factory/EffectsFactoryState.c
new file mode 100644
index 0000000..b364004
--- /dev/null
+++ b/media/libeffects/factory/EffectsFactoryState.c
@@ -0,0 +1,138 @@
+/*
+ * 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 "EffectsFactoryState"
+
+#include "EffectsFactoryState.h"
+
+#include "log/log.h"
+
+list_elem_t *gLibraryList;
+list_elem_t *gSkippedEffects;
+list_sub_elem_t *gSubEffectList;
+pthread_mutex_t gLibLock = PTHREAD_MUTEX_INITIALIZER;
+
+list_elem_t *gLibraryFailedList;  //list of lib_failed_entry_t: libraries failed to load
+
+
+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;
+}
+
+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;
+}
+
+
+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);
+}
diff --git a/media/libeffects/factory/EffectsFactoryState.h b/media/libeffects/factory/EffectsFactoryState.h
new file mode 100644
index 0000000..aef945e
--- /dev/null
+++ b/media/libeffects/factory/EffectsFactoryState.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_EFFECTSFACTORYSTATE_H_
+#define ANDROID_EFFECTSFACTORYSTATE_H_
+
+#include "EffectsFactory.h"
+
+#if __cplusplus
+extern "C" {
+#endif
+
+/** @file Contains the state shared with configuration loader of the Effect factory.
+ *        This global state should probably be refactor in a structure
+ *        provided by the config loader on EffectsFactory init.
+ *        This header also contains some helper functions to work on the state.
+ */
+
+extern list_elem_t *gLibraryList; // list of lib_entry_t: all currently loaded libraries
+// list of effects skipped because of duplicate uuid or invalid version
+extern list_elem_t *gSkippedEffects;
+// list of effect_descriptor and list of sub effects : all currently loaded
+// It does not contain effects without sub effects.
+extern list_sub_elem_t *gSubEffectList;
+extern pthread_mutex_t gLibLock; // controls access to gLibraryList
+
+extern list_elem_t *gLibraryFailedList;  //list of lib_failed_entry_t: libraries failed to load
+
+
+
+int findEffect(const effect_uuid_t *type,
+               const effect_uuid_t *uuid,
+               lib_entry_t **lib,
+               effect_descriptor_t **desc);
+
+int stringToUuid(const char *str, effect_uuid_t *uuid);
+/** Used to log UUIDs */
+int uuidToString(const effect_uuid_t *uuid, char *str, size_t maxLen);
+
+/** Used for debuging. */
+void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len, int indent);
+
+#if __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_EFFECTSFACTORYSTATE_H_
diff --git a/media/libeffects/factory/EffectsXmlConfigLoader.cpp b/media/libeffects/factory/EffectsXmlConfigLoader.cpp
new file mode 100644
index 0000000..42e00f8
--- /dev/null
+++ b/media/libeffects/factory/EffectsXmlConfigLoader.cpp
@@ -0,0 +1,335 @@
+/*
+ * 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 <set>
+#include <stdlib.h>
+#include <string>
+
+#include <log/log.h>
+
+#include <media/EffectsConfig.h>
+
+#include "EffectsConfigLoader.h"
+#include "EffectsFactoryState.h"
+#include "EffectsXmlConfigLoader.h"
+
+namespace android {
+
+using namespace effectsConfig;
+
+/////////////////////////////////////////////////
+//      Local functions
+/////////////////////////////////////////////////
+
+namespace {
+
+/** Similarly to dlopen, looks for the provided path in LD_EFFECT_LIBRARY_PATH.
+ * @return true if the library is found and set resolvedPath to its absolute path.
+ *         false if not found
+ */
+bool resolveLibrary(const std::string& path, std::string* resolvedPath) {
+    for (auto* libraryDirectory : LD_EFFECT_LIBRARY_PATH) {
+        std::string candidatePath = std::string(libraryDirectory) + '/' + path;
+        if (access(candidatePath.c_str(), R_OK) == 0) {
+            *resolvedPath = std::move(candidatePath);
+            return true;
+        }
+    }
+    return false;
+}
+
+/** Loads a library given its relative path and stores the result in libEntry.
+ * @return true on success with libEntry's path, handle and desc filled
+ *         false on success with libEntry's path filled with the path of the failed lib
+ * The caller MUST free the resources path (free) and handle (dlclose) if filled.
+ */
+bool loadLibrary(const char* relativePath, lib_entry_t* libEntry) noexcept {
+
+    std::string absolutePath;
+    if (!resolveLibrary(relativePath, &absolutePath)) {
+        ALOGE("Could not find library in effect directories: %s", relativePath);
+        libEntry->path = strdup(relativePath);
+        return false;
+    }
+    const char* path = absolutePath.c_str();
+    libEntry->path = strdup(path);
+
+    // Make sure the lib is closed on early return
+    std::unique_ptr<void, decltype(dlclose)*> libHandle(dlopen(path, RTLD_NOW),
+                                                       dlclose);
+    if (libHandle == nullptr) {
+        ALOGE("Could not dlopen library %s: %s", path, dlerror());
+        return false;
+    }
+
+    auto* description = static_cast<audio_effect_library_t*>(
+          dlsym(libHandle.get(), AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR));
+    if (description == nullptr) {
+        ALOGE("Invalid effect library, failed not find symbol '%s' in %s: %s",
+              AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR, path, dlerror());
+        return false;
+    }
+
+    if (description->tag != AUDIO_EFFECT_LIBRARY_TAG) {
+        ALOGE("Bad tag %#08x in description structure, expected %#08x for library %s",
+              description->tag, AUDIO_EFFECT_LIBRARY_TAG, path);
+        return false;
+    }
+
+    uint32_t majorVersion = EFFECT_API_VERSION_MAJOR(description->version);
+    uint32_t expectedMajorVersion = EFFECT_API_VERSION_MAJOR(EFFECT_LIBRARY_API_VERSION);
+    if (majorVersion != expectedMajorVersion) {
+        ALOGE("Unsupported major version %#08x, expected %#08x for library %s",
+              majorVersion, expectedMajorVersion, path);
+        return false;
+    }
+
+    libEntry->handle = libHandle.release();
+    libEntry->desc = description;
+    return true;
+}
+
+/** Because the structures will be destroyed by c code, using new to allocate shared structure
+ * is not possible. Provide a equivalent of unique_ptr for malloc/freed structure to make sure
+ * they are not leaked in the c++ code.
+ @{ */
+struct FreeDeleter {
+    void operator()(void* p) {
+        free(p);
+    }
+};
+/** unique_ptr for object created with malloc. */
+template <class T>
+using UniqueCPtr = std::unique_ptr<T, FreeDeleter>;
+
+/** c version of std::make_unique. Uses malloc and free. */
+template <class T>
+UniqueCPtr<T> makeUniqueC() {
+    T* ptr = new (malloc(sizeof(T))) T{}; // Use placement new to initialize the structure
+    return UniqueCPtr<T>{ptr};
+}
+
+/** @} */
+
+/** Push an not owned element in a list_elem link list with an optional lock. */
+template <class T, class ListElem>
+void listPush(T* object, ListElem** list, pthread_mutex_t* mutex = nullptr) noexcept {
+    auto listElem = makeUniqueC<ListElem>();
+    listElem->object = object;
+    if (mutex != nullptr) {
+        pthread_mutex_lock(mutex);
+    }
+    listElem->next = *list;
+    *list = listElem.release();
+    if (mutex != nullptr) {
+        pthread_mutex_unlock(mutex);
+    }
+}
+
+/** Push an owned element in a list_elem link list with an optional lock. */
+template <class T, class ListElem>
+void listPush(UniqueCPtr<T>&& object, ListElem** list, pthread_mutex_t* mutex = nullptr) noexcept {
+    listPush(object.release(), list, mutex);
+}
+
+size_t loadLibraries(const effectsConfig::Libraries& libs,
+                     list_elem_t** libList, pthread_mutex_t* libListLock,
+                     list_elem_t** libFailedList)
+{
+    size_t nbSkippedElement = 0;
+    for (auto& library : libs) {
+
+        // Construct a lib entry
+        auto libEntry = makeUniqueC<lib_entry_t>();
+        libEntry->name = strdup(library.name.c_str());
+        libEntry->effects = nullptr;
+        pthread_mutex_init(&libEntry->lock, nullptr);
+
+        if (!loadLibrary(library.path.c_str(), libEntry.get())) {
+            // Register library load failure
+            listPush(std::move(libEntry), libFailedList);
+            ++nbSkippedElement;
+            continue;
+        }
+        listPush(std::move(libEntry), libList, libListLock);
+    }
+    return nbSkippedElement;
+}
+
+/** Find a library with the given name in the given list. */
+lib_entry_t* findLibrary(const char* name, list_elem_t* list) {
+
+    while (list != nullptr) {
+        auto* object = static_cast<lib_entry_t*>(list->object);
+        if (strcmp(object->name, name) == 0) {
+            return object;
+        }
+        list = list->next;
+    }
+    return nullptr;
+}
+
+struct UuidStr {
+    /** Length of an uuid represented as string. @TODO: use a constant instead of 40. */
+    char buff[40];
+};
+
+/** @return a string representing the provided uuid.
+ * By not providing an output buffer, it is implicitly created in the caller context.
+ * In such case the return pointer has the same lifetime as the expression containing uuidToString()
+ */
+char* uuidToString(const effect_uuid_t& uuid, UuidStr&& str = {}) {
+    uuidToString(&uuid, str.buff, sizeof(str.buff));
+    return str.buff;
+}
+
+struct LoadEffectResult {
+    /** true if the effect is usable (aka, existing lib, desc, right version, unique uuid) */
+    bool success = false;
+    /** Set if the effect lib was found*/
+    lib_entry_t* lib = nullptr;
+    //* Set if the description was successfuly retrieved from the lib */
+    UniqueCPtr<effect_descriptor_t> effectDesc;
+};
+
+LoadEffectResult loadEffect(const EffectImpl& effect, const std::string& name,
+                            list_elem_t* libList) {
+    LoadEffectResult result;
+
+    // Find the effect library
+    result.lib = findLibrary(effect.library->name.c_str(), libList);
+    if (result.lib == nullptr) {
+        ALOGE("Could not find library %s to load effect %s",
+              effect.library->name.c_str(), name.c_str());
+        return result;
+    }
+
+    result.effectDesc = makeUniqueC<effect_descriptor_t>();
+
+    // Get the effect descriptor
+    if (result.lib->desc->get_descriptor(&effect.uuid, result.effectDesc.get()) != 0) {
+        ALOGE("Error querying effect %s on lib %s",
+              uuidToString(effect.uuid), result.lib->name);
+        result.effectDesc.reset();
+        return result;
+    }
+
+    // Dump effect for debug
+#if (LOG_NDEBUG==0)
+    char s[512];
+    dumpEffectDescriptor(result.effectDesc.get(), s, sizeof(s), 0 /* indent */);
+    ALOGV("loadEffect() read descriptor %p:%s", result.effectDesc.get(), s);
+#endif
+
+    // Check effect is supported
+    uint32_t expectedMajorVersion = EFFECT_API_VERSION_MAJOR(EFFECT_CONTROL_API_VERSION);
+    if (EFFECT_API_VERSION_MAJOR(result.effectDesc->apiVersion) != expectedMajorVersion) {
+        ALOGE("Bad API version %#08x for effect %s in lib %s, expected major %#08x",
+              result.effectDesc->apiVersion, name.c_str(), result.lib->name, expectedMajorVersion);
+        return result;
+    }
+
+    lib_entry_t *_;
+    if (findEffect(nullptr, &effect.uuid, &_, nullptr) == 0) {
+        ALOGE("Effect %s uuid %s already exist", uuidToString(effect.uuid), name.c_str());
+        return result;
+    }
+
+    result.success = true;
+    return result;
+}
+
+size_t loadEffects(const Effects& effects, list_elem_t* libList, list_elem_t** skippedEffects,
+                   list_sub_elem_t** subEffectList) {
+    size_t nbSkippedElement = 0;
+
+    for (auto& effect : effects) {
+
+        auto effectLoadResult = loadEffect(effect, effect.name, libList);
+        if (!effectLoadResult.success) {
+            if (effectLoadResult.effectDesc != nullptr) {
+                listPush(std::move(effectLoadResult.effectDesc), skippedEffects);
+            }
+            ++nbSkippedElement;
+            continue;
+        }
+
+        if (effect.isProxy) {
+            auto swEffectLoadResult = loadEffect(effect.libSw, effect.name + " libsw", libList);
+            auto hwEffectLoadResult = loadEffect(effect.libHw, effect.name + " libhw", libList);
+            if (!swEffectLoadResult.success || !hwEffectLoadResult.success) {
+                // Push the main effect in the skipped list even if only a subeffect is invalid
+                // as the main effect is not usable without its subeffects.
+                listPush(std::move(effectLoadResult.effectDesc), skippedEffects);
+                ++nbSkippedElement;
+                continue;
+            }
+            listPush(&effectLoadResult.effectDesc, subEffectList);
+
+            // Since we return a dummy descriptor for the proxy during
+            // get_descriptor call, we replace it with the corresponding
+            // sw effect descriptor, but keep the Proxy UUID
+            *effectLoadResult.effectDesc = *swEffectLoadResult.effectDesc;
+            effectLoadResult.effectDesc->uuid = effect.uuid;
+
+            effectLoadResult.effectDesc->flags |= EFFECT_FLAG_OFFLOAD_SUPPORTED;
+
+            auto registerSubEffect = [subEffectList](auto&& result) {
+                auto entry = makeUniqueC<sub_effect_entry_t>();
+                entry->object = result.effectDesc.release();
+                // lib_entry_t is stored since the sub effects are not linked to the library
+                entry->lib = result.lib;
+                listPush(std::move(entry), &(*subEffectList)->sub_elem);
+            };
+            registerSubEffect(std::move(swEffectLoadResult));
+            registerSubEffect(std::move(hwEffectLoadResult));
+        }
+
+        listPush(std::move(effectLoadResult.effectDesc), &effectLoadResult.lib->effects);
+    }
+    return nbSkippedElement;
+}
+
+} // namespace
+
+/////////////////////////////////////////////////
+//      Interface function
+/////////////////////////////////////////////////
+
+extern "C" ssize_t EffectLoadXmlEffectConfig(const char* path)
+{
+    using effectsConfig::parse;
+    auto result = path ? parse(path) : parse();
+    if (result.parsedConfig == nullptr) {
+        ALOGE("Failed to parse XML configuration file");
+        return -1;
+    }
+    result.nbSkippedElement += loadLibraries(result.parsedConfig->libraries,
+                                             &gLibraryList, &gLibLock, &gLibraryFailedList) +
+                               loadEffects(result.parsedConfig->effects, gLibraryList,
+                                           &gSkippedEffects, &gSubEffectList);
+
+    ALOGE_IF(result.nbSkippedElement != 0, "%zu errors during loading of configuration: %s",
+             result.nbSkippedElement, path ?: effectsConfig::DEFAULT_PATH);
+
+    return result.nbSkippedElement;
+}
+
+} // namespace android
diff --git a/media/libeffects/factory/EffectsXmlConfigLoader.h b/media/libeffects/factory/EffectsXmlConfigLoader.h
new file mode 100644
index 0000000..a3fe9a3
--- /dev/null
+++ b/media/libeffects/factory/EffectsXmlConfigLoader.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_EFFECTSXMLCONFIGLOADER_H
+#define ANDROID_EFFECTSXMLCONFIGLOADER_H
+
+#include <unistd.h>
+
+#include <cutils/compiler.h>
+
+#include "EffectsFactoryState.h"
+
+#if __cplusplus
+extern "C" {
+#endif
+
+/** Parses the platform effect xml configuration and stores its content in EffectFactoryState.
+ * @param[in] path of the configuration file or NULL to load the default one
+ * @return -1 on unrecoverable error (eg: no configuration file)
+ *         0 on success
+ *         the number of invalid elements (lib & effect) skipped if the config is partially invalid
+ * @note this function is exported for test purpose only. Do not call from outside this library.
+ */
+ANDROID_API
+ssize_t EffectLoadXmlEffectConfig(const char* path);
+
+#if __cplusplus
+} // extern "C"
+#endif
+
+#endif  // ANDROID_EFFECTSXMLCONFIGLOADER_H
diff --git a/media/libeffects/factory/include/media/EffectsFactoryApi.h b/media/libeffects/factory/include/media/EffectsFactoryApi.h
index 64a3212..a5a12eb 100644
--- a/media/libeffects/factory/include/media/EffectsFactoryApi.h
+++ b/media/libeffects/factory/include/media/EffectsFactoryApi.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_EFFECTSFACTORYAPI_H_
 #define ANDROID_EFFECTSFACTORYAPI_H_
 
+#include <cutils/compiler.h>
 #include <errno.h>
 #include <stdint.h>
 #include <sys/types.h>
@@ -52,6 +53,7 @@
 //        *pNumEffects:     updated with number of effects in factory
 //
 ////////////////////////////////////////////////////////////////////////////////
+ANDROID_API
 int EffectQueryNumberEffects(uint32_t *pNumEffects);
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -79,6 +81,7 @@
 //        *pDescriptor:     updated with the effect descriptor.
 //
 ////////////////////////////////////////////////////////////////////////////////
+ANDROID_API
 int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor);
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -110,6 +113,7 @@
 //        *pHandle:         updated with the effect handle.
 //
 ////////////////////////////////////////////////////////////////////////////////
+ANDROID_API
 int EffectCreate(const effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t ioId,
         effect_handle_t *pHandle);
 
@@ -130,6 +134,7 @@
 //                          -EINVAL     invalid interface handle
 //
 ////////////////////////////////////////////////////////////////////////////////
+ANDROID_API
 int EffectRelease(effect_handle_t handle);
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -153,6 +158,7 @@
 //        *pDescriptor:     updated with the effect descriptor.
 //
 ////////////////////////////////////////////////////////////////////////////////
+ANDROID_API
 int EffectGetDescriptor(const effect_uuid_t *pEffectUuid, effect_descriptor_t *pDescriptor);
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -169,8 +175,10 @@
 //                           1 if uuid is equal to EFFECT_UUID_NULL.
 //
 ////////////////////////////////////////////////////////////////////////////////
+ANDROID_API
 int EffectIsNullUuid(const effect_uuid_t *pEffectUuid);
 
+ANDROID_API
 int EffectDumpEffects(int fd);
 
 #if __cplusplus
diff --git a/media/libeffects/factory/test/DumpConfig.cpp b/media/libeffects/factory/test/DumpConfig.cpp
new file mode 100644
index 0000000..0a156b4
--- /dev/null
+++ b/media/libeffects/factory/test/DumpConfig.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#include <media/EffectsFactoryApi.h>
+#include <unistd.h>
+#include "EffectsXmlConfigLoader.h"
+#include "EffectsConfigLoader.h"
+
+int main(int argc, char* argv[]) {
+    const char* path = nullptr;
+    bool legacyFormat;
+
+    if (argc == 2 && strcmp(argv[1], "--legacy") == 0) {
+        legacyFormat = true;
+        fprintf(stderr, "Dumping legacy effect config file\n");
+    } else if ((argc == 2 || argc == 3) && strcmp(argv[1], "--xml") == 0) {
+        legacyFormat = false;
+        if (argc == 3) {
+            fprintf(stderr, "Dumping XML effect config file: %s\n", path);
+        } else {
+            fprintf(stderr, "Dumping default XML effect config file.\n");
+        }
+    } else {
+        fprintf(stderr, "Invalid arguments.\n"
+                        "Usage: %s [--legacy|--xml [FILE]]\n", argv[0]);
+        return 1;
+    }
+
+    if (!legacyFormat) {
+        ssize_t ret = EffectLoadXmlEffectConfig(path);
+        if (ret < 0) {
+            fprintf(stderr, "loadXmlEffectConfig failed, see logcat for detail.\n");
+            return 2;
+        }
+        if (ret > 0) {
+            fprintf(stderr, "Partially failed to load config. Skipped %zu elements, "
+                    "see logcat for detail.\n", (size_t)ret);
+        }
+    }
+
+    if (legacyFormat) {
+        auto ret = EffectLoadEffectConfig();
+        if (ret < 0) {
+            fprintf(stderr, "loadEffectConfig failed, see logcat for detail.\n");
+            return 3;
+        }
+        fprintf(stderr, "legacy loadEffectConfig has probably succeed, see logcat to make sure.\n");
+    }
+
+    if (EffectDumpEffects(STDOUT_FILENO) != 0) {
+        fprintf(stderr, "Effect dump failed, see logcat for detail.\n");
+        return 4;
+    }
+}
diff --git a/media/libnbaio/NBLog.cpp b/media/libnbaio/NBLog.cpp
index e3f4876..2b5b4ff 100644
--- a/media/libnbaio/NBLog.cpp
+++ b/media/libnbaio/NBLog.cpp
@@ -892,23 +892,14 @@
     ofs.close();
 }
 
-// converts a time series into a map. key: buffer period length. value: count
-static std::map<int, int> buildBuckets(const std::vector<int64_t> &samples) {
-    // TODO allow buckets of variable resolution
-    std::map<int, int> buckets;
-    for (size_t i = 1; i < samples.size(); ++i) {
-        ++buckets[deltaMs(samples[i - 1], samples[i])];
-    }
-    return buckets;
-}
-
 void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapshot)
 {
     //  CallStack cs(LOG_TAG);
     mFd = fd;
     mIndent = indent;
     String8 timestamp, body;
-    PerformanceAnalysis performanceAnalyzer; // used to call analysis functions
+    // FIXME: this is not thread safe
+    static PerformanceAnalysis performanceAnalysis; // used to store data and to call analysis functions
     size_t lost = snapshot.lost() + (snapshot.begin() - EntryIterator(snapshot.data()));
     if (lost > 0) {
         body.appendFormat("warning: lost %zu bytes worth of events", lost);
@@ -930,29 +921,7 @@
             memcpy(&hash, &(data->hash), sizeof(hash));
             int64_t ts;
             memcpy(&ts, &data->ts, sizeof(ts));
-            const std::pair<log_hash_t, int> key(hash, data->author);
-            // TODO might want to filter excessively high outliers, which are usually caused
-            // by the thread being inactive.
-            mHists[key].push_back(ts);
-            // store time series data for each reader in order to bucket it once there
-            // is enough data. Then, it is written to recentHists as a histogram.
-            mTimeStampSeries[data->author].push_back(ts);
-            // if length of the time series has reached kShortHistSize samples, do 1) and 2):
-            if (mTimeStampSeries[data->author].size() >= kShortHistSize) {
-                // 1) analyze the series to store all outliers and their exact timestamps:
-                performanceAnalyzer.storeOutlierData(data->author, mTimeStampSeries[data->author]);
-                 // 2) compute its histogram, append this to mRecentHists and erase the time series
-                mRecentHists.emplace_front(data->author,
-                                           buildBuckets(mTimeStampSeries[data->author]));
-                // do not let mRecentHists exceed capacity
-                // TODO: turn the FIFO queue into a circular buffer
-                if (mRecentHists.size() >= kRecentHistsCapacity) {
-                    mRecentHists.pop_back();
-                }
-                mTimeStampSeries.erase(data->author);
-            }
-            // if an element in mHists has not grown for a long time, delete
-            // TODO copy histogram data only to mRecentHistsBuffer and pop oldest
+            performanceAnalysis.logTsEntry(data->author, ts);
             ++entry;
             break;
         }
@@ -967,12 +936,10 @@
             break;
         }
     }
-    performanceAnalyzer.reportPerformance(&body, mRecentHists);
+    performanceAnalysis.reportPerformance(&body);
     if (!body.isEmpty()) {
         dumpLine(timestamp, body);
     }
-    // comment in for tests
-    // performanceAnalyzer.testFunction();
 }
 
 void NBLog::Reader::dump(int fd, size_t indent)
diff --git a/media/libnbaio/PerformanceAnalysis.cpp b/media/libnbaio/PerformanceAnalysis.cpp
index 84b7635..fa8f47e 100644
--- a/media/libnbaio/PerformanceAnalysis.cpp
+++ b/media/libnbaio/PerformanceAnalysis.cpp
@@ -45,8 +45,21 @@
 
 namespace android {
 
-PerformanceAnalysis::PerformanceAnalysis() : findGlitch(false) {
-    kPeriodMsCPU = static_cast<int>(PerformanceAnalysis::kPeriodMs * kRatio);
+PerformanceAnalysis::PerformanceAnalysis() {
+    // These variables will be (FIXME) learned from the data
+    kPeriodMs = 4; // typical buffer period (mode)
+    // average number of Ms spent processing buffer
+    kPeriodMsCPU = static_cast<int>(kPeriodMs * kRatio);
+}
+
+// converts a time series into a map. key: buffer period length. value: count
+static std::map<int, int> buildBuckets(const std::vector<int64_t> &samples) {
+    // TODO allow buckets of variable resolution
+    std::map<int, int> buckets;
+    for (size_t i = 1; i < samples.size(); ++i) {
+        ++buckets[deltaMs(samples[i - 1], samples[i])];
+    }
+    return buckets;
 }
 
 static int widthOf(int x) {
@@ -58,76 +71,164 @@
     return width;
 }
 
+// Takes a single buffer period timestamp entry with author information and stores it
+// in a temporary series of timestamps. Once the series is full, the data is analyzed,
+// stored, and emptied.
+// TODO: decide whether author or file location information is more important to store
+// for now, only stores author (thread)
+void PerformanceAnalysis::logTsEntry(int author, int64_t ts) {
+    // TODO might want to filter excessively high outliers, which are usually caused
+    // by the thread being inactive.
+    // Store time series data for each reader in order to bucket it once there
+    // is enough data. Then, write to recentHists as a histogram.
+    mTimeStampSeries[author].push_back(ts);
+    // if length of the time series has reached kShortHistSize samples, do 1) and 2):
+    if (mTimeStampSeries[author].size() >= kShortHistSize) {
+        // 1) analyze the series to store all outliers and their exact timestamps:
+        storeOutlierData(mTimeStampSeries[author]);
+        // 2) detect peaks in the outlier series
+        detectPeaks();
+        // 3) compute its histogram, append this to mRecentHists and erase the time series
+        // FIXME: need to store the timestamp of the beginning of each histogram
+        // FIXME: Restore LOG_HIST_FLUSH to separate histograms at every end-of-stream event
+        // A histogram should not span data between audio off/on timespans
+        mRecentHists.emplace_back(author,
+                                   buildBuckets(mTimeStampSeries[author]));
+        // do not let mRecentHists exceed capacity
+        // ALOGD("mRecentHists size: %d", static_cast<int>(mRecentHists.size()));
+        if (mRecentHists.size() >= kRecentHistsCapacity) {
+            //  ALOGD("popped back mRecentHists");
+            mRecentHists.pop_front();
+        }
+        mTimeStampSeries[author].clear();
+    }
+}
+
+// Given a series of outlier intervals (mOutlier data),
+// looks for changes in distribution (peaks), which can be either positive or negative.
+// The function sets the mean to the starting value and sigma to 0, and updates
+// them as long as no peak is detected. When a value is more than 'threshold'
+// standard deviations from the mean, a peak is detected and the mean and sigma
+// are set to the peak value and 0.
+void PerformanceAnalysis::detectPeaks() {
+    if (mOutlierData.empty()) {
+        ALOGD("peak detector called on empty array");
+        return;
+    }
+
+    // compute mean of the distribution. Used to check whether a value is large
+    const double kTypicalDiff = std::accumulate(
+        mOutlierData.begin(), mOutlierData.end(), 0,
+        [](auto &a, auto &b){return a + b.first;}) / mOutlierData.size();
+    // ALOGD("typicalDiff %f", kTypicalDiff);
+
+    // iterator at the beginning of a sequence, or updated to the most recent peak
+    std::deque<std::pair<uint64_t, uint64_t>>::iterator start = mOutlierData.begin();
+    // the mean and standard deviation are updated every time a peak is detected
+    // initialize first time. The mean from the previous sequence is stored
+    // for the next sequence. Here, they are initialized for the first time.
+    if (mPeakDetectorMean < 0) {
+        mPeakDetectorMean = static_cast<double>(start->first);
+        mPeakDetectorSd = 0;
+    }
+    auto sqr = [](auto x){ return x * x; };
+    for (auto it = mOutlierData.begin(); it != mOutlierData.end(); ++it) {
+        // no surprise occurred:
+        // the new element is a small number of standard deviations from the mean
+        if ((fabs(it->first - mPeakDetectorMean) < kStddevThreshold * mPeakDetectorSd) ||
+             // or: right after peak has been detected, the delta is smaller than average
+            (mPeakDetectorSd == 0 && fabs(it->first - mPeakDetectorMean) < kTypicalDiff)) {
+            // update the mean and sd:
+            // count number of elements (distance between start interator and current)
+            const int kN = std::distance(start, it) + 1;
+            // usual formulas for mean and sd
+            mPeakDetectorMean = std::accumulate(start, it + 1, 0.0,
+                                   [](auto &a, auto &b){return a + b.first;}) / kN;
+            mPeakDetectorSd = sqrt(std::accumulate(start, it + 1, 0.0,
+                      [=](auto &a, auto &b){ return a + sqr(b.first - mPeakDetectorMean);})) /
+                      ((kN > 1)? kN - 1 : kN); // kN - 1: mean is correlated with variance
+            // ALOGD("value, mean, sd: %f, %f, %f", static_cast<double>(it->first), mean, sd);
+        }
+        // surprising value: store peak timestamp and reset mean, sd, and start iterator
+        else {
+            mPeakTimestamps.emplace_back(it->second);
+            // TODO: remove pop_front once a circular buffer is in place
+            if (mPeakTimestamps.size() >= kShortHistSize) {
+                ALOGD("popped back mPeakTimestamps");
+                mPeakTimestamps.pop_front();
+            }
+            mPeakDetectorMean = static_cast<double>(it->first);
+            mPeakDetectorSd = 0;
+            start = it;
+        }
+    }
+    //for (const auto &it : mPeakTimestamps) {
+    //    ALOGE("mPeakTimestamps %f", static_cast<double>(it));
+    //}
+    return;
+}
+
+// Called by LogTsEntry. The input is a vector of timestamps.
+// Finds outliers and writes to mOutlierdata.
+// Each value in mOutlierdata consists of: <outlier timestamp, time elapsed since previous outlier>.
+// e.g. timestamps (ms) 1, 4, 5, 16, 18, 28 will produce pairs (4, 5), (13, 18).
+// This function is applied to the time series before it is converted into a histogram.
+void PerformanceAnalysis::storeOutlierData(const std::vector<int64_t> &timestamps) {
+    if (timestamps.size() < 1) {
+        ALOGE("storeOutlierData called on empty vector");
+        return;
+    }
+    // first pass: need to initialize
+    if (mElapsed == 0) {
+        mPrevNs = timestamps[0];
+    }
+    for (const auto &ts: timestamps) {
+        const uint64_t diffMs = static_cast<uint64_t>(deltaMs(mPrevNs, ts));
+        if (diffMs >= static_cast<uint64_t>(kOutlierMs)) {
+            mOutlierData.emplace_back(mElapsed, static_cast<uint64_t>(mPrevNs));
+            // Remove oldest value if the vector is full
+            // TODO: remove pop_front once circular buffer is in place
+            // FIXME: change kShortHistSize to some other constant. Make sure it is large
+            // enough that data will never be lost before being written to a long-term FIFO
+            if (mOutlierData.size() >= kShortHistSize) {
+                ALOGD("popped back mOutlierData");
+                mOutlierData.pop_front();
+            }
+            mElapsed = 0;
+        }
+        mElapsed += diffMs;
+        mPrevNs = ts;
+    }
+}
+
+
 // FIXME: delete this temporary test code, recycled for various new functions
 void PerformanceAnalysis::testFunction() {
     // produces values (4: 5000000), (13: 18000000)
     // ns timestamps of buffer periods
     const std::vector<int64_t>kTempTestData = {1000000, 4000000, 5000000,
                                                16000000, 18000000, 28000000};
-    const int kTestAuthor = 1;
-    PerformanceAnalysis::storeOutlierData(kTestAuthor, kTempTestData);
+    PerformanceAnalysis::storeOutlierData(kTempTestData);
     for (const auto &outlier: mOutlierData) {
         ALOGE("PerformanceAnalysis test %lld: %lld",
               static_cast<long long>(outlier.first), static_cast<long long>(outlier.second));
     }
+    detectPeaks();
 }
 
-// Each pair consists of: <outlier timestamp, time elapsed since previous outlier>.
-// The timestamp of the beginning of the outlier is recorded.
-// The elapsed time is from the timestamp of the previous outlier
-// e.g. timestamps (ms) 1, 4, 5, 16, 18, 28 will produce pairs (4, 5), (13, 18).
-// This function is applied to the time series before it is converted into a histogram.
-void PerformanceAnalysis::storeOutlierData(
-    int author, const std::vector<int64_t> &timestamps) {
-    if (timestamps.size() < 1) {
-        ALOGE("storeOutlierData called on empty vector");
-        return;
-    }
-    author++; // temp to avoid unused error until this value is
-    // either TODO: used or discarded from the arglist
-    author--;
-    uint64_t elapsed = 0;
-    int64_t prev = timestamps.at(0);
-    for (const auto &ts: timestamps) {
-        const uint64_t diff = static_cast<uint64_t>(deltaMs(prev, ts));
-        if (diff >= static_cast<uint64_t>(kOutlierMs)) {
-            mOutlierData.emplace_back(elapsed, static_cast<uint64_t>(prev));
-            elapsed = 0;
-        }
-        elapsed += diff;
-        prev = ts;
-    }
-    // ALOGE("storeOutlierData: result length %zu", outlierData.size());
-    // for (const auto &outlier: OutlierData) {
-    //    ALOGE("PerformanceAnalysis test %lld: %lld",
-    //          static_cast<long long>(outlier.first), static_cast<long long>(outlier.second));
-    //}
-}
-
-// TODO: implement peak detector
-/*
-  static void peakDetector() {
-  return;
-  } */
-
-// TODO put this function in separate file. Make it return a std::string instead of modifying body
-// TODO create a subclass of Reader for this and related work
+// TODO Make it return a std::string instead of modifying body --> is this still relevant?
 // FIXME: as can be seen when printing the values, the outlier timestamps typically occur
 // in the first histogram 35 to 38 indices from the end (most often 35).
-// TODO: build histogram buckets earlier and discard timestamps to save memory
 // TODO consider changing all ints to uint32_t or uint64_t
-void PerformanceAnalysis::reportPerformance(String8 *body,
-                                            const std::deque<std::pair
-                                            <int, short_histogram>> &shortHists,
-                                            int maxHeight) {
-    if (shortHists.size() < 1) {
+void PerformanceAnalysis::reportPerformance(String8 *body, int maxHeight) {
+    if (mRecentHists.size() < 1) {
+        ALOGD("reportPerformance: mRecentHists is empty");
         return;
     }
-    // this is temporary code, which only prints out one histogram
-    // of all data stored in buffer. The data is not erased, only overwritten.
+    ALOGD("reportPerformance: hists size %d", static_cast<int>(mRecentHists.size()));
     // TODO: more elaborate data analysis
     std::map<int, int> buckets;
-    for (const auto &shortHist: shortHists) {
+    for (const auto &shortHist: mRecentHists) {
         for (const auto &countPair : shortHist.second) {
             buckets[countPair.first] += countPair.second;
         }
@@ -222,14 +323,4 @@
     return;
 }
 
-bool PerformanceAnalysis::isFindGlitch() const
-{
-    return findGlitch;
-}
-
-void PerformanceAnalysis::setFindGlitch(bool s)
-{
-    findGlitch = s;
-}
-
 }   // namespace android
diff --git a/media/libnbaio/include/media/nbaio/NBLog.h b/media/libnbaio/include/media/nbaio/NBLog.h
index 6b08e94..0b5e24d 100644
--- a/media/libnbaio/include/media/nbaio/NBLog.h
+++ b/media/libnbaio/include/media/nbaio/NBLog.h
@@ -406,6 +406,7 @@
 public:
 
     // A snapshot of a readers buffer
+    // This is raw data. No analysis has been done on it
     class Snapshot {
     public:
         Snapshot() : mData(NULL), mLost(0) {}
@@ -445,6 +446,7 @@
     // get snapshot of readers fifo buffer, effectively consuming the buffer
     std::unique_ptr<Snapshot> getSnapshot();
     // dump a particular snapshot of the reader
+    // TODO: move dump to PerformanceAnalysis. Model/view/controller design
     void     dump(int fd, size_t indent, Snapshot & snap);
     // dump the current content of the reader's buffer (call getSnapshot() and previous dump())
     void     dump(int fd, size_t indent = 0);
@@ -452,9 +454,6 @@
 
 private:
 
-    // TODO: decide whether these belong in NBLog::Reader or in PerformanceAnalysis
-    static const int kShortHistSize = 50; // number of samples in a short-term histogram
-    static const int kRecentHistsCapacity = 100; // number of short-term histograms stored in memory
     static const std::set<Event> startingTypes;
     static const std::set<Event> endingTypes;
     /*const*/ Shared* const mShared;    // raw pointer to shared memory, actually const but not
@@ -467,23 +466,6 @@
     audio_utils_fifo_reader * const mFifoReader;    // used to read from FIFO,
                                                     // non-NULL unless constructor fails
 
-    // stores a short-term histogram of size determined by kShortHistSize
-    // TODO: unsigned, unsigned
-    using short_histogram = std::map<int, int>;
-
-    // each pair contains a sequence of timestamps (one histogram's worth)
-    // pair's log_hash_t is the hash of the source code location where the timestamp was taken
-    // pair's int points to the Reader that originated the entry
-    std::map<std::pair<log_hash_t, int>, std::vector<int64_t>> mHists;
-
-    // mHistsCopy stores timestamp vectors whose key is the reader thread index.
-    // TODO remove old mHists after changing the code
-    std::map<int, std::vector<int64_t>> mTimeStampSeries;
-
-    // stores fixed-size short buffer period histograms with hash and thread data
-    // TODO: Turn it into a circular buffer for better data flow
-    std::deque<std::pair<int, short_histogram>> mRecentHists;
-
     // TODO: it might be clearer, instead of a direct map from source location to vector of
     // timestamps, if we instead first mapped from source location to an object that
     // represented that location. And one_of its fields would be a vector of timestamps.
diff --git a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h
index 32ae38b..89699bf 100644
--- a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h
+++ b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h
@@ -22,79 +22,119 @@
 #include <map>
 #include <deque>
 #include <vector>
+#include "NBLog.h"
 
 namespace android {
 
 class String8;
 
 class PerformanceAnalysis {
-
+    // This class stores and analyzes audio processing wakeup timestamps from NBLog
+    // FIXME: currently, all performance data is stored in deques. Need to add a mutex.
+    // FIXME: continue this way until analysis is done in a separate thread. Then, use
+    // the fifo writer utilities.
 public:
 
-PerformanceAnalysis();
+    PerformanceAnalysis();
 
-// stores a short-term histogram of size determined by kShortHistSize
-// TODO: unsigned, unsigned
-// CHECK: is there a better way to use short_histogram than to write 'using'
-// both in this header file and in NBLog.h?
-using short_histogram = std::map<int, int>;
+    // FIXME: decide whether to use 64 or 32 bits
+    typedef uint64_t log_hash_t;
 
-// returns a vector of pairs <outlier timestamp, time elapsed since previous outlier
-// called by NBLog::Reader::dump before data is converted into histogram
-// TODO: currently, the elapsed time
-// The resolution is only as good as the ms duration of one shortHist
-void storeOutlierData(int author, const std::vector<int64_t> &timestamps);
+    // stores a short-term histogram of size determined by kShortHistSize
+    // key: observed buffer period. value: count
+    // TODO: unsigned, unsigned
+    // TODO: change this name to histogram
+    using short_histogram = std::map<int, int>;
 
-// TODO: delete this. temp for testing
-void testFunction();
+    using outlierInterval = uint64_t;
+    // int64_t timestamps are converted to uint64_t in PerformanceAnalysis::storeOutlierData,
+    // and all further analysis functions use uint64_t.
+    using timestamp = uint64_t;
+    using timestamp_raw = int64_t;
 
-// Given a series, looks for changes in distribution (peaks)
-// Returns a 'signal' array of the same length as the series, where each
-// value is mapped to -1, 0, or 1 based on whether a negative or positive peak
-// was detected, or no significant change occurred.
-// The function sets the mean to the starting value and sigma to 0, and updates
-// them as long as no peak is detected. When a value is more than 'threshold'
-// standard deviations from the mean, a peak is detected and the mean and sigma
-// are set to the peak value and 0.
-// static void peakDetector();
+    // Writes wakeup timestamp entry to log and runs analysis
+    // author is the thread ID
+    // TODO: check. if the thread has multiple histograms, is author info correct
+    // FIXME: remove author from arglist. Want to call these function separately on
+    // each thread’s data.
+    // FIXME: decide whether to store the hash (source file location) instead
+    // FIXME: If thread has multiple histograms, check that code works and correct
+    // author is stored (test with multiple threads). Need to check that the current
+    // code is not receiving data from multiple threads. This could cause odd values.
+    void logTsEntry(int author, timestamp_raw ts);
 
-// input: series of short histograms. output: prints an analysis of the
-// data to the console
-// TODO: change this so that it writes the analysis to the long-term
-// circular buffer and prints an analyses both for the short and long-term
-void reportPerformance(String8 *body,
-                       const std::deque<std::pair
-                       <int, short_histogram>> &shortHists,
-                       int maxHeight = 10);
+    // FIXME: make peakdetector and storeOutlierData a single function
+    // Input: mOutlierData. Looks at time elapsed between outliers
+    // finds significant changes in the distribution
+    // writes timestamps of significant changes to mPeakTimestamps
+    void detectPeaks();
 
-// if findGlitch is true, log warning when buffer periods caused glitch
-// TODO adapt this to the analysis in reportPerformance instead of logging
-void     alertIfGlitch(const std::vector<int64_t> &samples);
-bool     isFindGlitch() const;
-void     setFindGlitch(bool s);
+    // runs analysis on timestamp series before it is converted to a histogram
+    // finds outliers
+    // writes to mOutlierData <time elapsed since previous outlier, outlier timestamp>
+    void storeOutlierData(const std::vector<timestamp_raw> &timestamps);
 
-~PerformanceAnalysis() {}
+    // input: series of short histograms. Generates a string of analysis of the buffer periods
+    // TODO: WIP write more detailed analysis
+    // FIXME: move this data visualization to a separate class. Model/view/controller
+    void reportPerformance(String8 *body, int maxHeight = 10);
+
+    // TODO: delete this. temp for testing
+    void testFunction();
+
+    // This function used to detect glitches in a time series
+    // TODO incorporate this into the analysis (currently unused)
+    void     alertIfGlitch(const std::vector<timestamp_raw> &samples);
+
+    ~PerformanceAnalysis() {}
 
 private:
 
-// stores outlier analysis
-std::vector<std::pair<uint64_t, uint64_t>> mOutlierData;
+    // stores outlier analysis: <elapsed time between outliers in ms, outlier timestamp>
+    std::deque<std::pair<outlierInterval, timestamp>> mOutlierData;
 
-// stores long-term audio performance data
-// TODO: Turn it into a circular buffer
-std::deque<std::pair<int, int>> mPerformanceAnalysis;
+    // stores each timestamp at which a peak was detected
+    // a peak is a moment at which the average outlier interval changed significantly
+    std::deque<timestamp> mPeakTimestamps;
 
-// alert if a local buffer period sequence caused an audio glitch
-bool findGlitch;
-//TODO: measure these from the data (e.g., mode) as they may change.
-//const int kGlitchThreshMs = 7;
-// const int kMsPerSec = 1000;
-const int kNumBuff = 3; // number of buffers considered in local history
-const int kPeriodMs = 4; // current period length is ideally 4 ms
-const int kOutlierMs = 7; // values greater or equal to this cause glitches every time
-// DAC processing time for 4 ms buffer
-static constexpr double kRatio = 0.75; // estimate of CPU time as ratio of period length
-int kPeriodMsCPU; //compute based on kPeriodLen and kRatio
+    // FIFO of small histograms
+    // stores fixed-size short buffer period histograms with hash and thread data
+    // TODO: Turn it into a circular buffer for better data flow
+    std::deque<std::pair<int, short_histogram>> mRecentHists;
+
+    // map from author to vector of timestamps, collected from NBLog
+    // when a vector reaches its maximum size, analysis is run and the data is deleted
+    std::map<int, std::vector<timestamp_raw>> mTimeStampSeries;
+
+    // TODO: measure these from the data (e.g., mode) as they may change.
+    // const int kGlitchThreshMs = 7;
+    // const int kMsPerSec = 1000;
+
+    // Parameters used when detecting outliers
+    // TODO: learn some of these from the data, delete unused ones
+    // FIXME: decide whether to make kPeriodMs static.
+    // The non-const values are (TODO: will be) learned from the data
+    static const int kNumBuff = 3; // number of buffers considered in local history
+    int kPeriodMs; // current period length is ideally 4 ms
+    static const int kOutlierMs = 7; // values greater or equal to this cause glitches
+    // DAC processing time for 4 ms buffer
+    static constexpr double kRatio = 0.75; // estimate of CPU time as ratio of period length
+    int kPeriodMsCPU; // compute based on kPeriodLen and kRatio
+
+    // Peak detection: number of standard deviations from mean considered a significant change
+    static const int kStddevThreshold = 5;
+
+    static const int kRecentHistsCapacity = 100; // number of short-term histograms stored in memory
+    static const int kShortHistSize = 50; // number of samples in a short-term histogram
+
+    // these variables are stored in-class to ensure continuity while analyzing the timestamp
+    // series one short sequence at a time: the variables are not re-initialized every time.
+    // FIXME: create inner class for these variables and decide which other ones to add to it
+    double mPeakDetectorMean = -1;
+    double mPeakDetectorSd = -1;
+    // variables for storeOutlierData
+    uint64_t mElapsed = 0;
+    int64_t mPrevNs = -1;
 
 };
 
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 61a2b5f..6ed0d0e 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -972,6 +972,14 @@
         }
 
         if (handle != nullptr) {
+            ssize_t offset;
+            size_t size;
+            sp<IMemoryHeap> heap = frame->getMemory(&offset, &size);
+            if (heap->getHeapID() != mMemoryHeapBase->getHeapID()) {
+                ALOGE("%s: Mismatched heap ID, ignoring release (got %x, expected %x)",
+		     __FUNCTION__, heap->getHeapID(), mMemoryHeapBase->getHeapID());
+                return;
+            }
             uint32_t batchSize = 0;
             {
                 Mutex::Autolock autoLock(mBatchLock);
diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk
index ad340e5..65571f9 100644
--- a/services/audiopolicy/Android.mk
+++ b/services/audiopolicy/Android.mk
@@ -24,7 +24,8 @@
     libhardware_legacy \
     libserviceutility \
     libaudiopolicymanager \
-    libmedia_helper
+    libmedia_helper \
+    libeffectsconfig
 
 LOCAL_STATIC_LIBRARIES := \
     libaudiopolicycomponents
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
index ded2285..4f79ed2 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
@@ -71,7 +71,7 @@
 
     virtual void toAudioPort(struct audio_port *port) const;
 
-    virtual void importAudioPort(const sp<AudioPort>& port);
+    virtual void importAudioPort(const sp<AudioPort>& port, bool force = false);
 
     void addAudioProfile(const sp<AudioProfile> &profile) { mProfiles.add(profile); }
 
diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
index 9a52d22..1a644d7 100644
--- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
@@ -48,7 +48,7 @@
     // AudioPort
     virtual void attach(const sp<HwModule>& module);
     virtual void toAudioPort(struct audio_port *port) const;
-    virtual void importAudioPort(const sp<AudioPort>& port);
+    virtual void importAudioPort(const sp<AudioPort>& port, bool force = false);
 
     audio_port_handle_t getId() const;
     status_t dump(int fd, int spaces, int index, bool verbose = true) const;
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
index 6ed2cb7..fcf9070 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
@@ -128,7 +128,7 @@
     port->num_gains = i;
 }
 
-void AudioPort::importAudioPort(const sp<AudioPort>& port)
+void AudioPort::importAudioPort(const sp<AudioPort>& port, bool force __unused)
 {
     size_t indexToImport;
     for (indexToImport = 0; indexToImport < port->mProfiles.size(); indexToImport++) {
diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
index f0e48b6..a2c1165 100644
--- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
@@ -263,7 +263,10 @@
     strncpy(port->ext.device.address, mAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN);
 }
 
-void DeviceDescriptor::importAudioPort(const sp<AudioPort>& port) {
+void DeviceDescriptor::importAudioPort(const sp<AudioPort>& port, bool force) {
+    if (!force && !port->hasDynamicAudioProfile()) {
+        return;
+    }
     AudioPort::importAudioPort(port);
     port->pickAudioProfile(mSamplingRate, mChannelMask, mFormat);
 }
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 77b0182..3dcb22d 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -3724,7 +3724,7 @@
                         sp<DeviceDescriptor> devDesc = mAvailableInputDevices[index];
                         if (!devDesc->isAttached()) {
                             devDesc->attach(mHwModules[i]);
-                            devDesc->importAudioPort(inProfile);
+                            devDesc->importAudioPort(inProfile, true);
                         }
                     }
                 }
@@ -4087,8 +4087,8 @@
                 continue;
             }
 
-            ALOGV("opening output for device %08x with params %s profile %p",
-                                                      device, address.string(), profile.get());
+            ALOGV("opening output for device %08x with params %s profile %p name %s",
+                  device, address.string(), profile.get(), profile->getName().string());
             desc = new SwAudioOutputDescriptor(profile, mpClientInterface);
             desc->mDevice = device;
             audio_config_t config = AUDIO_CONFIG_INITIALIZER;
@@ -4337,6 +4337,10 @@
             config.channel_mask = desc->mChannelMask;
             config.format = desc->mFormat;
             audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
+
+            ALOGV("opening inputput for device %08x with params %s profile %p name %s",
+                  desc->mDevice, address.string(), profile.get(), profile->getName().string());
+
             status_t status = mpClientInterface->openInput(profile->getModuleHandle(),
                                                            &input,
                                                            &config,
diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp
index 654465d..84b1073 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.cpp
+++ b/services/audiopolicy/service/AudioPolicyEffects.cpp
@@ -20,8 +20,10 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <memory>
 #include <cutils/misc.h>
 #include <media/AudioEffect.h>
+#include <media/EffectsConfig.h>
 #include <system/audio.h>
 #include <system/audio_effects/audio_effects_conf.h>
 #include <utils/Vector.h>
@@ -39,11 +41,17 @@
 
 AudioPolicyEffects::AudioPolicyEffects()
 {
-    // load automatic audio effect modules
-    if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) {
-        loadAudioEffectConfig(AUDIO_EFFECT_VENDOR_CONFIG_FILE);
-    } else if (access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) {
-        loadAudioEffectConfig(AUDIO_EFFECT_DEFAULT_CONFIG_FILE);
+    status_t loadResult = loadAudioEffectXmlConfig();
+    if (loadResult < 0) {
+        ALOGW("Failed to load XML effect configuration, fallback to .conf");
+        // load automatic audio effect modules
+        if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) {
+            loadAudioEffectConfig(AUDIO_EFFECT_VENDOR_CONFIG_FILE);
+        } else if (access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) {
+            loadAudioEffectConfig(AUDIO_EFFECT_DEFAULT_CONFIG_FILE);
+        }
+    } else if (loadResult > 0) {
+        ALOGE("Effect config is partially invalid, skipped %d elements", loadResult);
     }
 }
 
@@ -685,6 +693,28 @@
     return NO_ERROR;
 }
 
+status_t AudioPolicyEffects::loadAudioEffectXmlConfig() {
+    auto result = effectsConfig::parse();
+    if (result.parsedConfig == nullptr) {
+        return -ENOENT;
+    }
+
+    auto loadProcessingChain = [](auto& processingChain, auto& streams) {
+        for (auto& stream : processingChain) {
+            auto effectDescs = std::make_unique<EffectDescVector>();
+            for (auto& effect : stream.effects) {
+                effectDescs->mEffects.add(
+                        new EffectDesc{effect.get().name.c_str(), effect.get().uuid});
+            }
+            streams.add(stream.type, effectDescs.release());
+        }
+    };
+    loadProcessingChain(result.parsedConfig->preprocess, mInputSources);
+    loadProcessingChain(result.parsedConfig->postprocess, mOutputStreams);
+    // Casting from ssize_t to status_t is probably safe, there should not be more than 2^31 errors
+    return result.nbSkippedElement;
+}
+
 status_t AudioPolicyEffects::loadAudioEffectConfig(const char *path)
 {
     cnode *root;
diff --git a/services/audiopolicy/service/AudioPolicyEffects.h b/services/audiopolicy/service/AudioPolicyEffects.h
index 0c74d87..59d5d14 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.h
+++ b/services/audiopolicy/service/AudioPolicyEffects.h
@@ -155,7 +155,8 @@
     audio_stream_type_t streamNameToEnum(const char *name);
 
     // Parse audio_effects.conf
-    status_t loadAudioEffectConfig(const char *path);
+    status_t loadAudioEffectConfig(const char *path); // TODO: add legacy in the name
+    status_t loadAudioEffectXmlConfig(); // TODO: remove "Xml" in the name
 
     // Load all effects descriptors in configuration file
     status_t loadEffects(cnode *root, Vector <EffectDesc *>& effects);