Read wakeup word information from xml file
authorJi-hoon Lee <dalton.lee@samsung.com>
Tue, 23 Oct 2018 02:21:14 +0000 (11:21 +0900)
committerJi-hoon Lee <dalton.lee@samsung.com>
Tue, 23 Oct 2018 07:13:37 +0000 (16:13 +0900)
Change-Id: I0e90669cf153879ac14977c27050cdd0d95af526

CMakeLists.txt
inc/multi_assistant_config.h [new file with mode: 0644]
packaging/org.tizen.multi-assistant-service.spec
src/multi_assistant_config.c [new file with mode: 0644]
src/multi_assistant_service.c

index df62d67..7c75002 100644 (file)
@@ -26,6 +26,7 @@ pkg_check_modules(pkgs REQUIRED
        dbus-1
        glib-2.0
        message-port
+       libxml-2.0
 )
 
 
@@ -61,7 +62,9 @@ LINK_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib)
 INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src)
 INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/inc)
 
-SET(SRCS src/multi_assistant_service.c
+SET(SRCS
+               src/multi_assistant_config.c
+               src/multi_assistant_service.c
                src/multi_assistant_service_plugin.c
                src/multi_assistant_dbus.c
                src/multi_assistant_dbus_server.c
diff --git a/inc/multi_assistant_config.h b/inc/multi_assistant_config.h
new file mode 100644 (file)
index 0000000..7d42902
--- /dev/null
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2011-2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 __MULTI_ASSISTANT_CONFIG_H__
+#define __MULTI_ASSISTANT_CONFIG_H__
+
+#include <tzplatform_config.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define MAX_WAKEUP_LIST_NUM 32
+#define MAX_SUPPORTED_LANGUAGE_NUM 128
+
+/**************************************************************************************
+ *** Definitions for xml file
+ *************************************************************************************/
+#define MA_TAG_ASSISTANT_BASE                          "multi-assistant"
+#define MA_TAG_ASSISTANT_NAME                          "name"
+#define MA_TAG_ASSISTANT_ID                                    "id"
+#define MA_TAG_ASSISTANT_ICON_PATH                     "icon-path"
+#define MA_TAG_ASSISTANT_LANGUAGE_SET          "languages"
+#define MA_TAG_ASSISTANT_LANGUAGE                      "lang"
+#define MA_TAG_ASSISTANT_WAKEUP_WORD_SET       "wakeup-words"
+#define MA_TAG_ASSISTANT_WAKEUP_WORD           "wakeup-word"
+
+/**************************************************************************************
+ *** Definitions for ETC
+ *************************************************************************************/
+#define MA_RETRY_COUNT 5
+
+#define MA_ASSISTANT_INFO              tzplatform_mkpath(TZ_USER_HOME, "share/.multiassistant/ma/1.0/assistant-info")
+
+typedef struct {
+       const char* app_id;
+       const char* name;
+       const char* icon_path;
+       const char* wakeup_list[MAX_WAKEUP_LIST_NUM];
+       int cnt_wakeup;
+       const char* supported_lang[MAX_SUPPORTED_LANGUAGE_NUM];
+       int cnt_lang;
+} ma_assistant_info_s;
+
+typedef int (*mas_config_assistant_info_cb)(const char* appid, const char* name, const char* icon_path, const char* wakeup_list[], int cnt_wakeup, const char* supported_lang[], int cnt_lang, void* user_data);
+int mas_config_get_assistant_info(mas_config_assistant_info_cb callback, void* user_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* __MULTI_ASSISTANT_CONFIG_H__ */
\ No newline at end of file
index cf338bd..d359957 100644 (file)
@@ -21,6 +21,7 @@ BuildRequires: pkgconfig(ecore)
 BuildRequires: pkgconfig(dbus-1)
 BuildRequires: pkgconfig(libtzplatform-config)
 BuildRequires: pkgconfig(message-port)
+BuildRequires: pkgconfig(libxml-2.0)
 
 BuildRequires: boost-system
 BuildRequires: boost-thread
diff --git a/src/multi_assistant_config.c b/src/multi_assistant_config.c
new file mode 100644 (file)
index 0000000..fa47d3f
--- /dev/null
@@ -0,0 +1,193 @@
+/**
+ * Copyright (c) 2011-2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 <libxml/parser.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include "multi_assistant_config.h"
+#include "multi_assistant_main.h"
+
+int mas_config_parse_assistant_info(mas_config_assistant_info_cb callback, const char *path, void* user_data)
+{
+       xmlDocPtr doc = NULL;
+       xmlNodePtr cur = NULL;
+       xmlChar *key;
+
+       int loop;
+       int retry_count = 0;
+
+       while (NULL == doc) {
+               doc = xmlParseFile(path);
+               if (NULL != doc) {
+                       break;
+               }
+
+               if (MA_RETRY_COUNT == retry_count++) {
+                       MAS_LOGE("[ERROR] Fail to parse file error : %s", path);
+                       xmlCleanupParser();
+                       return -1;
+               }
+               usleep(10000);
+       }
+
+       cur = xmlDocGetRootElement(doc);
+       if (cur == NULL) {
+               MAS_LOGE("[ERROR] Empty document");
+               xmlFreeDoc(doc);
+               return -1;
+       }
+
+       if (xmlStrcmp(cur->name, (const xmlChar *) MA_TAG_ASSISTANT_BASE)) {
+               MAS_LOGE("[ERROR] The wrong type, root node is NOT %s", MA_TAG_ASSISTANT_BASE);
+               xmlFreeDoc(doc);
+               return -1;
+       }
+
+       cur = cur->xmlChildrenNode;
+       if (cur == NULL) {
+               MAS_LOGE("[ERROR] Empty document");
+               xmlFreeDoc(doc);
+               return -1;
+       }
+
+       /* alloc assistant info */
+       ma_assistant_info_s* temp;
+       temp = (ma_assistant_info_s*)calloc(1, sizeof(ma_assistant_info_s));
+       if (NULL == temp) {
+               MAS_LOGE("[ERROR] Fail to allocate memory");
+               xmlFreeDoc(doc);
+               return -1;
+       }
+
+       temp->app_id = NULL;
+       temp->name = NULL;
+       temp->icon_path = NULL;
+       memset(temp->wakeup_list, 0x00, sizeof(temp->wakeup_list));
+       temp->cnt_wakeup = 0;
+       memset(temp->supported_lang, 0x00, sizeof(temp->supported_lang));
+       temp->cnt_lang = 0;
+
+       while (cur != NULL) {
+               if (cur->name && 0 == xmlStrcmp(cur->name, (const xmlChar *)MA_TAG_ASSISTANT_LANGUAGE_SET)) {
+                       xmlNodePtr child_node = cur->xmlChildrenNode;
+                       while (child_node != NULL) {
+                               if (child_node->name && 0 == xmlStrcmp(child_node->name, (const xmlChar*)MA_TAG_ASSISTANT_LANGUAGE)) {
+                                       key = xmlNodeGetContent(child_node);
+                                       if (key) {
+                                               temp->supported_lang[temp->cnt_lang++] = strdup((const char*)key);
+                                               MAS_LOGD("Language : %s", key);
+                                               xmlFree(key);
+                                       }
+                               }
+
+                               child_node = child_node->next;
+                       }
+               } else if (cur->name && 0 == xmlStrcmp(cur->name, (const xmlChar *)MA_TAG_ASSISTANT_WAKEUP_WORD_SET)) {
+                       xmlNodePtr child_node = cur->xmlChildrenNode;
+                       while (child_node != NULL) {
+                               if (child_node->name && 0 == xmlStrcmp(child_node->name, (const xmlChar*)MA_TAG_ASSISTANT_WAKEUP_WORD)) {
+                                       key = xmlNodeGetContent(child_node);
+                                       if (key) {
+                                               temp->wakeup_list[temp->cnt_wakeup++] = strdup((const char*)key);
+                                               MAS_LOGD("Wakeup Word : %s", key);
+                                               xmlFree(key);
+                                       }
+                               }
+
+                               child_node = child_node->next;
+                       }
+               } else if (cur->name && 0 == xmlStrcmp(cur->name, (const xmlChar*)MA_TAG_ASSISTANT_NAME)) {
+                       key = xmlNodeGetContent(cur);
+                       if (key) {
+                               temp->name = strdup((const char*)key);
+                               MAS_LOGD("Name : %s", key);
+                               xmlFree(key);
+                       }
+               } else if (cur->name && 0 == xmlStrcmp(cur->name, (const xmlChar*)MA_TAG_ASSISTANT_ID)) {
+                       key = xmlNodeGetContent(cur);
+                       if (key) {
+                               temp->app_id = strdup((const char*)key);
+                               MAS_LOGD("ID : %s", key);
+                               xmlFree(key);
+                       }
+               } else if (cur->name && 0 == xmlStrcmp(cur->name, (const xmlChar*)MA_TAG_ASSISTANT_ICON_PATH)) {
+                       key = xmlNodeGetContent(cur);
+                       if (key) {
+                               temp->icon_path = strdup((const char*)key);
+                               MAS_LOGD("Icon Path : %s", key);
+                               xmlFree(key);
+                       }
+               }
+
+               cur = cur->next;
+       }
+
+       if (callback) {
+               callback(temp->app_id, temp->name, temp->icon_path,
+                       temp->wakeup_list, temp->cnt_wakeup, temp->supported_lang, temp->cnt_lang, user_data);
+       }
+
+       if (temp->app_id) {
+               free((void*)temp->app_id);
+       }
+       if (temp->name) {
+               free((void*)temp->name);
+       }
+       if (temp->icon_path) {
+               free((void*)temp->icon_path);
+       }
+       for (loop = 0; loop < temp->cnt_wakeup; loop++) {
+               if (temp->wakeup_list[loop]) {
+                       free((void*)(temp->wakeup_list[loop]));
+               }
+       }
+       for (loop = 0; loop < temp->cnt_lang; loop++) {
+               if (temp->supported_lang[loop]) {
+                       free((void*)(temp->supported_lang[loop]));
+               }
+       }
+
+       free(temp);
+       xmlFreeDoc(doc);
+
+       return 0;
+}
+
+int mas_config_get_assistant_info(mas_config_assistant_info_cb callback, void* user_data)
+{
+       const char *suffix = ".xml";
+
+       DIR *d;
+       struct dirent *dir;
+       d = opendir(MA_ASSISTANT_INFO);
+
+       if (d) {
+               while (NULL != (dir = readdir(d))) {
+                       if (suffix && dir->d_name && strlen(suffix) <= strlen(dir->d_name)) {
+                               if (0 == strcmp(dir->d_name + strlen(dir->d_name) - strlen(suffix), suffix)) {
+                                       char fullpath[_POSIX_PATH_MAX];
+                                       snprintf(fullpath, _POSIX_PATH_MAX - 1, "%s/%s", MA_ASSISTANT_INFO, dir->d_name);
+                                       MAS_LOGD("Parsing file : %s\n", fullpath);
+                                       mas_config_parse_assistant_info(callback, fullpath, user_data);
+                               }
+                       }
+               }
+               closedir(d);
+       }
+
+       return 0;
+}
\ No newline at end of file
index 339b8ed..16011cc 100644 (file)
 #include "multi_assistant_service.h"
 #include "multi_assistant_service_plugin.h"
 #include "multi_assistant_dbus.h"
+#include "multi_assistant_config.h"
 
 static const char *g_current_lang = "en_US";
 static int g_local_port_id = -1;
 
 #define MAX_MACLIENT_INFO_NUM 16
+#define MAX_WAKEUP_WORDS_NUM 4
 #define MAX_WAKEUP_WORD_LEN 255
 typedef struct {
        Eina_Bool used;
-       const char wakeup_word[MAX_WAKEUP_WORD_LEN];
-       const char appid[MAX_APPID_LEN];
+       char appid[MAX_APPID_LEN];
+       char wakeup_word[MAX_WAKEUP_WORDS_NUM][MAX_WAKEUP_WORD_LEN];
 } ma_client_info;
 
-static ma_client_info g_maclient_info[MAX_MACLIENT_INFO_NUM] = {
-       {EINA_TRUE,     {"hi bixby"},           {"com.samsung.bixby-voice"}},
-       {EINA_TRUE,     {"alexa"},                      {"com.samsung.alexa-voice"}},
-       {EINA_TRUE,     {"hi google"},          {"com.samsung.google-voice"}},
-};
+static ma_client_info g_maclient_info[MAX_MACLIENT_INFO_NUM];
 
 typedef struct {
        int pid;
@@ -363,7 +361,7 @@ int mas_ui_client_change_assistant(const char* appid)
                for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
                        if (g_maclient_info[loop].used &&
                                strlen(g_maclient_info[loop].appid) > 0 &&
-                               strlen(g_maclient_info[loop].wakeup_word) > 0) {
+                               strlen(g_maclient_info[loop].wakeup_word[0]) > 0) {
                                if (strncmp(appid, g_maclient_info[loop].appid, MAX_APPID_LEN) == 0) {
                                        mas_launch_client_by_appid(g_maclient_info[loop].appid);
                                }
@@ -407,6 +405,49 @@ static void deinit_message_port(void)
        message_port_unregister_local_port(g_local_port_id);
 }
 
+int __mas_assistant_info_cb(const char* appid, const char* name,
+               const char* icon_path, const char* wakeup_list[], int cnt_wakeup,
+               const char* supported_lang[], int cnt_lang, void* user_data) {
+       MAS_LOGD("__mas_assistant_info_cb called");
+
+       if (NULL == appid) {
+               MAS_LOGD("app_id NULL, returning");
+               return -1;
+       }
+
+       int index = -1;
+       int loop = 0;
+       while(-1 == index && loop < MAX_MACLIENT_INFO_NUM) {
+               if (EINA_FALSE == g_maclient_info[loop].used) {
+                       index = loop;
+               }
+               loop++;
+       }
+       if (-1 != index) {
+               g_maclient_info[index].used = EINA_TRUE;
+               MAS_LOGD("app_id(%s)", appid);
+               strncpy(g_maclient_info[index].appid, appid, MAX_APPID_LEN);
+               g_maclient_info[index].appid[MAX_APPID_LEN - 1] = '\0';
+
+               for (loop = 0;loop < MAX_WAKEUP_WORDS_NUM;loop++) {
+                       if (loop < cnt_wakeup && wakeup_list[loop]) {
+                               MAS_LOGD("wakeup_list(%d)(%s)", loop, wakeup_list[loop]);
+                               strncpy(g_maclient_info[index].wakeup_word[loop], wakeup_list[loop], MAX_WAKEUP_WORD_LEN);
+                               g_maclient_info[index].wakeup_word[loop][MAX_WAKEUP_WORD_LEN - 1] = '\0';
+                       } else {
+                               strncpy(g_maclient_info[index].wakeup_word[loop], "", MAX_WAKEUP_WORD_LEN);
+                       }
+               }
+       } else {
+               MAS_LOGD("Couldn't find an empty slot for storing assistant info");
+       }
+
+       MAS_LOGD("__mas_assistant_info_cb end");
+
+       return 0;
+}
+
+
 static int init_wakeup(void)
 {
        MAS_LOGD("[Enter] init_wakeup ");
@@ -426,17 +467,24 @@ static int init_wakeup(void)
                return -1;
        }
 
-       for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
-               if (g_maclient_info[loop].used &&
-                       strlen(g_maclient_info[loop].appid) > 0 &&
-                       strlen(g_maclient_info[loop].wakeup_word) > 0) {
-                       MAS_LOGD("Registering wakeup word %s for app %s",
-                               g_maclient_info[loop].wakeup_word, g_maclient_info[loop].appid);
-                       if (0 != multi_assistant_service_plugin_set_wakeup_word("en_US", g_maclient_info[loop].wakeup_word)) {
-                               MAS_LOGE("Fail to stt wakeup word");
-                               return -1;
+       if (0 == mas_config_get_assistant_info(__mas_assistant_info_cb, NULL)) {
+               for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
+                       if (g_maclient_info[loop].used &&
+                               strlen(g_maclient_info[loop].appid) > 0) {
+                               for (int inner_loop = 0; inner_loop < MAX_WAKEUP_WORDS_NUM; inner_loop++) {
+                                       if (strlen(g_maclient_info[loop].wakeup_word[inner_loop]) > 0) {
+                                               MAS_LOGD("Registering wakeup word %s for app %s",
+                                                       g_maclient_info[loop].wakeup_word[inner_loop], g_maclient_info[loop].appid);
+                                               if (0 != multi_assistant_service_plugin_set_wakeup_word(
+                                                       "en_US", g_maclient_info[loop].wakeup_word[inner_loop])) {
+                                                       MAS_LOGE("Fail to stt wakeup word");
+                                               }
+                                       }
+                               }
                        }
                }
+       } else {
+               MAS_LOGE("Fail to load assistant info");
        }
 
        if (0 != multi_assistant_service_plugin_set_callbacks()) {
@@ -484,10 +532,13 @@ static const char* __get_client_appid_by_wakeup_word(const char *wakeup_word)
 
        for (loop = 0; loop < MAX_MACLIENT_INFO_NUM && appid == NULL; loop++) {
                if (g_maclient_info[loop].used &&
-                       strlen(g_maclient_info[loop].appid) > 0 &&
-                       strlen(g_maclient_info[loop].wakeup_word) > 0) {
-                       if (0 == strncmp(wakeup_word, g_maclient_info[loop].wakeup_word, MAX_WAKEUP_WORD_LEN)) {
-                               appid = g_maclient_info[loop].appid;
+                       strlen(g_maclient_info[loop].appid) > 0) {
+                       for (int inner_loop = 0; inner_loop < MAX_WAKEUP_WORDS_NUM; inner_loop++) {
+                               if (strlen(g_maclient_info[loop].wakeup_word[inner_loop]) > 0) {
+                                       if (0 == strncmp(wakeup_word, g_maclient_info[loop].wakeup_word[inner_loop], MAX_WAKEUP_WORD_LEN)) {
+                                               appid = g_maclient_info[loop].appid;
+                                       }
+                               }
                        }
                }
        }
@@ -496,18 +547,21 @@ static const char* __get_client_appid_by_wakeup_word(const char *wakeup_word)
        if (NULL == appid) {
                for (loop = 0; loop < MAX_MACLIENT_INFO_NUM && appid == NULL; loop++) {
                        if (g_maclient_info[loop].used &&
-                               strlen(g_maclient_info[loop].appid) > 0 &&
-                               strlen(g_maclient_info[loop].wakeup_word) > 0) {
-                               char comparand[MAX_WAKEUP_WORD_LEN];
-                               int comparand_index = 0;
-                               for (int index = 0; index < MAX_WAKEUP_WORD_LEN; index++) {
-                                       if (' ' != g_maclient_info[loop].wakeup_word[index]) {
-                                               comparand[comparand_index++] = g_maclient_info[loop].wakeup_word[index];
+                               strlen(g_maclient_info[loop].appid) > 0) {
+                               for (int inner_loop = 0; inner_loop < MAX_WAKEUP_WORDS_NUM; inner_loop++) {
+                                       if (strlen(g_maclient_info[loop].wakeup_word[inner_loop]) > 0) {
+                                               char comparand[MAX_WAKEUP_WORD_LEN];
+                                               int comparand_index = 0;
+                                               for (int index = 0; index < MAX_WAKEUP_WORD_LEN; index++) {
+                                                       if (' ' != g_maclient_info[loop].wakeup_word[inner_loop][index]) {
+                                                               comparand[comparand_index++] = g_maclient_info[loop].wakeup_word[inner_loop][index];
+                                                       }
+                                               }
+                                               if (0 == strncmp(wakeup_word, comparand, MAX_WAKEUP_WORD_LEN)) {
+                                                       appid = g_maclient_info[loop].appid;
+                                               }
                                        }
                                }
-                               if (0 == strncmp(wakeup_word, comparand, MAX_WAKEUP_WORD_LEN)) {
-                                       appid = g_maclient_info[loop].appid;
-                               }
                        }
                }
        }
@@ -555,10 +609,13 @@ int mas_set_current_client_by_wakeup_word(const char *wakeup_word)
 
        for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
                if (g_maclient_info[loop].used &&
-                       strlen(g_maclient_info[loop].appid) > 0 &&
-                       strlen(g_maclient_info[loop].wakeup_word) > 0) {
-                       if (0 == strncmp(wakeup_word, g_maclient_info[loop].wakeup_word, MAX_WAKEUP_WORD_LEN)) {
-                               g_current_maclient_info = loop;
+                       strlen(g_maclient_info[loop].appid) > 0) {
+                       for (int inner_loop = 0; inner_loop < MAX_WAKEUP_WORDS_NUM; inner_loop++) {
+                               if (strlen(g_maclient_info[loop].wakeup_word[inner_loop]) > 0) {
+                                       if (0 == strncmp(wakeup_word, g_maclient_info[loop].wakeup_word[inner_loop], MAX_WAKEUP_WORD_LEN)) {
+                                               g_current_maclient_info = loop;
+                                       }
+                               }
                        }
                }
        }
@@ -572,7 +629,7 @@ int mas_set_current_client_by_appid(const char *appid)
        for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
                if (g_maclient_info[loop].used &&
                        strlen(g_maclient_info[loop].appid) > 0 &&
-                       strlen(g_maclient_info[loop].wakeup_word) > 0) {
+                       strlen(g_maclient_info[loop].wakeup_word[0]) > 0) {
                        if (strncmp(appid, g_maclient_info[loop].appid, MAX_APPID_LEN) == 0) {
                                g_current_maclient_info = loop;
                        }