2 * Copyright 2018 Samsung Electronics Co., Ltd
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
8 * http://floralicense.org/license/
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.
18 #include <service_app.h>
19 #include <app_manager.h>
24 #include <message_port.h>
31 #include "multi_assistant_main.h"
32 #include "multi_assistant_service.h"
33 #include "multi_assistant_service_plugin.h"
34 #include "multi_assistant_dbus.h"
35 #include "multi_assistant_config.h"
37 static const char *g_current_lang = "en_US";
38 static int g_local_port_id = -1;
40 #define MAX_MACLIENT_INFO_NUM 16
41 #define MAX_WAKEUP_WORDS_NUM 4
42 #define MAX_WAKEUP_WORD_LEN 255
45 char appid[MAX_APPID_LEN];
46 char wakeup_word[MAX_WAKEUP_WORDS_NUM][MAX_WAKEUP_WORD_LEN];
49 static ma_client_info g_maclient_info[MAX_MACLIENT_INFO_NUM];
53 char appid[MAX_APPID_LEN];
56 static int g_current_maclient_info = 0;
57 static const char *g_wakeup_maclient_appid = NULL;
60 static GSList* g_client_list = NULL;
62 static int g_temp_speech_data_requested = 0;
63 void ma_client_set_temp_speech_data_requested(int val)
65 g_temp_speech_data_requested = val;
67 int ma_client_get_temp_speech_data_requested()
69 return g_temp_speech_data_requested;
72 int ma_client_create(ma_client_s *info)
75 MAS_LOGE("Input parameter is NULL"); //LCOV_EXCL_LINE
79 ma_client_s* data = NULL;
81 data = (ma_client_s*)calloc(1, sizeof(ma_client_s));
83 MAS_LOGE("[ERROR] Fail to allocate memory"); //LCOV_EXCL_LINE
84 return -1;// MA_ERROR_OUT_OF_MEMORY;
89 g_client_list = g_slist_append(g_client_list, data);
94 int ma_client_destroy(ma_client_s *client)
97 MAS_LOGE("Input parameter is NULL"); //LCOV_EXCL_LINE
98 return -1;// MA_ERROR_OPERATION_FAILED;
101 g_client_list = g_slist_remove(g_client_list, client);
109 ma_client_s* ma_client_find_by_appid(const char *appid)
112 MAS_LOGE("Input parameter is NULL"); //LCOV_EXCL_LINE
116 ma_client_s *data = NULL;
118 int count = g_slist_length(g_client_list);
121 for (i = 0; i < count; i++) {
122 data = g_slist_nth_data(g_client_list, i);
125 if (0 == strncmp(data->appid, appid, MAX_APPID_LEN)) {
131 MAS_LOGE("[ERROR] client Not found");
136 ma_client_s* ma_client_find_by_pid(int pid)
138 ma_client_s *data = NULL;
140 int count = g_slist_length(g_client_list);
143 for (i = 0; i < count; i++) {
144 data = g_slist_nth_data(g_client_list, i);
147 if (data->pid == pid) {
153 MAS_LOGE("[ERROR] client Not found");
158 int mas_client_initialize(int pid)
160 MAS_LOGD("[Enter] pid(%d)", pid);
162 char appid[MAX_APPID_LEN] = {'\0',};
163 if (AUL_R_OK == aul_app_get_appid_bypid(pid, appid, sizeof(appid))) {
164 appid[MAX_APPID_LEN - 1] = '\0';
166 MAS_LOGD("appid for pid %d is : %s", pid, (appid ? appid : "Unknown"));
168 /* Remove existing client that has same appid, if there's any */
169 ma_client_s *old_client = NULL;
170 old_client = ma_client_find_by_appid(appid);
172 ma_client_destroy(old_client);
176 /* And remove a client that has same pid also */
177 old_client = ma_client_find_by_pid(pid);
179 ma_client_destroy(old_client);
183 ma_client_s new_client;
184 new_client.pid = pid;
185 strncpy(new_client.appid, appid, MAX_APPID_LEN);
186 new_client.appid[MAX_APPID_LEN - 1] = '\0';
187 ma_client_create(&new_client);
189 const char *current_maclient_appid = NULL;
190 if (g_current_maclient_info >= 0 && g_current_maclient_info < MAX_MACLIENT_INFO_NUM) {
191 current_maclient_appid = g_maclient_info[g_current_maclient_info].appid;
194 if (current_maclient_appid && 0 == strncmp(current_maclient_appid, appid, MAX_APPID_LEN)) {
195 MAS_LOGD("MA client with current maclient appid connected!");
197 if (g_wakeup_maclient_appid && strncmp(g_wakeup_maclient_appid, appid, MAX_APPID_LEN) == 0) {
198 g_wakeup_maclient_appid = NULL;
199 mas_client_request_speech_data(pid);
201 MAS_LOGE("[ERROR] g_wakeup_maclient_appid and appid differ : %s %s",
202 (g_wakeup_maclient_appid ? g_wakeup_maclient_appid : "NULL"), appid);
205 MAS_LOGD("MA client connected, but its appid does not match with current maclient");
208 MAS_LOGE("[ERROR] Fail to retrieve appid");
214 int mas_client_deinitialize(int pid)
216 MAS_LOGD("[Enter] pid(%d)", pid);
217 ma_client_s *client = ma_client_find_by_pid(pid);
219 ma_client_destroy(client);
225 int mas_client_get_audio_format(int pid, int* rate, int* channel, int* audio_type)
227 MAS_LOGD("[Enter] pid(%d)", pid);
229 int ret = multi_assistant_service_plugin_get_recording_audio_format(rate, channel, audio_type);
231 MAS_LOGE("[ERROR] Fail to get recording audio format, ret(%d)", ret);
237 int mas_client_request_speech_data(int pid)
240 MAS_LOGD("[Enter] pid(%d)", pid);
242 char appid[MAX_APPID_LEN] = {'\0',};
243 if (AUL_R_OK == aul_app_get_appid_bypid(pid, appid, sizeof(appid))) {
244 appid[MAX_APPID_LEN - 1] = '\0';
245 const char *current_maclient_appid = NULL;
246 if (g_current_maclient_info >= 0 && g_current_maclient_info < MAX_MACLIENT_INFO_NUM) {
247 current_maclient_appid = g_maclient_info[g_current_maclient_info].appid;
250 if (current_maclient_appid && 0 == strncmp(current_maclient_appid, appid, MAX_APPID_LEN)) {
251 MAS_LOGD("appid %s matches with current MA Client, requesting speech data", appid);
252 ma_client_set_temp_speech_data_requested(1);
253 ret = wakeup_service_request_speech_data();
255 MAS_LOGE("[ERROR] Fail to request speech data(%d)", ret);
258 MAS_LOGE("appid %s does not match with current MA Client", appid);
265 int mas_client_send_asr_result(int pid, int event, char* asr_result)
267 MAS_LOGD("[Enter] pid(%d), event(%d), asr_result(%s)", pid, event, asr_result);
268 int ret = masc_ui_dbus_send_asr_result(pid, event, asr_result);
270 MAS_LOGE("[ERROR] Fail to send asr result, ret(%d)", ret);
273 // if final event is , launch assistant app which is invoked with wakeup word.
278 Eina_Bool __start_recording(void *data)
282 MAS_LOGD("[Enter] Start Recording again");
283 int ret = multi_assistant_service_plugin_start_recording();
285 MAS_LOGE("[ERROR] Fail to start recording, ret(%d)", ret);
290 int mas_client_send_result(int pid, char* display_text, char* utterance_text, char* result_json)
292 MAS_LOGD("[Enter] pid(%d), display_text(%s), utterance_text(%s), result_json(%s)", pid, display_text, utterance_text, result_json);
293 int ret = masc_ui_dbus_send_result(pid, display_text, utterance_text, result_json);
295 MAS_LOGE("[ERROR] Fail to send result, ret(%d)", ret);
298 ecore_timer_add(0, __start_recording, NULL);
303 int mas_ui_client_initialize(int pid)
305 MAS_LOGD("[Enter] pid(%d)", pid);
310 int mas_ui_client_deinitialize(int pid)
312 MAS_LOGD("[Enter] pid(%d)", pid);
317 int mas_ui_client_change_assistant(const char* appid)
322 MAS_LOGE("NULL parameter");
326 /* We are going to terminate existing clients for testing purpose */
327 int pid = mas_get_current_client_pid();
329 ma_client_s *client = ma_client_find_by_pid(pid);
330 if (client && strncmp(appid, client->appid, MAX_APPID_LEN) != 0) {
331 int ret = aul_terminate_pid(pid);
332 if (ret != AUL_R_OK) {
333 MAS_LOGW("aul_terminate_pid for pid %d returned %d", pid, ret);
338 mas_set_current_client_by_appid(appid);
339 if (mas_get_client_pid_by_appid(appid) != -1) {
340 MAS_LOGD("MA Client with appid %s exists, requesting speech data", (appid ? appid : "NULL"));
341 ma_client_set_temp_speech_data_requested(1);
342 int ret = wakeup_service_request_speech_data();
344 MAS_LOGE("[ERROR] Fail to request speech data(%d)", ret);
347 // Appropriate MA Client not available, trying to launch new one
348 MAS_LOGD("MA Client with appid %s does not exist, launching client", (appid ? appid : "NULL"));
350 /* The appid parameter might not exist after this function call, so we use appid string in our g_maclient_info */
351 for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
352 if (g_maclient_info[loop].used &&
353 0 < strlen(g_maclient_info[loop].appid) &&
354 0 < strlen(g_maclient_info[loop].wakeup_word[0])) {
355 if (strncmp(appid, g_maclient_info[loop].appid, MAX_APPID_LEN) == 0) {
356 mas_launch_client_by_appid(g_maclient_info[loop].appid);
365 /* for message port - receiver */
366 void __message_port_cb(int local_port_id, const char* remote_app_id, const char* remote_port, bool trusted_remote_port, bundle* message, void* user_data)
368 char *command = NULL;
369 bundle_get_str(message, "command", &command);
371 MAS_LOGD("Message from %s, command: %s", remote_app_id, command);
373 if (!strncmp(command, "start", strlen(command))) {
374 multi_assistant_service_plugin_start_recording();
375 } else if (!strncmp(command, "cancel", strlen(command))) {
376 multi_assistant_service_plugin_cancel_recording();
378 MAS_LOGE("[ERROR] command is wrong, command(%s)", command);
382 static void init_message_port(void)
384 MAS_LOGD("[Enter] init_message_port");
386 g_local_port_id = message_port_register_local_port("multi_assistant_service_message_port", __message_port_cb, NULL);
387 if (g_local_port_id < 0)
388 MAS_LOGE("[ERROR] message port register error: %d", g_local_port_id);
390 MAS_LOGD("[DEBUG] port id: %d", g_local_port_id);
393 static void deinit_message_port(void)
395 message_port_unregister_local_port(g_local_port_id);
398 int __mas_assistant_info_cb(const char* appid, const char* name,
399 const char* icon_path, const char* wakeup_list[], int cnt_wakeup,
400 const char* supported_lang[], int cnt_lang, void* user_data) {
401 MAS_LOGD("__mas_assistant_info_cb called");
404 MAS_LOGD("app_id NULL, returning");
410 while(-1 == index && loop < MAX_MACLIENT_INFO_NUM) {
411 if (EINA_FALSE == g_maclient_info[loop].used) {
417 g_maclient_info[index].used = EINA_TRUE;
418 MAS_LOGD("app_id(%s)", appid);
419 strncpy(g_maclient_info[index].appid, appid, MAX_APPID_LEN);
420 g_maclient_info[index].appid[MAX_APPID_LEN - 1] = '\0';
422 for (loop = 0;loop < MAX_WAKEUP_WORDS_NUM;loop++) {
423 if (loop < cnt_wakeup && wakeup_list[loop]) {
424 MAS_LOGD("wakeup_list(%d)(%s)", loop, wakeup_list[loop]);
425 strncpy(g_maclient_info[index].wakeup_word[loop], wakeup_list[loop], MAX_WAKEUP_WORD_LEN);
426 g_maclient_info[index].wakeup_word[loop][MAX_WAKEUP_WORD_LEN - 1] = '\0';
428 strncpy(g_maclient_info[index].wakeup_word[loop], "", MAX_WAKEUP_WORD_LEN);
432 MAS_LOGD("Couldn't find an empty slot for storing assistant info");
435 MAS_LOGD("__mas_assistant_info_cb end");
441 static int init_wakeup(void)
443 MAS_LOGD("[Enter] init_wakeup ");
445 int ret = mas_dbus_open_connection();
447 MAS_LOGE("[ERROR] Fail to open connection");
450 if (0 != multi_assistant_service_plugin_initialize()) {
451 MAS_LOGE("Fail to ws intialize");
455 if (0 != multi_assistant_service_plugin_set_language(g_current_lang)) {
456 MAS_LOGE("Fail to ws set language");
460 if (0 == mas_config_get_assistant_info(__mas_assistant_info_cb, NULL)) {
461 for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
462 if (g_maclient_info[loop].used &&
463 0 < strlen(g_maclient_info[loop].appid)) {
464 for (int inner_loop = 0; inner_loop < MAX_WAKEUP_WORDS_NUM; inner_loop++) {
465 if (0 < strlen(g_maclient_info[loop].wakeup_word[inner_loop])) {
466 MAS_LOGD("Registering wakeup word %s for app %s",
467 g_maclient_info[loop].wakeup_word[inner_loop], g_maclient_info[loop].appid);
468 if (0 != multi_assistant_service_plugin_set_wakeup_word(
469 "en_US", g_maclient_info[loop].wakeup_word[inner_loop])) {
470 MAS_LOGE("Fail to stt wakeup word");
477 MAS_LOGE("Fail to load assistant info");
480 if (0 != multi_assistant_service_plugin_set_callbacks()) {
481 MAS_LOGE("Fail to set callbacks");
485 if (0 != multi_assistant_service_plugin_start_recording()) {
486 MAS_LOGE("Fail to ws start recording");
495 static void deinit_wakeup(void)
497 MAS_LOGD("[Enter] deinit_wakeup ");
499 /* if (NULL != g_current_lang) {
500 free(g_current_lang);
501 g_current_lang = NULL;
504 deinit_message_port();
506 int ret = mas_dbus_close_connection();
508 MAS_LOGE("[ERROR] Fail to close connection");
511 if (0 != multi_assistant_service_plugin_deinitialize()) {
512 MAS_LOGE("Fail to ws deinitialize");
516 int mas_get_current_client_pid()
519 if (g_current_maclient_info >= 0 && g_current_maclient_info < MAX_MACLIENT_INFO_NUM) {
520 const char *current_maclient_appid = g_maclient_info[g_current_maclient_info].appid;
521 ma_client_s* client = ma_client_find_by_appid(current_maclient_appid);
529 int mas_get_client_pid_by_appid(const char *appid)
534 ma_client_s *client = NULL;
535 client = ma_client_find_by_appid(appid);
544 int mas_get_client_pid_by_wakeup_word(const char *wakeup_word)
546 const char *appid = mas_get_client_appid_by_wakeup_word(wakeup_word);
547 return mas_get_client_pid_by_appid(appid);
550 const char* mas_get_client_appid_by_wakeup_word(const char *wakeup_word)
553 const char *appid = NULL;
555 if (NULL == wakeup_word) return NULL;
557 for (loop = 0; loop < MAX_MACLIENT_INFO_NUM && NULL == appid; loop++) {
558 if (g_maclient_info[loop].used &&
559 0 < strlen(g_maclient_info[loop].appid)) {
560 for (int inner_loop = 0; inner_loop < MAX_WAKEUP_WORDS_NUM; inner_loop++) {
561 if (0 < strlen(g_maclient_info[loop].wakeup_word[inner_loop])) {
562 if (0 == strncmp(wakeup_word, g_maclient_info[loop].wakeup_word[inner_loop], MAX_WAKEUP_WORD_LEN)) {
563 appid = g_maclient_info[loop].appid;
570 /* Perform extended search, by eliminating blank characters */
572 for (loop = 0; loop < MAX_MACLIENT_INFO_NUM && NULL == appid; loop++) {
573 if (g_maclient_info[loop].used &&
574 0 < strlen(g_maclient_info[loop].appid)) {
575 for (int inner_loop = 0; inner_loop < MAX_WAKEUP_WORDS_NUM; inner_loop++) {
576 if (0 < strlen(g_maclient_info[loop].wakeup_word[inner_loop])) {
577 char comparand[MAX_WAKEUP_WORD_LEN];
578 int comparand_index = 0;
579 for (int index = 0; index < MAX_WAKEUP_WORD_LEN; index++) {
580 if (' ' != g_maclient_info[loop].wakeup_word[inner_loop][index]) {
581 comparand[comparand_index++] = g_maclient_info[loop].wakeup_word[inner_loop][index];
584 if (0 == strncmp(wakeup_word, comparand, MAX_WAKEUP_WORD_LEN)) {
585 appid = g_maclient_info[loop].appid;
596 int mas_set_current_client_by_wakeup_word(const char *wakeup_word)
601 for (loop = 0; loop < MAX_MACLIENT_INFO_NUM && -1 == ret; loop++) {
602 if (g_maclient_info[loop].used &&
603 0 < strlen(g_maclient_info[loop].appid)) {
604 for (int inner_loop = 0; inner_loop < MAX_WAKEUP_WORDS_NUM; inner_loop++) {
605 if (0 < strlen(g_maclient_info[loop].wakeup_word[inner_loop])) {
606 if (0 == strncmp(wakeup_word, g_maclient_info[loop].wakeup_word[inner_loop], MAX_WAKEUP_WORD_LEN)) {
607 g_current_maclient_info = loop;
614 /* Perform extended search, by eliminating blank characters */
616 for (loop = 0; loop < MAX_MACLIENT_INFO_NUM && -1 == ret; loop++) {
617 if (g_maclient_info[loop].used &&
618 0 < strlen(g_maclient_info[loop].appid)) {
619 for (int inner_loop = 0; inner_loop < MAX_WAKEUP_WORDS_NUM; inner_loop++) {
620 if (0 < strlen(g_maclient_info[loop].wakeup_word[inner_loop])) {
621 char comparand[MAX_WAKEUP_WORD_LEN];
622 int comparand_index = 0;
623 for (int index = 0; index < MAX_WAKEUP_WORD_LEN; index++) {
624 if (' ' != g_maclient_info[loop].wakeup_word[inner_loop][index]) {
625 comparand[comparand_index++] = g_maclient_info[loop].wakeup_word[inner_loop][index];
628 if (0 == strncmp(wakeup_word, comparand, MAX_WAKEUP_WORD_LEN)) {
629 g_current_maclient_info = loop;
641 int mas_set_current_client_by_appid(const char *appid)
645 for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
646 if (g_maclient_info[loop].used &&
647 0 < strlen(g_maclient_info[loop].appid) &&
648 0 < strlen(g_maclient_info[loop].wakeup_word[0])) {
649 if (strncmp(appid, g_maclient_info[loop].appid, MAX_APPID_LEN) == 0) {
650 g_current_maclient_info = loop;
657 int mas_launch_client_by_appid(const char *appid)
659 app_control_h app_control;
662 if (NULL == appid || 0 == strlen(appid)) {
663 MAS_LOGE("appid invalid, failed launching MA Client");
667 ret = app_control_create(&app_control);
668 if (APP_CONTROL_ERROR_NONE != ret) {
669 MAS_LOGW("app_control_create returned %08x", ret);
673 ret = app_control_set_operation(app_control, APP_CONTROL_OPERATION_DEFAULT);
674 if (APP_CONTROL_ERROR_NONE != ret) {
675 MAS_LOGW("app_control_set_operation returned %08x", ret);
676 app_control_destroy(app_control);
680 /* These extra data key/value should be defined somewhere in multi-assistant framework */
681 ret = app_control_add_extra_data(app_control, "request_speech_data", "true");
682 if (APP_CONTROL_ERROR_NONE != ret) {
683 MAS_LOGW("app_control_add_extra_data returned %08x", ret);
684 app_control_destroy(app_control);
688 ret = app_control_set_app_id(app_control, appid);
689 if (APP_CONTROL_ERROR_NONE != ret) {
690 MAS_LOGW("app_control_set_app_id returned %08x", ret);
691 app_control_destroy(app_control);
695 ret = app_control_send_launch_request(app_control, NULL, NULL);
696 if (APP_CONTROL_ERROR_NONE != ret) {
697 MAS_LOGW ("app_control_send_launch_request returned %08x, app_id=%s", ret, appid);
699 app_control_destroy (app_control);
701 if (APP_CONTROL_ERROR_NONE == ret) {
702 g_wakeup_maclient_appid = appid;
703 MAS_LOGD("g_wakeup_maclient_appid : %s", appid);
709 int mas_launch_client_by_wakeup_word(const char *wakeup_word)
711 const char *appid = mas_get_client_appid_by_wakeup_word(wakeup_word);
712 return mas_launch_client_by_appid(appid);
715 bool service_app_create(void *data)
717 // Todo: add your code here.
719 MAS_LOGD("[Enter] Service app create");
721 if (0 != init_wakeup()) {
722 MAS_LOGE("Fail to init wakeup service");
729 void service_app_terminate(void *data)
731 // Todo: add your code here.
736 void service_app_control(app_control_h app_control, void *data)
738 // Todo: add your code here.
743 service_app_lang_changed(app_event_info_h event_info, void *user_data)
745 /*APP_EVENT_LANGUAGE_CHANGED*/
750 service_app_region_changed(app_event_info_h event_info, void *user_data)
752 /*APP_EVENT_REGION_FORMAT_CHANGED*/
756 service_app_low_battery(app_event_info_h event_info, void *user_data)
758 /*APP_EVENT_LOW_BATTERY*/
762 service_app_low_memory(app_event_info_h event_info, void *user_data)
764 /*APP_EVENT_LOW_MEMORY*/
767 int main(int argc, char* argv[])
770 service_app_lifecycle_callback_s event_callback;
771 app_event_handler_h handlers[5] = {NULL, };
773 event_callback.create = service_app_create;
774 event_callback.terminate = service_app_terminate;
775 event_callback.app_control = service_app_control;
777 service_app_add_event_handler(&handlers[APP_EVENT_LOW_BATTERY], APP_EVENT_LOW_BATTERY, service_app_low_battery, &ad);
778 service_app_add_event_handler(&handlers[APP_EVENT_LOW_MEMORY], APP_EVENT_LOW_MEMORY, service_app_low_memory, &ad);
779 service_app_add_event_handler(&handlers[APP_EVENT_LANGUAGE_CHANGED], APP_EVENT_LANGUAGE_CHANGED, service_app_lang_changed, &ad);
780 service_app_add_event_handler(&handlers[APP_EVENT_REGION_FORMAT_CHANGED], APP_EVENT_REGION_FORMAT_CHANGED, service_app_region_changed, &ad);
782 return service_app_main(argc, argv, &event_callback, ad);