diff options
Diffstat (limited to 'src/components/application_manager/src/resumption/resumption_data_processor_impl.cc')
-rw-r--r-- | src/components/application_manager/src/resumption/resumption_data_processor_impl.cc | 1100 |
1 files changed, 1100 insertions, 0 deletions
diff --git a/src/components/application_manager/src/resumption/resumption_data_processor_impl.cc b/src/components/application_manager/src/resumption/resumption_data_processor_impl.cc new file mode 100644 index 0000000000..d1635eb557 --- /dev/null +++ b/src/components/application_manager/src/resumption/resumption_data_processor_impl.cc @@ -0,0 +1,1100 @@ +/* + Copyright (c) 2020, 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 <algorithm> + +#include "application_manager/application_manager.h" +#include "application_manager/commands/command_impl.h" +#include "application_manager/display_capabilities_builder.h" +#include "application_manager/event_engine/event_observer.h" +#include "application_manager/message_helper.h" +#include "application_manager/resumption/resumption_data_processor_impl.h" +#include "application_manager/smart_object_keys.h" + +namespace resumption { + +using app_mngr::AppFile; +using app_mngr::ApplicationSharedPtr; +using app_mngr::ButtonSubscriptions; +using app_mngr::ChoiceSetMap; +using app_mngr::MessageHelper; +namespace strings = app_mngr::strings; +namespace event_engine = app_mngr::event_engine; +namespace commands = app_mngr::commands; + +SDL_CREATE_LOG_VARIABLE("Resumption") + +bool ResumptionRequestID::operator<(const ResumptionRequestID& other) const { + return correlation_id < other.correlation_id || + function_id < other.function_id; +} + +ResumptionDataProcessorImpl::ResumptionDataProcessorImpl( + app_mngr::ApplicationManager& application_manager) + : event_engine::EventObserver(application_manager.event_dispatcher()) + , application_manager_(application_manager) + , resumption_status_lock_() + , register_callbacks_lock_() + , request_app_ids_lock_() {} + +ResumptionDataProcessorImpl::~ResumptionDataProcessorImpl() {} + +void ResumptionDataProcessorImpl::Restore( + ApplicationSharedPtr application, + smart_objects::SmartObject& saved_app, + ResumeCtrl::ResumptionCallBack callback) { + SDL_LOG_AUTO_TRACE(); + + if (!HasDataToRestore(saved_app)) { + SDL_LOG_DEBUG("No data to restore, resumption is successful"); + callback(mobile_apis::Result::SUCCESS, "Data resumption succesful"); + return; + } + + AddFiles(application, saved_app); + AddSubmenus(application, saved_app); + AddCommands(application, saved_app); + AddChoicesets(application, saved_app); + SetGlobalProperties(application, saved_app); + AddSubscriptions(application, saved_app); + AddWindows(application, saved_app); + + resumption_status_lock_.AcquireForReading(); + const auto app_id = application->app_id(); + bool is_requests_list_empty = true; + if (resumption_status_.find(app_id) != resumption_status_.end()) { + is_requests_list_empty = + resumption_status_[app_id].list_of_sent_requests.empty(); + } + resumption_status_lock_.Release(); + + if (!is_requests_list_empty) { + sync_primitives::AutoWriteLock lock(register_callbacks_lock_); + register_callbacks_[app_id] = callback; + } else { + SDL_LOG_DEBUG("No requests to HMI for " << app_id + << " , resumption is successful"); + callback(mobile_apis::Result::SUCCESS, "Data resumption successful"); + } +} + +bool ResumptionDataProcessorImpl::HasDataToRestore( + const smart_objects::SmartObject& saved_app) const { + SDL_LOG_AUTO_TRACE(); + + auto has_data_to_restore = [&saved_app]() -> bool { + return !saved_app[strings::application_files].empty() || + !saved_app[strings::application_submenus].empty() || + !saved_app[strings::application_commands].empty() || + !saved_app[strings::application_choice_sets].empty() || + !saved_app[strings::windows_info].empty(); + }; + + auto has_gp_to_restore = [&saved_app]() -> bool { + const smart_objects::SmartObject& global_properties = + saved_app[strings::application_global_properties]; + + return !global_properties[strings::help_prompt].empty() || + !global_properties[strings::keyboard_properties].empty() || + !global_properties[strings::menu_icon].empty() || + !global_properties[strings::menu_title].empty() || + !global_properties[strings::timeout_prompt].empty() || + !global_properties[strings::vr_help].empty() || + !global_properties[strings::vr_help_title].empty(); + }; + + auto has_subscriptions_to_restore = [&saved_app]() -> bool { + const smart_objects::SmartObject& subscriptions = + saved_app[strings::application_subscriptions]; + + const bool has_ivi_subscriptions = + !subscriptions[strings::application_vehicle_info].empty(); + + const bool has_button_subscriptions = + !subscriptions[strings::application_buttons].empty() && + !(subscriptions[strings::application_buttons].length() == 1 && + static_cast<hmi_apis::Common_ButtonName::eType>( + subscriptions[strings::application_buttons][0].asInt()) == + hmi_apis::Common_ButtonName::CUSTOM_BUTTON); + + const bool has_waypoints_subscriptions = + subscriptions[strings::subscribed_for_way_points].asBool(); + + const bool has_appservice_subscriptions = + subscriptions.keyExists(app_mngr::hmi_interface::app_service) && + !subscriptions[app_mngr::hmi_interface::app_service].empty(); + + const bool has_system_capability_subscriptions = + subscriptions.keyExists(strings::system_capability) && + !subscriptions[strings::system_capability].empty(); + + return has_ivi_subscriptions || has_button_subscriptions || + has_waypoints_subscriptions || has_appservice_subscriptions || + has_system_capability_subscriptions; + }; + + if (has_data_to_restore()) { + SDL_LOG_DEBUG("Application has data to restore"); + return true; + } + + if (has_gp_to_restore()) { + SDL_LOG_DEBUG("Application has global properties to restore"); + return true; + } + + if (has_subscriptions_to_restore()) { + SDL_LOG_DEBUG("Application has subscriptions to restore"); + return true; + } + + SDL_LOG_DEBUG("Application does not have any data to restore"); + return false; +} + +utils::Optional<uint32_t> +ResumptionDataProcessorImpl::GetAppIdWaitingForResponse( + const hmi_apis::FunctionID::eType function_id, const int32_t corr_id) { + SDL_LOG_AUTO_TRACE(); + + auto predicate = + [function_id, + corr_id](const std::pair<ResumptionRequestID, std::uint32_t>& item) { + return item.first.function_id == function_id && + item.first.correlation_id == corr_id; + }; + + sync_primitives::AutoReadLock lock(request_app_ids_lock_); + auto app_id_ptr = + std::find_if(request_app_ids_.begin(), request_app_ids_.end(), predicate); + + if (app_id_ptr == request_app_ids_.end()) { + return utils::Optional<uint32_t>::OptionalEmpty::EMPTY; + } + return utils::Optional<uint32_t>(app_id_ptr->second); +} + +utils::Optional<ResumptionRequest> ResumptionDataProcessorImpl::GetRequest( + const uint32_t app_id, + const hmi_apis::FunctionID::eType function_id, + const int32_t corr_id) { + SDL_LOG_AUTO_TRACE(); + + sync_primitives::AutoReadLock lock(resumption_status_lock_); + std::vector<ResumptionRequest>& list_of_sent_requests = + resumption_status_[app_id].list_of_sent_requests; + + if (resumption_status_.find(app_id) == resumption_status_.end()) { + SDL_LOG_ERROR("No resumption status info found for app_id: " << app_id); + return utils::Optional<ResumptionRequest>::OptionalEmpty::EMPTY; + } + + auto request_iter = + std::find_if(list_of_sent_requests.begin(), + list_of_sent_requests.end(), + [function_id, corr_id](const ResumptionRequest& request) { + return request.request_id.correlation_id == corr_id && + request.request_id.function_id == function_id; + }); + + if (list_of_sent_requests.end() == request_iter) { + return utils::Optional<ResumptionRequest>::OptionalEmpty::EMPTY; + } + return utils::Optional<ResumptionRequest>(*request_iter); +} + +void ResumptionDataProcessorImpl::ProcessResumptionStatus( + const uint32_t app_id, + const smart_objects::SmartObject& response, + const ResumptionRequest& found_request) { + SDL_LOG_AUTO_TRACE(); + + sync_primitives::AutoWriteLock lock(resumption_status_lock_); + ApplicationResumptionStatus& status = resumption_status_[app_id]; + + if (IsResponseSuccessful(response)) { + status.successful_requests.push_back(found_request); + } else { + status.error_requests.push_back(found_request); + } + + if (hmi_apis::FunctionID::VehicleInfo_SubscribeVehicleData == + found_request.request_id.function_id) { + CheckVehicleDataResponse(found_request.message, response, status); + } + + if (hmi_apis::FunctionID::UI_CreateWindow == + found_request.request_id.function_id) { + CheckCreateWindowResponse(found_request.message, response); + } +} + +bool ResumptionDataProcessorImpl::IsResumptionFinished( + const uint32_t app_id, const ResumptionRequest& found_request) { + SDL_LOG_AUTO_TRACE(); + + sync_primitives::AutoWriteLock lock(resumption_status_lock_); + auto& list_of_sent_requests = + resumption_status_[app_id].list_of_sent_requests; + auto request_iter = + std::find_if(list_of_sent_requests.begin(), + list_of_sent_requests.end(), + [found_request](const ResumptionRequest& request) { + return request.request_id.correlation_id == + found_request.request_id.correlation_id && + request.request_id.function_id == + found_request.request_id.function_id; + }); + list_of_sent_requests.erase(request_iter); + + return list_of_sent_requests.empty(); +} + +utils::Optional<ResumeCtrl::ResumptionCallBack> +ResumptionDataProcessorImpl::GetResumptionCallback(const uint32_t app_id) { + SDL_LOG_AUTO_TRACE(); + + sync_primitives::AutoReadLock lock(register_callbacks_lock_); + auto it = register_callbacks_.find(app_id); + if (it == register_callbacks_.end()) { + return utils::Optional< + ResumeCtrl::ResumptionCallBack>::OptionalEmpty::EMPTY; + } + return utils::Optional<ResumeCtrl::ResumptionCallBack>(it->second); +} + +bool ResumptionDataProcessorImpl::IsResumptionSuccessful( + const uint32_t app_id) { + sync_primitives::AutoReadLock lock(resumption_status_lock_); + auto it = resumption_status_.find(app_id); + if (resumption_status_.end() == it) { + return false; + } + + const ApplicationResumptionStatus& status = it->second; + return status.error_requests.empty() && + status.unsuccessful_vehicle_data_subscriptions_.empty(); +} + +void ResumptionDataProcessorImpl::EraseAppResumptionData( + const uint32_t app_id, + const hmi_apis::FunctionID::eType function_id, + const int32_t corr_id) { + SDL_LOG_AUTO_TRACE(); + + resumption_status_lock_.AcquireForWriting(); + resumption_status_.erase(app_id); + resumption_status_lock_.Release(); + + request_app_ids_lock_.AcquireForWriting(); + request_app_ids_.erase({function_id, corr_id}); + request_app_ids_lock_.Release(); + + register_callbacks_lock_.AcquireForWriting(); + register_callbacks_.erase(app_id); + register_callbacks_lock_.Release(); +} + +void ResumptionDataProcessorImpl::ProcessResponseFromHMI( + const smart_objects::SmartObject& response, + const hmi_apis::FunctionID::eType function_id, + const int32_t corr_id) { + SDL_LOG_AUTO_TRACE(); + SDL_LOG_TRACE("Now processing event with function id: " + << function_id << " correlation id: " << corr_id); + + auto found_app_id = GetAppIdWaitingForResponse(function_id, corr_id); + if (!found_app_id) { + SDL_LOG_ERROR("Application id for correlation id " + << corr_id << " and function id: " << function_id + << " was not found, such response is not expected."); + return; + } + const uint32_t app_id = *found_app_id; + SDL_LOG_DEBUG("app_id is: " << app_id); + + auto found_request = GetRequest(app_id, function_id, corr_id); + if (!found_request) { + SDL_LOG_ERROR("Request with function id " << function_id << " and corr id " + << corr_id << " not found"); + return; + } + auto request = *found_request; + + ProcessResumptionStatus(app_id, response, request); + + if (!IsResumptionFinished(app_id, request)) { + SDL_LOG_DEBUG("Resumption app " + << app_id << " not finished. Some requests are still waited"); + return; + } + + auto found_callback = GetResumptionCallback(app_id); + if (!found_callback) { + SDL_LOG_ERROR("Callback for app_id: " << app_id << " not found"); + return; + } + auto callback = *found_callback; + + if (IsResumptionSuccessful(app_id)) { + SDL_LOG_DEBUG("Resumption for app " << app_id << " successful"); + callback(mobile_apis::Result::SUCCESS, "Data resumption successful"); + application_manager_.state_controller().ResumePostponedWindows(app_id); + } else { + SDL_LOG_ERROR("Resumption for app " << app_id << " failed"); + callback(mobile_apis::Result::RESUME_FAILED, "Data resumption failed"); + RevertRestoredData(application_manager_.application(app_id)); + application_manager_.state_controller().DropPostponedWindows(app_id); + } + + EraseAppResumptionData(app_id, function_id, corr_id); +} + +void ResumptionDataProcessorImpl::HandleOnTimeOut( + const uint32_t corr_id, const hmi_apis::FunctionID::eType function_id) { + SDL_LOG_AUTO_TRACE(); + SDL_LOG_DEBUG("Handling timeout with corr id: " + << corr_id << " and function_id: " << function_id); + + auto error_response = MessageHelper::CreateNegativeResponseFromHmi( + function_id, + corr_id, + hmi_apis::Common_Result::GENERIC_ERROR, + std::string()); + ProcessResponseFromHMI(*error_response, function_id, corr_id); +} + +void ResumptionDataProcessorImpl::on_event(const event_engine::Event& event) { + SDL_LOG_AUTO_TRACE(); + SDL_LOG_DEBUG( + "Handling response message from HMI " + << event.id() << " " + << event.smart_object()[strings::params][strings::correlation_id] + .asInt()); + ProcessResponseFromHMI( + event.smart_object(), event.id(), event.smart_object_correlation_id()); +} + +std::vector<ResumptionRequest> GetAllFailedRequests( + uint32_t app_id, + const std::map<std::int32_t, ApplicationResumptionStatus>& + resumption_status, + sync_primitives::RWLock& resumption_status_lock) { + resumption_status_lock.AcquireForReading(); + std::vector<ResumptionRequest> failed_requests; + std::vector<ResumptionRequest> missed_requests; + auto it = resumption_status.find(app_id); + if (it != resumption_status.end()) { + failed_requests = it->second.error_requests; + missed_requests = it->second.list_of_sent_requests; + } + resumption_status_lock.Release(); + + failed_requests.insert( + failed_requests.end(), missed_requests.begin(), missed_requests.end()); + return failed_requests; +} + +void ResumptionDataProcessorImpl::RevertRestoredData( + ApplicationSharedPtr application) { + SDL_LOG_AUTO_TRACE(); + SDL_LOG_DEBUG("Reverting for app: " << application->app_id()); + DeleteSubmenus(application); + DeleteCommands(application); + DeleteChoicesets(application); + DeleteGlobalProperties(application); + DeleteSubscriptions(application); + DeleteWindowsSubscriptions(application); + + resumption_status_lock_.AcquireForWriting(); + resumption_status_.erase(application->app_id()); + resumption_status_lock_.Release(); + + register_callbacks_lock_.AcquireForWriting(); + register_callbacks_.erase(application->app_id()); + register_callbacks_lock_.Release(); +} + +void ResumptionDataProcessorImpl::SubscribeToResponse( + const int32_t app_id, const ResumptionRequest& request) { + SDL_LOG_AUTO_TRACE(); + SDL_LOG_DEBUG("App " << app_id << " subscribe on " + << request.request_id.function_id << " " + << request.request_id.correlation_id); + subscribe_on_event(request.request_id.function_id, + request.request_id.correlation_id); + + resumption_status_lock_.AcquireForWriting(); + resumption_status_[app_id].list_of_sent_requests.push_back(request); + resumption_status_lock_.Release(); + + request_app_ids_lock_.AcquireForWriting(); + request_app_ids_.insert(std::make_pair(request.request_id, app_id)); + request_app_ids_lock_.Release(); +} + +void ResumptionDataProcessorImpl::ProcessMessageToHMI( + smart_objects::SmartObjectSPtr message, bool subscribe_on_response) { + SDL_LOG_AUTO_TRACE(); + if (subscribe_on_response) { + auto function_id = static_cast<hmi_apis::FunctionID::eType>( + (*message)[strings::params][strings::function_id].asInt()); + + const int32_t hmi_correlation_id = + (*message)[strings::params][strings::correlation_id].asInt(); + + const int32_t app_id = + (*message)[strings::msg_params][strings::app_id].asInt(); + + ResumptionRequest wait_for_response; + wait_for_response.request_id.correlation_id = hmi_correlation_id; + wait_for_response.request_id.function_id = function_id; + wait_for_response.message = *message; + + SubscribeToResponse(app_id, wait_for_response); + } + if (!application_manager_.GetRPCService().ManageHMICommand(message)) { + SDL_LOG_ERROR("Unable to send request"); + } +} + +void ResumptionDataProcessorImpl::ProcessMessagesToHMI( + const smart_objects::SmartObjectList& messages) { + SDL_LOG_AUTO_TRACE(); + for (const auto& message : messages) { + const bool is_request_message = + application_manager::MessageType::kRequest == + (*message)[strings::params][strings::message_type].asInt(); + + ProcessMessageToHMI(message, is_request_message); + } +} + +void ResumptionDataProcessorImpl::AddFiles( + app_mngr::ApplicationSharedPtr application, + const smart_objects::SmartObject& saved_app) { + SDL_LOG_AUTO_TRACE(); + if (!saved_app.keyExists(strings::application_files)) { + SDL_LOG_ERROR("application_files section is not exists"); + return; + } + + const auto application_files = + saved_app[strings::application_files].asArray(); + + for (const auto file_data : *application_files) { + const bool is_persistent = file_data.keyExists(strings::persistent_file) && + file_data[strings::persistent_file].asBool(); + if (is_persistent) { + AppFile file; + file.is_persistent = is_persistent; + file.is_download_complete = + file_data[strings::is_download_complete].asBool(); + file.file_name = file_data[strings::sync_file_name].asString(); + file.file_type = static_cast<mobile_apis::FileType::eType>( + file_data[strings::file_type].asInt()); + application->AddFile(file); + } + } +} + +void ResumptionDataProcessorImpl::AddWindows( + application_manager::ApplicationSharedPtr application, + const ns_smart_device_link::ns_smart_objects::SmartObject& saved_app) { + SDL_LOG_AUTO_TRACE(); + + if (!saved_app.keyExists(strings::windows_info)) { + SDL_LOG_ERROR("windows_info section does not exist"); + return; + } + + const auto& windows_info = saved_app[strings::windows_info]; + auto request_list = MessageHelper::CreateUICreateWindowRequestsToHMI( + application, application_manager_, windows_info); + + ProcessMessagesToHMI(request_list); +} + +void ResumptionDataProcessorImpl::AddSubmenus( + ApplicationSharedPtr application, + const smart_objects::SmartObject& saved_app) { + SDL_LOG_AUTO_TRACE(); + + if (!saved_app.keyExists(strings::application_submenus)) { + SDL_LOG_ERROR("application_submenus section is not exists"); + return; + } + + const smart_objects::SmartObject& app_submenus = + saved_app[strings::application_submenus]; + + for (size_t i = 0; i < app_submenus.length(); ++i) { + const smart_objects::SmartObject& submenu = app_submenus[i]; + application->AddSubMenu(submenu[strings::menu_id].asUInt(), submenu); + } + + ProcessMessagesToHMI(MessageHelper::CreateAddSubMenuRequestsToHMI( + application, application_manager_)); +} + +utils::Optional<ResumptionRequest> FindResumptionSubmenuRequest( + uint32_t menu_id, std::vector<ResumptionRequest>& requests) { + using namespace utils; + + auto request_it = std::find_if( + requests.begin(), + requests.end(), + [menu_id](const ResumptionRequest& request) { + auto& msg_params = request.message[strings::msg_params]; + if (hmi_apis::FunctionID::UI_AddSubMenu == + request.request_id.function_id) { + uint32_t failed_menu_id = msg_params[strings::menu_id].asUInt(); + return failed_menu_id == menu_id; + } + return false; + }); + if (requests.end() != request_it) { + return Optional<ResumptionRequest>(*request_it); + } + return Optional<ResumptionRequest>::OptionalEmpty::EMPTY; +} + +void ResumptionDataProcessorImpl::DeleteSubmenus( + ApplicationSharedPtr application) { + SDL_LOG_AUTO_TRACE(); + + auto failed_requests = GetAllFailedRequests( + application->app_id(), resumption_status_, resumption_status_lock_); + + auto accessor = application->sub_menu_map(); + const auto& sub_menu_map = accessor.GetData(); + + for (const auto& smenu : sub_menu_map) { + auto failed_submenu_request = + FindResumptionSubmenuRequest(smenu.first, failed_requests); + if (!failed_submenu_request) { + MessageHelper::SendDeleteSubmenuRequest( + smenu.second, application, application_manager_); + } + application->RemoveSubMenu(smenu.first); + } +} + +void ResumptionDataProcessorImpl::AddCommands( + ApplicationSharedPtr application, + const smart_objects::SmartObject& saved_app) { + SDL_LOG_AUTO_TRACE(); + if (!saved_app.keyExists(strings::application_commands)) { + SDL_LOG_ERROR("application_commands section is not exists"); + return; + } + + const smart_objects::SmartObject& app_commands = + saved_app[strings::application_commands]; + + for (size_t cmd_num = 0; cmd_num < app_commands.length(); ++cmd_num) { + const smart_objects::SmartObject& command = app_commands[cmd_num]; + const uint32_t cmd_id = command[strings::cmd_id].asUInt(); + const uint32_t consecutive_num = + commands::CommandImpl::CalcCommandInternalConsecutiveNumber( + application); + + application->AddCommand(consecutive_num, command); + application->help_prompt_manager().OnVrCommandAdded( + cmd_id, command, true); // is_resumption =true + } + + ProcessMessagesToHMI(MessageHelper::CreateAddCommandRequestToHMI( + application, application_manager_)); +} + +utils::Optional<ResumptionRequest> FindCommandResumptionRequest( + uint32_t command_id, std::vector<ResumptionRequest>& requests) { + using namespace utils; + + auto request_it = std::find_if( + requests.begin(), + requests.end(), + [command_id](const ResumptionRequest& request) { + auto& msg_params = request.message[strings::msg_params]; + const bool is_vr_command = hmi_apis::FunctionID::VR_AddCommand == + request.request_id.function_id && + hmi_apis::Common_VRCommandType::Command == + msg_params[strings::type].asInt(); + const bool is_ui_command = hmi_apis::FunctionID::UI_AddCommand == + request.request_id.function_id; + + if (is_vr_command || is_ui_command) { + uint32_t cmd_id = msg_params[strings::cmd_id].asUInt(); + return cmd_id == command_id; + } + + return false; + }); + + if (requests.end() != request_it) { + return Optional<ResumptionRequest>(*request_it); + } + return Optional<ResumptionRequest>::OptionalEmpty::EMPTY; +} + +void ResumptionDataProcessorImpl::DeleteCommands( + ApplicationSharedPtr application) { + SDL_LOG_AUTO_TRACE(); + + auto failed_requests = GetAllFailedRequests( + application->app_id(), resumption_status_, resumption_status_lock_); + + auto is_vr_command_failed = [](const ResumptionRequest& failed_command) { + return failed_command.message[strings::msg_params].keyExists( + strings::vr_commands); + }; + + auto extract_cmd_id = + [](const smart_objects::SmartObject* const so_ptr) -> uint32_t { + if (so_ptr->keyExists(strings::cmd_id)) { + return so_ptr->getElement(strings::cmd_id).asUInt(); + } + + return 0; + }; + + auto accessor = application->commands_map(); + const auto& commands_map = accessor.GetData(); + + for (const auto& cmd : commands_map) { + const auto cmd_id = extract_cmd_id(cmd.second); + if (0 == cmd_id) { + SDL_LOG_ERROR("Can't extract cmd_id for command with internal number: " + << cmd.first); + continue; + } + + auto failed_command = FindCommandResumptionRequest(cmd_id, failed_requests); + + SDL_LOG_DEBUG(std::boolalpha << "Command with internal ID: " << cmd.first + << " and cmdID: " << cmd_id << " was failed: " + << static_cast<bool>(failed_command)); + if (!failed_command || (!is_vr_command_failed(*failed_command))) { + auto delete_VR_command_msg = MessageHelper::CreateDeleteVRCommandRequest( + cmd.second, + application, + application_manager_.GetNextHMICorrelationID()); + application_manager_.GetRPCService().ManageHMICommand( + delete_VR_command_msg); + } + + if (!failed_command || (is_vr_command_failed(*failed_command))) { + auto delete_UI_command_msg = MessageHelper::CreateDeleteUICommandRequest( + cmd.second, + application->app_id(), + application_manager_.GetNextHMICorrelationID()); + application_manager_.GetRPCService().ManageHMICommand( + delete_UI_command_msg); + } + + application->RemoveCommand(cmd_id); + application->help_prompt_manager().OnVrCommandDeleted(cmd_id, true); + } +} + +void ResumptionDataProcessorImpl::AddChoicesets( + ApplicationSharedPtr application, + const smart_objects::SmartObject& saved_app) { + SDL_LOG_AUTO_TRACE(); + if (!saved_app.keyExists(strings::application_choice_sets)) { + SDL_LOG_ERROR("There is no any choicesets"); + return; + } + + const smart_objects::SmartObject& app_choice_sets = + saved_app[strings::application_choice_sets]; + + for (size_t i = 0; i < app_choice_sets.length(); ++i) { + const smart_objects::SmartObject& choice_set = app_choice_sets[i]; + const int32_t choice_set_id = + choice_set[strings::interaction_choice_set_id].asInt(); + application->AddChoiceSet(choice_set_id, choice_set); + } + + ProcessMessagesToHMI(MessageHelper::CreateAddVRCommandRequestFromChoiceToHMI( + application, application_manager_)); +} + +utils::Optional<ResumptionRequest> FindResumptionChoiceSetRequest( + uint32_t command_id, std::vector<ResumptionRequest>& requests) { + using namespace utils; + + auto request_it = + std::find_if(requests.begin(), + requests.end(), + [command_id](const ResumptionRequest& request) { + auto& msg_params = request.message[strings::msg_params]; + if (msg_params.keyExists(strings::cmd_id) && + (msg_params[strings::type] == + hmi_apis::Common_VRCommandType::Choice)) { + uint32_t cmd_id = msg_params[strings::cmd_id].asUInt(); + return cmd_id == command_id; + } + return false; + }); + if (requests.end() != request_it) { + return Optional<ResumptionRequest>(*request_it); + } + return Optional<ResumptionRequest>::OptionalEmpty::EMPTY; +} + +void ResumptionDataProcessorImpl::DeleteChoicesets( + ApplicationSharedPtr application) { + SDL_LOG_AUTO_TRACE(); + + auto failed_requests = GetAllFailedRequests( + application->app_id(), resumption_status_, resumption_status_lock_); + + auto accessor = application->choice_set_map(); + const auto& choices = accessor.GetData(); + for (const auto& choice : choices) { + auto failed_choice_set = + FindResumptionChoiceSetRequest(choice.first, failed_requests); + if (!failed_choice_set) { + MessageHelper::SendDeleteChoiceSetRequest( + choice.second, application, application_manager_); + } + application->RemoveChoiceSet(choice.first); + } +} + +void ResumptionDataProcessorImpl::SetGlobalProperties( + ApplicationSharedPtr application, + const smart_objects::SmartObject& saved_app) { + SDL_LOG_AUTO_TRACE(); + if (!saved_app.keyExists(strings::application_global_properties)) { + SDL_LOG_DEBUG("application_global_properties section is not exists"); + return; + } + + const smart_objects::SmartObject& properties_so = + saved_app[strings::application_global_properties]; + application->load_global_properties(properties_so); + + ProcessMessagesToHMI(MessageHelper::CreateGlobalPropertiesRequestsToHMI( + application, application_manager_)); +} + +void ResumptionDataProcessorImpl::DeleteGlobalProperties( + ApplicationSharedPtr application) { + SDL_LOG_AUTO_TRACE(); + const uint32_t app_id = application->app_id(); + const auto result = + application_manager_.ResetAllApplicationGlobalProperties(app_id); + + resumption_status_lock_.AcquireForReading(); + std::vector<ResumptionRequest> requests; + if (resumption_status_.find(app_id) != resumption_status_.end()) { + requests = resumption_status_[app_id].successful_requests; + } + resumption_status_lock_.Release(); + + auto check_if_successful = + [requests](hmi_apis::FunctionID::eType function_id) { + for (auto& resumption_request : requests) { + auto request_func = + resumption_request.message[strings::params][strings::function_id] + .asInt(); + if (request_func == function_id) { + return true; + } + } + return false; + }; + + if (result.HasUIPropertiesReset() && + check_if_successful(hmi_apis::FunctionID::UI_SetGlobalProperties)) { + smart_objects::SmartObjectSPtr msg_params = + MessageHelper::CreateUIResetGlobalPropertiesRequest(result, + application); + auto msg = MessageHelper::CreateMessageForHMI( + hmi_apis::messageType::request, + application_manager_.GetNextHMICorrelationID()); + (*msg)[strings::params][strings::function_id] = + hmi_apis::FunctionID::UI_SetGlobalProperties; + (*msg)[strings::msg_params] = *msg_params; + ProcessMessageToHMI(msg, false); + } + + if (result.HasTTSPropertiesReset() && + check_if_successful(hmi_apis::FunctionID::TTS_SetGlobalProperties)) { + smart_objects::SmartObjectSPtr msg_params = + MessageHelper::CreateTTSResetGlobalPropertiesRequest(result, + application); + auto msg = MessageHelper::CreateMessageForHMI( + hmi_apis::messageType::request, + application_manager_.GetNextHMICorrelationID()); + (*msg)[strings::params][strings::function_id] = + hmi_apis::FunctionID::TTS_SetGlobalProperties; + + (*msg)[strings::msg_params] = *msg_params; + ProcessMessageToHMI(msg, false); + } +} + +void ResumptionDataProcessorImpl::AddSubscriptions( + ApplicationSharedPtr application, + const smart_objects::SmartObject& saved_app) { + SDL_LOG_AUTO_TRACE(); + + AddButtonsSubscriptions(application, saved_app); + AddPluginsSubscriptions(application, saved_app); +} + +void ResumptionDataProcessorImpl::AddButtonsSubscriptions( + ApplicationSharedPtr application, + const smart_objects::SmartObject& saved_app) { + SDL_LOG_AUTO_TRACE(); + + if (!saved_app.keyExists(strings::application_subscriptions)) { + SDL_LOG_DEBUG("application_subscriptions section is not exists"); + return; + } + + const smart_objects::SmartObject& subscriptions = + saved_app[strings::application_subscriptions]; + + if (subscriptions.keyExists(strings::application_buttons)) { + const smart_objects::SmartObject& subscriptions_buttons = + subscriptions[strings::application_buttons]; + mobile_apis::ButtonName::eType btn; + for (size_t i = 0; i < subscriptions_buttons.length(); ++i) { + btn = static_cast<mobile_apis::ButtonName::eType>( + (subscriptions_buttons[i]).asInt()); + application->SubscribeToButton(btn); + } + + ButtonSubscriptions button_subscriptions = + GetButtonSubscriptionsToResume(application); + + ProcessMessagesToHMI( + MessageHelper::CreateOnButtonSubscriptionNotificationsForApp( + application, application_manager_, button_subscriptions)); + } +} + +ButtonSubscriptions ResumptionDataProcessorImpl::GetButtonSubscriptionsToResume( + ApplicationSharedPtr application) const { + ButtonSubscriptions button_subscriptions = + application->SubscribedButtons().GetData(); + auto it = button_subscriptions.find(mobile_apis::ButtonName::CUSTOM_BUTTON); + + if (it != button_subscriptions.end()) { + button_subscriptions.erase(it); + } + + return button_subscriptions; +} + +void ResumptionDataProcessorImpl::AddPluginsSubscriptions( + ApplicationSharedPtr application, + const smart_objects::SmartObject& saved_app) { + SDL_LOG_AUTO_TRACE(); + + for (auto& extension : application->Extensions()) { + extension->ProcessResumption(saved_app); + } +} + +void ResumptionDataProcessorImpl::DeleteSubscriptions( + ApplicationSharedPtr application) { + SDL_LOG_AUTO_TRACE(); + DeleteButtonsSubscriptions(application); + DeletePluginsSubscriptions(application); +} + +void ResumptionDataProcessorImpl::DeleteButtonsSubscriptions( + ApplicationSharedPtr application) { + SDL_LOG_AUTO_TRACE(); + const ButtonSubscriptions button_subscriptions = + application->SubscribedButtons().GetData(); + for (auto& btn : button_subscriptions) { + const auto hmi_btn = static_cast<hmi_apis::Common_ButtonName::eType>(btn); + if (hmi_apis::Common_ButtonName::CUSTOM_BUTTON == hmi_btn) { + continue; + } + auto notification = MessageHelper::CreateOnButtonSubscriptionNotification( + application->hmi_app_id(), hmi_btn, false); + // is_subscribed = false + ProcessMessageToHMI(notification, false); + application->UnsubscribeFromButton(btn); + } +} + +void ResumptionDataProcessorImpl::DeleteWindowsSubscriptions( + ApplicationSharedPtr application) { + SDL_LOG_AUTO_TRACE(); + + const auto window_ids = application->GetWindowIds(); + for (const auto& window_id : window_ids) { + const auto hmi_state = application->CurrentHmiState(window_id); + if (mobile_apis::WindowType::WIDGET != hmi_state->window_type()) { + continue; + } + + SDL_LOG_DEBUG("Reverting CreateWindow for: " << window_id); + + auto delete_request = MessageHelper::CreateUIDeleteWindowRequestToHMI( + application, application_manager_, window_id); + const bool subscribe_on_request_events = false; + ProcessMessageToHMI(delete_request, subscribe_on_request_events); + + application->RemoveWindowInfo(window_id); + application->RemoveHMIState(window_id, + app_mngr::HmiState::StateID::STATE_ID_REGULAR); + application->remove_window_capability(window_id); + } +} + +void ResumptionDataProcessorImpl::DeletePluginsSubscriptions( + application_manager::ApplicationSharedPtr application) { + SDL_LOG_AUTO_TRACE(); + + resumption_status_lock_.AcquireForReading(); + auto it = resumption_status_.find(application->app_id()); + if (it == resumption_status_.end()) { + resumption_status_lock_.Release(); + return; + } + + const ApplicationResumptionStatus& status = it->second; + smart_objects::SmartObject extension_subscriptions; + for (auto ivi : status.successful_vehicle_data_subscriptions_) { + SDL_LOG_DEBUG("ivi " << ivi << " should be deleted"); + extension_subscriptions[ivi] = true; + } + resumption_status_lock_.Release(); + + for (auto& extension : application->Extensions()) { + extension->RevertResumption(extension_subscriptions); + } +} + +bool IsResponseSuccessful(const smart_objects::SmartObject& response) { + return !response[strings::params].keyExists(strings::error_msg); +} + +void ResumptionDataProcessorImpl::CheckVehicleDataResponse( + const smart_objects::SmartObject& request, + const smart_objects::SmartObject& response, + ApplicationResumptionStatus& status) { + SDL_LOG_AUTO_TRACE(); + const auto request_keys = request[strings::msg_params].enumerate(); + + if (!IsResponseSuccessful(response)) { + SDL_LOG_TRACE("Vehicle data request not successful"); + for (const auto key : request_keys) { + status.unsuccessful_vehicle_data_subscriptions_.push_back(key); + } + return; + } + + const auto& response_params = response[strings::msg_params]; + const auto response_keys = response_params.enumerate(); + for (auto& ivi : request_keys) { + const auto it = response_keys.find(ivi); + const auto kSuccess = hmi_apis::Common_VehicleDataResultCode::VDRC_SUCCESS; + const auto vd_result_code = + (response_keys.end() == it) + ? kSuccess + : response_params[ivi][strings::result_code].asInt(); + if (kSuccess != vd_result_code) { + SDL_LOG_TRACE("ivi " << ivi << " was NOT successfuly subscribed"); + + status.unsuccessful_vehicle_data_subscriptions_.push_back(ivi); + } else { + SDL_LOG_TRACE("ivi " << ivi << " was successfuly subscribed"); + status.successful_vehicle_data_subscriptions_.push_back(ivi); + } + } +} + +void ResumptionDataProcessorImpl::CheckCreateWindowResponse( + const smart_objects::SmartObject& request, + const smart_objects::SmartObject& response) const { + SDL_LOG_AUTO_TRACE(); + const auto correlation_id = + response[strings::params][strings::correlation_id].asInt(); + + const auto& msg_params = request[strings::msg_params]; + const auto app_id = msg_params[strings::app_id].asInt(); + + auto application = application_manager_.application(app_id); + if (!application) { + SDL_LOG_ERROR("Application is not registered by hmi id: " << app_id); + return; + } + + const auto window_id = msg_params[strings::window_id].asInt(); + if (!IsResponseSuccessful(response)) { + SDL_LOG_ERROR("UI_CreateWindow for correlation id: " << correlation_id + << " has failed"); + auto& builder = application->display_capabilities_builder(); + builder.ResetDisplayCapabilities(); + return; + } + + smart_objects::SmartObject window_info(smart_objects::SmartType_Map); + auto fill_optional_param = [&window_info, + &msg_params](const std::string& key) { + if (msg_params.keyExists(key)) { + window_info[key] = msg_params[key].asString(); + } + }; + fill_optional_param(strings::associated_service_type); + fill_optional_param(strings::duplicate_updates_from_window_id); + + const auto window_name = msg_params[strings::window_name].asString(); + window_info[strings::window_name] = window_name; + application->SetWindowInfo(window_id, window_info); + + const auto window_type = static_cast<mobile_apis::WindowType::eType>( + msg_params[strings::window_type].asInt()); + + // State should be initialized with INVALID_ENUM value to let state + // controller trigger OnHmiStatus notification sending + auto initial_state = application_manager_.CreateRegularState( + application, + window_type, + mobile_apis::HMILevel::INVALID_ENUM, + mobile_apis::AudioStreamingState::INVALID_ENUM, + mobile_apis::VideoStreamingState::INVALID_ENUM, + mobile_apis::SystemContext::INVALID_ENUM); + application->SetInitialState(window_id, window_name, initial_state); + + // Default HMI level for all windows except the main one is always NONE + application_manager_.state_controller().OnAppWindowAdded( + application, window_id, window_type, mobile_apis::HMILevel::HMI_NONE); +} + +} // namespace resumption |