diff options
Diffstat (limited to 'src/components/functional_module/src/plugin_manager.cc')
-rw-r--r-- | src/components/functional_module/src/plugin_manager.cc | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/src/components/functional_module/src/plugin_manager.cc b/src/components/functional_module/src/plugin_manager.cc new file mode 100644 index 0000000000..a32fdbf773 --- /dev/null +++ b/src/components/functional_module/src/plugin_manager.cc @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2017, 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 <dlfcn.h> +#include <algorithm> +#include "functional_module/plugin_manager.h" +#include "functional_module/function_ids.h" +#include "protocol/common.h" +#include "utils/file_system.h" +#include "utils/logger.h" +#include "json/json.h" + +namespace functional_modules { + +CREATE_LOGGERPTR_GLOBAL(logger_, "PluginManager") + +namespace { +const std::string ExtractMethodName(application_manager::MessagePtr msg) { + DCHECK_OR_RETURN(msg, ""); + Json::Value value; + Json::Reader reader; + reader.parse(msg->json_message(), value); + + const char* kMethod = "method"; + const char* kResult = "result"; + const char* kError = "error"; + const char* kData = "data"; + + if (value.isMember(kMethod)) { + return value.get(kMethod, "").asCString(); + } + if (value.isMember(kResult)) { + const Json::Value& result = value.get(kResult, Json::Value()); + return result.get(kMethod, "").asCString(); + } + if (value.isMember(kError)) { + const Json::Value& error = value.get(kError, Json::Value()); + const Json::Value& data = error.get(kData, Json::Value()); + return data.get(kMethod, "").asCString(); + } + + return std::string(); +} +} // namespace + +typedef std::map<ModuleID, ModulePtr>::iterator PluginsIterator; +typedef std::map<RCFunctionID, ModulePtr>::iterator PluginFunctionsIterator; +typedef std::map<HMIFunctionID, ModulePtr>::iterator PluginHMIFunctionsIterator; + +PluginManager::PluginManager() : service_() { + LOG4CXX_DEBUG(logger_, "Creating plugin mgr"); +} + +PluginManager::~PluginManager() { + mobile_subscribers_.clear(); + hmi_subscribers_.clear(); + UnloadPlugins(); +} + +int PluginManager::LoadPlugins(const std::string& plugin_path) { + LOG4CXX_INFO(logger_, "Loading plugins from " << plugin_path); + std::vector<std::string> plugin_files = file_system::ListFiles(plugin_path); + for (size_t i = 0; i < plugin_files.size(); ++i) { + size_t pos = plugin_files[i].find_last_of("."); + if (std::string::npos != pos) { + if (plugin_files[i].substr(pos + 1).compare("so") != 0) { + continue; + } + } else { + continue; + } + std::string full_name = plugin_path + '/' + plugin_files[i]; + void* generic_plugin_dll = dlopen(full_name.c_str(), RTLD_LAZY); + if (NULL == generic_plugin_dll) { + LOG4CXX_ERROR(logger_, + "Failed to open dll " << plugin_files[i] << "\n" + << dlerror()); + continue; + } + typedef GenericModule* (*Create)(); + Create create_manager = + reinterpret_cast<Create>(dlsym(generic_plugin_dll, "Create")); + char* error_string = dlerror(); + if (NULL != error_string) { + LOG4CXX_ERROR(logger_, + "Failed to export dll's " << plugin_files[i] << " symbols\n" + << error_string); + dlclose(generic_plugin_dll); + continue; + } + ModulePtr module = create_manager(); + if (!module) { + LOG4CXX_ERROR(logger_, + "Failed to create plugin main class " << plugin_files[i]); + dlclose(generic_plugin_dll); + continue; + } else { + LOG4CXX_DEBUG(logger_, + "Opened and working plugin from " << plugin_files[i] + << " with id " + << module->GetModuleID()); + dlls_.insert(std::pair<ModuleID, void*>(module->GetModuleID(), + generic_plugin_dll)); + plugins_.insert( + std::pair<ModuleID, ModulePtr>(module->GetModuleID(), module)); + std::deque<RCFunctionID> subscribers = + module->GetPluginInfo().rc_function_list; + for (size_t i = 0; i < subscribers.size(); ++i) { + mobile_subscribers_.insert( + std::pair<RCFunctionID, ModulePtr>(subscribers[i], module)); + } + + std::deque<HMIFunctionID> hmi_subscribers = + module->GetPluginInfo().hmi_function_list; + for (size_t i = 0; i < hmi_subscribers.size(); ++i) { + hmi_subscribers_.insert( + std::pair<HMIFunctionID, ModulePtr>(hmi_subscribers[i], module)); + } + module->set_service(service_); + module->AddObserver(this); + } + } + return plugins_.size(); +} + +void PluginManager::UnloadPlugins() { + for (Modules::iterator it = plugins_.begin(); plugins_.end() != it; ++it) { + it->second->RemoveObserver(this); + } + plugins_.clear(); + + for (std::map<ModuleID, void*>::iterator it = dlls_.begin(); + dlls_.end() != it; + ++it) { + dlclose(it->second); + } + dlls_.clear(); +} + +bool PluginManager::IsMessageForPlugin(application_manager::MessagePtr msg) { + DCHECK(msg); + if (!msg) { + LOG4CXX_ERROR(logger_, "Null pointer message was received."); + return false; + } + if (protocol_handler::MajorProtocolVersion::PROTOCOL_VERSION_UNKNOWN != + msg->protocol_version() && + protocol_handler::MajorProtocolVersion::PROTOCOL_VERSION_HMI != + msg->protocol_version()) { + RCFunctionID id = static_cast<RCFunctionID>(msg->function_id()); + return (mobile_subscribers_.find(id) != mobile_subscribers_.end()); + } else { + return false; + } +} + +bool PluginManager::IsHMIMessageForPlugin(application_manager::MessagePtr msg) { + DCHECK(msg); + if (!msg) { + LOG4CXX_ERROR(logger_, "Null pointer message was received."); + return false; + } + + Json::Value value; + Json::Reader reader; + reader.parse(msg->json_message(), value); + if (protocol_handler::MajorProtocolVersion::PROTOCOL_VERSION_HMI == + msg->protocol_version()) { + std::string msg_method; + // Request or notification from HMI + if (value.isMember("method") && value["method"].isString()) { + msg_method = value["method"].asCString(); + // Response from HMI + } else if (value.isMember("result") && value["result"].isMember("method") && + value["result"]["method"].isString()) { + msg_method = value["result"]["method"].asCString(); + // Error response from HMI + } else if (value.isMember("error") && value["error"].isMember("data") && + value["error"]["data"].isMember("method") && + value["error"]["data"]["method"].isString()) { + msg_method = value["error"]["data"]["method"].asCString(); + } else { + LOG4CXX_WARN(logger_, + "Message with HMI protocol version can not be handled by " + "plugin manager, because required 'method' field was not " + "found, or was containing an invalid string."); + return false; + } + + return (hmi_subscribers_.find(msg_method) != hmi_subscribers_.end()); + } + + return false; +} + +ProcessResult PluginManager::ProcessMessage( + application_manager::MessagePtr msg) { + DCHECK(msg); + if (!msg) { + LOG4CXX_ERROR(logger_, "Null pointer message was received."); + return ProcessResult::CANNOT_PROCESS; + } + + protocol_handler::MajorProtocolVersion version = msg->protocol_version(); + if (protocol_handler::MajorProtocolVersion::PROTOCOL_VERSION_UNKNOWN == + version || + protocol_handler::MajorProtocolVersion::PROTOCOL_VERSION_HMI == version) { + return ProcessResult::CANNOT_PROCESS; + } + + ProcessResult result = ProcessResult::CANNOT_PROCESS; + PluginFunctionsIterator subscribed_plugin_itr = + mobile_subscribers_.find(static_cast<RCFunctionID>(msg->function_id())); + if (mobile_subscribers_.end() != subscribed_plugin_itr) { + result = subscribed_plugin_itr->second->ProcessMessage(msg); + if (ProcessResult::PROCESSED != result) { + LOG4CXX_ERROR(logger_, "Plugin failed to process message."); + } + } + + return result; +} + +ProcessResult PluginManager::ProcessHMIMessage( + application_manager::MessagePtr msg) { + DCHECK_OR_RETURN(msg, ProcessResult::CANNOT_PROCESS); + + if (protocol_handler::MajorProtocolVersion::PROTOCOL_VERSION_HMI != + msg->protocol_version()) { + return ProcessResult::CANNOT_PROCESS; + } + + const std::string& msg_method = ExtractMethodName(msg); + if (msg_method.empty()) { + LOG4CXX_WARN(logger_, + "Message with HMI protocol version can not be handled by " + "plugin manager, because required 'method' field was not " + "found, or was containing an invalid string."); + return ProcessResult::CANNOT_PROCESS; + } + + LOG4CXX_DEBUG(logger_, "Parsed method name is " << msg_method); + + ProcessResult result = ProcessResult::CANNOT_PROCESS; + PluginHMIFunctionsIterator subscribed_plugin_itr = + hmi_subscribers_.find(msg_method); + if (hmi_subscribers_.end() != subscribed_plugin_itr) { + result = subscribed_plugin_itr->second->ProcessHMIMessage(msg); + } + + return result; +} + +void PluginManager::OnServiceStateChanged(ServiceState state) { + for (PluginsIterator it = plugins_.begin(); plugins_.end() != it; ++it) { + it->second->OnServiceStateChanged(state); + } +} + +void PluginManager::OnError(ModuleObserver::Errors error, ModuleID module_id) { + std::string error_string; + switch (error) { + case ModuleObserver::Errors::OUT_OF_MEMORY: + error_string = "Module run out of memory."; + break; + case ModuleObserver::Errors::FS_FAILURE: + error_string = "Plugin failed to run file system operation."; + break; + default: { + LOG4CXX_ERROR(logger_, + "Error " << error_string << " was received from module " + << module_id); + } break; + } +} + +void PluginManager::RemoveAppExtension(uint32_t app_id) { + for (PluginsIterator it = plugins_.begin(); plugins_.end() != it; ++it) { + it->second->RemoveAppExtension(app_id); + } +} + +bool PluginManager::IsAppForPlugins( + application_manager::ApplicationSharedPtr app) { + DCHECK(app); + if (!app) { + return false; + } + + bool res = false; + for (PluginsIterator it = plugins_.begin(); plugins_.end() != it; ++it) { + res = res || it->second->IsAppForPlugin(app); + } + return res; +} + +bool PluginManager::IsAppForPlugin( + application_manager::ApplicationSharedPtr app, ModuleID module_id) const { + DCHECK_OR_RETURN(app, false); + Modules::const_iterator i = plugins_.find(module_id); + return i != plugins_.end() ? i->second->IsAppForPlugin(app) : false; +} + +void PluginManager::OnAppHMILevelChanged( + application_manager::ApplicationSharedPtr app, + mobile_apis::HMILevel::eType old_level) { + DCHECK_OR_RETURN_VOID(app); + for (PluginsIterator it = plugins_.begin(); plugins_.end() != it; ++it) { + if (it->second->IsAppForPlugin(app)) { + LOG4CXX_DEBUG(logger_, + "Application " << app->name().AsMBString() << " of plugin " + << it->second->GetModuleID() + << " has changed level from " << old_level + << " to " << app->hmi_level()); + it->second->OnAppHMILevelChanged(app, old_level); + } + } +} + +typedef std::map<ModuleID, ModulePtr>::value_type PluginsValueType; + +struct HandleApplicationEvent { + private: + const functional_modules::ApplicationEvent event_; + application_manager::ApplicationSharedPtr application_; + + public: + HandleApplicationEvent(functional_modules::ApplicationEvent e, + application_manager::ApplicationSharedPtr application) + : event_(e), application_(application) {} + void operator()(PluginsValueType& p) { + p.second->OnApplicationEvent(event_, application_); + } +}; + +struct HandlePolicyEvent { + private: + const functional_modules::PolicyEvent event_; + + public: + HandlePolicyEvent(functional_modules::PolicyEvent e) : event_(e) {} + void operator()(PluginsValueType& p) { + p.second->OnPolicyEvent(event_); + } +}; + +void PluginManager::OnApplicationEvent( + functional_modules::ApplicationEvent event, + application_manager::ApplicationSharedPtr application) { + LOG4CXX_AUTO_TRACE(logger_); + if (application) { + std::for_each(plugins_.begin(), + plugins_.end(), + HandleApplicationEvent(event, application)); + } +} + +void PluginManager::OnPolicyEvent(PolicyEvent event) { + LOG4CXX_AUTO_TRACE(logger_); + std::for_each(plugins_.begin(), plugins_.end(), HandlePolicyEvent(event)); +} + +PluginManager::Modules& PluginManager::plugins() { + return plugins_; +} + +} // namespace functional_modules |