summaryrefslogtreecommitdiff
path: root/src/components/application_manager/rpc_plugins/sdl_rpc_plugin/src/commands/mobile/perform_interaction_request.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/application_manager/rpc_plugins/sdl_rpc_plugin/src/commands/mobile/perform_interaction_request.cc')
-rw-r--r--src/components/application_manager/rpc_plugins/sdl_rpc_plugin/src/commands/mobile/perform_interaction_request.cc1068
1 files changed, 1068 insertions, 0 deletions
diff --git a/src/components/application_manager/rpc_plugins/sdl_rpc_plugin/src/commands/mobile/perform_interaction_request.cc b/src/components/application_manager/rpc_plugins/sdl_rpc_plugin/src/commands/mobile/perform_interaction_request.cc
new file mode 100644
index 0000000000..42489fd8bd
--- /dev/null
+++ b/src/components/application_manager/rpc_plugins/sdl_rpc_plugin/src/commands/mobile/perform_interaction_request.cc
@@ -0,0 +1,1068 @@
+/*
+
+ Copyright (c) 2018, Ford Motor Company
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided with the
+ distribution.
+
+ Neither the name of the Ford Motor Company nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <numeric>
+#include <string.h>
+#include <string>
+#include "sdl_rpc_plugin/commands/mobile/perform_interaction_request.h"
+
+#include "application_manager/application_impl.h"
+#include "application_manager/message_helper.h"
+
+#include "interfaces/MOBILE_API.h"
+#include "interfaces/HMI_API.h"
+#include "utils/file_system.h"
+#include "utils/helpers.h"
+#include "utils/custom_string.h"
+#include "utils/gen_hash.h"
+
+namespace sdl_rpc_plugin {
+using namespace application_manager;
+
+namespace commands {
+
+namespace custom_str = utils::custom_string;
+
+uint32_t PerformInteractionRequest::pi_requests_count_ = 0;
+
+PerformInteractionRequest::PerformInteractionRequest(
+ const application_manager::commands::MessageSharedPtr& message,
+ ApplicationManager& application_manager,
+ app_mngr::rpc_service::RPCService& rpc_service,
+ app_mngr::HMICapabilities& hmi_capabilities,
+ policy::PolicyHandlerInterface& policy_handler)
+ : CommandRequestImpl(message,
+ application_manager,
+ rpc_service,
+ hmi_capabilities,
+ policy_handler)
+ , interaction_mode_(mobile_apis::InteractionMode::INVALID_ENUM)
+ , ui_response_received_(false)
+ , vr_response_received_(false)
+ , app_pi_was_active_before_(false)
+ , vr_result_code_(hmi_apis::Common_Result::INVALID_ENUM)
+ , ui_result_code_(hmi_apis::Common_Result::INVALID_ENUM) {
+ subscribe_on_event(hmi_apis::FunctionID::UI_OnResetTimeout);
+ subscribe_on_event(hmi_apis::FunctionID::VR_OnCommand);
+ subscribe_on_event(hmi_apis::FunctionID::Buttons_OnButtonPress);
+}
+
+PerformInteractionRequest::~PerformInteractionRequest() {}
+
+bool PerformInteractionRequest::Init() {
+ /* Timeout in milliseconds.
+ If omitted a standard value of 10000 milliseconds is used.*/
+ if ((*message_)[strings::msg_params].keyExists(strings::timeout)) {
+ default_timeout_ =
+ (*message_)[strings::msg_params][strings::timeout].asUInt();
+ }
+
+ interaction_mode_ = static_cast<mobile_apis::InteractionMode::eType>(
+ (*message_)[strings::msg_params][strings::interaction_mode].asInt());
+
+ if (mobile_apis::InteractionMode::BOTH == interaction_mode_ ||
+ mobile_apis::InteractionMode::MANUAL_ONLY == interaction_mode_) {
+ default_timeout_ *= 2;
+ }
+ return true;
+}
+
+void PerformInteractionRequest::Run() {
+ LOG4CXX_AUTO_TRACE(logger_);
+
+ ApplicationSharedPtr app = application_manager_.application(connection_key());
+
+ if (!app) {
+ LOG4CXX_ERROR(logger_, "Application is not registered");
+ SendResponse(false, mobile_apis::Result::APPLICATION_NOT_REGISTERED);
+ return;
+ }
+
+ if (app->is_perform_interaction_active()) {
+ LOG4CXX_DEBUG(logger_, "Application has active PerformInteraction");
+ app_pi_was_active_before_ = true;
+ }
+
+ smart_objects::SmartObject& msg_params = (*message_)[strings::msg_params];
+ mobile_apis::LayoutMode::eType interaction_layout =
+ mobile_apis::LayoutMode::INVALID_ENUM;
+
+ if (msg_params.keyExists(hmi_request::interaction_layout)) {
+ interaction_layout = static_cast<mobile_apis::LayoutMode::eType>(
+ msg_params[hmi_request::interaction_layout].asInt());
+ }
+
+ if ((mobile_apis::InteractionMode::VR_ONLY == interaction_mode_) &&
+ (mobile_apis::LayoutMode::KEYBOARD == interaction_layout)) {
+ LOG4CXX_ERROR(logger_,
+ "PerformInteraction contains InteractionMode"
+ "=VR_ONLY and interactionLayout=KEYBOARD");
+ SendResponse(false, mobile_apis::Result::INVALID_DATA);
+ return;
+ }
+
+ const size_t choice_set_id_list_length =
+ msg_params[strings::interaction_choice_set_id_list].length();
+
+ if (0 == choice_set_id_list_length) {
+ if (mobile_apis::LayoutMode::KEYBOARD == interaction_layout) {
+ if (mobile_apis::InteractionMode::BOTH == interaction_mode_) {
+ LOG4CXX_ERROR(logger_,
+ "interactionChoiceSetIDList is empty,"
+ " InteractionMode=BOTH and"
+ " interactionLayout=KEYBOARD");
+ SendResponse(false, mobile_apis::Result::INVALID_DATA);
+ return;
+ }
+ } else {
+ LOG4CXX_ERROR(logger_,
+ "interactionChoiceSetIDList is empty"
+ " and interactionLayout!=KEYBOARD");
+ SendResponse(false, mobile_apis::Result::INVALID_DATA);
+ return;
+ }
+ }
+
+ if (!CheckChoiceIDFromRequest(
+ app,
+ choice_set_id_list_length,
+ msg_params[strings::interaction_choice_set_id_list])) {
+ LOG4CXX_ERROR(logger_,
+ "PerformInteraction has choice sets with "
+ "duplicated IDs or application does not have choice sets");
+ SendResponse(false, mobile_apis::Result::INVALID_ID);
+ return;
+ }
+
+ if (msg_params.keyExists(strings::vr_help)) {
+ if (mobile_apis::Result::INVALID_DATA ==
+ MessageHelper::VerifyImageVrHelpItems(
+ msg_params[strings::vr_help], app, application_manager_)) {
+ LOG4CXX_ERROR(logger_,
+ "Verification of " << strings::vr_help << " failed.");
+ SendResponse(false, mobile_apis::Result::INVALID_DATA);
+ return;
+ }
+ }
+
+ if (IsWhiteSpaceExist()) {
+ LOG4CXX_ERROR(logger_,
+ "Incoming perform interaction has contains \t\n \\t \\n");
+ SendResponse(false, mobile_apis::Result::INVALID_DATA);
+ return;
+ }
+
+ switch (interaction_mode_) {
+ case mobile_apis::InteractionMode::BOTH: {
+ LOG4CXX_DEBUG(logger_, "Interaction Mode: BOTH");
+ if (!CheckChoiceSetVRSynonyms(app) || !CheckChoiceSetMenuNames(app) ||
+ !CheckVrHelpItemPositions(app) ||
+ !CheckChoiceSetListVRCommands(app)) {
+ return;
+ }
+ break;
+ }
+ case mobile_apis::InteractionMode::MANUAL_ONLY: {
+ LOG4CXX_DEBUG(logger_, "Interaction Mode: MANUAL_ONLY");
+ if (!CheckChoiceSetVRSynonyms(app) || !CheckChoiceSetMenuNames(app) ||
+ !CheckVrHelpItemPositions(app)) {
+ return;
+ }
+ break;
+ }
+ case mobile_apis::InteractionMode::VR_ONLY: {
+ LOG4CXX_DEBUG(logger_, "Interaction Mode: VR_ONLY");
+ if (!CheckChoiceSetVRSynonyms(app) || !CheckVrHelpItemPositions(app) ||
+ !CheckChoiceSetListVRCommands(app)) {
+ return;
+ }
+ break;
+ }
+ default: {
+ LOG4CXX_ERROR(logger_, "Unknown interaction mode");
+ return;
+ }
+ }
+
+ app->set_perform_interaction_mode(static_cast<int32_t>(interaction_mode_));
+ app->set_perform_interaction_active(true);
+ app->set_perform_interaction_layout(interaction_layout);
+ // increment amount of active requests
+ ++pi_requests_count_;
+ SendVRPerformInteractionRequest(app);
+ SendUIPerformInteractionRequest(app);
+}
+
+void PerformInteractionRequest::on_event(const event_engine::Event& event) {
+ LOG4CXX_AUTO_TRACE(logger_);
+ const smart_objects::SmartObject& message = event.smart_object();
+ smart_objects::SmartObject msg_param =
+ smart_objects::SmartObject(smart_objects::SmartType_Map);
+
+ switch (event.id()) {
+ case hmi_apis::FunctionID::UI_OnResetTimeout: {
+ LOG4CXX_DEBUG(logger_, "Received UI_OnResetTimeout event");
+ application_manager_.updateRequestTimeout(
+ connection_key(), correlation_id(), default_timeout());
+ break;
+ }
+ case hmi_apis::FunctionID::UI_PerformInteraction: {
+ LOG4CXX_DEBUG(logger_, "Received UI_PerformInteraction event");
+ EndAwaitForInterface(HmiInterfaces::HMI_INTERFACE_UI);
+ ui_response_received_ = true;
+ unsubscribe_from_event(hmi_apis::FunctionID::UI_PerformInteraction);
+ ui_result_code_ = static_cast<hmi_apis::Common_Result::eType>(
+ message[strings::params][hmi_response::code].asUInt());
+ GetInfo(message, ui_info_);
+ ProcessUIResponse(event.smart_object(), msg_param);
+ break;
+ }
+ case hmi_apis::FunctionID::VR_PerformInteraction: {
+ LOG4CXX_DEBUG(logger_, "Received VR_PerformInteraction");
+ EndAwaitForInterface(HmiInterfaces::HMI_INTERFACE_VR);
+ vr_response_received_ = true;
+ unsubscribe_from_event(hmi_apis::FunctionID::VR_PerformInteraction);
+ vr_result_code_ = static_cast<hmi_apis::Common_Result::eType>(
+ message[strings::params][hmi_response::code].asUInt());
+ GetInfo(message, vr_info_);
+ if (ProcessVRResponse(event.smart_object(), msg_param)) {
+ return;
+ }
+ break;
+ }
+ default: {
+ LOG4CXX_ERROR(logger_, "Received unknown event" << event.id());
+ break;
+ }
+ }
+
+ if (!HasHMIResponsesToWait()) {
+ LOG4CXX_DEBUG(logger_, "Send response in BOTH iteraction mode");
+ SendBothModeResponse(msg_param);
+ }
+}
+
+void PerformInteractionRequest::onTimeOut() {
+ LOG4CXX_AUTO_TRACE(logger_);
+
+ switch (interaction_mode_) {
+ case mobile_apis::InteractionMode::BOTH: {
+ LOG4CXX_DEBUG(logger_, "Interaction Mode: BOTH");
+ if (true == vr_response_received_) {
+ unsubscribe_from_event(hmi_apis::FunctionID::UI_PerformInteraction);
+ DisablePerformInteraction();
+ CommandRequestImpl::onTimeOut();
+ } else {
+ application_manager_.updateRequestTimeout(
+ connection_key(), correlation_id(), default_timeout());
+ }
+ break;
+ }
+ case mobile_apis::InteractionMode::VR_ONLY: {
+ LOG4CXX_DEBUG(logger_, "Interaction Mode: VR_ONLY");
+ unsubscribe_from_event(hmi_apis::FunctionID::UI_PerformInteraction);
+ DisablePerformInteraction();
+ CommandRequestImpl::onTimeOut();
+ break;
+ }
+ case mobile_apis::InteractionMode::MANUAL_ONLY: {
+ LOG4CXX_DEBUG(logger_, "InteractionMode: MANUAL_ONLY");
+ unsubscribe_from_event(hmi_apis::FunctionID::UI_PerformInteraction);
+ DisablePerformInteraction();
+ CommandRequestImpl::onTimeOut();
+ break;
+ }
+ default: {
+ LOG4CXX_ERROR(logger_, "INVALID ENUM");
+ return;
+ }
+ };
+}
+
+bool PerformInteractionRequest::ProcessVRResponse(
+ const smart_objects::SmartObject& message,
+ smart_objects::SmartObject& msg_params) {
+ LOG4CXX_AUTO_TRACE(logger_);
+ using namespace hmi_apis;
+ using namespace mobile_apis;
+ using namespace smart_objects;
+ using namespace helpers;
+
+ ApplicationSharedPtr app = application_manager_.application(connection_key());
+
+ if (!app) {
+ LOG4CXX_ERROR(logger_, "NULL pointer");
+ return false;
+ }
+
+ msg_params[strings::trigger_source] =
+ static_cast<int32_t>(TriggerSource::TS_VR);
+
+ const bool is_vr_aborted_timeout = Compare<Common_Result::eType, EQ, ONE>(
+ vr_result_code_, Common_Result::ABORTED, Common_Result::TIMED_OUT);
+
+ if (is_vr_aborted_timeout) {
+ LOG4CXX_DEBUG(logger_, "VR response aborted");
+ if (InteractionMode::VR_ONLY == interaction_mode_) {
+ LOG4CXX_DEBUG(logger_, "Aborted or Timeout Send Close Popup");
+ TerminatePerformInteraction();
+ SendResponse(false, MessageHelper::HMIToMobileResult(vr_result_code_));
+ return true;
+ }
+ LOG4CXX_DEBUG(logger_, "Update timeout for UI");
+ application_manager_.updateRequestTimeout(
+ connection_key(), correlation_id(), default_timeout());
+ return false;
+ }
+
+ const SmartObject& hmi_msg_params = message[strings::msg_params];
+ if (hmi_msg_params.keyExists(strings::choice_id)) {
+ const int choice_id = hmi_msg_params[strings::choice_id].asInt();
+ if (!CheckChoiceIDFromResponse(app, choice_id)) {
+ LOG4CXX_ERROR(logger_, "Wrong choiceID was received from HMI");
+ TerminatePerformInteraction();
+ SendResponse(
+ false, Result::GENERIC_ERROR, "Wrong choiceID was received from HMI");
+ return true;
+ }
+ msg_params[strings::choice_id] = choice_id;
+ }
+
+ const bool is_vr_result_success = Compare<Common_Result::eType, EQ, ONE>(
+ vr_result_code_, Common_Result::SUCCESS, Common_Result::WARNINGS);
+
+ if (is_vr_result_success &&
+ InteractionMode::MANUAL_ONLY == interaction_mode_) {
+ LOG4CXX_DEBUG(logger_,
+ "VR response is successfull in MANUAL_ONLY mode "
+ << "Wait for UI response");
+ // in case MANUAL_ONLY mode VR.PI SUCCESS just return
+ return false;
+ }
+
+ return false;
+}
+
+void PerformInteractionRequest::ProcessUIResponse(
+ const smart_objects::SmartObject& message,
+ smart_objects::SmartObject& msg_params) {
+ LOG4CXX_AUTO_TRACE(logger_);
+ using namespace helpers;
+ using namespace smart_objects;
+
+ ApplicationSharedPtr app = application_manager_.application(connection_key());
+ if (!app) {
+ LOG4CXX_ERROR(logger_, "NULL pointer");
+ return;
+ }
+
+ HmiInterfaces::InterfaceState ui_interface_state =
+ application_manager_.hmi_interfaces().GetInterfaceState(
+ HmiInterfaces::HMI_INTERFACE_UI);
+ bool result = false;
+ result = Compare<hmi_apis::Common_Result::eType, EQ, ONE>(
+ ui_result_code_,
+ hmi_apis::Common_Result::SUCCESS,
+ hmi_apis::Common_Result::WARNINGS);
+
+ result = result ||
+ (hmi_apis::Common_Result::UNSUPPORTED_RESOURCE == ui_result_code_ &&
+ HmiInterfaces::STATE_NOT_AVAILABLE != ui_interface_state);
+
+ const bool is_pi_warning = Compare<hmi_apis::Common_Result::eType, EQ, ONE>(
+ ui_result_code_, hmi_apis::Common_Result::WARNINGS);
+
+ const bool is_pi_unsupported =
+ Compare<hmi_apis::Common_Result::eType, EQ, ONE>(
+ ui_result_code_, hmi_apis::Common_Result::UNSUPPORTED_RESOURCE);
+
+ if (result) {
+ if (is_pi_unsupported) {
+ ui_result_code_ = hmi_apis::Common_Result::UNSUPPORTED_RESOURCE;
+ ui_info_ = message[strings::msg_params][strings::info].asString();
+ } else {
+ if (message.keyExists(strings::msg_params)) {
+ msg_params = message[strings::msg_params];
+ }
+ if (is_pi_warning) {
+ ui_result_code_ = hmi_apis::Common_Result::WARNINGS;
+ ui_info_ = message[strings::msg_params][strings::info].asString();
+ }
+ }
+
+ // result code must be GENERIC_ERROR in case wrong choice_id
+ if (msg_params.keyExists(strings::choice_id)) {
+ if (!CheckChoiceIDFromResponse(app,
+ msg_params[strings::choice_id].asInt())) {
+ ui_result_code_ = hmi_apis::Common_Result::GENERIC_ERROR;
+ ui_info_ = "Wrong choiceID was received from HMI";
+ } else {
+ msg_params[strings::trigger_source] =
+ mobile_apis::TriggerSource::TS_MENU;
+ }
+ } else if (msg_params.keyExists(strings::manual_text_entry)) {
+ msg_params[strings::trigger_source] =
+ mobile_apis::TriggerSource::TS_KEYBOARD;
+ if (msg_params[strings::manual_text_entry].empty()) {
+ msg_params.erase(strings::manual_text_entry);
+ }
+ }
+ }
+}
+
+void PerformInteractionRequest::SendUIPerformInteractionRequest(
+ application_manager::ApplicationSharedPtr const app) {
+ LOG4CXX_AUTO_TRACE(logger_);
+ smart_objects::SmartObject& choice_set_id_list =
+ (*message_)[strings::msg_params][strings::interaction_choice_set_id_list];
+
+ smart_objects::SmartObject msg_params =
+ smart_objects::SmartObject(smart_objects::SmartType_Map);
+
+ mobile_apis::InteractionMode::eType mode =
+ static_cast<mobile_apis::InteractionMode::eType>(
+ (*message_)[strings::msg_params][strings::interaction_mode].asInt());
+
+ if (mobile_apis::InteractionMode::VR_ONLY != mode) {
+ msg_params[hmi_request::initial_text][hmi_request::field_name] =
+ static_cast<int32_t>(
+ hmi_apis::Common_TextFieldName::initialInteractionText);
+ msg_params[hmi_request::initial_text][hmi_request::field_text] =
+ (*message_)[strings::msg_params][hmi_request::initial_text];
+ }
+ bool is_vr_help_item = false;
+ if (mobile_apis::InteractionMode::MANUAL_ONLY != mode) {
+ msg_params[strings::vr_help_title] =
+ (*message_)[strings::msg_params][strings::initial_text].asString();
+ if ((*message_)[strings::msg_params].keyExists(strings::vr_help)) {
+ is_vr_help_item = true;
+ msg_params[strings::vr_help] =
+ (*message_)[strings::msg_params][strings::vr_help];
+ }
+ }
+
+ if (mobile_apis::InteractionMode::BOTH == mode ||
+ mobile_apis::InteractionMode::MANUAL_ONLY == mode) {
+ msg_params[strings::timeout] = default_timeout_ / 2;
+ } else {
+ msg_params[strings::timeout] = default_timeout_;
+ }
+ msg_params[strings::app_id] = app->app_id();
+ if (mobile_apis::InteractionMode::VR_ONLY != mode) {
+ msg_params[strings::choice_set] =
+ smart_objects::SmartObject(smart_objects::SmartType_Array);
+ }
+ int32_t index_array_of_vr_help = 0;
+ for (size_t i = 0; i < choice_set_id_list.length(); ++i) {
+ smart_objects::SmartObject* choice_set =
+ app->FindChoiceSet(choice_set_id_list[i].asInt());
+ if (choice_set) {
+ // save perform interaction choice set
+ app->AddPerformInteractionChoiceSet(
+ correlation_id(), choice_set_id_list[i].asInt(), *choice_set);
+ for (size_t j = 0; j < (*choice_set)[strings::choice_set].length(); ++j) {
+ if (mobile_apis::InteractionMode::VR_ONLY != mode) {
+ size_t index = msg_params[strings::choice_set].length();
+ msg_params[strings::choice_set][index] =
+ (*choice_set)[strings::choice_set][j];
+ // vrCommands should be added via VR.AddCommand only
+ msg_params[strings::choice_set][index].erase(strings::vr_commands);
+ }
+ if (mobile_apis::InteractionMode::MANUAL_ONLY != mode &&
+ !is_vr_help_item) {
+ smart_objects::SmartObject& vr_commands =
+ (*choice_set)[strings::choice_set][j][strings::vr_commands];
+ if (0 < vr_commands.length()) {
+ // copy only first synonym
+ smart_objects::SmartObject item(smart_objects::SmartType_Map);
+ item[strings::text] = vr_commands[0].asString();
+ item[strings::position] = index_array_of_vr_help + 1;
+ msg_params[strings::vr_help][index_array_of_vr_help++] = item;
+ }
+ }
+ }
+ }
+ }
+ if ((*message_)[strings::msg_params].keyExists(
+ hmi_request::interaction_layout) &&
+ mobile_apis::InteractionMode::VR_ONLY != mode) {
+ msg_params[hmi_request::interaction_layout] =
+ (*message_)[strings::msg_params][hmi_request::interaction_layout]
+ .asInt();
+ }
+ StartAwaitForInterface(HmiInterfaces::HMI_INTERFACE_UI);
+ SendHMIRequest(
+ hmi_apis::FunctionID::UI_PerformInteraction, &msg_params, true);
+}
+
+void PerformInteractionRequest::SendVRPerformInteractionRequest(
+ application_manager::ApplicationSharedPtr const app) {
+ LOG4CXX_AUTO_TRACE(logger_);
+
+ smart_objects::SmartObject msg_params =
+ smart_objects::SmartObject(smart_objects::SmartType_Map);
+
+ smart_objects::SmartObject& choice_list =
+ (*message_)[strings::msg_params][strings::interaction_choice_set_id_list];
+
+ if (mobile_apis::InteractionMode::MANUAL_ONLY != interaction_mode_) {
+ msg_params[strings::grammar_id] =
+ smart_objects::SmartObject(smart_objects::SmartType_Array);
+
+ int32_t grammar_id_index = 0;
+ for (uint32_t i = 0; i < choice_list.length(); ++i) {
+ smart_objects::SmartObject* choice_set =
+ app->FindChoiceSet(choice_list[i].asInt());
+ if (!choice_set) {
+ LOG4CXX_WARN(logger_, "Couldn't found choiceset");
+ continue;
+ }
+ msg_params[strings::grammar_id][grammar_id_index++] =
+ (*choice_set)[strings::grammar_id].asUInt();
+ }
+ }
+
+ std::vector<std::string> invalid_params;
+ if ((*message_)[strings::msg_params].keyExists(strings::help_prompt)) {
+ smart_objects::SmartObject& help_prompt =
+ (*message_)[strings::msg_params][strings::help_prompt];
+ mobile_apis::Result::eType verification_result =
+ MessageHelper::VerifyTtsFiles(help_prompt, app, application_manager_);
+
+ if (mobile_apis::Result::FILE_NOT_FOUND == verification_result) {
+ LOG4CXX_WARN(logger_,
+ "MessageHelper::VerifyTtsFiles return "
+ << verification_result);
+ invalid_params.push_back("help_prompt");
+ } else {
+ msg_params[strings::help_prompt] = help_prompt;
+ }
+ } else {
+ if (choice_list.length() != 0) {
+ msg_params[strings::help_prompt] =
+ smart_objects::SmartObject(smart_objects::SmartType_Array);
+ }
+ int32_t index = 0;
+ for (uint32_t i = 0; i < choice_list.length(); ++i) {
+ smart_objects::SmartObject* choice_set =
+ app->FindChoiceSet(choice_list[i].asInt());
+
+ if (choice_set) {
+ for (uint32_t j = 0; j < (*choice_set)[strings::choice_set].length();
+ ++j) {
+ smart_objects::SmartObject& vr_commands =
+ (*choice_set)[strings::choice_set][j][strings::vr_commands];
+ if (0 < vr_commands.length()) {
+ // copy only first synonym
+ smart_objects::SmartObject item(smart_objects::SmartType_Map);
+ // Since there is no custom data from application side, SDL should
+ // construct prompt and append delimiter to each item
+ item[strings::type] = hmi_apis::Common_SpeechCapabilities::SC_TEXT;
+ item[strings::text] =
+ vr_commands[0].asString() +
+ application_manager_.get_settings().tts_delimiter();
+ msg_params[strings::help_prompt][index++] = item;
+ }
+ }
+ } else {
+ LOG4CXX_ERROR(logger_, "Can't found choiceSet!");
+ }
+ }
+ }
+
+ if ((*message_)[strings::msg_params].keyExists(strings::timeout_prompt)) {
+ smart_objects::SmartObject& timeout_prompt =
+ (*message_)[strings::msg_params][strings::timeout_prompt];
+ mobile_apis::Result::eType verification_result =
+ MessageHelper::VerifyTtsFiles(
+ timeout_prompt, app, application_manager_);
+
+ if (mobile_apis::Result::FILE_NOT_FOUND == verification_result) {
+ LOG4CXX_WARN(logger_,
+ "MessageHelper::VerifyTtsFiles return "
+ << verification_result);
+ invalid_params.push_back("timeout_prompt");
+ } else {
+ msg_params[strings::timeout_prompt] = timeout_prompt;
+ }
+ } else {
+ if (msg_params.keyExists(strings::help_prompt)) {
+ msg_params[strings::timeout_prompt] = msg_params[strings::help_prompt];
+ }
+ }
+
+ if ((*message_)[strings::msg_params].keyExists(strings::initial_prompt)) {
+ smart_objects::SmartObject& initial_prompt =
+ (*message_)[strings::msg_params][strings::initial_prompt];
+ mobile_apis::Result::eType verification_result =
+ MessageHelper::VerifyTtsFiles(
+ initial_prompt, app, application_manager_);
+
+ if (mobile_apis::Result::FILE_NOT_FOUND == verification_result) {
+ LOG4CXX_WARN(logger_,
+ "MessageHelper::VerifyTtsFiles return "
+ << verification_result);
+ invalid_params.push_back("initial_prompt");
+ } else {
+ msg_params[strings::initial_prompt] = initial_prompt;
+ }
+ }
+
+ if (!invalid_params.empty()) {
+ const std::string params_list =
+ std::accumulate(std::begin(invalid_params),
+ std::end(invalid_params),
+ std::string(""),
+ [](std::string& first, std::string& second) {
+ return first.empty() ? second : first + ", " + second;
+ });
+ const std::string info =
+ "One or more files needed for " + params_list + " are not present";
+ SendResponse(false, mobile_apis::Result::FILE_NOT_FOUND, info.c_str());
+ return;
+ }
+
+ mobile_apis::InteractionMode::eType mode =
+ static_cast<mobile_apis::InteractionMode::eType>(
+ (*message_)[strings::msg_params][strings::interaction_mode].asInt());
+
+ if (mobile_apis::InteractionMode::BOTH == mode ||
+ mobile_apis::InteractionMode::MANUAL_ONLY == mode) {
+ msg_params[strings::timeout] = default_timeout_ / 2;
+ } else {
+ msg_params[strings::timeout] = default_timeout_;
+ }
+ msg_params[strings::app_id] = app->app_id();
+ StartAwaitForInterface(HmiInterfaces::HMI_INTERFACE_VR);
+ SendHMIRequest(
+ hmi_apis::FunctionID::VR_PerformInteraction, &msg_params, true);
+}
+
+bool PerformInteractionRequest::CheckChoiceSetMenuNames(
+ application_manager::ApplicationSharedPtr const app) {
+ LOG4CXX_AUTO_TRACE(logger_);
+
+ smart_objects::SmartObject& choice_list =
+ (*message_)[strings::msg_params][strings::interaction_choice_set_id_list];
+
+ for (size_t i = 0; i < choice_list.length(); ++i) {
+ // choice_set contains SmartObject msg_params
+ smart_objects::SmartObject* i_choice_set =
+ app->FindChoiceSet(choice_list[i].asInt());
+
+ for (size_t j = 0; j < choice_list.length(); ++j) {
+ smart_objects::SmartObject* j_choice_set =
+ app->FindChoiceSet(choice_list[j].asInt());
+
+ if (i == j) {
+ // skip check the same element
+ continue;
+ }
+
+ if (!i_choice_set || !j_choice_set) {
+ LOG4CXX_ERROR(logger_, "Invalid ID");
+ SendResponse(false, mobile_apis::Result::INVALID_ID);
+ return false;
+ }
+
+ size_t ii = 0;
+ size_t jj = 0;
+ for (; ii < (*i_choice_set)[strings::choice_set].length(); ++ii) {
+ for (; jj < (*j_choice_set)[strings::choice_set].length(); ++jj) {
+ const std::string& ii_menu_name =
+ (*i_choice_set)[strings::choice_set][ii][strings::menu_name]
+ .asString();
+ const std::string& jj_menu_name =
+ (*j_choice_set)[strings::choice_set][jj][strings::menu_name]
+ .asString();
+
+ if (ii_menu_name == jj_menu_name) {
+ LOG4CXX_ERROR(logger_, "Choice set has duplicated menu name");
+ SendResponse(false,
+ mobile_apis::Result::DUPLICATE_NAME,
+ "Choice set has duplicated menu name");
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool PerformInteractionRequest::CheckChoiceSetVRSynonyms(
+ application_manager::ApplicationSharedPtr const app) {
+ LOG4CXX_AUTO_TRACE(logger_);
+
+ smart_objects::SmartObject& choice_list =
+ (*message_)[strings::msg_params][strings::interaction_choice_set_id_list];
+
+ for (size_t i = 0; i < choice_list.length(); ++i) {
+ // choice_set contains SmartObject msg_params
+ smart_objects::SmartObject* i_choice_set =
+ app->FindChoiceSet(choice_list[i].asInt());
+
+ for (size_t j = 0; j < choice_list.length(); ++j) {
+ smart_objects::SmartObject* j_choice_set =
+ app->FindChoiceSet(choice_list[j].asInt());
+
+ if (i == j) {
+ // skip check the same element
+ continue;
+ }
+
+ if ((!i_choice_set) || (!j_choice_set)) {
+ LOG4CXX_ERROR(logger_, "Invalid ID");
+ SendResponse(false, mobile_apis::Result::INVALID_ID);
+ return false;
+ }
+
+ size_t ii = 0;
+ size_t jj = 0;
+ for (; ii < (*i_choice_set)[strings::choice_set].length(); ++ii) {
+ for (; jj < (*j_choice_set)[strings::choice_set].length(); ++jj) {
+ if (!((*i_choice_set)[strings::choice_set][ii].keyExists(
+ strings::vr_commands) &&
+ (*j_choice_set)[strings::choice_set][jj].keyExists(
+ strings::vr_commands))) {
+ LOG4CXX_DEBUG(logger_,
+ "One or both sets has missing vr commands, skipping "
+ "synonym check");
+ return true;
+ }
+ // choice_set pointer contains SmartObject msg_params
+ smart_objects::SmartObject& ii_vr_commands =
+ (*i_choice_set)[strings::choice_set][ii][strings::vr_commands];
+
+ smart_objects::SmartObject& jj_vr_commands =
+ (*j_choice_set)[strings::choice_set][jj][strings::vr_commands];
+
+ for (size_t iii = 0; iii < ii_vr_commands.length(); ++iii) {
+ for (size_t jjj = 0; jjj < jj_vr_commands.length(); ++jjj) {
+ const custom_str::CustomString& vr_cmd_i =
+ ii_vr_commands[iii].asCustomString();
+ const custom_str::CustomString& vr_cmd_j =
+ jj_vr_commands[jjj].asCustomString();
+ if (vr_cmd_i.CompareIgnoreCase(vr_cmd_j)) {
+ LOG4CXX_ERROR(logger_, "Choice set has duplicated VR synonym");
+ SendResponse(false,
+ mobile_apis::Result::DUPLICATE_NAME,
+ "Choice set has duplicated VR synonym");
+ return false;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return true;
+}
+
+bool PerformInteractionRequest::CheckVrHelpItemPositions(
+ application_manager::ApplicationSharedPtr const app) {
+ LOG4CXX_AUTO_TRACE(logger_);
+
+ if (!(*message_)[strings::msg_params].keyExists(strings::vr_help)) {
+ LOG4CXX_DEBUG(logger_, strings::vr_help << " is omitted.");
+ return true;
+ }
+
+ smart_objects::SmartObject& vr_help =
+ (*message_)[strings::msg_params][strings::vr_help];
+
+ int32_t position = 1;
+ for (size_t i = 0; i < vr_help.length(); ++i) {
+ if (position != vr_help[i][strings::position].asInt()) {
+ LOG4CXX_ERROR(logger_, "Non-sequential vrHelp item position");
+ SendResponse(false,
+ mobile_apis::Result::REJECTED,
+ "Non-sequential vrHelp item position");
+ return false;
+ }
+ ++position;
+ }
+ return true;
+}
+
+void PerformInteractionRequest::DisablePerformInteraction() {
+ LOG4CXX_AUTO_TRACE(logger_);
+
+ ApplicationSharedPtr app = application_manager_.application(connection_key());
+ if (!app) {
+ LOG4CXX_ERROR(logger_, "NULL pointer");
+ return;
+ }
+
+ if (app->is_perform_interaction_active()) {
+ // decrease amount of active requests
+ --pi_requests_count_;
+ if (!pi_requests_count_) {
+ app->set_perform_interaction_active(false);
+ app->set_perform_interaction_mode(-1);
+ }
+ }
+ app->DeletePerformInteractionChoiceSet(correlation_id());
+}
+
+bool PerformInteractionRequest::IsWhiteSpaceExist() {
+ LOG4CXX_AUTO_TRACE(logger_);
+ const char* str = NULL;
+
+ str = (*message_)[strings::msg_params][strings::initial_text].asCharArray();
+ if (!CheckSyntax(str)) {
+ LOG4CXX_ERROR(logger_, "Invalid initial_text syntax check failed");
+ return true;
+ }
+
+ if ((*message_)[strings::msg_params].keyExists(strings::initial_prompt)) {
+ const smart_objects::SmartArray* ip_array =
+ (*message_)[strings::msg_params][strings::initial_prompt].asArray();
+
+ smart_objects::SmartArray::const_iterator it_ip = ip_array->begin();
+ smart_objects::SmartArray::const_iterator it_ip_end = ip_array->end();
+
+ for (; it_ip != it_ip_end; ++it_ip) {
+ str = (*it_ip)[strings::text].asCharArray();
+ if (strlen(str) && !CheckSyntax(str)) {
+ LOG4CXX_ERROR(logger_, "Invalid initial_prompt syntax check failed");
+ return true;
+ }
+ }
+ }
+
+ if ((*message_)[strings::msg_params].keyExists(strings::help_prompt)) {
+ const smart_objects::SmartArray* hp_array =
+ (*message_)[strings::msg_params][strings::help_prompt].asArray();
+
+ smart_objects::SmartArray::const_iterator it_hp = hp_array->begin();
+ smart_objects::SmartArray::const_iterator it_hp_end = hp_array->end();
+
+ for (; it_hp != it_hp_end; ++it_hp) {
+ str = (*it_hp)[strings::text].asCharArray();
+ if (strlen(str) && !CheckSyntax(str)) {
+ LOG4CXX_ERROR(logger_, "Invalid help_prompt syntax check failed");
+ return true;
+ }
+ }
+ }
+
+ if ((*message_)[strings::msg_params].keyExists(strings::timeout_prompt)) {
+ const smart_objects::SmartArray* tp_array =
+ (*message_)[strings::msg_params][strings::timeout_prompt].asArray();
+
+ smart_objects::SmartArray::const_iterator it_tp = tp_array->begin();
+ smart_objects::SmartArray::const_iterator it_tp_end = tp_array->end();
+
+ for (; it_tp != it_tp_end; ++it_tp) {
+ str = (*it_tp)[strings::text].asCharArray();
+ if (strlen(str) && !CheckSyntax(str)) {
+ LOG4CXX_ERROR(logger_, "Invalid timeout_prompt syntax check failed");
+ return true;
+ }
+ }
+ }
+
+ if ((*message_)[strings::msg_params].keyExists(strings::vr_help)) {
+ const smart_objects::SmartArray* vh_array =
+ (*message_)[strings::msg_params][strings::vr_help].asArray();
+
+ smart_objects::SmartArray::const_iterator it_vh = vh_array->begin();
+ smart_objects::SmartArray::const_iterator it_vh_end = vh_array->end();
+
+ for (; it_vh != it_vh_end; ++it_vh) {
+ str = (*it_vh)[strings::text].asCharArray();
+ if (!CheckSyntax(str)) {
+ LOG4CXX_ERROR(logger_, "Invalid vr_help syntax check failed");
+ return true;
+ }
+
+ if ((*it_vh).keyExists(strings::image)) {
+ str = (*it_vh)[strings::image][strings::value].asCharArray();
+ if (!CheckSyntax(str)) {
+ LOG4CXX_ERROR(logger_,
+ "Invalid vr_help image value syntax check failed");
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void PerformInteractionRequest::TerminatePerformInteraction() {
+ LOG4CXX_AUTO_TRACE(logger_);
+
+ smart_objects::SmartObject msg_params =
+ smart_objects::SmartObject(smart_objects::SmartType_Map);
+ msg_params[hmi_request::method_name] = "UI.PerformInteraction";
+ SendHMIRequest(hmi_apis::FunctionID::UI_ClosePopUp, &msg_params);
+ DisablePerformInteraction();
+}
+
+bool PerformInteractionRequest::CheckChoiceIDFromResponse(
+ ApplicationSharedPtr app, int32_t choice_id) {
+ LOG4CXX_AUTO_TRACE(logger_);
+ const DataAccessor<PerformChoiceSetMap> accessor =
+ app->performinteraction_choice_set_map();
+ const PerformChoiceSetMap& choice_set_map = accessor.GetData();
+
+ PerformChoiceSetMap::const_iterator choice_set_map_it =
+ choice_set_map.find(correlation_id());
+ if (choice_set_map.end() != choice_set_map_it) {
+ const PerformChoice& choice = choice_set_map_it->second;
+ PerformChoice::const_iterator it = choice.begin();
+ for (; choice.end() != it; ++it) {
+ const smart_objects::SmartObject& choice_set =
+ (*it->second).getElement(strings::choice_set);
+ for (size_t j = 0; j < choice_set.length(); ++j) {
+ if (choice_id ==
+ choice_set.getElement(j).getElement(strings::choice_id).asInt()) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool PerformInteractionRequest::CheckChoiceSetListVRCommands(
+ ApplicationSharedPtr app) {
+ LOG4CXX_AUTO_TRACE(logger_);
+
+ const smart_objects::SmartObject& choice_set_id_list =
+ (*message_)[strings::msg_params][strings::interaction_choice_set_id_list];
+
+ smart_objects::SmartObject* choice_set = nullptr;
+
+ for (size_t i = 0; i < choice_set_id_list.length(); ++i) {
+ choice_set = app->FindChoiceSet(choice_set_id_list[i].asInt());
+
+ // this should never ever happen since this was already checked
+ if (choice_set == nullptr) {
+ LOG4CXX_ERROR(
+ logger_,
+ "Couldn't find choiceset_id = " << choice_set_id_list[i].asInt());
+ SendResponse(false, mobile_apis::Result::INVALID_ID);
+ return false;
+ }
+
+ const smart_objects::SmartObject& choices_list =
+ (*choice_set)[strings::choice_set];
+ auto vr_status = MessageHelper::CheckChoiceSetVRCommands(choices_list);
+
+ // if not all choices have vr commands
+ if (vr_status != MessageHelper::ChoiceSetVRCommandsStatus::ALL) {
+ LOG4CXX_ERROR(logger_,
+ "PerformInteraction has choice sets with "
+ "missing vrCommands, not in MANUAL_ONLY mode");
+ SendResponse(false,
+ mobile_apis::Result::INVALID_DATA,
+ "Some choices don't contain VR commands.");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool PerformInteractionRequest::CheckChoiceIDFromRequest(
+ ApplicationSharedPtr app,
+ const size_t choice_set_id_list_length,
+ const smart_objects::SmartObject& choice_set_id_list) const {
+ LOG4CXX_AUTO_TRACE(logger_);
+
+ size_t choice_list_length = 0;
+ std::set<uint32_t> choice_id_set;
+ smart_objects::SmartObject* choice_set = 0;
+ std::pair<std::set<uint32_t>::iterator, bool> ins_res;
+
+ for (size_t i = 0; i < choice_set_id_list_length; ++i) {
+ choice_set = app->FindChoiceSet(choice_set_id_list[i].asInt());
+ if (!choice_set) {
+ LOG4CXX_ERROR(
+ logger_,
+ "Couldn't find choiceset_id = " << choice_set_id_list[i].asInt());
+ return false;
+ }
+
+ choice_list_length = (*choice_set)[strings::choice_set].length();
+ const smart_objects::SmartObject& choices_list =
+ (*choice_set)[strings::choice_set];
+ for (size_t k = 0; k < choice_list_length; ++k) {
+ ins_res =
+ choice_id_set.insert(choices_list[k][strings::choice_id].asInt());
+ if (!ins_res.second) {
+ LOG4CXX_ERROR(logger_,
+ "choice with ID "
+ << choices_list[k][strings::choice_id].asInt()
+ << " already exists");
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+const bool PerformInteractionRequest::HasHMIResponsesToWait() const {
+ LOG4CXX_AUTO_TRACE(logger_);
+ return !ui_response_received_ || !vr_response_received_;
+}
+
+void PerformInteractionRequest::SendBothModeResponse(
+ const smart_objects::SmartObject& msg_param) {
+ LOG4CXX_AUTO_TRACE(logger_);
+ mobile_apis::Result::eType perform_interaction_result_code =
+ mobile_apis::Result::INVALID_ENUM;
+ app_mngr::commands::ResponseInfo ui_perform_info(
+ ui_result_code_, HmiInterfaces::HMI_INTERFACE_UI, application_manager_);
+ app_mngr::commands::ResponseInfo vr_perform_info(
+ vr_result_code_, HmiInterfaces::HMI_INTERFACE_VR, application_manager_);
+ const bool result =
+ PrepareResultForMobileResponse(ui_perform_info, vr_perform_info);
+ perform_interaction_result_code =
+ PrepareResultCodeForResponse(ui_perform_info, vr_perform_info);
+ const smart_objects::SmartObject* response_params =
+ msg_param.empty() ? NULL : &msg_param;
+ std::string info = app_mngr::commands::MergeInfos(
+ ui_perform_info, ui_info_, vr_perform_info, vr_info_);
+ DisablePerformInteraction();
+ SendResponse(result,
+ perform_interaction_result_code,
+ info.empty() ? NULL : info.c_str(),
+ response_params);
+}
+
+} // namespace commands
+
+} // namespace application_manager