diff options
Diffstat (limited to 'src/components/connection_handler')
12 files changed, 2770 insertions, 0 deletions
diff --git a/src/components/connection_handler/CMakeLists.txt b/src/components/connection_handler/CMakeLists.txt new file mode 100644 index 0000000000..18946ac349 --- /dev/null +++ b/src/components/connection_handler/CMakeLists.txt @@ -0,0 +1,23 @@ +include_directories ( + ./include/ + ../protocol_handler/include/ + ../config_profile/include/ + ../utils/include/ + ${ENCRYPTION_INCLUDE_DIRECTORY} + ${LOG4CXX_INCLUDE_DIRECTORY} +) + +set (SOURCES + ./src/connection_handler_impl.cc + ./src/connection.cc + ./src/device.cc + ./src/heartbeat_monitor.cc +) + +set(LIBRARIES + ConfigProfile + ProtocolLibrary +) + +add_library(connectionHandler ${SOURCES}) +target_link_libraries(connectionHandler encryption) diff --git a/src/components/connection_handler/include/connection_handler/connection.h b/src/components/connection_handler/include/connection_handler/connection.h new file mode 100644 index 0000000000..6bcbc3fdcd --- /dev/null +++ b/src/components/connection_handler/include/connection_handler/connection.h @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2014, 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. + */ + +#ifndef SRC_COMPONENTS_CONNECTION_HANDLER_INCLUDE_CONNECTION_HANDLER_CONNECTION_H_ +#define SRC_COMPONENTS_CONNECTION_HANDLER_INCLUDE_CONNECTION_HANDLER_CONNECTION_H_ + +#include <map> +#include <vector> + +#include "utils/lock.h" +#include "connection_handler/device.h" +#include "connection_handler/heartbeat_monitor.h" +#include "protocol/service_type.h" + +#ifdef ENABLE_SECURITY +namespace security_manager { +class SSLContext; +} // namespace security_manager +#endif // ENABLE_SECURITY + +/** + * \namespace connection_handler + * \brief SmartDeviceLink connection_handler namespace. + */ +namespace connection_handler { + +class ConnectionHandler; + +/** + * \brief Type for ConnectionHandle + */ +typedef int32_t ConnectionHandle; + +/** + * \brief Type for Connections map + * Key is ConnectionHandle which is unique + */ +typedef std::map<int32_t, Connection*> ConnectionList; + +/** + * \brief ServiceType + */ +struct Service { + protocol_handler::ServiceType service_type; + bool is_protected_; + Service() + : service_type(protocol_handler::kInvalidServiceType), + is_protected_(false) { + } + explicit Service(protocol_handler::ServiceType service_type) + : service_type(service_type), + is_protected_(false) { + } + bool operator==(const protocol_handler::ServiceType service_type) const { + return this->service_type == service_type; + } +}; + +/** + * \brief Type for Session Services + */ +typedef std::vector<Service> ServiceList; + +struct Session { + ServiceList service_list; + uint8_t protocol_version; +#ifdef ENABLE_SECURITY + security_manager::SSLContext *ssl_context; +#endif // ENABLE_SECURITY + Session() + : service_list() +#ifdef ENABLE_SECURITY + , ssl_context(NULL) +#endif // ENABLE_SECURITY + {} + explicit Session(const ServiceList &services, uint8_t protocol_version) + : service_list(services), + protocol_version(protocol_version) +#ifdef ENABLE_SECURITY + , ssl_context(NULL) +#endif // ENABLE_SECURITY + {} + Service *FindService(const protocol_handler::ServiceType &service_type); + const Service *FindService(const protocol_handler::ServiceType &service_type) const; +}; + +/** + * \brief Type for Session map + */ +typedef std::map<uint8_t, Session> SessionMap; + +/** + * \class Connection + * \brief Stores connection information + */ +class Connection { + public: + /** + * \brief Class constructor + */ + Connection(ConnectionHandle connection_handle, + DeviceHandle connection_device_handle, + ConnectionHandler *connection_handler, + int32_t heartbeat_timeout); + + /** + * \brief Destructor + */ + ~Connection(); + + /** + * \brief Returns device handle + * \return DeviceHandle + */ + ConnectionHandle connection_handle() const; + + /** + * \brief Returns connection device handle + * \return ConnectionDeviceHandle + */ + DeviceHandle connection_device_handle(); + + /** + * \brief Adds session to connection + * \return new session id or 0 in case of issues + */ + uint32_t AddNewSession(); + + /** + * \brief Removes session from connection + * \param session session ID + * \return session_id or 0 in case of issues + */ + uint32_t RemoveSession(uint8_t session_id); + + /** + * \brief Adds uprotected service to session or + * check protection to service has been started before + * \param session_id session ID + * \param service_type Type of service + * \param is_protected protection state + * \return TRUE on success, otherwise FALSE + */ + bool AddNewService(uint8_t session_id, + protocol_handler::ServiceType service_type, + const bool is_protected); + /** + * \brief Removes service from session + * \param session_id session ID + * \param service_type Type of service + * \return TRUE on success, otherwise FALSE + */ + bool RemoveService(uint8_t session_id, + protocol_handler::ServiceType service_type); +#ifdef ENABLE_SECURITY + /** + * \brief Sets crypto context of service + * \param session_id Identifier of the session + * \param context SSL for connection + * \return \c true in case of service is protected or \c false otherwise + */ + int SetSSLContext(uint8_t session_id, + security_manager::SSLContext *context); + /** + * \brief Gets crypto context of session, use service_type to get NULL + * SSLContext for not protected services or ControlService (0x0) + * to get current SSLContext of connection + * \param session_id Identifier of the session + * \param service_type Type of service + * \return \ref SSLContext of connection + */ + security_manager::SSLContext *GetSSLContext( + const uint8_t session_id, + const protocol_handler::ServiceType &service_type) const; + /** + * \brief Set protection flag to service in session by key + * to get current SSLContext of connection + * \param session_id Identifier of the session + * \param service_type Type of service + */ + void SetProtectionFlag( + const uint8_t session_id, + const protocol_handler::ServiceType &service_type); +#endif // ENABLE_SECURITY + /** + * \brief Returns map of sessions which have been opened in + * current connection. + */ + const SessionMap session_map() const; + + /** + * \brief Close session + * \param session_id session id + */ + void CloseSession(uint8_t session_id); + + /** + * \brief Prevent session from being closed by heartbeat timeout + * \param session_id session id + */ + void KeepAlive(uint8_t session_id); + + /** + * \brief Start heartbeat for specified session + * \param session_id session id + */ + void StartHeartBeat(uint8_t session_id); + + /** + * \brief Send heartbeat to mobile app + * \param session_id session id + */ + void SendHeartBeat(uint8_t session_id); + + /** + * Sets heart beat timeout + * @param timeout in seconds + */ + void SetHeartBeatTimeout(int32_t timeout); + + /** + * \brief changes protocol version in session + * \param session_id session id + * \param protocol_version protocol version registered application + */ + void UpdateProtocolVersionSession(uint8_t session_id, uint8_t protocol_version); + + /** + * \brief checks if session supports heartbeat + * \param session_id session id + * \return TRUE on success, otherwise FALSE + */ + bool SupportHeartBeat(uint8_t session_id); + + + private: + /** + * \brief Current connection handler. + */ + ConnectionHandler *connection_handler_; + + /** + * \brief Current connection handle. + */ + ConnectionHandle connection_handle_; + + /** + * \brief DeviceHandle of this connection. + */ + DeviceHandle connection_device_handle_; + + /** + * \brief session/services map + */ + SessionMap session_map_; + + mutable sync_primitives::Lock session_map_lock_; + + /** + * \brief monitor that closes connection if there is no traffic over it + */ + HeartBeatMonitor *heartbeat_monitor_; + threads::Thread *heart_beat_monitor_thread_; + + DISALLOW_COPY_AND_ASSIGN(Connection); +}; + +} // namespace connection_handler +#endif // SRC_COMPONENTS_CONNECTION_HANDLER_INCLUDE_CONNECTION_HANDLER_CONNECTION_H_ diff --git a/src/components/connection_handler/include/connection_handler/connection_handler.h b/src/components/connection_handler/include/connection_handler/connection_handler.h new file mode 100644 index 0000000000..08bef97155 --- /dev/null +++ b/src/components/connection_handler/include/connection_handler/connection_handler.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2014, 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. + */ + +#ifndef SRC_COMPONENTS_CONNECTION_HANDLER_INCLUDE_CONNECTION_HANDLER_CONNECTION_HANDLER_H_ +#define SRC_COMPONENTS_CONNECTION_HANDLER_INCLUDE_CONNECTION_HANDLER_CONNECTION_HANDLER_H_ + +#include "transport_manager/transport_manager_listener.h" +#include "protocol_handler/session_observer.h" +#include "connection_handler/connection_handler_observer.h" +#include "connection_handler/device.h" +#include "connection_handler/connection.h" +#include "connection_handler/devices_discovery_starter.h" + +/** + * \namespace connection_handler + * \brief SmartDeviceLink connection_handler namespace. + */ +namespace connection_handler { +/** + * \class ConnectionHandler + * \brief SmartDeviceLink ConnectionHandler interface class + */ +class ConnectionHandler { + public: + /** + * \brief Sets observer pointer for ConnectionHandler. + * \param observer Pointer to observer object. + **/ + virtual void set_connection_handler_observer( + ConnectionHandlerObserver *observer) = 0; + + /** + * \brief Sets pointer to TransportManager. + * \param transportManager Pointer to TransportManager object. + **/ + virtual void set_transport_manager( + transport_manager::TransportManager *transport_manager) = 0; + + virtual void StartTransportManager() = 0; + + virtual void ConnectToDevice( + connection_handler::DeviceHandle device_handle) = 0; + + virtual void ConnectToAllDevices() = 0; + + /** + * @brief Close the connection revoked by Policy + * @param connection_key pair of connection and session id + */ + virtual void CloseRevokedConnection(uint32_t connection_key) = 0; + + /** + * \brief Close all associated sessions and close the connection pointed by handle + */ + virtual void CloseConnection(ConnectionHandle connection_handle) = 0; + + /** + * \brief Return count of session for specified connection + * \param connection_key pair of connection handle and session id + */ + virtual uint32_t GetConnectionSessionsCount(uint32_t connection_key) = 0; + + /** + * Gets device id by mac address + * @param mac_address + * @return true if successfully + */ + virtual bool GetDeviceID(const std::string &mac_address, + DeviceHandle *device_handle) = 0; + + /** + * Close session associated with the key + */ + virtual void CloseSession(uint32_t key) = 0; + + /** + * Close session + */ + virtual void CloseSession(ConnectionHandle connection_handle, + uint8_t session_id) = 0; + + /** + * \brief Start heartbeat for specified session + * + * \param connection_key pair of connection and session id + */ + virtual void StartSessionHeartBeat(uint32_t connection_key) = 0; + + /** + * \brief Send heartbeat to mobile app + */ + virtual void SendHeartBeat(ConnectionHandle connection_handle, + uint8_t session_id) = 0; + + /** + * Sets heart beat timeout for specified session + * @param connection_key pair of connection and session id + * @param timeout in seconds + */ + virtual void SetHeartBeatTimeout(uint32_t connection_key, + int32_t timeout) = 0; + + /** + * \brief binds protocol version with session + * + * \param connection_key pair of connection and session id + * \param protocol_version contains protocol version of + * \registered application. + */ + virtual void BindProtocolVersionWithSession(uint32_t connection_key, + uint8_t protocol_version) = 0; + + protected: + /** + * \brief Destructor + */ + virtual ~ConnectionHandler() { + } +}; +} // namespace connection_handler + +#endif // SRC_COMPONENTS_CONNECTION_HANDLER_INCLUDE_CONNECTION_HANDLER_CONNECTION_HANDLER_H_ diff --git a/src/components/connection_handler/include/connection_handler/connection_handler_impl.h b/src/components/connection_handler/include/connection_handler/connection_handler_impl.h new file mode 100644 index 0000000000..00ebdf0ab5 --- /dev/null +++ b/src/components/connection_handler/include/connection_handler/connection_handler_impl.h @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2014, 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. + */ + +#ifndef SRC_COMPONENTS_CONNECTION_HANDLER_INCLUDE_CONNECTION_HANDLER_CONNECTION_HANDLER_IMPL_H_ +#define SRC_COMPONENTS_CONNECTION_HANDLER_INCLUDE_CONNECTION_HANDLER_CONNECTION_HANDLER_IMPL_H_ + +#include <map> +#include <list> +#include <string> +#include <vector> + +#include "transport_manager/transport_manager_listener_empty.h" +#include "protocol_handler/session_observer.h" +#include "protocol_handler/protocol_handler.h" +#include "connection_handler/connection_handler_observer.h" +#include "connection_handler/device.h" +#include "connection_handler/connection.h" +#include "connection_handler/devices_discovery_starter.h" +#include "connection_handler/connection_handler.h" +#include "utils/logger.h" +#include "utils/macro.h" +#include "utils/lock.h" +#include "utils/stl_utils.h" +#include "utils/singleton.h" + +/** + * \namespace connection_handler + * \brief SmartDeviceLink connection_handler namespace. + */ +namespace connection_handler { +/** + * \class ConnectionHandlerImpl + * \brief SmartDeviceLink connection_handler main class + * Observes TransportManager and ProtocolHandler, + * stores information regarding connections + * and sessions and provides it to AppManager. + */ +class ConnectionHandlerImpl : public ConnectionHandler, + public transport_manager::TransportManagerListenerEmpty, + public protocol_handler::SessionObserver, + public DevicesDiscoveryStarter, + public utils::Singleton<ConnectionHandlerImpl> { + public: + /** + * \brief Destructor + */ + virtual ~ConnectionHandlerImpl(); + + void Stop(); + /** + * \brief Sets observer pointer for connection_handler. + * \param observer Pointer to observer object. + */ + virtual void set_connection_handler_observer( + ConnectionHandlerObserver *observer); + + /** + * \brief Sets pointer to TransportManager. + * \param transport_mngr Pointer to TransportManager object. + **/ + virtual void set_transport_manager( + transport_manager::TransportManager *transport_mngr); + + /** + * \brief Sets pointer to ProtocolHandler. + * \param protocol_handler Pointer to ProtocolHandler object. + **/ + void set_protocol_handler( + protocol_handler::ProtocolHandler *protocol_handler); + + /** + * \brief Connects to all services of device + * \param deviceHandle Handle of device to connect to + */ + virtual void ConnectToDevice(connection_handler::DeviceHandle device_handle); + + virtual void ConnectToAllDevices(); + + virtual void StartTransportManager(); + + virtual void OnDeviceListUpdated( + const std::vector<transport_manager::DeviceInfo> &); + + virtual void OnFindNewApplicationsRequest(); + + /** + * \brief Available devices list updated. + * + * Called when device scanning initiated with scanForNewDevices + * is completed or devices connected via background procedures. + * + * \param DeviceList New list of available devices. + **/ + virtual void OnDeviceFound(const transport_manager::DeviceInfo &device_info); + virtual void OnDeviceAdded(const transport_manager::DeviceInfo &device_info); + virtual void OnDeviceRemoved(const transport_manager::DeviceInfo &device_info); + + virtual void OnScanDevicesFinished(); + virtual void OnScanDevicesFailed( + const transport_manager::SearchDeviceError &error); + + /** + * \brief Notifies about established connection. + * + * \param connection_id ID of new connection. + **/ + virtual void OnConnectionEstablished( + const transport_manager::DeviceInfo &device_info, + const transport_manager::ConnectionUID &connection_id); + virtual void OnConnectionFailed( + const transport_manager::DeviceInfo &device_info, + const transport_manager::ConnectError &error); + virtual void OnConnectionClosed( + transport_manager::ConnectionUID connection_id); + virtual void OnConnectionClosedFailure( + transport_manager::ConnectionUID connection_id, + const transport_manager::DisconnectError &error); + virtual void OnUnexpectedDisconnect( + transport_manager::ConnectionUID connection_id, + const transport_manager::CommunicationError &error); + virtual void OnDeviceConnectionLost( + const connection_handler::DeviceHandle &device, + const transport_manager::DisconnectDeviceError &error); + /** + * \brief Informs about failure during DisconnectDevice procedure of TM + * \param device Information about disconnected device + * \param error Information about possible reason of loosing connection + */ + virtual void OnDisconnectFailed( + const connection_handler::DeviceHandle &device, + const transport_manager::DisconnectDeviceError &error); + + /** + * \brief Callback function used by ProtocolHandler + * when Mobile Application initiates start of new session. + * \param connection_handle Connection identifier within which session has to be started. + * \param session_id Identifier of the session to be started + * \param service_type Type of service + * \param is_protected would be service protected + * \param hash_id pointer for session hash identifier + * \return uint32_t Id (number) of new session if successful, otherwise 0. + */ + virtual uint32_t OnSessionStartedCallback(const transport_manager::ConnectionUID &connection_handle, + const uint8_t session_id, + const protocol_handler::ServiceType &service_type, + const bool is_protected, uint32_t* hash_id); + + /** + * \brief Callback function used by ProtocolHandler + * when Mobile Application initiates session ending. + * \param connection_handle Connection identifier within which session exists + * \param sessionId Identifier of the session to be ended + * \param hashCode Hash used only in second version of SmartDeviceLink protocol. + * If not equal to hash assigned to session on start then operation fails. + * \return uint32_t 0 if operation fails, session key otherwise + */ + virtual uint32_t OnSessionEndedCallback( + const transport_manager::ConnectionUID &connection_handle, + const uint8_t session_id, const uint32_t &hashCode, + const protocol_handler::ServiceType &service_type); + + /** + * \brief Creates unique identifier of session (can be used as hash) + * from given connection identifier + * within which session exists and session number. + * \param connection_handle Connection identifier within which session exists + * \param sessionId Identifier of the session + * \return int32_t Unique key for session + */ + virtual uint32_t KeyFromPair( + transport_manager::ConnectionUID connection_handle, + uint8_t session_id); + + /** + * \brief Returns connection identifier and session number from given session key + * \param key Unique key used by other components as session identifier + * \param connection_handle Returned: Connection identifier within which session exists + * \param sessionId Returned: Number of session + */ + virtual void PairFromKey(uint32_t key, + transport_manager::ConnectionUID *connection_handle, + uint8_t *session_id); + + /** + * \brief information about given Connection Key. + * \param key Unique key used by other components as session identifier + * \param app_id Returned: ApplicationID + * \param sessions_list Returned: List of session keys + * \param device_id Returned: DeviceID + * \return int32_t -1 in case of error or 0 in case of success + */ + virtual int32_t GetDataOnSessionKey(uint32_t key, uint32_t *app_id = 0, + std::list<int32_t> *sessions_list = NULL, + uint32_t *device_id = 0); + + /** + * \brief information about device + * \param device_handle + * \param device_name Returned: name of device + * \param applications_list Returned: applications on device + * \param mac_address Returned: List of session keys + * \param connection_type Returned: type of connection (USB, BT, etc.) + * \return int32_t -1 in case of error or 0 in case of success + */ + virtual int32_t GetDataOnDeviceID(connection_handler::DeviceHandle device_handle, + std::string *device_name = NULL, + std::list<uint32_t> *applications_list = NULL, + std::string *mac_address = NULL, + std::string* connection_type = NULL); +#ifdef ENABLE_SECURITY + /** + * \brief Sets crypto context of connection + * \param key Unique key used by other components as session identifier + * \param context SSLContext to be set + * \return \c SecurityQuery::ProtectSessionResult value + */ + int SetSSLContext( + const uint32_t &key, + security_manager::SSLContext *context) OVERRIDE; + + /** + * \brief Gets crypto context of connection, use service_type to get NULL + * SSLContext for not protected services or ControlService (0x0) + * to get current SSLContext of connection + * \param key Unique key used by other components as session identifier + * \param service_type Type of service + * \return \ref SSLContext of connection + */ + security_manager::SSLContext *GetSSLContext( + const uint32_t &key, + const protocol_handler::ServiceType &service_type) OVERRIDE; + /** + * \brief Set protection flag to service in session by key + * \param key Unique key used by other components as session identifier + * \param service_type Type of service + */ + void SetProtectionFlag( + const uint32_t &key, + const protocol_handler::ServiceType &service_type) OVERRIDE; +#endif // ENABLE_SECURITY + + /** + * \brief Get device handle by mac address + * \param mac_address uniq address + * \param device_handle + * \return true on sucess otherwise false. + */ + virtual bool GetDeviceID(const std::string &mac_address, + DeviceHandle *device_handle); + + /** + * \brief Method which should start devices discovering + */ + virtual void StartDevicesDiscovery(); + + /** + * @brief Close the connection revoked by Policy + * @param connection_key pair of connection and session id + */ + virtual void CloseRevokedConnection(uint32_t connection_key); + + /** + * @brief Close the connection pointed by handle + * @param connection_handle Connection unique id + */ + virtual void CloseConnection(ConnectionHandle connection_handle) OVERRIDE; + + /** + * \brief Close session associated with the key + * \param key Unique key used by other components as session identifier + */ + virtual void CloseSession(uint32_t key); + + /** + * \brief Function used by HearbeatMonitior to close session on HB timeout + * \param connection_handle Connection handler within which session exists + * \param session_id Identifier of the session to be ended + */ + virtual void CloseSession(ConnectionHandle connection_handle, + uint8_t session_id); + + /** + * \brief Return count of session for specified connection + * \param connection_key pair of connection handle and session id + */ + virtual uint32_t GetConnectionSessionsCount(uint32_t connection_key); + + /** + * \brief Send heartbeat message to mobile app + */ + virtual void SendHeartBeat(ConnectionHandle connection_handle, + uint8_t session_id); + + /** + * \brief Start heartbeat for specified session + * + * \param connection_key pair of connection and session id + */ + virtual void StartSessionHeartBeat(uint32_t connection_key); + + /** + * Sets heart beat timeout for specified session + * @param connection_key pair of connection and session id + * @param timeout in seconds + */ + virtual void SetHeartBeatTimeout(uint32_t connection_key, int32_t timeout); + + /** + * \brief Keep connection associated with the key from being closed by heartbeat monitor + */ + void KeepConnectionAlive(uint32_t connection_key, uint8_t session_id); + + /** + * \brief binds protocol version with session + * + * \param connection_key pair of connection and session id + * \param protocol_version contains protocol version of + * \registered application. + */ + virtual void BindProtocolVersionWithSession(uint32_t connection_key, + uint8_t protocol_version); + + /** + * \brief returns TRUE if session supports sending HEART BEAT ACK to mobile side + * \param connection_handle Connection identifier whithin which session exists + * \param sessionId Identifier of the session + * \return TRUE if session has protocol version which supports heartbeat otherwise returns FALSE + */ + virtual bool IsHeartBeatSupported( + transport_manager::ConnectionUID connection_handle, + uint8_t session_id); + private: + /** + * \brief Default class constructor + */ + ConnectionHandlerImpl(); + + /** + * \brief Disconnect application. + * + * \param device_handle DeviceHandle of disconnected device. + * \param connection_handle Connection handle. + **/ + void RemoveConnection(const ConnectionHandle connection_handle); + + void OnConnectionEnded( + const transport_manager::ConnectionUID &connection_id); + + /** + * \brief Pointer to observer + */ + ConnectionHandlerObserver *connection_handler_observer_; + + /** + * \brief Pointer to TransportManager + */ + transport_manager::TransportManager *transport_manager_; + + protocol_handler::ProtocolHandler *protocol_handler_; + + /** + * \brief List of devices + */ + DeviceMap device_list_; + + /** + * \brief List of connections + */ + ConnectionList connection_list_; + + /** + * \brief Lock for applications list + */ + mutable sync_primitives::Lock connection_list_lock_; + + /** + * \brief Cleans connection list on destruction + */ + utils::StlMapDeleter<ConnectionList> connection_list_deleter_; + +#ifdef BUILD_TESTS + // Methods for test usage + public: + ConnectionList &getConnectionList(); + void addDeviceConnection( + const transport_manager::DeviceInfo &device_info, + const transport_manager::ConnectionUID &connection_id); +#endif + private: + FRIEND_BASE_SINGLETON_CLASS(ConnectionHandlerImpl); + DISALLOW_COPY_AND_ASSIGN(ConnectionHandlerImpl); +}; +} // namespace connection_handler +#endif // SRC_COMPONENTS_CONNECTION_HANDLER_INCLUDE_CONNECTION_HANDLER_CONNECTION_HANDLER_IMPL_H_ diff --git a/src/components/connection_handler/include/connection_handler/connection_handler_observer.h b/src/components/connection_handler/include/connection_handler/connection_handler_observer.h new file mode 100644 index 0000000000..cd419c124a --- /dev/null +++ b/src/components/connection_handler/include/connection_handler/connection_handler_observer.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2014, 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. + */ + +#ifndef SRC_COMPONENTS_CONNECTION_HANDLER_INCLUDE_CONNECTION_HANDLER_CONNECTION_HANDLER_OBSERVER_H_ +#define SRC_COMPONENTS_CONNECTION_HANDLER_INCLUDE_CONNECTION_HANDLER_CONNECTION_HANDLER_OBSERVER_H_ + +#include "connection_handler/device.h" +#include "connection_handler/connection.h" +#include "protocol/service_type.h" + +/** + * \namespace connection_handler + * \brief SmartDeviceLink connection_handler namespace. + */ +namespace connection_handler { + +/** + * \class ConnectionHandlerObserver + * \brief ConnectionHandlerObserver class + */ +class ConnectionHandlerObserver { + public: + /** + * \brief Available devices list updated. + * + * Called when device scanning initiated with scanForNewDevices + * is completed. + * + * \param DeviceList New list of available devices. + **/ + virtual void OnDeviceListUpdated( + const connection_handler::DeviceMap &device_list) = 0; + + /** + * @brief Reaction to "Find new applications" request + */ + virtual void OnFindNewApplicationsRequest() = 0; + + /** + * \brief Removes device. + * + * Called when device has been removed from a list. + * + * \param DeviceHandle Handle of removed device. + **/ + virtual void RemoveDevice( + const connection_handler::DeviceHandle &device_handle) = 0; + + /** + * \brief Callback function used by connection_handler + * when Mobile Application initiates start of new service. + * \param deviceHandle Device identifier within which session has to be started. + * \param sessionKey Key of started session. + * \param type Established service type + */ + virtual bool OnServiceStartedCallback( + const connection_handler::DeviceHandle &device_handle, + const int32_t &session_key, + const protocol_handler::ServiceType &type) = 0; + + /** + * \brief Callback function used by connection_handler + * when Mobile Application initiates service ending. + * \param sessionKey Key of session which should be ended + */ + virtual void OnServiceEndedCallback( + const int32_t &session_key, + const protocol_handler::ServiceType &type) = 0; + + protected: + /** + * \brief Destructor + */ + virtual ~ConnectionHandlerObserver() { + } +}; +} // namespace connection_handler + +#endif // SRC_COMPONENTS_CONNECTION_HANDLER_INCLUDE_CONNECTION_HANDLER_CONNECTION_HANDLER_OBSERVER_H_ diff --git a/src/components/connection_handler/include/connection_handler/device.h b/src/components/connection_handler/include/connection_handler/device.h new file mode 100644 index 0000000000..c37c4cc62c --- /dev/null +++ b/src/components/connection_handler/include/connection_handler/device.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2014, 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. + */ + +#ifndef SRC_COMPONENTS_CONNECTION_HANDLER_INCLUDE_CONNECTION_HANDLER_DEVICE_H_ +#define SRC_COMPONENTS_CONNECTION_HANDLER_INCLUDE_CONNECTION_HANDLER_DEVICE_H_ + +#include <string> +#include <map> +#include <vector> + +/** + * \namespace connection_handler + * \brief SmartDeviceLink connection_handler namespace. + */ +namespace connection_handler { + +/** + * \brief Type for DeviceHandle + */ +typedef uint32_t DeviceHandle; +typedef std::vector<int32_t> AppList; + +/** + * \class Device + * \brief Connection class + */ +class Device { + public: + /** + * \brief Class constructor + */ + Device(DeviceHandle device_handle, const std::string &user_friendly_name, + const std::string &mac_address = "", const std::string& connection_type = ""); + + /** + * \brief Returns device handle + * \return DeviceHandle + */ + DeviceHandle device_handle() const; + + /** + * \brief Returns user frendly device name + * \return UserFriendlyName + */ + std::string user_friendly_name() const; + + /** + *\brief Also should be used for iOS USB connections + *(better not know why this same method) + */ + std::string mac_address() const; + + /** + * @brief The type of connection used by device. + * @return connection type (USB_serial, BTMAC, etc.) + */ + std::string connection_type() const; + + private: + /** + * \brief Uniq device handle. + */ + DeviceHandle device_handle_; + + /** + * \brief User-friendly device name. + */ + std::string user_friendly_name_; + + /** + * \brief Mac address of device if available + */ + std::string mac_address_; + + /** + * \brief The type of connection used by device. + */ + std::string connection_type_; +}; + +/** + * \brief Type for Devices map + */ +typedef std::map<int32_t, Device> DeviceMap; +} // namespace connection_handler +#endif // SRC_COMPONENTS_CONNECTION_HANDLER_INCLUDE_CONNECTION_HANDLER_DEVICE_H_ diff --git a/src/components/connection_handler/include/connection_handler/devices_discovery_starter.h b/src/components/connection_handler/include/connection_handler/devices_discovery_starter.h new file mode 100644 index 0000000000..bd5885a273 --- /dev/null +++ b/src/components/connection_handler/include/connection_handler/devices_discovery_starter.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014, 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. + */ + +#ifndef SRC_COMPONENTS_CONNECTION_HANDLER_INCLUDE_CONNECTION_HANDLER_DEVICES_DISCOVERY_STARTER_H_ +#define SRC_COMPONENTS_CONNECTION_HANDLER_INCLUDE_CONNECTION_HANDLER_DEVICES_DISCOVERY_STARTER_H_ + +#include "connection_handler/device.h" + +/** + * \namespace connection_handler + * \brief SmartDeviceLink connection_handler namespace. + */ +namespace connection_handler { +/** + * \class DevicesDiscoveryStarter + * \brief Starter of devices discovering process class + */ +class DevicesDiscoveryStarter { + public: + /** + * \brief Method which should start devices discoveryng + */ + virtual void StartDevicesDiscovery()=0; + + /** + * \brief Connects to all services of device + * \param deviceHandle Handle of device to connect to + */ + virtual void ConnectToDevice( + connection_handler::DeviceHandle device_handle) = 0; + + virtual void StartTransportManager() = 0; + + protected: + /** + * \brief Destructor + */ + virtual ~DevicesDiscoveryStarter() { + } +}; +} // namespace connection_handler + +#endif // SRC_COMPONENTS_CONNECTION_HANDLER_INCLUDE_CONNECTION_HANDLER_DEVICES_DISCOVERY_STARTER_H_ diff --git a/src/components/connection_handler/include/connection_handler/heartbeat_monitor.h b/src/components/connection_handler/include/connection_handler/heartbeat_monitor.h new file mode 100644 index 0000000000..9b641ce01c --- /dev/null +++ b/src/components/connection_handler/include/connection_handler/heartbeat_monitor.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2013, 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. + */ +#ifndef SRC_COMPONENTS_CONNECTION_HANDLER_INCLUDE_HEARTBEAT_MONITOR_H_ +#define SRC_COMPONENTS_CONNECTION_HANDLER_INCLUDE_HEARTBEAT_MONITOR_H_ + +#include <stdint.h> +#include <map> + +#include "utils/threads/thread.h" +#include "utils/threads/thread_delegate.h" +#include "utils/date_time.h" +#include "utils/macro.h" +#include "utils/lock.h" + +namespace connection_handler { + +class Connection; + +/* + * Starts hearbeat timer for session and when it elapses closes it + */ +class HeartBeatMonitor: public threads::ThreadDelegate { + public: + HeartBeatMonitor(int32_t heartbeat_timeout_seconds, + Connection *connection); + + /** + * Thread procedure. + */ + virtual void threadMain(); + + /** + * \brief add and remove session + */ + void AddSession(uint8_t session_id); + void RemoveSession(uint8_t session_id); + + /** + * \brief Resets timer preventing session from being killed + */ + void KeepAlive(uint8_t session_id); + + /** + * \brief Thread exit procedure. + */ + virtual bool exitThreadMain(); + + void set_heartbeat_timeout_seconds(int32_t timeout); + + private: + struct SessionState; + typedef std::map<uint8_t, SessionState> SessionMap; + + // \brief Heartbeat timeout, should be read from profile + int32_t heartbeat_timeout_seconds_; + // \brief Connection that must be closed when timeout elapsed + Connection *connection_; + + static const int32_t kDefaultCycleTimeout = 100000; + + struct SessionState { + TimevalStruct heartbeat_expiration; + bool is_heartbeat_sent; + }; + + // \brief monitored sessions collection + + SessionMap sessions_; + + sync_primitives::Lock sessions_list_lock_; // recurcive + sync_primitives::Lock main_thread_lock_; + mutable sync_primitives::Lock heartbeat_timeout_seconds_lock_; + + volatile bool run_; + + void Process(); + void RefreshExpiration(TimevalStruct* expiration) const; + inline bool HasTimeoutElapsed(const TimevalStruct& expiration) const; + + DISALLOW_COPY_AND_ASSIGN(HeartBeatMonitor); +}; + +} // namespace connection_handler + +#endif // SRC_COMPONENTS_CONNECTION_HANDLER_INCLUDE_HEARTBEAT_MONITOR_H_ diff --git a/src/components/connection_handler/src/connection.cc b/src/components/connection_handler/src/connection.cc new file mode 100644 index 0000000000..bd8b478ba5 --- /dev/null +++ b/src/components/connection_handler/src/connection.cc @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2014, 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 <limits.h> + +#include <algorithm> + +#include "connection_handler/connection.h" +#include "connection_handler/connection_handler.h" +#include "protocol_handler/protocol_packet.h" +#include "utils/logger.h" +#include "utils/macro.h" + +#ifdef ENABLE_SECURITY +#include "security_manager/ssl_context.h" +#include "security_manager/security_manager.h" +#endif // ENABLE_SECURITY + +/** + * \namespace connection_handler + * \brief SmartDeviceLink ConnectionHandler namespace. + */ +namespace connection_handler { + +CREATE_LOGGERPTR_GLOBAL(logger_, "ConnectionHandler") + +Service *Session::FindService(const protocol_handler::ServiceType &service_type) { + ServiceList::iterator service_it = + std::find(service_list.begin(), service_list.end(), service_type); + if(service_it != service_list.end()){ + return &(*service_it); + } + return NULL; +} + +const Service *Session::FindService(const protocol_handler::ServiceType &service_type) const { + ServiceList::const_iterator service_it = + std::find(service_list.begin(), service_list.end(), service_type); + if(service_it != service_list.end()){ + return &(*service_it); + } + return NULL; +} + +Connection::Connection(ConnectionHandle connection_handle, + DeviceHandle connection_device_handle, + ConnectionHandler *connection_handler, + int32_t heartbeat_timeout) + : connection_handler_(connection_handler), + connection_handle_(connection_handle), + connection_device_handle_(connection_device_handle) { + LOG4CXX_TRACE_ENTER(logger_); + DCHECK(connection_handler_); + + heartbeat_monitor_ = new HeartBeatMonitor(heartbeat_timeout, this); + heart_beat_monitor_thread_ = new threads::Thread("HeartBeatMonitor", + heartbeat_monitor_); + heart_beat_monitor_thread_->start(); +} + +Connection::~Connection() { + LOG4CXX_TRACE_ENTER(logger_); + heart_beat_monitor_thread_->stop(); + delete heart_beat_monitor_thread_; + sync_primitives::AutoLock lock(session_map_lock_); + session_map_.clear(); + LOG4CXX_TRACE_EXIT(logger_); +} + +// Finds a key not presented in std::map<unsigned char, T> +// Returns 0 if that key not found +namespace { +template <class T> +uint32_t findGap(const std::map<unsigned char, T> &map) { + for (uint32_t i = 1; i <= UCHAR_MAX; ++i) { + if (map.find(i) == map.end()) { + return i; + } + } + return 0; +} +} // namespace + +uint32_t Connection::AddNewSession() { + LOG4CXX_TRACE_ENTER(logger_); + sync_primitives::AutoLock lock(session_map_lock_); + const uint32_t session_id = findGap(session_map_); + if (session_id > 0) { + Session& new_session = session_map_[session_id]; + new_session.protocol_version = ::protocol_handler::PROTOCOL_VERSION_2; + new_session.service_list.push_back(Service(protocol_handler::kRpc)); + new_session.service_list.push_back(Service(protocol_handler::kBulk)); + } + return session_id; +} + +uint32_t Connection::RemoveSession(uint8_t session_id) { + sync_primitives::AutoLock lock(session_map_lock_); + SessionMap::iterator it = session_map_.find(session_id); + if (session_map_.end() == it) { + LOG4CXX_WARN(logger_, "Session not found in this connection!"); + return 0; + } + heartbeat_monitor_->RemoveSession(session_id); + session_map_.erase(session_id); + return session_id; +} + +bool Connection::AddNewService(uint8_t session_id, + protocol_handler::ServiceType service_type, + const bool request_protection) { + // Ignore wrong services + if (protocol_handler::kControl == service_type || + protocol_handler::kInvalidServiceType == service_type ) { + LOG4CXX_WARN(logger_, "Wrong service " << static_cast<int>(service_type)); + return false; + } + sync_primitives::AutoLock lock(session_map_lock_); + + SessionMap::iterator session_it = session_map_.find(session_id); + if (session_it == session_map_.end()) { + LOG4CXX_WARN(logger_, "Session not found in this connection!"); + return false; + } + Session &session = session_it->second; + Service *service = session.FindService(service_type); + // if service already exists + if (service) { +#ifdef ENABLE_SECURITY + if(!request_protection) { + LOG4CXX_WARN(logger_, "Session " << static_cast<int>(session_id) << + " already has unprotected service "<< static_cast<int>(service_type)); + return false; + } + if (service->is_protected_) { + LOG4CXX_WARN(logger_, "Session " << static_cast<int>(session_id) << + " already has protected service "<< static_cast<int>(service_type)); + return false; + } + // For unproteced service could be start protection + return true; +#else + // Service already exists + return false; +#endif // ENABLE_SECURITY + } + // id service is not exists + session.service_list.push_back(Service(service_type)); + return true; +} + +inline bool is_incorrect_for_remove_service( + const protocol_handler::ServiceType service_type) { + return + // Control type is internal part of session + protocol_handler::kControl == service_type || + // RPC and bulk service is necessary part of session + protocol_handler::kRpc == service_type || + protocol_handler::kBulk == service_type || + // Invalid service is not part of session + protocol_handler::kInvalidServiceType == service_type; +} + +bool Connection::RemoveService( + uint8_t session_id, protocol_handler::ServiceType service_type) { + // Ignore wrong and required for Session services + if (is_incorrect_for_remove_service(service_type)) { + LOG4CXX_WARN(logger_, "Could not remove service " + << static_cast<int>(service_type)); + return false; + } + sync_primitives::AutoLock lock(session_map_lock_); + + SessionMap::iterator session_it = session_map_.find(session_id); + if (session_map_.end() == session_it) { + LOG4CXX_WARN(logger_, "Session not found in this connection!"); + return false; + } + + ServiceList &service_list = session_it->second.service_list; + ServiceList::iterator service_it = + find(service_list.begin(), service_list.end(), service_type); + if (service_list.end() == service_it) { + LOG4CXX_WARN(logger_, "Session " << session_id << " didn't established" + " service " << service_type); + return false; + } + service_list.erase(service_it); + return true; +} + +#ifdef ENABLE_SECURITY +int Connection::SetSSLContext(uint8_t session_id, + security_manager::SSLContext *context) { + sync_primitives::AutoLock lock(session_map_lock_); + SessionMap::iterator session_it = session_map_.find(session_id); + if (session_it == session_map_.end()) { + LOG4CXX_WARN(logger_, "Session not found in this connection!"); + return security_manager::SecurityManager::ERROR_INTERNAL; + } + Session &session = session_it->second; + session.ssl_context = context; + return security_manager::SecurityManager::ERROR_SUCCESS; +} + +security_manager::SSLContext *Connection::GetSSLContext( + const uint8_t session_id, const protocol_handler::ServiceType &service_type) const { + LOG4CXX_TRACE(logger_, "Connection::GetSSLContext"); + sync_primitives::AutoLock lock(session_map_lock_); + SessionMap::const_iterator session_it = session_map_.find(session_id); + if (session_it == session_map_.end()) { + LOG4CXX_WARN(logger_, "Session not found in this connection!"); + return NULL; + } + const Session &session = session_it->second; + // for control services return current SSLContext value + if (protocol_handler::kControl == service_type) + return session.ssl_context; + const Service *service = session.FindService(service_type); + if (!service) { + LOG4CXX_WARN(logger_, "Service not found in this session!"); + return NULL; + } + if (!service->is_protected_) + return NULL; + LOG4CXX_TRACE(logger_, "SSLContext is " << session.ssl_context); + return session.ssl_context; +} + +void Connection::SetProtectionFlag( + const uint8_t session_id, const protocol_handler::ServiceType &service_type) { + LOG4CXX_TRACE(logger_, "Connection::SetProtectionFlag"); + sync_primitives::AutoLock lock(session_map_lock_); + SessionMap::iterator session_it = session_map_.find(session_id); + if (session_it == session_map_.end()) { + LOG4CXX_WARN(logger_, "Session not found in this connection!"); + return; + } + Session &session = session_it->second; + Service *service = session.FindService(service_type); + if (!service) { + LOG4CXX_WARN(logger_, "Service not found in this session!"); + return; + } + service->is_protected_ = true; + // Rpc and bulk shall be protected as one service + if (service->service_type == protocol_handler::kRpc) { + Service *service_bulk = session.FindService(protocol_handler::kBulk); + DCHECK(service_bulk); + service_bulk->is_protected_ = true; + } else if (service->service_type == protocol_handler::kBulk) { + Service *service_rpc = session.FindService(protocol_handler::kRpc); + DCHECK(service_rpc); + service_rpc->is_protected_ = true; + } +} +#endif // ENABLE_SECURITY + +ConnectionHandle Connection::connection_handle() const { + return connection_handle_; +} + +DeviceHandle Connection::connection_device_handle() { + return connection_device_handle_; +} + +const SessionMap Connection::session_map() const { + sync_primitives::AutoLock lock(session_map_lock_); + return session_map_; +} + +void Connection::CloseSession(uint8_t session_id) { + size_t size; + { + sync_primitives::AutoLock lock(session_map_lock_); + + SessionMap::iterator session_it = session_map_.find(session_id); + if (session_it == session_map_.end()) { + return; + } + size = session_map_.size(); + } + + connection_handler_->CloseSession(connection_handle_, session_id); + + //Close connection if it is last session + if (1 == size) { + connection_handler_->CloseConnection(connection_handle_); + } +} + +void Connection::UpdateProtocolVersionSession( + uint8_t session_id, uint8_t protocol_version) { + sync_primitives::AutoLock lock(session_map_lock_); + SessionMap::iterator session_it = session_map_.find(session_id); + if (session_map_.end() == session_it) { + LOG4CXX_WARN(logger_, "Session not found in this connection!"); + return; + } + Session &session = session_it->second; + session.protocol_version = protocol_version; +} + +bool Connection::SupportHeartBeat(uint8_t session_id) { + sync_primitives::AutoLock lock(session_map_lock_); + SessionMap::iterator session_it = session_map_.find(session_id); + if (session_map_.end() == session_it) { + LOG4CXX_WARN(logger_, "Session not found in this connection!"); + return false; + } + Session &session = session_it->second; + return ::protocol_handler::PROTOCOL_VERSION_3 == session.protocol_version; +} + +void Connection::StartHeartBeat(uint8_t session_id) { + heartbeat_monitor_->AddSession(session_id); +} + +void Connection::SendHeartBeat(uint8_t session_id) { + connection_handler_->SendHeartBeat(connection_handle_, session_id); +} + +void Connection::KeepAlive(uint8_t session_id) { + heartbeat_monitor_->KeepAlive(session_id); +} + +void Connection::SetHeartBeatTimeout(int32_t timeout) { + heartbeat_monitor_->set_heartbeat_timeout_seconds(timeout); +} + +} // namespace connection_handler diff --git a/src/components/connection_handler/src/connection_handler_impl.cc b/src/components/connection_handler/src/connection_handler_impl.cc new file mode 100644 index 0000000000..f1dae841a6 --- /dev/null +++ b/src/components/connection_handler/src/connection_handler_impl.cc @@ -0,0 +1,863 @@ +/* + * Copyright (c) 2014, 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 <strings.h> + +#include <string> +#include <list> +#include <algorithm> + +#include "connection_handler/connection_handler_impl.h" +#include "transport_manager/info.h" +#include "config_profile/profile.h" + +#ifdef ENABLE_SECURITY +#include "security_manager/security_manager.h" +#endif // ENABLE_SECURITY + +namespace { +int32_t HeartBeatTimeout() { + return profile::Profile::instance()->heart_beat_timeout(); +} +} // namespace + +/** + * \namespace connection_handler + * \brief SmartDeviceLink ConnectionHandler namespace. + */ +namespace connection_handler { + +CREATE_LOGGERPTR_GLOBAL(logger_, "ConnectionHandler") + +ConnectionHandle HandleFromConnectionUID(transport_manager::ConnectionUID uid) { + return ConnectionHandle(uid); +} + +transport_manager::ConnectionUID ConnectionUIDFromHandle( + ConnectionHandle handle) { + return transport_manager::ConnectionUID(handle); +} + +ConnectionHandlerImpl::ConnectionHandlerImpl() + : connection_handler_observer_(NULL), + transport_manager_(NULL), + protocol_handler_(NULL), + connection_list_deleter_(&connection_list_) { +} + +ConnectionHandlerImpl::~ConnectionHandlerImpl() { + LOG4CXX_TRACE(logger_, "Destructing ConnectionHandlerImpl."); +} + +void ConnectionHandlerImpl::Stop() { + LOG4CXX_TRACE_ENTER(logger_); + ConnectionList::iterator itr = connection_list_.begin(); + while (itr != connection_list_.end()) { + RemoveConnection(itr->second->connection_handle()); + itr = connection_list_.begin(); + } + LOG4CXX_TRACE_EXIT(logger_); +} + +void ConnectionHandlerImpl::set_connection_handler_observer( + ConnectionHandlerObserver *observer) { + LOG4CXX_DEBUG(logger_, "ConnectionHandlerImpl::set_connection_handler_observer() " + << observer); + if (!observer) { + LOG4CXX_WARN(logger_, "Set Null pointer to observer."); + } + connection_handler_observer_ = observer; +} + +void ConnectionHandlerImpl::set_transport_manager( + transport_manager::TransportManager *transport_mngr) { + LOG4CXX_DEBUG(logger_, "ConnectionHandlerImpl::set_transport_manager() " + << transport_mngr); + if (!transport_mngr) { + LOG4CXX_ERROR(logger_, "Null pointer to TransportManager."); + return; + } + transport_manager_ = transport_mngr; +} + +void ConnectionHandlerImpl::set_protocol_handler( + protocol_handler::ProtocolHandler *protocol_handler) { + LOG4CXX_DEBUG(logger_, "ConnectionHandlerImpl::set_protocol_handler()" + << protocol_handler); + if (!protocol_handler) { + LOG4CXX_WARN(logger_, "Set Null pointer to protocol handler."); + } + protocol_handler_ = protocol_handler; +} + +void ConnectionHandlerImpl::OnDeviceListUpdated( + const std::vector<transport_manager::DeviceInfo>&) { + LOG4CXX_TRACE(logger_, "ConnectionHandlerImpl::OnDeviceListUpdated()"); + if (connection_handler_observer_) { + connection_handler_observer_->OnDeviceListUpdated(device_list_); + } +} + +void ConnectionHandlerImpl::OnFindNewApplicationsRequest() { + if (connection_handler_observer_) { + connection_handler_observer_->OnFindNewApplicationsRequest(); + } +} + +void ConnectionHandlerImpl::OnDeviceFound( + const transport_manager::DeviceInfo&) { + LOG4CXX_TRACE(logger_, "ConnectionHandlerImpl::OnDeviceFound()"); +} + +void ConnectionHandlerImpl::OnDeviceAdded( + const transport_manager::DeviceInfo &device_info) { + LOG4CXX_TRACE(logger_, "ConnectionHandlerImpl::OnDeviceAdded()"); + device_list_.insert( + DeviceMap::value_type( + device_info.device_handle(), + Device(device_info.device_handle(), device_info.name(), + device_info.mac_address(), device_info.connection_type()))); + if (connection_handler_observer_) { + connection_handler_observer_->OnDeviceListUpdated(device_list_); + } +} + +void ConnectionHandlerImpl::OnDeviceRemoved( + const transport_manager::DeviceInfo &device_info) { + LOG4CXX_TRACE(logger_, "ConnectionHandlerImpl::OnDeviceRemoved()"); + // Device has been removed. Perform all needed actions. + // 1. Delete all the connections and sessions of this device + // 2. Delete device from a list + // 3. Let observer know that device has been deleted. + + std::vector<ConnectionHandle> connections_to_remove; + { + sync_primitives::AutoLock lock(connection_list_lock_); + for (ConnectionList::iterator it = connection_list_.begin(); + it != connection_list_.end(); ++it) { + if (device_info.device_handle() == + (*it).second->connection_device_handle()) { + connections_to_remove.push_back((*it).first); + } + } + } + + std::vector<ConnectionHandle>::iterator it = connections_to_remove.begin(); + for (; it != connections_to_remove.end(); ++it) { + RemoveConnection(*it); + } + + device_list_.erase(device_info.device_handle()); + if (connection_handler_observer_) { + connection_handler_observer_->RemoveDevice(device_info.device_handle()); + connection_handler_observer_->OnDeviceListUpdated(device_list_); + } +} + +void ConnectionHandlerImpl::OnScanDevicesFinished() { + LOG4CXX_TRACE(logger_, "Scan devices finished successfully."); +} + +void ConnectionHandlerImpl::OnScanDevicesFailed( + const transport_manager::SearchDeviceError &error) { + LOG4CXX_WARN(logger_, "Scan devices failed. " << error.text()); +} + +void ConnectionHandlerImpl::OnConnectionEstablished( + const transport_manager::DeviceInfo &device_info, + const transport_manager::ConnectionUID &connection_id) { + LOG4CXX_TRACE(logger_, "ConnectionHandlerImpl::OnConnectionEstablished()"); + + DeviceMap::iterator it = device_list_.find(device_info.device_handle()); + if (device_list_.end() == it) { + LOG4CXX_ERROR(logger_, "Unknown device!"); + return; + } + LOG4CXX_INFO(logger_, "Add Connection:" << connection_id << " to the list."); + sync_primitives::AutoLock lock(connection_list_lock_); + connection_list_.insert( + ConnectionList::value_type( + connection_id, + new Connection(connection_id, device_info.device_handle(), this, + HeartBeatTimeout()))); +} + +void ConnectionHandlerImpl::OnConnectionFailed( + const transport_manager::DeviceInfo &device_info, + const transport_manager::ConnectError &error) { + // TODO(PV): implement + LOG4CXX_ERROR(logger_, "Failed connecting."); +} + +void ConnectionHandlerImpl::OnConnectionClosed( + transport_manager::ConnectionUID connection_id) { + LOG4CXX_INFO(logger_, "ConnectionHandlerImpl::OnConnectionClosed"); + + OnConnectionEnded(connection_id); +} + +void ConnectionHandlerImpl::OnConnectionClosedFailure( + transport_manager::ConnectionUID connection_id, + const transport_manager::DisconnectError &error) { + // TODO(PV): implement + LOG4CXX_ERROR(logger_, "ConnectionHandlerImpl::OnConnectionClosedFailure"); +} + +void ConnectionHandlerImpl::OnUnexpectedDisconnect( + transport_manager::ConnectionUID connection_id, + const transport_manager::CommunicationError &error) { + LOG4CXX_ERROR(logger_, "ConnectionHandlerImpl::OnUnexpectedDisconnect"); + + OnConnectionEnded(connection_id); +} + +void ConnectionHandlerImpl::OnDeviceConnectionLost( + const transport_manager::DeviceHandle &device, + const transport_manager::DisconnectDeviceError &error) { + // TODO(PV): implement + LOG4CXX_ERROR(logger_, "Lost connection with device " << device); +} + +void ConnectionHandlerImpl::OnDisconnectFailed( + const transport_manager::DeviceHandle &device, + const transport_manager::DisconnectDeviceError &error) { + // TODO(PV): implement + LOG4CXX_ERROR(logger_, "Trying to Disconnect device failed."); +} + +void ConnectionHandlerImpl::RemoveConnection( + const ConnectionHandle connection_handle) { + LOG4CXX_INFO(logger_, "ConnectionHandlerImpl::RemoveConnection()"); + + OnConnectionEnded(connection_handle); +} + +#ifdef ENABLE_SECURITY +namespace { +bool AllowProtection(const protocol_handler::ServiceType &service_type, + const bool is_protected) { + if (is_protected) { + // Check deliver-specific services (which shall not be protected) + const std::list<int> force_unprotected_list = + profile::Profile::instance()->ReadIntContainer( + "Security Manager", "ForceUnprotectedService", NULL); + if (std::find(force_unprotected_list.begin(), force_unprotected_list.end(), service_type) != + force_unprotected_list.end()) { + LOG4CXX_ERROR(logger_, "Service " << static_cast<int>(service_type) + << " is forbidden to be protected"); + return false; + } + } else { + // Check deliver-specific services (which shall be protected) + const std::list<int> force_protected_list = + profile::Profile::instance()->ReadIntContainer( + "Security Manager", "ForceProtectedService", NULL); + if (std::find(force_protected_list.begin(), force_protected_list.end(), service_type) != + force_protected_list.end()) { + LOG4CXX_ERROR(logger_, "Service " << static_cast<int>(service_type) + << " shall be protected"); + return false; + } + } + return true; +} +} // namespace +#endif // ENABLE_SECURITY + +uint32_t ConnectionHandlerImpl::OnSessionStartedCallback( + const transport_manager::ConnectionUID &connection_handle, + const uint8_t session_id, const protocol_handler::ServiceType &service_type, + const bool is_protected, uint32_t* hash_id) { + LOG4CXX_TRACE_ENTER(logger_); + + if (hash_id) { + *hash_id = protocol_handler::HASH_ID_WRONG; + } +#ifdef ENABLE_SECURITY + if (!AllowProtection(service_type, is_protected)) { + return 0; + } +#endif // ENABLE_SECURITY + + sync_primitives::AutoLock lock(connection_list_lock_); + ConnectionList::iterator it = connection_list_.find(connection_handle); + if (connection_list_.end() == it) { + LOG4CXX_ERROR(logger_, "Unknown connection!"); + LOG4CXX_TRACE_EXIT(logger_); + return 0; + } + uint32_t new_session_id = 0; + + Connection *connection = it->second; + if ((0 == session_id) && (protocol_handler::kRpc == service_type)) { + new_session_id = connection->AddNewSession(); + if (0 == new_session_id) { + LOG4CXX_ERROR(logger_, "Couldn't start new session!"); + LOG4CXX_TRACE_EXIT(logger_); + return 0; + } + if (hash_id) { + *hash_id = KeyFromPair(connection_handle, new_session_id); + } + } else { // Could be create new service or protected exists one + if (!connection->AddNewService(session_id, service_type, is_protected)) { + LOG4CXX_ERROR(logger_, "Couldn't establish " +#ifdef ENABLE_SECURITY + << (is_protected ? "protected" : "non-protected") +#endif // ENABLE_SECURITY + << " service " << static_cast<int>(service_type) + << " for session " << static_cast<int>(session_id)); + LOG4CXX_TRACE_EXIT(logger_); + return 0; + } + new_session_id = session_id; + if (hash_id) { + *hash_id = protocol_handler::HASH_ID_NOT_SUPPORTED; + } + } + + if (connection_handler_observer_) { + const uint32_t session_key = KeyFromPair(connection_handle, new_session_id); + const bool success = connection_handler_observer_->OnServiceStartedCallback( + connection->connection_device_handle(), session_key, service_type); + if (!success) { + if (protocol_handler::kRpc == service_type) { + connection->RemoveSession(new_session_id); + } else { + connection->RemoveService(session_id, service_type); + } + LOG4CXX_TRACE_EXIT(logger_); + return 0; + } + } + LOG4CXX_TRACE_EXIT(logger_); + return new_session_id; +} + +uint32_t ConnectionHandlerImpl::OnSessionEndedCallback( + const uint32_t &connection_handle, const uint8_t session_id, + const uint32_t &hashCode, + const protocol_handler::ServiceType &service_type) { + LOG4CXX_TRACE(logger_, "ConnectionHandlerImpl::OnSessionEndedCallback()"); + + sync_primitives::AutoLock lock(connection_list_lock_); + ConnectionList::iterator it = connection_list_.find(connection_handle); + if (connection_list_.end() == it) { + LOG4CXX_WARN(logger_, "Unknown connection!"); + return 0; + } + Connection *connection = it->second; + const uint32_t session_key = KeyFromPair(connection_handle, session_id); + + if (protocol_handler::kRpc == service_type) { + LOG4CXX_INFO(logger_, "Session " << static_cast<uint32_t>(session_id) + << " to be removed"); + // old version of protocol doesn't support hash + if (protocol_handler::HASH_ID_NOT_SUPPORTED != hashCode) { + if (protocol_handler::HASH_ID_WRONG == hashCode || + session_key != hashCode) { + LOG4CXX_WARN(logger_, "Wrong hash_id for session " + << static_cast<uint32_t>(session_id)); + return 0; + } + } + if (!connection->RemoveSession(session_id)) { + LOG4CXX_WARN(logger_, "Couldn't remove session " + << static_cast<uint32_t>(session_id)); + return 0; + } + } else { + LOG4CXX_INFO(logger_, "Service " << static_cast<uint32_t>(service_type) + << " to be removed"); + if (!connection->RemoveService(session_id, service_type)) { + LOG4CXX_WARN(logger_, "Couldn't remove service " + << static_cast<uint32_t>(service_type)); + return 0; + } + } + + if (connection_handler_observer_) { + connection_handler_observer_->OnServiceEndedCallback(session_key, + service_type); + } + return session_key; +} + +uint32_t ConnectionHandlerImpl::KeyFromPair( + transport_manager::ConnectionUID connection_handle, uint8_t session_id) { + const uint32_t key = connection_handle | (session_id << 16); + LOG4CXX_TRACE(logger_, "Key for ConnectionHandle:" + << static_cast<uint32_t>(connection_handle) + << " Session:" << static_cast<uint32_t>(session_id) + << " is: " << static_cast<uint32_t>(key)); + if (protocol_handler::HASH_ID_WRONG == key) { + LOG4CXX_ERROR(logger_, "Connection key is WRONG_HASH_ID " + "(session id shall be greater 0)"); + } + return key; +} + +void ConnectionHandlerImpl::PairFromKey(uint32_t key, + uint32_t *connection_handle, + uint8_t *session_id) { + *connection_handle = key & 0xFF00FFFF; + *session_id = key >> 16; + LOG4CXX_TRACE(logger_, "ConnectionHandle:" + << static_cast<int32_t>(*connection_handle) + << " Session:" << static_cast<int32_t>(*session_id) + << " for key:" << static_cast<int32_t>(key)); +} + +int32_t ConnectionHandlerImpl::GetDataOnSessionKey( + uint32_t key, uint32_t *app_id, std::list<int32_t> *sessions_list, + uint32_t *device_id) { + LOG4CXX_TRACE(logger_, "ConnectionHandlerImpl::GetDataOnSessionKey"); + + int32_t result = -1; + transport_manager::ConnectionUID conn_handle = 0; + uint8_t session_id = 0; + PairFromKey(key, &conn_handle, &session_id); + ConnectionList::iterator it = connection_list_.find(conn_handle); + + if (connection_list_.end() == it) { + LOG4CXX_ERROR(logger_, "Unknown connection!"); + return result; + } + + Connection &connection = *it->second; + if (device_id) { + *device_id = connection.connection_device_handle(); + } + + if (sessions_list) { + sessions_list->clear(); + } + + if (0 == session_id) { + LOG4CXX_WARN( + logger_, + "No sessions in connection " << static_cast<int32_t>(conn_handle)); + if (app_id) { + *app_id = 0; + } + } else { + if (app_id) { + *app_id = KeyFromPair(conn_handle, session_id); + } + + LOG4CXX_INFO(logger_, "Connection " + << static_cast<int32_t>(conn_handle) + << " has " << connection.session_map().size() + << " sessions."); + + if (sessions_list) { + const SessionMap session_map = connection.session_map(); + for (SessionMap::const_iterator session_it = session_map.begin(); + session_map.end() != session_it; ++session_it) { + sessions_list->push_back(KeyFromPair(conn_handle, it->first)); + } + } + + result = 0; + } + + return result; +} + +struct CompareMAC { + explicit CompareMAC(const std::string &mac) : mac_(mac) {} + bool operator() (const DeviceMap::value_type &device) { + return strcasecmp(device.second.mac_address().c_str(), mac_.c_str()) == 0; + } + private: + std::string mac_; +}; + +bool ConnectionHandlerImpl::GetDeviceID(const std::string &mac_address, + DeviceHandle *device_handle) { + DeviceMap::const_iterator it = std::find_if(device_list_.begin(), + device_list_.end(), + CompareMAC(mac_address)); + if (it != device_list_.end()) { + if (device_handle) { + *device_handle = it->first; + } + return true; + } + return false; +} + +int32_t ConnectionHandlerImpl::GetDataOnDeviceID( + DeviceHandle device_handle, + std::string *device_name, + std::list<uint32_t> *applications_list, std::string *mac_address, + std::string* connection_type ) { + LOG4CXX_TRACE(logger_, "ConnectionHandlerImpl::GetDataOnDeviceID"); + + int32_t result = -1; + DeviceMap::iterator it = device_list_.find(device_handle); + if (device_list_.end() == it) { + LOG4CXX_ERROR(logger_, "Device not found!"); + return result; + } + + if (device_name) { + *device_name = it->second.user_friendly_name(); + } + if (connection_type) { + *connection_type = it->second.connection_type(); + } + if (applications_list) { + applications_list->clear(); + sync_primitives::AutoLock connection_list_lock(connection_list_lock_); + for (ConnectionList::iterator itr = connection_list_.begin(); + itr != connection_list_.end(); ++itr) { + if (device_handle == (*itr).second->connection_device_handle()) { + const SessionMap &session_map = (itr->second)->session_map(); + for (SessionMap::const_iterator session_it = session_map.begin(); + session_map.end() != session_it; ++session_it) { + const transport_manager::ConnectionUID &connection_handle = itr->first; + const uint32_t session_id = session_it->first; + const uint32_t application_id = KeyFromPair(connection_handle, session_id); + applications_list->push_back(application_id); + } + } + } + } + + if (mac_address) { + *mac_address = it->second.mac_address(); + } + + result = 0; + + return result; +} +#ifdef ENABLE_SECURITY +int ConnectionHandlerImpl::SetSSLContext( + const uint32_t &key, security_manager::SSLContext *context) { + LOG4CXX_TRACE(logger_, "ConnectionHandlerImpl::SetSSLContext"); + transport_manager::ConnectionUID connection_handle = 0; + uint8_t session_id = 0; + PairFromKey(key, &connection_handle, &session_id); + + sync_primitives::AutoLock lock(connection_list_lock_); + ConnectionList::iterator it = connection_list_.find(connection_handle); + if (connection_list_.end() == it) { + LOG4CXX_ERROR(logger_, "Unknown connection!"); + return security_manager::SecurityManager::ERROR_INTERNAL; + } + Connection &connection = *it->second; + return connection.SetSSLContext(session_id, context); +} + +security_manager::SSLContext *ConnectionHandlerImpl::GetSSLContext( + const uint32_t &key, const protocol_handler::ServiceType &service_type) { + LOG4CXX_TRACE(logger_, "ConnectionHandlerImpl::GetSSLContext"); + transport_manager::ConnectionUID connection_handle = 0; + uint8_t session_id = 0; + PairFromKey(key, &connection_handle, &session_id); + + sync_primitives::AutoLock lock(connection_list_lock_); + ConnectionList::iterator it = connection_list_.find(connection_handle); + if (connection_list_.end() == it) { + LOG4CXX_ERROR(logger_, "Unknown connection!"); + return NULL; + } + Connection &connection = *it->second; + return connection.GetSSLContext(session_id, service_type); +} + +void ConnectionHandlerImpl::SetProtectionFlag( + const uint32_t &key, const protocol_handler::ServiceType &service_type) { + LOG4CXX_TRACE(logger_, "ConnectionHandlerImpl::SetProtectionFlag"); + transport_manager::ConnectionUID connection_handle = 0; + uint8_t session_id = 0; + PairFromKey(key, &connection_handle, &session_id); + + sync_primitives::AutoLock lock(connection_list_lock_); + ConnectionList::iterator it = connection_list_.find(connection_handle); + if (connection_list_.end() == it) { + LOG4CXX_ERROR(logger_, "Unknown connection!"); + return; + } + Connection &connection = *it->second; + connection.SetProtectionFlag(session_id, service_type); +} +#endif // ENABLE_SECURITY + +void ConnectionHandlerImpl::StartDevicesDiscovery() { + LOG4CXX_TRACE(logger_, "ConnectionHandlerImpl::StartDevicesDiscovery()"); + + if (NULL == transport_manager_) { + LOG4CXX_ERROR(logger_, "Null pointer to TransportManager."); + return; + } + transport_manager_->SearchDevices(); + if (connection_handler_observer_) { + connection_handler_observer_->OnDeviceListUpdated(device_list_); + } +} + +void ConnectionHandlerImpl::ConnectToDevice( + connection_handler::DeviceHandle device_handle) { + connection_handler::DeviceMap::const_iterator it_in; + it_in = device_list_.find(device_handle); + if (device_list_.end() != it_in) { + LOG4CXX_INFO_EXT(logger_, + "Connecting to device with handle " << device_handle); + if (transport_manager_) { + if (transport_manager::E_SUCCESS + != transport_manager_->ConnectDevice(device_handle)) { + LOG4CXX_WARN(logger_, "Can't connect to device"); + } + } else { + LOG4CXX_ERROR(logger_, "Null pointer to TransportManager."); + } + } else { + LOG4CXX_ERROR( + logger_, "Application Manager wanted to connect to non-existing device"); + } +} + +void ConnectionHandlerImpl::ConnectToAllDevices() { + for (DeviceMap::iterator i = device_list_.begin(); i != device_list_.end(); ++i) { + connection_handler::DeviceHandle device_handle = i->first; + ConnectToDevice(device_handle); + } +} + +void ConnectionHandlerImpl::StartTransportManager() { + LOG4CXX_INFO(logger_, "ConnectionHandlerImpl::StartTransportManager()"); + if (NULL == transport_manager_) { + LOG4CXX_ERROR(logger_, "Null pointer to TransportManager."); + return; + } + + transport_manager_->Visibility(true); +} + +void ConnectionHandlerImpl::CloseRevokedConnection(uint32_t connection_key) { + LOG4CXX_TRACE(logger_, "ConnectionHandlerImpl::CloseRevokedConnection"); + + uint32_t connection_handle = 0; + uint8_t session_id = 0; + PairFromKey(connection_key, &connection_handle, &session_id); + + CloseConnection(connection_handle); +} + +void ConnectionHandlerImpl::CloseConnection( + ConnectionHandle connection_handle) { + LOG4CXX_TRACE(logger_, "ConnectionHandlerImpl::CloseConnection"); + if (!transport_manager_) { + LOG4CXX_ERROR(logger_, "Null pointer to TransportManager."); + return; + } + transport_manager::ConnectionUID connection_uid = + ConnectionUIDFromHandle(connection_handle); + transport_manager_->DisconnectForce(connection_uid); +} + +uint32_t ConnectionHandlerImpl::GetConnectionSessionsCount( + uint32_t connection_key) { + uint32_t connection_handle = 0; + uint8_t session_id = 0; + PairFromKey(connection_key, &connection_handle, &session_id); + + sync_primitives::AutoLock lock(connection_list_lock_); + ConnectionList::iterator itr = connection_list_.find(connection_handle); + + if (connection_list_.end() != itr) { + return itr->second->session_map().size(); + } + + return 0; +} + +void ConnectionHandlerImpl::CloseSession(uint32_t key) { + LOG4CXX_TRACE(logger_, "ConnectionHandlerImpl::CloseSession"); + + uint32_t connection_handle = 0; + uint8_t session_id = 0; + PairFromKey(key, &connection_handle, &session_id); + + CloseSession(connection_handle, session_id); +} + +void ConnectionHandlerImpl::CloseSession(ConnectionHandle connection_handle, + uint8_t session_id) { + if (protocol_handler_) { + protocol_handler_->SendEndSession(connection_handle, session_id); + } + + transport_manager::ConnectionUID connection_id = + ConnectionUIDFromHandle(connection_handle); + + sync_primitives::AutoLock connection_list_lock(connection_list_lock_); + ConnectionList::iterator itr = connection_list_.find(connection_id); + + if (connection_list_.end() != itr) { + if (connection_handler_observer_) { + SessionMap session_map = itr->second->session_map(); + SessionMap::iterator session_it = session_map.find(session_id); + if (session_it != session_map.end()) { + const Session &session = session_it->second; + const ServiceList &service_list = session.service_list; + ServiceList::const_iterator it = service_list.begin(); + for (;it != service_list.end(); ++it) { + const uint32_t session_key = KeyFromPair(connection_id, session_id); + const protocol_handler::ServiceType service_type = it->service_type; + connection_handler_observer_->OnServiceEndedCallback(session_key, + service_type); + } + } + } + itr->second->RemoveSession(session_id); + } +} + +void ConnectionHandlerImpl::StartSessionHeartBeat(uint32_t connection_key) { + uint32_t connection_handle = 0; + uint8_t session_id = 0; + PairFromKey(connection_key, &connection_handle, &session_id); + + sync_primitives::AutoLock lock(connection_list_lock_); + ConnectionList::iterator it = connection_list_.find(connection_handle); + if (connection_list_.end() != it) { + it->second->StartHeartBeat(session_id); + } +} + +void ConnectionHandlerImpl::SetHeartBeatTimeout(uint32_t connection_key, + int32_t timeout) { + uint32_t connection_handle = 0; + uint8_t session_id = 0; + PairFromKey(connection_key, &connection_handle, &session_id); + sync_primitives::AutoLock lock(connection_list_lock_); + ConnectionList::iterator it = connection_list_.find(connection_handle); + if (connection_list_.end() != it) { + it->second->SetHeartBeatTimeout(timeout); + } +} + +void ConnectionHandlerImpl::SendHeartBeat(ConnectionHandle connection_handle, + uint8_t session_id) { + transport_manager::ConnectionUID connection_uid = + ConnectionUIDFromHandle(connection_handle); + if (protocol_handler_) { + protocol_handler_->SendHeartBeat(connection_uid, session_id); + } +} + +void ConnectionHandlerImpl::KeepConnectionAlive(uint32_t connection_key, + uint8_t session_id) { + sync_primitives::AutoLock lock(connection_list_lock_); + + ConnectionList::iterator it = connection_list_.find(connection_key); + if (connection_list_.end() != it) { + it->second->KeepAlive(session_id); + } +} + +void ConnectionHandlerImpl::OnConnectionEnded( + const transport_manager::ConnectionUID &connection_id) { + LOG4CXX_INFO(logger_, "Delete Connection: " << static_cast<int32_t>(connection_id) + << " from the list."); + + sync_primitives::AutoLock lock(connection_list_lock_); + ConnectionList::iterator itr = connection_list_.find(connection_id); + if (connection_list_.end() == itr) { + LOG4CXX_ERROR(logger_, "Connection not found!"); + return; + } + + if (connection_handler_observer_) { + const Connection *connection = itr->second; + const SessionMap session_map = connection->session_map(); + + for (SessionMap::const_iterator session_it = session_map.begin(); + session_map.end() != session_it; ++session_it) { + const uint32_t session_key = KeyFromPair(connection_id, session_it->first); + const ServiceList &service_list = session_it->second.service_list; + for (ServiceList::const_iterator service_it = service_list.begin(), end = + service_list.end(); service_it != end; ++service_it) { + connection_handler_observer_->OnServiceEndedCallback( + session_key, service_it->service_type); + } + } + } + delete itr->second; + connection_list_.erase(itr); +} + +void ConnectionHandlerImpl::BindProtocolVersionWithSession( + uint32_t connection_key, uint8_t protocol_version) { + LOG4CXX_INFO(logger_, "ConnectionHandlerImpl::BindProtocolVersionWithSession()"); + uint32_t connection_handle = 0; + uint8_t session_id = 0; + PairFromKey(connection_key, &connection_handle, &session_id); + + sync_primitives::AutoLock lock(connection_list_lock_); + ConnectionList::iterator it = connection_list_.find(connection_handle); + if (connection_list_.end() != it) { + it->second->UpdateProtocolVersionSession(session_id, protocol_version); + } +} + +bool ConnectionHandlerImpl::IsHeartBeatSupported( + transport_manager::ConnectionUID connection_handle,uint8_t session_id) { + LOG4CXX_INFO(logger_, "ConnectionHandlerImpl::IsHeartBeatSupported()"); + sync_primitives::AutoLock lock(connection_list_lock_); + uint32_t connection = static_cast<uint32_t>(connection_handle); + ConnectionList::iterator it = connection_list_.find(connection); + if (connection_list_.end() == it) { + LOG4CXX_WARN(logger_, "Connection not found !"); + return false; + } + return it->second->SupportHeartBeat(session_id); +} + +#ifdef BUILD_TESTS +ConnectionList &ConnectionHandlerImpl::getConnectionList() { + return connection_list_; +} + +void ConnectionHandlerImpl::addDeviceConnection( + const transport_manager::DeviceInfo &device_info, + const transport_manager::ConnectionUID &connection_id) { + // Add Device + OnDeviceAdded(device_info); + // Add connection + OnConnectionEstablished(device_info, connection_id); +} +#endif // BUILD_TESTS +} // namespace connection_handler diff --git a/src/components/connection_handler/src/device.cc b/src/components/connection_handler/src/device.cc new file mode 100644 index 0000000000..93638bd370 --- /dev/null +++ b/src/components/connection_handler/src/device.cc @@ -0,0 +1,74 @@ +/** + * \file Device.cpp + * \brief Device class implementation. + * + * Copyright (c) 2013, 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 "connection_handler/device.h" +#include "encryption/hashing.h" +#include "utils/logger.h" + +/** + * \namespace connection_handler + * \brief SmartDeviceLink ConnectionHandler namespace. + */ +namespace connection_handler { + +CREATE_LOGGERPTR_GLOBAL(logger_, "ConnectionHandler") + +Device::Device(DeviceHandle device_handle, + const std::string &user_friendly_name, + const std::string &mac_address, const std::string& connection_type) + : device_handle_(device_handle), + user_friendly_name_(user_friendly_name), + mac_address_(mac_address), + connection_type_(connection_type){ + mac_address_ = encryption::MakeHash(mac_address); +} + +DeviceHandle Device::device_handle() const { + return device_handle_; +} + +std::string Device::user_friendly_name() const { + return user_friendly_name_; +} + +std::string Device::mac_address() const { + return mac_address_; +} + +std::string Device::connection_type() const { + return connection_type_; +} + +} // namespace connection_handler diff --git a/src/components/connection_handler/src/heartbeat_monitor.cc b/src/components/connection_handler/src/heartbeat_monitor.cc new file mode 100644 index 0000000000..6a5a9e723e --- /dev/null +++ b/src/components/connection_handler/src/heartbeat_monitor.cc @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2013-2014, 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 "connection_handler/heartbeat_monitor.h" + +#include <unistd.h> + +#include "utils/logger.h" +#include "connection_handler/connection.h" + +namespace connection_handler { + +using namespace sync_primitives; + +CREATE_LOGGERPTR_GLOBAL(logger_, "HeartBeatMonitor") + +HeartBeatMonitor::HeartBeatMonitor(int32_t heartbeat_timeout_seconds, + Connection *connection) + : heartbeat_timeout_seconds_(heartbeat_timeout_seconds), + connection_(connection), + sessions_list_lock_(true), + run_(true) { +} + +bool HeartBeatMonitor::HasTimeoutElapsed(const TimevalStruct& expiration) const { + TimevalStruct now = date_time::DateTime::getCurrentTime(); + return date_time::DateTime::Greater(now, expiration); +} + +void HeartBeatMonitor::Process() { + AutoLock auto_lock(sessions_list_lock_); + + SessionMap::iterator it = sessions_.begin(); + while (it != sessions_.end()) { + SessionState &state = it->second; + if (HasTimeoutElapsed(state.heartbeat_expiration)) { + const uint8_t session_id = it->first; + if (state.is_heartbeat_sent) { + LOG4CXX_DEBUG(logger_, + "Session with id " << static_cast<int32_t>(session_id) << " timed out, closing"); + connection_->CloseSession(session_id); + it = sessions_.begin(); + continue; + } else { + LOG4CXX_DEBUG(logger_, + "Send heart beat into session with id " << static_cast<int32_t>(session_id)); + RefreshExpiration(&state.heartbeat_expiration); + connection_->SendHeartBeat(it->first); + state.is_heartbeat_sent = true; + } + } + + ++it; + } +} + +void HeartBeatMonitor::RefreshExpiration(TimevalStruct* expiration) const { + LOG4CXX_TRACE_ENTER(logger_); + sync_primitives::AutoLock locker(heartbeat_timeout_seconds_lock_); + DCHECK(expiration); + *expiration = date_time::DateTime::getCurrentTime(); + expiration->tv_sec += heartbeat_timeout_seconds_; + LOG4CXX_TRACE_EXIT(logger_); +} + +void HeartBeatMonitor::threadMain() { + AutoLock main_lock(main_thread_lock_); + LOG4CXX_DEBUG( + logger_, + "Start heart beat monitor. Timeout is " << heartbeat_timeout_seconds_); + while (run_) { + usleep(kDefaultCycleTimeout); + Process(); + } +} + +void HeartBeatMonitor::AddSession(uint8_t session_id) { + LOG4CXX_DEBUG(logger_, "Add session with id " << static_cast<int32_t>(session_id)); + AutoLock auto_lock(sessions_list_lock_); + if (sessions_.end() != sessions_.find(session_id)) { + LOG4CXX_WARN( + logger_, + "Session with id " << static_cast<int32_t>(session_id) << " already exists"); + return; + } + SessionState session_state; + RefreshExpiration(&session_state.heartbeat_expiration); + session_state.is_heartbeat_sent = false; + sessions_[session_id] = session_state; + + LOG4CXX_INFO( + logger_, + "Start heartbeat for session " << static_cast<int32_t>(session_id)); +} + +void HeartBeatMonitor::RemoveSession(uint8_t session_id) { + AutoLock auto_lock(sessions_list_lock_); + + if (sessions_.end() != sessions_.find(session_id)) { + LOG4CXX_INFO(logger_, + "Remove session with id " << static_cast<int32_t>(session_id)); + sessions_.erase(session_id); + } +} + +void HeartBeatMonitor::KeepAlive(uint8_t session_id) { + AutoLock auto_lock(sessions_list_lock_); + + if (sessions_.end() != sessions_.find(session_id)) { + LOG4CXX_INFO( + logger_, + "Resetting heart beat timer for session with id " << static_cast<int32_t>(session_id)); + + RefreshExpiration(&sessions_[session_id].heartbeat_expiration); + sessions_[session_id].is_heartbeat_sent = false; + } +} + +bool HeartBeatMonitor::exitThreadMain() { + LOG4CXX_TRACE_ENTER(logger_); + run_ = false; + AutoLock main_lock(main_thread_lock_); + LOG4CXX_TRACE_EXIT(logger_); + return true; +} + +void HeartBeatMonitor::set_heartbeat_timeout_seconds(int32_t timeout) { + LOG4CXX_DEBUG(logger_, "Set new heart beat timeout " << timeout); + { + AutoLock locker(heartbeat_timeout_seconds_lock_); + heartbeat_timeout_seconds_ = timeout; + } + + AutoLock session_locker(sessions_list_lock_); + for (SessionMap::iterator i = sessions_.begin(); i != sessions_.end(); ++i) { + SessionState& session_state = i->second; + RefreshExpiration(&session_state.heartbeat_expiration); + } +} + +} // namespace connection_handler |