summaryrefslogtreecommitdiff
path: root/src/components/application_manager/src/state_controller.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/application_manager/src/state_controller.cc')
-rw-r--r--src/components/application_manager/src/state_controller.cc436
1 files changed, 436 insertions, 0 deletions
diff --git a/src/components/application_manager/src/state_controller.cc b/src/components/application_manager/src/state_controller.cc
new file mode 100644
index 000000000..720312adb
--- /dev/null
+++ b/src/components/application_manager/src/state_controller.cc
@@ -0,0 +1,436 @@
+/*
+ Copyright (c) 2015, 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 "application_manager/state_controller.h"
+#include "application_manager/application_manager_impl.h"
+#include "application_manager/message_helper.h"
+#include "utils/helpers.h"
+
+namespace application_manager {
+
+CREATE_LOGGERPTR_GLOBAL(logger_, "StateController");
+
+bool IsStatusChanged(HmiStatePtr old_state,
+ HmiStatePtr new_state) {
+ if (old_state->hmi_level() != new_state->hmi_level()
+ || old_state->audio_streaming_state() != new_state->audio_streaming_state()
+ || old_state->system_context() != new_state->system_context() ) {
+ return true;
+ }
+ return false;
+}
+
+StateController::StateController():EventObserver() {
+ subscribe_on_event(hmi_apis::FunctionID::BasicCommunication_OnEmergencyEvent);
+ subscribe_on_event(hmi_apis::FunctionID::BasicCommunication_OnPhoneCall);
+ subscribe_on_event(hmi_apis::FunctionID::TTS_Started);
+ subscribe_on_event(hmi_apis::FunctionID::TTS_Stopped);
+ subscribe_on_event(hmi_apis::FunctionID::VR_Started);
+ subscribe_on_event(hmi_apis::FunctionID::VR_Stopped);
+}
+
+void StateController::SetRegularState(ApplicationSharedPtr app,
+ const mobile_apis::AudioStreamingState::eType audio_state) {
+ if (!app) {
+ return;
+ }
+ HmiStatePtr prev_state = app->RegularHmiState();
+ DCHECK_OR_RETURN_VOID(prev_state);
+ HmiStatePtr hmi_state = CreateHmiState(app->app_id(),
+ HmiState::StateID::STATE_ID_REGULAR);
+ DCHECK_OR_RETURN_VOID(hmi_state);
+ hmi_state->set_hmi_level(prev_state->hmi_level());
+ hmi_state->set_audio_streaming_state(audio_state);
+ hmi_state->set_system_context(prev_state->system_context());
+ SetRegularState<false>(app, hmi_state);
+}
+
+void StateController::HmiLevelConflictResolver::operator ()
+ (ApplicationSharedPtr to_resolve) {
+ using namespace mobile_apis;
+ using namespace helpers;
+ DCHECK_OR_RETURN_VOID(state_ctrl_);
+ if (to_resolve == applied_)
+ return;
+ if (Compare<HMILevel::eType, EQ, ONE>(state_->hmi_level(),
+ HMILevel::HMI_FULL,
+ HMILevel::HMI_LIMITED)) {
+ HmiStatePtr cur_state = to_resolve->RegularHmiState();
+ if (Compare<HMILevel::eType, EQ, ONE>(cur_state->hmi_level(),
+ HMILevel::HMI_FULL,
+ HMILevel::HMI_LIMITED)) {
+ if (applied_->IsAudioApplication() && state_ctrl_->IsSameAppType(applied_, to_resolve)) {
+ state_ctrl_->SetupRegularHmiState(to_resolve, HMILevel::HMI_BACKGROUND,
+ AudioStreamingState::NOT_AUDIBLE);
+ } else {
+ state_ctrl_->SetupRegularHmiState(to_resolve, HMILevel::HMI_LIMITED,
+ AudioStreamingState::AUDIBLE);
+ }
+ }
+ }
+}
+
+void StateController::SetupRegularHmiState(ApplicationSharedPtr app,
+ HmiStatePtr state) {
+ using namespace mobile_apis;
+ LOG4CXX_AUTO_TRACE(logger_);
+ DCHECK_OR_RETURN_VOID(state);
+ LOG4CXX_DEBUG(logger_, "hmi_level " << state->hmi_level() <<
+ ", audio_state " << state->audio_streaming_state() <<
+ ", system_context " << state->system_context());
+ HmiStatePtr curr_state = app->CurrentHmiState();
+ HmiStatePtr old_state = CreateHmiState(app->app_id(),
+ HmiState::StateID::STATE_ID_REGULAR);
+ DCHECK_OR_RETURN_VOID(old_state);
+ old_state->set_hmi_level(curr_state->hmi_level());
+ old_state->set_audio_streaming_state(curr_state->audio_streaming_state());
+ old_state->set_system_context(curr_state->system_context());
+ app->SetRegularState(state);
+ if (state->hmi_level() == mobile_apis::HMILevel::HMI_NONE) {
+ app->ResetDataInNone();
+ }
+ if (!app->IsAudioApplication()) {
+ if (state->hmi_level() == HMILevel::HMI_LIMITED) {
+ LOG4CXX_ERROR(logger_, "Trying to setup LIMITED to non audio app");
+ state->set_hmi_level(HMILevel::HMI_BACKGROUND);
+ }
+ if (state->audio_streaming_state() != AudioStreamingState::NOT_AUDIBLE) {
+ LOG4CXX_ERROR(logger_, "Trying to setup audio state " <<
+ state->audio_streaming_state() <<" to non audio app");
+ state->set_audio_streaming_state(AudioStreamingState::NOT_AUDIBLE);
+ }
+ }
+ HmiStatePtr new_state = app->CurrentHmiState();
+ OnStateChanged(app, old_state, new_state);
+}
+
+void StateController::SetupRegularHmiState(ApplicationSharedPtr app,
+ const mobile_apis::HMILevel::eType hmi_level,
+ const mobile_apis::AudioStreamingState::eType audio_state) {
+ using namespace mobile_apis;
+ using namespace helpers;
+ LOG4CXX_AUTO_TRACE(logger_);
+ DCHECK_OR_RETURN_VOID(app);
+ HmiStatePtr prev_state = app->RegularHmiState();
+ DCHECK_OR_RETURN_VOID(prev_state);
+ HmiStatePtr new_state = CreateHmiState(app->app_id(),
+ HmiState::StateID::STATE_ID_REGULAR);
+ DCHECK_OR_RETURN_VOID(new_state);
+ new_state->set_hmi_level(hmi_level);
+ new_state->set_audio_streaming_state(audio_state);
+ new_state->set_system_context(prev_state->system_context());
+ SetupRegularHmiState(app, new_state);
+}
+
+void StateController::ApplyRegularState(ApplicationSharedPtr app,
+ HmiStatePtr state) {
+ DCHECK_OR_RETURN_VOID(app);
+ DCHECK_OR_RETURN_VOID(state);
+ DCHECK_OR_RETURN_VOID(state->state_id() == HmiState::STATE_ID_REGULAR);
+ SetupRegularHmiState(app, state);
+ ForEachApplication<HmiLevelConflictResolver, ApplicationManagerImpl>
+ (HmiLevelConflictResolver(app, state, this));
+}
+
+bool StateController::IsSameAppType(ApplicationConstSharedPtr app1,
+ ApplicationConstSharedPtr app2) {
+ return app1->is_media_application() == app2->is_media_application() ||
+ app1->is_navi() == app2->is_navi() ||
+ app1->is_voice_communication_supported() == app2->is_voice_communication_supported();
+}
+
+void StateController::on_event(const event_engine::Event& event) {
+ using namespace smart_objects;
+ using namespace event_engine;
+ using namespace hmi_apis;
+
+ LOG4CXX_AUTO_TRACE(logger_);
+ const SmartObject& message = event.smart_object();
+ const FunctionID::eType id = static_cast<FunctionID::eType> (event.id());
+ switch (id) {
+ case FunctionID::BasicCommunication_ActivateApp: {
+ OnActivateAppResponse(message);
+ break;
+ }
+ case FunctionID::BasicCommunication_OnEmergencyEvent: {
+ bool is_active =
+ message[strings::msg_params][hmi_response::enabled].asBool();
+ if (is_active) {
+ OnSafetyModeEnabled();
+ } else {
+ OnSafetyModeDisabled();
+ }
+ break;
+ }
+ case FunctionID::BasicCommunication_OnPhoneCall: {
+ bool is_active =
+ message[strings::msg_params][hmi_notification::is_active].asBool();
+ if (is_active) {
+ OnPhoneCallStarted();
+ } else {
+ OnPhoneCallEnded();
+ }
+ break;
+ }
+ case FunctionID::VR_Started: {
+ OnVRStarted();
+ break;
+ }
+ case FunctionID::VR_Stopped: {
+ OnVREnded();
+ break;
+ }
+ case FunctionID::TTS_Started: {
+ OnTTSStarted();
+ break;
+ }
+ case FunctionID::TTS_Stopped: {
+ OnTTSStopped();
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void StateController::OnStateChanged(ApplicationSharedPtr app,
+ HmiStatePtr old_state,
+ HmiStatePtr new_state) {
+ DCHECK_OR_RETURN_VOID(app);
+ DCHECK_OR_RETURN_VOID(old_state);
+ DCHECK_OR_RETURN_VOID(new_state);
+ if (IsStatusChanged(old_state, new_state)) {
+ MessageHelper::SendHMIStatusNotification(*app);
+ if (new_state->hmi_level() == mobile_apis::HMILevel::HMI_NONE) {
+ app->ResetDataInNone();
+ }
+ ApplicationManagerImpl::instance()->OnHMILevelChanged(app->app_id(),
+ old_state->hmi_level(), new_state->hmi_level());
+ } else {
+ LOG4CXX_ERROR(logger_, "Status not changed");
+ }
+}
+
+void StateController::ApplyStatesForApp(ApplicationSharedPtr app) {
+ LOG4CXX_AUTO_TRACE(logger_);
+ sync_primitives::AutoLock autolock(active_states_lock_);
+ DCHECK_OR_RETURN_VOID(app);
+ StateIDList::iterator it = active_states_.begin();
+ for(; it != active_states_.end(); ++it) {
+ HmiStatePtr new_state = CreateHmiState(app->app_id(), *it);
+ DCHECK_OR_RETURN_VOID(new_state);
+ DCHECK_OR_RETURN_VOID(new_state->state_id() != HmiState::STATE_ID_REGULAR);
+ HmiStatePtr old_hmi_state = app->CurrentHmiState();
+ new_state->set_parent(old_hmi_state);
+ app->AddHMIState(new_state);
+ }
+
+}
+
+void StateController::TempStateStarted(HmiState::StateID ID) {
+ LOG4CXX_AUTO_TRACE(logger_);
+ sync_primitives::AutoLock autolock(active_states_lock_);
+ StateIDList::iterator it =
+ std::find(active_states_.begin(), active_states_.end(), ID);
+ if (it == active_states_.end()) {
+ active_states_.push_back(ID);
+ } else {
+ LOG4CXX_ERROR(logger_, "StateID " << ID <<" is already active");
+ }
+}
+
+void StateController::TempStateStopped(HmiState::StateID ID) {
+ LOG4CXX_AUTO_TRACE(logger_);
+ sync_primitives::AutoLock autolock(active_states_lock_);
+ active_states_.remove(ID);
+}
+void StateController::OnActivateAppResponse(
+ const smart_objects::SmartObject& message) {
+ const hmi_apis::Common_Result::eType code =
+ static_cast<hmi_apis::Common_Result::eType>(
+ message[strings::params][hmi_response::code].asInt());
+ const int32_t correlation_id = message[strings::params]
+ [strings::correlation_id].asInt();
+ const uint32_t hmi_app_id = ApplicationManagerImpl::instance()->
+ application_id(correlation_id);
+ ApplicationSharedPtr application = ApplicationManagerImpl::instance()->
+ application_by_hmi_app(hmi_app_id);
+ if (application) {
+ HmiStatePtr pending_state = waiting_for_activate[application->app_id()];
+ DCHECK_OR_RETURN_VOID(pending_state);
+ if (code != hmi_apis::Common_Result::SUCCESS) {
+ const HmiStatePtr cur = application->RegularHmiState();
+ pending_state->set_hmi_level(cur->hmi_level());
+ }
+ ApplyRegularState(application, pending_state);
+ }
+}
+
+
+void StateController::OnPhoneCallStarted() {
+ LOG4CXX_AUTO_TRACE(logger_);
+ ForEachApplication(std::bind1st(
+ std::mem_fun(
+ &StateController::HMIStateStarted<HmiState::STATE_ID_PHONE_CALL>),
+ this)
+ );
+ TempStateStarted(HmiState::STATE_ID_PHONE_CALL);
+}
+
+void StateController::OnPhoneCallEnded() {
+ LOG4CXX_AUTO_TRACE(logger_);
+ ForEachApplication(std::bind1st(
+ std::mem_fun(
+ &StateController::HMIStateStopped<HmiState::STATE_ID_PHONE_CALL>),
+ this)
+ );
+ TempStateStopped(HmiState::STATE_ID_PHONE_CALL);
+}
+
+void StateController::OnSafetyModeEnabled() {
+ LOG4CXX_AUTO_TRACE(logger_);
+ ForEachApplication(std::bind1st(
+ std::mem_fun(
+ &StateController::HMIStateStarted<HmiState::STATE_ID_SAFETY_MODE>),
+ this)
+ );
+ TempStateStarted(HmiState::STATE_ID_SAFETY_MODE);
+}
+
+void StateController::OnSafetyModeDisabled() {
+ LOG4CXX_AUTO_TRACE(logger_);
+
+ ForEachApplication(std::bind1st(
+ std::mem_fun(
+ &StateController::HMIStateStopped<HmiState::STATE_ID_SAFETY_MODE>),
+ this)
+ );
+ TempStateStopped(HmiState::STATE_ID_SAFETY_MODE);
+}
+
+void StateController::OnVRStarted() {
+ LOG4CXX_AUTO_TRACE(logger_);
+ ForEachApplication(std::bind1st(
+ std::mem_fun(
+ &StateController::HMIStateStarted<HmiState::STATE_ID_VR_SESSION>),
+ this)
+ );
+ TempStateStarted(HmiState::STATE_ID_VR_SESSION);
+}
+
+void StateController::OnVREnded() {
+ LOG4CXX_AUTO_TRACE(logger_);
+ ForEachApplication(std::bind1st(
+ std::mem_fun(
+ &StateController::HMIStateStopped<HmiState::STATE_ID_VR_SESSION>),
+ this)
+ );
+ TempStateStopped(HmiState::STATE_ID_VR_SESSION);
+}
+
+void StateController::OnTTSStarted() {
+ LOG4CXX_AUTO_TRACE(logger_);
+ ForEachApplication(std::bind1st(
+ std::mem_fun(
+ &StateController::HMIStateStarted<HmiState::STATE_ID_TTS_SESSION>),
+ this)
+ );
+ TempStateStarted(HmiState::STATE_ID_TTS_SESSION);
+}
+
+void StateController::OnTTSStopped() {
+ LOG4CXX_AUTO_TRACE(logger_);
+ ForEachApplication(std::bind1st(
+ std::mem_fun(
+ &StateController::HMIStateStopped<HmiState::STATE_ID_TTS_SESSION>),
+ this)
+ );
+ TempStateStopped(HmiState::STATE_ID_TTS_SESSION);
+}
+
+void StateController::OnNaviStreamingStarted() {
+ LOG4CXX_AUTO_TRACE(logger_);
+ ForEachApplication(std::bind1st(
+ std::mem_fun(
+ &StateController::HMIStateStarted<HmiState::STATE_ID_NAVI_STREAMING>),
+ this)
+ );
+ TempStateStarted(HmiState::STATE_ID_NAVI_STREAMING);
+}
+
+void StateController::OnNaviStreamingStopped() {
+ LOG4CXX_AUTO_TRACE(logger_);
+ ForEachApplication(std::bind1st(
+ std::mem_fun(
+ &StateController::HMIStateStopped<HmiState::STATE_ID_NAVI_STREAMING>),
+ this)
+ );
+ TempStateStopped(HmiState::STATE_ID_NAVI_STREAMING);
+}
+
+HmiStatePtr StateController::CreateHmiState(uint32_t app_id, HmiState::StateID state_id) {
+ LOG4CXX_AUTO_TRACE(logger_);
+ HmiStatePtr new_state;
+ switch (state_id) {
+ case HmiState::STATE_ID_PHONE_CALL: {
+ new_state.reset(new PhoneCallHmiState(app_id, state_context_));
+ break;
+ }
+ case HmiState::STATE_ID_SAFETY_MODE: {
+ new_state.reset(new SafetyModeHmiState(app_id, state_context_));
+ break;
+ }
+ case HmiState::STATE_ID_VR_SESSION: {
+ new_state.reset(new VRHmiState(app_id, state_context_));
+ break;
+ }
+ case HmiState::STATE_ID_TTS_SESSION: {
+ new_state.reset(new TTSHmiState(app_id, state_context_));
+ break;
+ }
+ case HmiState::STATE_ID_NAVI_STREAMING: {
+ new_state.reset(new NaviStreamingHmiState(app_id, state_context_));
+ break;
+ }
+ case HmiState::STATE_ID_REGULAR: {
+ new_state.reset(new HmiState(app_id, state_context_));
+ break;
+ }
+ default:
+ LOG4CXX_FATAL(logger_, "Invalid state_id " << state_id);
+ NOTREACHED();
+ break;
+ }
+ return new_state;
+}
+
+}