Merge commit 'b9b5dd3' into tizen 50/230550/1
authorJi-hoon Lee <dalton.lee@samsung.com>
Fri, 10 Apr 2020 12:33:02 +0000 (21:33 +0900)
committerJi-hoon Lee <dalton.lee@samsung.com>
Fri, 10 Apr 2020 12:33:02 +0000 (21:33 +0900)
Change-Id: I94abe17b257facb23833bade02a32a6024d7fa2e

1  2 
src/service_main.cpp

index 8fd818a,0000000..c494304
mode 100644,000000..100644
--- /dev/null
@@@ -1,1479 -1,0 +1,1484 @@@
 +/*
 + * Copyright 2020 Samsung Electronics Co., Ltd
 + *
 + * Licensed under the Flora License, Version 1.1 (the License);
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at
 + *
 + * http://floralicense.org/license/
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an AS IS BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +#include <tizen.h>
 +#include <service_app.h>
 +#include <app_manager.h>
 +#include <app.h>
 +#include <malloc.h>
 +#include <Ecore.h>
 +#include <vconf.h>
 +#include <pkgmgr-info.h>
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <string.h>
 +#include <glib.h>
 +
 +#include "service_common.h"
 +#include "service_main.h"
 +#include "service_plugin.h"
 +#include "service_ipc_dbus.h"
 +#include "service_config.h"
 +
 +static CServiceMain* g_service_main = nullptr;
 +
 +bool CServiceMain::check_preprocessing_assistant_exists()
 +{
 +      bool ret = false;
 +
 +      boost::optional<std::string> preprocessing_appid =
 +              mPreferenceManager.get_string(WAKEUP_SETTINGS_KEY_PREPROCESSING_ASSISTANT_APPID);
 +      if (preprocessing_appid) {
 +              for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
 +                      if (mClientInfo[loop].used &&
 +                              strncmp((*preprocessing_appid).c_str(), mClientInfo[loop].appid, MAX_APPID_LEN) == 0) {
 +                              if (mClientManager.check_client_validity_by_appid(*preprocessing_appid)) {
 +                                      ret = true;
 +                              }
 +                      }
 +              }
 +      }
 +
 +      MAS_LOGD("result : %d", ret);
 +
 +      return ret;
 +}
 +
 +bool CServiceMain::is_current_preprocessing_assistant(const char* appid)
 +{
 +      if (NULL == appid) return false;
 +
 +      bool ret = false;
 +
 +      boost::optional<std::string> preprocessing_appid =
 +              mPreferenceManager.get_string(WAKEUP_SETTINGS_KEY_PREPROCESSING_ASSISTANT_APPID);
 +      if (preprocessing_appid) {
 +              if (strncmp((*preprocessing_appid).c_str(), appid, MAX_APPID_LEN) == 0) {
 +                      ret = true;
 +              }
 +      }
 +
 +      return ret;
 +}
 +
 +int CServiceMain::mas_client_get_audio_format(int pid, int* rate, int* channel, int* audio_type)
 +{
 +      MAS_LOGD("[Enter] pid(%d)", pid);
 +
 +      int ret = mServicePlugin.get_recording_audio_format(rate, channel, audio_type);
 +      if (0 != ret){
 +              MAS_LOGE("[ERROR] Fail to get recording audio format, ret(%d)", ret);
 +      }
 +
 +      return ret;
 +}
 +
 +#define MAX_LOCAL_VARIABLE_STRING_LEN 256
 +int CServiceMain::mas_client_get_audio_source_type(int pid, char** type)
 +{
 +      MAS_LOGD("[Enter] pid(%d)", pid);
 +
 +      if (NULL == type) return -1;
 +
 +      static char cached[MAX_LOCAL_VARIABLE_STRING_LEN] = {'\0'};
 +      int ret = mServicePlugin.get_recording_audio_source_type(type);
 +      if (0 != ret){
 +              MAS_LOGE("[ERROR] Fail to get recording audio source type, ret(%d)", ret);
 +              *type = cached;
 +      } else if (*type) {
 +              strncpy(cached, *type, MAX_LOCAL_VARIABLE_STRING_LEN - 1);
 +              cached[MAX_LOCAL_VARIABLE_STRING_LEN - 1] = '\0';
 +      }
 +
 +      return ret;
 +}
 +
 +int CServiceMain::mas_client_send_preprocessing_information(int pid)
 +{
 +      int ret = -1;
 +      MAS_LOGD("[Enter] pid(%d)", pid);
 +
 +      boost::optional<std::string> preprocessing_appid =
 +              mPreferenceManager.get_string(WAKEUP_SETTINGS_KEY_PREPROCESSING_ASSISTANT_APPID);
 +      if (preprocessing_appid) {
 +              MAS_LOGD("preprocessing_assistant_appid : %s", (*preprocessing_appid).c_str());
 +              ret = mServiceIpc.send_preprocessing_information(pid, (*preprocessing_appid).c_str());
 +      }
 +
 +      return ret;
 +}
 +
 +int CServiceMain::mas_client_send_voice_key_status_change(int pid, ma_voice_key_status_e status)
 +{
 +      int ret = -1;
 +      MAS_LOGD("[Enter] pid(%d)", pid);
 +
 +      ret = mServiceIpc.change_voice_key_status(pid, status);
 +      if (0 != ret) {
 +              MAS_LOGE("[ERROR] Fail to send voice key status changed information, ret(%d)", ret);
 +      }
 +      mLastVoiceKeyStatus = status;
 +
 +      return ret;
 +}
 +
 +int CServiceMain::mas_client_send_asr_result(int pid, int event, const char* asr_result)
 +{
 +      MAS_LOGD("[Enter] pid(%d), event(%d), asr_result(%s)", pid, event, asr_result);
 +      int ret = mServiceIpc.masc_ui_dbus_send_asr_result(pid, event, asr_result);
 +      if (0 != ret){
 +              MAS_LOGE("[ERROR] Fail to send asr result, ret(%d)", ret);
 +      }
 +
 +      // if final event is , launch assistant app which is invoked with wakeup word.
 +      /* TO_DO */
 +      return ret;
 +}
 +
 +int CServiceMain::mas_client_send_result(int pid, const char* display_text,
 +      const char* utterance_text, const char* result_json)
 +{
 +      MAS_LOGD("[Enter] pid(%d), display_text(%s), utterance_text(%s), result_json(%s)", pid, display_text, utterance_text, result_json);
 +      int ret = mServiceIpc.masc_ui_dbus_send_result(pid, display_text, utterance_text, result_json);
 +      if (0 != ret){
 +              MAS_LOGE("[ERROR] Fail to send result, ret(%d)", ret);
 +      }
 +
 +      std::string pid_appid;
 +      boost::optional<std::string> appid_by_pid = mApplicationManager.get_appid_by_pid(pid);
 +      if (appid_by_pid) {
 +              pid_appid = *appid_by_pid;
 +      }
 +      mServicePlugin.update_recognition_result(pid_appid.c_str(), MA_RECOGNITION_RESULT_EVENT_SUCCESS);
 +
 +      return ret;
 +}
 +
 +int CServiceMain::mas_client_send_recognition_result(int pid, int result)
 +{
 +      MAS_LOGD("[Enter] pid(%d), result(%d)", pid, result);
 +      int ret = mServiceIpc.masc_ui_dbus_send_recognition_result(pid, result);
 +      if (0 != ret){
 +              MAS_LOGE("[ERROR] Fail to send recognition result, ret(%d)", ret);
 +      }
 +
 +      std::string pid_appid;
 +      boost::optional<std::string> appid_by_pid = mApplicationManager.get_appid_by_pid(pid);
 +      if (appid_by_pid) {
 +              pid_appid = *appid_by_pid;
 +      }
 +      mServicePlugin.update_recognition_result(pid_appid.c_str(), result);
 +
 +      return ret;
 +}
 +
 +int CServiceMain::mas_client_start_streaming_audio_data(int pid, int type)
 +{
 +      int ret = -1;
 +      switch(type) {
 +              case MA_AUDIO_STREAMING_DATA_TYPE_CURRENT_UTTERANCE:
 +                      ret = mServicePlugin.start_streaming_utterance_data();
 +                      mas_process_preprocessing_state_event(PREPROCESSING_STATE_EVENT_UTTERANCE_STREAMING_STARTED);
 +                      break;
 +              case MA_AUDIO_STREAMING_DATA_TYPE_PREVIOUS_UTTERANCE:
 +                      ret = mServicePlugin.start_streaming_previous_utterance_data();
 +                      /* Preprocessing is not required for previous utterance streaming */
 +                      break;
 +              case MA_AUDIO_STREAMING_DATA_TYPE_FOLLOW_UP_SPEECH:
 +                      ret = mServicePlugin.start_streaming_follow_up_data();
 +                      mas_process_preprocessing_state_event(PREPROCESSING_STATE_EVENT_FOLLOW_UP_STREAMING_STARTED);
 +                      break;
 +      }
 +      return ret;
 +}
 +
 +int CServiceMain::mas_client_stop_streaming_audio_data(int pid, int type)
 +{
 +      int ret = -1;
 +      switch(type) {
 +              case MA_AUDIO_STREAMING_DATA_TYPE_CURRENT_UTTERANCE:
 +                      ret = mServicePlugin.stop_streaming_utterance_data();
 +                      break;
 +              case MA_AUDIO_STREAMING_DATA_TYPE_PREVIOUS_UTTERANCE:
 +                      ret = mServicePlugin.stop_streaming_previous_utterance_data();
 +                      break;
 +              case MA_AUDIO_STREAMING_DATA_TYPE_FOLLOW_UP_SPEECH:
 +                      ret = mServicePlugin.stop_streaming_follow_up_data();
 +                      break;
 +      }
 +      return ret;
 +}
 +
 +int CServiceMain::mas_client_update_voice_feedback_state(int pid, int state)
 +{
 +      std::string pid_appid;
 +      boost::optional<std::string> appid_by_pid = mApplicationManager.get_appid_by_pid(pid);
 +      if (appid_by_pid) {
 +              pid_appid = *appid_by_pid;
 +      }
 +      mServicePlugin.update_voice_feedback_state(pid_appid.c_str(), state);
 +      return 0;
 +}
 +
 +int CServiceMain::mas_client_set_assistant_specific_command(int pid, const char *command)
 +{
 +      std::string pid_appid;
 +      boost::optional<std::string> appid_by_pid = mApplicationManager.get_appid_by_pid(pid);
 +      if (appid_by_pid) {
 +              pid_appid = *appid_by_pid;
 +      }
 +      mServicePlugin.set_assistant_specific_command(pid_appid.c_str(), command);
 +      return 0;
 +}
 +
 +int CServiceMain::mas_client_set_background_volume(int pid, double ratio)
 +{
 +      std::string pid_appid;
 +      boost::optional<std::string> appid_by_pid = mApplicationManager.get_appid_by_pid(pid);
 +      if (appid_by_pid) {
 +              pid_appid = *appid_by_pid;
 +      }
 +      mServicePlugin.set_background_volume(pid_appid.c_str(), ratio);
 +      return 0;
 +}
 +
 +int CServiceMain::mas_client_set_preprocessing_allow_mode(int pid, ma_preprocessing_allow_mode_e mode, const char* appid)
 +{
 +      std::string pid_appid;
 +      boost::optional<std::string> appid_by_pid = mApplicationManager.get_appid_by_pid(pid);
 +      if (appid_by_pid) {
 +              pid_appid = *appid_by_pid;
 +      }
 +
 +      for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
 +              if (mClientInfo[loop].used) {
 +                      if (0 == pid_appid.compare(mClientInfo[loop].appid)) {
 +                              mClientInfo[loop].preprocessing_allow_mode = mode;
 +                              if (appid) {
 +                                      strncpy(mClientInfo[loop].preprocessing_allow_appid, appid, MAX_APPID_LEN);
 +                                      mClientInfo[loop].preprocessing_allow_appid[MAX_APPID_LEN - 1] = '\0';
 +                              } else {
 +                                      mClientInfo[loop].preprocessing_allow_appid[0] = '\0';
 +                              }
 +                      }
 +              }
 +      }
 +
 +      mas_process_preprocessing_state_event(PREPROCESSING_STATE_EVENT_PREPROCESSING_ALLOW_MODE_CHANGED);
 +
 +      return 0;
 +}
 +
 +int CServiceMain::mas_client_send_preprocessing_result(int pid, bool result)
 +{
 +      std::string pid_appid;
 +      boost::optional<std::string> appid_by_pid = mApplicationManager.get_appid_by_pid(pid);
 +      if (appid_by_pid) {
 +              pid_appid = *appid_by_pid;
 +      }
 +      if (!is_current_preprocessing_assistant(pid_appid.c_str())) return -1;
 +
 +      const char *current_maclient_appid = NULL;
 +      if (mCurrentClientInfo >= 0 && mCurrentClientInfo < MAX_MACLIENT_INFO_NUM) {
 +              current_maclient_appid = mClientInfo[mCurrentClientInfo].appid;
 +      }
 +
 +      if (result) {
 +              MAS_LOGD("Preprocessing succeeded, bring (%s) to foreground", pid_appid.c_str());
 +              mas_process_preprocessing_state_event(PREPROCESSING_STATE_EVENT_PREPROCESSING_SUCCEEDED);
 +      } else {
 +              MAS_LOGD("Preprocessing failed, bring (%s) to foreground", current_maclient_appid);
 +              mas_process_preprocessing_state_event(PREPROCESSING_STATE_EVENT_PREPROCESSING_FAILED);
 +      }
 +
 +      if (current_maclient_appid) {
 +              int pid_by_appid = mClientManager.find_client_pid_by_appid(
 +                      std::string{current_maclient_appid});
 +              mServiceIpc.send_preprocessing_result(pid_by_appid, result);
 +      }
 +
 +      return 0;
 +}
 +
 +int CServiceMain::mas_client_set_wake_word_audio_require_flag(int pid, bool require)
 +{
 +      std::string pid_appid;
 +      boost::optional<std::string> appid_by_pid = mApplicationManager.get_appid_by_pid(pid);
 +      if (appid_by_pid) {
 +              pid_appid = *appid_by_pid;
 +      }
 +
 +      mServicePlugin.set_wake_word_audio_require_flag(pid_appid.c_str(), require);
 +      return 0;
 +}
 +
 +int CServiceMain::mas_client_set_assistant_language(int pid, const char* language)
 +{
 +      std::string pid_appid;
 +      boost::optional<std::string> appid_by_pid = mApplicationManager.get_appid_by_pid(pid);
 +      if (appid_by_pid) {
 +              pid_appid = *appid_by_pid;
 +      }
 +
 +      mServicePlugin.set_assistant_language(pid_appid.c_str(), language);
 +      return 0;
 +}
 +
 +int CServiceMain::mas_client_add_wake_word(int pid, const char* wake_word, const char* language)
 +{
 +      std::string pid_appid;
 +      boost::optional<std::string> appid_by_pid = mApplicationManager.get_appid_by_pid(pid);
 +      if (appid_by_pid) {
 +              pid_appid = *appid_by_pid;
 +      }
 +
 +      for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
 +              if (mClientInfo[loop].used &&
 +                      0 == pid_appid.compare(mClientInfo[loop].appid)) {
 +                      int ret = mServiceConfig.add_custom_wake_word(wake_word, language,
 +                              mClientInfo[loop].wakeup_word,
 +                              mClientInfo[loop].wakeup_language);
 +                      if (0 == ret) {
 +                              mServiceConfig.save_custom_wake_words(pid_appid.c_str(),
 +                                      mClientInfo[loop].wakeup_word,
 +                                      mClientInfo[loop].wakeup_language);
 +                      } else {
 +                              LOGE("add new wake word failed!");
 +                              return -1;
 +                      }
 +              }
 +      }
 +
 +      mServicePlugin.add_assistant_wakeup_word(pid_appid.c_str(), wake_word, language);
 +      return 0;
 +}
 +
 +int CServiceMain::mas_client_remove_wake_word(int pid, const char* wake_word, const char* language)
 +{
 +      std::string pid_appid;
 +      boost::optional<std::string> appid_by_pid = mApplicationManager.get_appid_by_pid(pid);
 +      if (appid_by_pid) {
 +              pid_appid = *appid_by_pid;
 +      }
 +
 +      for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
 +              if (mClientInfo[loop].used &&
 +                      0 == pid_appid.compare(mClientInfo[loop].appid)) {
 +                      int ret = mServiceConfig.remove_custom_wake_word(wake_word, language,
 +                              mClientInfo[loop].wakeup_word,
 +                              mClientInfo[loop].wakeup_language);
 +                      if (0 == ret) {
 +                              mServiceConfig.save_custom_wake_words(pid_appid.c_str(),
 +                                      mClientInfo[loop].wakeup_word,
 +                                      mClientInfo[loop].wakeup_language);
 +                      }
 +              }
 +      }
 +
 +      mServicePlugin.remove_assistant_wakeup_word(pid_appid.c_str(), wake_word, language);
 +      return 0;
 +}
 +
 +int CServiceMain::mas_ui_client_initialize(int pid)
 +{
 +      MAS_LOGD("[Enter] pid(%d)", pid);
 +
 +      return 0;
 +}
 +
 +int CServiceMain::mas_ui_client_deinitialize(int pid)
 +{
 +      MAS_LOGD("[Enter] pid(%d)", pid);
 +
 +      return 0;
 +}
 +
 +int CServiceMain::mas_ui_client_change_assistant(const char* appid)
 +{
 +      MAS_LOGD("[Enter]");
 +
 +      if (NULL == appid) {
 +              MAS_LOGE("NULL parameter");
 +              return -1;
 +      }
 +
 +      bool use_custom_ui = mas_get_client_custom_ui_option_by_appid(appid);
 +      mServiceIpc.masc_ui_dbus_enable_common_ui(!use_custom_ui);
 +
 +      mas_set_current_client_by_appid(appid);
 +      int pid = mas_get_client_pid_by_appid(appid);
 +      if (pid != -1) {
 +              mas_bring_client_to_foreground(appid);
 +              mas_client_send_preprocessing_information(pid);
 +              if (MA_VOICE_KEY_STATUS_PRESSED == mLastVoiceKeyStatus) {
 +                      mas_client_send_voice_key_status_change(pid, mLastVoiceKeyStatus);
 +              }
 +
 +              mServiceIpc.change_active_state(pid, MA_ACTIVE_STATE_ACTIVE);
 +              MAS_LOGD("MA Client with appid %s exists, requesting speech data", (appid ? appid : "NULL"));
 +              /*
 +              int ret = mServicePlugin.start_streaming_utterance_data();
 +              if (0 != ret) {
 +                      MAS_LOGE("[ERROR] Fail to start streaming utterance data(%d)", ret);
 +              }
 +              */
 +      } else {
 +              // Appropriate MA Client not available, trying to launch new one
 +              MAS_LOGD("MA Client with appid %s does not exist, launching client", (appid ? appid : "NULL"));
 +
 +              /* The appid parameter might not exist after this function call, so we use appid string in our mClientInfo */
 +              for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
 +                      if (mClientInfo[loop].used &&
 +                              0 < strlen(mClientInfo[loop].appid) &&
 +                              0 < strlen(mClientInfo[loop].wakeup_word[0])) {
 +                              if (strncmp(appid, mClientInfo[loop].appid, MAX_APPID_LEN) == 0) {
 +                                      mas_launch_client_by_appid(mClientInfo[loop].appid, CLIENT_LAUNCH_MODE_ACTIVATION);
 +                              }
 +                      }
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +static int mas_assistant_info_cb(ma_assistant_info_s* info, void* user_data) {
 +      int ret = -1;
 +      CServiceMain* service_main = static_cast<CServiceMain*>(user_data);
 +      if (service_main) {
 +              ret = service_main->add_assistant_info(info);
 +      }
 +      return ret;
 +}
 +
 +int CServiceMain::add_assistant_info(ma_assistant_info_s* info) {
 +      MAS_LOGD("__mas_assistant_info_cb called");
 +
 +      if (NULL == info) {
 +              MAS_LOGE("info NULL, returning");
 +              return -1;
 +      }
 +      if (NULL == info->app_id) {
 +              MAS_LOGE("app_id NULL, returning");
 +              return -1;
 +      }
 +
 +      int index = -1;
 +      int loop = 0;
 +      while(-1 == index && loop < MAX_MACLIENT_INFO_NUM) {
 +              if (false == mClientInfo[loop].used) {
 +                      index = loop;
 +              }
 +              loop++;
 +      }
 +      if (-1 != index) {
 +              mClientInfo[index].used = true;
 +              mClientInfo[index].preprocessing_allow_mode = MA_PREPROCESSING_ALLOW_NONE;
 +              mClientInfo[index].preprocessing_allow_appid[0] = '\0';
 +              MAS_LOGD("app_id(%s)", info->app_id);
 +              strncpy(mClientInfo[index].appid, info->app_id, MAX_APPID_LEN);
 +              mClientInfo[index].appid[MAX_APPID_LEN - 1] = '\0';
 +
 +              if (is_current_preprocessing_assistant(mClientInfo[index].appid)) {
 +                      mCurrentPreprocessingClientInfo = index;
 +              }
 +
 +              for (loop = 0;loop < MAX_WAKEUP_WORDS_NUM;loop++) {
 +                      if (loop < info->cnt_wakeup && info->wakeup_list[loop]) {
 +                              MAS_LOGD("wakeup_list(%d)(%s)(%s)", loop, info->wakeup_list[loop], info->wakeup_language[loop]);
 +                              strncpy(mClientInfo[index].wakeup_word[loop], info->wakeup_list[loop], MAX_WAKEUP_WORD_LEN);
 +                              mClientInfo[index].wakeup_word[loop][MAX_WAKEUP_WORD_LEN - 1] = '\0';
 +                              if (info->wakeup_language[loop]) {
 +                                      strncpy(mClientInfo[index].wakeup_language[loop], info->wakeup_language[loop], MAX_SUPPORTED_LANGUAGE_LEN);
 +                                      mClientInfo[index].wakeup_language[loop][MAX_SUPPORTED_LANGUAGE_LEN - 1] = '\0';
 +                              } else {
 +                                      strncpy(mClientInfo[index].wakeup_language[loop], "", MAX_SUPPORTED_LANGUAGE_LEN);
 +                              }
 +                      } else {
 +                              strncpy(mClientInfo[index].wakeup_word[loop], "", MAX_WAKEUP_WORD_LEN);
 +                      }
 +              }
 +
 +              for (loop = 0;loop < MAX_SUPPORTED_LANGUAGES_NUM;loop++) {
 +                      if (loop < info->cnt_lang && info->supported_lang[loop]) {
 +                              MAS_LOGD("supported_lang(%d)(%s)", loop, info->supported_lang[loop]);
 +                              strncpy(mClientInfo[index].supported_language[loop], info->supported_lang[loop], MAX_SUPPORTED_LANGUAGE_LEN);
 +                              mClientInfo[index].supported_language[loop][MAX_SUPPORTED_LANGUAGE_LEN - 1] = '\0';
 +                      } else {
 +                              strncpy(mClientInfo[index].supported_language[loop], "", MAX_SUPPORTED_LANGUAGE_LEN);
 +                      }
 +              }
 +
 +              MAS_LOGD("wakeup_engine(%s)", info->wakeup_engine);
 +              if (info->wakeup_engine) {
 +                      strncpy(mClientInfo[index].wakeup_engine, info->wakeup_engine, MAX_APPID_LEN);
 +                      mClientInfo[index].wakeup_engine[MAX_APPID_LEN - 1] = '\0';
 +              } else {
 +                      mClientInfo[index].wakeup_engine[0] = '\0';
 +                      MAS_LOGW("Wakeup engine information not provided for : %s", info->app_id);
 +              }
 +              mClientInfo[index].custom_ui_option = info->custom_ui_option;
 +
 +              MAS_LOGD("voice_key_support_mode(%d)", info->voice_key_support_mode);
 +              mClientInfo[index].voice_key_support_mode = info->voice_key_support_mode;
 +              MAS_LOGD("voice_key_tap_duration(%f)", info->voice_key_tap_duration);
 +              mClientInfo[index].voice_key_tap_duration = info->voice_key_tap_duration;
 +      } else {
 +              MAS_LOGD("Couldn't find an empty slot for storing assistant info");
 +      }
 +
 +      MAS_LOGD("__mas_assistant_info_cb end");
 +
 +      return 0;
 +}
 +
 +static void active_state_changed_cb(std::string key, void* user_data)
 +{
 +      IPreferenceManager* manager = static_cast<IPreferenceManager*>(user_data);
 +      if (nullptr == manager) return;
 +
 +      boost::optional<bool> activated =
 +              manager->get_bool(MULTI_ASSISTANT_SETTINGS_ACTIVATED);
 +      if (activated) {
 +              MAS_LOGD("multi-assistant active state : %d\n", *activated);
 +
 +              CServicePlugin *plugin = nullptr;
 +              if (g_service_main) {
 +                      plugin = g_service_main->get_service_plugin();
 +              }
 +              if (plugin) {
 +                      if (*activated) {
 +                              plugin->activate();
 +                      } else {
 +                              plugin->deactivate();
 +                      }
 +              } else {
 +                      MAS_LOGE("Could not change plugin state : %p %p", g_service_main, plugin);
 +              }
 +      }
 +}
 +
 +int CServiceMain::initialize_service_plugin(void)
 +{
 +      if (0 != mServicePlugin.initialize()) {
 +              MAS_LOGE("Fail to ws intialize");
 +              return -1;
 +      }
 +
 +      if (0 != mServicePlugin.set_language(mCurrentLanguage.c_str())) {
 +              MAS_LOGE("Fail to ws set language");
 +              return -1;
 +      }
 +
++      memset(&mClientInfo, 0x00, sizeof(mClientInfo));
++      mCurrentClientInfo = -1;
++      mCurrentPreprocessingClientInfo = -1;
++      mWakeupClientAppId.clear();
++
 +      if (0 == mServiceConfig.get_assistant_info(mas_assistant_info_cb, this)) {
 +              for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
 +                      int inner_loop;
 +                      if (0 < strlen(mClientInfo[loop].appid)) {
 +                              mServiceConfig.load_custom_wake_words(mClientInfo[loop].appid,
 +                                      mClientInfo[loop].wakeup_word, mClientInfo[loop].wakeup_language);
 +                              if (0 < strlen(mClientInfo[loop].wakeup_engine)) {
 +                                      mServicePlugin.set_assistant_wakeup_engine(
 +                                              mClientInfo[loop].appid,
 +                                              mClientInfo[loop].wakeup_engine);
 +                              }
 +                              for (inner_loop = 0; inner_loop < MAX_WAKEUP_WORDS_NUM; inner_loop++) {
 +                                      if (0 < strlen(mClientInfo[loop].wakeup_word[inner_loop])) {
 +                                              MAS_LOGD("Registering wakeup word %s for app %s",
 +                                                      mClientInfo[loop].wakeup_word[inner_loop], mClientInfo[loop].appid);
 +                                              if (0 != mServicePlugin.add_assistant_wakeup_word(
 +                                                      mClientInfo[loop].appid,
 +                                                      mClientInfo[loop].wakeup_word[inner_loop],
 +                                                      mClientInfo[loop].wakeup_language[inner_loop])) {
 +                                                      MAS_LOGE("Fail to add assistant's wakeup word");
 +                                              }
 +                                      }
 +                              }
 +                              for (inner_loop = 0; inner_loop < MAX_SUPPORTED_LANGUAGES_NUM; inner_loop++) {
 +                                      if (0 < strlen(mClientInfo[loop].supported_language[inner_loop])) {
 +                                              MAS_LOGD("Adding language %s for app %s",
 +                                                      mClientInfo[loop].supported_language[inner_loop], mClientInfo[loop].appid);
 +                                              if (0 != mServicePlugin.add_assistant_language(
 +                                                      mClientInfo[loop].appid,
 +                                                      mClientInfo[loop].supported_language[inner_loop])) {
 +                                                      MAS_LOGE("Fail to add assistant's language");
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +      } else {
 +              MAS_LOGE("Fail to load assistant info");
 +      }
 +
 +      if (0 != mServicePlugin.set_callbacks()) {
 +              MAS_LOGE("Fail to set callbacks");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +int CServiceMain::deinitialize_service_plugin(void)
 +{
 +      MAS_LOGI("[ENTER]");
 +      if (0 != mServicePlugin.deactivate()) {
 +              MAS_LOGE("Fail to deactivate");
 +      }
 +      if (0 != mServicePlugin.deinitialize()) {
 +              MAS_LOGE("Fail to deinitialize");
 +      }
 +      MAS_LOGI("[END]");
 +      return 0;
 +}
 +
 +int CServiceMain::process_activated_setting()
 +{
 +      if (mPreferenceManager.register_changed_callback(
 +              MULTI_ASSISTANT_SETTINGS_ACTIVATED, active_state_changed_cb, &mPreferenceManager)) {
 +              /* Activate / deactivate according to the vconf key setting */
 +              active_state_changed_cb(std::string{}, &mPreferenceManager);
 +      } else {
 +#ifdef ENABLE_MULTI_ASSISTANT_BY_DEFAULT
 +              /* Multi-assistant needs to be enabled by default, unless disabled explicitly */
 +              mServicePlugin.activate();
 +              const char *default_assistant = NULL;
 +              if (0 == mServicePlugin.get_default_assistant(&default_assistant)) {
 +                      if (NULL == default_assistant) {
 +                              if (mClientInfo[0].used) {
 +                                      default_assistant = mClientInfo[0].appid;
 +                                      MAS_LOGW("No default assistant, setting %s as default", default_assistant);
 +                                      mServicePlugin.set_default_assistant(default_assistant);
 +                              } else {
 +                                      MAS_LOGE("No default assistant, and no assistant installed");
 +                              }
 +                      }
 +              }
 +#endif
 +      }
 +      return 0;
 +}
 +
 +int CServiceMain::mas_get_current_client_pid()
 +{
 +      int ret = -1;
 +      if (mCurrentClientInfo >= 0 && mCurrentClientInfo < MAX_MACLIENT_INFO_NUM) {
 +              const char *appid = mClientInfo[mCurrentClientInfo].appid;
 +              if (appid) {
 +                      ret = mClientManager.find_client_pid_by_appid(std::string{appid});
 +              }
 +      }
 +      return ret;
 +}
 +
 +int CServiceMain::mas_get_current_preprocessing_client_pid()
 +{
 +      int ret = -1;
 +      if (mCurrentPreprocessingClientInfo >= 0 && mCurrentPreprocessingClientInfo < MAX_MACLIENT_INFO_NUM) {
 +              const char *appid = mClientInfo[mCurrentPreprocessingClientInfo].appid;
 +              if (appid) {
 +                      ret = mClientManager.find_client_pid_by_appid(std::string{appid});
 +              }
 +      }
 +      return ret;
 +}
 +
 +int CServiceMain::mas_get_client_pid_by_appid(const char *appid)
 +{
 +      int ret = -1;
 +
 +      if (appid) {
 +              ret = mClientManager.find_client_pid_by_appid(std::string{appid});
 +      }
 +
 +      if (-1 != ret && !mApplicationManager.is_application_running(ret)) {
 +              MAS_LOGE("The PID for %s was %d, but it seems to be terminated", appid, ret);
 +              on_deinitialize(ret);
 +              ret = -1;
 +      }
 +
 +      return ret;
 +}
 +
 +bool CServiceMain::mas_get_client_custom_ui_option_by_appid(const char *appid)
 +{
 +      bool ret = false;
 +      for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
 +              if (mClientInfo[loop].used &&
 +                      0 < strlen(mClientInfo[loop].appid) &&
 +                      0 < strlen(mClientInfo[loop].wakeup_word[0])) {
 +                      if (strncmp(appid, mClientInfo[loop].appid, MAX_APPID_LEN) == 0) {
 +                              ret = mClientInfo[loop].custom_ui_option;
 +                      }
 +              }
 +      }
 +      return ret;
 +}
 +
 +int CServiceMain::mas_get_client_pid_by_wakeup_word(const char *wakeup_word)
 +{
 +      const char *appid = mas_get_client_appid_by_wakeup_word(wakeup_word);
 +      return mas_get_client_pid_by_appid(appid);
 +}
 +
 +const char* CServiceMain::mas_get_client_appid_by_wakeup_word(const char *wakeup_word)
 +{
 +      int loop;
 +      const char *appid = NULL;
 +
 +      if (NULL == wakeup_word) return NULL;
 +
 +      for (loop = 0; loop < MAX_MACLIENT_INFO_NUM && NULL == appid; loop++) {
 +              if (mClientInfo[loop].used &&
 +                      0 < strlen(mClientInfo[loop].appid)) {
 +                      for (int inner_loop = 0; inner_loop < MAX_WAKEUP_WORDS_NUM; inner_loop++) {
 +                              if (0 < strlen(mClientInfo[loop].wakeup_word[inner_loop])) {
 +                                      if (0 == strncmp(wakeup_word, mClientInfo[loop].wakeup_word[inner_loop], MAX_WAKEUP_WORD_LEN)) {
 +                                              appid = mClientInfo[loop].appid;
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      /* Perform extended search, by eliminating blank characters */
 +      if (NULL == appid) {
 +              for (loop = 0; loop < MAX_MACLIENT_INFO_NUM && NULL == appid; loop++) {
 +                      if (mClientInfo[loop].used &&
 +                              0 < strlen(mClientInfo[loop].appid)) {
 +                              for (int inner_loop = 0; inner_loop < MAX_WAKEUP_WORDS_NUM; inner_loop++) {
 +                                      if (0 < strlen(mClientInfo[loop].wakeup_word[inner_loop])) {
 +                                              char comparand[MAX_WAKEUP_WORD_LEN];
 +                                              int comparand_index = 0;
 +                                              for (int index = 0; index < MAX_WAKEUP_WORD_LEN; index++) {
 +                                                      if (' ' != mClientInfo[loop].wakeup_word[inner_loop][index]) {
 +                                                              comparand[comparand_index++] = mClientInfo[loop].wakeup_word[inner_loop][index];
 +                                                      }
 +                                              }
 +                                              if (0 == strncmp(wakeup_word, comparand, MAX_WAKEUP_WORD_LEN)) {
 +                                                      appid = mClientInfo[loop].appid;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      return appid;
 +}
 +
 +int CServiceMain::mas_set_current_client_by_wakeup_word(const char *wakeup_word)
 +{
 +      int loop;
 +      int ret = -1;
 +      int prev_selection = mCurrentClientInfo;
 +
 +      for (loop = 0; loop < MAX_MACLIENT_INFO_NUM && -1 == ret; loop++) {
 +              if (mClientInfo[loop].used &&
 +                      0 < strlen(mClientInfo[loop].appid)) {
 +                      for (int inner_loop = 0; inner_loop < MAX_WAKEUP_WORDS_NUM; inner_loop++) {
 +                              if (0 < strlen(mClientInfo[loop].wakeup_word[inner_loop])) {
 +                                      if (0 == strncmp(wakeup_word, mClientInfo[loop].wakeup_word[inner_loop], MAX_WAKEUP_WORD_LEN)) {
 +                                              mCurrentClientInfo = loop;
 +                                              ret = 0;
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +      /* Perform extended search, by eliminating blank characters */
 +      if (ret == -1) {
 +              for (loop = 0; loop < MAX_MACLIENT_INFO_NUM && -1 == ret; loop++) {
 +                      if (mClientInfo[loop].used &&
 +                              0 < strlen(mClientInfo[loop].appid)) {
 +                              for (int inner_loop = 0; inner_loop < MAX_WAKEUP_WORDS_NUM; inner_loop++) {
 +                                      if (0 < strlen(mClientInfo[loop].wakeup_word[inner_loop])) {
 +                                              char comparand[MAX_WAKEUP_WORD_LEN];
 +                                              int comparand_index = 0;
 +                                              for (int index = 0; index < MAX_WAKEUP_WORD_LEN; index++) {
 +                                                      if (' ' != mClientInfo[loop].wakeup_word[inner_loop][index]) {
 +                                                              comparand[comparand_index++] = mClientInfo[loop].wakeup_word[inner_loop][index];
 +                                                      }
 +                                              }
 +                                              if (0 == strncmp(wakeup_word, comparand, MAX_WAKEUP_WORD_LEN)) {
 +                                                      mCurrentClientInfo = loop;
 +                                                      ret = 0;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      if (mCurrentClientInfo != prev_selection) {
 +              if (prev_selection >= 0 && prev_selection < MAX_MACLIENT_INFO_NUM) {
 +                      int pid = mas_get_client_pid_by_appid(mClientInfo[prev_selection].appid);
 +                      mServiceIpc.change_active_state(pid, MA_ACTIVE_STATE_INACTIVE);
 +              }
 +      }
 +
 +      return ret;
 +}
 +
 +int CServiceMain::mas_set_current_client_by_appid(const char *appid)
 +{
 +      int ret = -1;
 +      int prev_selection = mCurrentClientInfo;
 +
 +      for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
 +              if (mClientInfo[loop].used &&
 +                      0 < strlen(mClientInfo[loop].appid) &&
 +                      0 < strlen(mClientInfo[loop].wakeup_word[0])) {
 +                      if (strncmp(appid, mClientInfo[loop].appid, MAX_APPID_LEN) == 0) {
 +                              mCurrentClientInfo = loop;
 +                              ret = 0;
 +                      }
 +              }
 +      }
 +
 +      if (mCurrentClientInfo != prev_selection) {
 +              if (prev_selection >= 0 && prev_selection < MAX_MACLIENT_INFO_NUM) {
 +                      int pid = mas_get_client_pid_by_appid(mClientInfo[prev_selection].appid);
 +                      mServiceIpc.change_active_state(pid, MA_ACTIVE_STATE_INACTIVE);
 +              }
 +      }
 +
 +      return ret;
 +}
 +
 +int CServiceMain::mas_launch_client_by_appid(const char *appid, CLIENT_LAUNCH_MODE launch_mode)
 +{
 +      int result = 0;
 +
 +      if (NULL == appid || 0 == strlen(appid)) {
 +              MAS_LOGE("appid invalid, failed launching MA Client");
 +              return -1;
 +      }
 +
 +      if (CLIENT_LAUNCH_MODE_PRELAUNCH == launch_mode) {
 +              if (mApplicationManager.is_application_running(appid)) {
 +                      MAS_LOGE("appid %s is already running, no need for a prelaunch", appid);
 +                      return -1;
 +              }
 +
 +              result = mApplicationManager.launch_app_async(appid, true);
 +      } else {
 +              result = mApplicationManager.launch_app_async(appid, false);
 +      }
 +
 +      if (CLIENT_LAUNCH_MODE_ACTIVATION == launch_mode) {
 +              bool found = false;
 +              for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
 +                      if (mClientInfo[loop].used &&
 +                              0 < strlen(mClientInfo[loop].appid)) {
 +                              if (strncmp(appid, mClientInfo[loop].appid, MAX_APPID_LEN) == 0) {
 +                                      mWakeupClientAppId = mClientInfo[loop].appid;
 +                                      found = true;
 +                              }
 +                      }
 +              }
 +              MAS_LOGD("mWakeupClientAppId : %s, %d", mWakeupClientAppId.c_str(), found);
 +      }
 +
 +      return result;
 +}
 +
 +int CServiceMain::mas_bring_client_to_foreground(const char* appid)
 +{
 +      int ret = 0;
 +
 +      if (NULL == appid || 0 == strlen(appid)) {
 +              MAS_LOGE("appid invalid, failed launching MA Client");
 +              return -1;
 +      }
 +
 +      if (!mApplicationManager.bring_app_to_foreground(appid)) {
 +              ret = -1;
 +      }
 +
 +      return ret;
 +}
 +
 +int CServiceMain::mas_launch_client_by_wakeup_word(const char *wakeup_word)
 +{
 +      const char *appid = mas_get_client_appid_by_wakeup_word(wakeup_word);
 +      return mas_launch_client_by_appid(appid, CLIENT_LAUNCH_MODE_ACTIVATION);
 +}
 +
 +int CServiceMain::mas_prelaunch_default_assistant()
 +{
 +      /* CHECK NEEDED : should the code segment below and activation logic above be moved to wakeup manger? */
 +      boost::optional<bool> prelaunch_mode =
 +              mPreferenceManager.get_bool(WAKEUP_SETTINGS_KEY_PRELAUNCH_MODE);
 +      if (prelaunch_mode && *prelaunch_mode) {
 +              const char *default_assistant = NULL;
 +              if (0 == mServicePlugin.get_default_assistant(&default_assistant)) {
 +                      if (!(mApplicationManager.is_application_running(default_assistant))) {
 +                              MAS_LOGD("prelaunching default_assistant_appid : %s", default_assistant);
 +                              mas_launch_client_by_appid(default_assistant, CLIENT_LAUNCH_MODE_PRELAUNCH);
 +                      }
 +              }
 +      }
 +      return 0;
 +}
 +
 +int CServiceMain::mas_update_voice_key_support_mode()
 +{
 +      /* CHECK NEEDED : should the code segment below and activation logic above be moved to wakeup manger? */
 +      bool successful = false;
 +      const char *default_assistant = NULL;
 +      if (0 == mServicePlugin.get_default_assistant(&default_assistant)) {
 +              for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
 +                      if (mClientInfo[loop].used) {
 +                              if (default_assistant &&
 +                                      strncmp(default_assistant, mClientInfo[loop].appid, MAX_APPID_LEN) == 0) {
 +                                      float duration = mClientInfo[loop].voice_key_tap_duration;
 +                                      if (0.0f < duration) {
 +                                              mServicePlugin.set_voice_key_tap_duration(duration);
 +                                      } else {
 +                                              mServicePlugin.unset_voice_key_tap_duration();
 +                                      }
 +                                      mServicePlugin.set_voice_key_support_mode(
 +                                              mClientInfo[loop].voice_key_support_mode);
 +                                      successful = true;
 +                              }
 +                      }
 +              }
 +      }
 +
 +      if (!successful) {
 +              mServicePlugin.unset_voice_key_tap_duration();
 +              mServicePlugin.set_voice_key_support_mode(VOICE_KEY_SUPPORT_MODE_NONE);
 +      }
 +      return 0;
 +}
 +
 +ma_preprocessing_allow_mode_e CServiceMain::get_preprocessing_allow_mode(const char* appid)
 +{
 +      for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM; loop++) {
 +              if (appid && mClientInfo[loop].used) {
 +                      if (strncmp(appid, mClientInfo[loop].appid, MAX_APPID_LEN) == 0) {
 +                              return mClientInfo[loop].preprocessing_allow_mode;
 +                      }
 +              }
 +      }
 +      return MA_PREPROCESSING_ALLOW_NONE;
 +}
 +
 +/* This might need to be read from settings in the future, but using macro for now */
 +//#define BRING_PREPROCESSING_ASSISTANT_TO_FRONT
 +
 +int CServiceMain::mas_process_preprocessing_state_event(PREPROCESSING_STATE_EVENT event)
 +{
 +      const char* current_maclient_appid = NULL;
 +      const char* preprocessing_allow_appid = NULL;
 +      if (mCurrentClientInfo >= 0 && mCurrentClientInfo < MAX_MACLIENT_INFO_NUM) {
 +              current_maclient_appid = mClientInfo[mCurrentClientInfo].appid;
 +              preprocessing_allow_appid = mClientInfo[mCurrentClientInfo].preprocessing_allow_appid;
 +      }
 +      ma_preprocessing_allow_mode_e mode = get_preprocessing_allow_mode(current_maclient_appid);
 +
 +      switch (event) {
 +              case PREPROCESSING_STATE_EVENT_ASSISTANT_ACTIVATED:
 +              {
 +#ifndef BRING_PREPROCESSING_ASSISTANT_TO_FRONT
 +                      /* If there is no need to bring preprocessing assistant to front,
 +                              current_maclient should always be brought to front */
 +                      mas_bring_client_to_foreground(current_maclient_appid);
 +#endif
 +                      mCurrentPreprocessingState = PREPROCESSING_STATE_WAKEUP_PREPROCESS_DISABLED;
 +                      if (check_preprocessing_assistant_exists()) {
 +                              if (MA_PREPROCESSING_ALLOW_UTTERANCE == mode ||
 +                                      MA_PREPROCESSING_ALLOW_ALL == mode) {
 +                                      if (is_current_preprocessing_assistant(preprocessing_allow_appid)) {
 +                                              mCurrentPreprocessingState = PREPROCESSING_STATE_WAKEUP_PREPROCESS_ENABLED;
 +                                      }
 +                              }
 +                      } else {
 +#ifdef BRING_PREPROCESSING_ASSISTANT_TO_FRONT
 +                              /* If preprocessing assistant does not exist, there is no way to enable
 +                                      preprocessing assistant, so bring current maclient to front right away */
 +                              mas_bring_client_to_foreground(current_maclient_appid);
 +#endif
 +                      }
 +              }
 +              break;
 +              case PREPROCESSING_STATE_EVENT_PREPROCESSING_ALLOW_MODE_CHANGED:
 +              {
 +                      mCurrentPreprocessingState = PREPROCESSING_STATE_WAKEUP_PREPROCESS_DISABLED;
 +                      /* Enable preprocessing mode only if the preprocessing assistant exists */
 +                      if (check_preprocessing_assistant_exists()) {
 +                              if (MA_PREPROCESSING_ALLOW_UTTERANCE == mode ||
 +                                      MA_PREPROCESSING_ALLOW_ALL == mode) {
 +                                      if (is_current_preprocessing_assistant(preprocessing_allow_appid)) {
 +                                              mCurrentPreprocessingState = PREPROCESSING_STATE_WAKEUP_PREPROCESS_ENABLED;
 +                                      }
 +                              }
 +                      }
 +              }
 +              break;
 +              case PREPROCESSING_STATE_EVENT_UTTERANCE_STREAMING_STARTED:
 +              {
 +                      if (PREPROCESSING_STATE_WAKEUP_PREPROCESS_ENABLED == mCurrentPreprocessingState) {
 +                              mCurrentPreprocessingState = PREPROCESSING_STATE_PREPROCESSING_UTTERANCE;
 +                      } else if (PREPROCESSING_STATE_WAKEUP_PREPROCESS_DISABLED == mCurrentPreprocessingState) {
 +                              /* If preprocessing assistant does not exist, the current_maclient
 +                                      would have been brought to front already on wakeup event */
 +#ifdef BRING_PREPROCESSING_ASSISTANT_TO_FRONT
 +                              if (check_preprocessing_assistant_exists()) {
 +                                      mas_bring_client_to_foreground(current_maclient_appid);
 +                              }
 +#endif
 +                              mCurrentPreprocessingState = PREPROCESSING_STATE_NONE;
 +                      }
 +              }
 +              break;
 +              case PREPROCESSING_STATE_EVENT_FOLLOW_UP_STREAMING_STARTED:
 +              {
 +                      mCurrentPreprocessingState = PREPROCESSING_STATE_NONE;
 +                      if (check_preprocessing_assistant_exists()) {
 +                              if (MA_PREPROCESSING_ALLOW_FOLLOW_UP == mode ||
 +                                      MA_PREPROCESSING_ALLOW_ALL == mode) {
 +                                      mCurrentPreprocessingState = PREPROCESSING_STATE_PREPROCESSING_FOLLOW_UP;
 +                              }
 +                      }
 +              }
 +              break;
 +              case PREPROCESSING_STATE_EVENT_PREPROCESSING_SUCCEEDED:
 +              {
 +#ifdef BRING_PREPROCESSING_ASSISTANT_TO_FRONT
 +                      if (PREPROCESSING_STATE_EVENT_UTTERANCE_STREAMING_STARTED == mCurrentPreprocessingState ||
 +                              PREPROCESSING_STATE_EVENT_FOLLOW_UP_STREAMING_STARTED == mCurrentPreprocessingState) {
 +                              boost::optional<std::string> preprocessing_assistant =
 +                                      mPreferenceManager.get_bool(WAKEUP_SETTINGS_KEY_PREPROCESSING_ASSISTANT_APPID);
 +                              if (preprocessing_assistant) {
 +                                      MAS_LOGD("preprocessing_assistant_appid : %s", (*preprocessing_assistant).c_str());
 +                                      mas_bring_client_to_foreground((*preprocessing_assistant).c_str());
 +                              }
 +                      }
 +#endif
 +                      mCurrentPreprocessingState = PREPROCESSING_STATE_NONE;
 +              }
 +              break;
 +              case PREPROCESSING_STATE_EVENT_PREPROCESSING_FAILED:
 +              {
 +#ifdef BRING_PREPROCESSING_ASSISTANT_TO_FRONT
 +                      if (PREPROCESSING_STATE_EVENT_UTTERANCE_STREAMING_STARTED == mCurrentPreprocessingState ||
 +                              PREPROCESSING_STATE_EVENT_FOLLOW_UP_STREAMING_STARTED == mCurrentPreprocessingState) {
 +                              mas_bring_client_to_foreground(current_maclient_appid);
 +                      }
 +#endif
 +                      mCurrentPreprocessingState = PREPROCESSING_STATE_NONE;
 +              }
 +              break;
 +      }
 +      return 0;
 +}
 +
 +int CServiceMain::mas_set_current_service_state(ma_service_state_e state)
 +{
 +      mCurrentServiceState = state;
 +
 +      int count = mClientManager.get_client_num();
 +      int i;
 +
 +      for (i = 0; i < count; i++) {
 +              int pid = mClientManager.find_client_pid_by_index(i);
 +
 +              if (-1 != pid) {
 +                      int ret = mServiceIpc.change_service_state(pid, state);
 +                      if (0 != ret) {
 +                              MAS_LOGE("[ERROR] Fail to set service state change to %d, ret(%d)", pid, ret);
 +                      }
 +              }
 +      }
 +      return 0;
 +}
 +
 +ma_service_state_e CServiceMain::mas_get_current_service_state()
 +{
 +      return mCurrentServiceState;
 +}
 +
 +bool CServiceMain::is_valid_wakeup_engine(const char* appid)
 +{
 +      for (int loop = 0; loop < MAX_MACLIENT_INFO_NUM;loop++) {
 +              if (mClientInfo[loop].used) {
 +                      LOGD("comparing appid : %s %s", mClientInfo[loop].wakeup_engine, appid);
 +                      if (0 == strncmp(mClientInfo[loop].wakeup_engine, appid, MAX_APPID_LEN)) {
 +                              return true;
 +                      }
 +              }
 +      }
 +      return false;
 +}
 +
 +int pkg_app_list_cb(const pkgmgrinfo_appinfo_h handle, void *user_data)
 +{
 +      if (nullptr == g_service_main) return -1;
 +
 +      char *appid = NULL;
 +
 +      int ret = pkgmgrinfo_appinfo_get_appid(handle, &appid);
 +      if (PMINFO_R_OK == ret && NULL != appid) {
 +              int *result = (int*)user_data;
 +              if (result) {
 +                      bool exists = g_service_main->is_valid_wakeup_engine(appid);
 +                      if (exists) {
 +                              *result = 1;
 +                      }
 +              }
 +      } else {
 +              LOGE("pkgmgrinfo_appinfo_get_appid failed! error code=%d", ret);
 +      }
 +
 +      return 0;
 +}
 +
 +/*
 +INFO: Package install/update/uninstall scenario
 +Install and Uninstall are obviously simple.
 +   Install: just INSTALL
 +   Uninstall: just UNINSTALL
 +Update package (change the source codes and Run As again), there are four scenarios:
 +1. UPDATE
 +   Source code change
 +2. UNINSTALL -> INSTALL
 +   This happens when Tizen IDE Property > Tizen SDK > Rapid Development Support > Check "Enable Project specific settings"
 +   and change Application ID in tizen-manifest.xml file and Run As.
 +3. UPDATE -> INSTALL
 +   This happens when Tizen IDE Property > Tizen SDK > Rapid Development Support > Uncheck "Enable Project specific settings"
 +   and change Application ID in tizen-manifest.xml file and Run As.
 +   At UPDATE event, pkgid (package parameter) is invalid...
 +4. UPDATE
 +   Exceptionally, only UPDATE can be called when Application ID in tizen-manifest.xml file is changed.
 +   At UPDATE event, pkgid (package parameter) is valid, and only appid is changed; the previous appid is invalid.
 +*/
 +static void _package_manager_event_cb(const char *type, const char *package, package_manager_event_type_e event_type, package_manager_event_state_e event_state, int progress, package_manager_error_e error, void *user_data)
 +{
 +      CServiceMain* service_main = static_cast<CServiceMain*>(user_data);
 +
 +      int ret = 0;
 +      uid_t uid = getuid();
 +      pkgmgrinfo_pkginfo_h handle = NULL;
 +      static bool in_progress = false;
 +      bool should_exit = false;
 +
 +      if (!package || !type)
 +              return;
 +
 +      if (PACKAGE_MANAGER_EVENT_TYPE_UPDATE != event_type &&
 +              PACKAGE_MANAGER_EVENT_TYPE_INSTALL != event_type &&
 +              PACKAGE_MANAGER_EVENT_TYPE_UNINSTALL != event_type)
 +              return;
 +
 +      if (PACKAGE_MANAGER_EVENT_STATE_STARTED != event_state &&
 +              PACKAGE_MANAGER_EVENT_STATE_COMPLETED != event_state)
 +              return;
 +
 +      bool user = false;
 +      MAS_LOGD("type=%s package=%s event_type=%d event_state=%d progress=%d error=%d",
 +              type, package, event_type, event_state, progress, error);
 +      ret = pkgmgrinfo_pkginfo_get_pkginfo(package, &handle);
 +      if (ret != PMINFO_R_OK || NULL == handle) {
 +              MAS_LOGW("Failed to call pkgmgrinfo_pkginfo_get_pkginfo & get_usr_pkginfo(\"%s\",~) returned %d, uid : %d", package, ret, getuid());
 +              /* Try to get in user packages */
 +              user = true;
 +              ret = pkgmgrinfo_pkginfo_get_usr_pkginfo(package, uid, &handle);
 +              if (ret != PMINFO_R_OK || NULL == handle) {
 +                      MAS_LOGW("Failed to call pkgmgrinfo_pkginfo_get_pkginfo & get_usr_pkginfo(\"%s\",~) returned %d, uid : %d", package, ret, getuid());
 +                      return;
 +              }
 +      }
 +
 +      if (user) {
 +              /* Try to get in user packages */
 +              pkgmgrinfo_appinfo_get_usr_list(handle, PMINFO_ALL_APP, pkg_app_list_cb, (void *)&ret, uid);
 +      }  else {
 +              /* Try to get in global packages */
 +              pkgmgrinfo_appinfo_get_list(handle, PMINFO_ALL_APP, pkg_app_list_cb, (void *)&ret);
 +      }
 +      if (1 == ret) {
 +              if (PACKAGE_MANAGER_EVENT_STATE_STARTED == event_state) {
 +                      MAS_LOGI("processing PACKAGE_MANAGER_EVENT_STATE_STARTED event : %d", event_type);
 +                      if (false == in_progress) {
 +                              in_progress = true;
 +                              if (service_main) {
 +                                      service_main->deinitialize_service_plugin();
 +                              } else {
 +                                      MAS_LOGE("service_main is NULL");
 +                              }
 +                      }
 +              } else if (PACKAGE_MANAGER_EVENT_STATE_COMPLETED == event_state) {
 +                      MAS_LOGI("processing PACKAGE_MANAGER_EVENT_STATE_COMPLETED event : %d", event_type);
 +                      if (false == in_progress) {
 +                              if (service_main) {
 +                                      service_main->deinitialize_service_plugin();
 +                              } else {
 +                                      MAS_LOGE("service_main is NULL");
 +                              }
 +                      }
 +                      /*
 +                      if (service_main) {
 +                              service_main->initialize_service_plugin();
 +                              service_main->process_activated_setting();
 +                      } else {
 +                              MAS_LOGE("service_main is NULL");
 +                      }
 +                      */
 +                      should_exit = true;
 +                      in_progress = false;
 +              }
 +      }
 +
 +      pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
 +
 +      if (should_exit) {
 +              LOGI("Now restarting multi-assistant-service for reloading updated modules");
 +              service_app_exit();
 +      }
 +
 +      return;
 +}
 +
 +bool CServiceMain::app_create(void *data)
 +{
 +      // Todo: add your code here.
 +      MAS_LOGD("[Enter] Service app create");
 +
 +      g_service_main = this;
 +
 +      mClientManager.set_application_manager(&mApplicationManager);
 +
 +      mServiceIpc.set_client_manager(&mClientManager);
 +      mServiceIpc.set_service_ipc_observer(this);
 +
 +      mServicePlugin.set_service_ipc(&mServiceIpc);
 +      mServicePlugin.set_service_main(this);
 +
 +      int ret = mServiceIpc.open_connection();
 +      if (0 != ret) {
 +              MAS_LOGE("[ERROR] Fail to open connection");
 +      }
 +
 +      initialize_service_plugin();
 +
 +      process_activated_setting();
 +
 +      mas_prelaunch_default_assistant();
 +      mas_update_voice_key_support_mode();
 +
 +      /* For the case of preprocessing assistant, it always have to be launched beforehand */
 +      boost::optional<std::string> preprocessing_assistant =
 +              mPreferenceManager.get_string(WAKEUP_SETTINGS_KEY_PREPROCESSING_ASSISTANT_APPID);
 +      if (preprocessing_assistant) {
 +              MAS_LOGD("prelaunching preprocessing_assistant_appid : %s", (*preprocessing_assistant).c_str());
 +              mas_launch_client_by_appid((*preprocessing_assistant).c_str(), CLIENT_LAUNCH_MODE_PRELAUNCH);
 +      }
 +
 +      if (!mPackageManagerHandle) {
 +              int ret = package_manager_create(&mPackageManagerHandle);
 +              if (ret == PACKAGE_MANAGER_ERROR_NONE) {
 +                      ret = package_manager_set_event_cb(mPackageManagerHandle, _package_manager_event_cb, this);
 +                      if (ret == PACKAGE_MANAGER_ERROR_NONE) {
 +                              LOGD("package_manager_set_event_cb succeeded.");
 +                      } else {
 +                              LOGE("package_manager_set_event_cb failed(%d)", ret);
 +                      }
 +              } else {
 +                      LOGE("package_manager_create failed(%d)", ret);
 +              }
 +      }
 +
 +      return true;
 +}
 +
 +void CServiceMain::app_terminate(void *data)
 +{
 +      MAS_LOGI("[ENTER]");
 +      if (mPackageManagerHandle) {
 +              package_manager_unset_event_cb(mPackageManagerHandle);
 +              package_manager_destroy(mPackageManagerHandle);
 +              mPackageManagerHandle = NULL;
 +      }
 +
 +      deinitialize_service_plugin();
 +
 +      mPreferenceManager.unregister_changed_callback(
 +              MULTI_ASSISTANT_SETTINGS_ACTIVATED, active_state_changed_cb);
 +
 +      int ret = mServiceIpc.close_connection();
 +      if (0 != ret) {
 +              MAS_LOGE("[ERROR] Fail to close connection");
 +      }
 +
 +      g_service_main = nullptr;
 +
 +      MAS_LOGI("[END]");
 +      return;
 +}
 +
 +int CServiceMain::on_initialize(int pid) {
 +      MAS_LOGD("[Enter] pid(%d)", pid);
 +
 +      std::string pid_appid;
 +      boost::optional<std::string> appid_by_pid = mApplicationManager.get_appid_by_pid(pid);
 +      if (appid_by_pid) {
 +              pid_appid = *appid_by_pid;
 +              MAS_LOGD("appid for pid %d is : %s", pid, pid_appid.c_str());
 +
 +              /* Remove existing client that has same appid, if there's any */
 +              mClientManager.destroy_client_by_appid(pid_appid.c_str());
 +
 +              /* And remove a client that has same pid also */
 +              mClientManager.destroy_client_by_pid(pid);
 +
 +              mClientManager.create_client(pid, pid_appid.c_str());
 +
 +              const char *current_maclient_appid = NULL;
 +              if (mCurrentClientInfo >= 0 && mCurrentClientInfo < MAX_MACLIENT_INFO_NUM) {
 +                      current_maclient_appid = mClientInfo[mCurrentClientInfo].appid;
 +              }
 +
 +              mas_client_send_preprocessing_information(pid);
 +              if (MA_VOICE_KEY_STATUS_PRESSED == mLastVoiceKeyStatus) {
 +                      mas_client_send_voice_key_status_change(pid, mLastVoiceKeyStatus);
 +              }
 +              if (current_maclient_appid && 0 == pid_appid.compare(current_maclient_appid)) {
 +                      MAS_LOGD("MA client with current maclient appid connected!");
 +
 +                      if (0 == mWakeupClientAppId.compare(pid_appid)) {
 +                              mWakeupClientAppId.clear();
 +                              mServiceIpc.change_active_state(pid, MA_ACTIVE_STATE_ACTIVE);
 +                              mas_process_preprocessing_state_event(PREPROCESSING_STATE_EVENT_ASSISTANT_ACTIVATED);
 +                      } else {
 +                              MAS_LOGE("[ERROR] mWakeupClientAppId and appid differ : %s %s",
 +                                      mWakeupClientAppId.c_str(), pid_appid.c_str());
 +                      }
 +              } else {
 +                      MAS_LOGD("MA client connected, but its appid does not match with current maclient");
 +              }
 +
 +              mServiceIpc.change_service_state(pid, mas_get_current_service_state());
 +      } else {
 +              MAS_LOGE("[ERROR] Fail to retrieve appid");
 +      }
 +
 +      return 0;
 +}
 +
 +int CServiceMain::on_deinitialize(int pid) {
 +      MAS_LOGD("[Enter] pid(%d)", pid);
 +      mClientManager.destroy_client_by_pid(pid);
 +      return 0;
 +}
 +
 +int CServiceMain::on_get_audio_format(int pid, int& rate, int& channel, int& audio_type) {
 +      int main_rate, main_channel, main_audio_type;
 +      int ret = mas_client_get_audio_format(pid,
 +              &main_rate, &main_channel, &main_audio_type);
 +      rate = main_rate;
 +      channel = main_channel;
 +      audio_type = main_audio_type;
 +      return ret;
 +}
 +
 +int CServiceMain::on_get_audio_source_type(int pid, std::string& type) {
 +      char *main_type = nullptr;
 +      int ret = mas_client_get_audio_source_type(pid, &main_type);
 +      if (0 == ret && main_type) {
 +              type = std::string{main_type};
 +      }
 +      return ret;
 +}
 +
 +int CServiceMain::on_send_asr_result(int pid, int event, std::string asr_result) {
 +      return mas_client_send_asr_result(pid, event, asr_result.c_str());
 +}
 +
 +int CServiceMain::on_send_result(int pid, std::string display_text,
 +      std::string utterance_text, std::string result_json) {
 +      return mas_client_send_result(pid,
 +              display_text.c_str(), utterance_text.c_str(), result_json.c_str());
 +}
 +
 +int CServiceMain::on_send_recognition_result(int pid, int result) {
 +      return mas_client_send_recognition_result(pid, result);
 +}
 +
 +int CServiceMain::on_start_streaming_audio_data(int pid, int type) {
 +      return mas_client_start_streaming_audio_data(pid, type);
 +}
 +
 +int CServiceMain::on_stop_streaming_audio_data(int pid, int type) {
 +      return mas_client_stop_streaming_audio_data(pid, type);
 +}
 +
 +int CServiceMain::on_update_voice_feedback_state(int pid, int state) {
 +      return mas_client_update_voice_feedback_state(pid, state);
 +}
 +
 +int CServiceMain::on_send_assistant_specific_command(int pid, std::string command) {
 +      return mas_client_set_assistant_specific_command(pid, command.c_str());
 +}
 +
 +int CServiceMain::on_set_background_volume(int pid, double ratio) {
 +      return mas_client_set_background_volume(pid, ratio);
 +}
 +
 +int CServiceMain::on_set_preprocessing_allow_mode(int pid, int mode, std::string app_id) {
 +      return mas_client_set_preprocessing_allow_mode(pid,
 +              static_cast<ma_preprocessing_allow_mode_e>(mode), app_id.c_str());
 +}
 +
 +int CServiceMain::on_send_preprocessing_result(int pid, int result) {
 +      return mas_client_send_preprocessing_result(pid, result);
 +}
 +
 +int CServiceMain::on_set_wake_word_audio_require_flag(int pid, int require) {
 +      return mas_client_set_wake_word_audio_require_flag(pid, require);
 +}
 +
 +int CServiceMain::on_set_assistant_language(int pid, std::string language) {
 +      return mas_client_set_assistant_language(pid, language.c_str());
 +}
 +
 +int CServiceMain::on_add_wake_word(int pid, std::string wake_word, std::string language) {
 +      return mas_client_add_wake_word(pid, wake_word.c_str(), language.c_str());
 +}
 +
 +int CServiceMain::on_remove_wake_word(int pid, std::string wake_word, std::string language) {
 +      return mas_client_remove_wake_word(pid, wake_word.c_str(), language.c_str());
 +}
 +
 +int CServiceMain::on_ui_initialize(int pid)
 +{
 +      return mas_ui_client_initialize(pid);
 +}
 +
 +int CServiceMain::on_ui_deinitialize(int pid)
 +{
 +      return mas_ui_client_deinitialize(pid);
 +}
 +
 +int CServiceMain::on_ui_change_assistant(std::string app_id)
 +{
 +      return mas_ui_client_change_assistant(app_id.c_str());
 +}