[Feature]add MT2731_MP2_MR2_SVN388 baseline version

Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/multimedia/audio-tuning/audio-xml-parser/mt2635/AudioParamParser.c b/src/multimedia/audio-tuning/audio-xml-parser/mt2635/AudioParamParser.c
new file mode 100644
index 0000000..796c307
--- /dev/null
+++ b/src/multimedia/audio-tuning/audio-xml-parser/mt2635/AudioParamParser.c
@@ -0,0 +1,1464 @@
+#include "AudioParamParserPriv.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#ifdef __linux__
+#include <dirent.h>
+#include <unistd.h>
+#else
+#include <windows.h>
+#pragma warning( disable : 4996 )
+#endif
+
+static AppHandle appHandleInst;
+static int appHandleInited = 0;
+
+#ifdef FORCE_DEBUG_LEVEL
+int appDebugLevel = DEBUG_LEVEL;        /* Global debug level setting */
+#else
+int appDebugLevel = INFO_LEVEL;         /* Global debug level setting */
+#endif
+
+FILE *appLogFp = NULL;
+int outputLogToStdout = 0;
+
+#ifndef WIN32
+static pthread_rwlock_t appHandleInstLock = PTHREAD_RWLOCK_INITIALIZER;
+static const char *appHandleInstLockCallerFun = NULL;  /* Used to cache the lock holder */
+#else
+int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, PVOID pvReserved)
+{
+    return TRUE;
+}
+#endif
+
+EXPORT APP_STATUS appHandleInit(AppHandle *appHandle)
+{
+#ifdef _DEBUG
+    /* Alwasy show the output console for debug build */
+    appHandleRedirectIOToConsole();
+#endif
+
+    INFO_LOG("appHandle = 0x%p\n", appHandle);
+
+    if (appHandle)
+    {
+        appHandle->xmlDir = NULL;
+        appHandle->xmlCusDir = NULL;
+        appHandle->audioTypeHash = NULL;
+        appHandle->featureOptionsHash = NULL;
+        appHandle->featureOptionsDoc = NULL;
+        appHandle->noficyCbList = NULL;
+#ifndef WIN32
+        appHandle->lockCallerFun = NULL;
+        appHandle->appThreadExit = 0;
+        appHandle->inotifyFd = 0;
+
+        pthread_rwlock_init(&appHandle->lock, NULL);
+#endif
+        appHandleShowAudioTypeSupportedVerInfo(appHandle);
+        return APP_NO_ERROR;
+    }
+    else
+    {
+        WARN_LOG("AppHandle is NULL!\n");
+        return APP_ERROR;
+    }
+}
+
+EXPORT APP_STATUS appHandleUninit(AppHandle *appHandle)
+{
+    INFO_LOG("appHandle = 0x%p\n", appHandle);
+
+    if (!appHandle)
+    {
+        WARN_LOG("AppHandle is NULL!\n");
+        return APP_ERROR;
+    }
+    else
+    {
+        NotifyCb *notifyCb, *tmp;
+
+        /* Lock */
+        appHandleWriteLock(appHandle, __FUNCTION__);
+
+        if (appHandle->xmlDir)
+        {
+            free(appHandle->xmlDir);
+        }
+        appHandle->xmlDir = NULL;
+
+        if (appHandle->xmlCusDir)
+        {
+            free(appHandle->xmlCusDir);
+        }
+        appHandle->xmlCusDir = NULL;
+
+        if (appHandle->featureOptionsDoc)
+        {
+            xmlFreeDoc(appHandle->featureOptionsDoc);
+        }
+        appHandle->featureOptionsDoc = NULL;
+
+#ifndef WIN32
+        if (appHandle->appThreadExit == 0)
+        {
+            void *status;
+            appHandle->appThreadExit = 1;
+            INFO_LOG("Send signal to appThread\n");
+            pthread_kill(appHandle->appThread, SIGUSR1);
+
+            /* TODO: Don't join the inotify thread, since the read function is block waiting */
+            INFO_LOG("Waiting inotify thread join...\n");
+            pthread_join(appHandle->appThread, &status);
+            INFO_LOG("inotify thread joined\n");
+            appHandle->inotifyFd = 0;
+        }
+#endif
+
+        // release notify callback list
+        LL_FOREACH_SAFE(appHandle->noficyCbList, notifyCb, tmp)
+        {
+            LL_DELETE(appHandle->noficyCbList, notifyCb);
+            free(notifyCb);
+        }
+        appHandle->noficyCbList = NULL;
+
+        /* If appHandle is singleton instance, reset the init info */
+        if (appHandle == &appHandleInst)
+        {
+            appHandleInited = 0;
+        }
+
+        appHandleReleaseAudioTypeHash(appHandle);
+
+        appHandleReleaseFeatureOptionsHash(appHandle);
+
+        xmlCleanupParser();
+
+        /* Flush app log */
+        if (appLogFp)
+        {
+            fflush(appLogFp);
+        }
+
+        /* Unlock */
+        appHandleUnlock(appHandle);
+
+        return APP_NO_ERROR;
+    }
+}
+
+EXPORT const char *appHandleGetBuildTimeStamp()
+{
+    return __DATE__" "__TIME__;
+}
+
+EXPORT int appHandleWriteLock(AppHandle *appHandle, const char *callerFun)
+{
+    int res = 0;
+
+    if (!appHandle)
+    {
+        WARN_LOG("appHandle is NULL\n");
+        return res;
+    }
+
+#ifndef WIN32
+    while (1)
+    {
+        if (pthread_rwlock_trywrlock(&appHandle->lock) == 0)
+        {
+            appHandle->lockCallerFun = callerFun;
+            DEBUG_LOG("AppHandle lock is locked by %s()\n", appHandle->lockCallerFun);
+            break;
+        }
+        else
+        {
+            DEBUG_LOG("Cannot lock the AppHandle lock, delay some time. (the locker is %s())\n", appHandle->lockCallerFun);
+            utilUsleep(1);
+        }
+    }
+#else
+    //DEBUG_LOG("Not support this function yet!\n");
+#endif
+    return res;
+}
+
+EXPORT int appHandleReadLock(AppHandle *appHandle, const char *callerFun)
+{
+    int res = 0;
+
+    if (!appHandle)
+    {
+        WARN_LOG("appHandle is NULL\n");
+        return res;
+    }
+
+#ifndef WIN32
+    while (1)
+    {
+        if (pthread_rwlock_tryrdlock(&appHandle->lock) == 0)
+        {
+            appHandle->lockCallerFun = callerFun;
+            DEBUG_LOG("AppHandle lock is locked by %s()\n", appHandle->lockCallerFun);
+            break;
+        }
+        else
+        {
+            DEBUG_LOG("Cannot lock the AppHandle lock, delay some time. (the locker is %s())\n", appHandle->lockCallerFun);
+            utilUsleep(1);
+        }
+    }
+#else
+    //DEBUG_LOG("Not support this function yet!\n");
+#endif
+    return res;
+}
+
+EXPORT int appHandleUnlock(AppHandle *appHandle)
+{
+    int res = 0;
+
+    if (!appHandle)
+    {
+        WARN_LOG("appHandle is NULL\n");
+        return res;
+    }
+
+#ifndef WIN32
+    DEBUG_LOG("Unlock appHandle lock\n");
+    res = pthread_rwlock_unlock(&appHandle->lock);
+#endif
+    return res;
+}
+
+EXPORT int appHandleInstWriteLock(const char *callerFun)
+{
+    int res = 0;
+
+#ifndef WIN32
+    while (1)
+    {
+        if (pthread_rwlock_trywrlock(&appHandleInstLock) == 0)
+        {
+            appHandleInstLockCallerFun = callerFun;
+            DEBUG_LOG("%s acquired the appHandleInstLock\n", callerFun);
+            break;
+        }
+        else
+        {
+            DEBUG_LOG("Cannot lock the appHandleInstLock, delay some time. (the locker is %s)\n", callerFun);
+            utilUsleep(1);
+        }
+    }
+#else
+    //DEBUG_LOG("Not support this function yet!\n");
+#endif
+    return res;
+}
+
+EXPORT int appHandleInstUnlock()
+{
+    int res = 0;
+#ifndef WIN32
+    DEBUG_LOG("Unlock appHandleInst lock\n");
+    res = pthread_rwlock_unlock(&appHandleInstLock);
+#endif
+    return res;
+}
+
+EXPORT FeatureOption *featureOptionCreate(const char *name, const char *value)
+{
+    FeatureOption *featureOption = malloc(sizeof(FeatureOption));
+    featureOption->name = strdup(name);
+    featureOption->value = strdup(value);
+    return featureOption;
+}
+
+EXPORT void featureOptionRelease(FeatureOption *featureOption)
+{
+    free(featureOption->name);
+    free(featureOption->value);
+    free(featureOption);
+}
+
+EXPORT void appHandleReleaseFeatureOptionsHash(AppHandle *appHandle)
+{
+    if (appHandle->featureOptionsHash)
+    {
+        FeatureOption *tmp, *item;
+        HASH_ITER(hh, appHandle->featureOptionsHash, item, tmp)
+        {
+            HASH_DEL(appHandle->featureOptionsHash, item);
+            featureOptionRelease(item);
+        }
+    }
+    appHandle->featureOptionsHash = NULL;
+}
+
+EXPORT AppHandle *appHandleGetInstance()
+{
+    appHandleInstWriteLock(__FUNCTION__);
+
+    INFO_LOG("");
+
+    if (!appHandleInited)
+    {
+        appHandleInit(&appHandleInst);
+#ifdef WIN32
+        appHandleParseXml(&appHandleInst, XML_FOLDER_ON_TUNING_TOOL, XML_CUS_FOLDER_ON_TUNING_TOOL);
+#else
+        appHandleParseXml(&appHandleInst, XML_FOLDER_ON_DEVICE, XML_CUS_FOLDER_ON_DEVICE);
+#endif
+        appHandleInited = 1;
+    }
+
+    appHandleInstUnlock();
+
+    return &appHandleInst;
+}
+
+EXPORT APP_STATUS appHandleParseXml(AppHandle *appHandle, const char *dir, const char *cusDir)
+{
+    INFO_LOG("appHandle = 0x%p, dir = %s, cusDir = %s\n", appHandle, dir, cusDir);
+
+    if (!appHandle)
+    {
+        ERR_LOG("appHandle is NULL!\n");
+        return APP_ERROR;
+    }
+
+    if (!dir)
+    {
+        ERR_LOG("dir is NULL\n");
+        return APP_ERROR;
+    }
+
+    if (appHandle->xmlDir || appHandle->xmlCusDir)
+    {
+        ERR_LOG("XML already parsed, don't call the appHandleParseXml twice!\n");
+        return APP_ERROR;
+    }
+
+    appHandleWriteLock(appHandle, __FUNCTION__);
+
+    appHandle->xmlDir = strdup(dir);
+#if defined(APP_FORCE_ENABLE_CUS_XML) || defined(WIN32) || defined(CONFIG_MT_ENG_BUILD)
+    appHandle->xmlCusDir = strdup(cusDir);
+#else
+#if !defined(WIN32)
+    if (isCustXmlEnable())
+    {
+        appHandle->xmlCusDir = strdup(cusDir);
+    }
+    else
+    {
+        INFO_LOG("Cust XML folder not enabled");
+    }
+#endif
+#endif
+    INFO_LOG("XmlDir = %s, XmlCusDir = %s\n", appHandle->xmlDir, appHandle->xmlCusDir);
+
+    /* Load feature options information */
+    appHandleLoadDirFeatureOptionsInfo(appHandle);
+
+    /* Load audio type information */
+    appHandleLoadDirAudioTypeInfo(appHandle);
+
+    appHandleUnlock(appHandle);
+
+#ifndef WIN32
+    /* Setup file system monitor thread */
+    if (appHandle->xmlCusDir)
+    {
+        if (pthread_create(&appHandle->appThread, NULL, appHandleThreadLoop, (void *)appHandle))
+        {
+            ERR_LOG("Create app thread fail!\n");
+            return APP_ERROR;
+        }
+        else
+        {
+            INFO_LOG("Create app thread successfully\n");
+        }
+    }
+    else
+    {
+        WARN_LOG("Cus folder is NULL, don't create FS monitor thread\n");
+    }
+#endif
+    return APP_NO_ERROR;
+}
+
+EXPORT APP_STATUS appHandleLoadDirFeatureOptionsInfo(AppHandle *appHandle)
+{
+    struct stat fileStat;
+    int strLen;
+    char *featureOptionsFile = NULL;
+    xmlNode *node = NULL;
+    xmlNode *root = NULL;
+    xmlChar *name = NULL;
+    xmlChar *value = NULL;
+
+    if (!appHandle)
+    {
+        ERR_LOG("appHandle is NULL!\n");
+        return APP_ERROR;
+    }
+
+    if (!appHandle->xmlDir)
+    {
+        ERR_LOG("xmlDir is NULL!\n");
+        return APP_ERROR;
+    }
+
+    if (appHandle->featureOptionsHash)
+    {
+        WARN_LOG("Feature options already loaded, don't reload it!\n");
+        return APP_NO_ERROR;
+    }
+
+    /* Check cus folder xml first */
+    strLen = strlen(appHandle->xmlDir) + strlen(FEATURE_OPTIONS_XML) + 2;
+    featureOptionsFile = (char *)malloc(strLen);
+    sprintf(featureOptionsFile, "%s%s%s", appHandle->xmlDir, FOLDER, FEATURE_OPTIONS_XML);
+
+    if (stat(featureOptionsFile, &fileStat) == -1)
+    {
+        ERR_LOG("No %s file\n", featureOptionsFile);
+        free(featureOptionsFile);
+        return APP_ERROR;
+    }
+
+    appHandle->featureOptionsDoc = xmlParseFile(featureOptionsFile);
+    if (appHandle->featureOptionsDoc == NULL)
+    {
+        ERR_LOG("Failed to parse %s\n", featureOptionsFile);
+        free(featureOptionsFile);
+        return APP_ERROR;
+    }
+    else
+    {
+        INFO_LOG("Load xml file successfully. (%s)\n", featureOptionsFile);
+    }
+    free(featureOptionsFile);
+
+    /* Parse informatino to feature options hash */
+    root = xmlDocGetRootElement(appHandle->featureOptionsDoc);
+    if (!root)
+    {
+        ERR_LOG("Root element is NULL\n");
+        return APP_ERROR;
+    }
+
+    node = findXmlNodeByElemName(root, ELEM_AUDIO_FEATURE_OPTIONS);
+    if (node && node->children)
+    {
+        node = node->children;
+    }
+    else
+    {
+        ERR_LOG("No feature options found!\n");
+        return APP_ERROR;
+    }
+
+    while ((node = findXmlNodeByElemName(node->next, ELEM_PARAM)))
+    {
+        FeatureOption *featureOption;
+        name = xmlGetProp(node, (const xmlChar *)ATTRI_NAME);
+        value = xmlGetProp(node, (const xmlChar *)ATTRI_VALUE);
+
+        featureOption = featureOptionCreate((const char *)name, (const char *)value);
+        HASH_ADD_KEYPTR(hh, appHandle->featureOptionsHash, featureOption->name, strlen(featureOption->name), featureOption);
+
+        if (name)
+        {
+            xmlFree(name);
+        }
+
+        if (value)
+        {
+            xmlFree(value);
+        }
+    }
+
+    return APP_NO_ERROR;
+}
+
+EXPORT APP_STATUS appHandleLoadDirAudioTypeInfo(AppHandle *appHandle)
+{
+    size_t numOfAudioParamXml = 0;
+    char audioType[MAX_AUDIO_TYPE_LEN];
+
+#ifdef __linux__
+    struct dirent **namelist;
+    int i;
+    int total;
+
+    if (!appHandle)
+    {
+        ERR_LOG("appHandle is NULL!\n");
+        return APP_ERROR;
+    }
+
+    total = scandir(appHandle->xmlDir, &namelist, 0, alphasort);
+
+    /* Release old audio type first */
+    appHandleReleaseAudioTypeHash(appHandle);
+
+    if (total < 0)
+    {
+        ERR_LOG("Scandir error\n");
+    }
+    else
+    {
+        for (i = 0; i < total; i++)
+        {
+            if (strstr(namelist[i]->d_name, AUDIO_PARAM_XML_POSFIX) == NULL)
+            {
+                DEBUG_LOG("File name's posfix is not AudioParam.xml (%s)\n", namelist[i]->d_name);
+                continue;
+            }
+
+            sscanf(namelist[i]->d_name, AUDIO_TYPE_FMT_STR(MAX_AUDIO_TYPE_LEN), audioType);
+            if (appHandleIsValidAudioType(appHandle, audioType))
+            {
+                appHandleAddAudioType(appHandle, audioType);
+            }
+            else
+            {
+                WARN_LOG("Invalid audio param xml = %s\n", namelist[i]->d_name);
+            }
+        }
+    }
+#else
+    WIN32_FIND_DATA FindFileData;
+    HANDLE hFind;
+    UT_string *path = NULL;
+
+    if (!appHandle)
+    {
+        ERR_LOG("appHandle is NULL!\n");
+        return APP_ERROR;
+    }
+
+    /* Release old audio type first */
+    appHandleReleaseAudioTypeHash(appHandle);
+
+    /* Check preload xml folder */
+    utstring_new(path);
+    utstring_printf(path, "%s"FOLDER"*"AUDIO_PARAM_XML_POSFIX, appHandle->xmlDir);
+    hFind = FindFirstFile(utstring_body(path), &FindFileData);
+    utstring_free(path);
+
+    if (hFind == INVALID_HANDLE_VALUE)
+    {
+        WARN_LOG("No xml found!\n");
+        return APP_ERROR;
+    }
+
+    do
+    {
+        sscanf(FindFileData.cFileName, AUDIO_TYPE_FMT_STR(MAX_AUDIO_TYPE_LEN), audioType);
+
+        if (appHandleIsValidAudioType(appHandle, audioType))
+        {
+            appHandleAddAudioType(appHandle, audioType);
+        }
+        else
+        {
+            INFO_LOG("Invalid audio param xml = %s\n", FindFileData.cFileName);
+        }
+    }
+    while (FindNextFile(hFind, &FindFileData));
+#endif
+
+    /* Load all XMLs */
+    appHandleLoadAllAudioTypeXml(appHandle);
+    INFO_LOG("Load all audio type XML - ok\n");
+
+    /* Modify data depends on feature options */
+    appHandleReviseXmlDocByFeatureOptions(appHandle);
+
+    /* Load hash info from XML */
+    appHandleLoadAllAudioTypeHash(appHandle);
+    INFO_LOG("Load all audio Hash - ok\n");
+
+    if (appDebugLevel == DEBUG_LEVEL)
+    {
+        appHandleDumpAudioTypeList(appHandle);
+    }
+
+    return APP_NO_ERROR;
+}
+
+EXPORT size_t appHandleGetNumOfAudioType(AppHandle *appHandle)
+{
+    if (!appHandle)
+    {
+        ERR_LOG("appHandle is NULL!\n");
+        return APP_ERROR;
+    }
+
+    return HASH_COUNT(appHandle->audioTypeHash);
+}
+
+EXPORT APP_STATUS appHandleLoadAllAudioTypeXml(AppHandle *appHandle)
+{
+    size_t i;
+    size_t count = appHandleGetNumOfAudioType(appHandle);
+
+    for (i = 0; i < count; i++)
+    {
+        AudioType *audioType = appHandleGetAudioTypeByIndex(appHandle, i);
+
+        /* Load xml struct */
+        if (appHandleLoadAudioTypeXml(appHandle, audioType) == APP_ERROR)
+        {
+            WARN_LOG("Load audio type XML failed. (%s)\n", audioType->name);
+        }
+    }
+
+    return APP_NO_ERROR;
+}
+
+EXPORT APP_STATUS appHandleLoadAudioTypeXml(AppHandle *appHandle, AudioType *audioType)
+{
+    char *audioTypeFile;
+
+    INFO_LOG("audioType = %s\n", audioType->name);
+
+    // Load AudioParamXml
+    audioTypeFile = appHandleGetAudioTypeFilePath(appHandle, audioType->name, AUDIO_PARAM_XML_POSFIX);
+    if (audioTypeFile == NULL)
+    {
+        WARN_LOG("The AudioTypeFile(%s%s) doesn't exist.\n", audioType->name, AUDIO_PARAM_XML_POSFIX);
+        return APP_ERROR;
+    }
+
+    audioType->audioParamDoc = xmlParseFile(audioTypeFile);
+
+    if (audioType->audioParamDoc == NULL)
+    {
+        ERR_LOG("Failed to parse %s\n", audioTypeFile);
+        free(audioTypeFile);
+
+        // Audio param file broken, load preload xml file instead
+        audioTypeFile = appHandleGetPreloadAudioTypeFilePath(appHandle, audioType->name, AUDIO_PARAM_XML_POSFIX);
+        if (audioTypeFile == NULL)
+        {
+            WARN_LOG("The AudioTypeFile(%s%s) doesn't exist.\n", audioType->name, AUDIO_PARAM_XML_POSFIX);
+            return APP_ERROR;
+        }
+
+        WARN_LOG("Trying to load preload %s file instead of broken XML file!\n", audioTypeFile);
+        audioType->audioParamDoc = xmlParseFile(audioTypeFile);
+        if (audioType->audioParamDoc == NULL)
+        {
+            ERR_LOG("Failed to parse %s\n", audioTypeFile);
+            free(audioTypeFile);
+            return APP_ERROR;
+        }
+        else
+        {
+            INFO_LOG("Load xml file successfully. (%s)\n", audioTypeFile);
+        }
+    }
+    else
+    {
+        INFO_LOG("Load xml file successfully. (%s)\n", audioTypeFile);
+    }
+
+    free(audioTypeFile);
+
+    // Load ParamUnitDescXml
+    audioTypeFile = appHandleGetAudioTypeFilePath(appHandle, audioType->name, PARAM_UNIT_DESC_XML_POSFIX);
+    if (audioTypeFile == NULL)
+    {
+        WARN_LOG("The AudioTypeFile(%s%s) doesn't exist.\n", audioType->name, PARAM_UNIT_DESC_XML_POSFIX);
+        return APP_ERROR;
+    }
+
+    audioType->paramUnitDescDoc = xmlParseFile(audioTypeFile);
+    if (audioType->paramUnitDescDoc == NULL)
+    {
+        ERR_LOG("Failed to parse %s%s\n", audioTypeFile, PARAM_UNIT_DESC_XML_POSFIX);
+        free(audioTypeFile);
+        return APP_ERROR;
+    }
+    else
+    {
+        INFO_LOG("Load xml file successfully. (%s)\n", audioTypeFile);
+    }
+    free(audioTypeFile);
+
+#ifdef WIN32
+    // Load ParamTreeViewXml only for tuning tool
+    audioTypeFile = appHandleGetAudioTypeFilePath(appHandle, audioType->name, PARAM_TREE_VIEW_XML_POSFIX);
+    if (audioTypeFile == NULL)
+    {
+        INFO_LOG("The AudioTypeFile(%s%s) doesn't exist.\n", audioType->name, PARAM_TREE_VIEW_XML_POSFIX);
+        free(audioTypeFile);
+    }
+    else
+    {
+        audioType->paramTreeViewDoc = xmlParseFile(audioTypeFile);
+        if (audioType->audioParamDoc == NULL)
+        {
+            DEBUG_LOG("Failed to parse %s%s\n", audioTypeFile, PARAM_TREE_VIEW_XML_POSFIX);
+        }
+        else
+        {
+            INFO_LOG("Load xml file successfully. (%s)\n", audioTypeFile);
+        }
+        free(audioTypeFile);
+    }
+#endif
+
+    /* Get tab name info */
+    audioTypeParseTabName(audioType);
+
+    /* Get version info */
+    if (audioTypeParseXmlVer(audioType) == APP_ERROR)
+    {
+        ERR_LOG("Cannot parse xml version info. (%s)\n", audioType->name);
+        return APP_ERROR;
+    }
+
+#ifndef WIN32
+    /* XML Version check for device driver or HAL */
+    if (!audioTypeIsDeviceSupportedXmlVer(audioType))
+    {
+        abort();
+    }
+#endif
+
+    return APP_NO_ERROR;
+}
+
+EXPORT char *appHandleGetAudioTypeFilePath(AppHandle *appHandle, const char *audioType, const char *posfix)
+{
+    /* Check cus folder xml first */
+    struct stat fileStat;
+    int strLen;
+    char *path;
+
+    if (appHandle->xmlCusDir && !strcmp(posfix, AUDIO_PARAM_XML_POSFIX))
+    {
+        strLen = strlen(appHandle->xmlCusDir) + strlen(audioType) + strlen(posfix) + 2;
+        path = (char *)malloc(strLen);
+        sprintf(path, "%s%s%s%s", appHandle->xmlCusDir, FOLDER, audioType, posfix);
+
+        if (stat(path, &fileStat) != -1)
+        {
+            return path;
+        }
+        else
+        {
+            free(path);
+        }
+    }
+
+    /* Check default folder */
+    strLen = strlen(appHandle->xmlDir) + strlen(audioType) + strlen(posfix) + 2;
+    path = (char *)malloc(strLen);
+    sprintf(path, "%s%s%s%s", appHandle->xmlDir, FOLDER, audioType, posfix);
+
+    if (stat(path, &fileStat) != -1)
+    {
+        return path;
+    }
+
+    free(path);
+    return NULL;
+}
+
+EXPORT char *appHandleGetPreloadAudioTypeFilePath(AppHandle *appHandle, const char *audioType, const char *posfix)
+{
+    /* Check cus folder xml first */
+    struct stat fileStat;
+    int strLen;
+    char *path;
+
+    /* Check default folder */
+    strLen = strlen(appHandle->xmlDir) + strlen(audioType) + strlen(posfix) + 2;
+    path = (char *)malloc(strLen);
+    sprintf(path, "%s%s%s%s", appHandle->xmlDir, FOLDER, audioType, posfix);
+
+    if (stat(path, &fileStat) != -1)
+    {
+        return path;
+    }
+
+    free(path);
+    return NULL;
+}
+
+EXPORT int appHandleIsValidAudioType(AppHandle *appHandle, const char *audioType)
+{
+    char *filePath;
+
+    assert(appHandle != NULL);
+    filePath = appHandleGetAudioTypeFilePath(appHandle, audioType, PARAM_UNIT_DESC_XML_POSFIX);
+    if (filePath == NULL)
+    {
+        ERR_LOG("%s audio type is not valid! (%s is not exist)\n", audioType, filePath);
+        free(filePath);
+        return 0;
+    }
+
+    free(filePath);
+    return 1;
+}
+
+EXPORT AudioType *appHandleAddAudioType(AppHandle *appHandle, const char *audioTypeName)
+{
+    AudioType *audioType;
+
+    if (!appHandle)
+    {
+        ERR_LOG("The appHandle is NULL\n");
+        return NULL;
+    }
+
+    if (!audioTypeName)
+    {
+        ERR_LOG("The audioTypeName is NULL\n");
+        return NULL;
+    }
+
+    audioType = audioTypeCreate(appHandle, audioTypeName);
+
+    /* Add audio type to hash */
+    HASH_ADD_KEYPTR(hh, appHandle->audioTypeHash, audioType->name, strlen(audioType->name), audioType);
+
+    return audioType;
+}
+
+EXPORT AudioType *appHandleGetAudioTypeByIndex(AppHandle *appHandle, size_t index)
+{
+    AudioType *audioType = NULL;
+    size_t i = 0;
+
+    DEBUG_LOG("appHandle = 0x%p, index = %lu\n", appHandle, index);
+
+    if (!appHandle)
+    {
+        ERR_LOG("appHandle is NULL!\n");
+        return NULL;
+    }
+
+    for (audioType = appHandle->audioTypeHash; audioType ; audioType = audioType->hh.next)
+    {
+        if (index == i++)
+        {
+            return audioType;
+        }
+    }
+
+    return NULL;
+}
+
+EXPORT AudioType *appHandleGetAudioTypeByName(AppHandle *appHandle, const char *name)
+{
+    AudioType *audioType = NULL;
+
+    INFO_LOG("appHandle = 0x%p, name = %s\n", appHandle, name);
+
+    if (!appHandle)
+    {
+        ERR_LOG("appHandle is NULL!\n");
+        return NULL;
+    }
+
+    HASH_FIND_STR(appHandle->audioTypeHash, name, audioType);
+
+    return audioType;
+}
+
+EXPORT void appHandleReleaseAudioTypeHash(AppHandle *appHandle)
+{
+    if (appHandle->audioTypeHash)
+    {
+        AudioType *tmp, *item;
+        HASH_ITER(hh, appHandle->audioTypeHash, item, tmp)
+        {
+            HASH_DEL(appHandle->audioTypeHash, item);
+            audioTypeRelease(item);
+        }
+    }
+    appHandle->audioTypeHash = NULL;
+}
+
+EXPORT void appHandleDumpAudioTypeList(AppHandle *appHandle)
+{
+    size_t index = 0;
+    size_t numOfAudioType = appHandleGetNumOfAudioType(appHandle);
+    INFO_LOG("=================================\n");
+    INFO_LOG("Totoal num of Audio Type List = %lu\n", numOfAudioType);
+    for (index = 0; index < numOfAudioType; index++)
+    {
+        AudioType *audioType = appHandleGetAudioTypeByIndex(appHandle, index);
+        INFO_LOG("AudioType[%lu] = %s\n", index, audioType->name);
+        audioTypeDump(audioType);
+    }
+}
+
+EXPORT APP_STATUS appHandleLoadAllAudioTypeHash(AppHandle *appHandle)
+{
+    size_t index = 0;
+    size_t numOfAudioType = appHandleGetNumOfAudioType(appHandle);
+    /* Load stage1 information */
+    for (index = 0; index < numOfAudioType; index++)
+    {
+        AudioType *audioType = appHandleGetAudioTypeByIndex(appHandle, index);
+        audioTypeLoadStage1Hash(audioType);
+    }
+
+    /* Load stage2 information (ex: ParamTreeView's switch object)*/
+    for (index = 0; index < numOfAudioType; index++)
+    {
+        AudioType *audioType = appHandleGetAudioTypeByIndex(appHandle, index);
+        audioTypeLoadStage2Hash(audioType);
+    }
+
+    return APP_NO_ERROR;
+}
+
+EXPORT void appHandleRegXmlChangedCb(AppHandle *appHandle, NOTIFY_CB_FUN callbackFun)
+{
+    INFO_LOG("appHandle = 0x%p, callbackFun = 0x%p\n", appHandle, callbackFun);
+
+    appHandleWriteLock(appHandle, __FUNCTION__);
+
+    if (appHandle && callbackFun)
+    {
+        /* Checking the duplicated callback function registration */
+        NotifyCb *notifyCb;
+        LL_FOREACH(appHandle->noficyCbList, notifyCb)
+        {
+            if (notifyCb->cb == callbackFun)
+            {
+                INFO_LOG("Same callback function found. ignore it\n");
+                appHandleUnlock(appHandle);
+                return;
+            }
+        }
+
+        notifyCb = malloc(sizeof(NotifyCb));
+        notifyCb->cb = callbackFun;
+        LL_APPEND(appHandle->noficyCbList, notifyCb);
+    }
+    else
+    {
+        WARN_LOG("Cannot register xml callback! (AppHandle = 0x%p, callbackFun = 0x%p)\n", appHandle, callbackFun);
+    }
+
+    appHandleUnlock(appHandle);
+}
+
+EXPORT void appHandleUnregXmlChangedCb(AppHandle *appHandle, NOTIFY_CB_FUN callbackFun)
+{
+    INFO_LOG("appHandle = 0x%p, callbackFun = 0x%p\n", appHandle, callbackFun);
+
+    appHandleWriteLock(appHandle, __FUNCTION__);
+
+    if (appHandle && callbackFun)
+    {
+        NotifyCb *notifyCb, *tmp;
+        LL_FOREACH_SAFE(appHandle->noficyCbList, notifyCb, tmp)
+        {
+            if (notifyCb->cb == callbackFun)
+            {
+                LL_DELETE(appHandle->noficyCbList, notifyCb);
+                free(notifyCb);
+                INFO_LOG("Callback function unregistered. (0x%p, 0x%p)\n", callbackFun, callbackFun);
+                break;
+            }
+        }
+    }
+    else
+    {
+        WARN_LOG("Cannot unregister xml callback! (AppHandle = 0x%p, callbackFun = %p)\n", appHandle, callbackFun);
+    }
+
+    appHandleUnlock(appHandle);
+}
+
+EXPORT void *appHandleThreadLoop(void *arg)
+{
+#if defined(WIN32)
+    /* Always disable on WIN32 */
+    return NULL;
+#else
+    /* This thread only work on linux platform */
+    /* Only eng load could monitor custom folder */
+    AppHandle *appHandle = (AppHandle *)arg;
+    ssize_t len;
+    char buf[INOTIFY_BUF_SIZE];
+    char *ptr;
+    const struct inotify_event *event;
+
+#if !defined(APP_FORCE_ENABLE_CUS_XML) && !defined(CONFIG_MT_ENG_BUILD)
+    /* User load, check NVRam status */
+    if (isCustXmlEnable())
+    {
+        INFO_LOG("User load, cust xml enabled\n");
+    }
+    else
+    {
+        INFO_LOG("User load but cust xml disabled\n");
+        return NULL;
+    }
+#endif
+
+    if (!appHandle->xmlCusDir)
+    {
+        WARN_LOG("xmlCusDir is NULL, don't run the appHandleThreadLoop !!!");
+        exit(1);
+    }
+
+    /* Create folder first to make inotify work */
+    utilMkdir(appHandle->xmlCusDir);
+
+    /* Register signal handler */
+    struct sigaction sa;
+    sa.sa_handler = NULL;
+    sa.sa_sigaction = &signalHandler;
+    sa.sa_flags = SA_SIGINFO;
+    sigemptyset(&sa.sa_mask);
+
+    if (sigaction(SIGUSR1, &sa, NULL) < 0)
+    {
+        ERR_LOG("sigaction fail");
+        exit(1);
+    }
+
+    /* inotify registration */
+    appHandle->inotifyFd = inotify_init();
+    if (appHandle->inotifyFd < 0)
+    {
+        ERR_LOG("inotify_init failed !!!");
+        exit(1);
+    }
+
+    INFO_LOG("Add inotify monitor path = %s, fd = %d\n", appHandle->xmlCusDir, appHandle->inotifyFd);
+
+    while (1)
+    {
+        if (inotify_add_watch(appHandle->inotifyFd, appHandle->xmlCusDir, IN_CLOSE_WRITE) < 0)
+        {
+            ERR_LOG("inotify_add_watch failed !!! try again...");
+            utilMkdir(appHandle->xmlCusDir);
+            utilUsleep(1000000);
+        } else {
+            break;
+        }
+    }
+
+    while (!appHandle->appThreadExit)
+    {
+        INFO_LOG("inotify read waiting... (fd = %d)\n", appHandle->inotifyFd);
+        len = read(appHandle->inotifyFd, buf, sizeof(buf));
+
+        if (len < 0)
+        {
+            if (appHandle->appThreadExit)
+            {
+                break;
+            }
+
+            ERR_LOG("inotify read error!\n");
+            pthread_exit(NULL);
+        }
+
+        /* Loop over all events in the buffer */
+        for (ptr = buf; ptr < buf + len; ptr += sizeof(struct inotify_event) + event->len)
+        {
+            event = (const struct inotify_event *) ptr;
+            if (event->len)
+            {
+                NotifyCb *notifyCb;
+                char audioTypeName[MAX_AUDIO_TYPE_LEN];
+                AudioType *audioType;
+
+                if (strstr(event->name, AUDIO_PARAM_XML_POSFIX) == NULL)
+                {
+                    INFO_LOG("File name's posfix is not AudioParam.xml (%s)\n", event->name);
+                    continue;
+                }
+
+                sscanf(event->name, AUDIO_TYPE_FMT_STR(MAX_AUDIO_TYPE_LEN), audioTypeName);
+                INFO_LOG("XML File chanegd (%s)\n", event->name);
+
+                audioType = appHandleGetAudioTypeByName(appHandle, audioTypeName);
+                if (audioType)
+                {
+                    audioType->allowReload = 1;
+                }
+
+                /* notify users */
+                INFO_LOG("Notify all callback function.\n");
+                LL_FOREACH(appHandle->noficyCbList, notifyCb)
+                {
+                    INFO_LOG("Notify callback function. (0x%p, %pf)\n", notifyCb->cb, notifyCb->cb);
+                    (*notifyCb->cb)(appHandle, audioTypeName);
+                }
+            }
+        }
+    }
+
+    inotify_rm_watch(appHandle->inotifyFd, IN_CLOSE_NOWRITE);
+
+    if (appHandle->inotifyFd)
+    {
+        INFO_LOG("close inotify handle %d\n", appHandle->inotifyFd);
+        close(appHandle->inotifyFd);
+    }
+
+    INFO_LOG("appHandleThreadLoop exit\n");
+    return NULL;
+#endif
+}
+
+EXPORT APP_STATUS appHandleReloadAudioType(AppHandle *appHandle, const char *audioTypeName)
+{
+    /* Release old audioType */
+    char *audioTypeFile;
+    AudioType *audioType;
+
+    INFO_LOG("appHandle = 0x%p, audioTypeName = %s\n", appHandle, audioTypeName);
+
+    audioType = appHandleGetAudioTypeByName(appHandle, audioTypeName);
+    if (!audioType)
+    {
+        ERR_LOG("Invalid AudioType name = %s\n", audioTypeName);
+        return APP_ERROR;
+    }
+
+    /* Write lock */
+    audioTypeWriteLock(audioType, __FUNCTION__);
+
+    /* Checking if the audioType reloaded */
+    if (!audioType->allowReload)
+    {
+        INFO_LOG("AudioType is already reloaded!\n");
+        audioTypeUnlock(audioType);
+        return APP_NO_ERROR;
+    }
+
+    /* Release audio param data */
+    audioTypeReleaseAudioParam(audioType);
+    /* Release audio param xml */
+    if (audioType->audioParamDoc)
+    {
+        xmlFreeDoc(audioType->audioParamDoc);
+    }
+
+    /* Load AudioParam XML */
+    audioTypeFile = appHandleGetAudioTypeFilePath(appHandle, audioType->name, AUDIO_PARAM_XML_POSFIX);
+    if (audioTypeFile == NULL)
+    {
+        WARN_LOG("The AudioTypeFile(%s%s) doesn't exist.\n", audioType->name, AUDIO_PARAM_XML_POSFIX);
+        audioTypeUnlock(audioType);
+        return APP_ERROR;
+    }
+
+    audioType->audioParamDoc = xmlParseFile(audioTypeFile);
+    if (audioType->audioParamDoc == NULL)
+    {
+        ERR_LOG("Failed to parse %s\n", audioTypeFile);
+        free(audioTypeFile);
+        audioTypeUnlock(audioType);
+        return APP_ERROR;
+    }
+    else
+    {
+        INFO_LOG("Load xml file successfully. (%s)\n", audioTypeFile);
+    }
+
+    free(audioTypeFile);
+
+    /* Load AudioParam hash */
+    if (audioTypeLoadParamUnitHash(audioType) == APP_ERROR)
+    {
+        audioTypeUnlock(audioType);
+        return APP_ERROR;
+    }
+
+    if (audioTypeLoadParamTreeHash(audioType) == APP_ERROR)
+    {
+        audioTypeUnlock(audioType);
+        return APP_ERROR;
+    }
+
+    /* AudioType reloaded */
+    audioType->allowReload = 0;
+
+    audioTypeUnlock(audioType);
+    return APP_NO_ERROR;
+}
+
+EXPORT const char *appHandleGetFeatureOptionValue(AppHandle *appHandle, const char *featureOptionName)
+{
+    FeatureOption *featureOption = NULL;
+
+    if (!appHandle)
+    {
+        ERR_LOG("appHandle is NULL\n");
+        return NULL;
+    }
+
+    if (!featureOptionName)
+    {
+        DEBUG_LOG("featureOptionName is NULL\n");
+        return NULL;
+    }
+
+    HASH_FIND_STR(appHandle->featureOptionsHash, featureOptionName, featureOption);
+    if (featureOption)
+    {
+        return featureOption->value;
+    }
+
+    return NULL;
+}
+
+EXPORT int appHandleIsFeatureOptionEnabled(AppHandle *appHandle, const char *featureOptionName)
+{
+    const char *featureOptionValueStr;
+    if (!appHandle)
+    {
+        WARN_LOG("appHandle is NULL\n");
+        return 0;
+    }
+
+    if (!featureOptionName)
+    {
+        WARN_LOG("featureOptionName is NULL\n");
+        return 0;
+    }
+
+    featureOptionValueStr = appHandleGetFeatureOptionValue(appHandle, featureOptionName);
+    if (featureOptionValueStr)
+    {
+        return !strcmp(featureOptionValueStr, "yes");
+    }
+    else
+    {
+        DEBUG_LOG("No %s such feature option\n", featureOptionName);
+        return 0;
+    }
+}
+
+EXPORT size_t appHandleGetNumOfFeatureOption(AppHandle *appHandle)
+{
+    if (!appHandle)
+    {
+        ERR_LOG("appHandle is NULL!\n");
+        return APP_ERROR;
+    }
+
+    return HASH_COUNT(appHandle->featureOptionsHash);
+}
+
+EXPORT FeatureOption *appHandleGetFeatureOptionByIndex(AppHandle *appHandle, size_t index)
+{
+    FeatureOption *featureOption = NULL;
+    size_t i = 0;
+
+    if (!appHandle)
+    {
+        ERR_LOG("appHandle is NULL\n");
+        return NULL;
+    }
+
+    for (featureOption = appHandle->featureOptionsHash; featureOption ; featureOption = featureOption->hh.next)
+    {
+        if (index == i++)
+        {
+            return featureOption;
+        }
+    }
+
+    return NULL;
+}
+
+/* This function is only work for windows */
+EXPORT void appHandleRedirectIOToConsole()
+{
+    INFO_LOG("");
+#ifdef WIN32
+    outputLogToStdout = 1;
+    redirectIOToConsole();
+#endif
+}
+
+int removeNodeByFeatureOption(AppHandle *appHandle, xmlNode *categoryNode)
+{
+    /* Process Category of CategoryTpe Node */
+    xmlChar *featureOption = xmlNodeGetProp(categoryNode, ATTRI_FEATURE_OPTION);
+    if (featureOption)
+    {
+        int Not = 0;
+        if (featureOption[0] == '!')
+        {
+            Not = 1;
+            featureOption++;
+        }
+
+        if (!(Not ^ appHandleIsFeatureOptionEnabled(appHandle, (char *)featureOption)))
+        {
+            xmlNode *deleteNode = categoryNode;
+            categoryNode = categoryNode->next;
+
+            INFO_LOG("Remove %s category (%s feature option is disabled)\n", xmlNodeGetProp(deleteNode, ATTRI_NAME), featureOption);
+            xmlUnlinkNode(deleteNode);
+            xmlFreeNode(deleteNode);
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+EXPORT void appHandleReviseXmlDocByFeatureOptions(AppHandle *appHandle)
+{
+    // Travel all audioType's category & category group node
+    size_t i;
+    size_t numOfAppHandle = appHandleGetNumOfAudioType(appHandle);
+    for (i = 0; i < numOfAppHandle; i++)
+    {
+        xmlNode *categoryTypeListNode, *categoryTypeNode, *categoryGroupNode, *categoryNode, *prevCategoryGroupNode, *prevCategoryNode, *prevCategoryTypeNode;
+        AudioType *audioType = appHandleGetAudioTypeByIndex(appHandle, i);
+        categoryTypeListNode = audioTypeGetCategoryTypeListNode(audioType);
+        if (!categoryTypeListNode)
+        {
+            continue;
+        }
+
+        categoryTypeNode = categoryTypeListNode->children;
+        while ((categoryTypeNode = findXmlNodeByElemName(categoryTypeNode->next, ELEM_CATEGORY_TYPE)))
+        {
+            prevCategoryTypeNode = categoryTypeNode->prev;
+            if (removeNodeByFeatureOption(appHandle, categoryTypeNode))
+            {
+                categoryTypeNode = prevCategoryTypeNode;
+                continue;
+            }
+
+            /* Process CategoryType node */
+            categoryGroupNode = categoryTypeNode->children;
+            while ((categoryGroupNode = findXmlNodeByElemName(categoryGroupNode->next, ELEM_CATEGORY_GROUP)))
+            {
+                /* Process CategoryGroup of CategoryType Node */
+                prevCategoryGroupNode = categoryGroupNode->prev;
+                if (removeNodeByFeatureOption(appHandle, categoryGroupNode))
+                {
+                    categoryGroupNode = prevCategoryGroupNode;
+                    continue;
+                }
+
+                categoryNode = categoryGroupNode->children;
+                while ((categoryNode = findXmlNodeByElemName(categoryNode->next, ELEM_CATEGORY)))
+                {
+                    /* Process Category of CategoryGroup Node */
+                    prevCategoryNode = categoryNode->prev;
+                    if (removeNodeByFeatureOption(appHandle, categoryNode))
+                    {
+                        categoryNode = prevCategoryNode;
+                    }
+                }
+            }
+
+            categoryNode = categoryTypeNode->children;
+            while ((categoryNode = findXmlNodeByElemName(categoryNode->next, ELEM_CATEGORY)))
+            {
+                prevCategoryNode = categoryNode->prev;
+                if (removeNodeByFeatureOption(appHandle, categoryNode))
+                {
+                    categoryNode = prevCategoryNode;
+                }
+            }
+        }
+    }
+}
+
+EXPORT APP_STATUS appHandleCompressFiles(const char* srcDir, const char* destFile)
+{
+#ifdef WIN32
+    INFO_LOG("%s(), src = %s, dest = %s\n", __FUNCTION__, srcDir, destFile);
+    if (!srcDir || !destFile)
+    {
+        ERR_LOG("%s(), srcDir or destFile is NULL\n", __FUNCTION__);
+        return APP_ERROR;
+    } else {
+        UT_string *path = NULL;
+        utstring_new(path);
+        utstring_printf(path, "a -tzip %s %s\\*", destFile, srcDir);
+        utilShellExecute("7za.exe", utstring_body(path));
+        utstring_free(path);
+    }
+#else
+    ERR_LOG("Not support on linux\n");
+#endif
+    return APP_NO_ERROR;
+}
+
+EXPORT APP_STATUS appHandleUncompressFile(const char* srcFile, const char* destDir)
+{
+#ifdef WIN32
+    INFO_LOG("%s(), src = %s, dest = %s\n", __FUNCTION__, srcFile, destDir);
+    if (!srcFile || !destDir)
+    {
+        ERR_LOG("%s(), srcFile or destDir is NULL\n", __FUNCTION__);
+        return APP_ERROR;
+    } else {
+        UT_string *path = NULL;
+        utstring_new(path);
+        utstring_printf(path, "x %s -y -o%s\\", srcFile, destDir);
+        utilShellExecute("7za.exe", utstring_body(path));
+        utstring_free(path);
+    }
+#else
+    ERR_LOG("Not support on linux\n");
+#endif
+    return APP_NO_ERROR;
+}
+
+EXPORT APP_STATUS appHandleGetAudioTypeSupportedVerInfo(const char* audioTypeName, int* paramUnitDescVerMaj, int* paramUnitDescVerMin, int* audioParamVerMaj, int* audioParamVerMin)
+{
+    int i = 0;
+    while(audioTypeSupportVerInfo[i].audioTypeName != NULL)
+    {
+        if (!strcmp(audioTypeName, audioTypeSupportVerInfo[i].audioTypeName))
+        {
+            *paramUnitDescVerMaj = audioTypeSupportVerInfo[i].paramUnitDescVerMaj;
+            *paramUnitDescVerMin = audioTypeSupportVerInfo[i].paramUnitDescVerMin;
+            *audioParamVerMaj = audioTypeSupportVerInfo[i].audioParamVerMaj;
+            *audioParamVerMin = audioTypeSupportVerInfo[i].audioParamVerMin;
+            return APP_NO_ERROR;
+        }
+        i++;
+    }
+
+    ERR_LOG("%s AudioType version support info not found!\n", audioTypeName);
+    return APP_ERROR;
+}
+
+EXPORT void appHandleShowAudioTypeSupportedVerInfo(AppHandle* appHandle)
+{
+    int i = 0;
+    INFO_LOG("=======================\n");
+    while(audioTypeSupportVerInfo[i].audioTypeName != NULL)
+    {
+        INFO_LOG("[%d] %s, ParamUnitDesc ver(%d.%d), AudioParam ver(%d.%d)\n",
+            i,
+            audioTypeSupportVerInfo[i].audioTypeName, audioTypeSupportVerInfo[i].paramUnitDescVerMaj,
+            audioTypeSupportVerInfo[i].paramUnitDescVerMin, audioTypeSupportVerInfo[i].audioParamVerMaj, audioTypeSupportVerInfo[i].audioParamVerMin);
+        i++;
+    }
+}