diff options
author | JackLivio <jack@livio.io> | 2018-09-04 11:27:42 -0400 |
---|---|---|
committer | JackLivio <jack@livio.io> | 2018-09-04 11:27:42 -0400 |
commit | 548867072facaeb1d696b4534c8e64f9f9ea13d5 (patch) | |
tree | c275015ac588514a5140145bad24d1688d6c6af7 /src/components/connection_handler/src | |
parent | 6bb3a0e5d9938ddfbf36994fe9937d6fafb047b7 (diff) | |
parent | ddc79ce414adcd966bdab61b814ce8a422e780f8 (diff) | |
download | sdl_core-548867072facaeb1d696b4534c8e64f9f9ea13d5.tar.gz |
Merge remote-tracking branch 'origin/develop' into fix/Wrong_connection_closure
# Conflicts:
# src/components/connection_handler/test/heart_beat_monitor_test.cc
Diffstat (limited to 'src/components/connection_handler/src')
3 files changed, 578 insertions, 140 deletions
diff --git a/src/components/connection_handler/src/connection.cc b/src/components/connection_handler/src/connection.cc index 6bed3e96bc..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) { @@ -398,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..045985f650 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; + if (session_id != 0) { + SessionTransports st = GetSessionTransports(session_id); + 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 @@ -464,15 +428,7 @@ void ConnectionHandlerImpl::OnSessionStartedCallback( 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 +465,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 +503,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 +542,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 +572,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 +612,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 +656,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 +836,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 +906,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 +919,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 +1428,28 @@ 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)); + + protocol_handler_->SendEndService(st.primary_transport, + st.secondary_transport, + session_id, + service_type); + } } } @@ -1179,12 +1510,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 +1536,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 +1583,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 6172f63ca8..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() { @@ -166,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; } @@ -196,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 |