c3288f3cb52ac0be1d4cbcb29f1f5c50ea3e8a76
[platform/core/uifw/multi-assistant-service.git] / src / service_config.cpp
1 /*
2  * Copyright 2020 Samsung Electronics Co., Ltd
3  *
4  * Licensed under the Flora License, Version 1.1 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://floralicense.org/license/
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <libxml/parser.h>
18 #include <sys/types.h>
19 #include <dirent.h>
20 #include <unistd.h>
21
22 #include <app_preference.h>
23
24 #include "service_config.h"
25 #include "service_common.h"
26
27 int CServiceConfig::parse_assistant_info(service_config_assistant_info_cb callback,
28         const char *path, void* user_data)
29 {
30         xmlDocPtr doc = NULL;
31         xmlNodePtr cur = NULL;
32         xmlChar *key;
33
34         int loop;
35         int retry_count = 0;
36
37         while (NULL == doc) {
38                 doc = xmlParseFile(path);
39                 if (NULL != doc) {
40                         break;
41                 }
42
43                 if (MA_RETRY_COUNT == retry_count++) {
44                         MAS_LOGE("[ERROR] Fail to parse file error : %s", path);
45                         xmlCleanupParser();
46                         return -1;
47                 }
48                 usleep(10000);
49         }
50
51         cur = xmlDocGetRootElement(doc);
52         if (cur == NULL) {
53                 MAS_LOGE("[ERROR] Empty document");
54                 xmlFreeDoc(doc);
55                 return -1;
56         }
57
58         if (xmlStrcmp(cur->name, (const xmlChar *) MA_TAG_ASSISTANT_BASE)) {
59                 MAS_LOGE("[ERROR] The wrong type, root node is NOT %s", MA_TAG_ASSISTANT_BASE);
60                 xmlFreeDoc(doc);
61                 return -1;
62         }
63
64         cur = cur->xmlChildrenNode;
65         if (cur == NULL) {
66                 MAS_LOGE("[ERROR] Empty document");
67                 xmlFreeDoc(doc);
68                 return -1;
69         }
70
71         /* alloc assistant info */
72         ma_assistant_info_s temp;
73
74         while (cur != NULL) {
75                 if (cur->name && 0 == xmlStrcmp(cur->name, (const xmlChar *)MA_TAG_ASSISTANT_LANGUAGE_SET)) {
76                         xmlNodePtr child_node = cur->xmlChildrenNode;
77                         while (child_node != NULL) {
78                                 if (child_node->name && 0 == xmlStrcmp(child_node->name, (const xmlChar*)MA_TAG_ASSISTANT_LANGUAGE)) {
79                                         key = xmlNodeGetContent(child_node);
80                                         if (key) {
81                                                 temp.supported_lang[temp.cnt_lang++] = strdup((const char*)key);
82                                                 MAS_LOGD("Language : %s", key);
83                                                 xmlFree(key);
84                                         }
85                                 }
86
87                                 child_node = child_node->next;
88                         }
89                 } else if (cur->name && 0 == xmlStrcmp(cur->name, (const xmlChar *)MA_TAG_ASSISTANT_WAKEUP_WORD_SET)) {
90                         xmlNodePtr child_node = cur->xmlChildrenNode;
91                         while (child_node != NULL) {
92                                 if (child_node->name && 0 == xmlStrcmp(child_node->name, (const xmlChar*)MA_TAG_ASSISTANT_WAKEUP_WORD)) {
93                                         key = xmlNodeGetContent(child_node);
94                                         if (key) {
95                                                 temp.wakeup_list[temp.cnt_wakeup] = strdup((const char*)key);
96                                                 MAS_LOGD("Wakeup Word : %s", key);
97                                                 xmlFree(key);
98                                         }
99                                         xmlChar* prop = xmlNodeGetLang(child_node);
100                                         if (prop) {
101                                                 temp.wakeup_language[temp.cnt_wakeup] = strdup((const char*)prop);
102                                                 MAS_LOGD("Wakeup Language for %s : %s", temp.wakeup_list[temp.cnt_wakeup], prop);
103                                                 xmlFree(prop);
104                                         }
105                                         temp.cnt_wakeup++;
106                                 }
107
108                                 child_node = child_node->next;
109                         }
110                 } else if (cur->name && 0 == xmlStrcmp(cur->name, (const xmlChar*)MA_TAG_ASSISTANT_NAME)) {
111                         key = xmlNodeGetContent(cur);
112                         if (key) {
113                                 temp.name = strdup((const char*)key);
114                                 MAS_LOGD("Name : %s", key);
115                                 xmlFree(key);
116                         }
117                 } else if (cur->name && 0 == xmlStrcmp(cur->name, (const xmlChar*)MA_TAG_ASSISTANT_APPID)) {
118                         key = xmlNodeGetContent(cur);
119                         if (key) {
120                                 temp.app_id = strdup((const char*)key);
121                                 MAS_LOGD("ID : %s", key);
122                                 xmlFree(key);
123                         }
124                 } else if (cur->name && 0 == xmlStrcmp(cur->name, (const xmlChar*)MA_TAG_ASSISTANT_ICON_PATH)) {
125                         key = xmlNodeGetContent(cur);
126                         if (key) {
127                                 temp.icon_path = strdup((const char*)key);
128                                 MAS_LOGD("Icon Path : %s", key);
129                                 xmlFree(key);
130                         }
131                 } else if (cur->name && 0 == xmlStrcmp(cur->name, (const xmlChar*)MA_TAG_ASSISTANT_WAKEUP_ENGINE_APPID)) {
132                         key = xmlNodeGetContent(cur);
133                         if (key) {
134                                 temp.wakeup_engine = strdup((const char*)key);
135                                 MAS_LOGD("Wakeup Engine : %s", key);
136                                 xmlFree(key);
137                         }
138                 } else if (cur->name && 0 == xmlStrcmp(cur->name, (const xmlChar*)MA_TAG_ASSISTANT_CUSTOM_UI)) {
139                         key = xmlNodeGetContent(cur);
140                         if (key) {
141                                 if (0 == xmlStrcasecmp(key, reinterpret_cast<const xmlChar*>("true"))) {
142                                         temp.custom_ui_option = true;
143                                 }
144                                 MAS_LOGD("Use custom UI : %d", temp.custom_ui_option);
145                                 xmlFree(key);
146                         }
147                 } else if (cur->name && 0 == xmlStrcmp(cur->name, (const xmlChar*)MA_TAG_ASSISTANT_VOICE_KEY_SUPPORT_MODE)) {
148                         key = xmlNodeGetContent(cur);
149                         if (key) {
150                                 if (xmlStrcmp(cur->name, (const xmlChar*)VOICE_KEY_SUPPORT_MODE_STRING_ALL)) {
151                                         temp.voice_key_support_mode = VOICE_KEY_SUPPORT_MODE_ALL;
152                                 } else if (xmlStrcmp(cur->name, (const xmlChar*)VOICE_KEY_SUPPORT_MODE_STRING_TAP_TO_TALK)) {
153                                         temp.voice_key_support_mode = VOICE_KEY_SUPPORT_MODE_TAP_TO_TALK;
154                                 } else if (xmlStrcmp(cur->name, (const xmlChar*)VOICE_KEY_SUPPORT_MODE_STRING_PUSH_TO_TALK)) {
155                                         temp.voice_key_support_mode = VOICE_KEY_SUPPORT_MODE_PUSH_TO_TALK;
156                                 } else {
157                                         temp.voice_key_support_mode = VOICE_KEY_SUPPORT_MODE_NONE;
158                                 }
159                                 MAS_LOGD("Voice key support mode : %s", cur->name);
160                                 xmlFree(key);
161                         }
162                 } else if (cur->name && 0 == xmlStrcmp(cur->name, (const xmlChar*)MA_TAG_ASSISTANT_VOICE_KEY_TAP_DURATION)) {
163                         key = xmlNodeGetContent(cur);
164                         if (key) {
165                                 temp.voice_key_tap_duration = atof((const char*)key);
166                                 MAS_LOGD("Voice key tap duration : %s", key);
167                                 xmlFree(key);
168                         }
169                 }
170
171                 cur = cur->next;
172         }
173
174         if (callback) {
175                 callback(&temp, user_data);
176         }
177
178         if (temp.app_id) {
179                 free((void*)temp.app_id);
180         }
181         if (temp.name) {
182                 free((void*)temp.name);
183         }
184         if (temp.icon_path) {
185                 free((void*)temp.icon_path);
186         }
187         for (loop = 0; loop < temp.cnt_wakeup; loop++) {
188                 if (temp.wakeup_list[loop]) {
189                         free((void*)(temp.wakeup_list[loop]));
190                 }
191         }
192         for (loop = 0; loop < temp.cnt_lang; loop++) {
193                 if (temp.supported_lang[loop]) {
194                         free((void*)(temp.supported_lang[loop]));
195                 }
196         }
197         if (temp.wakeup_engine) {
198                 free((void*)temp.wakeup_engine);
199         }
200
201         xmlFreeDoc(doc);
202
203         return 0;
204 }
205
206 int CServiceConfig::get_assistant_info(service_config_assistant_info_cb callback, void* user_data)
207 {
208         const char *suffix = ".xml";
209
210         DIR *d;
211         struct dirent *dir;
212         d = opendir(MA_ASSISTANT_INFO);
213
214         if (d) {
215                 while (NULL != (dir = readdir(d))) {
216                         if (suffix && strlen(suffix) <= strlen(dir->d_name)) {
217                                 if (0 == strcmp(dir->d_name + strlen(dir->d_name) - strlen(suffix), suffix)) {
218                                         char fullpath[_POSIX_PATH_MAX];
219                                         snprintf(fullpath, _POSIX_PATH_MAX - 1, "%s/%s", MA_ASSISTANT_INFO, dir->d_name);
220                                         MAS_LOGD("Parsing file : %s\n", fullpath);
221                                         parse_assistant_info(callback, fullpath, user_data);
222                                 }
223                         }
224                 }
225                 closedir(d);
226         }
227
228         return 0;
229 }
230
231 int CServiceConfig::add_custom_wake_word(const char* wake_word, const char* language,
232         char wakeup_word_storage[MAX_WAKEUP_WORDS_NUM][MAX_WAKEUP_WORD_LEN],
233         char wakeup_language_storage[MAX_WAKEUP_WORDS_NUM][MAX_SUPPORTED_LANGUAGE_LEN])
234 {
235         if (nullptr == wake_word || nullptr == language) return -1;
236
237         bool found = false;
238         for (int loop = 0;false == found && loop < MAX_WAKEUP_WORDS_NUM;loop++) {
239                 if (0 == strncmp(wakeup_word_storage[loop], wake_word, MAX_WAKEUP_WORD_LEN) &&
240                         0 == strncmp(wakeup_language_storage[loop], language, MAX_SUPPORTED_LANGUAGE_LEN)) {
241                         LOGE("The wakeup word already exists!");
242                         return -1; /* Already exists */
243                 }
244                 if (0 == strlen(wakeup_word_storage[loop])) {
245                         strncpy(wakeup_word_storage[loop], wake_word, MAX_WAKEUP_WORD_LEN);
246                         wakeup_word_storage[loop][MAX_WAKEUP_WORD_LEN - 1] = '\0';
247                         strncpy(wakeup_language_storage[loop], language, MAX_SUPPORTED_LANGUAGE_LEN);
248                         wakeup_language_storage[loop][MAX_SUPPORTED_LANGUAGE_LEN - 1] = '\0';
249                         found = true;
250                 }
251         }
252         if (!found) {
253                 LOGE("No empty slot found while trying to add new wake word!");
254                 return -1;
255         }
256         return 0;
257 }
258
259 int CServiceConfig::remove_custom_wake_word(const char* wake_word, const char* language,
260         char wakeup_word_storage[MAX_WAKEUP_WORDS_NUM][MAX_WAKEUP_WORD_LEN],
261         char wakeup_language_storage[MAX_WAKEUP_WORDS_NUM][MAX_SUPPORTED_LANGUAGE_LEN])
262 {
263         if (nullptr == wake_word || nullptr == language) return -1;
264
265         bool found = false;
266         for (int loop = 0;loop < MAX_WAKEUP_WORDS_NUM;loop++) {
267                 if (0 == strncmp(wakeup_word_storage[loop], wake_word, MAX_WAKEUP_WORD_LEN) &&
268                         0 == strncmp(wakeup_language_storage[loop], language, MAX_SUPPORTED_LANGUAGE_LEN)) {
269                         for (int shift = loop;shift < MAX_WAKEUP_WORDS_NUM - 1;shift++) {
270                                 strncpy(wakeup_word_storage[shift],
271                                         wakeup_word_storage[shift + 1], MAX_WAKEUP_WORD_LEN);
272                                 wakeup_word_storage[shift][MAX_WAKEUP_WORD_LEN - 1] = '\0';
273                                 strncpy(wakeup_language_storage[shift],
274                                         wakeup_language_storage[shift + 1], MAX_SUPPORTED_LANGUAGE_LEN);
275                                 wakeup_language_storage[shift][MAX_SUPPORTED_LANGUAGE_LEN - 1] = '\0';
276                         }
277                         memset(wakeup_word_storage[MAX_WAKEUP_WORDS_NUM - 1],
278                                 0x00, sizeof(char) * MAX_WAKEUP_WORD_LEN);
279                         memset(wakeup_language_storage[MAX_WAKEUP_WORDS_NUM - 1],
280                                 0x00, sizeof(char) * MAX_SUPPORTED_LANGUAGE_LEN);
281
282                         loop--; /* Just in case there are duplicated items */
283                         found = true;
284                 }
285         }
286         if (!found) return -1;
287         return 0;
288 }
289
290 int CServiceConfig::load_custom_wake_words(const char* app_id,
291         char wakeup_word_storage[MAX_WAKEUP_WORDS_NUM][MAX_WAKEUP_WORD_LEN],
292         char wakeup_language_storage[MAX_WAKEUP_WORDS_NUM][MAX_SUPPORTED_LANGUAGE_LEN])
293 {
294         const char delimeter = '|';
295
296         /* Add 1 for additional pipe character */
297         char wakeup_words[MAX_WAKEUP_WORDS_NUM * (MAX_WAKEUP_WORD_LEN + 1)];
298         char wakeup_languages[MAX_WAKEUP_WORDS_NUM * (MAX_SUPPORTED_LANGUAGE_LEN + 1)];
299         memset(wakeup_words, 0x00, sizeof(wakeup_words));
300         memset(wakeup_languages, 0x00, sizeof(wakeup_languages));
301
302         bool existing = false;
303         preference_is_existing("custom_wakeup_words", &existing);
304         if (!existing) return -1;
305         preference_is_existing("custom_wakeup_languages", &existing);
306         if (!existing) return -1;
307
308         char* value = NULL;
309         preference_get_string("custom_wakeup_words", &value);
310         if (NULL == value) return -1;
311         strncpy(wakeup_words, value, sizeof(wakeup_words) - 1);
312         wakeup_words[sizeof(wakeup_words) - 1] = '\0';
313         LOGD("Custom wakeup words loaded : %s", wakeup_words);
314         free(value);
315
316         preference_get_string("custom_wakeup_languages", &value);
317         if (NULL == value) return -1;
318         strncpy(wakeup_languages, value, sizeof(wakeup_languages) - 1);
319         wakeup_languages[sizeof(wakeup_languages) - 1] = '\0';
320         LOGD("Custom wakeup languages loaded : %s", wakeup_languages);
321         free(value);
322
323         char *word_start = wakeup_words;
324         char *language_start = wakeup_languages;
325         char *word_end = NULL;
326         char *language_end = NULL;
327         int index = 0;
328         while (index < MAX_WAKEUP_WORDS_NUM) {
329                 word_end = strchrnul(word_start, delimeter);
330                 language_end = strchrnul(language_start, delimeter);
331                 if ('\0' == *word_end || '\0' == *language_end) break;
332                 *word_end = '\0';
333                 *language_end = '\0';
334                 if (0 == strlen(word_start)) break;
335                 strncpy(wakeup_word_storage[index], word_start, MAX_WAKEUP_WORD_LEN - 1);
336                 strncpy(wakeup_language_storage[index], language_start, MAX_SUPPORTED_LANGUAGE_LEN - 1);
337                 word_start = word_end + 1;
338                 language_start = language_end + 1;
339                 LOGD("Added custom wakeup word : (%s) (%s)",
340                         wakeup_word_storage[index], wakeup_language_storage[index]);
341                 index++;
342         }
343
344         return 0;
345 }
346
347 int CServiceConfig::save_custom_wake_words(const char* app_id,
348         char wakeup_word_storage[MAX_WAKEUP_WORDS_NUM][MAX_WAKEUP_WORD_LEN],
349         char wakeup_language_storage[MAX_WAKEUP_WORDS_NUM][MAX_SUPPORTED_LANGUAGE_LEN])
350 {
351         const char* delimeter = "|";
352         /* Add 1 for additional pipe character */
353         char wakeup_words[MAX_WAKEUP_WORDS_NUM * (MAX_WAKEUP_WORD_LEN + 1)];
354         char wakeup_languages[MAX_WAKEUP_WORDS_NUM * (MAX_SUPPORTED_LANGUAGE_LEN + 1)];
355         memset(wakeup_words, 0x00, sizeof(wakeup_words));
356         memset(wakeup_languages, 0x00, sizeof(wakeup_languages));
357         for (int loop = 0;loop < MAX_WAKEUP_WORDS_NUM;loop++) {
358                 if (strlen(wakeup_word_storage[loop]) > 0) {
359                         int wakeup_words_len = strlen(wakeup_words);
360                         strncat(wakeup_words, wakeup_word_storage[loop],
361                                 sizeof(wakeup_words) - wakeup_words_len - 1);
362                         strncat(wakeup_words, delimeter, strlen(delimeter));
363                         int wakeup_languages_len = strlen(wakeup_languages);
364                         strncat(wakeup_languages, wakeup_language_storage[loop],
365                                 sizeof(wakeup_languages) - wakeup_languages_len - 1);
366                         strncat(wakeup_languages, delimeter, strlen(delimeter));
367                 }
368         }
369         wakeup_words[sizeof(wakeup_words) - 1] = '\0';
370         wakeup_languages[sizeof(wakeup_languages) - 1] = '\0';
371         preference_set_string("custom_wakeup_words", wakeup_words);
372         preference_set_string("custom_wakeup_languages", wakeup_languages);
373         return 0;
374 }
375
376 int CServiceConfig::get_custom_wake_word_num(
377         char wakeup_word_storage[MAX_WAKEUP_WORDS_NUM][MAX_WAKEUP_WORD_LEN],
378         char wakeup_language_storage[MAX_WAKEUP_WORDS_NUM][MAX_SUPPORTED_LANGUAGE_LEN])
379 {
380         int num = 0;
381         for (int loop = 0;loop < MAX_WAKEUP_WORDS_NUM;loop++) {
382                 if (0 < strlen(wakeup_word_storage[loop])) {
383                         num++;
384                 }
385         }
386         return num;
387 }
388
389 bool CServiceConfig::has_custom_wake_word(const char* wake_word, const char* language,
390         char wakeup_word_storage[MAX_WAKEUP_WORDS_NUM][MAX_WAKEUP_WORD_LEN],
391         char wakeup_language_storage[MAX_WAKEUP_WORDS_NUM][MAX_SUPPORTED_LANGUAGE_LEN])
392 {
393         if (nullptr == wake_word || nullptr == language) return false;
394
395         for (int loop = 0;loop < MAX_WAKEUP_WORDS_NUM;loop++) {
396                 if (0 == strncmp(wakeup_word_storage[loop], wake_word, MAX_WAKEUP_WORD_LEN) &&
397                         0 == strncmp(wakeup_language_storage[loop], language, MAX_SUPPORTED_LANGUAGE_LEN)) {
398                         return true;
399                 }
400         }
401         return false;
402 }