diff options
Diffstat (limited to 'src/components/connection_handler')
9 files changed, 1305 insertions, 356 deletions
diff --git a/src/components/connection_handler/include/connection_handler/connection.h b/src/components/connection_handler/include/connection_handler/connection.h index 9b72d60776..4f900bb65e 100644 --- a/src/components/connection_handler/include/connection_handler/connection.h +++ b/src/components/connection_handler/include/connection_handler/connection.h @@ -72,13 +72,18 @@ typedef std::map<int32_t, Connection*> ConnectionList; */ struct Service { protocol_handler::ServiceType service_type; + transport_manager::ConnectionUID connection_id; bool is_protected_; Service() : service_type(protocol_handler::kInvalidServiceType) + , connection_id(0) , is_protected_(false) {} - explicit Service(protocol_handler::ServiceType service_type) - : service_type(service_type), is_protected_(false) {} + explicit Service(protocol_handler::ServiceType service_type, + transport_manager::ConnectionUID connection_id) + : service_type(service_type) + , connection_id(connection_id) + , is_protected_(false) {} bool operator==(const protocol_handler::ServiceType service_type) const { return this->service_type == service_type; @@ -154,9 +159,11 @@ class Connection { /** * @brief Adds session to connection + * @param connection_handle Connection Handle for the session * @return new session id or 0 in case of issues */ - uint32_t AddNewSession(); + uint32_t AddNewSession( + const transport_manager::ConnectionUID connection_handle); /** * @brief Removes session from connection @@ -171,11 +178,13 @@ class Connection { * @param session_id session ID * @param service_type Type of service * @param is_protected protection state + * @param connection_id Connection ID associated with the service * @return TRUE on success, otherwise FALSE */ bool AddNewService(uint8_t session_id, protocol_handler::ServiceType service_type, - const bool is_protected); + const bool is_protected, + transport_manager::ConnectionUID connection_id); /** * @brief Removes service from session * @param session_id session ID @@ -184,6 +193,18 @@ class Connection { */ bool RemoveService(uint8_t session_id, protocol_handler::ServiceType service_type); + + /** + * @brief Removes secondary service from session + * @param secondary_connection_handle connection identifying services to be + * removed + * @param removed_services_list Returned: List of service types removed + * @return the session ID associated with the services removed + */ + uint8_t RemoveSecondaryServices( + transport_manager::ConnectionUID secondary_connection_handle, + std::list<protocol_handler::ServiceType>& removed_services_list); + #ifdef ENABLE_SECURITY /** * @brief Sets crypto context of service @@ -213,10 +234,11 @@ class Connection { const protocol_handler::ServiceType& service_type); #endif // ENABLE_SECURITY - /** - * @brief Returns map of sessions which have been opened in - * current connection. - */ + + /** + * @brief Returns map of sessions which have been opened in + * current connection. + */ const SessionMap session_map() const; /** @@ -283,6 +305,20 @@ class Connection { */ bool ProtocolVersion(uint8_t session_id, uint8_t& protocol_version); + /** + * @brief Returns the primary connection handle associated with this + * connection + * @return ConnectionHandle + */ + ConnectionHandle primary_connection_handle() const; + + /** + * @brief Sets the primary connection handle + * @param primary_connection_handle the primary connection handle to + * associate with this connection + */ + void SetPrimaryConnectionHandle(ConnectionHandle primary_connection_handle); + private: /** * @brief Current connection handler. @@ -304,7 +340,12 @@ class Connection { */ SessionMap session_map_; - mutable sync_primitives::Lock session_map_lock_; + mutable sync_primitives::RecursiveLock session_map_lock_; + + /** + * @brief primary connection handle for secondary connections + */ + ConnectionHandle primary_connection_handle_; /** * @brief monitor that closes connection if there is no traffic over it 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 index 66b2d7cf16..1ab70ce702 100644 --- a/src/components/connection_handler/include/connection_handler/connection_handler_impl.h +++ b/src/components/connection_handler/include/connection_handler/connection_handler_impl.h @@ -54,11 +54,14 @@ #include "utils/stl_utils.h" #include "utils/rwlock.h" +const transport_manager::ConnectionUID kDisabledSecondary = 0xFFFFFFFF; + /** * \namespace connection_handler * \brief SmartDeviceLink connection_handler namespace. */ namespace connection_handler { + /** * \class ConnectionHandlerImpl * \brief SmartDeviceLink connection_handler main class @@ -193,25 +196,6 @@ class ConnectionHandlerImpl /** * \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. - * \deprecated - */ - 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 start of new session. * Result must be notified through NotifySessionStartedContext(). * \param connection_handle Connection identifier within which session * has to be started. @@ -227,22 +211,7 @@ class ConnectionHandlerImpl const protocol_handler::ServiceType& service_type, const bool is_protected, const BsonObject* params); - /** - * \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 - * \deprecated - */ - uint32_t OnSessionEndedCallback( - const transport_manager::ConnectionUID connection_handle, - const uint8_t session_id, - const uint32_t& hashCode, - const protocol_handler::ServiceType& service_type) OVERRIDE; + /** * \brief Callback function used by ProtocolHandler * when Mobile Application initiates session ending. @@ -274,6 +243,24 @@ class ConnectionHandlerImpl void OnMalformedMessageCallback(const uint32_t& connection_key) OVERRIDE; /** + * @brief Converts connection handle to transport type string used in + * smartDeviceLink.ini file, e.g. "TCP_WIFI" + * @param connection_handle A connection identifier + * @return string representation of the transport of the device + */ + const std::string TransportTypeProfileStringFromConnHandle( + transport_manager::ConnectionUID connection_handle) const; + + /** + * @brief Converts device handle to transport type string used in + * smartDeviceLink.ini file, e.g. "TCP_WIFI" + * @param device_handle A device handle + * @return string representation of the transport of the device + */ + const std::string TransportTypeProfileStringFromDeviceHandle( + DeviceHandle device_handle) const; + + /** * \brief Creates unique identifier of session (can be used as hash) * from given connection identifier * within which session exists and session number. @@ -503,26 +490,49 @@ class ConnectionHandlerImpl std::list<int32_t>* sessions_list, connection_handler::DeviceHandle* device_id) const OVERRIDE; - /** - * DEPRECATED - * \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 - */ - int32_t GetDataOnSessionKey(uint32_t key, - uint32_t* app_id, - std::list<int32_t>* sessions_list, - uint32_t* device_id) const OVERRIDE; - const ConnectionHandlerSettings& get_settings() const OVERRIDE; const protocol_handler::SessionObserver& get_session_observer(); DevicesDiscoveryStarter& get_device_discovery_starter(); /** + * \brief Add a session. This is meant to be called from Connection class. + * \param primary_transport_id the primary connection ID to associate with the + * newly created session + * \return new session id, or 0 if failed + **/ + uint32_t AddSession( + const transport_manager::ConnectionUID primary_transport_id) OVERRIDE; + + /** + * \brief Remove a session. This is meant to be called from Connection class. + * \param session_id ID of the session to remove + * \return true if successful, false otherwise + **/ + bool RemoveSession(uint8_t session_id) OVERRIDE; + + DataAccessor<SessionConnectionMap> session_connection_map() OVERRIDE; + + /** + * \brief Associate a secondary transport ID with a session + * \param session_id the session ID + * \param connection_id the new secondary connection ID to associate with the + * session + * \return the SessionTransports (newly) associated with the session + **/ + SessionTransports SetSecondaryTransportID( + uint8_t session_id, + transport_manager::ConnectionUID secondary_transport_id) OVERRIDE; + + /** + * \brief Retrieve the session transports associated with a session + * \param session_id the session ID + * \return the SessionTransports associated with the session + **/ + const SessionTransports GetSessionTransports( + uint8_t session_id) const OVERRIDE; + + /** * \brief Invoked when observer's OnServiceStartedCallback is completed * \param session_key the key of started session passed to * OnServiceStartedCallback(). @@ -538,6 +548,28 @@ class ConnectionHandlerImpl bool result, std::vector<std::string>& rejected_params); + /** + * \brief Called when secondary transport with given session ID is established + * \param primary_connection_handle Set to identifier of primary connection + * \param secondary_connection_handle Identifier of secondary connection + * \param sessionid session ID taken from Register Secondary Transport frame + **/ + bool OnSecondaryTransportStarted( + transport_manager::ConnectionUID& primary_connection_handle, + const transport_manager::ConnectionUID secondary_connection_handle, + const uint8_t session_id) OVERRIDE; + + /** + * \brief Called when secondary transport shuts down + * \param primary_connection_handle Identifier of primary connection + * \param secondary_connection_handle Identifier of secondary connection + * transport + **/ + void OnSecondaryTransportEnded( + const transport_manager::ConnectionUID primary_connection_handle, + const transport_manager::ConnectionUID secondary_connection_handle) + OVERRIDE; + private: /** * \brief Disconnect application. @@ -549,6 +581,9 @@ class ConnectionHandlerImpl void OnConnectionEnded(const transport_manager::ConnectionUID connection_id); + const uint8_t GetSessionIdFromSecondaryTransport( + transport_manager::ConnectionUID secondary_transport_id) const; + const ConnectionHandlerSettings& settings_; /** * \brief Pointer to observer @@ -568,6 +603,13 @@ class ConnectionHandlerImpl DeviceMap device_list_; /** + * @brief session/connection map + */ + SessionConnectionMap session_connection_map_; + mutable std::shared_ptr<sync_primitives::RecursiveLock> + session_connection_map_lock_ptr_; + + /** * \brief List of connections */ ConnectionList connection_list_; @@ -587,6 +629,11 @@ class ConnectionHandlerImpl std::map<uint32_t, protocol_handler::SessionContext> start_service_context_map_; + /** + * @brief connection object as it's being closed + */ + Connection* ending_connection_; + #ifdef BUILD_TESTS // Methods for test usage public: @@ -595,6 +642,9 @@ class ConnectionHandlerImpl void addDeviceConnection( const transport_manager::DeviceInfo& device_info, const transport_manager::ConnectionUID connection_id); + SessionConnectionMap& getSessionConnectionMap() { + return session_connection_map_; + } #endif private: DISALLOW_COPY_AND_ASSIGN(ConnectionHandlerImpl); diff --git a/src/components/connection_handler/include/connection_handler/heartbeat_monitor.h b/src/components/connection_handler/include/connection_handler/heartbeat_monitor.h index 4d1d07112c..cf956353dd 100644 --- a/src/components/connection_handler/include/connection_handler/heartbeat_monitor.h +++ b/src/components/connection_handler/include/connection_handler/heartbeat_monitor.h @@ -102,7 +102,7 @@ class HeartBeatMonitor : public threads::ThreadDelegate { void RefreshExpiration(); uint32_t heartbeat_timeout_mseconds_; - TimevalStruct heartbeat_expiration_; + date_time::TimeDuration heartbeat_expiration_; bool is_heartbeat_sent_; }; @@ -111,7 +111,7 @@ class HeartBeatMonitor : public threads::ThreadDelegate { typedef std::map<uint8_t, SessionState> SessionMap; SessionMap sessions_; - sync_primitives::Lock sessions_list_lock_; // recurcive + sync_primitives::RecursiveLock sessions_list_lock_; // recurcive sync_primitives::Lock main_thread_lock_; mutable sync_primitives::Lock heartbeat_timeout_seconds_lock_; sync_primitives::ConditionalVariable heartbeat_monitor_; diff --git a/src/components/connection_handler/src/connection.cc b/src/components/connection_handler/src/connection.cc index 16b88c4164..3f5b0e1901 100644 --- a/src/components/connection_handler/src/connection.cc +++ b/src/components/connection_handler/src/connection.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Ford Motor Company + * Copyright (c) 2018, Ford Motor Company * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -81,7 +81,7 @@ Connection::Connection(ConnectionHandle connection_handle, : connection_handler_(connection_handler) , connection_handle_(connection_handle) , connection_device_handle_(connection_device_handle) - , session_map_lock_(true) + , primary_connection_handle_(0) , heartbeat_timeout_(heartbeat_timeout) { LOG4CXX_AUTO_TRACE(logger_); DCHECK(connection_handler_); @@ -97,39 +97,62 @@ Connection::~Connection() { heart_beat_monitor_thread_->join(); delete heartbeat_monitor_; threads::DeleteThread(heart_beat_monitor_thread_); - sync_primitives::AutoLock lock(session_map_lock_); - session_map_.clear(); -} -// 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; - } + // Before clearing out the session_map_, we must remove all sessions + // associated with this Connection from the SessionConnectionMap. + + // NESTED LOCK: make sure to lock session_map_lock_ then ConnectionHandler's + // session_connection_map_lock_ptr_ (which will be taken in RemoveSession). + sync_primitives::AutoLock lock(session_map_lock_); + SessionMap::iterator session_it = session_map_.begin(); + while (session_it != session_map_.end()) { + LOG4CXX_INFO( + logger_, + "Removed Session ID " + << static_cast<int>(session_it->first) + << " from Session/Connection Map in Connection Destructor"); + connection_handler_->RemoveSession(session_it->first); + session_it++; } - return 0; + + session_map_.clear(); } -} // namespace -uint32_t Connection::AddNewSession() { +uint32_t Connection::AddNewSession( + const transport_manager::ConnectionUID connection_handle) { LOG4CXX_AUTO_TRACE(logger_); + + // NESTED LOCK: make sure to lock session_map_lock_ then ConnectionHandler's + // session_connection_map_lock_ptr_ (which will be taken in AddSession) sync_primitives::AutoLock lock(session_map_lock_); - const uint32_t session_id = findGap(session_map_); + + // Even though we have our own SessionMap, we use the Connection Handler's + // SessionConnectionMap to generate a session ID. We want to make sure that + // session IDs are globally unique, and not only unique within a Connection. + const uint32_t session_id = + connection_handler_->AddSession(connection_handle); 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)); + new_session.service_list.push_back( + Service(protocol_handler::kRpc, connection_handle)); + new_session.service_list.push_back( + Service(protocol_handler::kBulk, connection_handle)); } + return session_id; } uint32_t Connection::RemoveSession(uint8_t session_id) { + LOG4CXX_AUTO_TRACE(logger_); + + // Again, a NESTED lock, but it follows the rules. sync_primitives::AutoLock lock(session_map_lock_); + + if (!connection_handler_->RemoveSession(session_id)) { + return 0; + } + SessionMap::iterator it = session_map_.find(session_id); if (session_map_.end() == it) { LOG4CXX_WARN(logger_, "Session not found in this connection!"); @@ -137,12 +160,14 @@ uint32_t Connection::RemoveSession(uint8_t session_id) { } 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) { + const bool request_protection, + transport_manager::ConnectionUID connection_id) { // Ignore wrong services if (protocol_handler::kControl == service_type || protocol_handler::kInvalidServiceType == service_type) { @@ -152,7 +177,9 @@ bool Connection::AddNewService(uint8_t session_id, LOG4CXX_DEBUG(logger_, "Add service " << service_type << " for session " - << static_cast<uint32_t>(session_id)); + << static_cast<uint32_t>(session_id) + << " using connection ID " + << static_cast<uint32_t>(connection_id)); sync_primitives::AutoLock lock(session_map_lock_); SessionMap::iterator session_it = session_map_.find(session_id); @@ -201,7 +228,7 @@ bool Connection::AddNewService(uint8_t session_id, #endif // ENABLE_SECURITY } // id service is not exists - session.service_list.push_back(Service(service_type)); + session.service_list.push_back(Service(service_type, connection_id)); return true; } @@ -246,6 +273,63 @@ bool Connection::RemoveService(uint8_t session_id, return true; } +uint8_t Connection::RemoveSecondaryServices( + transport_manager::ConnectionUID secondary_connection_handle, + std::list<protocol_handler::ServiceType>& removed_services_list) { + LOG4CXX_AUTO_TRACE(logger_); + + uint8_t found_session_id = 0; + sync_primitives::AutoLock lock(session_map_lock_); + + LOG4CXX_INFO(logger_, + "RemoveSecondaryServices looking for services on Connection ID " + << static_cast<int>(secondary_connection_handle)); + + // Walk the SessionMap in the primary connection, and for each + // Session, we walk its ServiceList, looking for all the services + // that were running on the now-closed Secondary Connection. + for (SessionMap::iterator session_it = session_map_.begin(); + session_map_.end() != session_it; + ++session_it) { + LOG4CXX_INFO(logger_, + "RemoveSecondaryServices found session ID " + << static_cast<int>(session_it->first)); + + // Now, for each session, walk the its ServiceList, looking for services + // that were using secondary)_connection_handle. If we find such a service, + // set session_found and break out of the outer loop. + ServiceList& service_list = session_it->second.service_list; + ServiceList::iterator service_it = service_list.begin(); + for (; service_it != service_list.end();) { + LOG4CXX_INFO(logger_, + "RemoveSecondaryServices found service ID " + << static_cast<int>(service_it->service_type)); + if (service_it->connection_id == secondary_connection_handle) { + found_session_id = session_it->first; + + LOG4CXX_INFO(logger_, + "RemoveSecondaryServices removing Service " + << static_cast<int>(service_it->service_type) + << " in session " + << static_cast<int>(found_session_id)); + + removed_services_list.push_back(service_it->service_type); + service_it = service_list.erase(service_it); + } else { + service_it++; + } + } + + // If we found a session that had services running on the secondary + // connection, we're done. + if (found_session_id != 0) { + break; + } + } + + return found_session_id; +} + #ifdef ENABLE_SECURITY int Connection::SetSSLContext(uint8_t session_id, security_manager::SSLContext* context) { @@ -346,7 +430,6 @@ const SessionMap Connection::session_map() const { } void Connection::CloseSession(uint8_t session_id) { - size_t size; { sync_primitives::AutoLock lock(session_map_lock_); @@ -354,16 +437,10 @@ void Connection::CloseSession(uint8_t session_id) { if (session_it == session_map_.end()) { return; } - size = session_map_.size(); } connection_handler_->CloseSession( connection_handle_, session_id, connection_handler::kCommon); - - // Close connection if it is last session - if (1 == size) { - connection_handler_->CloseConnection(connection_handle_); - } } void Connection::UpdateProtocolVersionSession(uint8_t session_id, @@ -405,6 +482,15 @@ bool Connection::ProtocolVersion(uint8_t session_id, return true; } +ConnectionHandle Connection::primary_connection_handle() const { + return primary_connection_handle_; +} + +void Connection::SetPrimaryConnectionHandle( + ConnectionHandle primary_connection_handle) { + primary_connection_handle_ = primary_connection_handle; +} + void Connection::StartHeartBeat(uint8_t session_id) { heartbeat_monitor_->AddSession(session_id); } diff --git a/src/components/connection_handler/src/connection_handler_impl.cc b/src/components/connection_handler/src/connection_handler_impl.cc index 5b26304302..478127c42a 100644 --- a/src/components/connection_handler/src/connection_handler_impl.cc +++ b/src/components/connection_handler/src/connection_handler_impl.cc @@ -68,11 +68,14 @@ ConnectionHandlerImpl::ConnectionHandlerImpl( , connection_handler_observer_(NULL) , transport_manager_(tm) , protocol_handler_(NULL) + , session_connection_map_lock_ptr_( + std::make_shared<sync_primitives::RecursiveLock>()) , connection_list_lock_() , connection_handler_observer_lock_() , connection_list_deleter_(&connection_list_) , start_service_context_map_lock_() - , start_service_context_map_() {} + , start_service_context_map_() + , ending_connection_(NULL) {} ConnectionHandlerImpl::~ConnectionHandlerImpl() { LOG4CXX_AUTO_TRACE(logger_); @@ -316,6 +319,7 @@ void ConnectionHandlerImpl::RemoveConnection( bool AllowProtection(const ConnectionHandlerSettings& settings, const protocol_handler::ServiceType& service_type, const bool is_protected) { + LOG4CXX_AUTO_TRACE(logger_); const std::vector<int>& force_unprotected_list = is_protected ? settings.force_unprotected_service() : settings.force_protected_service(); @@ -334,86 +338,42 @@ bool AllowProtection(const ConnectionHandlerSettings& settings, } #endif // ENABLE_SECURITY -uint32_t ConnectionHandlerImpl::OnSessionStartedCallback( +void 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) { + const BsonObject* params) { LOG4CXX_AUTO_TRACE(logger_); - if (hash_id) { - *hash_id = protocol_handler::HASH_ID_WRONG; - } - -#ifdef ENABLE_SECURITY - if (!AllowProtection(get_settings(), service_type, is_protected)) { - return 0; - } -#endif // ENABLE_SECURITY - sync_primitives::AutoReadLock lock(connection_list_lock_); - ConnectionList::iterator it = connection_list_.find(connection_handle); - if (connection_list_.end() == it) { - LOG4CXX_ERROR(logger_, "Unknown connection!"); - return 0; - } - uint32_t new_session_id = 0; + std::vector<std::string> rejected_params; - 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!"); - 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)); - return 0; - } - new_session_id = session_id; - if (hash_id) { - *hash_id = protocol_handler::HASH_ID_NOT_SUPPORTED; - } - } - sync_primitives::AutoReadLock read_lock(connection_handler_observer_lock_); - 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) { + // In case this is a Session running on a Secondary Transport, we need to + // find the Sessions's primary transport. In this case, "connection_handle" + // reflects the secondary transport, which we need for the various callbacks, + // so they can send appropriate Ack or NAK messages on the correct transport. + transport_manager::ConnectionUID primary_connection_handle = + connection_handle; + SessionTransports st = GetSessionTransports(session_id); + if (session_id != 0) { + if (st.primary_transport == 0) { LOG4CXX_WARN(logger_, - "Service starting forbidden by connection_handler_observer"); - if (protocol_handler::kRpc == service_type) { - connection->RemoveSession(new_session_id); - } else { - connection->RemoveService(session_id, service_type); - } - return 0; + "OnSessionStartedCallback could not find Session in the " + "Session/Connection Map!"); + } else { + LOG4CXX_INFO(logger_, + "OnSessionStartedCallback found session " + << static_cast<int>(session_id) + << " with primary connection " + << static_cast<int>(st.primary_transport) + << " and secondary connection " + << static_cast<int>(st.secondary_transport)); + primary_connection_handle = st.primary_transport; } } - return new_session_id; -} -void ConnectionHandlerImpl::OnSessionStartedCallback( - const transport_manager::ConnectionUID connection_handle, - const uint8_t session_id, - const protocol_handler::ServiceType& service_type, - const bool is_protected, - const BsonObject* params) { - LOG4CXX_AUTO_TRACE(logger_); - - std::vector<std::string> rejected_params; - protocol_handler::SessionContext context(connection_handle, + protocol_handler::SessionContext context(primary_connection_handle, + connection_handle, session_id, 0, service_type, @@ -427,7 +387,8 @@ void ConnectionHandlerImpl::OnSessionStartedCallback( } #endif // ENABLE_SECURITY sync_primitives::AutoReadLock lock(connection_list_lock_); - ConnectionList::iterator it = connection_list_.find(connection_handle); + ConnectionList::iterator it = + connection_list_.find(primary_connection_handle); if (connection_list_.end() == it) { LOG4CXX_ERROR(logger_, "Unknown connection!"); protocol_handler_->NotifySessionStarted(context, rejected_params); @@ -439,15 +400,18 @@ void ConnectionHandlerImpl::OnSessionStartedCallback( !connection->SessionServiceExists(session_id, service_type); if ((0 == session_id) && (protocol_handler::kRpc == service_type)) { - context.new_session_id_ = connection->AddNewSession(); + context.new_session_id_ = + connection->AddNewSession(primary_connection_handle); if (0 == context.new_session_id_) { LOG4CXX_ERROR(logger_, "Couldn't start new session!"); protocol_handler_->NotifySessionStarted(context, rejected_params); return; } - context.hash_id_ = KeyFromPair(connection_handle, context.new_session_id_); + context.hash_id_ = + KeyFromPair(primary_connection_handle, context.new_session_id_); } else { // Could be create new service or protected exists one - if (!connection->AddNewService(session_id, service_type, is_protected)) { + if (!connection->AddNewService( + session_id, service_type, is_protected, connection_handle)) { LOG4CXX_ERROR(logger_, "Couldn't establish " #ifdef ENABLE_SECURITY @@ -460,19 +424,16 @@ void ConnectionHandlerImpl::OnSessionStartedCallback( } context.new_session_id_ = session_id; context.hash_id_ = protocol_handler::HASH_ID_NOT_SUPPORTED; + + if (st.secondary_transport == connection_handle) { + sync_primitives::AutoLock auto_lock(session_connection_map_lock_ptr_); + st.secondary_transport_services.push_back(service_type); + } } sync_primitives::AutoReadLock read_lock(connection_handler_observer_lock_); if (connection_handler_observer_) { const uint32_t session_key = - KeyFromPair(connection_handle, context.new_session_id_); - - uint32_t app_id = 0; - GetDataOnSessionKey( - session_key, &app_id, NULL, static_cast<DeviceHandle*>(NULL)); - if (app_id > 0) { - context.is_ptu_required_ = - !connection_handler_observer_->CheckAppIsNavi(app_id); - } + KeyFromPair(primary_connection_handle, context.new_session_id_); { sync_primitives::AutoLock auto_lock(start_service_context_map_lock_); @@ -509,10 +470,12 @@ void ConnectionHandlerImpl::NotifyServiceStartedResult( start_service_context_map_.erase(it); } + // We need the context's primary connection so we can manage its services list Connection* connection = NULL; { sync_primitives::AutoReadLock lock(connection_list_lock_); - ConnectionList::iterator it = connection_list_.find(context.connection_id_); + ConnectionList::iterator it = + connection_list_.find(context.primary_connection_id_); if (connection_list_.end() == it) { LOG4CXX_ERROR(logger_, "connection not found"); return; @@ -545,6 +508,28 @@ void ConnectionHandlerImpl::OnApplicationFloodCallBack( uint8_t session_id = 0; PairFromKey(connection_key, &connection_handle, &session_id); + // In case this is a Session running on a Secondary Transport, + // "connection_handle" will reflect the active (secondary) transport. + // To close the conneciton and its sessions properly, we need to find + // the Sessions's primary transport/connection. + if (session_id != 0) { + SessionTransports st = GetSessionTransports(session_id); + if (st.primary_transport == 0) { + LOG4CXX_WARN(logger_, + "OnApplicationFloodCallBack could not find Session in the " + "Session/Connection Map!"); + } else { + LOG4CXX_INFO(logger_, + "OnApplicationFloodCallBack found session " + << static_cast<int>(session_id) + << " with primary connection " + << static_cast<int>(st.primary_transport) + << " and secondary connection " + << static_cast<int>(st.secondary_transport)); + connection_handle = st.primary_transport; + } + } + LOG4CXX_INFO(logger_, "Disconnect flooding application"); if (session_id != 0) { CloseSession(connection_handle, session_id, kFlood); @@ -562,6 +547,28 @@ void ConnectionHandlerImpl::OnMalformedMessageCallback( uint8_t session_id = 0; PairFromKey(connection_key, &connection_handle, &session_id); + // In case this is a Session running on a Secondary Transport, + // "connection_handle" will reflect the active (secondary) transport. + // To close the conneciton and its sessions properly, we need to find + // the Sessions's primary transport/connection. + if (session_id != 0) { + SessionTransports st = GetSessionTransports(session_id); + if (st.primary_transport == 0) { + LOG4CXX_WARN(logger_, + "OnMalformedMessageCallback could not find Session in the " + "Session/Connection Map!"); + } else { + LOG4CXX_INFO(logger_, + "OnMalformedMessageCallback found session " + << static_cast<int>(session_id) + << " with primary connection " + << static_cast<int>(st.primary_transport) + << " and secondary connection " + << static_cast<int>(st.secondary_transport)); + connection_handle = st.primary_transport; + } + } + LOG4CXX_INFO(logger_, "Disconnect malformed messaging application"); CloseConnectionSessions(connection_handle, kMalformed); CloseConnection(connection_handle); @@ -570,22 +577,37 @@ void ConnectionHandlerImpl::OnMalformedMessageCallback( uint32_t ConnectionHandlerImpl::OnSessionEndedCallback( const transport_manager::ConnectionUID connection_handle, const uint8_t session_id, - const uint32_t& hashCode, - const protocol_handler::ServiceType& service_type) { - uint32_t hashValue = hashCode; - return OnSessionEndedCallback( - connection_handle, session_id, &hashValue, service_type); -} - -uint32_t ConnectionHandlerImpl::OnSessionEndedCallback( - const transport_manager::ConnectionUID connection_handle, - const uint8_t session_id, uint32_t* hashCode, const protocol_handler::ServiceType& service_type) { LOG4CXX_AUTO_TRACE(logger_); + // In case this is a Session running on a Secondary Transport, we need to + // find the Sessions's primary transport. In this case, "connection_handle" + // reflects the secondary transport, which we need for the various callbacks, + // so they can send appropriate Ack or NAK messages on the correct transport. + transport_manager::ConnectionUID primary_connection_handle = + connection_handle; + if (session_id != 0) { + SessionTransports st = GetSessionTransports(session_id); + if (st.primary_transport == 0) { + LOG4CXX_WARN(logger_, + "OnSessionEndedCallback could not find Session in the " + "Session/Connection Map!"); + } else { + LOG4CXX_INFO(logger_, + "OnSessionEndedCallback found session " + << static_cast<int>(session_id) + << " with primary connection " + << static_cast<int>(st.primary_transport) + << " and secondary connection " + << static_cast<int>(st.secondary_transport)); + primary_connection_handle = st.primary_transport; + } + } + connection_list_lock_.AcquireForReading(); - ConnectionList::iterator it = connection_list_.find(connection_handle); + ConnectionList::iterator it = + connection_list_.find(primary_connection_handle); if (connection_list_.end() == it) { LOG4CXX_WARN(logger_, "Unknown connection!"); connection_list_lock_.Release(); @@ -595,7 +617,8 @@ uint32_t ConnectionHandlerImpl::OnSessionEndedCallback( connection_list_lock_.Release(); Connection* connection = connection_item.second; - const uint32_t session_key = KeyFromPair(connection_handle, session_id); + const uint32_t session_key = + KeyFromPair(primary_connection_handle, session_id); if (protocol_handler::kRpc == service_type) { LOG4CXX_INFO(logger_, @@ -638,6 +661,178 @@ uint32_t ConnectionHandlerImpl::OnSessionEndedCallback( return session_key; } +bool ConnectionHandlerImpl::OnSecondaryTransportStarted( + transport_manager::ConnectionUID& primary_connection_handle, + const transport_manager::ConnectionUID secondary_connection_handle, + const uint8_t session_id) { + LOG4CXX_AUTO_TRACE(logger_); + + if (session_id == 0) { + LOG4CXX_WARN(logger_, "Session id for secondary transport is invalid"); + return false; + } + + DeviceHandle device_handle; + Connection* connection; + { + sync_primitives::AutoReadLock lock(connection_list_lock_); + ConnectionList::iterator it = + connection_list_.find(secondary_connection_handle); + if (connection_list_.end() == it) { + LOG4CXX_WARN(logger_, + "Unknown connection " << secondary_connection_handle); + return false; + } + + connection = it->second; + device_handle = connection->connection_device_handle(); + } + + // Add the secondary transport connection ID to the SessionConnectionMap + SessionTransports st = + SetSecondaryTransportID(session_id, secondary_connection_handle); + primary_connection_handle = st.primary_transport; + if (st.secondary_transport != secondary_connection_handle) { + LOG4CXX_WARN(logger_, + "Failed setting the session's secondary transport ID"); + return false; + } + + connection->SetPrimaryConnectionHandle(primary_connection_handle); + + const uint32_t session_key = + KeyFromPair(primary_connection_handle, session_id); + + sync_primitives::AutoReadLock read_lock(connection_handler_observer_lock_); + if (connection_handler_observer_) { + LOG4CXX_TRACE(logger_, + "Calling Connection Handler Observer's " + "OnSecondaryTransportStartedCallback"); + connection_handler_observer_->OnSecondaryTransportStartedCallback( + device_handle, session_key); + } + + return true; +} + +void ConnectionHandlerImpl::OnSecondaryTransportEnded( + const transport_manager::ConnectionUID primary_connection_handle, + const transport_manager::ConnectionUID secondary_connection_handle) { + LOG4CXX_AUTO_TRACE(logger_); + + LOG4CXX_INFO(logger_, + "Secondary Transport: " + << static_cast<int32_t>(secondary_connection_handle) + << " ended. Cleaning up services from primary connection ID " + << static_cast<int32_t>(primary_connection_handle)); + connection_list_lock_.AcquireForReading(); + ConnectionList::iterator itr = + connection_list_.find(primary_connection_handle); + if (connection_list_.end() == itr) { + LOG4CXX_ERROR(logger_, "Primary Connection not found!"); + connection_list_lock_.Release(); + return; + } + Connection* connection = itr->second; + connection_list_lock_.Release(); + + if (connection != NULL) { + std::list<protocol_handler::ServiceType> removed_services_list; + uint8_t session_id = connection->RemoveSecondaryServices( + secondary_connection_handle, removed_services_list); + + if (session_id == 0) { + // The secondary services have already been removed from the primary + // connection, so we find the session associated with this secondary + // transport in the SessionConnectionMap + session_id = + GetSessionIdFromSecondaryTransport(secondary_connection_handle); + } + + if (session_id != 0) { + { + sync_primitives::AutoReadLock read_lock( + connection_handler_observer_lock_); + if (connection_handler_observer_) { + const uint32_t session_key = + KeyFromPair(primary_connection_handle, session_id); + + // Walk the returned list of services and call the ServiceEnded + // callback for each + std::list<protocol_handler::ServiceType>::const_iterator it = + removed_services_list.begin(); + for (; removed_services_list.end() != it; ++it) { + connection_handler_observer_->OnServiceEndedCallback( + session_key, *it, CloseSessionReason::kCommon); + } + + connection_handler_observer_->OnSecondaryTransportEndedCallback( + session_key); + } + } + + // Clear the secondary connection from the Session/Connection map entry + // associated with this session + SetSecondaryTransportID(session_id, 0); + } + } +} + +const std::string +ConnectionHandlerImpl::TransportTypeProfileStringFromConnHandle( + transport_manager::ConnectionUID connection_handle) const { + LOG4CXX_AUTO_TRACE(logger_); + + sync_primitives::AutoReadLock lock(connection_list_lock_); + ConnectionList::const_iterator it = connection_list_.find(connection_handle); + if (connection_list_.end() == it) { + LOG4CXX_WARN(logger_, "Unknown connection " << connection_handle); + return std::string(); + } else { + DeviceHandle device_handle = it->second->connection_device_handle(); + return TransportTypeProfileStringFromDeviceHandle(device_handle); + } +} + +const std::string +ConnectionHandlerImpl::TransportTypeProfileStringFromDeviceHandle( + DeviceHandle device_handle) const { + std::string connection_type; + DeviceMap::const_iterator it = device_list_.find(device_handle); + if (device_list_.end() == it) { + LOG4CXX_ERROR(logger_, "Device not found!"); + } else { + connection_type = it->second.connection_type(); + } + + // Caution: this should be in sync with devicesType map in + // transport_adapter_impl.cc + if (connection_type == "USB_AOA") { + return std::string("AOA_USB"); + } else if (connection_type == "BLUETOOTH") { + return std::string("SPP_BLUETOOTH"); + } else if (connection_type == "USB_IOS") { + return std::string("IAP_USB"); + } else if (connection_type == "BLUETOOTH_IOS") { + return std::string("IAP_BLUETOOTH"); + } else if (connection_type == "WIFI") { + return std::string("TCP_WIFI"); + } else if (connection_type == "USB_IOS_HOST_MODE") { + return std::string("IAP_USB_HOST_MODE"); + } else if (connection_type == "USB_IOS_DEVICE_MODE") { + return std::string("IAP_USB_DEVICE_MODE"); + } else if (connection_type == "CARPLAY_WIRELESS_IOS") { + return std::string("IAP_CARPLAY"); +#ifdef BUILD_TESTS + } else if (connection_type == "BTMAC") { + return std::string("BTMAC"); +#endif + } else { + LOG4CXX_WARN(logger_, "Unknown transport type string: " << connection_type); + return std::string(); + } +} + uint32_t ConnectionHandlerImpl::KeyFromPair( transport_manager::ConnectionUID connection_handle, uint8_t session_id) const { @@ -646,7 +841,7 @@ uint32_t ConnectionHandlerImpl::KeyFromPair( "Key for ConnectionHandle:" << static_cast<uint32_t>(connection_handle) << " Session:" << static_cast<uint32_t>(session_id) - << " is: " << static_cast<uint32_t>(key)); + << " is: 0x" << std::hex << static_cast<uint32_t>(key)); if (protocol_handler::HASH_ID_WRONG == key) { LOG4CXX_ERROR(logger_, "Connection key is WRONG_HASH_ID " @@ -716,18 +911,6 @@ int32_t ConnectionHandlerImpl::GetDataOnSessionKey( return 0; } -int32_t ConnectionHandlerImpl::GetDataOnSessionKey( - uint32_t key, - uint32_t* app_id, - std::list<int32_t>* sessions_list, - uint32_t* device_id) const { - LOG4CXX_AUTO_TRACE(logger_); - DeviceHandle handle; - int32_t result = GetDataOnSessionKey(key, app_id, sessions_list, &handle); - *device_id = static_cast<uint32_t>(handle); - return result; -} - const ConnectionHandlerSettings& ConnectionHandlerImpl::get_settings() const { return settings_; } @@ -741,6 +924,139 @@ DevicesDiscoveryStarter& ConnectionHandlerImpl::get_device_discovery_starter() { return *this; } +// 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 ConnectionHandlerImpl::AddSession( + const transport_manager::ConnectionUID primary_transport_id) { + LOG4CXX_AUTO_TRACE(logger_); + + sync_primitives::AutoLock auto_lock(session_connection_map_lock_ptr_); + const uint32_t session_id = findGap(session_connection_map_); + if (session_id > 0) { + LOG4CXX_INFO(logger_, + "New session ID " << session_id << " and Connection Id " + << static_cast<int>(primary_transport_id) + << " added to Session/Connection Map"); + SessionTransports st; + st.primary_transport = primary_transport_id; + st.secondary_transport = 0; + session_connection_map_[session_id] = st; + } else { + LOG4CXX_WARN(logger_, + "Session/Connection Map could not create a new session ID!!!"); + } + + return session_id; +} + +bool ConnectionHandlerImpl::RemoveSession(uint8_t session_id) { + LOG4CXX_AUTO_TRACE(logger_); + + sync_primitives::AutoLock auto_lock(session_connection_map_lock_ptr_); + SessionConnectionMap::iterator itr = session_connection_map_.find(session_id); + if (session_connection_map_.end() == itr) { + LOG4CXX_WARN(logger_, "Session not found in Session/Connection Map!"); + return false; + } + + LOG4CXX_INFO(logger_, + "Removed Session ID " << static_cast<int>(session_id) + << " from Session/Connection Map"); + session_connection_map_.erase(session_id); + return true; +} + +DataAccessor<SessionConnectionMap> +ConnectionHandlerImpl::session_connection_map() { + return DataAccessor<SessionConnectionMap>(session_connection_map_, + session_connection_map_lock_ptr_); +} + +SessionTransports ConnectionHandlerImpl::SetSecondaryTransportID( + uint8_t session_id, + transport_manager::ConnectionUID secondary_transport_id) { + SessionTransports st; + + sync_primitives::AutoLock auto_lock(session_connection_map_lock_ptr_); + SessionConnectionMap::iterator it = session_connection_map_.find(session_id); + if (session_connection_map_.end() == it) { + LOG4CXX_WARN(logger_, + "SetSecondaryTransportID: session ID " + << static_cast<int>(session_id) + << " not found in Session/Connection map"); + st.primary_transport = 0; + st.secondary_transport = 0; + } else { + st = it->second; + + // The only time we overwrite an existing entry in the map is if the new + // secondary transport ID is kDisabledSecondary, which effectively DISABLES + // the secondary transport feature for the session, or if the new secondary + // transport ID is 0, which means a secondary transport has shut down + if (st.secondary_transport != 0 && + secondary_transport_id != kDisabledSecondary && + secondary_transport_id != 0) { + LOG4CXX_WARN(logger_, + "SetSecondaryTransportID: session ID " + << static_cast<int>(session_id) + << " already has a secondary connection " + << static_cast<int>(st.secondary_transport) + << " in the Session/Connection map"); + } else { + st.secondary_transport = secondary_transport_id; + session_connection_map_[session_id] = st; + } + } + + return st; +} + +const SessionTransports ConnectionHandlerImpl::GetSessionTransports( + uint8_t session_id) const { + SessionTransports st; + sync_primitives::AutoLock auto_lock(session_connection_map_lock_ptr_); + SessionConnectionMap::const_iterator it = + session_connection_map_.find(session_id); + if (session_connection_map_.end() == it) { + st.primary_transport = 0; + st.secondary_transport = 0; + } else { + st = it->second; + } + + return st; +} + +const uint8_t ConnectionHandlerImpl::GetSessionIdFromSecondaryTransport( + transport_manager::ConnectionUID secondary_transport_id) const { + sync_primitives::AutoLock auto_lock(session_connection_map_lock_ptr_); + SessionConnectionMap::const_iterator it = session_connection_map_.begin(); + for (; session_connection_map_.end() != it; it++) { + SessionTransports st = it->second; + if (st.secondary_transport == secondary_transport_id) { + return it->first; + } + } + + LOG4CXX_ERROR(logger_, + "Could not find secondary transport ID " + << static_cast<int>(secondary_transport_id) + << " in the Session/Connection map"); + return 0; +} + struct CompareMAC { explicit CompareMAC(const std::string& mac) : mac_(mac) {} bool operator()(const DeviceMap::value_type& device) { @@ -1117,8 +1433,33 @@ void ConnectionHandlerImpl::SendEndService(uint32_t key, uint8_t service_type) { uint32_t connection_handle = 0; uint8_t session_id = 0; PairFromKey(key, &connection_handle, &session_id); - protocol_handler_->SendEndService( - connection_handle, session_id, service_type); + + // If the service is running on a secondary transport, we need to retrieve + // that transport from the SessionConnection Map + SessionTransports st = GetSessionTransports(session_id); + if (st.primary_transport == 0) { + LOG4CXX_WARN(logger_, + "SendEndService could not find Session in the " + "Session/Connection Map!"); + } else { + LOG4CXX_INFO(logger_, + "SendEndService found session " + << static_cast<int>(session_id) + << " with primary connection " + << static_cast<int>(st.primary_transport) + << " and secondary connection " + << static_cast<int>(st.secondary_transport)); + sync_primitives::AutoLock auto_lock(session_connection_map_lock_ptr_); + auto it = std::find(st.secondary_transport_services.begin(), + st.secondary_transport_services.end(), + service_type); + if (it != st.secondary_transport_services.end()) { + connection_handle = st.secondary_transport; + st.secondary_transport_services.erase(it); + } + protocol_handler_->SendEndService( + st.primary_transport, connection_handle, session_id, service_type); + } } } @@ -1179,12 +1520,16 @@ void ConnectionHandlerImpl::OnConnectionEnded( connection_list_lock_.Release(); return; } - std::auto_ptr<Connection> connection(itr->second); + std::unique_ptr<Connection> connection(itr->second); connection_list_.erase(itr); connection_list_lock_.Release(); sync_primitives::AutoReadLock read_lock(connection_handler_observer_lock_); if (connection_handler_observer_ && connection.get() != NULL) { + // We have to remember the Connection object we just removed from + // connection_list_, because we will need to retrieve the protocol + // version from it inside of OnServiceEndedCallback + ending_connection_ = connection.get(); const SessionMap session_map = connection->session_map(); for (SessionMap::const_iterator session_it = session_map.begin(); @@ -1201,6 +1546,13 @@ void ConnectionHandlerImpl::OnConnectionEnded( session_key, service_it->service_type, CloseSessionReason::kCommon); } } + ending_connection_ = NULL; + } + + ConnectionHandle primary_connection_handle = + connection->primary_connection_handle(); + if (primary_connection_handle != 0) { + OnSecondaryTransportEnded(primary_connection_handle, connection_id); } } @@ -1241,6 +1593,10 @@ bool ConnectionHandlerImpl::ProtocolVersionUsed( ConnectionList::const_iterator it = connection_list_.find(connection_id); if (connection_list_.end() != it) { return it->second->ProtocolVersion(session_id, protocol_version); + } else if (ending_connection_ && + static_cast<uint32_t>(ending_connection_->connection_handle()) == + connection_id) { + return ending_connection_->ProtocolVersion(session_id, protocol_version); } LOG4CXX_WARN(logger_, "Connection not found !"); return false; diff --git a/src/components/connection_handler/src/heartbeat_monitor.cc b/src/components/connection_handler/src/heartbeat_monitor.cc index f3a2322810..4c2d5899af 100644 --- a/src/components/connection_handler/src/heartbeat_monitor.cc +++ b/src/components/connection_handler/src/heartbeat_monitor.cc @@ -47,7 +47,6 @@ HeartBeatMonitor::HeartBeatMonitor(uint32_t heartbeat_timeout_mseconds, Connection* connection) : default_heartbeat_timeout_(heartbeat_timeout_mseconds) , connection_(connection) - , sessions_list_lock_(true) , run_(true) {} void HeartBeatMonitor::Process() { @@ -90,18 +89,20 @@ void HeartBeatMonitor::threadMain() { } void HeartBeatMonitor::AddSession(uint8_t session_id) { - LOG4CXX_DEBUG(logger_, - "Add session with id " << static_cast<int32_t>(session_id)); + const uint32_t converted_session_id = static_cast<int32_t>(session_id); + UNUSED(converted_session_id); + LOG4CXX_DEBUG(logger_, "Add session with id " << converted_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"); + "Session with id: " << converted_session_id + << " already exists"); return; } sessions_.insert( std::make_pair(session_id, SessionState(default_heartbeat_timeout_))); - LOG4CXX_INFO(logger_, "Start heartbeat for session " << session_id); + LOG4CXX_INFO(logger_, + "Start heartbeat for session: " << converted_session_id); } void HeartBeatMonitor::RemoveSession(uint8_t session_id) { @@ -164,8 +165,8 @@ HeartBeatMonitor::SessionState::SessionState( void HeartBeatMonitor::SessionState::RefreshExpiration() { LOG4CXX_DEBUG(logger_, "Refresh expiration: " << heartbeat_timeout_mseconds_); using namespace date_time; - TimevalStruct time = DateTime::getCurrentTime(); - DateTime::AddMilliseconds(time, heartbeat_timeout_mseconds_); + date_time::TimeDuration time = getCurrentTime(); + AddMilliseconds(time, heartbeat_timeout_mseconds_); heartbeat_expiration_ = time; } @@ -194,8 +195,8 @@ void HeartBeatMonitor::SessionState::KeepAlive() { } bool HeartBeatMonitor::SessionState::HasTimeoutElapsed() { - TimevalStruct now = date_time::DateTime::getCurrentTime(); - return date_time::DateTime::Greater(now, heartbeat_expiration_); + date_time::TimeDuration now = date_time::getCurrentTime(); + return date_time::Greater(now, heartbeat_expiration_); } } // namespace connection_handler diff --git a/src/components/connection_handler/test/connection_handler_impl_test.cc b/src/components/connection_handler/test/connection_handler_impl_test.cc index d0b9ce4ae4..64de24b103 100644 --- a/src/components/connection_handler/test/connection_handler_impl_test.cc +++ b/src/components/connection_handler/test/connection_handler_impl_test.cc @@ -88,7 +88,7 @@ class ConnectionHandlerTest : public ::testing::Test { mac_address_ = "test_address"; connection_handler_ = new ConnectionHandlerImpl( - mock_connection_handler_settings, mock_transport_manager_); + mock_connection_handler_settings_, mock_transport_manager_); uid_ = 1u; connection_key_ = connection_handler_->KeyFromPair(0, 0u); protected_services_.clear(); @@ -101,23 +101,29 @@ class ConnectionHandlerTest : public ::testing::Test { void SetSpecificServices() { #ifdef ENABLE_SECURITY - ON_CALL(mock_connection_handler_settings, force_protected_service()) + ON_CALL(mock_connection_handler_settings_, force_protected_service()) .WillByDefault(ReturnRefOfCopy(protected_services_)); - ON_CALL(mock_connection_handler_settings, force_unprotected_service()) + ON_CALL(mock_connection_handler_settings_, force_unprotected_service()) .WillByDefault(ReturnRefOfCopy(unprotected_services_)); #endif // ENABLE_SECURITY } + // Additional SetUp void AddTestDeviceConnection() { const transport_manager::DeviceInfo device_info( device_handle_, mac_address_, device_name_, connection_type_); // Add Device and connection - ON_CALL(mock_connection_handler_settings, heart_beat_timeout()) + ON_CALL(mock_connection_handler_settings_, heart_beat_timeout()) .WillByDefault(Return(1000u)); connection_handler_->addDeviceConnection(device_info, uid_); connection_key_ = connection_handler_->KeyFromPair(uid_, 0u); // Remove all specific services } + + /** + * @brief Adds test session + * @return session_id + */ void AddTestSession() { protocol_handler_test::MockProtocolHandler temp_protocol_handler; connection_handler_->set_protocol_handler(&temp_protocol_handler); @@ -134,9 +140,11 @@ class ConnectionHandlerTest : public ::testing::Test { connection_handler_->KeyFromPair(uid_, out_context_.new_session_id_); CheckSessionExists(uid_, out_context_.new_session_id_); } + uint32_t SessionHash(const uint32_t connection, const uint32_t session) { return connection_handler_->KeyFromPair(connection, session); } + void AddTestService(ServiceType service_type) { EXPECT_NE(0u, out_context_.new_session_id_); EXPECT_EQ(SessionHash(uid_, out_context_.new_session_id_), @@ -198,10 +206,12 @@ class ConnectionHandlerTest : public ::testing::Test { } // Check Service Wrapper - void CheckServiceExists(const int connectionId, - const int session_id, - const ::protocol_handler::ServiceType serviceId, - const bool exists) { + void CheckServiceExists( + const int connectionId, + const int session_id, + const ::protocol_handler::ServiceType serviceId, + const ::transport_manager::ConnectionUID serviceConnectionId, + const bool exists) { // Check all trees to find Service and check own protected value const ConnectionList& connection_list = connection_handler_->getConnectionList(); @@ -221,6 +231,7 @@ class ConnectionHandlerTest : public ::testing::Test { std::find(service_list.begin(), service_list.end(), serviceId); if (exists) { ASSERT_NE(serv_it, service_list.end()); + ASSERT_EQ(serv_it->connection_id, serviceConnectionId); } else { ASSERT_EQ(serv_it, service_list.end()); } @@ -306,12 +317,37 @@ class ConnectionHandlerTest : public ::testing::Test { connection->ProtocolVersion(session_id, check_protocol_version)); EXPECT_EQ(check_protocol_version, protocol_version); } + void AddSecondaryTestService(ServiceType service_type) { + EXPECT_NE(0u, out_context_.new_session_id_); + EXPECT_EQ(SessionHash(uid_, out_context_.new_session_id_), + out_context_.hash_id_); + CheckSessionExists(uid_, out_context_.new_session_id_); + + // Set protocol version to 5 + ChangeProtocol(uid_, + out_context_.new_session_id_, + protocol_handler::PROTOCOL_VERSION_5); + + connection_handler_test::MockConnectionHandlerObserver + temp_connection_handler_observer; + connection_handler_->set_connection_handler_observer( + &temp_connection_handler_observer); + EXPECT_CALL(temp_connection_handler_observer, + OnServiceStartedCallback(_, _, service_type, _)).Times(1); + + connection_handler_->OnSessionStartedCallback(2u, + out_context_.new_session_id_, + service_type, + PROTECTION_OFF, + static_cast<BsonObject*>(0)); + connection_handler_->set_connection_handler_observer(NULL); + } ConnectionHandlerImpl* connection_handler_; testing::NiceMock<transport_manager_test::MockTransportManager> mock_transport_manager_; testing::NiceMock<MockConnectionHandlerSettings> - mock_connection_handler_settings; + mock_connection_handler_settings_; protocol_handler_test::MockProtocolHandler mock_protocol_handler_; transport_manager::DeviceHandle device_handle_; transport_manager::ConnectionUID uid_; @@ -376,23 +412,23 @@ TEST_F(ConnectionHandlerTest, GetAppIdOnSessionKey) { AddTestSession(); uint32_t app_id = 0; - const uint32_t testid = SessionHash(uid_, out_context_.new_session_id_); + const uint32_t test_id = SessionHash(uid_, out_context_.new_session_id_); - connection_handler::DeviceHandle null_handle = 0; + connection_handler::DeviceHandle* device_id = NULL; EXPECT_EQ(0, connection_handler_->GetDataOnSessionKey( - connection_key_, &app_id, NULL, &null_handle)); - EXPECT_EQ(testid, app_id); + connection_key_, &app_id, NULL, device_id)); + EXPECT_EQ(test_id, app_id); } TEST_F(ConnectionHandlerTest, GetAppIdOnSessionKey_SessionNotStarted) { AddTestDeviceConnection(); uint32_t app_id = 0; - connection_handler::DeviceHandle null_handle = 0; + connection_handler::DeviceHandle* device_id = NULL; EXPECT_EQ(-1, connection_handler_->GetDataOnSessionKey( - connection_key_, &app_id, NULL, &null_handle)); + connection_key_, &app_id, NULL, device_id)); } TEST_F(ConnectionHandlerTest, GetDeviceID) { @@ -500,6 +536,13 @@ TEST_F(ConnectionHandlerTest, GetPairFromKey) { EXPECT_EQ(out_context_.new_session_id_, session_id); } +MATCHER_P(SameDevice, device, "") { + return arg.device_handle() == device.device_handle() && + arg.user_friendly_name() == device.user_friendly_name() && + arg.mac_address() == device.mac_address() && + arg.connection_type() == device.connection_type(); +} + TEST_F(ConnectionHandlerTest, IsHeartBeatSupported) { AddTestDeviceConnection(); AddTestSession(); @@ -508,17 +551,10 @@ TEST_F(ConnectionHandlerTest, IsHeartBeatSupported) { uid_, out_context_.new_session_id_)); } -MATCHER_P(SameDevice, device, "") { - return arg.device_handle() == device.device_handle() && - arg.user_friendly_name() == device.user_friendly_name() && - arg.mac_address() == device.mac_address() && - arg.connection_type() == device.connection_type(); -} - TEST_F(ConnectionHandlerTest, SendEndServiceWithoutSetProtocolHandler) { AddTestDeviceConnection(); AddTestSession(); - EXPECT_CALL(mock_protocol_handler_, SendEndService(_, _, kRpc)).Times(0); + EXPECT_CALL(mock_protocol_handler_, SendEndService(_, _, _, kRpc)).Times(0); connection_handler_->SendEndService(connection_key_, kRpc); } @@ -526,7 +562,7 @@ TEST_F(ConnectionHandlerTest, SendEndService) { AddTestDeviceConnection(); AddTestSession(); connection_handler_->set_protocol_handler(&mock_protocol_handler_); - EXPECT_CALL(mock_protocol_handler_, SendEndService(_, _, kRpc)); + EXPECT_CALL(mock_protocol_handler_, SendEndService(_, _, _, kRpc)); connection_handler_->SendEndService(connection_key_, kRpc); } @@ -1112,7 +1148,7 @@ TEST_F(ConnectionHandlerTest, StartService_withServices) { PROTECTION_OFF, static_cast<BsonObject*>(NULL)); EXPECT_NE(0u, audio_context.new_session_id_); - CheckServiceExists(uid_, audio_context.new_session_id_, kAudio, true); + CheckServiceExists(uid_, audio_context.new_session_id_, kAudio, uid_, true); EXPECT_EQ(protocol_handler::HASH_ID_NOT_SUPPORTED, audio_context.hash_id_); // Start Audio service @@ -1122,7 +1158,8 @@ TEST_F(ConnectionHandlerTest, StartService_withServices) { PROTECTION_OFF, static_cast<BsonObject*>(NULL)); EXPECT_NE(0u, video_context.new_session_id_); - CheckServiceExists(uid_, video_context.new_session_id_, kMobileNav, true); + CheckServiceExists( + uid_, video_context.new_session_id_, kMobileNav, uid_, true); EXPECT_EQ(protocol_handler::HASH_ID_NOT_SUPPORTED, video_context.hash_id_); connection_handler_->set_protocol_handler(NULL); @@ -1150,7 +1187,8 @@ TEST_F(ConnectionHandlerTest, StartService_withServices_withParams) { PROTECTION_OFF, dummy_param); EXPECT_EQ(out_context_.new_session_id_, video_context.new_session_id_); - CheckServiceExists(uid_, out_context_.new_session_id_, kMobileNav, true); + CheckServiceExists( + uid_, out_context_.new_session_id_, kMobileNav, uid_, true); EXPECT_EQ(protocol_handler::HASH_ID_NOT_SUPPORTED, video_context.hash_id_); connection_handler_->set_protocol_handler(NULL); @@ -1174,7 +1212,7 @@ TEST_F(ConnectionHandlerTest, ServiceStop_UnExistService) { connection_handler_->OnSessionEndedCallback( uid_, out_context_.new_session_id_, &dummy_hash, kAudio); EXPECT_EQ(0u, end_session_result); - CheckServiceExists(uid_, out_context_.new_session_id_, kAudio, false); + CheckServiceExists(uid_, out_context_.new_session_id_, kAudio, uid_, false); } TEST_F(ConnectionHandlerTest, ServiceStop) { @@ -1204,7 +1242,7 @@ TEST_F(ConnectionHandlerTest, ServiceStop) { connection_handler_->OnSessionEndedCallback( uid_, out_context_.new_session_id_, &some_hash_id, kAudio); EXPECT_EQ(connection_key_, end_session_result); - CheckServiceExists(uid_, out_context_.new_session_id_, kAudio, false); + CheckServiceExists(uid_, out_context_.new_session_id_, kAudio, uid_, false); } } @@ -1235,8 +1273,8 @@ TEST_F(ConnectionHandlerTest, SessionStop_CheckSpecificHash) { for (uint32_t session = 0; session < 0xFF; ++session) { AddTestSession(); - uint32_t wrong_hash = protocol_handler::HASH_ID_WRONG; - uint32_t hash = protocol_handler::HASH_ID_NOT_SUPPORTED; + uint32_t hash = connection_key_; + uint32_t wrong_hash = hash + 1; const uint32_t end_audio_wrong_hash = connection_handler_->OnSessionEndedCallback( @@ -1273,9 +1311,6 @@ TEST_F(ConnectionHandlerTest, SessionStarted_WithRpc) { true, ByRef(empty))); - EXPECT_CALL(mock_connection_handler_observer, CheckAppIsNavi(_)) - .WillOnce(Return(true)); - connection_handler_->set_protocol_handler(&mock_protocol_handler_); EXPECT_CALL(mock_protocol_handler_, NotifySessionStarted(_, _)) .WillOnce(SaveArg<0>(&out_context_)); @@ -1312,8 +1347,6 @@ TEST_F(ConnectionHandlerTest, ServiceStarted_Video_SUCCESS) { session_key, true, ByRef(empty))); - EXPECT_CALL(mock_connection_handler_observer, CheckAppIsNavi(_)) - .WillOnce(Return(true)); // confirm that NotifySessionStarted() is called connection_handler_->set_protocol_handler(&mock_protocol_handler_); @@ -1354,8 +1387,6 @@ TEST_F(ConnectionHandlerTest, ServiceStarted_Video_FAILURE) { session_key, false, ByRef(empty))); - EXPECT_CALL(mock_connection_handler_observer, CheckAppIsNavi(_)) - .WillOnce(Return(true)); // confirm that NotifySessionStarted() is called connection_handler_->set_protocol_handler(&mock_protocol_handler_); @@ -1446,9 +1477,6 @@ TEST_F(ConnectionHandlerTest, ServiceStarted_Video_Multiple) { session_key1, true, ByRef(empty)))); - EXPECT_CALL(mock_connection_handler_observer, CheckAppIsNavi(_)) - .Times(2) - .WillRepeatedly(Return(true)); // verify that connection handler will not mix up the two results SessionContext new_context_first, new_context_second; @@ -1474,7 +1502,7 @@ TEST_F(ConnectionHandlerTest, ServiceStarted_Video_Multiple) { TEST_F(ConnectionHandlerTest, SessionStarted_StartSession_SecureSpecific_Unprotect) { - EXPECT_CALL(mock_connection_handler_settings, heart_beat_timeout()) + EXPECT_CALL(mock_connection_handler_settings_, heart_beat_timeout()) .WillOnce(Return(heartbeat_timeout)); // Add virtual device and connection AddTestDeviceConnection(); @@ -2013,6 +2041,193 @@ TEST_F(ConnectionHandlerTest, OnDeviceConnectionSwitching) { connection_handler_->OnDeviceSwitchingStart(mac_address_, second_mac_address); } +TEST_F(ConnectionHandlerTest, StartStopSecondarySession) { + // Add virtual device and connection + AddTestDeviceConnection(); + // Start new session with RPC service + AddTestSession(); + + connection_handler_test::MockConnectionHandlerObserver + mock_connection_handler_observer; + connection_handler_->set_connection_handler_observer( + &mock_connection_handler_observer); + + // Start a session on a secondary transport + const transport_manager::DeviceInfo secondary_device_info( + device_handle_, mac_address_, device_name_, std::string("WIFI")); + const transport_manager::ConnectionUID secondary_uid = 2u; + // Add Device and connection + ON_CALL(mock_connection_handler_settings_, heart_beat_timeout()) + .WillByDefault(Return(1000u)); + connection_handler_->addDeviceConnection(secondary_device_info, + secondary_uid); + + EXPECT_CALL(mock_connection_handler_observer, + OnSecondaryTransportStartedCallback(device_handle_, _)).Times(1); + + connection_handler_->OnSecondaryTransportStarted( + uid_, secondary_uid, out_context_.new_session_id_); + + SessionTransports st = + connection_handler_->GetSessionTransports(out_context_.new_session_id_); + EXPECT_EQ(st.primary_transport, uid_); + EXPECT_EQ(st.secondary_transport, secondary_uid); + + AddSecondaryTestService(kAudio); + AddSecondaryTestService(kMobileNav); + + CheckServiceExists( + uid_, out_context_.new_session_id_, kAudio, secondary_uid, true); + CheckServiceExists( + uid_, out_context_.new_session_id_, kMobileNav, secondary_uid, true); + + connection_handler_->set_connection_handler_observer( + &mock_connection_handler_observer); + EXPECT_CALL(mock_connection_handler_observer, + OnSecondaryTransportEndedCallback(_)).Times(1); + EXPECT_CALL(mock_connection_handler_observer, + OnServiceEndedCallback(_, kAudio, _)).Times(1); + EXPECT_CALL(mock_connection_handler_observer, + OnServiceEndedCallback(_, kMobileNav, _)).Times(1); + + connection_handler_->OnSecondaryTransportEnded(uid_, secondary_uid); + + st = connection_handler_->GetSessionTransports(out_context_.new_session_id_); + EXPECT_EQ(st.primary_transport, uid_); + EXPECT_EQ(st.secondary_transport, 0u); + + CheckServiceExists( + uid_, out_context_.new_session_id_, kAudio, secondary_uid, false); + CheckServiceExists( + uid_, out_context_.new_session_id_, kMobileNav, secondary_uid, false); +} + +TEST_F(ConnectionHandlerTest, StopSecondarySession_NoService) { + AddTestDeviceConnection(); + AddTestSession(); + + connection_handler_test::MockConnectionHandlerObserver + mock_connection_handler_observer; + connection_handler_->set_connection_handler_observer( + &mock_connection_handler_observer); + + const transport_manager::DeviceInfo secondary_device_info( + device_handle_, mac_address_, device_name_, std::string("WIFI")); + const transport_manager::ConnectionUID secondary_uid = 123u; + ON_CALL(mock_connection_handler_settings_, heart_beat_timeout()) + .WillByDefault(Return(1000u)); + connection_handler_->addDeviceConnection(secondary_device_info, + secondary_uid); + + EXPECT_CALL(mock_connection_handler_observer, + OnSecondaryTransportStartedCallback(device_handle_, _)).Times(1); + connection_handler_->OnSecondaryTransportStarted( + uid_, secondary_uid, out_context_.new_session_id_); + + // check if OnSecondaryTransportEndedCallback is triggered with correct + // session ID even if we don't have any services + EXPECT_CALL(mock_connection_handler_observer, + OnSecondaryTransportEndedCallback(_)); + EXPECT_CALL(mock_connection_handler_observer, OnServiceEndedCallback(_, _, _)) + .Times(0); + + connection_handler_->OnSecondaryTransportEnded(uid_, secondary_uid); + + SessionTransports st = + connection_handler_->GetSessionTransports(out_context_.new_session_id_); + EXPECT_EQ(st.primary_transport, uid_); + EXPECT_EQ(st.secondary_transport, 0u); +} + +TEST_F(ConnectionHandlerTest, ConnectionType_valid) { + AddTestDeviceConnection(); + AddTestSession(); + + std::string ret = + connection_handler_->TransportTypeProfileStringFromConnHandle(uid_); + EXPECT_EQ(connection_type_, ret); +} + +TEST_F(ConnectionHandlerTest, ConnectionType_invalid) { + AddTestDeviceConnection(); + + transport_manager::ConnectionUID invalid_uid = 12345; + ASSERT_TRUE(invalid_uid != uid_); + std::string ret = + connection_handler_->TransportTypeProfileStringFromConnHandle( + invalid_uid); + EXPECT_EQ(std::string(), ret); +} + +TEST_F(ConnectionHandlerTest, SetSecondaryTransportID_UpdateSuccess) { + uint8_t session_id = 123; + transport_manager::ConnectionUID primary_uid = 100; + transport_manager::ConnectionUID secondary_uid = 0; + + SessionConnectionMap& session_connection_map = + connection_handler_->getSessionConnectionMap(); + // secondary transport's ID is 0 + SessionTransports st = {primary_uid, secondary_uid}; + session_connection_map[session_id] = st; + + secondary_uid = 200; + st = connection_handler_->SetSecondaryTransportID(session_id, secondary_uid); + EXPECT_EQ(primary_uid, st.primary_transport); + EXPECT_EQ(secondary_uid, st.secondary_transport); +} + +TEST_F(ConnectionHandlerTest, SetSecondaryTransportID_UpdateFailure) { + uint8_t session_id = 123; + transport_manager::ConnectionUID primary_uid = 100; + transport_manager::ConnectionUID secondary_uid = 300; + + SessionConnectionMap& session_connection_map = + connection_handler_->getSessionConnectionMap(); + // secondary transport's ID is already assigned + SessionTransports st = {primary_uid, secondary_uid}; + session_connection_map[session_id] = st; + + st = connection_handler_->SetSecondaryTransportID(session_id, 500); + EXPECT_EQ(primary_uid, st.primary_transport); + // secondary transport's ID is NOT updated + EXPECT_EQ(secondary_uid, st.secondary_transport); +} + +TEST_F(ConnectionHandlerTest, SetSecondaryTransportID_OverwirteSecondaryUID) { + uint8_t session_id = 123; + transport_manager::ConnectionUID primary_uid = 200; + transport_manager::ConnectionUID secondary_uid = 500; + + SessionConnectionMap& session_connection_map = + connection_handler_->getSessionConnectionMap(); + SessionTransports st = {primary_uid, secondary_uid}; + session_connection_map[session_id] = st; + + secondary_uid = kDisabledSecondary; + st = connection_handler_->SetSecondaryTransportID(session_id, secondary_uid); + EXPECT_EQ(primary_uid, st.primary_transport); + // secondary transport's ID is updated + EXPECT_EQ(secondary_uid, st.secondary_transport); +} + +TEST_F(ConnectionHandlerTest, SetSecondaryTransportID_Failure) { + uint8_t session_id = 123; + transport_manager::ConnectionUID primary_uid = 100; + transport_manager::ConnectionUID secondary_uid = 0; + + SessionConnectionMap& session_connection_map = + connection_handler_->getSessionConnectionMap(); + SessionTransports st = {primary_uid, secondary_uid}; + session_connection_map[session_id] = st; + + uint8_t invalid_session_id = 10; + secondary_uid = 300; + st = connection_handler_->SetSecondaryTransportID(invalid_session_id, + secondary_uid); + EXPECT_EQ(0u, st.primary_transport); + EXPECT_EQ(0u, st.secondary_transport); +} + } // namespace connection_handler_test } // namespace components } // namespace test diff --git a/src/components/connection_handler/test/connection_test.cc b/src/components/connection_handler/test/connection_test.cc index de21dd1e97..98b83f5fc4 100644 --- a/src/components/connection_handler/test/connection_test.cc +++ b/src/components/connection_handler/test/connection_test.cc @@ -36,6 +36,7 @@ #include "protocol/common.h" #include "connection_handler/connection.h" +#include "connection_handler/mock_connection_handler.h" #include "connection_handler/connection_handler_impl.h" #include "protocol/service_type.h" #include "connection_handler/mock_connection_handler_settings.h" @@ -56,6 +57,8 @@ namespace connection_handler_test { using namespace ::connection_handler; using namespace ::protocol_handler; +using ::testing::Return; + class ConnectionTest : public ::testing::Test { protected: void SetUp() OVERRIDE { @@ -78,7 +81,7 @@ class ConnectionTest : public ::testing::Test { session_id, protocol_handler::PROTOCOL_VERSION_3); } void StartDefaultSession() { - session_id = connection_->AddNewSession(); + session_id = connection_->AddNewSession(kDefaultConnectionHandle); EXPECT_NE(session_id, 0u); const SessionMap sessionMap = connection_->session_map(); EXPECT_FALSE(sessionMap.empty()); @@ -88,13 +91,14 @@ class ConnectionTest : public ::testing::Test { std::find(serviceList.begin(), serviceList.end(), kRpc); const bool found_result = (it != serviceList.end()); EXPECT_TRUE(found_result); + EXPECT_EQ(connection_->primary_connection_handle(), 0); } void AddNewService(const ServiceType service_type, const bool protection, const bool expect_add_new_service_call_result, const bool expect_exist_service) { - const bool result = - connection_->AddNewService(session_id, service_type, protection); + const bool result = connection_->AddNewService( + session_id, service_type, protection, kDefaultConnectionHandle); EXPECT_EQ(result, expect_add_new_service_call_result); #ifdef ENABLE_SECURITY @@ -110,12 +114,35 @@ class ConnectionTest : public ::testing::Test { std::find(newServiceList.begin(), newServiceList.end(), service_type); const bool found_result = it != newServiceList.end(); EXPECT_EQ(expect_exist_service, found_result); -#ifdef ENABLE_SECURITY if (found_result) { const Service& service = *it; + transport_manager::ConnectionUID expected_connection_handle = + kDefaultConnectionHandle; + EXPECT_EQ(service.connection_id, expected_connection_handle); +#ifdef ENABLE_SECURITY EXPECT_EQ(service.is_protected_, protection); - } #endif // ENABLE_SECURITY + } + } + void AddNewSecondaryService(const ServiceType service_type) { + const bool result = connection_->AddNewService( + session_id, service_type, false, kSecondaryConnectionHandle); + EXPECT_EQ(result, true); + + const SessionMap session_map = connection_->session_map(); + EXPECT_FALSE(session_map.empty()); + const ServiceList newServiceList = session_map.begin()->second.service_list; + EXPECT_FALSE(newServiceList.empty()); + const ServiceList::const_iterator it = + std::find(newServiceList.begin(), newServiceList.end(), service_type); + const bool found_result = it != newServiceList.end(); + EXPECT_TRUE(found_result); + if (found_result) { + const Service& service = *it; + transport_manager::ConnectionUID expected_secondary_connection_handle = + kSecondaryConnectionHandle; + EXPECT_EQ(service.connection_id, expected_secondary_connection_handle); + } } void RemoveService(const ServiceType service_type, @@ -141,6 +168,8 @@ class ConnectionTest : public ::testing::Test { transport_manager_mock; ConnectionHandlerImpl* connection_handler_; uint32_t session_id; + static const transport_manager::ConnectionUID kDefaultConnectionHandle = 1; + static const transport_manager::ConnectionUID kSecondaryConnectionHandle = 2; }; TEST_F(ConnectionTest, Session_TryGetProtocolVersionWithoutSession) { @@ -236,13 +265,17 @@ TEST_F(ConnectionTest, HeartBeat_Protocol5_ZeroHeartBeat_NotSupported) { // Try to add service without session TEST_F(ConnectionTest, Session_AddNewServiceWithoutSession) { - EXPECT_EQ(connection_->AddNewService(session_id, kAudio, true), + EXPECT_EQ(connection_->AddNewService( + session_id, kAudio, true, kDefaultConnectionHandle), EXPECT_RETURN_FALSE); - EXPECT_EQ(connection_->AddNewService(session_id, kAudio, false), + EXPECT_EQ(connection_->AddNewService( + session_id, kAudio, false, kDefaultConnectionHandle), EXPECT_RETURN_FALSE); - EXPECT_EQ(connection_->AddNewService(session_id, kMobileNav, true), + EXPECT_EQ(connection_->AddNewService( + session_id, kMobileNav, true, kDefaultConnectionHandle), EXPECT_RETURN_FALSE); - EXPECT_EQ(connection_->AddNewService(session_id, kMobileNav, false), + EXPECT_EQ(connection_->AddNewService( + session_id, kMobileNav, false, kDefaultConnectionHandle), EXPECT_RETURN_FALSE); } @@ -409,6 +442,133 @@ TEST_F(ConnectionTest, RemoveSession) { EXPECT_EQ(0u, connection_->RemoveSession(session_id)); } +TEST_F(ConnectionTest, AddNewSession_VerifyAddSessionCalled) { + MockConnectionHandler mock_connection_handler; + + ConnectionHandle connection_handle = 123; + DeviceHandle device_handle = 0u; + uint32_t heart_beat = 10000u; + Connection* connection = new Connection( + connection_handle, device_handle, &mock_connection_handler, heart_beat); + + transport_manager::ConnectionUID connection_handle_uid = 1; + uint32_t mock_session_id = 2; + EXPECT_CALL(mock_connection_handler, AddSession(connection_handle_uid)) + .WillOnce(Return(mock_session_id)); + + uint32_t sid = connection->AddNewSession(connection_handle_uid); + EXPECT_EQ(mock_session_id, sid); + + EXPECT_CALL(mock_connection_handler, RemoveSession(mock_session_id)) + .WillOnce(Return(true)); // invoked by destructor of connection + delete connection; +} + +TEST_F(ConnectionTest, RemoveSession_VerifyRemoveSessionCalled) { + MockConnectionHandler mock_connection_handler; + + ConnectionHandle connection_handle = 123; + DeviceHandle device_handle = 0u; + uint32_t heart_beat = 10000u; + Connection* connection = new Connection( + connection_handle, device_handle, &mock_connection_handler, heart_beat); + + transport_manager::ConnectionUID connection_handle_uid = 1; + uint32_t mock_session_id = 10; + EXPECT_CALL(mock_connection_handler, AddSession(connection_handle_uid)) + .WillOnce(Return(mock_session_id)); + EXPECT_CALL(mock_connection_handler, + RemoveSession(static_cast<uint8_t>(mock_session_id))) + .WillOnce(Return(true)); + + uint32_t sid = connection->AddNewSession(connection_handle_uid); + + uint32_t ret = connection->RemoveSession(sid); + EXPECT_EQ(sid, ret); + + delete connection; +} + +TEST_F(ConnectionTest, SecondarySessionTest) { + StartSession(); + AddNewService( + kRpc, PROTECTION_OFF, EXPECT_RETURN_FALSE, EXPECT_SERVICE_EXISTS); + + const ConnectionHandle connectionHandle = 0; + const DeviceHandle device_handle = 0u; + const uint32_t heart_beat = 0u; + Connection* secondary_connection = new Connection( + connectionHandle, device_handle, connection_handler_, heart_beat); + + secondary_connection->SetPrimaryConnectionHandle(kDefaultConnectionHandle); + connection_handler::ConnectionHandle expected_primary_connection_handle = + kDefaultConnectionHandle; + EXPECT_EQ(secondary_connection->primary_connection_handle(), + expected_primary_connection_handle); + + AddNewSecondaryService(kAudio); + AddNewSecondaryService(kMobileNav); + + delete secondary_connection; +} + +TEST_F(ConnectionTest, RemoveSecondaryServices_SUCCESS) { + StartSession(); + + ServiceType services[2] = {kMobileNav, kAudio}; + AddNewSecondaryService(services[0]); + AddNewSecondaryService(services[1]); + size_t services_count = sizeof(services) / sizeof(services[0]); + + std::list<ServiceType> removed_services; + uint8_t ret_session_id = connection_->RemoveSecondaryServices( + kSecondaryConnectionHandle, removed_services); + + // check return value + EXPECT_EQ(session_id, ret_session_id); + // check returned list + EXPECT_EQ(services_count, removed_services.size()); + std::list<protocol_handler::ServiceType>::iterator it; + it = std::find(removed_services.begin(), removed_services.end(), services[0]); + EXPECT_TRUE(it != removed_services.end()); + it = std::find(removed_services.begin(), removed_services.end(), services[1]); + EXPECT_TRUE(it != removed_services.end()); +} + +TEST_F(ConnectionTest, RemoveSecondaryServices_NoService) { + StartSession(); + /* do not call AddNewSecondaryService() */ + + std::list<ServiceType> removed_services; + uint8_t ret_session_id = connection_->RemoveSecondaryServices( + kSecondaryConnectionHandle, removed_services); + + // check return value + EXPECT_EQ(0, ret_session_id); + // check returned list + EXPECT_EQ(0u, removed_services.size()); +} + +TEST_F(ConnectionTest, RemoveSecondaryServices_InvalidConnectionHandle) { + StartSession(); + + ServiceType services[2] = {kMobileNav, kAudio}; + AddNewSecondaryService(services[0]); + AddNewSecondaryService(services[1]); + + transport_manager::ConnectionUID invalid_connection_handle = 123; + ASSERT_TRUE(kSecondaryConnectionHandle != invalid_connection_handle); + + std::list<ServiceType> removed_services; + uint8_t ret_session_id = connection_->RemoveSecondaryServices( + invalid_connection_handle, removed_services); + + // check return value + EXPECT_EQ(0, ret_session_id); + // check returned list + EXPECT_EQ(0u, removed_services.size()); +} + #ifdef ENABLE_SECURITY TEST_F(ConnectionTest, SetSSLContextWithoutSession) { diff --git a/src/components/connection_handler/test/heart_beat_monitor_test.cc b/src/components/connection_handler/test/heart_beat_monitor_test.cc index 4c67c97191..ccfff34116 100644 --- a/src/components/connection_handler/test/heart_beat_monitor_test.cc +++ b/src/components/connection_handler/test/heart_beat_monitor_test.cc @@ -43,6 +43,7 @@ namespace { const int32_t MILLISECONDS_IN_SECOND = 1000; const int32_t MICROSECONDS_IN_MILLISECONDS = 1000; const int32_t MICROSECONDS_IN_SECOND = 1000 * 1000; +const uint32_t kTime_offset = 20u; } namespace test { @@ -51,27 +52,34 @@ namespace connection_handler_test { using ::testing::DoAll; using ::testing::_; +using ::testing::Return; class HeartBeatMonitorTest : public testing::Test { public: - HeartBeatMonitorTest() : conn(NULL) { - kTimeout = 5000u; - } + HeartBeatMonitorTest() + : connection_(NULL) + , timeout_(100u) + , time_allowance_multiplier_(1.5) + , expiration_timeout_(timeout_ * 2u) {} protected: - testing::NiceMock<MockConnectionHandler> connection_handler_mock; - connection_handler::Connection* conn; - uint32_t kTimeout; - static const connection_handler::ConnectionHandle kConnectionHandle = + testing::NiceMock<MockConnectionHandler> connection_handler_mock_; + connection_handler::Connection* connection_; + const uint32_t timeout_; + const float_t time_allowance_multiplier_; + const uint32_t expiration_timeout_; + static const connection_handler::ConnectionHandle connection_handle_ = 0xABCDEF; + static const transport_manager::ConnectionUID kDefaultConnectionHandle = 1; + static const uint32_t kDefaultSessionId = 1; - virtual void SetUp() { - conn = new connection_handler::Connection( - kConnectionHandle, 0, &connection_handler_mock, kTimeout); + void SetUp() OVERRIDE { + connection_ = new connection_handler::Connection( + connection_handle_, 0, &connection_handler_mock_, timeout_); } void TearDown() OVERRIDE { - delete conn; + delete connection_; } }; @@ -80,159 +88,191 @@ ACTION_P2(RemoveSession, conn, session_id) { } TEST_F(HeartBeatMonitorTest, TimerNotStarted) { + EXPECT_CALL(connection_handler_mock_, AddSession(_)) + .WillOnce(Return(kDefaultSessionId)); + EXPECT_CALL(connection_handler_mock_, RemoveSession(kDefaultSessionId)) + .WillOnce(Return(true)); // called by destructor of Connection + // Whithout StartHeartBeat nothing to be call - EXPECT_CALL(connection_handler_mock, CloseSession(_, _)).Times(0); - EXPECT_CALL(connection_handler_mock, CloseConnection(_)).Times(0); - EXPECT_CALL(connection_handler_mock, SendHeartBeat(_, _)).Times(0); + EXPECT_CALL(connection_handler_mock_, CloseSession(_, _)).Times(0); + EXPECT_CALL(connection_handler_mock_, CloseConnection(_)).Times(0); + EXPECT_CALL(connection_handler_mock_, SendHeartBeat(_, _)).Times(0); - conn->AddNewSession(); + connection_->AddNewSession(kDefaultConnectionHandle); } TEST_F(HeartBeatMonitorTest, TimerNotElapsed) { - EXPECT_CALL(connection_handler_mock, SendHeartBeat(_, _)).Times(0); - EXPECT_CALL(connection_handler_mock, CloseSession(_, _)).Times(0); - EXPECT_CALL(connection_handler_mock, CloseConnection(_)).Times(0); + EXPECT_CALL(connection_handler_mock_, AddSession(_)) + .WillOnce(Return(kDefaultSessionId)); + EXPECT_CALL(connection_handler_mock_, RemoveSession(kDefaultSessionId)) + .WillOnce(Return(true)); + + EXPECT_CALL(connection_handler_mock_, SendHeartBeat(_, _)).Times(0); + EXPECT_CALL(connection_handler_mock_, CloseSession(_, _)).Times(0); + EXPECT_CALL(connection_handler_mock_, CloseConnection(_)).Times(0); - const uint32_t session = conn->AddNewSession(); - conn->StartHeartBeat(session); + const uint32_t session = connection_->AddNewSession(kDefaultConnectionHandle); + connection_->StartHeartBeat(session); } TEST_F(HeartBeatMonitorTest, TimerElapsed) { - const uint32_t session = conn->AddNewSession(); + EXPECT_CALL(connection_handler_mock_, AddSession(_)) + .WillOnce(Return(kDefaultSessionId)); + EXPECT_CALL(connection_handler_mock_, RemoveSession(kDefaultSessionId)) + .WillOnce(Return(true)); // invoked by RemoveSession action + + const uint32_t session = connection_->AddNewSession(kDefaultConnectionHandle); TestAsyncWaiter waiter; uint32_t times = 0; - EXPECT_CALL(connection_handler_mock, CloseSession(_, session, _)) - .WillOnce( - DoAll(NotifyTestAsyncWaiter(&waiter), RemoveSession(conn, session))); - times++; - EXPECT_CALL(connection_handler_mock, CloseConnection(_)) - .WillOnce(NotifyTestAsyncWaiter(&waiter)); + EXPECT_CALL(connection_handler_mock_, CloseSession(_, session, _)) + .WillOnce(DoAll(NotifyTestAsyncWaiter(&waiter), + RemoveSession(connection_, session))); times++; - EXPECT_CALL(connection_handler_mock, SendHeartBeat(_, session)) + EXPECT_CALL(connection_handler_mock_, SendHeartBeat(_, session)) .WillOnce(NotifyTestAsyncWaiter(&waiter)); times++; - conn->StartHeartBeat(session); + connection_->StartHeartBeat(session); EXPECT_TRUE(waiter.WaitFor( times, - 2 * kTimeout * MICROSECONDS_IN_MILLISECONDS + MICROSECONDS_IN_SECOND)); + 2 * timeout_ * MICROSECONDS_IN_MILLISECONDS + MICROSECONDS_IN_SECOND)); } TEST_F(HeartBeatMonitorTest, KeptAlive) { - EXPECT_CALL(connection_handler_mock, CloseSession(_, _)).Times(0); - EXPECT_CALL(connection_handler_mock, CloseConnection(_)).Times(0); - EXPECT_CALL(connection_handler_mock, SendHeartBeat(_, _)).Times(0); - - const uint32_t session = conn->AddNewSession(); - conn->StartHeartBeat(session); - usleep(kTimeout * MICROSECONDS_IN_MILLISECONDS - MICROSECONDS_IN_SECOND); - conn->KeepAlive(session); - usleep(kTimeout * MICROSECONDS_IN_MILLISECONDS - MICROSECONDS_IN_SECOND); - conn->KeepAlive(session); - usleep(kTimeout * MICROSECONDS_IN_MILLISECONDS - MICROSECONDS_IN_SECOND); - conn->KeepAlive(session); - usleep(kTimeout * MICROSECONDS_IN_MILLISECONDS - MICROSECONDS_IN_SECOND); + EXPECT_CALL(connection_handler_mock_, AddSession(_)) + .WillOnce(Return(kDefaultSessionId)); + EXPECT_CALL(connection_handler_mock_, RemoveSession(kDefaultSessionId)) + .WillOnce(Return(true)); + + EXPECT_CALL(connection_handler_mock_, CloseSession(_, _)).Times(0); + EXPECT_CALL(connection_handler_mock_, CloseConnection(_)).Times(0); + EXPECT_CALL(connection_handler_mock_, SendHeartBeat(_, _)).Times(0); + + const uint32_t session = connection_->AddNewSession(kDefaultConnectionHandle); + connection_->StartHeartBeat(session); + usleep(timeout_); + connection_->KeepAlive(session); + usleep(timeout_); + connection_->KeepAlive(session); } TEST_F(HeartBeatMonitorTest, NotKeptAlive) { - const uint32_t session = conn->AddNewSession(); + EXPECT_CALL(connection_handler_mock_, AddSession(_)) + .WillOnce(Return(kDefaultSessionId)); + EXPECT_CALL(connection_handler_mock_, RemoveSession(kDefaultSessionId)) + .WillOnce(Return(true)); + + const uint32_t session = connection_->AddNewSession(kDefaultConnectionHandle); TestAsyncWaiter waiter; uint32_t times = 0; - EXPECT_CALL(connection_handler_mock, SendHeartBeat(_, session)) + EXPECT_CALL(connection_handler_mock_, SendHeartBeat(_, session)) .WillOnce(NotifyTestAsyncWaiter(&waiter)); times++; - EXPECT_CALL(connection_handler_mock, CloseSession(_, session, _)) - .WillOnce( - DoAll(NotifyTestAsyncWaiter(&waiter), RemoveSession(conn, session))); - times++; - EXPECT_CALL(connection_handler_mock, CloseConnection(_)) - .WillOnce(NotifyTestAsyncWaiter(&waiter)); + EXPECT_CALL(connection_handler_mock_, CloseSession(_, session, _)) + .WillOnce(DoAll(NotifyTestAsyncWaiter(&waiter), + RemoveSession(connection_, session))); times++; - conn->StartHeartBeat(session); - usleep(kTimeout * MICROSECONDS_IN_MILLISECONDS - MICROSECONDS_IN_SECOND); - conn->KeepAlive(session); - usleep(kTimeout * MICROSECONDS_IN_MILLISECONDS - MICROSECONDS_IN_SECOND); - conn->KeepAlive(session); - usleep(kTimeout * MICROSECONDS_IN_MILLISECONDS - MICROSECONDS_IN_SECOND); - conn->KeepAlive(session); - usleep(2 * kTimeout * MICROSECONDS_IN_MILLISECONDS + MICROSECONDS_IN_SECOND); + connection_->StartHeartBeat(session); + usleep(timeout_); + connection_->KeepAlive(session); EXPECT_TRUE(waiter.WaitFor( times, - 2 * kTimeout * MICROSECONDS_IN_MILLISECONDS + MICROSECONDS_IN_SECOND)); + 2 * timeout_ * MICROSECONDS_IN_MILLISECONDS + MICROSECONDS_IN_SECOND)); } TEST_F(HeartBeatMonitorTest, TwoSessionsElapsed) { - const uint32_t kSession1 = conn->AddNewSession(); - const uint32_t kSession2 = conn->AddNewSession(); + const uint32_t kMockSessionId1 = 1; + const uint32_t kMockSessionId2 = 2; + EXPECT_CALL(connection_handler_mock_, AddSession(_)) + .WillOnce(Return(kMockSessionId1)) + .WillOnce(Return(kMockSessionId2)); + EXPECT_CALL(connection_handler_mock_, RemoveSession(kMockSessionId1)) + .WillOnce(Return(true)); + EXPECT_CALL(connection_handler_mock_, RemoveSession(kMockSessionId2)) + .WillOnce(Return(true)); + + const uint32_t kSession1 = + connection_->AddNewSession(kDefaultConnectionHandle); + + const transport_manager::ConnectionUID kAnotherConnectionHandle = 2; + const uint32_t kSession2 = + connection_->AddNewSession(kAnotherConnectionHandle); TestAsyncWaiter waiter; uint32_t times = 0; - EXPECT_CALL(connection_handler_mock, CloseSession(_, kSession1, _)) + EXPECT_CALL(connection_handler_mock_, CloseSession(_, kSession1, _)) .WillOnce(DoAll(NotifyTestAsyncWaiter(&waiter), - RemoveSession(conn, kSession1))); + RemoveSession(connection_, kSession1))); times++; - EXPECT_CALL(connection_handler_mock, CloseSession(_, kSession2, _)) + EXPECT_CALL(connection_handler_mock_, CloseSession(_, kSession2, _)) .WillOnce(DoAll(NotifyTestAsyncWaiter(&waiter), - RemoveSession(conn, kSession2))); - times++; - EXPECT_CALL(connection_handler_mock, CloseConnection(_)) - .WillOnce(NotifyTestAsyncWaiter(&waiter)); + RemoveSession(connection_, kSession2))); times++; - EXPECT_CALL(connection_handler_mock, SendHeartBeat(_, kSession1)) + + EXPECT_CALL(connection_handler_mock_, SendHeartBeat(_, kSession1)) .WillOnce(NotifyTestAsyncWaiter(&waiter)); times++; - EXPECT_CALL(connection_handler_mock, SendHeartBeat(_, kSession2)) + EXPECT_CALL(connection_handler_mock_, SendHeartBeat(_, kSession2)) .WillOnce(NotifyTestAsyncWaiter(&waiter)); times++; - conn->StartHeartBeat(kSession1); - conn->StartHeartBeat(kSession2); + connection_->StartHeartBeat(kSession1); + connection_->StartHeartBeat(kSession2); EXPECT_TRUE(waiter.WaitFor( times, - 2 * kTimeout * MICROSECONDS_IN_MILLISECONDS + MICROSECONDS_IN_SECOND)); + 2 * timeout_ * MICROSECONDS_IN_MILLISECONDS + MICROSECONDS_IN_SECOND)); } TEST_F(HeartBeatMonitorTest, IncreaseHeartBeatTimeout) { - const uint32_t kSession = conn->AddNewSession(); + EXPECT_CALL(connection_handler_mock_, AddSession(_)) + .WillOnce(Return(kDefaultSessionId)); + EXPECT_CALL(connection_handler_mock_, RemoveSession(kDefaultSessionId)) + .WillOnce(Return(true)); + + const uint32_t kSession = + connection_->AddNewSession(kDefaultConnectionHandle); - EXPECT_CALL(connection_handler_mock, CloseSession(_, _)).Times(0); - EXPECT_CALL(connection_handler_mock, CloseConnection(_)).Times(0); - EXPECT_CALL(connection_handler_mock, SendHeartBeat(_, _)).Times(0); + EXPECT_CALL(connection_handler_mock_, CloseSession(_, _)).Times(0); + EXPECT_CALL(connection_handler_mock_, CloseConnection(_)).Times(0); + EXPECT_CALL(connection_handler_mock_, SendHeartBeat(_, _)).Times(0); - const uint32_t kNewTimeout = kTimeout + MICROSECONDS_IN_MILLISECONDS; - conn->StartHeartBeat(kSession); - conn->SetHeartBeatTimeout(kNewTimeout, kSession); + const uint32_t kNewTimeout = timeout_ + MICROSECONDS_IN_MILLISECONDS; + connection_->StartHeartBeat(kSession); + connection_->SetHeartBeatTimeout(kNewTimeout, kSession); } TEST_F(HeartBeatMonitorTest, DecreaseHeartBeatTimeout) { - const uint32_t kSession = conn->AddNewSession(); + EXPECT_CALL(connection_handler_mock_, AddSession(_)) + .WillOnce(Return(kDefaultSessionId)); + EXPECT_CALL(connection_handler_mock_, RemoveSession(kDefaultSessionId)) + .WillOnce(Return(true)); + + const uint32_t kSession = + connection_->AddNewSession(kDefaultConnectionHandle); TestAsyncWaiter waiter; uint32_t times = 0; - EXPECT_CALL(connection_handler_mock, CloseSession(_, kSession, _)) - .WillOnce( - DoAll(NotifyTestAsyncWaiter(&waiter), RemoveSession(conn, kSession))); - times++; - EXPECT_CALL(connection_handler_mock, CloseConnection(_)) - .WillOnce(NotifyTestAsyncWaiter(&waiter)); + EXPECT_CALL(connection_handler_mock_, CloseSession(_, kSession, _)) + .WillOnce(DoAll(NotifyTestAsyncWaiter(&waiter), + RemoveSession(connection_, kSession))); times++; - EXPECT_CALL(connection_handler_mock, SendHeartBeat(_, kSession)) + EXPECT_CALL(connection_handler_mock_, SendHeartBeat(_, kSession)) .WillOnce(NotifyTestAsyncWaiter(&waiter)); times++; - const uint32_t kNewTimeout = kTimeout - MICROSECONDS_IN_MILLISECONDS; - conn->StartHeartBeat(kSession); - conn->SetHeartBeatTimeout(kNewTimeout, kSession); + const uint32_t new_timeout = timeout_ - kTime_offset; + connection_->StartHeartBeat(kSession); + connection_->SetHeartBeatTimeout(new_timeout, kSession); EXPECT_TRUE(waiter.WaitFor( times, - 2 * kTimeout * MICROSECONDS_IN_MILLISECONDS + MICROSECONDS_IN_SECOND)); + 2 * timeout_ * MICROSECONDS_IN_MILLISECONDS + MICROSECONDS_IN_SECOND)); } } // namespace connection_handler_test |