Add exception handlers for the functions in external modules
[platform/core/uifw/multi-assistant-service.git] / plugins / wakeup-manager / src / wakeup_engine_manager.cpp
1 #include "wakeup_engine_manager.h"
2 #include "wakeup_manager_main.h"
3
4 #include <dlfcn.h>
5 #include <algorithm>
6 #include <pkgmgr-info.h>
7 #include <deque>
8
9 namespace multiassistant
10 {
11 namespace wakeup
12 {
13
14 /* Need to check whether this value needs to be configurable */
15 static int g_speech_pcm_wait_count = 400;
16
17 /* Utility function for checking if an element exists in a container */
18 template<class C, class T>
19 static auto contains(const C& v, const T& x) -> decltype(end(v), true)
20 {
21         return end(v) != find(begin(v), end(v), x);
22 }
23
24 CWakeupEngineManager::CWakeupEngineManager()
25 {
26 }
27
28 CWakeupEngineManager::~CWakeupEngineManager()
29 {
30 }
31
32 CWakeupEngineManager::CWakeupEngineManager(IEngineEventObserver *observer) : CWakeupEngineManager()
33 {
34         subscribe(observer);
35 }
36
37 void CWakeupEngineManager::initialize()
38 {
39         DIR* dp = opendir(MA_WAKEUP_ENGINE_PATH);
40         if (nullptr == dp) {
41                 MWR_LOGD("Failed opening directory : %s", (const char*)MA_WAKEUP_ENGINE_PATH);
42         } else {
43                 struct dirent *dirp = nullptr;
44                 char dirpath[_POSIX_PATH_MAX];
45                 do {
46                         dirp = readdir(dp);
47
48                         if (nullptr != dirp) {
49                                 const string current_directory{"."};
50                                 const string parent_directory{".."};
51                                 if (0 == current_directory.compare(dirp->d_name) ||
52                                         0 == parent_directory.compare(dirp->d_name))
53                                         continue;
54
55                                 if (DT_DIR != dirp->d_type) /* If not a directory */
56                                         continue;
57
58                                 int dirpath_len = strlen(MA_WAKEUP_ENGINE_PATH) + strlen(dirp->d_name) + 1;
59                                 if (dirpath_len >= _POSIX_PATH_MAX) {
60                                         MWR_LOGD("File path is too long : %s", dirp->d_name);
61                                         closedir(dp);
62                                         return;
63                                 }
64
65                                 memset(dirpath, '\0', _POSIX_PATH_MAX);
66                                 snprintf(dirpath, _POSIX_PATH_MAX, "%s/%s",
67                                         (const char*)(MA_WAKEUP_ENGINE_PATH), dirp->d_name);
68
69                                 add_engine_directory(string{dirp->d_name}, dirpath);
70                         }
71                 } while (nullptr != dirp);
72
73                 closedir(dp);
74         }
75 }
76
77 void CWakeupEngineManager::deinitialize()
78 {
79         for (auto& info : mEngineInfo) {
80                 try {
81                         if (info.interface.set_wakeup_event_callback) {
82                                 info.interface.set_wakeup_event_callback(nullptr, nullptr);
83                         }
84                         if (info.interface.set_speech_status_callback) {
85                                 info.interface.set_speech_status_callback(nullptr, nullptr);
86                         }
87                         if (info.interface.set_error_callback) {
88                                 info.interface.set_error_callback(nullptr, nullptr);
89                         }
90                         if (info.interface.set_audio_data_require_status_callback) {
91                                 info.interface.set_audio_data_require_status_callback(nullptr, nullptr);
92                         }
93                         if (info.interface.deinitialize) {
94                                 info.interface.deinitialize();
95                         }
96                 } catch (const std::exception& e) {
97                         MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
98                                 info.engine_name.c_str(), e.what());
99                 }
100                 if (info.engine_handle) {
101                         dlclose(info.engine_handle);
102                         info.engine_handle = nullptr;
103                 }
104         }
105         mSelectedEngine = nullptr;
106         mEngineInfo.clear();
107 }
108
109 void CWakeupEngineManager::subscribe(IEngineEventObserver *observer)
110 {
111         mObservers.push_back(observer);
112         MWR_LOGD("Added Observer : %p %zu", observer, mObservers.size());
113 }
114
115 void CWakeupEngineManager::unsubscribe(IEngineEventObserver *observer)
116 {
117         auto iter = find(mObservers.begin(), mObservers.end(), observer);
118         if (iter != mObservers.end()) {
119                 mObservers.erase(iter);
120         }
121 }
122
123 bool CWakeupEngineManager::get_audio_data_required()
124 {
125         return mAudioDataRequired;
126 }
127
128 void CWakeupEngineManager::set_selected_wakeup_info(wakeup_event_info wakeup_info)
129 {
130         mSelectedEngine = nullptr;
131         for (const auto& info : mEngineInfo) {
132                 string appid = string{wakeup_info.wakeup_appid};
133                 bool found = contains(info.assistant_list, appid);
134
135                 if (found) {
136                         mSelectedEngine = &info;
137                         MWR_LOGD("Selected : %s", info.engine_name.c_str());
138                 }
139         }
140 }
141
142 bool CWakeupEngineManager::set_language(string language)
143 {
144         for (const auto& info : mEngineInfo) {
145                 if (info.interface.set_language) {
146                         try {
147                                 info.interface.set_language(language.c_str());
148                         } catch (const std::exception& e) {
149                                 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
150                                         info.engine_name.c_str(), e.what());
151                         }
152                 }
153         }
154         return true;
155 }
156
157 void CWakeupEngineManager::set_assistant_activated(string appid, bool activated)
158 {
159         MWR_LOGD("[ENTER] : %s %d", appid.c_str(), activated);
160         for (auto& info : mEngineInfo) {
161                 const auto& iter = find_if(info.assistant_list.begin(), info.assistant_list.end(),
162                         [appid](const string& assistant) {
163                                 return (0 == assistant.compare(appid));
164                         });
165
166                 /* If the appid is in the assistant list */
167                 if (info.assistant_list.end() != iter) {
168                         bool previously_activated = info.activated;
169                         if (activated) {
170                                 info.activated_assistants.insert(appid);
171                         } else {
172                                 info.activated_assistants.erase(appid);
173                         }
174                         info.activated = (info.activated_assistants.size() > 0);
175                         if (previously_activated != info.activated) {
176                                 if (info.activated) {
177                                         try {
178                                                 info.interface.activate();
179                                         } catch (const std::exception& e) {
180                                                 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
181                                                         info.engine_name.c_str(), e.what());
182                                         }
183                                 } else {
184                                         try {
185                                                 info.interface.deactivate();
186                                         } catch (const std::exception& e) {
187                                                 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
188                                                         info.engine_name.c_str(), e.what());
189                                         }
190                                 }
191                                 /* Activated status changed, need to update audio_data_require_status too */
192                                 on_audio_data_require_status(info.engine_name, info.audio_data_require_status);
193                         }
194                 }
195         }
196 }
197
198 void CWakeupEngineManager::set_wake_word_audio_require_flag(bool require)
199 {
200         MWR_LOGD("[ENTER]");
201         mWakeWordAudioRequired = require;
202         for (const auto& info : mEngineInfo) {
203                 if (info.interface.set_wake_word_audio_require_flag) {
204                         try {
205                                 info.interface.set_wake_word_audio_require_flag(require);
206                         } catch (const std::exception& e) {
207                                 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
208                                         info.engine_name.c_str(), e.what());
209                         }
210                 }
211         }
212 }
213
214 void CWakeupEngineManager::streaming_speech_data_thread_func()
215 {
216         MWR_LOGD("[ENTER]");
217
218         if (nullptr == mSelectedEngine)
219                 return;
220
221         const wakeup_engine_interface *interface = &(mSelectedEngine->interface);
222
223         if (NULL == interface ||
224                 NULL == interface->get_utterance_data ||
225                 NULL == interface->get_utterance_data_count)
226                 return;
227
228         MWR_LOGD("data_count : %d", interface->get_utterance_data_count());
229
230         wakeup_speech_data speech_data;
231         int index = 0;
232         bool finish_event_sent = false;
233
234         if (mWakeWordAudioRequired &&
235                 NULL != interface->get_wake_word_data &&
236                 NULL != interface->get_wake_word_data_count) {
237                 for (const auto& observer : mObservers) {
238                         if (observer) {
239                                 if (!observer->on_audio_streaming_data_section(MA_AUDIO_STREAMING_DATA_SECTION_WAKE_WORD)) {
240                                         LOGE("[Recorder WARNING] One of the observer returned false");
241                                 }
242                         }
243                 }
244                 int count = interface->get_wake_word_data_count();
245                 while (!(mStopStreamingThread.load()) && index < count) {
246                         int ret = interface->get_wake_word_data(index, &speech_data);
247                         if (0 == ret) {
248                                 for (const auto& observer : mObservers) {
249                                         if (observer) {
250                                                 if (!observer->on_streaming_audio_data(
251                                                         speech_data.event, speech_data.buffer, speech_data.len)) {
252                                                         LOGE("[Recorder WARNING] One of the observer returned false");
253                                                 }
254                                         }
255                                 }
256                         } else {
257                                 break;
258                         }
259                         index++;
260                 }
261                 index = 0;
262                 for (const auto& observer : mObservers) {
263                         if (observer) {
264                                 if (!observer->on_audio_streaming_data_section(MA_AUDIO_STREAMING_DATA_SECTION_UTTERANCE)) {
265                                         LOGE("[Recorder WARNING] One of the observer returned false");
266                                 }
267                         }
268                 }
269         }
270
271         while (!(mStopStreamingThread.load())) {
272                 int ret = -1;
273                 int cnt = 0;
274
275                 /* get feedback data */
276                 if (interface && interface->get_utterance_data) {
277                         ret = interface->get_utterance_data(index, &speech_data);
278                         if (0 != ret) {
279                                 /* empty queue */
280                                 MWR_LOGD("[DEBUG] No feedback data. Waiting mode : %d", ret);
281
282                                 /* waiting */
283                                 while (!(mStopStreamingThread.load())) {
284                                         this_thread::sleep_for(chrono::milliseconds(10));
285                                         if (index < interface->get_utterance_data_count()) {
286                                                 MWR_LOGI("[INFO] Resume thread");
287                                                 break;
288                                         }
289                                         if (g_speech_pcm_wait_count < cnt) {
290                                                 MWR_LOGE("[ERROR] Wrong request, there's no pcm data");
291                                                 for (const auto& observer : mObservers) {
292                                                         if (observer) {
293                                                                 if (!observer->on_streaming_audio_data(
294                                                                         WAKEUP_SPEECH_STREAMING_EVENT_FAIL, NULL, 0)) {
295                                                                         LOGE("[Recorder WARNING] One of the observer returned false");
296                                                                 }
297                                                         }
298                                                 }
299                                                 return;
300                                         }
301                                         cnt++;
302                                 }
303                                 MWR_LOGI("[INFO] Finish to wait for new feedback data come");
304
305                                 /* resume feedback thread */
306                                 continue;
307                         }
308
309                         for (const auto& observer : mObservers) {
310                                 if (observer) {
311                                         if (!observer->on_streaming_audio_data(
312                                                 speech_data.event, speech_data.buffer, speech_data.len)) {
313                                                 LOGE("[Recorder WARNING] One of the observer returned false");
314                                         }
315                                 }
316                         }
317
318                         if (WAKEUP_SPEECH_STREAMING_EVENT_FINISH == speech_data.event) {
319                                 MWR_LOGI("[INFO] Finish to get and send speech data");
320                                 finish_event_sent = true;
321                                 break;
322                         }
323
324                         index++;
325                 }
326         }
327
328         if (true != finish_event_sent) {
329                 unsigned char final_buffer[2] = {'\0', };
330                 for (const auto& observer : mObservers) {
331                         if (observer) {
332                                 if (!observer->on_streaming_audio_data(
333                                         WAKEUP_SPEECH_STREAMING_EVENT_FINISH, final_buffer, sizeof(final_buffer))) {
334                                         LOGE("[Recorder WARNING] One of the observer returned false");
335                                 }
336                         }
337                 }
338         }
339 }
340
341 void CWakeupEngineManager::start_streaming_current_utterance_data()
342 {
343         if (mStreamingThread.joinable()) {
344                 MWR_LOGE("ERROR : mStreamingThread is joinable, will not start a new thread");
345                 return;
346         }
347         mStreamingThread = thread(&CWakeupEngineManager::streaming_speech_data_thread_func, this);
348 }
349
350 void CWakeupEngineManager::stop_streaming_current_utterance_data()
351 {
352         if (mStreamingThread.joinable()) {
353                 MWR_LOGD("mStreamingThread is joinable, trying join()");
354                 mStopStreamingThread.store(true);
355                 mStreamingThread.join();
356         }
357         mStopStreamingThread.store(false);
358 }
359
360 void CWakeupEngineManager::update_manager_state(wakeup_manager_state_e state)
361 {
362         for (const auto& info : mEngineInfo) {
363                 if (info.interface.update_manager_state) {
364                         try {
365                                 info.interface.update_manager_state(state);
366                         } catch (const std::exception& e) {
367                                 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
368                                         info.engine_name.c_str(), e.what());
369                         }
370                 }
371         }
372 }
373
374 void CWakeupEngineManager::update_recognition_result(string appid, int result)
375 {
376         if (mSelectedEngine) {
377                 if (mSelectedEngine->interface.update_recognition_result) {
378                         mSelectedEngine->interface.update_recognition_result(appid.c_str(), result);
379                 }
380         }
381 }
382
383 void CWakeupEngineManager::engine_add_target_assistant(string engine_name, string appid)
384 {
385         const auto& iter = find_if(mEngineInfo.begin(), mEngineInfo.end(),
386                 [engine_name](const EngineInfo& info) {
387                         return (0 == info.engine_name.compare(engine_name));
388                 });
389
390         if (mEngineInfo.end() == iter) {
391                 /* Not found, add new library */
392                 pkgmgrinfo_appinfo_h handle;
393                 int ret = pkgmgrinfo_appinfo_get_appinfo(engine_name.c_str(), &handle);
394                 if (PMINFO_R_OK == ret) {
395                         char *root_path = nullptr;
396                         ret = pkgmgrinfo_appinfo_get_root_path(handle, &root_path);
397                         if (PMINFO_R_OK == ret && nullptr != root_path) {
398                                 string path = root_path;
399                                 path += "/";
400                                 path += MA_WAKEUP_DEDICATED_ENGINE_PATH;
401                                 add_engine(engine_name, path);
402                         }
403                         pkgmgrinfo_appinfo_destroy_appinfo(handle);
404                 }
405                 /* Find again to add appid to the newly created engine's assistant list */
406                 const auto &new_iter = find_if(mEngineInfo.begin(), mEngineInfo.end(),
407                         [engine_name](const EngineInfo& info) {
408                                 return (0 == info.engine_name.compare(engine_name));
409                         });
410                 if (mEngineInfo.end() != new_iter) {
411                         new_iter->assistant_list.push_back(appid);
412                 }
413         } else {
414                 /* If the engine already exists, simply add the appid to the assistant list */
415                 iter->assistant_list.push_back(appid);
416         }
417 }
418
419 void CWakeupEngineManager::engine_add_wakeup_word(string appid, string wakeup_word, string language)
420 {
421         for (const auto& info : mEngineInfo) {
422                 bool found = contains(info.assistant_list, appid);
423                 if (found) {
424                         if (info.interface.add_wakeup_word) {
425                                 try {
426                                         info.interface.add_wakeup_word(appid.c_str(), wakeup_word.c_str(), language.c_str());
427                                 } catch (const std::exception& e) {
428                                         MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
429                                                 info.engine_name.c_str(), e.what());
430                                 }
431                         }
432                 }
433         }
434 }
435
436 void CWakeupEngineManager::engine_set_assistant_specific_command(string appid, string command)
437 {
438         for (const auto& info : mEngineInfo) {
439                 bool found = contains(info.assistant_list, appid);
440                 if (found) {
441                         if (info.interface.set_assistant_specific_command) {
442                                 try {
443                                         info.interface.set_assistant_specific_command(appid.c_str(), command.c_str());
444                                 } catch (const std::exception& e) {
445                                         MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
446                                                 info.engine_name.c_str(), e.what());
447                                 }
448                         }
449                 }
450         }
451 }
452
453 void CWakeupEngineManager::engine_feed_audio_data(long time, void* data, int len)
454 {
455         for (const auto& info : mEngineInfo) {
456                 if (info.activated &&
457                         info.audio_data_require_status &&
458                         info.interface.feed_audio_data) {
459                         try {
460                                 int ret = info.interface.feed_audio_data(time, data, len);
461                                 if (0 != ret) {
462                                         LOGE("[ERROR] Fail to feed speech data, ret(%d) : %s", ret, info.engine_name.c_str());
463                                 }
464                         } catch (const std::exception& e) {
465                                 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
466                                         info.engine_name.c_str(), e.what());
467                         }
468                 }
469         }
470 }
471
472 bool CWakeupEngineManager::on_wakeup_event(string engine_name, wakeup_event_info info)
473 {
474         MWR_LOGD("[ENTER]");
475
476         for (const auto& observer : mObservers) {
477                 if (observer) {
478                         if (!observer->on_wakeup_event(engine_name, info)) {
479                                 LOGE("[Recorder WARNING] One of the observer returned false");
480                         }
481                 }
482         }
483
484         return true;
485 }
486
487 bool CWakeupEngineManager::on_speech_status(string engine_name, wakeup_service_speech_status_e status)
488 {
489         MWR_LOGD("[ENTER]");
490
491         for (const auto& observer : mObservers) {
492                 if (observer) {
493                         if (!observer->on_speech_status(engine_name, status)) {
494                                 LOGE("[Recorder WARNING] One of the observer returned false");
495                         }
496                 }
497         }
498
499         return true;
500 }
501
502 bool CWakeupEngineManager::on_error(string engine_name, int error_code, string error_message)
503 {
504         MWR_LOGD("[ENTER]");
505
506         for (const auto& observer : mObservers) {
507                 if (observer) {
508                         if (!observer->on_error(engine_name, error_code, error_message)) {
509                                 LOGE("[Recorder WARNING] One of the observer returned false");
510                         }
511                 }
512         }
513
514         return true;
515 }
516
517 bool CWakeupEngineManager::on_audio_data_require_status(string engine_name, bool require)
518 {
519         MWR_LOGD("[ENTER] %s, %d", engine_name.c_str(), require);
520
521         bool found = false;
522         // LOCK REQUIRED
523         int count = 0;
524         for (auto& info : mEngineInfo) {
525                 if (info.engine_name.compare(engine_name) == 0) {
526                         found = true;
527                         info.audio_data_require_status = require;
528                 }
529                 if (info.activated && info.audio_data_require_status) {
530                         count++;
531                 }
532         }
533         MWR_LOGD("count : %d", count);
534         if (count > 0) {
535                 mAudioDataRequired = true;
536         } else {
537                 mAudioDataRequired = false;
538         }
539
540         if (found) {
541                 for (const auto& observer : mObservers) {
542                         if (observer) {
543                                 if (!observer->on_audio_data_require_status(engine_name, require)) {
544                                         LOGE("[Recorder WARNING] One of the observer returned false");
545                                 }
546                         }
547                 }
548         }
549         // UNLOCK REQUIRED
550         return true;
551 }
552
553 void CWakeupEngineManager::add_engine(string name, string path)
554 {
555         MWR_LOGD("Name (%s), Filepath(%s)", name.c_str(), path.c_str());
556
557         char* error = NULL;
558         EngineInfo info;
559         info.engine_handle = dlopen(path.c_str(), RTLD_LAZY);
560         if (nullptr != (error = dlerror()) || nullptr == info.engine_handle) {
561                 MWR_LOGD("[ERROR] Fail to dlopen(%s), error(%s)", path.c_str(), error);
562                 if (info.engine_handle) dlclose(info.engine_handle);
563                 return;
564         }
565
566         /* Interfaces without version information */
567         info.interface.initialize =
568                 (wakeup_engine_initialize)dlsym(info.engine_handle,
569                 MA_WAKEUP_ENGINE_FUNC_INITIALIZE);
570         info.interface.deinitialize =
571                 (wakeup_engine_deinitialize)dlsym(info.engine_handle,
572                 MA_WAKEUP_ENGINE_FUNC_DEINITIALIZE);
573         info.interface.activate =
574                 (wakeup_engine_activate)dlsym(info.engine_handle,
575                 MA_WAKEUP_ENGINE_FUNC_ACTIVATE);
576         info.interface.deactivate =
577                 (wakeup_engine_deactivate)dlsym(info.engine_handle,
578                 MA_WAKEUP_ENGINE_FUNC_DEACTIVATE);
579         info.interface.add_wakeup_word =
580                 (wakeup_engine_add_wakeup_word)dlsym(info.engine_handle,
581                 MA_WAKEUP_ENGINE_FUNC_ADD_WAKEUP_WORD);
582         info.interface.add_language =
583                 (wakeup_engine_add_language)dlsym(info.engine_handle,
584                 MA_WAKEUP_ENGINE_FUNC_ADD_LANGUAGE);
585         info.interface.set_language =
586                 (wakeup_engine_set_language)dlsym(info.engine_handle,
587                 MA_WAKEUP_ENGINE_FUNC_SET_LANGUAGE);
588         info.interface.update_manager_state =
589                 (wakeup_engine_update_manager_state)dlsym(info.engine_handle,
590                 MA_WAKEUP_ENGINE_FUNC_UPDATE_MANAGER_STATE);
591         info.interface.update_recognition_result =
592                 (wakeup_engine_update_recognition_result)dlsym(info.engine_handle,
593                 MA_WAKEUP_ENGINE_FUNC_UPDATE_RECOGNITION_RESULT);
594         info.interface.set_audio_format =
595                 (wakeup_engine_set_audio_format)dlsym(info.engine_handle,
596                 MA_WAKEUP_ENGINE_FUNC_SET_AUDIO_FORMAT);
597         info.interface.get_audio_format =
598                 (wakeup_engine_get_audio_format)dlsym(info.engine_handle,
599                 MA_WAKEUP_ENGINE_FUNC_GET_AUDIO_FORMAT);
600         info.interface.feed_audio_data =
601                 (wakeup_engine_feed_audio_data)dlsym(info.engine_handle,
602                 MA_WAKEUP_ENGINE_FUNC_FEED_AUDIO_DATA);
603         info.interface.get_utterance_data_count =
604                 (wakeup_engine_get_utterance_data_count)dlsym(info.engine_handle,
605                 MA_WAKEUP_ENGINE_FUNC_GET_UTTERANCE_DATA_COUNT);
606         info.interface.get_utterance_data =
607                 (wakeup_engine_get_utterance_data)dlsym(info.engine_handle,
608                 MA_WAKEUP_ENGINE_FUNC_GET_UTTERANCE_DATA);
609         info.interface.get_wake_word_data_count =
610                 (wakeup_engine_get_wake_word_data_count)dlsym(info.engine_handle,
611                 MA_WAKEUP_ENGINE_FUNC_GET_WAKE_WORD_DATA_COUNT);
612         info.interface.get_wake_word_data =
613                 (wakeup_engine_get_wake_word_data)dlsym(info.engine_handle,
614                 MA_WAKEUP_ENGINE_FUNC_GET_WAKE_WORD_DATA);
615         info.interface.set_assistant_specific_command =
616                 (wakeup_engine_set_assistant_specific_command)dlsym(info.engine_handle,
617                 MA_WAKEUP_ENGINE_FUNC_SET_ASSISTANT_SPECIFIC_COMMAND);
618         info.interface.set_wake_word_audio_require_flag =
619                 (wakeup_engine_set_wake_word_audio_require_flag)dlsym(info.engine_handle,
620                 MA_WAKEUP_ENGINE_FUNC_SET_WAKE_WORD_AUDIO_REQUIRE_FLAG);
621         info.interface.set_wakeup_event_callback =
622                 (wakeup_engine_set_wakeup_event_callback)dlsym(info.engine_handle,
623                 MA_WAKEUP_ENGINE_FUNC_SET_WAKEUP_EVENT_CALLBACK);
624         info.interface.set_speech_status_callback =
625                 (wakeup_engine_set_speech_status_callback)dlsym(info.engine_handle,
626                 MA_WAKEUP_ENGINE_FUNC_SET_SPEECH_STATUS_CALLBACK);
627         info.interface.set_error_callback =
628                 (wakeup_engine_set_error_callback)dlsym(info.engine_handle,
629                 MA_WAKEUP_ENGINE_FUNC_SET_ERROR_CALLBACK);
630         info.interface.set_audio_data_require_status_callback =
631                 (wakeup_engine_set_audio_data_require_status_callback)dlsym(info.engine_handle,
632                 MA_WAKEUP_ENGINE_FUNC_SET_AUDIO_DATA_REQUIRE_STATUS_CALLBACK);
633
634         /* Interfaces after version 1 */
635         info.interface.get_version =
636                 (wakeup_engine_get_version)dlsym(info.engine_handle,
637                 MA_WAKEUP_ENGINE_FUNC_GET_VERSION);
638
639         info.version = 0;
640         info.engine_path = path;
641         info.engine_name = name;
642
643         info.activated = false;
644         info.audio_data_require_status = false;
645
646         /* All the necessary information has already been set properly */
647         mEngineInfo.push_back(info);
648
649         MWR_LOGD("Initializing wakeup engine : %s %p",
650                 info.engine_path.c_str(),
651                 info.interface.initialize);
652
653         /* Workaround for registering C-style callbacks */
654         typedef struct {
655                 CWakeupEngineManager *manager;
656                 string engine_name;
657         } CallbackUserData;
658
659         static deque<CallbackUserData> callback_user_data;
660
661         CallbackUserData user_data;
662         user_data.manager = this;
663         user_data.engine_name = info.engine_name;
664         callback_user_data.push_back(user_data);
665
666         try {
667                 if (info.interface.set_wakeup_event_callback) {
668                         info.interface.set_wakeup_event_callback(
669                                 [](wakeup_event_info info, void* user_data) {
670                                         CallbackUserData *data = static_cast<CallbackUserData*>(user_data);
671                                         if (nullptr == data) return;
672                                         if (nullptr == data->manager) return;
673                                         info.wakeup_engine = data->engine_name.c_str();
674                                         data->manager->on_wakeup_event(data->engine_name, info);
675                                 }, &(callback_user_data.back()));
676                 }
677
678                 if (info.interface.set_audio_data_require_status_callback) {
679                         info.interface.set_audio_data_require_status_callback(
680                                 [](bool require, void* user_data) {
681                                         CallbackUserData *data = static_cast<CallbackUserData*>(user_data);
682                                         if (nullptr == data) return;
683                                         if (nullptr == data->manager) return;
684                                         data->manager->on_audio_data_require_status(data->engine_name, require);
685                                 }, &(callback_user_data.back()));
686                 }
687
688                 if (info.interface.initialize) {
689                         info.interface.initialize();
690                 }
691                 if (info.interface.get_version) {
692                         int version;
693                         if (0 == info.interface.get_version(&version)) {
694                                 info.version = version;
695                         }
696                 }
697         } catch (const std::exception& e) {
698                 MWR_LOGE("[ERROR] wakeup engine %s threw exception : %s",
699                         info.engine_name.c_str(), e.what());
700         }
701 }
702
703 void CWakeupEngineManager::add_engine_directory(string name, string path)
704 {
705         if (0 == path.size()) return;
706
707         DIR* dp = opendir(path.c_str());
708         if (NULL == dp) {
709                 MWR_LOGD("Failed opening directory : %s", path.c_str());
710         } else {
711                 struct dirent *dirp = NULL;
712                 string filepath;
713                 do {
714                         dirp = readdir(dp);
715
716                         if (NULL != dirp) {
717                                 if (!strcmp(".", dirp->d_name) || !strcmp("..", dirp->d_name))
718                                         continue;
719
720                                 if (DT_REG != dirp->d_type) /* If not a regular file */
721                                         continue;
722
723                                 filepath = path;
724                                 filepath += "/";
725                                 filepath += dirp->d_name;
726
727                                 if (filepath.length() >= _POSIX_PATH_MAX) {
728                                         MWR_LOGD("File path is too long : %s", filepath.c_str());
729                                         closedir(dp);
730                                         return;
731                                 }
732                                 add_engine(name, filepath);
733                         }
734                 } while (NULL != dirp);
735
736                 closedir(dp);
737         }
738 }
739
740 } // wakeup
741 } // multiassistant