b458553e7f4636d93e8ca62c2fcca79558fe07a4
[platform/core/uifw/multi-assistant-service.git] / plugins / wakeup-manager / src / wakeup_engine_manager.cpp
1 #include <multi_assistant_service.h>
2
3 #include "wakeup_engine_manager.h"
4 #include "wakeup_manager_main.h"
5
6 #include <dlfcn.h>
7 #include <algorithm>
8 #include <pkgmgr-info.h>
9 #include <deque>
10
11 namespace multiassistant
12 {
13 namespace wakeup
14 {
15
16 /* Sound buf save for test */
17 #if 0
18 #define BUF_SAVE_MODE
19 #endif
20
21 #ifdef BUF_SAVE_MODE
22 static char g_temp_file_name[128] = {'\0', };
23
24 static FILE* g_pFile = NULL;
25
26 static int g_count = 1;
27 #endif
28
29 /* Need to check whether this value needs to be configurable */
30 static int g_speech_pcm_wait_count = 800;
31
32 /* Utility function for checking if an element exists in a container */
33 template<class C, class T>
34 static auto contains(const C& v, const T& x) -> decltype(end(v), true)
35 {
36         return end(v) != find(begin(v), end(v), x);
37 }
38
39 CWakeupEngineManager::CWakeupEngineManager()
40 {
41 }
42
43 CWakeupEngineManager::~CWakeupEngineManager()
44 {
45         MWR_LOGI("Wakeup Engine Manager is now being destroyed");
46 }
47
48 CWakeupEngineManager::CWakeupEngineManager(IEngineEventObserver *observer) : CWakeupEngineManager()
49 {
50         subscribe(observer);
51 }
52
53 void CWakeupEngineManager::initialize()
54 {
55         DIR* dp = opendir(MA_WAKEUP_ENGINE_PATH);
56         if (nullptr == dp) {
57                 MWR_LOGD("Failed opening directory : %s", (const char*)MA_WAKEUP_ENGINE_PATH);
58         } else {
59                 struct dirent *dirp = nullptr;
60                 char dirpath[_POSIX_PATH_MAX];
61                 do {
62                         dirp = readdir(dp);
63
64                         if (nullptr != dirp) {
65                                 const string current_directory{"."};
66                                 const string parent_directory{".."};
67                                 if (0 == current_directory.compare(dirp->d_name) ||
68                                         0 == parent_directory.compare(dirp->d_name))
69                                         continue;
70
71                                 if (DT_DIR != dirp->d_type) /* If not a directory */
72                                         continue;
73
74                                 int dirpath_len = strlen(MA_WAKEUP_ENGINE_PATH) + strlen(dirp->d_name) + 1;
75                                 if (dirpath_len >= _POSIX_PATH_MAX) {
76                                         MWR_LOGD("File path is too long : %s", dirp->d_name);
77                                         closedir(dp);
78                                         return;
79                                 }
80
81                                 memset(dirpath, '\0', _POSIX_PATH_MAX);
82                                 snprintf(dirpath, _POSIX_PATH_MAX, "%s/%s",
83                                         (const char*)(MA_WAKEUP_ENGINE_PATH), dirp->d_name);
84
85                                 add_engine_directory(string{dirp->d_name}, dirpath);
86                         }
87                 } while (nullptr != dirp);
88
89                 closedir(dp);
90         }
91 }
92
93 void CWakeupEngineManager::deinitialize()
94 {
95         MWR_LOGI("[START]");
96         if (mStreamingThread.joinable()) {
97                 MWR_LOGD("mStreamingThread is joinable, trying join()");
98                 mStopStreamingThread.store(true);
99                 mStreamingThread.join();
100         }
101         mStopStreamingThread.store(false);
102
103         for (auto& info : mEngineInfo) {
104                 try {
105                         if (info.interface.set_wakeup_event_callback) {
106                                 info.interface.set_wakeup_event_callback(nullptr, nullptr);
107                         }
108                         if (info.interface.set_speech_status_callback) {
109                                 info.interface.set_speech_status_callback(nullptr, nullptr);
110                         }
111                         if (info.interface.set_error_callback) {
112                                 info.interface.set_error_callback(nullptr, nullptr);
113                         }
114                         if (info.interface.set_audio_data_require_status_callback) {
115                                 info.interface.set_audio_data_require_status_callback(nullptr, nullptr);
116                         }
117                         if (info.interface.set_wakeup_engine_command_callback) {
118                                 info.interface.set_wakeup_engine_command_callback(nullptr, nullptr);
119                         }
120                         MWR_LOGI("Trying to deinitialize engine : %s", info.engine_name.c_str());
121                         if (info.interface.deinitialize) {
122                                 int ret = info.interface.deinitialize();
123                                 MWR_LOGI("Deinitialization [%s] returned %d", info.engine_name.c_str(), ret);
124                         }
125                 } catch (const std::exception& e) {
126                         MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
127                                 info.engine_name.c_str(), e.what());
128                 }
129                 if (info.engine_handle) {
130                         int ret = dlclose(info.engine_handle);
131                         MWR_LOGI("Closing [%s] returned %d, [%s]", info.engine_name.c_str(), ret,
132                                 ((0 == ret) ? "" : dlerror()));
133                         info.engine_handle = nullptr;
134                 }
135         }
136         mSelectedEngine = nullptr;
137         mEngineInfo.clear();
138         MWR_LOGI("[END]");
139 }
140
141 void CWakeupEngineManager::subscribe(IEngineEventObserver *observer)
142 {
143         mObservers.push_back(observer);
144         MWR_LOGD("Added Observer : %p %zu", observer, mObservers.size());
145 }
146
147 void CWakeupEngineManager::unsubscribe(IEngineEventObserver *observer)
148 {
149         auto iter = find(mObservers.begin(), mObservers.end(), observer);
150         if (iter != mObservers.end()) {
151                 mObservers.erase(iter);
152         }
153 }
154
155 bool CWakeupEngineManager::get_audio_data_required()
156 {
157         return mAudioDataRequired;
158 }
159
160 void CWakeupEngineManager::set_selected_wakeup_info(mas_wakeup_event_info wakeup_info)
161 {
162         mSelectedEngine = nullptr;
163
164         const auto& iter = find_if(mEngineInfo.begin(), mEngineInfo.end(),
165                 [wakeup_info](const EngineInfo& info) {
166                         if (nullptr == wakeup_info.wakeup_engine)
167                                 return false;
168
169                         return (0 == info.engine_name.compare(wakeup_info.wakeup_engine));
170                 });
171
172         if (iter != mEngineInfo.end()) {
173                 mSelectedEngine = &(*iter);
174                 MWR_LOGD("Selected : %s", iter->engine_name.c_str());
175         }
176 }
177
178 bool CWakeupEngineManager::set_language(string language)
179 {
180         for (const auto& info : mEngineInfo) {
181                 if (info.interface.set_language) {
182                         try {
183                                 info.interface.set_language(language.c_str());
184                         } catch (const std::exception& e) {
185                                 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
186                                         info.engine_name.c_str(), e.what());
187                         }
188                 }
189         }
190         return true;
191 }
192
193 bool CWakeupEngineManager::set_assistant_language(string appid, string language)
194 {
195         for (auto& info : mEngineInfo) {
196                 const auto& iter = find_if(info.assistant_list.begin(), info.assistant_list.end(),
197                         [appid](const string& assistant) {
198                                 return (0 == assistant.compare(appid));
199                         });
200
201                 /* If the appid is in the assistant list */
202                 if (info.assistant_list.end() != iter) {
203                         try {
204                                 int ret = info.interface.set_language(language.c_str());
205                                 MWR_LOGI("set_language returned %d : %s %s %s",
206                                         ret, appid.c_str(), info.engine_name.c_str(), language.c_str());
207                         } catch (const std::exception& e) {
208                                 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
209                                         info.engine_name.c_str(), e.what());
210                         }
211                 }
212         }
213         return true;
214 }
215
216 void CWakeupEngineManager::set_assistant_activated(string appid, bool activated)
217 {
218         MWR_LOGI("[ENTER] : %s %d", appid.c_str(), activated);
219         for (auto& info : mEngineInfo) {
220                 const auto& iter = find_if(info.assistant_list.begin(), info.assistant_list.end(),
221                         [appid](const string& assistant) {
222                                 return (0 == assistant.compare(appid));
223                         });
224
225                 /* If the appid is in the assistant list */
226                 if (info.assistant_list.end() != iter) {
227                         MWR_LOGI("%s has %s", info.engine_name.c_str(), appid.c_str());
228                         bool previously_activated = info.activated;
229                         if (activated) {
230                                 info.activated_assistants.insert(appid);
231                         } else {
232                                 info.activated_assistants.erase(appid);
233                         }
234                         info.activated = (info.activated_assistants.size() > 0);
235                         if (previously_activated != info.activated) {
236                                 if (info.activated) {
237                                         try {
238                                                 info.interface.activate();
239                                         } catch (const std::exception& e) {
240                                                 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
241                                                         info.engine_name.c_str(), e.what());
242                                         }
243                                 } else {
244                                         try {
245                                                 info.interface.deactivate();
246                                         } catch (const std::exception& e) {
247                                                 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
248                                                         info.engine_name.c_str(), e.what());
249                                         }
250                                 }
251                                 /* Activated status changed, need to update audio_data_require_status too */
252                                 on_audio_data_require_status(info.engine_name, info.audio_data_require_status);
253                         }
254                 } else {
255                         MWR_LOGI("%s does not have %s", info.engine_name.c_str(), appid.c_str());
256                 }
257         }
258 }
259
260 void CWakeupEngineManager::set_wake_word_audio_require_flag(bool require)
261 {
262         MWR_LOGD("[ENTER]");
263         mWakeWordAudioRequired = require;
264         for (const auto& info : mEngineInfo) {
265                 if (info.interface.set_wake_word_audio_require_flag) {
266                         try {
267                                 info.interface.set_wake_word_audio_require_flag(require);
268                         } catch (const std::exception& e) {
269                                 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
270                                         info.engine_name.c_str(), e.what());
271                         }
272                 }
273         }
274 }
275
276 void CWakeupEngineManager::streaming_speech_data_thread_func()
277 {
278         MWR_LOGI("[ENTER]");
279
280         if (nullptr == mSelectedEngine) {
281                 MWR_LOGE("No Engine Selected");
282                 return;
283         }
284
285         const wakeup_engine_interface *interface = &(mSelectedEngine->interface);
286
287         if (NULL == interface ||
288                 NULL == interface->get_utterance_data ||
289                 NULL == interface->get_utterance_data_count)
290                 return;
291
292         MWR_LOGD("data_count : %d", interface->get_utterance_data_count());
293
294 #ifdef BUF_SAVE_MODE
295         if (g_pFile) {
296                 fclose(g_pFile);
297                 g_pFile = NULL;
298         } else {
299                 MWR_LOGD("[Recorder Info] File not found!");
300         }
301
302         while (1) {
303                 snprintf(g_temp_file_name, sizeof(g_temp_file_name), "/tmp/ma_wue_%d_%d", getpid(), g_count);
304                 int ret = access(g_temp_file_name, 0);
305
306                 if (0 == ret) {
307                         MWR_LOGD("[Recorder ERROR] File is already exist");
308                         if (0 == remove(g_temp_file_name)) {
309                                 MWR_LOGD("[Recorder] Remove file");
310                                 break;
311                         } else {
312                                 g_count++;
313                         }
314                 } else {
315                         break;
316                 }
317         }
318
319         MWR_LOGD("[Recorder] Temp file name=[%s]", g_temp_file_name);
320
321         /* open test file */
322         g_pFile = fopen(g_temp_file_name, "wb+x");
323         if (!g_pFile) {
324                 MWR_LOGD("[Recorder ERROR] File not found!");
325                 return;
326         }
327         g_count++;
328 #endif
329
330         mas_speech_data speech_data;
331         int index = 0;
332         bool finish_event_sent = false;
333
334         if (mWakeWordAudioRequired &&
335                 NULL != interface->get_wake_word_data &&
336                 NULL != interface->get_wake_word_data_count) {
337                 for (const auto& observer : mObservers) {
338                         if (observer) {
339                                 if (!observer->on_audio_streaming_data_section(MA_AUDIO_STREAMING_DATA_SECTION_WAKE_WORD)) {
340                                         LOGE("[Recorder WARNING] One of the observer returned false");
341                                 }
342                         }
343                 }
344                 int count = interface->get_wake_word_data_count();
345                 while (!(mStopStreamingThread.load()) && index < count) {
346                         int ret = interface->get_wake_word_data(index, &speech_data);
347                         if (0 == ret) {
348 #ifdef BUF_SAVE_MODE
349                                 if (g_pFile)
350                                         fwrite(speech_data.buffer, 1, speech_data.len, g_pFile);
351 #endif
352                                 for (const auto& observer : mObservers) {
353                                         if (observer) {
354                                                 if (!observer->on_streaming_audio_data(
355                                                         speech_data.event, speech_data.buffer, speech_data.len)) {
356                                                         LOGE("[Recorder WARNING] One of the observer returned false");
357                                                 }
358                                         }
359                                 }
360                         } else {
361                                 break;
362                         }
363                         index++;
364                 }
365                 index = 0;
366                 for (const auto& observer : mObservers) {
367                         if (observer) {
368                                 if (!observer->on_audio_streaming_data_section(MA_AUDIO_STREAMING_DATA_SECTION_UTTERANCE)) {
369                                         LOGE("[Recorder WARNING] One of the observer returned false");
370                                 }
371                         }
372                 }
373         }
374
375         int burst_count = 0;
376         while (!(mStopStreamingThread.load())) {
377                 int ret = -1;
378                 int cnt = 0;
379
380                 /* get feedback data */
381                 if (interface && interface->get_utterance_data) {
382                         ret = interface->get_utterance_data(index, &speech_data);
383                         if (0 != ret) {
384                                 /* empty queue */
385                                 MWR_LOGD("[DEBUG] No feedback data. Waiting mode : %d", ret);
386
387                                 /* waiting */
388                                 while (!(mStopStreamingThread.load())) {
389                                         burst_count = 0;
390                                         this_thread::sleep_for(chrono::milliseconds(10));
391                                         if (index < interface->get_utterance_data_count()) {
392                                                 MWR_LOGD("[INFO] Resume thread");
393                                                 break;
394                                         }
395                                         if (g_speech_pcm_wait_count < cnt) {
396                                                 unsigned char final_buffer[2] = {'\0', };
397                                                 MWR_LOGE("[ERROR] Wrong request, there's no pcm data");
398 #ifdef BUF_SAVE_MODE
399                                                 if (g_pFile) {
400                                                         fwrite(final_buffer, 1, sizeof(final_buffer), g_pFile);
401                                                         MWR_LOGE("[Recorder SUCCESS] File Close");
402                                                         fclose(g_pFile);
403                                                         g_pFile = NULL;
404                                                 }
405 #endif
406                                                 for (const auto& observer : mObservers) {
407                                                         if (observer) {
408                                                                 if (!observer->on_streaming_audio_data(
409                                                                         MAS_SPEECH_STREAMING_EVENT_FAIL, NULL, 0)) {
410                                                                         LOGE("[Recorder WARNING] One of the observer returned false");
411                                                                 }
412                                                                 if (!observer->on_streaming_audio_data(
413                                                                         MAS_SPEECH_STREAMING_EVENT_FINISH, final_buffer, sizeof(final_buffer))) {
414                                                                         LOGE("[Recorder WARNING] One of the observer returned false");
415                                                                 }
416                                                         }
417                                                 }
418                                                 return;
419                                         }
420                                         cnt++;
421                                 }
422                                 MWR_LOGD("[INFO] Finish to wait for new feedback data come");
423
424                                 /* resume feedback thread */
425                                 continue;
426                         }
427
428 #ifdef BUF_SAVE_MODE
429                         if (g_pFile)
430                                 fwrite(speech_data.buffer, 1, speech_data.len, g_pFile);
431
432                         if (MAS_SPEECH_STREAMING_EVENT_FINISH == speech_data.event) {
433                                 if (g_pFile) {
434                                         MWR_LOGE("[Recorder SUCCESS] File Close");
435                                         fclose(g_pFile);
436                                         g_pFile = NULL;
437                                 } else {
438                                         MWR_LOGE("[Recorder ERROR] File not found!");
439                                 }
440                         }
441 #endif
442                         const int sleep_duration_in_millis = 10;
443                         const int max_burst_count = 3;
444                         if (++burst_count >= max_burst_count) {
445                                 burst_count = 0;
446                                 this_thread::sleep_for(chrono::milliseconds(sleep_duration_in_millis));
447                                 MWR_LOGI("[INFO] Streaming data burst transmission detected, forcing sleep");
448                         }
449                         for (const auto& observer : mObservers) {
450                                 if (observer) {
451                                         if (!observer->on_streaming_audio_data(
452                                                 speech_data.event, speech_data.buffer, speech_data.len)) {
453                                                 LOGE("[Recorder WARNING] One of the observer returned false");
454                                         }
455                                 }
456                         }
457
458                         if (MAS_SPEECH_STREAMING_EVENT_FINISH == speech_data.event) {
459                                 MWR_LOGI("[INFO] Finish to get and send speech data");
460                                 finish_event_sent = true;
461                                 break;
462                         }
463
464                         index++;
465                 }
466         }
467
468         if (true != finish_event_sent) {
469                 unsigned char final_buffer[2] = {'\0', };
470                 for (const auto& observer : mObservers) {
471                         if (observer) {
472                                 if (!observer->on_streaming_audio_data(
473                                         MAS_SPEECH_STREAMING_EVENT_FINISH, final_buffer, sizeof(final_buffer))) {
474                                         LOGE("[Recorder WARNING] One of the observer returned false");
475                                 }
476                         }
477                 }
478 #ifdef BUF_SAVE_MODE
479                 if (g_pFile) {
480                         fwrite(final_buffer, 1, sizeof(final_buffer), g_pFile);
481                         MWR_LOGE("[Recorder SUCCESS] File Close");
482                         fclose(g_pFile);
483                         g_pFile = NULL;
484                 }
485 #endif
486         }
487
488         MWR_LOGI("[EXIT]");
489 }
490
491 void CWakeupEngineManager::start_streaming_current_utterance_data()
492 {
493         if (mStreamingThread.joinable()) {
494                 MWR_LOGE("ERROR : mStreamingThread is joinable, will not start a new thread");
495                 return;
496         }
497         mStreamingThread = thread(&CWakeupEngineManager::streaming_speech_data_thread_func, this);
498 }
499
500 void CWakeupEngineManager::stop_streaming_current_utterance_data()
501 {
502         if (mStreamingThread.joinable()) {
503                 MWR_LOGD("mStreamingThread is joinable, trying join()");
504                 mStopStreamingThread.store(true);
505                 mStreamingThread.join();
506         }
507         mStopStreamingThread.store(false);
508 }
509
510 void CWakeupEngineManager::update_manager_state(wakeup_manager_state_e state)
511 {
512         for (const auto& info : mEngineInfo) {
513                 if (info.interface.update_manager_state) {
514                         try {
515                                 info.interface.update_manager_state(state);
516                         } catch (const std::exception& e) {
517                                 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
518                                         info.engine_name.c_str(), e.what());
519                         }
520                 }
521         }
522         mWakeupManagerState = state;
523 }
524
525 void CWakeupEngineManager::update_recognition_result(string appid, int result)
526 {
527         if (mSelectedEngine) {
528                 if (mSelectedEngine->interface.update_recognition_result) {
529                         mSelectedEngine->interface.update_recognition_result(appid.c_str(), result);
530                 }
531         }
532 }
533
534 void CWakeupEngineManager::engine_add_target_assistant(string engine_name, string appid)
535 {
536         const auto& iter = find_if(mEngineInfo.begin(), mEngineInfo.end(),
537                 [engine_name](const EngineInfo& info) {
538                         return (0 == info.engine_name.compare(engine_name));
539                 });
540
541         if (mEngineInfo.end() == iter) {
542                 /* Not found, add new library */
543                 pkgmgrinfo_appinfo_h handle;
544                 int ret = pkgmgrinfo_appinfo_get_appinfo(engine_name.c_str(), &handle);
545                 if (PMINFO_R_OK == ret) {
546                         char *root_path = nullptr;
547                         ret = pkgmgrinfo_appinfo_get_root_path(handle, &root_path);
548                         if (PMINFO_R_OK == ret && nullptr != root_path) {
549                                 string path = root_path;
550                                 path += "/";
551                                 path += MA_WAKEUP_DEDICATED_ENGINE_PATH;
552                                 add_engine(engine_name, path);
553                         }
554                         pkgmgrinfo_appinfo_destroy_appinfo(handle);
555                 }
556                 /* Find again to add appid to the newly created engine's assistant list */
557                 const auto &new_iter = find_if(mEngineInfo.begin(), mEngineInfo.end(),
558                         [engine_name](const EngineInfo& info) {
559                                 return (0 == info.engine_name.compare(engine_name));
560                         });
561                 if (mEngineInfo.end() != new_iter) {
562                         new_iter->assistant_list.push_back(appid);
563                 }
564                 for (const auto assistant : new_iter->assistant_list) {
565                         MWR_LOGI("Assistant List : %s %s", assistant.c_str(), new_iter->engine_name.c_str());
566                 }
567         } else {
568                 /* If the engine already exists, simply add the appid to the assistant list */
569                 iter->assistant_list.push_back(appid);
570                 for (const auto assistant : iter->assistant_list) {
571                         MWR_LOGI("Assistant List : %s %s", assistant.c_str(), iter->engine_name.c_str());
572                 }
573         }
574 }
575
576 void CWakeupEngineManager::engine_add_wakeup_word(string appid, string wakeup_word, string language)
577 {
578         for (const auto& info : mEngineInfo) {
579                 bool found = contains(info.assistant_list, appid);
580                 if (found) {
581                         if (info.interface.add_wakeup_word) {
582                                 try {
583                                         info.interface.add_wakeup_word(appid.c_str(), wakeup_word.c_str(), language.c_str());
584                                 } catch (const std::exception& e) {
585                                         MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
586                                                 info.engine_name.c_str(), e.what());
587                                 }
588                         } else {
589                                 MWR_LOGE("Wakeup Engine does not provide add_wakeup_word");
590                         }
591                 }
592         }
593 }
594
595 void CWakeupEngineManager::engine_remove_wakeup_word(string appid, string wakeup_word, string language)
596 {
597         for (const auto& info : mEngineInfo) {
598                 bool found = contains(info.assistant_list, appid);
599                 if (found) {
600                         if (info.interface.remove_wakeup_word) {
601                                 try {
602                                         info.interface.remove_wakeup_word(appid.c_str(), wakeup_word.c_str(), language.c_str());
603                                 } catch (const std::exception& e) {
604                                         MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
605                                                 info.engine_name.c_str(), e.what());
606                                 }
607                         } else {
608                                 MWR_LOGE("Wakeup Engine does not provide remove_wakeup_word");
609                         }
610                 }
611         }
612 }
613
614 void CWakeupEngineManager::engine_set_assistant_specific_command(string appid, string command)
615 {
616         for (const auto& info : mEngineInfo) {
617                 bool found = contains(info.assistant_list, appid);
618                 if (found) {
619                         if (info.interface.set_assistant_specific_command) {
620                                 try {
621                                         info.interface.set_assistant_specific_command(appid.c_str(), command.c_str());
622                                 } catch (const std::exception& e) {
623                                         MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
624                                                 info.engine_name.c_str(), e.what());
625                                 }
626                         }
627                 }
628         }
629 }
630
631 void CWakeupEngineManager::engine_feed_audio_data(long time, void* data, int len)
632 {
633         for (const auto& info : mEngineInfo) {
634                 if (info.activated &&
635                         info.audio_data_require_status &&
636                         info.interface.feed_audio_data) {
637                         try {
638                                 bool filtered_out = false;
639                                 /* After a wakeup event, wakeup engines other than the selected one
640                                    does not need to receive audio data. */
641                                 if (WAKEUP_MANAGER_STATE_UTTERANCE == mWakeupManagerState) {
642                                         if (mSelectedEngine && &info != mSelectedEngine) {
643                                                 filtered_out = true;
644                                         }
645                                 }
646                                 if (!filtered_out) {
647                                         int ret = info.interface.feed_audio_data(time, data, len);
648                                         if (0 != ret) {
649                                                 LOGE("[ERROR] Fail to feed speech data, ret(%d) : %s", ret, info.engine_name.c_str());
650                                         }
651                                 }
652                         } catch (const std::exception& e) {
653                                 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
654                                         info.engine_name.c_str(), e.what());
655                         }
656                 }
657         }
658 }
659
660 void CWakeupEngineManager::engine_set_dependency_module_command(string engine_name, string command)
661 {
662         const auto& iter = find_if(mEngineInfo.begin(), mEngineInfo.end(),
663                 [engine_name](const EngineInfo& info) {
664                         return (0 == info.engine_name.compare(engine_name));
665                 });
666
667         if (mEngineInfo.end() != iter) {
668                 if (iter->activated &&
669                         iter->interface.set_dependency_module_command) {
670                         try {
671                                 int ret = iter->interface.set_dependency_module_command(command.c_str());
672                                 if (0 != ret) {
673                                         LOGE("[ERROR] Fail to set dependency module command, ret(%d) : %s",
674                                                 ret, iter->engine_name.c_str());
675                                 }
676                         } catch (const std::exception& e) {
677                                 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
678                                         iter->engine_name.c_str(), e.what());
679                         }
680                 }
681         }
682 }
683
684 bool CWakeupEngineManager::on_wakeup_event(string engine_name, mas_wakeup_event_info info)
685 {
686         MWR_LOGD("[ENTER]");
687
688         for (const auto& observer : mObservers) {
689                 if (observer) {
690                         if (!observer->on_wakeup_event(engine_name, info)) {
691                                 LOGE("[Recorder WARNING] One of the observer returned false");
692                         }
693                 }
694         }
695
696         return true;
697 }
698
699 bool CWakeupEngineManager::on_speech_status(string engine_name, mas_speech_status_e status)
700 {
701         MWR_LOGD("[ENTER]");
702
703         for (const auto& observer : mObservers) {
704                 if (observer) {
705                         if (!observer->on_speech_status(engine_name, status)) {
706                                 LOGE("[Recorder WARNING] One of the observer returned false");
707                         }
708                 }
709         }
710
711         return true;
712 }
713
714 bool CWakeupEngineManager::on_error(string engine_name, int error_code, string error_message)
715 {
716         MWR_LOGD("[ENTER]");
717
718         for (const auto& observer : mObservers) {
719                 if (observer) {
720                         if (!observer->on_error(engine_name, error_code, error_message)) {
721                                 LOGE("[Recorder WARNING] One of the observer returned false");
722                         }
723                 }
724         }
725
726         return true;
727 }
728
729 bool CWakeupEngineManager::on_audio_data_require_status(string engine_name, bool require)
730 {
731         MWR_LOGI("[ENTER] %s, %d", engine_name.c_str(), require);
732
733         bool found = false;
734         // LOCK REQUIRED
735         int count = 0;
736         for (auto& info : mEngineInfo) {
737                 if (info.engine_name.compare(engine_name) == 0) {
738                         found = true;
739                         info.audio_data_require_status = require;
740                 }
741                 if (info.activated && info.audio_data_require_status) {
742                         count++;
743                 }
744         }
745         MWR_LOGD("count : %d", count);
746         if (count > 0) {
747                 mAudioDataRequired = true;
748         } else {
749                 mAudioDataRequired = false;
750         }
751
752         if (found) {
753                 for (const auto& observer : mObservers) {
754                         if (observer) {
755                                 if (!observer->on_audio_data_require_status(engine_name, require)) {
756                                         LOGE("[Recorder WARNING] One of the observer returned false");
757                                 }
758                         }
759                 }
760         }
761         // UNLOCK REQUIRED
762         return true;
763 }
764
765 bool CWakeupEngineManager::on_wakeup_engine_command(string engine_name, mas_wakeup_engine_command_target_e target, string assistant_name, string command)
766 {
767         MWR_LOGI("[ENTER] : %s %d %s %s",
768                 engine_name.c_str(), target, assistant_name.c_str(), command.c_str());
769
770         for (const auto& observer : mObservers) {
771                 if (observer) {
772                         if (MAS_WAKEUP_ENGINE_COMMAND_TARGET_DEPENDENCY_MODULE == target) {
773                                 if (!observer->on_wakeup_engine_command(target, engine_name, assistant_name, command)) {
774                                         LOGE("[Recorder WARNING] One of the observer returned false");
775                                 }
776                         } else {
777                                 const auto& iter = find_if(mEngineInfo.begin(), mEngineInfo.end(),
778                                         [engine_name](const EngineInfo& info) {
779                                                 return (0 == info.engine_name.compare(engine_name));
780                                         });
781                                 if (mEngineInfo.end() != iter) {
782                                         for (const auto& assistant : iter->assistant_list) {
783                                                 if (0 == assistant_name.compare(assistant) ||
784                                                         MAS_WAKEUP_ENGINE_COMMAND_TARGET_ALL_ASSISTANTS == target) {
785                                                                 MWR_LOGI("Calling on_wakeup_engine_command for %s", assistant.c_str());
786                                                         if (!observer->on_wakeup_engine_command(target, engine_name, assistant, command)) {
787                                                                 LOGE("[Recorder WARNING] One of the observer returned false");
788                                                         }
789                                                 }
790                                         }
791                                 }
792                         }
793                 }
794         }
795
796         return true;
797 }
798
799 void CWakeupEngineManager::add_engine(string name, string path)
800 {
801         MWR_LOGD("Name (%s), Filepath(%s)", name.c_str(), path.c_str());
802
803         char* error = NULL;
804         EngineInfo info;
805         info.engine_handle = dlopen(path.c_str(), RTLD_LAZY);
806         if (nullptr != (error = dlerror()) || nullptr == info.engine_handle) {
807                 MWR_LOGD("[ERROR] Fail to dlopen(%s), error(%s)", path.c_str(), error);
808                 if (info.engine_handle) dlclose(info.engine_handle);
809                 return;
810         }
811
812         /* Interfaces without version information */
813         info.interface.initialize =
814                 (wakeup_engine_initialize)dlsym(info.engine_handle,
815                 MA_WAKEUP_ENGINE_FUNC_INITIALIZE);
816         info.interface.deinitialize =
817                 (wakeup_engine_deinitialize)dlsym(info.engine_handle,
818                 MA_WAKEUP_ENGINE_FUNC_DEINITIALIZE);
819         info.interface.activate =
820                 (wakeup_engine_activate)dlsym(info.engine_handle,
821                 MA_WAKEUP_ENGINE_FUNC_ACTIVATE);
822         info.interface.deactivate =
823                 (wakeup_engine_deactivate)dlsym(info.engine_handle,
824                 MA_WAKEUP_ENGINE_FUNC_DEACTIVATE);
825         info.interface.add_wakeup_word =
826                 (wakeup_engine_add_wakeup_word)dlsym(info.engine_handle,
827                 MA_WAKEUP_ENGINE_FUNC_ADD_WAKEUP_WORD);
828         info.interface.remove_wakeup_word =
829                 (wakeup_engine_remove_wakeup_word)dlsym(info.engine_handle,
830                 MA_WAKEUP_ENGINE_FUNC_REMOVE_WAKEUP_WORD);
831         info.interface.add_language =
832                 (wakeup_engine_add_language)dlsym(info.engine_handle,
833                 MA_WAKEUP_ENGINE_FUNC_ADD_LANGUAGE);
834         info.interface.set_language =
835                 (wakeup_engine_set_language)dlsym(info.engine_handle,
836                 MA_WAKEUP_ENGINE_FUNC_SET_LANGUAGE);
837         info.interface.update_manager_state =
838                 (wakeup_engine_update_manager_state)dlsym(info.engine_handle,
839                 MA_WAKEUP_ENGINE_FUNC_UPDATE_MANAGER_STATE);
840         info.interface.update_recognition_result =
841                 (wakeup_engine_update_recognition_result)dlsym(info.engine_handle,
842                 MA_WAKEUP_ENGINE_FUNC_UPDATE_RECOGNITION_RESULT);
843         info.interface.set_audio_format =
844                 (wakeup_engine_set_audio_format)dlsym(info.engine_handle,
845                 MA_WAKEUP_ENGINE_FUNC_SET_AUDIO_FORMAT);
846         info.interface.get_audio_format =
847                 (wakeup_engine_get_audio_format)dlsym(info.engine_handle,
848                 MA_WAKEUP_ENGINE_FUNC_GET_AUDIO_FORMAT);
849         info.interface.feed_audio_data =
850                 (wakeup_engine_feed_audio_data)dlsym(info.engine_handle,
851                 MA_WAKEUP_ENGINE_FUNC_FEED_AUDIO_DATA);
852         info.interface.get_utterance_data_count =
853                 (wakeup_engine_get_utterance_data_count)dlsym(info.engine_handle,
854                 MA_WAKEUP_ENGINE_FUNC_GET_UTTERANCE_DATA_COUNT);
855         info.interface.get_utterance_data =
856                 (wakeup_engine_get_utterance_data)dlsym(info.engine_handle,
857                 MA_WAKEUP_ENGINE_FUNC_GET_UTTERANCE_DATA);
858         info.interface.get_wake_word_data_count =
859                 (wakeup_engine_get_wake_word_data_count)dlsym(info.engine_handle,
860                 MA_WAKEUP_ENGINE_FUNC_GET_WAKE_WORD_DATA_COUNT);
861         info.interface.get_wake_word_data =
862                 (wakeup_engine_get_wake_word_data)dlsym(info.engine_handle,
863                 MA_WAKEUP_ENGINE_FUNC_GET_WAKE_WORD_DATA);
864         info.interface.set_assistant_specific_command =
865                 (wakeup_engine_set_assistant_specific_command)dlsym(info.engine_handle,
866                 MA_WAKEUP_ENGINE_FUNC_SET_ASSISTANT_SPECIFIC_COMMAND);
867         info.interface.set_wake_word_audio_require_flag =
868                 (wakeup_engine_set_wake_word_audio_require_flag)dlsym(info.engine_handle,
869                 MA_WAKEUP_ENGINE_FUNC_SET_WAKE_WORD_AUDIO_REQUIRE_FLAG);
870         info.interface.set_wakeup_event_callback =
871                 (wakeup_engine_set_wakeup_event_callback)dlsym(info.engine_handle,
872                 MA_WAKEUP_ENGINE_FUNC_SET_WAKEUP_EVENT_CALLBACK);
873         info.interface.set_speech_status_callback =
874                 (wakeup_engine_set_speech_status_callback)dlsym(info.engine_handle,
875                 MA_WAKEUP_ENGINE_FUNC_SET_SPEECH_STATUS_CALLBACK);
876         info.interface.set_error_callback =
877                 (wakeup_engine_set_error_callback)dlsym(info.engine_handle,
878                 MA_WAKEUP_ENGINE_FUNC_SET_ERROR_CALLBACK);
879         info.interface.set_audio_data_require_status_callback =
880                 (wakeup_engine_set_audio_data_require_status_callback)dlsym(info.engine_handle,
881                 MA_WAKEUP_ENGINE_FUNC_SET_AUDIO_DATA_REQUIRE_STATUS_CALLBACK);
882         info.interface.set_wakeup_engine_command_callback =
883                 (wakeup_engine_set_wakeup_engine_command_callback)dlsym(info.engine_handle,
884                 MA_WAKEUP_ENGINE_FUNC_SET_WAKEUP_ENGINE_COMMAND_CALLBACK);
885
886         /* Interfaces after version 1 */
887         info.interface.get_version =
888                 (wakeup_engine_get_version)dlsym(info.engine_handle,
889                 MA_WAKEUP_ENGINE_FUNC_GET_VERSION);
890         info.interface.set_dependency_module_command =
891                 (wakeup_engine_set_dependency_module_command)dlsym(info.engine_handle,
892                 MA_WAKEUP_ENGINE_FUNC_SET_DEPENDENCY_MODULE_COMMAND);
893
894         info.version = 0;
895         info.engine_path = path;
896         info.engine_name = name;
897
898         info.activated = false;
899         info.audio_data_require_status = false;
900
901         /* All the necessary information has already been set properly */
902         mEngineInfo.push_back(info);
903
904         /* Workaround for registering C-style callbacks */
905         typedef struct {
906                 CWakeupEngineManager *manager;
907                 string engine_name;
908         } CallbackUserData;
909
910         static deque<CallbackUserData> callback_user_data;
911
912         CallbackUserData user_data;
913         user_data.manager = this;
914         user_data.engine_name = info.engine_name;
915         callback_user_data.push_back(user_data);
916
917         MWR_LOGI("Initializing wakeup engine : %s %p %p",
918                 info.engine_path.c_str(),
919                 info.interface.initialize,
920                 &callback_user_data.back());
921
922         try {
923                 if (info.interface.set_wakeup_event_callback) {
924                         info.interface.set_wakeup_event_callback(
925                                 [](mas_wakeup_event_info info, void* user_data) {
926                                         MWR_LOGI("user_data : %p", user_data);
927                                         CallbackUserData *data = static_cast<CallbackUserData*>(user_data);
928                                         if (nullptr == data) return;
929                                         if (nullptr == data->manager) return;
930                                         info.wakeup_engine = data->engine_name.c_str();
931                                         data->manager->on_wakeup_event(data->engine_name, info);
932                                 }, &(callback_user_data.back()));
933                 }
934
935                 if (info.interface.set_audio_data_require_status_callback) {
936                         info.interface.set_audio_data_require_status_callback(
937                                 [](bool require, void* user_data) {
938                                         MWR_LOGI("user_data : %p", user_data);
939                                         CallbackUserData *data = static_cast<CallbackUserData*>(user_data);
940                                         if (nullptr == data) return;
941                                         if (nullptr == data->manager) return;
942                                         data->manager->on_audio_data_require_status(data->engine_name, require);
943                                 }, &(callback_user_data.back()));
944                 }
945
946                 if (info.interface.set_wakeup_engine_command_callback) {
947                         info.interface.set_wakeup_engine_command_callback(
948                                 [](mas_wakeup_engine_command_target_e target,
949                                         const char* assistant_name, const char* command, void* user_data) {
950                                         MWR_LOGI("user_data : %p", user_data);
951                                         CallbackUserData* data = static_cast<CallbackUserData*>(user_data);
952                                         if (nullptr == data) return;
953                                         if (nullptr == data->manager) return;
954                                         if (nullptr == command) return;
955                                         data->manager->on_wakeup_engine_command(
956                                                 data->engine_name, target, (assistant_name ? assistant_name : ""), command);
957                                 }, &(callback_user_data.back()));
958                 }
959
960                 if (info.interface.initialize) {
961                         info.interface.initialize();
962                 }
963                 if (info.interface.get_version) {
964                         int version;
965                         if (0 == info.interface.get_version(&version)) {
966                                 info.version = version;
967                         }
968                 }
969         } catch (const std::exception& e) {
970                 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
971                         info.engine_name.c_str(), e.what());
972         }
973 }
974
975 void CWakeupEngineManager::add_engine_directory(string name, string path)
976 {
977         if (0 == path.size()) return;
978
979         DIR* dp = opendir(path.c_str());
980         if (NULL == dp) {
981                 MWR_LOGD("Failed opening directory : %s", path.c_str());
982         } else {
983                 struct dirent *dirp = NULL;
984                 string filepath;
985                 do {
986                         dirp = readdir(dp);
987
988                         if (NULL != dirp) {
989                                 if (!strcmp(".", dirp->d_name) || !strcmp("..", dirp->d_name))
990                                         continue;
991
992                                 if (DT_REG != dirp->d_type) /* If not a regular file */
993                                         continue;
994
995                                 filepath = path;
996                                 filepath += "/";
997                                 filepath += dirp->d_name;
998
999                                 if (filepath.length() >= _POSIX_PATH_MAX) {
1000                                         MWR_LOGD("File path is too long : %s", filepath.c_str());
1001                                         closedir(dp);
1002                                         return;
1003                                 }
1004                                 add_engine(name, filepath);
1005                         }
1006                 } while (NULL != dirp);
1007
1008                 closedir(dp);
1009         }
1010 }
1011
1012 } // wakeup
1013 } // multiassistant