diff options
author | Conlain Kelly <conlain.k@gmail.com> | 2018-07-18 13:11:19 -0400 |
---|---|---|
committer | Conlain Kelly <conlain.k@gmail.com> | 2018-07-18 13:11:25 -0400 |
commit | e380ed1779e4317437cea63f070a8345b41f2d33 (patch) | |
tree | b2d6669663b91a3f235c2ce4a00c0c888751ff20 /src/components/protocol_handler | |
parent | 09c941dd2ffd4f98ba706895a67133318c3c5007 (diff) | |
parent | 4f21cbafb247664bd7b89bf2d39944764b1763b1 (diff) | |
download | sdl_core-e380ed1779e4317437cea63f070a8345b41f2d33.tar.gz |
Merge branch 'develop' into feature/boost_lock_implementation
Diffstat (limited to 'src/components/protocol_handler')
11 files changed, 2118 insertions, 161 deletions
diff --git a/src/components/protocol_handler/CMakeLists.txt b/src/components/protocol_handler/CMakeLists.txt index ed3aaaf24c..d57211040a 100644 --- a/src/components/protocol_handler/CMakeLists.txt +++ b/src/components/protocol_handler/CMakeLists.txt @@ -65,7 +65,7 @@ endforeach() add_library(ProtocolHandler ${SOURCES}) -add_dependencies(ProtocolHandler libbson) +#add_dependencies(ProtocolHandler libbson) target_link_libraries(ProtocolHandler ${LIBRARIES}) if(BUILD_TESTS) diff --git a/src/components/protocol_handler/include/protocol_handler/handshake_handler.h b/src/components/protocol_handler/include/protocol_handler/handshake_handler.h index 0ef40290f2..8b7f28d50e 100644 --- a/src/components/protocol_handler/include/protocol_handler/handshake_handler.h +++ b/src/components/protocol_handler/include/protocol_handler/handshake_handler.h @@ -61,14 +61,14 @@ class HandshakeHandler : public security_manager::SecurityManagerListener { const std::vector<int>& force_protected_service, const bool is_new_service, ProtocolPacket::ProtocolVersion& full_version, - std::shared_ptr<uint8_t> payload); + std::shared_ptr<BsonObject> payload); HandshakeHandler(ProtocolHandlerImpl& protocol_handler, SessionObserver& session_observer, ProtocolPacket::ProtocolVersion& full_version, const SessionContext& context, const uint8_t protocol_version, - std::shared_ptr<uint8_t> payload); + std::shared_ptr<BsonObject> payload); ~HandshakeHandler(); @@ -90,6 +90,12 @@ class HandshakeHandler : public security_manager::SecurityManagerListener { security_manager::SSLContext::HandshakeResult result) OVERRIDE; /** + * @brief Notification about handshake failure + * @return true on success notification handling or false otherwise + */ + bool OnHandshakeFailed() OVERRIDE; + + /** * @brief Notification that certificate update is required. */ void OnCertificateUpdateRequired() OVERRIDE; @@ -120,7 +126,7 @@ class HandshakeHandler : public security_manager::SecurityManagerListener { SessionContext context_; ProtocolPacket::ProtocolVersion full_version_; const uint8_t protocol_version_; - std::shared_ptr<uint8_t> payload_; + std::shared_ptr<BsonObject> payload_; }; } // namespace protocol_handler diff --git a/src/components/protocol_handler/include/protocol_handler/protocol_handler_impl.h b/src/components/protocol_handler/include/protocol_handler/protocol_handler_impl.h index 0efb81cdd7..fb685f33d3 100644 --- a/src/components/protocol_handler/include/protocol_handler/protocol_handler_impl.h +++ b/src/components/protocol_handler/include/protocol_handler/protocol_handler_impl.h @@ -44,6 +44,7 @@ #include "utils/threads/message_loop_thread.h" #include "utils/shared_ptr.h" #include "utils/messagemeter.h" +#include "utils/custom_string.h" #include "protocol_handler/protocol_handler.h" #include "protocol_handler/protocol_packet.h" @@ -55,6 +56,7 @@ #include "transport_manager/common.h" #include "transport_manager/transport_manager.h" #include "transport_manager/transport_manager_listener_empty.h" +#include "transport_manager/transport_adapter/transport_adapter.h" #include "connection_handler/connection_handler.h" #include "application_manager/policies/policy_handler_observer.h" @@ -131,6 +133,30 @@ typedef threads::MessageLoopThread< utils::PrioritizedQueue<RawFordMessageFromMobile> > FromMobileQueue; typedef threads::MessageLoopThread< utils::PrioritizedQueue<RawFordMessageToMobile> > ToMobileQueue; + +// Type to allow easy mapping between a device type and transport +// characteristics +typedef enum { + TT_NONE = -1, + TT_USB = 0, + TT_BLUETOOTH = 1, + TT_WIFI = 2 +} TransportType; + +struct TransportDescription { + TransportDescription(const TransportType transport_type, + const bool ios_transport, + const bool android_transport) + : transport_type_(transport_type) + , ios_transport_(ios_transport) + , android_transport_(android_transport) {} + + TransportType transport_type_; + bool ios_transport_; + bool android_transport_; +}; + +typedef std::map<std::string, TransportDescription> TransportTypes; } // namespace impl /** @@ -231,10 +257,20 @@ class ProtocolHandlerImpl */ void SendEndSession(int32_t connection_id, uint8_t session_id); - void SendEndService(int32_t connection_id, + /** + * \brief Sends ending session to mobile application + * \param primary_connection_id Identifier of connection within which + * service exists + * \param connection_id Identifier of the actual transport for the service + * \param session_id ID of session to be ended + */ + void SendEndService(int32_t primary_connection_id, + int32_t connection_id, uint8_t session_id, uint8_t service_type); + void NotifyOnFailedHandshake() OVERRIDE; + // TODO(Ezamakhov): move Ack/Nack as interface for StartSessionHandler /** * \brief Sends acknowledgement of starting session to mobile application @@ -420,10 +456,19 @@ class ProtocolHandlerImpl const impl::ToMobileQueue& get_to_mobile_queue() const { return raw_ford_messages_to_mobile_; } + + void set_tcp_config(bool tcp_enabled, + std::string tcp_address, + std::string tcp_port) { + tcp_enabled_ = tcp_enabled; + tcp_ip_address_ = tcp_address; + tcp_port_ = tcp_port; + } #endif private: - void SendEndServicePrivate(int32_t connection_id, + void SendEndServicePrivate(int32_t primary_connection_id, + int32_t connection_id, uint8_t session_id, uint8_t service_type); @@ -434,6 +479,28 @@ class ProtocolHandlerImpl uint8_t session_id, uint32_t message_id); + /* + * Prepare and send TransportUpdateEvent message + */ + void SendTransportUpdateEvent(ConnectionID connection_id, uint8_t session_id); + + /* + * Prepare and send RegisterSecondaryTransportAck message + */ + RESULT_CODE SendRegisterSecondaryTransportAck( + ConnectionID connection_id, + ConnectionID primary_transport_connection_id, + uint8_t session_id); + + /* + * Prepare and send RegisterSecondaryTransportNAck message + */ + RESULT_CODE SendRegisterSecondaryTransportNAck( + ConnectionID connection_id, + ConnectionID primary_transport_connection_id, + uint8_t session_id, + BsonObject* reason = NULL); + /** * @brief Notifies about receiving message from TM. * @@ -472,13 +539,18 @@ class ProtocolHandlerImpl void OnConnectionClosed( const transport_manager::ConnectionUID connection_id) OVERRIDE; + void OnUnexpectedDisconnect( + const transport_manager::ConnectionUID connection_id, + const transport_manager::CommunicationError& error) OVERRIDE; + /** - * @brief OnPTUFinished the callback which signals PTU has finished + * @brief Notifies that configuration of a transport has been updated. * - * @param ptu_result the result from the PTU - true if successful, - * otherwise false. + * @param configs pairs of key and value that represent configuration. */ - void OnPTUFinished(const bool ptu_result) OVERRIDE; + void OnTransportConfigUpdated( + const transport_manager::transport_adapter::TransportConfig& configs) + OVERRIDE; /** * @brief Notifies subscribers about message @@ -581,6 +653,9 @@ class ProtocolHandlerImpl RESULT_CODE HandleControlMessageStartSession(const ProtocolFramePtr packet); + RESULT_CODE HandleControlMessageRegisterSecondaryTransport( + const ProtocolFramePtr packet); + RESULT_CODE HandleControlMessageHeartBeat(const ProtocolPacket& packet); void PopValideAndExpirateMultiframes(); @@ -609,6 +684,32 @@ class ProtocolHandlerImpl */ uint8_t SupportedSDLProtocolVersion() const; + const impl::TransportDescription GetTransportTypeFromConnectionType( + const std::string& device_type) const; + + const bool ParseSecondaryTransportConfiguration( + const ConnectionID connection_id, + std::vector<std::string>& secondaryTransports, + std::vector<int32_t>& audioServiceTransports, + std::vector<int32_t>& videoServiceTransports) const; + + void GenerateSecondaryTransportsForStartSessionAck( + const std::vector<std::string>& secondary_transport_types, + bool device_is_ios, + bool device_is_android, + std::vector<std::string>& secondaryTransports) const; + + void GenerateServiceTransportsForStartSessionAck( + bool secondary_enabled, + const std::vector<std::string>& service_transports, + const std::string& primary_connection_type, + const impl::TransportType primary_transport_type, + const std::vector<std::string>& secondary_transport_types, + std::vector<int32_t>& serviceTransports) const; + + const std::string TransportTypeFromTransport( + const utils::custom_string::CustomString& transport) const; + const ProtocolHandlerSettings& settings_; /** @@ -683,10 +784,6 @@ class ProtocolHandlerImpl #ifdef ENABLE_SECURITY security_manager::SecurityManager* security_manager_; - - bool is_ptu_triggered_; - std::list<std::shared_ptr<HandshakeHandler> > ptu_pending_handlers_; - sync_primitives::Lock ptu_handlers_lock_; #endif // ENABLE_SECURITY // Thread that pumps non-parsed messages coming from mobile side. @@ -699,6 +796,10 @@ class ProtocolHandlerImpl sync_primitives::Lock start_session_frame_map_lock_; StartSessionFrameMap start_session_frame_map_; + bool tcp_enabled_; + std::string tcp_port_; + std::string tcp_ip_address_; + #ifdef TELEMETRY_MONITOR PHTelemetryObserver* metric_observer_; #endif // TELEMETRY_MONITOR diff --git a/src/components/protocol_handler/include/protocol_handler/protocol_packet.h b/src/components/protocol_handler/include/protocol_handler/protocol_packet.h index 1c427533e6..d3e3ec5809 100644 --- a/src/components/protocol_handler/include/protocol_handler/protocol_packet.h +++ b/src/components/protocol_handler/include/protocol_handler/protocol_packet.h @@ -252,6 +252,12 @@ class ProtocolPacket { const size_t messageSize); /** + * @brief Calculates FIRST_FRAME data for further handling of consecutive + * frames + */ + void HandleRawFirstFrameData(const uint8_t* message); + + /** * \brief Getter of protocol version. */ uint8_t protocol_version() const; @@ -326,6 +332,11 @@ class ProtocolPacket { ConnectionID connection_id() const; /** + * \brief Setter of Connection Identifier + */ + void set_connection_id(ConnectionID connection_id); + + /** * \brief Getter for data payload size */ uint32_t payload_size() const; diff --git a/src/components/protocol_handler/src/handshake_handler.cc b/src/components/protocol_handler/src/handshake_handler.cc index 055ff2cf45..8db551cfd6 100644 --- a/src/components/protocol_handler/src/handshake_handler.cc +++ b/src/components/protocol_handler/src/handshake_handler.cc @@ -55,7 +55,7 @@ HandshakeHandler::HandshakeHandler( const std::vector<int>& force_protected_service, const bool is_new_service, ProtocolPacket::ProtocolVersion& full_version, - std::shared_ptr<uint8_t> payload) + std::shared_ptr<BsonObject> payload) : protocol_handler_(protocol_handler) , session_observer_(session_observer) , context_() @@ -69,7 +69,7 @@ HandshakeHandler::HandshakeHandler( ProtocolPacket::ProtocolVersion& full_version, const SessionContext& context, const uint8_t protocol_version, - std::shared_ptr<uint8_t> payload) + std::shared_ptr<BsonObject> payload) : protocol_handler_(protocol_handler) , session_observer_(session_observer) , context_(context) @@ -92,6 +92,19 @@ bool HandshakeHandler::GetPolicyCertificateData(std::string& data) const { void HandshakeHandler::OnCertificateUpdateRequired() {} +bool HandshakeHandler::OnHandshakeFailed() { + if (payload_) { + ProcessFailedHandshake(*payload_); + } else { + BsonObject params; + bson_object_initialize_default(¶ms); + ProcessFailedHandshake(params); + bson_object_deinitialize(¶ms); + } + + return true; +} + bool HandshakeHandler::OnHandshakeDone( uint32_t connection_key, security_manager::SSLContext::HandshakeResult result) { @@ -110,20 +123,23 @@ bool HandshakeHandler::OnHandshakeDone( const bool success = result == security_manager::SSLContext::Handshake_Result_Success; - BsonObject params; if (payload_) { - params = bson_object_from_bytes(payload_.get()); + if (success) { + ProcessSuccessfulHandshake(connection_key, *payload_); + } else { + ProcessFailedHandshake(*payload_); + } } else { + BsonObject params; bson_object_initialize_default(¶ms); + if (success) { + ProcessSuccessfulHandshake(connection_key, params); + } else { + ProcessFailedHandshake(params); + } + bson_object_deinitialize(¶ms); } - if (success) { - ProcessSuccessfulHandshake(connection_key, params); - } else { - ProcessFailedHandshake(params); - } - - bson_object_deinitialize(¶ms); return true; } diff --git a/src/components/protocol_handler/src/multiframe_builder.cc b/src/components/protocol_handler/src/multiframe_builder.cc index 5a1fc6d205..cf8a23ddc1 100644 --- a/src/components/protocol_handler/src/multiframe_builder.cc +++ b/src/components/protocol_handler/src/multiframe_builder.cc @@ -91,6 +91,8 @@ bool MultiFrameBuilder::RemoveConnection(const ConnectionID connection_id) { ProtocolFramePtrList MultiFrameBuilder::PopMultiframes() { LOG4CXX_AUTO_TRACE(logger_); LOG4CXX_DEBUG(logger_, "Current state is: " << multiframes_map_); + LOG4CXX_DEBUG(logger_, + "Current multiframe map size is: " << multiframes_map_.size()); ProtocolFramePtrList outpute_frame_list; for (MultiFrameMap::iterator connection_it = multiframes_map_.begin(); connection_it != multiframes_map_.end(); diff --git a/src/components/protocol_handler/src/protocol_handler_impl.cc b/src/components/protocol_handler/src/protocol_handler_impl.cc index 762b986782..6548f86c6a 100644 --- a/src/components/protocol_handler/src/protocol_handler_impl.cc +++ b/src/components/protocol_handler/src/protocol_handler_impl.cc @@ -31,6 +31,7 @@ */ #include "protocol_handler/protocol_handler_impl.h" +#include <arpa/inet.h> // for INET6_ADDRSTRLEN #include <memory.h> #include <algorithm> // std::find #include <bson_object.h> @@ -39,6 +40,7 @@ #include "connection_handler/connection_handler_impl.h" #include "protocol_handler/session_observer.h" #include "utils/byte_order.h" +#include "utils/helpers.h" #include "protocol/common.h" #ifdef ENABLE_SECURITY @@ -59,7 +61,8 @@ std::string ConvertPacketDataToString(const uint8_t* data, const size_t kStackSize = 65536; -ProtocolPacket::ProtocolVersion defaultProtocolVersion(5, 0, 0); +ProtocolPacket::ProtocolVersion defaultProtocolVersion(5, 1, 0); +ProtocolPacket::ProtocolVersion minMultipleTransportsVersion(5, 1, 0); ProtocolHandlerImpl::ProtocolHandlerImpl( const ProtocolHandlerSettings& settings, @@ -75,7 +78,6 @@ ProtocolHandlerImpl::ProtocolHandlerImpl( , #ifdef ENABLE_SECURITY security_manager_(NULL) - , is_ptu_triggered_(false) , #endif // ENABLE_SECURITY raw_ford_messages_from_mobile_( @@ -84,6 +86,7 @@ ProtocolHandlerImpl::ProtocolHandlerImpl( "PH ToMobile", this, threads::ThreadOptions(kStackSize)) , start_session_frame_map_lock_() , start_session_frame_map_() + , tcp_enabled_(false) #ifdef TELEMETRY_MONITOR , metric_observer_(NULL) #endif // TELEMETRY_MONITOR @@ -242,6 +245,8 @@ void ProtocolHandlerImpl::SendStartSessionAck( BsonObject& params) { LOG4CXX_AUTO_TRACE(logger_); + bool send_transport_update_event = false; + uint8_t ack_protocol_version = SupportedSDLProtocolVersion(); const bool proxy_supports_v5_protocol = @@ -279,16 +284,28 @@ void ProtocolHandlerImpl::SendStartSessionAck( if (ack_protocol_version >= PROTOCOL_VERSION_5) { ServiceType serviceTypeValue = ServiceTypeFromByte(service_type); - bson_object_put_int64( + const bool mtu_written = bson_object_put_int64( ¶ms, strings::mtu, static_cast<int64_t>( protocol_header_validator_.max_payload_size_by_service_type( serviceTypeValue))); + LOG4CXX_DEBUG(logger_, + "MTU parameter was written to bson params: " + << mtu_written << "; Value: " + << static_cast<int32_t>( + bson_object_get_int64(¶ms, strings::mtu))); + if (serviceTypeValue == kRpc) { // Hash ID is only used in RPC case - bson_object_put_int32( + const bool hash_written = bson_object_put_int32( ¶ms, strings::hash_id, static_cast<int32_t>(hash_id)); + LOG4CXX_DEBUG(logger_, + "Hash parameter was written to bson params: " + << hash_written << "; Value: " + << static_cast<int32_t>(bson_object_get_int32( + ¶ms, strings::hash_id))); + // Minimum protocol version supported by both ProtocolPacket::ProtocolVersion* minVersion = (full_version.majorVersion < PROTOCOL_VERSION_5) @@ -297,8 +314,99 @@ void ProtocolHandlerImpl::SendStartSessionAck( defaultProtocolVersion); char protocolVersionString[256]; strncpy(protocolVersionString, (*minVersion).to_string().c_str(), 255); - bson_object_put_string( + + const bool protocol_ver_written = bson_object_put_string( ¶ms, strings::protocol_version, protocolVersionString); + LOG4CXX_DEBUG( + logger_, + "Protocol version parameter was written to bson params: " + << protocol_ver_written << "; Value: " + << bson_object_get_string(¶ms, strings::protocol_version)); + + LOG4CXX_INFO(logger_, + "Protocol Version String " << protocolVersionString); + + std::vector<std::string> secondaryTransports; + std::vector<int32_t> audioServiceTransports; + std::vector<int32_t> videoServiceTransports; + if (*minVersion >= minMultipleTransportsVersion) { + if (ParseSecondaryTransportConfiguration(connection_id, + secondaryTransports, + audioServiceTransports, + videoServiceTransports)) { + LOG4CXX_DEBUG(logger_, "Multiple transports are enabled."); + BsonArray secondaryTransportsArr; + bson_array_initialize(&secondaryTransportsArr, + secondaryTransports.size()); + for (unsigned int i = 0; i < secondaryTransports.size(); i++) { + char secondaryTransport[255]; + strncpy(secondaryTransport, + secondaryTransports[i].c_str(), + sizeof(secondaryTransport)); + secondaryTransport[sizeof(secondaryTransport) - 1] = '\0'; + LOG4CXX_DEBUG( + logger_, + "Adding " + << secondaryTransport + << " to secondaryTransports parameter of StartSessionAck"); + bson_array_add_string(&secondaryTransportsArr, secondaryTransport); + } + bson_object_put_array( + ¶ms, strings::secondary_transports, &secondaryTransportsArr); + + BsonArray audioServiceTransportsArr; + bson_array_initialize(&audioServiceTransportsArr, + audioServiceTransports.size()); + for (unsigned int i = 0; i < audioServiceTransports.size(); i++) { + LOG4CXX_DEBUG(logger_, + "Adding " << audioServiceTransports[i] + << " to audioServiceTransports parameter " + "of StartSessionAck"); + bson_array_add_int32(&audioServiceTransportsArr, + audioServiceTransports[i]); + } + bson_object_put_array(¶ms, + strings::audio_service_transports, + &audioServiceTransportsArr); + + BsonArray videoServiceTransportsArr; + bson_array_initialize(&videoServiceTransportsArr, + videoServiceTransports.size()); + for (unsigned int i = 0; i < videoServiceTransports.size(); i++) { + LOG4CXX_DEBUG(logger_, + "Adding " << videoServiceTransports[i] + << " to videoServiceTransports parameter " + "of StartSessionAck"); + bson_array_add_int32(&videoServiceTransportsArr, + videoServiceTransports[i]); + } + bson_object_put_array(¶ms, + strings::video_service_transports, + &videoServiceTransportsArr); + + if (settings_.multiple_transports_enabled()) { + send_transport_update_event = true; + } else { + LOG4CXX_DEBUG( + logger_, + "Multiple transports feature is disabled by configuration"); + // In this case, we must remember that this session will never have + // a secondary transport. + connection_handler_.SetSecondaryTransportID(session_id, + kDisabledSecondary); + } + } else { + LOG4CXX_WARN( + logger_, + "Failed to set up secondary transport and service type params"); + connection_handler_.SetSecondaryTransportID(session_id, + kDisabledSecondary); + } + } else { + LOG4CXX_INFO(logger_, "Older protocol version. No multiple transports"); + connection_handler_.SetSecondaryTransportID(session_id, + kDisabledSecondary); + } } uint8_t* payloadBytes = bson_object_to_bytes(¶ms); ptr->set_data(payloadBytes, bson_object_size(¶ms)); @@ -316,6 +424,16 @@ void ProtocolHandlerImpl::SendStartSessionAck( << static_cast<int32_t>(service_type) << " session_id " << static_cast<int32_t>(session_id) << " protection " << (protection ? "ON" : "OFF")); + + if (send_transport_update_event) { + // Wait until the StartService ACK has been processed for sending. + // The TransportUpdateEvent has a higher priority, being that it's + // a SERVICE_TYPE_CONTROL message. (The ACK is SERVICE_TYPE_RPC.) + LOG4CXX_DEBUG(logger_, "Waiting for the MessageToMobile queue to be empty"); + raw_ford_messages_to_mobile_.WaitDumpQueue(); + LOG4CXX_DEBUG(logger_, "Sending the TransportUpdate event"); + SendTransportUpdateEvent(connection_id, session_id); + } } void ProtocolHandlerImpl::SendStartSessionNAck(ConnectionID connection_id, @@ -473,14 +591,18 @@ void ProtocolHandlerImpl::SendEndSessionAck(ConnectionID connection_id, << static_cast<int32_t>(session_id)); } -void ProtocolHandlerImpl::SendEndServicePrivate(int32_t connection_id, +void ProtocolHandlerImpl::SendEndServicePrivate(int32_t primary_connection_id, + int32_t connection_id, uint8_t session_id, uint8_t service_type) { LOG4CXX_AUTO_TRACE(logger_); uint8_t protocol_version; if (session_observer_.ProtocolVersionUsed( - connection_id, session_id, protocol_version)) { + primary_connection_id, session_id, protocol_version)) { + LOG4CXX_TRACE(logger_, + "SendEndServicePrivate using protocol version " + << static_cast<int32_t>(protocol_version)); ProtocolFramePtr ptr( new protocol_handler::ProtocolPacket(connection_id, protocol_version, @@ -495,25 +617,31 @@ void ProtocolHandlerImpl::SendEndServicePrivate(int32_t connection_id, raw_ford_messages_to_mobile_.PostMessage( impl::RawFordMessageToMobile(ptr, false)); LOG4CXX_DEBUG(logger_, - "SendEndSession() for connection " - << connection_id << " for service_type " << service_type + "SendEndServicePrivate() for connection " + << primary_connection_id << " for service_type " + << static_cast<int>(service_type) + << " service connection " << connection_id << " session_id " << static_cast<int32_t>(session_id)); } else { LOG4CXX_WARN( logger_, - "SendEndSession is failed connection or session does not exist"); + "SendEndServicePrivate is failed connection or session does not exist"); } } void ProtocolHandlerImpl::SendEndSession(int32_t connection_id, uint8_t session_id) { - SendEndServicePrivate(connection_id, session_id, SERVICE_TYPE_RPC); + // A session is always associated with a primary connection ID + SendEndServicePrivate( + connection_id, connection_id, session_id, SERVICE_TYPE_RPC); } -void ProtocolHandlerImpl::SendEndService(int32_t connection_id, +void ProtocolHandlerImpl::SendEndService(int32_t primary_connection_id, + int32_t connection_id, uint8_t session_id, uint8_t service_type) { - SendEndServicePrivate(connection_id, session_id, service_type); + SendEndServicePrivate( + primary_connection_id, connection_id, session_id, service_type); } RESULT_CODE ProtocolHandlerImpl::SendHeartBeatAck(ConnectionID connection_id, @@ -545,6 +673,139 @@ RESULT_CODE ProtocolHandlerImpl::SendHeartBeatAck(ConnectionID connection_id, return RESULT_FAIL; } +void ProtocolHandlerImpl::SendTransportUpdateEvent(ConnectionID connection_id, + uint8_t session_id) { + LOG4CXX_AUTO_TRACE(logger_); + + uint8_t protocol_version; + if (session_observer_.ProtocolVersionUsed( + connection_id, session_id, protocol_version)) { + ProtocolFramePtr ptr( + new protocol_handler::ProtocolPacket(connection_id, + protocol_version, + PROTECTION_OFF, + FRAME_TYPE_CONTROL, + SERVICE_TYPE_CONTROL, + FRAME_DATA_TRANSPORT_EVENT_UPDATE, + session_id, + 0, + message_counters_[session_id]++)); + + BsonObject payload_obj; + bson_object_initialize_default(&payload_obj); + + int32_t tcp_port = atoi(tcp_port_.c_str()); + char tcp_ip_address[INET6_ADDRSTRLEN + 1]; + if (tcp_enabled_ && (tcp_port != 0)) { + strncpy(tcp_ip_address, tcp_ip_address_.c_str(), INET6_ADDRSTRLEN); + tcp_ip_address[INET6_ADDRSTRLEN] = '\0'; + bson_object_put_string( + &payload_obj, strings::tcp_ip_address, tcp_ip_address); + bson_object_put_int32(&payload_obj, strings::tcp_port, tcp_port); + } else { + tcp_ip_address[0] = '\0'; + bson_object_put_string( + &payload_obj, strings::tcp_ip_address, tcp_ip_address); + // omit TCP port number + } + LOG4CXX_INFO(logger_, + "SendTransportUpdateEvent IP address: " + << tcp_ip_address << " Port: " << tcp_port); + + uint8_t* payloadBytes = bson_object_to_bytes(&payload_obj); + ptr->set_data(payloadBytes, bson_object_size(&payload_obj)); + free(payloadBytes); + bson_object_deinitialize(&payload_obj); + + raw_ford_messages_to_mobile_.PostMessage( + impl::RawFordMessageToMobile(ptr, false)); + + LOG4CXX_DEBUG(logger_, + "SendTransportUpdateEvent() for connection " + << connection_id << " for session " + << static_cast<int32_t>(session_id)); + } else { + LOG4CXX_WARN(logger_, + "SendTransportUpdateEvent is failed connection or session " + "does not exist"); + } +} + +RESULT_CODE ProtocolHandlerImpl::SendRegisterSecondaryTransportAck( + ConnectionID connection_id, + ConnectionID primary_transport_connection_id, + uint8_t session_id) { + LOG4CXX_AUTO_TRACE(logger_); + + // acquire the protocol version from primary transport + uint8_t protocol_version; + if (session_observer_.ProtocolVersionUsed( + primary_transport_connection_id, session_id, protocol_version)) { + ProtocolFramePtr ptr(new protocol_handler::ProtocolPacket( + connection_id, + protocol_version, + PROTECTION_OFF, + FRAME_TYPE_CONTROL, + SERVICE_TYPE_CONTROL, + FRAME_DATA_REGISTER_SECONDARY_TRANSPORT_ACK, + session_id, + 0u, + 2)); + + raw_ford_messages_to_mobile_.PostMessage( + impl::RawFordMessageToMobile(ptr, false)); + return RESULT_OK; + } + LOG4CXX_WARN(logger_, + "RegisterSecondaryTransportAck is failed connection or session " + "does not exist"); + return RESULT_FAIL; +} + +RESULT_CODE ProtocolHandlerImpl::SendRegisterSecondaryTransportNAck( + ConnectionID connection_id, + ConnectionID primary_transport_connection_id, + uint8_t session_id, + BsonObject* reason) { + LOG4CXX_AUTO_TRACE(logger_); + + // If mobile sends an invalid session ID and we cannot find out the Connection + // ID of primary transport, then we use version 5. (The multiple-transports + // feature is added in 5.1.0.) + uint8_t protocol_version = PROTOCOL_VERSION_5; + if (primary_transport_connection_id > 0) { + // acquire the protocol version from primary transport + if (!session_observer_.ProtocolVersionUsed( + primary_transport_connection_id, session_id, protocol_version)) { + LOG4CXX_WARN(logger_, + "Failed to acquire protocol version for " + "RegisterSecondaryTransportNAck"); + return RESULT_FAIL; + } + } + + ProtocolFramePtr ptr(new protocol_handler::ProtocolPacket( + connection_id, + protocol_version, + PROTECTION_OFF, + FRAME_TYPE_CONTROL, + SERVICE_TYPE_CONTROL, + FRAME_DATA_REGISTER_SECONDARY_TRANSPORT_NACK, + session_id, + 0u, + 2)); + + if (reason) { + uint8_t* payloadBytes = bson_object_to_bytes(reason); + ptr->set_data(payloadBytes, bson_object_size(reason)); + free(payloadBytes); + } + + raw_ford_messages_to_mobile_.PostMessage( + impl::RawFordMessageToMobile(ptr, false)); + return RESULT_OK; +} + void ProtocolHandlerImpl::SendHeartBeat(int32_t connection_id, uint8_t session_id) { LOG4CXX_AUTO_TRACE(logger_); @@ -839,57 +1100,83 @@ void ProtocolHandlerImpl::OnConnectionClosed( multiframe_builder_.RemoveConnection(connection_id); } -void ProtocolHandlerImpl::OnPTUFinished(const bool ptu_result) { - LOG4CXX_AUTO_TRACE(logger_); +void ProtocolHandlerImpl::OnUnexpectedDisconnect( + const transport_manager::ConnectionUID connection_id, + const transport_manager::CommunicationError& error) { + OnConnectionClosed(connection_id); +} +void ProtocolHandlerImpl::NotifyOnFailedHandshake() { + LOG4CXX_AUTO_TRACE(logger_); #ifdef ENABLE_SECURITY - sync_primitives::AutoLock lock(ptu_handlers_lock_); + security_manager_->NotifyListenersOnHandshakeFailed(); +#endif // ENABLE_SECURITY +} - if (!is_ptu_triggered_) { - LOG4CXX_ERROR(logger_, - "PTU was not triggered by service starting. Ignored"); +void ProtocolHandlerImpl::OnTransportConfigUpdated( + const transport_manager::transport_adapter::TransportConfig& configs) { + LOG4CXX_AUTO_TRACE(logger_); + + transport_manager::transport_adapter::TransportConfig::const_iterator it = + configs.find(transport_manager::transport_adapter::tc_enabled); + if (configs.end() == it) { + LOG4CXX_WARN(logger_, "No enabled field in OnTransportConfigUpdated"); return; } - const bool is_cert_expired = security_manager_->IsCertificateUpdateRequired(); - for (auto handler : ptu_pending_handlers_) { - security_manager::SSLContext* ssl_context = - is_cert_expired - ? NULL - : security_manager_->CreateSSLContext(handler->connection_key()); - - if (!ssl_context) { - const std::string error("CreateSSLContext failed"); - LOG4CXX_ERROR(logger_, error); - security_manager_->SendInternalError( - handler->connection_key(), - security_manager::SecurityManager::ERROR_INTERNAL, - error); - - handler->OnHandshakeDone( - handler->connection_key(), - security_manager::SSLContext::Handshake_Result_Fail); + bool tcp_enabled = (0 == strcmp("true", it->second.c_str())); + std::string tcp_port; - continue; + if (tcp_enabled) { + it = configs.find(transport_manager::transport_adapter::tc_tcp_port); + if (configs.end() == it) { + LOG4CXX_WARN(logger_, "No port field in OnTransportConfigUpdated"); + return; } + tcp_port = it->second; - if (ssl_context->IsInitCompleted()) { - handler->OnHandshakeDone( - handler->connection_key(), - security_manager::SSLContext::Handshake_Result_Success); - } else { - security_manager_->AddListener(new HandshakeHandler(*handler)); - if (!ssl_context->IsHandshakePending()) { - // Start handshake process - security_manager_->StartHandshake(handler->connection_key()); - } + it = configs.find(transport_manager::transport_adapter::tc_tcp_ip_address); + if (configs.end() == it) { + LOG4CXX_WARN(logger_, "No IP address field in OnTransportConfigUpdated"); + return; } + tcp_enabled_ = true; + tcp_port_ = tcp_port; + tcp_ip_address_ = it->second; + } else { + tcp_enabled_ = false; + tcp_port_.clear(); + tcp_ip_address_.clear(); + } + + LOG4CXX_INFO(logger_, + "OnTransportConfigUpdated: new config enabled is " + << tcp_enabled_ << ". Port is " << tcp_port_ + << ". IP Address is " << tcp_ip_address_); + + // Walk the SessionConnection map and find all sessions that need a + // TransportUpdate Event. Sessions flagged with kDisabledSecondary in their + // secondary transport are ineligible for secondary transport, and + // therefore don't get this event. + DataAccessor<connection_handler::SessionConnectionMap> + session_connection_map_accessor = + connection_handler_.session_connection_map(); + const connection_handler::SessionConnectionMap& session_connection_map = + session_connection_map_accessor.GetData(); + connection_handler::SessionConnectionMap::const_iterator itr = + session_connection_map.begin(); + while (itr != session_connection_map.end()) { + const connection_handler::SessionTransports st = itr->second; + LOG4CXX_INFO(logger_, + "OnTransportConfigUpdated found session " + << itr->first << " with primary connection " + << st.primary_transport << " and secondary connection " + << st.secondary_transport); + if (st.secondary_transport != kDisabledSecondary) { + SendTransportUpdateEvent(st.primary_transport, itr->first); + } + itr++; } - - LOG4CXX_DEBUG(logger_, "Handshake handlers were notified"); - ptu_pending_handlers_.clear(); - is_ptu_triggered_ = false; -#endif // ENABLE_SECURITY } RESULT_CODE ProtocolHandlerImpl::SendFrame(const ProtocolFramePtr packet) { @@ -1076,6 +1363,13 @@ RESULT_CODE ProtocolHandlerImpl::HandleSingleFrameMessage( << packet->data_size() << "; message " << ConvertPacketDataToString(packet->data(), packet->data_size())); + // Replace a potential secondary transport ID in the packet with the primary + // transport ID + const connection_handler::SessionTransports st = + connection_handler_.GetSessionTransports(packet->session_id()); + if (st.primary_transport != 0) { + packet->set_connection_id(st.primary_transport); + } const uint32_t connection_key = session_observer_.KeyFromPair( packet->connection_id(), packet->session_id()); @@ -1108,6 +1402,14 @@ RESULT_CODE ProtocolHandlerImpl::HandleMultiFrameMessage( const ProtocolFramePtr packet) { LOG4CXX_AUTO_TRACE(logger_); + // Replace a potential secondary transport ID in the packet with the primary + // transport ID + const connection_handler::SessionTransports st = + connection_handler_.GetSessionTransports(packet->session_id()); + if (st.primary_transport != 0) { + packet->set_connection_id(st.primary_transport); + } + if (multiframe_builder_.AddFrame(packet) != RESULT_OK) { LOG4CXX_WARN(logger_, "Frame assembling issue"); } @@ -1145,6 +1447,10 @@ RESULT_CODE ProtocolHandlerImpl::HandleControlMessage( << packet->connection_id()); return RESULT_OK; } + case FRAME_DATA_REGISTER_SECONDARY_TRANSPORT: { + LOG4CXX_TRACE(logger_, "FrameData: RegisterSecondaryTransport"); + return HandleControlMessageRegisterSecondaryTransport(packet); + } default: LOG4CXX_WARN(logger_, "Control message of type " @@ -1286,7 +1592,8 @@ RESULT_CODE ProtocolHandlerImpl::HandleControlMessageStartSession( session_observer_.KeyFromPair(connection_id, session_id); security_manager::SSLContext* ssl_context = - security_manager_->CreateSSLContext(connection_key); + security_manager_->CreateSSLContext( + connection_key, security_manager::SecurityManager::kUseExisting); if (!ssl_context) { const std::string error("CreateSSLContext failed"); LOG4CXX_ERROR(logger_, error); @@ -1416,11 +1723,11 @@ RESULT_CODE ProtocolHandlerImpl::HandleControlMessageStartSession( } #ifdef ENABLE_SECURITY + const uint8_t protocol_version = packet->protocol_version(); const bool protection = - // Protocol version 1 is not support protection - (packet->protocol_version() > PROTOCOL_VERSION_1) - ? packet->protection_flag() - : false; + // Protocol version 1 does not support protection + (protocol_version > PROTOCOL_VERSION_1) ? packet->protection_flag() + : false; #else const bool protection = false; #endif // ENABLE_SECURITY @@ -1428,6 +1735,11 @@ RESULT_CODE ProtocolHandlerImpl::HandleControlMessageStartSession( const ConnectionID connection_id = packet->connection_id(); const uint8_t session_id = packet->session_id(); + LOG4CXX_INFO(logger_, + "StartSession ID " << static_cast<int>(session_id) + << " and Connection ID " + << static_cast<int>(connection_id)); + { sync_primitives::AutoLock auto_lock(start_session_frame_map_lock_); start_session_frame_map_[std::make_pair(connection_id, session_id)] = @@ -1441,6 +1753,50 @@ RESULT_CODE ProtocolHandlerImpl::HandleControlMessageStartSession( return RESULT_OK; } +RESULT_CODE ProtocolHandlerImpl::HandleControlMessageRegisterSecondaryTransport( + const ProtocolFramePtr packet) { + LOG4CXX_AUTO_TRACE(logger_); + const uint8_t session_id = packet->session_id(); + const ConnectionID connection_id = packet->connection_id(); + ConnectionID primary_connection_id = 0; + + LOG4CXX_INFO(logger_, + "RegisterSecondaryTransport ID " + << static_cast<int>(session_id) << " and Connection ID " + << static_cast<int>(connection_id)); + + if (connection_handler_.OnSecondaryTransportStarted( + primary_connection_id, connection_id, session_id)) { + SendRegisterSecondaryTransportAck( + connection_id, primary_connection_id, session_id); + } else { + char reason[256]; + BsonObject registerSecondaryTransportNackObj; + bson_object_initialize_default(®isterSecondaryTransportNackObj); + if (0 == session_id) { + strncpy(reason, + "RegisterSecondaryTransport MUST include a non-zero session ID", + 255); + } else if (primary_connection_id == 0) { + strncpy(reason, "RegisterSecondaryTransport session ID not found", 255); + } else { + strncpy( + reason, + "RegisterSecondaryTransport session ID has already been registered", + 255); + } + bson_object_put_string( + ®isterSecondaryTransportNackObj, strings::reason, reason); + SendRegisterSecondaryTransportNAck(connection_id, + primary_connection_id, + session_id, + ®isterSecondaryTransportNackObj); + bson_object_deinitialize(®isterSecondaryTransportNackObj); + } + + return RESULT_OK; +} + void ProtocolHandlerImpl::NotifySessionStartedResult( int32_t connection_id, uint8_t session_id, @@ -1450,6 +1806,7 @@ void ProtocolHandlerImpl::NotifySessionStartedResult( std::vector<std::string>& rejected_params) { LOG4CXX_AUTO_TRACE(logger_); protocol_handler::SessionContext context(connection_id, + connection_id, session_id, generated_session_id, ServiceType::kInvalidServiceType, @@ -1552,51 +1909,19 @@ void ProtocolHandlerImpl::NotifySessionStarted( const uint32_t connection_key = session_observer_.KeyFromPair( context.connection_id_, context.new_session_id_); - std::shared_ptr<uint8_t> bson_object_bytes( - bson_object_to_bytes(start_session_ack_params.get()), - [](uint8_t* p) { delete[] p; }); - std::shared_ptr<HandshakeHandler> handler = std::make_shared<HandshakeHandler>(*this, session_observer_, *fullVersion, context, packet->protocol_version(), - bson_object_bytes); - - const bool is_certificate_empty = - security_manager_->IsPolicyCertificateDataEmpty(); - - const bool is_certificate_expired = - is_certificate_empty || - security_manager_->IsCertificateUpdateRequired(); - - if (context.is_ptu_required_ && is_certificate_empty) { - LOG4CXX_DEBUG(logger_, - "PTU for StartSessionHandler " - << handler.get() - << " is required and certificate data is empty"); - - sync_primitives::AutoLock lock(ptu_handlers_lock_); - if (!is_ptu_triggered_) { - LOG4CXX_DEBUG(logger_, - "PTU is not triggered yet. " - << "Starting PTU and postponing SSL handshake"); - - ptu_pending_handlers_.push_back(handler); - is_ptu_triggered_ = true; - security_manager_->NotifyOnCertificateUpdateRequired(); - } else { - LOG4CXX_DEBUG(logger_, "PTU has been triggered. Added to pending."); - ptu_pending_handlers_.push_back(handler); - } - return; - } + start_session_ack_params); security_manager::SSLContext* ssl_context = - is_certificate_expired - ? NULL - : security_manager_->CreateSSLContext(connection_key); + security_manager_->CreateSSLContext( + connection_key, + security_manager::SecurityManager::ContextCreationStrategy:: + kUseExisting); if (!ssl_context) { const std::string error("CreateSSLContext failed"); LOG4CXX_ERROR(logger_, error); @@ -1630,12 +1955,27 @@ void ProtocolHandlerImpl::NotifySessionStarted( *fullVersion, *start_session_ack_params); } else { - security_manager_->AddListener(new HandshakeHandler(*handler)); + LOG4CXX_DEBUG(logger_, + "Adding Handshake handler to listeners: " << handler.get()); + security_manager::SecurityManagerListener* listener = + new HandshakeHandler(*handler); + security_manager_->AddListener(listener); + if (!ssl_context->IsHandshakePending()) { // Start handshake process security_manager_->StartHandshake(connection_key); + + if (!security_manager_->IsSystemTimeProviderReady()) { + security_manager_->RemoveListener(listener); + SendStartSessionNAck(context.connection_id_, + packet->session_id(), + protocol_version, + packet->service_type(), + rejected_params); + } } } + LOG4CXX_DEBUG(logger_, "Protection establishing for connection " << connection_key << " is in progress"); @@ -1688,6 +2028,7 @@ RESULT_CODE ProtocolHandlerImpl::HandleControlMessageHeartBeat( } void ProtocolHandlerImpl::PopValideAndExpirateMultiframes() { + LOG4CXX_AUTO_TRACE(logger_); const ProtocolFramePtrList& frame_list = multiframe_builder_.PopMultiframes(); for (ProtocolFramePtrList::const_iterator it = frame_list.begin(); it != frame_list.end(); @@ -1845,7 +2186,9 @@ RESULT_CODE ProtocolHandlerImpl::EncryptFrame(ProtocolFramePtr packet) { DCHECK(packet); // Control frames and data over control service shall be unprotected if (packet->service_type() == kControl || - packet->frame_type() == FRAME_TYPE_CONTROL) { + // For protocol v5 control frames could be protected + (packet->frame_type() == FRAME_TYPE_CONTROL && + packet->protocol_version() < PROTOCOL_VERSION_5)) { return RESULT_OK; } if (!security_manager_) { @@ -1888,12 +2231,30 @@ RESULT_CODE ProtocolHandlerImpl::EncryptFrame(ProtocolFramePtr packet) { RESULT_CODE ProtocolHandlerImpl::DecryptFrame(ProtocolFramePtr packet) { DCHECK(packet); - if (!packet->protection_flag() || - // Control frames and data over control service shall be unprotected - packet->service_type() == kControl || - packet->frame_type() == FRAME_TYPE_CONTROL) { + + bool shoud_not_decrypt; + if (packet->protocol_version() >= PROTOCOL_VERSION_5) { + // For v5 protocol control frames except StartService could be encrypted + shoud_not_decrypt = + !packet->protection_flag() || packet->service_type() == kControl || + (FRAME_TYPE_CONTROL == packet->frame_type() && + helpers::Compare<ServiceType, helpers::EQ, helpers::ONE>( + static_cast<ServiceType>(packet->service_type()), + kMobileNav, + kAudio, + kRpc)); + } else { + // Control frames and data over control service shall be unprotected + shoud_not_decrypt = !packet->protection_flag() || + packet->service_type() == kControl || + packet->frame_type() == FRAME_TYPE_CONTROL; + } + + if (shoud_not_decrypt) { + LOG4CXX_DEBUG(logger_, "Frame will not be decrypted"); return RESULT_OK; } + if (!security_manager_) { LOG4CXX_WARN(logger_, "No security_manager_ set."); return RESULT_FAIL; @@ -1934,6 +2295,11 @@ RESULT_CODE ProtocolHandlerImpl::DecryptFrame(ProtocolFramePtr packet) { << out_data_size << " bytes"); DCHECK(out_data); DCHECK(out_data_size); + // Special handling for decrypted FIRST_FRAME + if (packet->frame_type() == FRAME_TYPE_FIRST && packet->protection_flag()) { + packet->HandleRawFirstFrameData(out_data); + return RESULT_OK; + } packet->set_data(out_data, out_data_size); return RESULT_OK; } @@ -2007,4 +2373,241 @@ uint8_t ProtocolHandlerImpl::SupportedSDLProtocolVersion() const { LOG4CXX_AUTO_TRACE(logger_); return get_settings().max_supported_protocol_version(); } + +const impl::TransportTypes transportTypes = { + std::make_pair( + std::string("AOA_USB"), + impl::TransportDescription(impl::TransportType::TT_USB, false, true)), + std::make_pair(std::string("SPP_BLUETOOTH"), + impl::TransportDescription( + impl::TransportType::TT_BLUETOOTH, false, true)), + std::make_pair(std::string("IAP_BLUETOOTH"), + impl::TransportDescription( + impl::TransportType::TT_BLUETOOTH, true, false)), + std::make_pair( + std::string("IAP_USB"), + impl::TransportDescription(impl::TransportType::TT_USB, true, false)), + std::make_pair( + std::string("TCP_WIFI"), + impl::TransportDescription(impl::TransportType::TT_WIFI, true, true)), + std::make_pair( + std::string("IAP_USB_HOST_MODE"), + impl::TransportDescription(impl::TransportType::TT_USB, true, false)), + std::make_pair( + std::string("IAP_USB_DEVICE_MODE"), + impl::TransportDescription(impl::TransportType::TT_USB, true, false)), + std::make_pair( + std::string("IAP_CARPLAY"), + impl::TransportDescription(impl::TransportType::TT_WIFI, true, false))}; + +const impl::TransportDescription +ProtocolHandlerImpl::GetTransportTypeFromConnectionType( + const std::string& connection_type) const { + impl::TransportDescription result = + impl::TransportDescription(impl::TransportType::TT_NONE, false, false); + impl::TransportTypes::const_iterator it = + transportTypes.find(connection_type); + if (it != transportTypes.end()) { + result = it->second; + } else { + LOG4CXX_ERROR(logger_, "Unknown connection type " << connection_type); + } + + return result; +} + +const bool ProtocolHandlerImpl::ParseSecondaryTransportConfiguration( + const ConnectionID connection_id, + std::vector<std::string>& secondaryTransports, + std::vector<int32_t>& audioServiceTransports, + std::vector<int32_t>& videoServiceTransports) const { + LOG4CXX_AUTO_TRACE(logger_); + std::vector<std::string> secondary_transport_types; + + // First discover what the connection type of the primary transport is + // and look up the allowed secondary transports for that primary transport + const std::string connection_type = + session_observer_.TransportTypeProfileStringFromConnHandle(connection_id); + const impl::TransportDescription td = + GetTransportTypeFromConnectionType(connection_type); + if (settings_.multiple_transports_enabled()) { + if (td.transport_type_ == impl::TransportType::TT_USB) { + secondary_transport_types = settings_.secondary_transports_for_usb(); + } else if (td.transport_type_ == impl::TransportType::TT_BLUETOOTH) { + secondary_transport_types = + settings_.secondary_transports_for_bluetooth(); + } else if (td.transport_type_ == impl::TransportType::TT_WIFI) { + secondary_transport_types = settings_.secondary_transports_for_wifi(); + } else { + LOG4CXX_ERROR( + logger_, + "Bad or unknown device type in ParseSecondaryTransportConfiguration"); + return false; + } + } + // note: even if settings_.multiple_transports_enabled() is false, we still + // send out an empty "secondaryTransports" parameter, along with + // "videoServiceTransports" and "audioServiceTransports" params which are + // useful without secondary transport. + + // Then, generate the "secondaryTransports" array for the StartSession ACK + GenerateSecondaryTransportsForStartSessionAck(secondary_transport_types, + td.ios_transport_, + td.android_transport_, + secondaryTransports); + + // Next, figure out which connections audio or video services are allowed on + GenerateServiceTransportsForStartSessionAck( + settings_.multiple_transports_enabled(), + settings_.audio_service_transports(), + connection_type, + td.transport_type_, + secondary_transport_types, + audioServiceTransports); + + GenerateServiceTransportsForStartSessionAck( + settings_.multiple_transports_enabled(), + settings_.video_service_transports(), + connection_type, + td.transport_type_, + secondary_transport_types, + videoServiceTransports); + + return true; +} + +void ProtocolHandlerImpl::GenerateSecondaryTransportsForStartSessionAck( + const std::vector<std::string>& secondary_transport_types, + bool device_is_ios, + bool device_is_android, + std::vector<std::string>& secondaryTransports) const { + LOG4CXX_AUTO_TRACE(logger_); + + // Parse the "secondary_transport_types" vector (which comes from + // smartDeviceLink.ini). For each entry in the vector, add an + // appropriate string to the secondaryTransports + std::vector<std::string>::const_iterator it = + secondary_transport_types.begin(); + while (it != secondary_transport_types.end()) { + const utils::custom_string::CustomString transport_type(*it); + if (transport_type.CompareIgnoreCase("USB")) { + if (device_is_ios) { + LOG4CXX_TRACE( + logger_, + "Adding IAP_USB to secondaryTransports for StartSessionAck"); + secondaryTransports.push_back("IAP_USB"); + } + if (device_is_android) { + LOG4CXX_TRACE( + logger_, + "Adding AOA_USB to secondaryTransports for StartSessionAck"); + secondaryTransports.push_back("AOA_USB"); + } + } else if (transport_type.CompareIgnoreCase("Bluetooth")) { + if (device_is_ios) { + LOG4CXX_TRACE( + logger_, + "Adding IAP_BLUETOOTH to secondaryTransports for StartSessionAck"); + secondaryTransports.push_back("IAP_BLUETOOTH"); + } + if (device_is_android) { + LOG4CXX_TRACE( + logger_, + "Adding SPP_BLUETOOTH to secondaryTransports for StartSessionAck"); + secondaryTransports.push_back("SPP_BLUETOOTH"); + } + } + if (transport_type.CompareIgnoreCase("WiFi")) { + LOG4CXX_TRACE( + logger_, + "Adding TCP_WIFI to secondaryTransports for StartSessionAck"); + secondaryTransports.push_back("TCP_WIFI"); + } + + it++; + } +} + +void ProtocolHandlerImpl::GenerateServiceTransportsForStartSessionAck( + bool secondary_enabled, + const std::vector<std::string>& service_transports, + const std::string& primary_connection_type, + const impl::TransportType primary_transport_type, + const std::vector<std::string>& secondary_transport_types, + std::vector<int32_t>& serviceTransports) const { + LOG4CXX_AUTO_TRACE(logger_); + + if (service_transports.size() == 0) { + if (secondary_enabled && !secondary_transport_types.empty()) { + LOG4CXX_TRACE(logger_, + "Empty Service Transports. Allowing service to run on both " + "connections"); + serviceTransports.push_back(1); + serviceTransports.push_back(2); + } else { + serviceTransports.push_back(1); + } + } else { + bool fPrimaryAdded = false; + bool fSecondaryAdded = false; + std::vector<std::string>::const_iterator it = service_transports.begin(); + for (; it != service_transports.end(); it++) { + const utils::custom_string::CustomString transport(*it); + LOG4CXX_TRACE(logger_, + "Service Allowed to run on " << transport.c_str() + << " transport"); + + if (!fPrimaryAdded && + (transport.CompareIgnoreCase(primary_connection_type.c_str()) || + (transport.CompareIgnoreCase("IAP_USB") && + primary_transport_type == impl::TransportType::TT_USB))) { + LOG4CXX_TRACE(logger_, "Service allowed on primary transport"); + serviceTransports.push_back(1); + fPrimaryAdded = true; + } + + if (!fSecondaryAdded) { + const utils::custom_string::CustomString transport_type( + TransportTypeFromTransport(transport)); + std::vector<std::string>::const_iterator found = + std::find_if(secondary_transport_types.begin(), + secondary_transport_types.end(), + [&](const std::string& secondary_transport_type) { + return transport_type.CompareIgnoreCase( + secondary_transport_type.c_str()); + }); + if (found != secondary_transport_types.end()) { + LOG4CXX_TRACE(logger_, "Service allowed on secondary transport"); + serviceTransports.push_back(2); + fSecondaryAdded = true; + } + } + + if (fPrimaryAdded && fSecondaryAdded) { + break; + } + } + } +} + +const std::string ProtocolHandlerImpl::TransportTypeFromTransport( + const utils::custom_string::CustomString& transport) const { + std::string transport_type; + + if (transport.CompareIgnoreCase("IAP_BLUETOOTH") || + transport.CompareIgnoreCase("SPP_BLUETOOTH")) { + transport_type = "Bluetooth"; + } else if (transport.CompareIgnoreCase("IAP_USB") || + transport.CompareIgnoreCase("AOA_USB") || + transport.CompareIgnoreCase("IAP_USB_HOST_MODE") || + transport.CompareIgnoreCase("IAP_USB_DEVICE_MODE")) { + transport_type = "USB"; + } else if (transport.CompareIgnoreCase("TCP_WIFI") || + transport.CompareIgnoreCase("IAP_CARPLAY")) { + transport_type = "WiFi"; + } + + return transport_type; +} + } // namespace protocol_handler diff --git a/src/components/protocol_handler/src/protocol_packet.cc b/src/components/protocol_handler/src/protocol_packet.cc index ae52849de6..3cd9e7f781 100644 --- a/src/components/protocol_handler/src/protocol_packet.cc +++ b/src/components/protocol_handler/src/protocol_packet.cc @@ -304,8 +304,8 @@ RESULT_CODE ProtocolPacket::ProtocolHeaderValidator::validate( // Check frame info for each frame type // Frame type shall be 0x00 (Control), 0x01 (Single), 0x02 (First), 0x03 // (Consecutive) - // For Control frames Frame info value shall be from 0x00 to 0x06 or 0xFE(Data - // Ack), 0xFF(HB Ack) + // For Control frames Frame info value shall be from 0x00 to 0x09 or + // 0xFD(Transport Event Update), 0xFE(Data Ack), 0xFF(HB Ack) // For Single and First frames Frame info value shall be equal 0x00 switch (header.frameType) { case FRAME_TYPE_CONTROL: { @@ -317,6 +317,10 @@ RESULT_CODE ProtocolPacket::ProtocolHeaderValidator::validate( case FRAME_DATA_END_SERVICE: case FRAME_DATA_END_SERVICE_ACK: case FRAME_DATA_END_SERVICE_NACK: + case FRAME_DATA_REGISTER_SECONDARY_TRANSPORT: + case FRAME_DATA_REGISTER_SECONDARY_TRANSPORT_ACK: + case FRAME_DATA_REGISTER_SECONDARY_TRANSPORT_NACK: + case FRAME_DATA_TRANSPORT_EVENT_UPDATE: case FRAME_DATA_SERVICE_DATA_ACK: case FRAME_DATA_HEART_BEAT_ACK: break; @@ -520,6 +524,17 @@ bool ProtocolPacket::operator==(const ProtocolPacket& other) const { return false; } +void ProtocolPacket::HandleRawFirstFrameData(const uint8_t* message) { + LOG4CXX_AUTO_TRACE(logger_); + payload_size_ = 0; + const uint8_t* data = message; + uint32_t total_data_bytes = data[0] << 24; + total_data_bytes |= data[1] << 16; + total_data_bytes |= data[2] << 8; + total_data_bytes |= data[3]; + set_total_data_bytes(total_data_bytes); +} + RESULT_CODE ProtocolPacket::deserializePacket(const uint8_t* message, const size_t messageSize) { LOG4CXX_AUTO_TRACE(logger_); @@ -532,18 +547,15 @@ RESULT_CODE ProtocolPacket::deserializePacket(const uint8_t* message, packet_data_.totalDataBytes = packet_header_.dataSize; uint32_t dataPayloadSize = 0; - if ((offset < messageSize) && packet_header_.frameType != FRAME_TYPE_FIRST) { + if ((offset < messageSize)) { dataPayloadSize = messageSize - offset; } - if (packet_header_.frameType == FRAME_TYPE_FIRST) { + if (packet_header_.frameType == FRAME_TYPE_FIRST && + !packet_header_.protection_flag) { payload_size_ = 0; const uint8_t* data = message + offset; - uint32_t total_data_bytes = data[0] << 24; - total_data_bytes |= data[1] << 16; - total_data_bytes |= data[2] << 8; - total_data_bytes |= data[3]; - set_total_data_bytes(total_data_bytes); + HandleRawFirstFrameData(data); if (0 == packet_data_.data) { return RESULT_FAIL; } @@ -602,6 +614,8 @@ uint8_t* ProtocolPacket::data() const { } void ProtocolPacket::set_total_data_bytes(size_t dataBytes) { + LOG4CXX_AUTO_TRACE(logger_); + LOG4CXX_DEBUG(logger_, "Data bytes : " << dataBytes); if (dataBytes) { delete[] packet_data_.data; packet_data_.data = new (std::nothrow) uint8_t[dataBytes]; @@ -632,6 +646,10 @@ ConnectionID ProtocolPacket::connection_id() const { return connection_id_; } +void ProtocolPacket::set_connection_id(ConnectionID connection_id) { + connection_id_ = connection_id; +} + uint32_t ProtocolPacket::payload_size() const { return payload_size_; } diff --git a/src/components/protocol_handler/test/incoming_data_handler_test.cc b/src/components/protocol_handler/test/incoming_data_handler_test.cc index d0a311583c..393579927c 100644 --- a/src/components/protocol_handler/test/incoming_data_handler_test.cc +++ b/src/components/protocol_handler/test/incoming_data_handler_test.cc @@ -393,13 +393,13 @@ TEST_F(IncomingDataHandlerTest, MalformedPacket_FrameType) { } } -// For Control frames Frame info value shall be from 0x00 to 0x06 or 0xFE(Data -// Ack), 0xFF(HB Ack) +// For Control frames Frame info value shall be from 0x00 to 0x09 or 0xFD +// (Transport Update Event), 0xFE(Data Ack), 0xFF(HB Ack) TEST_F(IncomingDataHandlerTest, MalformedPacket_ControlFrame) { FrameList malformed_packets; std::vector<uint8_t> malformed_frame_data; - for (uint8_t frame_type = FRAME_DATA_END_SERVICE_NACK + 1; - frame_type < FRAME_DATA_SERVICE_DATA_ACK; + for (uint8_t frame_type = FRAME_DATA_REGISTER_SECONDARY_TRANSPORT_NACK + 1; + frame_type < FRAME_DATA_TRANSPORT_EVENT_UPDATE; ++frame_type) { malformed_frame_data.push_back(frame_type); } diff --git a/src/components/protocol_handler/test/protocol_handler_tm_test.cc b/src/components/protocol_handler/test/protocol_handler_tm_test.cc index 77de1705da..2e06cd702e 100644 --- a/src/components/protocol_handler/test/protocol_handler_tm_test.cc +++ b/src/components/protocol_handler/test/protocol_handler_tm_test.cc @@ -41,15 +41,26 @@ #include "protocol_handler/mock_protocol_handler_settings.h" #include "protocol_handler/mock_session_observer.h" #include "connection_handler/mock_connection_handler.h" +#include "connection_handler/connection_handler_impl.h" #ifdef ENABLE_SECURITY #include "security_manager/mock_security_manager.h" #include "security_manager/mock_ssl_context.h" #endif // ENABLE_SECURITY #include "transport_manager/mock_transport_manager.h" +#include "utils/mock_system_time_handler.h" #include "utils/make_shared.h" #include "utils/test_async_waiter.h" #include <bson_object.h> +namespace transport_manager { +namespace transport_adapter { +// taken from transport_adapter_impl.cc +const char* tc_enabled = "enabled"; +const char* tc_tcp_port = "tcp_port"; +const char* tc_tcp_ip_address = "tcp_ip_address"; +} +} + namespace test { namespace components { namespace protocol_handler_test { @@ -87,6 +98,10 @@ using protocol_handler::FRAME_DATA_SERVICE_DATA_ACK; using protocol_handler::FRAME_DATA_SINGLE; using protocol_handler::FRAME_DATA_FIRST; using protocol_handler::FRAME_DATA_LAST_CONSECUTIVE; +using protocol_handler::FRAME_DATA_REGISTER_SECONDARY_TRANSPORT; +using protocol_handler::FRAME_DATA_REGISTER_SECONDARY_TRANSPORT_ACK; +using protocol_handler::FRAME_DATA_REGISTER_SECONDARY_TRANSPORT_NACK; +using protocol_handler::FRAME_DATA_TRANSPORT_EVENT_UPDATE; using protocol_handler::kRpc; using protocol_handler::kControl; using protocol_handler::kAudio; @@ -95,16 +110,24 @@ using protocol_handler::kBulk; using protocol_handler::kInvalidServiceType; // For TM states using transport_manager::TransportManagerListener; +using test::components::security_manager_test::MockSystemTimeHandler; using transport_manager::E_SUCCESS; using transport_manager::DeviceInfo; +#ifdef ENABLE_SECURITY +// For security +using ContextCreationStrategy = + security_manager::SecurityManager::ContextCreationStrategy; +#endif // ENABLE_SECURITY // For CH entities using connection_handler::DeviceHandle; // Google Testing Framework Entities using ::testing::Return; +using ::testing::ReturnRef; using ::testing::ReturnRefOfCopy; using ::testing::ReturnNull; using ::testing::An; using ::testing::AnyOf; +using ::testing::AtLeast; using ::testing::ByRef; using ::testing::DoAll; using ::testing::SaveArg; @@ -183,6 +206,9 @@ class ProtocolHandlerImplTest : public ::testing::Test { . // Return false to avoid call KeepConnectionAlive WillRepeatedly(Return(false)); + + session_connection_map_lock_ptr_ = + std::make_shared<sync_primitives::Lock>(); } void TearDown() OVERRIDE { @@ -209,6 +235,7 @@ class ProtocolHandlerImplTest : public ::testing::Test { const uint32_t hash_id, const bool protection_flag) { return protocol_handler::SessionContext(connection_id, + connection_id, initial_session_id, new_session_id, service_type, @@ -313,10 +340,11 @@ class ProtocolHandlerImplTest : public ::testing::Test { uint8_t service_type, uint8_t sessionId, uint32_t frame_data, + uint8_t protocol_version = PROTOCOL_VERSION_3, uint32_t dataSize = 0u, const uint8_t* data = NULL) { SendTMMessage(connection_id, - PROTOCOL_VERSION_3, + protocol_version, protection, FRAME_TYPE_CONTROL, service_type, @@ -327,6 +355,18 @@ class ProtocolHandlerImplTest : public ::testing::Test { data); } + void VerifySecondaryTransportParamsInStartSessionAck( + bool config_multiple_transports_enabled, + const std::vector<std::string>& config_secondary_transports_for_usb, + const std::vector<std::string>& config_secondary_transports_for_bluetooth, + const std::vector<std::string>& config_secondary_transports_for_wifi, + const std::vector<std::string>& config_audio_service_transports, + const std::vector<std::string>& config_video_service_transports, + const std::string& connection_type_string, + const std::vector<std::string>& expected_transport_strings, + const std::vector<int32_t>& expected_audio_service_transports, + const std::vector<int32_t>& expected_video_service_transports); + testing::NiceMock<MockProtocolHandlerSettings> protocol_handler_settings_mock; ::utils::SharedPtr<ProtocolHandlerImpl> protocol_handler_impl; TransportManagerListener* tm_listener; @@ -353,6 +393,10 @@ class ProtocolHandlerImplTest : public ::testing::Test { std::vector<int> force_unprotected_services; #endif // ENABLE_SECURITY std::vector<std::string> empty_rejected_param_; + // Used by OnTransportConfigUpdated() tests. The lifetime of these objects + // should be longer than that of a test case. + connection_handler::SessionConnectionMap session_connection_map_; + std::shared_ptr<sync_primitives::Lock> session_connection_map_lock_ptr_; }; #ifdef ENABLE_SECURITY @@ -618,6 +662,18 @@ TEST_F(ProtocolHandlerImplTest, const ::transport_manager::ConnectionUID connection_id2 = 0xBu; const uint8_t session_id2 = 2u; +#ifdef ENABLE_SECURITY + AddSecurityManager(); + + EXPECT_CALL(session_observer_mock, KeyFromPair(connection_id2, session_id2)) + .WillOnce(Return(connection_key)); + + EXPECT_CALL(session_observer_mock, + GetSSLContext(connection_key, start_service)) + .Times(2) + .WillRepeatedly(ReturnNull()); +#endif // ENABLE_SECURITY + EXPECT_CALL(session_observer_mock, IsHeartBeatSupported(connection_id1, _)) .WillRepeatedly(Return(false)); EXPECT_CALL(session_observer_mock, IsHeartBeatSupported(connection_id2, _)) @@ -987,7 +1043,10 @@ TEST_F(ProtocolHandlerImplTest, SecurityEnable_StartSessionProtected_Fail) { SetProtocolVersion2(); // Expect start protection for unprotected session - EXPECT_CALL(security_manager_mock, CreateSSLContext(connection_key)) + EXPECT_CALL(security_manager_mock, + CreateSSLContext(connection_key, + security_manager::SecurityManager:: + ContextCreationStrategy::kUseExisting)) . // Return fail protection WillOnce(DoAll(NotifyTestAsyncWaiter(&waiter), ReturnNull())); @@ -1042,7 +1101,7 @@ TEST_F(ProtocolHandlerImplTest, SetProtocolVersion2(); // call new SSLContext creation - EXPECT_CALL(security_manager_mock, CreateSSLContext(connection_key)) + EXPECT_CALL(security_manager_mock, CreateSSLContext(connection_key, _)) . // Return new SSLContext WillOnce( @@ -1119,7 +1178,7 @@ TEST_F(ProtocolHandlerImplTest, .WillOnce(ReturnRefOfCopy(services)); // call new SSLContext creation - EXPECT_CALL(security_manager_mock, CreateSSLContext(connection_key)) + EXPECT_CALL(security_manager_mock, CreateSSLContext(connection_key, _)) . // Return new SSLContext WillOnce(Return(&ssl_context_mock)); @@ -1198,7 +1257,7 @@ TEST_F(ProtocolHandlerImplTest, times++; // call new SSLContext creation - EXPECT_CALL(security_manager_mock, CreateSSLContext(connection_key)) + EXPECT_CALL(security_manager_mock, CreateSSLContext(connection_key, _)) . // Return new SSLContext WillOnce( @@ -1296,7 +1355,7 @@ TEST_F( times++; // call new SSLContext creation - EXPECT_CALL(security_manager_mock, CreateSSLContext(connection_key)) + EXPECT_CALL(security_manager_mock, CreateSSLContext(connection_key, _)) . // Return new SSLContext WillOnce( @@ -1392,7 +1451,10 @@ TEST_F(ProtocolHandlerImplTest, times++; // call new SSLContext creation - EXPECT_CALL(security_manager_mock, CreateSSLContext(connection_key)) + EXPECT_CALL(security_manager_mock, + CreateSSLContext(connection_key, + security_manager::SecurityManager:: + ContextCreationStrategy::kUseExisting)) . // Return new SSLContext WillOnce( @@ -1420,27 +1482,37 @@ TEST_F(ProtocolHandlerImplTest, // Expect add listener for handshake result EXPECT_CALL(security_manager_mock, AddListener(_)) - // Emulate handshake fail - .WillOnce(Invoke(OnHandshakeDoneFunctor( - connection_key, - security_manager::SSLContext::Handshake_Result_Success))); + // Emulate handshake + .WillOnce( + DoAll(NotifyTestAsyncWaiter(&waiter), + Invoke(OnHandshakeDoneFunctor( + connection_key, + security_manager::SSLContext::Handshake_Result_Success)))); + times++; // Listener check SSLContext EXPECT_CALL(session_observer_mock, GetSSLContext(connection_key, start_service)) . // Emulate protection for service is not enabled - WillOnce(ReturnNull()); + WillOnce(DoAll(NotifyTestAsyncWaiter(&waiter), ReturnNull())); + times++; + + EXPECT_CALL(security_manager_mock, IsSystemTimeProviderReady()) + .WillOnce(DoAll(NotifyTestAsyncWaiter(&waiter), Return(true))); + times++; - // Expect service protection enable EXPECT_CALL(session_observer_mock, - SetProtectionFlag(connection_key, start_service)); + SetProtectionFlag(connection_key, start_service)) + .WillOnce(NotifyTestAsyncWaiter(&waiter)); + times++; - // Expect send Ack with PROTECTION_OFF (on fail handshake) + // Expect send Ack with PROTECTION_ON (on successfull handshake) EXPECT_CALL(transport_manager_mock, SendMessageToDevice( ControlMessage(FRAME_DATA_START_SERVICE_ACK, PROTECTION_ON))) .WillOnce(DoAll(NotifyTestAsyncWaiter(&waiter), Return(E_SUCCESS))); + times++; SendControlMessage( @@ -1450,6 +1522,1131 @@ TEST_F(ProtocolHandlerImplTest, } #endif // ENABLE_SECURITY +void ProtocolHandlerImplTest::VerifySecondaryTransportParamsInStartSessionAck( + bool config_multiple_transports_enabled, + const std::vector<std::string>& config_secondary_transports_for_usb, + const std::vector<std::string>& config_secondary_transports_for_bluetooth, + const std::vector<std::string>& config_secondary_transports_for_wifi, + const std::vector<std::string>& config_audio_service_transports, + const std::vector<std::string>& config_video_service_transports, + const std::string& connection_type_string, + const std::vector<std::string>& expected_transport_strings, + const std::vector<int32_t>& expected_audio_service_transports, + const std::vector<int32_t>& expected_video_service_transports) { + const size_t maximum_rpc_payload_size = 1500; + EXPECT_CALL(protocol_handler_settings_mock, maximum_rpc_payload_size()) + .WillRepeatedly(Return(maximum_rpc_payload_size)); + InitProtocolHandlerImpl(0u, 0u); + + TestAsyncWaiter waiter; + uint32_t times = 0; + + const uint8_t input_protocol_version = 5; + const uint32_t hash_id = 123456; + ProtocolPacket::ProtocolVersion full_version(5, 1, 0); + char full_version_string[] = "5.1.0"; + + // configuration setup + EXPECT_CALL(protocol_handler_settings_mock, max_supported_protocol_version()) + .WillRepeatedly(Return(PROTOCOL_VERSION_5)); + EXPECT_CALL(protocol_handler_settings_mock, multiple_transports_enabled()) + .WillRepeatedly(Return(config_multiple_transports_enabled)); + EXPECT_CALL(protocol_handler_settings_mock, secondary_transports_for_usb()) + .Times(AtLeast(0)) + .WillRepeatedly(ReturnRef(config_secondary_transports_for_usb)); + EXPECT_CALL(protocol_handler_settings_mock, + secondary_transports_for_bluetooth()) + .Times(AtLeast(0)) + .WillRepeatedly(ReturnRef(config_secondary_transports_for_bluetooth)); + EXPECT_CALL(protocol_handler_settings_mock, secondary_transports_for_wifi()) + .Times(AtLeast(0)) + .WillRepeatedly(ReturnRef(config_secondary_transports_for_wifi)); + EXPECT_CALL(protocol_handler_settings_mock, audio_service_transports()) + .WillOnce(ReturnRef(config_audio_service_transports)); + EXPECT_CALL(protocol_handler_settings_mock, video_service_transports()) + .WillOnce(ReturnRef(config_video_service_transports)); + + EXPECT_CALL(session_observer_mock, + TransportTypeProfileStringFromConnHandle(connection_id)) + .WillRepeatedly(Return(connection_type_string)); + + // Prepare expected BSON parameters. When we add another param in Start + // Service ACK frame in future, it should be also added here. + BsonObject expected_obj; + bson_object_initialize_default(&expected_obj); + // mtu + bson_object_put_int64(&expected_obj, + protocol_handler::strings::mtu, + static_cast<int64_t>(maximum_rpc_payload_size)); + // hashId + bson_object_put_int32(&expected_obj, + protocol_handler::strings::hash_id, + static_cast<int32_t>(hash_id)); + // protocolVersion + bson_object_put_string(&expected_obj, + protocol_handler::strings::protocol_version, + full_version_string); + // secondaryTransports + BsonArray secondary_transports; + bson_array_initialize(&secondary_transports, + expected_transport_strings.size()); + for (std::vector<std::string>::const_iterator it = + expected_transport_strings.begin(); + it != expected_transport_strings.end(); + ++it) { + // note: if there is no transport allowed, we can either make the array + // empty, or completely omit the array. (The spec allows both cases.) In + // this test case we make the array empty. + bson_array_add_string(&secondary_transports, + const_cast<char*>(it->c_str())); + } + bson_object_put_array(&expected_obj, + protocol_handler::strings::secondary_transports, + &secondary_transports); + // audioServiceTransports + BsonArray audio_service_transports; + if (expected_audio_service_transports.size() > 0) { + bson_array_initialize(&audio_service_transports, + expected_audio_service_transports.size()); + for (std::vector<int32_t>::const_iterator it = + expected_audio_service_transports.begin(); + it != expected_audio_service_transports.end(); + ++it) { + bson_array_add_int32(&audio_service_transports, *it); + } + bson_object_put_array(&expected_obj, + protocol_handler::strings::audio_service_transports, + &audio_service_transports); + } + // videoServiceTransports + BsonArray video_service_transports; + if (expected_video_service_transports.size() > 0) { + bson_array_initialize(&video_service_transports, + expected_video_service_transports.size()); + for (std::vector<int32_t>::const_iterator it = + expected_video_service_transports.begin(); + it != expected_video_service_transports.end(); + ++it) { + bson_array_add_int32(&video_service_transports, *it); + } + bson_object_put_array(&expected_obj, + protocol_handler::strings::video_service_transports, + &video_service_transports); + } + + std::vector<uint8_t> expected_param = + CreateVectorFromBsonObject(&expected_obj); + + bson_object_deinitialize(&expected_obj); + + EXPECT_CALL(transport_manager_mock, + SendMessageToDevice(ControlMessage(FRAME_DATA_START_SERVICE_ACK, + PROTECTION_OFF, + connection_id, + Eq(expected_param)))) + .WillOnce(DoAll(NotifyTestAsyncWaiter(&waiter), Return(E_SUCCESS))); + times++; + +#ifdef ENABLE_SECURITY + AddSecurityManager(); + + EXPECT_CALL(session_observer_mock, KeyFromPair(connection_id, session_id)) + .WillOnce(Return(connection_key)); + + EXPECT_CALL(session_observer_mock, GetSSLContext(connection_key, kRpc)) + .WillOnce(ReturnNull()); +#endif // ENABLE_SECURITY + + protocol_handler_impl->SendStartSessionAck(connection_id, + session_id, + input_protocol_version, + hash_id, + protocol_handler::SERVICE_TYPE_RPC, + false /* protection */, + full_version); + + EXPECT_TRUE(waiter.WaitFor(times, kAsyncExpectationsTimeout)); +} + +TEST_F(ProtocolHandlerImplTest, + StartSessionAck_SecondaryTransportParams_Enabled) { + // config allows secondary transport only when connected through Bluetooth, + // and the secondary is Wi-Fi + std::vector<std::string> secondary_transports_for_usb; // empty + std::vector<std::string> secondary_transports_for_bluetooth; + secondary_transports_for_bluetooth.push_back("WiFi"); + std::vector<std::string> secondary_transports_for_wifi; // empty + // config allows video and audio services to run on all transports except + // Bluetooth + std::vector<std::string> audio_service_transports; + audio_service_transports.push_back("IAP_USB"); + audio_service_transports.push_back("IAP_USB_HOST_MODE"); + audio_service_transports.push_back("IAP_USB_DEVICE_MODE"); + audio_service_transports.push_back("IAP_CARPLAY"); + audio_service_transports.push_back("AOA_USB"); + audio_service_transports.push_back("TCP_WIFI"); + std::vector<std::string> video_service_transports; + video_service_transports.push_back("IAP_USB"); + video_service_transports.push_back("IAP_USB_HOST_MODE"); + video_service_transports.push_back("IAP_USB_DEVICE_MODE"); + video_service_transports.push_back("IAP_CARPLAY"); + video_service_transports.push_back("AOA_USB"); + video_service_transports.push_back("TCP_WIFI"); + + // assume the device is Android and is connected through Bluetooth SPP + std::string connection_type_string("SPP_BLUETOOTH"); + + // Core should specify WiFi for secondary transport, and should allow video + // and audio services only on secondary transport + std::vector<std::string> expected_transport_strings; + expected_transport_strings.push_back("TCP_WIFI"); + std::vector<int32_t> expected_audio_service_transports; + expected_audio_service_transports.push_back(2); + std::vector<int32_t> expected_video_service_transports; + expected_video_service_transports.push_back(2); + + // A TransportUpdateEvent is also issued after Start Service ACK. We don't + // check it in this test case. + EXPECT_CALL(session_observer_mock, ProtocolVersionUsed(_, _, _)) + .WillRepeatedly(Return(false)); + + VerifySecondaryTransportParamsInStartSessionAck( + true, + secondary_transports_for_usb, + secondary_transports_for_bluetooth, + secondary_transports_for_wifi, + audio_service_transports, + video_service_transports, + connection_type_string, + expected_transport_strings, + expected_audio_service_transports, + expected_video_service_transports); +} + +TEST_F(ProtocolHandlerImplTest, + StartSessionAck_SecondaryTransportParams_NoSecondaryTransport) { + // config allows secondary transport only when connected through Bluetooth, + // and the secondary is Wi-Fi + std::vector<std::string> secondary_transports_for_usb; // empty + std::vector<std::string> secondary_transports_for_bluetooth; + secondary_transports_for_bluetooth.push_back("WiFi"); + std::vector<std::string> secondary_transports_for_wifi; // empty + // config allows video and audio services to run on all transports except + // Bluetooth + std::vector<std::string> audio_service_transports; + audio_service_transports.push_back("IAP_USB"); + audio_service_transports.push_back("IAP_USB_HOST_MODE"); + audio_service_transports.push_back("IAP_USB_DEVICE_MODE"); + audio_service_transports.push_back("IAP_CARPLAY"); + audio_service_transports.push_back("AOA_USB"); + audio_service_transports.push_back("TCP_WIFI"); + std::vector<std::string> video_service_transports; + video_service_transports.push_back("IAP_USB"); + video_service_transports.push_back("IAP_USB_HOST_MODE"); + video_service_transports.push_back("IAP_USB_DEVICE_MODE"); + video_service_transports.push_back("IAP_CARPLAY"); + video_service_transports.push_back("AOA_USB"); + video_service_transports.push_back("TCP_WIFI"); + + // assume the device is iOS and is connected through iAP over USB + std::string connection_type_string("IAP_USB"); + + // Core should not offer any secondary transport. It will allow both video + // and audio services on primary transport. + std::vector<std::string> expected_transport_strings; // empty + std::vector<int32_t> expected_audio_service_transports; + expected_audio_service_transports.push_back(1); + std::vector<int32_t> expected_video_service_transports; + expected_video_service_transports.push_back(1); + + EXPECT_CALL(session_observer_mock, ProtocolVersionUsed(_, _, _)) + .WillRepeatedly(Return(false)); + + VerifySecondaryTransportParamsInStartSessionAck( + true, + secondary_transports_for_usb, + secondary_transports_for_bluetooth, + secondary_transports_for_wifi, + audio_service_transports, + video_service_transports, + connection_type_string, + expected_transport_strings, + expected_audio_service_transports, + expected_video_service_transports); +} + +TEST_F(ProtocolHandlerImplTest, + StartSessionAck_SecondaryTransportParams_MultipleSecondaryTransports) { + // config allows secondary transport only when connected through Bluetooth, + // and the secondary is Wi-Fi and USB + std::vector<std::string> secondary_transports_for_usb; // empty + std::vector<std::string> secondary_transports_for_bluetooth; + secondary_transports_for_bluetooth.push_back("WiFi"); + secondary_transports_for_bluetooth.push_back("USB"); + std::vector<std::string> secondary_transports_for_wifi; // empty + // config allows video and audio services to run on all transports except + // Bluetooth + std::vector<std::string> audio_service_transports; + audio_service_transports.push_back("IAP_USB"); + audio_service_transports.push_back("IAP_USB_HOST_MODE"); + audio_service_transports.push_back("IAP_USB_DEVICE_MODE"); + audio_service_transports.push_back("IAP_CARPLAY"); + audio_service_transports.push_back("AOA_USB"); + audio_service_transports.push_back("TCP_WIFI"); + std::vector<std::string> video_service_transports; + video_service_transports.push_back("IAP_USB"); + video_service_transports.push_back("IAP_USB_HOST_MODE"); + video_service_transports.push_back("IAP_USB_DEVICE_MODE"); + video_service_transports.push_back("IAP_CARPLAY"); + video_service_transports.push_back("AOA_USB"); + video_service_transports.push_back("TCP_WIFI"); + + // assume the device is iOS and is connected through iAP over Bluetooth + std::string connection_type_string("IAP_BLUETOOTH"); + + // Core should offer both Wi-Fi and USB for secondary transport. Since the + // device is iOS, Core should specify "IAP_USB". + std::vector<std::string> expected_transport_strings; + expected_transport_strings.push_back("TCP_WIFI"); + expected_transport_strings.push_back("IAP_USB"); + std::vector<int32_t> expected_audio_service_transports; + expected_audio_service_transports.push_back(2); + std::vector<int32_t> expected_video_service_transports; + expected_video_service_transports.push_back(2); + + EXPECT_CALL(session_observer_mock, ProtocolVersionUsed(_, _, _)) + .WillRepeatedly(Return(false)); + + VerifySecondaryTransportParamsInStartSessionAck( + true, + secondary_transports_for_usb, + secondary_transports_for_bluetooth, + secondary_transports_for_wifi, + audio_service_transports, + video_service_transports, + connection_type_string, + expected_transport_strings, + expected_audio_service_transports, + expected_video_service_transports); +} + +TEST_F( + ProtocolHandlerImplTest, + StartSessionAck_SecondaryTransportParams_ServiceAllowedOnBothTransports) { + std::vector<std::string> secondary_transports_for_usb; + secondary_transports_for_usb.push_back("WiFi"); + std::vector<std::string> secondary_transports_for_bluetooth; + secondary_transports_for_bluetooth.push_back("USB"); + std::vector<std::string> secondary_transports_for_wifi; // empty + // config allows video service to run on Wi-Fi transports only, and audio + // service to run on all transports + std::vector<std::string> audio_service_transports; + audio_service_transports.push_back("IAP_BLUETOOTH"); + audio_service_transports.push_back("IAP_USB"); + audio_service_transports.push_back("IAP_USB_HOST_MODE"); + audio_service_transports.push_back("IAP_USB_DEVICE_MODE"); + audio_service_transports.push_back("IAP_CARPLAY"); + audio_service_transports.push_back("SPP_BLUETOOTH"); + audio_service_transports.push_back("AOA_USB"); + audio_service_transports.push_back("TCP_WIFI"); + std::vector<std::string> video_service_transports; + video_service_transports.push_back("IAP_CARPLAY"); + video_service_transports.push_back("TCP_WIFI"); + + // assume the device is Android and is connected through AOA + std::string connection_type_string("AOA_USB"); + + // Core should offer Wi-Fi for secondary transport. It should allow audio + // service to run on both primary and secondary, while video service to run + // on secondary only. Since the list specifies AOA_USB then TCP_WIFI, the + // priority is primary > secondary. + std::vector<std::string> expected_transport_strings; + expected_transport_strings.push_back("TCP_WIFI"); + std::vector<int32_t> expected_audio_service_transports; + expected_audio_service_transports.push_back(1); // primary preferred + expected_audio_service_transports.push_back(2); + std::vector<int32_t> expected_video_service_transports; + expected_video_service_transports.push_back(2); + + EXPECT_CALL(session_observer_mock, ProtocolVersionUsed(_, _, _)) + .WillRepeatedly(Return(false)); + + VerifySecondaryTransportParamsInStartSessionAck( + true, + secondary_transports_for_usb, + secondary_transports_for_bluetooth, + secondary_transports_for_wifi, + audio_service_transports, + video_service_transports, + connection_type_string, + expected_transport_strings, + expected_audio_service_transports, + expected_video_service_transports); +} + +TEST_F(ProtocolHandlerImplTest, + StartSessionAck_SecondaryTransportParams_SecondaryDisabled) { + std::vector<std::string> secondary_transports_for_usb; // empty + std::vector<std::string> secondary_transports_for_bluetooth; // empty + std::vector<std::string> secondary_transports_for_wifi; // empty + // config allows video and audio services to run on all transports + std::vector<std::string> audio_service_transports; + audio_service_transports.push_back("IAP_BLUETOOTH"); + audio_service_transports.push_back("IAP_USB"); + audio_service_transports.push_back("IAP_USB_HOST_MODE"); + audio_service_transports.push_back("IAP_USB_DEVICE_MODE"); + audio_service_transports.push_back("IAP_CARPLAY"); + audio_service_transports.push_back("SPP_BLUETOOTH"); + audio_service_transports.push_back("AOA_USB"); + audio_service_transports.push_back("TCP_WIFI"); + std::vector<std::string> video_service_transports; + video_service_transports.push_back("IAP_BLUETOOTH"); + video_service_transports.push_back("IAP_USB"); + video_service_transports.push_back("IAP_USB_HOST_MODE"); + video_service_transports.push_back("IAP_USB_DEVICE_MODE"); + video_service_transports.push_back("IAP_CARPLAY"); + video_service_transports.push_back("SPP_BLUETOOTH"); + video_service_transports.push_back("AOA_USB"); + video_service_transports.push_back("TCP_WIFI"); + + // assume the device is iOS and is connected through iAP over Bluetooth + std::string connection_type_string("IAP_BLUETOOTH"); + + // Core should not offer any secondary transport. It should still send + // the video/audio service transport lists. + std::vector<std::string> expected_transport_strings; // empty + std::vector<int32_t> expected_audio_service_transports; + expected_audio_service_transports.push_back(1); + std::vector<int32_t> expected_video_service_transports; + expected_video_service_transports.push_back(1); + + connection_handler::SessionTransports dummy_st = {0, 0}; + EXPECT_CALL(connection_handler_mock, + SetSecondaryTransportID(_, kDisabledSecondary)) + .WillOnce(Return(dummy_st)); + + EXPECT_CALL(session_observer_mock, ProtocolVersionUsed(_, _, _)) + .WillRepeatedly(Return(false)); + + VerifySecondaryTransportParamsInStartSessionAck( + false, /* disabled */ + secondary_transports_for_usb, + secondary_transports_for_bluetooth, + secondary_transports_for_wifi, + audio_service_transports, + video_service_transports, + connection_type_string, + expected_transport_strings, + expected_audio_service_transports, + expected_video_service_transports); +} + +TEST_F(ProtocolHandlerImplTest, + StartSessionAck_SecondaryTransportParams_ServicesMapEmpty) { + std::vector<std::string> secondary_transports_for_usb; // empty + std::vector<std::string> secondary_transports_for_bluetooth; + secondary_transports_for_bluetooth.push_back("USB"); + std::vector<std::string> secondary_transports_for_wifi; + secondary_transports_for_wifi.push_back("USB"); + // config does not specify video and audio services + std::vector<std::string> audio_service_transports; // empty + std::vector<std::string> video_service_transports; // empty + + // assume the device is connected through Wi-Fi (so not sure if it's iOS or + // Android) + std::string connection_type_string("TCP_WIFI"); + + // Core should offer USB transport for secondary transport. (Since the OS type + // is unknown, it will offer both IAP_USB and AOA_USB.) Also, it should allow + // video/audio services on all transports. + std::vector<std::string> expected_transport_strings; + expected_transport_strings.push_back("IAP_USB"); + expected_transport_strings.push_back("AOA_USB"); + std::vector<int32_t> expected_audio_service_transports; + expected_audio_service_transports.push_back(1); + expected_audio_service_transports.push_back(2); + std::vector<int32_t> expected_video_service_transports; + expected_video_service_transports.push_back(1); + expected_video_service_transports.push_back(2); + + EXPECT_CALL(session_observer_mock, ProtocolVersionUsed(_, _, _)) + .WillRepeatedly(Return(false)); + + VerifySecondaryTransportParamsInStartSessionAck( + true, + secondary_transports_for_usb, + secondary_transports_for_bluetooth, + secondary_transports_for_wifi, + audio_service_transports, + video_service_transports, + connection_type_string, + expected_transport_strings, + expected_audio_service_transports, + expected_video_service_transports); +} + +TEST_F( + ProtocolHandlerImplTest, + StartSessionAck_SecondaryTransportParams_SecondaryDisabled_ServicesMapEmpty) { + std::vector<std::string> secondary_transports_for_usb; // empty + std::vector<std::string> secondary_transports_for_bluetooth; // empty + std::vector<std::string> secondary_transports_for_wifi; // empty + // config does not specify video and audio services + std::vector<std::string> audio_service_transports; // empty + std::vector<std::string> video_service_transports; // empty + + std::string connection_type_string("IAP_BLUETOOTH"); + + // Core should not offer any secondary transport. It should still send + // the video/audio service transport lists. + std::vector<std::string> expected_transport_strings; // empty + std::vector<int32_t> expected_audio_service_transports; + expected_audio_service_transports.push_back(1); + std::vector<int32_t> expected_video_service_transports; + expected_video_service_transports.push_back(1); + + connection_handler::SessionTransports dummy_st = {0, 0}; + EXPECT_CALL(connection_handler_mock, + SetSecondaryTransportID(_, kDisabledSecondary)) + .WillOnce(Return(dummy_st)); + + EXPECT_CALL(session_observer_mock, ProtocolVersionUsed(_, _, _)) + .WillRepeatedly(Return(false)); + + VerifySecondaryTransportParamsInStartSessionAck( + false, /* disabled */ + secondary_transports_for_usb, + secondary_transports_for_bluetooth, + secondary_transports_for_wifi, + audio_service_transports, + video_service_transports, + connection_type_string, + expected_transport_strings, + expected_audio_service_transports, + expected_video_service_transports); +} + +// Secondary transport param should not be included for apps with v5.0.0 +TEST_F(ProtocolHandlerImplTest, + StartSessionAck_Unprotected_NoSecondaryTransportParamsForV5) { + TestAsyncWaiter waiter; + uint32_t times = 0; + + const uint8_t input_protocol_version = 5; + const uint32_t hash_id = 123456; + ProtocolPacket::ProtocolVersion full_version(5, 0, 0); + char full_version_string[] = "5.0.0"; + + const size_t maximum_rpc_payload_size = 1500; + EXPECT_CALL(protocol_handler_settings_mock, maximum_rpc_payload_size()) + .WillRepeatedly(Return(maximum_rpc_payload_size)); + InitProtocolHandlerImpl(0u, 0u); + + // configuration + std::vector<std::string> config_secondary_transports_for_usb; // empty + std::vector<std::string> config_secondary_transports_for_bluetooth; + config_secondary_transports_for_bluetooth.push_back("USB"); + std::vector<std::string> config_secondary_transports_for_wifi; + config_secondary_transports_for_wifi.push_back("USB"); + + // assume the device is iOS and is connected through iAP over Bluetooth + std::string connection_type_string("IAP_BLUETOOTH"); + + // configuration setup + EXPECT_CALL(protocol_handler_settings_mock, max_supported_protocol_version()) + .WillRepeatedly(Return(PROTOCOL_VERSION_5)); + EXPECT_CALL(protocol_handler_settings_mock, multiple_transports_enabled()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(protocol_handler_settings_mock, secondary_transports_for_usb()) + .Times(AtLeast(0)) + .WillRepeatedly(ReturnRef(config_secondary_transports_for_usb)); + EXPECT_CALL(protocol_handler_settings_mock, + secondary_transports_for_bluetooth()) + .Times(AtLeast(0)) + .WillRepeatedly(ReturnRef(config_secondary_transports_for_bluetooth)); + EXPECT_CALL(protocol_handler_settings_mock, secondary_transports_for_wifi()) + .Times(AtLeast(0)) + .WillRepeatedly(ReturnRef(config_secondary_transports_for_wifi)); + + EXPECT_CALL(session_observer_mock, + TransportTypeProfileStringFromConnHandle(connection_id)) + .WillRepeatedly(Return(connection_type_string)); + + // BSON params should not include any of "secondaryTransports", + // "audioServiceTransports" and "videoServiceTransports" since v5.0.0 app + // does not understand them + BsonObject expected_obj; + bson_object_initialize_default(&expected_obj); + // mtu + bson_object_put_int64(&expected_obj, + protocol_handler::strings::mtu, + static_cast<int64_t>(maximum_rpc_payload_size)); + // hashId + bson_object_put_int32(&expected_obj, + protocol_handler::strings::hash_id, + static_cast<int32_t>(hash_id)); + // protocolVersion + bson_object_put_string(&expected_obj, + protocol_handler::strings::protocol_version, + full_version_string); + + std::vector<uint8_t> expected_param = + CreateVectorFromBsonObject(&expected_obj); + + bson_object_deinitialize(&expected_obj); + + EXPECT_CALL(transport_manager_mock, + SendMessageToDevice(ControlMessage(FRAME_DATA_START_SERVICE_ACK, + PROTECTION_OFF, + connection_id, + Eq(expected_param)))) + .WillOnce(DoAll(NotifyTestAsyncWaiter(&waiter), Return(E_SUCCESS))); + times++; + + connection_handler::SessionTransports dummy_st = {0, 0}; + EXPECT_CALL(connection_handler_mock, + SetSecondaryTransportID(_, kDisabledSecondary)) + .WillOnce(Return(dummy_st)); + + // Since the protocol version is less than 5.1.0, Core should not issue + // TransportEventUpdate frame. Enable ProtocolVersionUsed() call and verify + // that transport_manager_mock will NOT receive another SendMessageToDevice() + // call. + ON_CALL(session_observer_mock, ProtocolVersionUsed(_, _, _)) + .WillByDefault(Return(true)); + +#ifdef ENABLE_SECURITY + AddSecurityManager(); + + EXPECT_CALL(session_observer_mock, KeyFromPair(connection_id, session_id)) + .WillOnce(Return(connection_key)); + + EXPECT_CALL(session_observer_mock, GetSSLContext(connection_key, kRpc)) + .WillOnce(ReturnNull()); +#endif // ENABLE_SECURITY + + protocol_handler_impl->SendStartSessionAck(connection_id, + session_id, + input_protocol_version, + hash_id, + protocol_handler::SERVICE_TYPE_RPC, + false /* protection */, + full_version); + + EXPECT_TRUE(waiter.WaitFor(times, kAsyncExpectationsTimeout)); +} + +TEST_F(ProtocolHandlerImplTest, StartSessionAck_PrimaryTransportUSBHostMode) { + // config allows secondary transport only when connected through Bluetooth, + // and the secondary is Wi-Fi + std::vector<std::string> secondary_transports_for_usb; + secondary_transports_for_usb.push_back("WiFi"); + std::vector<std::string> secondary_transports_for_bluetooth; // empty + std::vector<std::string> secondary_transports_for_wifi; // empty + // config allows video and audio services to run on all transports except + // Bluetooth + std::vector<std::string> audio_service_transports; + audio_service_transports.push_back("IAP_USB_DEVICE_MODE"); + audio_service_transports.push_back("IAP_CARPLAY"); + audio_service_transports.push_back("AOA_USB"); + audio_service_transports.push_back("TCP_WIFI"); + std::vector<std::string> video_service_transports; + video_service_transports.push_back("IAP_USB"); + video_service_transports.push_back("IAP_CARPLAY"); + video_service_transports.push_back("AOA_USB"); + video_service_transports.push_back("TCP_WIFI"); + + // assume the device is IOS and is connected through USB Host Mode + std::string connection_type_string("IAP_USB_HOST_MODE"); + + // Core should specify WiFi for secondary transport, and should allow video + // services on both transports, and audio only on secondary transport + std::vector<std::string> expected_transport_strings; + expected_transport_strings.push_back("TCP_WIFI"); + std::vector<int32_t> expected_audio_service_transports; + expected_audio_service_transports.push_back(2); + std::vector<int32_t> expected_video_service_transports; + expected_video_service_transports.push_back(1); + expected_video_service_transports.push_back(2); + + // A TransportUpdateEvent is also issued after Start Service ACK. We don't + // check it in this test case. + EXPECT_CALL(session_observer_mock, ProtocolVersionUsed(_, _, _)) + .WillRepeatedly(Return(false)); + + VerifySecondaryTransportParamsInStartSessionAck( + true, + secondary_transports_for_usb, + secondary_transports_for_bluetooth, + secondary_transports_for_wifi, + audio_service_transports, + video_service_transports, + connection_type_string, + expected_transport_strings, + expected_audio_service_transports, + expected_video_service_transports); +} + +TEST_F(ProtocolHandlerImplTest, + TransportEventUpdate_afterVersionNegotiation_TCPEnabled) { + TestAsyncWaiter waiter; + uint32_t times = 0; + + const uint8_t input_protocol_version = 5; + const uint32_t hash_id = 123456; + ProtocolPacket::ProtocolVersion full_version(5, 1, 0); + + const size_t maximum_rpc_payload_size = 1500; + EXPECT_CALL(protocol_handler_settings_mock, maximum_rpc_payload_size()) + .WillRepeatedly(Return(maximum_rpc_payload_size)); + InitProtocolHandlerImpl(0u, 0u); + + // TCP configuration setup + bool tcp_enabled = true; + char tcp_address[] = "192.168.1.1"; + int32_t tcp_port = 12345; + std::string tcp_port_str = "12345"; + protocol_handler_impl->set_tcp_config( + tcp_enabled, std::string(tcp_address), tcp_port_str); + + // configuration setup + std::vector<std::string> config_secondary_transports_for_usb; // empty + std::vector<std::string> config_secondary_transports_for_bluetooth; + config_secondary_transports_for_bluetooth.push_back("WiFi"); + std::vector<std::string> config_secondary_transports_for_wifi; // empty + std::vector<std::string> config_audio_service_transports; + config_audio_service_transports.push_back("IAP_USB"); + config_audio_service_transports.push_back("IAP_USB_HOST_MODE"); + config_audio_service_transports.push_back("IAP_USB_DEVICE_MODE"); + config_audio_service_transports.push_back("IAP_CARPLAY"); + config_audio_service_transports.push_back("AOA_USB"); + config_audio_service_transports.push_back("TCP_WIFI"); + std::vector<std::string> config_video_service_transports; + config_video_service_transports.push_back("IAP_USB"); + config_video_service_transports.push_back("IAP_USB_HOST_MODE"); + config_video_service_transports.push_back("IAP_USB_DEVICE_MODE"); + config_video_service_transports.push_back("IAP_CARPLAY"); + config_video_service_transports.push_back("AOA_USB"); + config_video_service_transports.push_back("TCP_WIFI"); + + EXPECT_CALL(protocol_handler_settings_mock, max_supported_protocol_version()) + .WillRepeatedly(Return(PROTOCOL_VERSION_5)); + EXPECT_CALL(protocol_handler_settings_mock, multiple_transports_enabled()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(protocol_handler_settings_mock, secondary_transports_for_usb()) + .Times(AtLeast(0)) + .WillRepeatedly(ReturnRef(config_secondary_transports_for_usb)); + EXPECT_CALL(protocol_handler_settings_mock, + secondary_transports_for_bluetooth()) + .Times(AtLeast(0)) + .WillRepeatedly(ReturnRef(config_secondary_transports_for_bluetooth)); + EXPECT_CALL(protocol_handler_settings_mock, secondary_transports_for_wifi()) + .Times(AtLeast(0)) + .WillRepeatedly(ReturnRef(config_secondary_transports_for_wifi)); + EXPECT_CALL(protocol_handler_settings_mock, audio_service_transports()) + .WillOnce(ReturnRef(config_audio_service_transports)); + EXPECT_CALL(protocol_handler_settings_mock, video_service_transports()) + .WillOnce(ReturnRef(config_video_service_transports)); + + // assume the device is iOS and is connected through iAP over Bluetooth + std::string connection_type_string("IAP_BLUETOOTH"); + + EXPECT_CALL(session_observer_mock, + TransportTypeProfileStringFromConnHandle(connection_id)) + .WillRepeatedly(Return(connection_type_string)); + + EXPECT_CALL( + transport_manager_mock, + SendMessageToDevice(ControlMessage( + FRAME_DATA_START_SERVICE_ACK, PROTECTION_OFF, connection_id, _))) + .WillOnce(DoAll(NotifyTestAsyncWaiter(&waiter), Return(E_SUCCESS))); + times++; + + EXPECT_CALL(session_observer_mock, ProtocolVersionUsed(_, _, _)) + .WillRepeatedly( + DoAll(SetArgReferee<2>(PROTOCOL_VERSION_5), Return(true))); + + BsonObject expected_obj; + bson_object_initialize_default(&expected_obj); + // IP address + bson_object_put_string( + &expected_obj, protocol_handler::strings::tcp_ip_address, tcp_address); + // TCP port number + bson_object_put_int32( + &expected_obj, protocol_handler::strings::tcp_port, tcp_port); + + std::vector<uint8_t> expected_param = + CreateVectorFromBsonObject(&expected_obj); + + bson_object_deinitialize(&expected_obj); + + EXPECT_CALL( + transport_manager_mock, + SendMessageToDevice(ControlMessage(FRAME_DATA_TRANSPORT_EVENT_UPDATE, + PROTECTION_OFF, + connection_id, + Eq(expected_param)))) + .WillOnce(DoAll(NotifyTestAsyncWaiter(&waiter), Return(E_SUCCESS))); + times++; + +#ifdef ENABLE_SECURITY + AddSecurityManager(); + + EXPECT_CALL(session_observer_mock, KeyFromPair(connection_id, session_id)) + .WillOnce(Return(connection_key)); + + EXPECT_CALL(session_observer_mock, GetSSLContext(connection_key, kRpc)) + .WillOnce(ReturnNull()); +#endif // ENABLE_SECURITY + + protocol_handler_impl->SendStartSessionAck(connection_id, + session_id, + input_protocol_version, + hash_id, + protocol_handler::SERVICE_TYPE_RPC, + false /* protection */, + full_version); + + EXPECT_TRUE(waiter.WaitFor(times, kAsyncExpectationsTimeout)); +} + +TEST_F(ProtocolHandlerImplTest, + TransportEventUpdate_afterVersionNegotiation_TCPDisabled) { + TestAsyncWaiter waiter; + uint32_t times = 0; + + const uint8_t input_protocol_version = 5; + const uint32_t hash_id = 123456; + ProtocolPacket::ProtocolVersion full_version(5, 1, 0); + + const size_t maximum_rpc_payload_size = 1500; + EXPECT_CALL(protocol_handler_settings_mock, maximum_rpc_payload_size()) + .WillRepeatedly(Return(maximum_rpc_payload_size)); + InitProtocolHandlerImpl(0u, 0u); + + // TCP configuration setup + bool tcp_enabled = false; + char tcp_address[] = "192.168.2.3"; + std::string tcp_port_str = "12345"; + protocol_handler_impl->set_tcp_config( + tcp_enabled, std::string(tcp_address), tcp_port_str); + + std::vector<std::string> config_secondary_transports_for_usb; // empty + std::vector<std::string> config_secondary_transports_for_bluetooth; + config_secondary_transports_for_bluetooth.push_back("WiFi"); + std::vector<std::string> config_secondary_transports_for_wifi; // empty + std::vector<std::string> config_audio_service_transports; + config_audio_service_transports.push_back("IAP_USB"); + config_audio_service_transports.push_back("IAP_USB_HOST_MODE"); + config_audio_service_transports.push_back("IAP_USB_DEVICE_MODE"); + config_audio_service_transports.push_back("IAP_CARPLAY"); + config_audio_service_transports.push_back("AOA_USB"); + config_audio_service_transports.push_back("TCP_WIFI"); + std::vector<std::string> config_video_service_transports; + config_video_service_transports.push_back("IAP_USB"); + config_video_service_transports.push_back("IAP_USB_HOST_MODE"); + config_video_service_transports.push_back("IAP_USB_DEVICE_MODE"); + config_video_service_transports.push_back("IAP_CARPLAY"); + config_video_service_transports.push_back("AOA_USB"); + config_video_service_transports.push_back("TCP_WIFI"); + + EXPECT_CALL(protocol_handler_settings_mock, max_supported_protocol_version()) + .WillRepeatedly(Return(PROTOCOL_VERSION_5)); + EXPECT_CALL(protocol_handler_settings_mock, multiple_transports_enabled()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(protocol_handler_settings_mock, secondary_transports_for_usb()) + .Times(AtLeast(0)) + .WillRepeatedly(ReturnRef(config_secondary_transports_for_usb)); + EXPECT_CALL(protocol_handler_settings_mock, + secondary_transports_for_bluetooth()) + .Times(AtLeast(0)) + .WillRepeatedly(ReturnRef(config_secondary_transports_for_bluetooth)); + EXPECT_CALL(protocol_handler_settings_mock, secondary_transports_for_wifi()) + .Times(AtLeast(0)) + .WillRepeatedly(ReturnRef(config_secondary_transports_for_wifi)); + EXPECT_CALL(protocol_handler_settings_mock, audio_service_transports()) + .WillOnce(ReturnRef(config_audio_service_transports)); + EXPECT_CALL(protocol_handler_settings_mock, video_service_transports()) + .WillOnce(ReturnRef(config_video_service_transports)); + + // assume the device is iOS and is connected through iAP over Bluetooth + std::string connection_type_string("IAP_BLUETOOTH"); + + EXPECT_CALL(session_observer_mock, + TransportTypeProfileStringFromConnHandle(connection_id)) + .WillRepeatedly(Return(connection_type_string)); + + EXPECT_CALL( + transport_manager_mock, + SendMessageToDevice(ControlMessage( + FRAME_DATA_START_SERVICE_ACK, PROTECTION_OFF, connection_id, _))) + .WillOnce(DoAll(NotifyTestAsyncWaiter(&waiter), Return(E_SUCCESS))); + times++; + + EXPECT_CALL(session_observer_mock, ProtocolVersionUsed(_, _, _)) + .WillRepeatedly( + DoAll(SetArgReferee<2>(PROTOCOL_VERSION_5), Return(true))); + + BsonObject expected_obj; + bson_object_initialize_default(&expected_obj); + // IP address + char empty_ip_address[] = ""; + bson_object_put_string(&expected_obj, + protocol_handler::strings::tcp_ip_address, + empty_ip_address); + // TCP port number should be omitted + + std::vector<uint8_t> expected_param = + CreateVectorFromBsonObject(&expected_obj); + + bson_object_deinitialize(&expected_obj); + + EXPECT_CALL( + transport_manager_mock, + SendMessageToDevice(ControlMessage(FRAME_DATA_TRANSPORT_EVENT_UPDATE, + PROTECTION_OFF, + connection_id, + Eq(expected_param)))) + .WillOnce(DoAll(NotifyTestAsyncWaiter(&waiter), Return(E_SUCCESS))); + times++; + +#ifdef ENABLE_SECURITY + AddSecurityManager(); + + EXPECT_CALL(session_observer_mock, KeyFromPair(connection_id, session_id)) + .WillOnce(Return(connection_key)); + + EXPECT_CALL(session_observer_mock, GetSSLContext(connection_key, kRpc)) + .WillOnce(ReturnNull()); +#endif // ENABLE_SECURITY + + protocol_handler_impl->SendStartSessionAck(connection_id, + session_id, + input_protocol_version, + hash_id, + protocol_handler::SERVICE_TYPE_RPC, + false /* protection */, + full_version); + + EXPECT_TRUE(waiter.WaitFor(times, kAsyncExpectationsTimeout)); +} + +TEST_F(ProtocolHandlerImplTest, + OnTransportConfigUpdated_TransportEventUpdate_TCPEnabled) { + using connection_handler::SessionConnectionMap; + using connection_handler::SessionTransports; + + TestAsyncWaiter waiter; + uint32_t times = 0; + + char tcp_address[] = "172.16.2.3"; + int32_t tcp_port = 23456; + std::string tcp_port_str = "23456"; + + transport_manager::transport_adapter::TransportConfig configs; + configs[transport_manager::transport_adapter::tc_enabled] = + std::string("true"); + configs[transport_manager::transport_adapter::tc_tcp_port] = tcp_port_str; + configs[transport_manager::transport_adapter::tc_tcp_ip_address] = + std::string(tcp_address); + + transport_manager::ConnectionUID device1_primary_connection_id = 100; + transport_manager::ConnectionUID device2_primary_connection_id = 101; + transport_manager::ConnectionUID device2_secondary_connection_id = 150; + + SessionTransports st1 = {device1_primary_connection_id, kDisabledSecondary}; + SessionTransports st2 = {device2_primary_connection_id, + device2_secondary_connection_id}; + session_connection_map_[0x11] = st1; + session_connection_map_[0x22] = st2; + + EXPECT_CALL(connection_handler_mock, session_connection_map()) + .WillOnce(Return(DataAccessor<SessionConnectionMap>( + session_connection_map_, session_connection_map_lock_ptr_))); + + EXPECT_CALL(session_observer_mock, ProtocolVersionUsed(_, _, _)) + .WillRepeatedly( + DoAll(SetArgReferee<2>(PROTOCOL_VERSION_5), Return(true))); + + BsonObject expected_obj; + bson_object_initialize_default(&expected_obj); + // IP address + bson_object_put_string( + &expected_obj, protocol_handler::strings::tcp_ip_address, tcp_address); + // TCP port number + bson_object_put_int32( + &expected_obj, protocol_handler::strings::tcp_port, tcp_port); + + std::vector<uint8_t> expected_param = + CreateVectorFromBsonObject(&expected_obj); + + bson_object_deinitialize(&expected_obj); + + // since device 1 doesn't support secondary transport feature, + // TransportEvetUpdate should be delivered only to device 2 + EXPECT_CALL( + transport_manager_mock, + SendMessageToDevice(ControlMessage(FRAME_DATA_TRANSPORT_EVENT_UPDATE, + PROTECTION_OFF, + device2_primary_connection_id, + Eq(expected_param)))) + .WillOnce(DoAll(NotifyTestAsyncWaiter(&waiter), Return(E_SUCCESS))); + times++; + + tm_listener->OnTransportConfigUpdated(configs); + + EXPECT_TRUE(waiter.WaitFor(times, kAsyncExpectationsTimeout)); +} + +TEST_F(ProtocolHandlerImplTest, + OnTransportConfigUpdated_TransportEventUpdate_TCPDisabled) { + using connection_handler::SessionConnectionMap; + using connection_handler::SessionTransports; + + TestAsyncWaiter waiter; + uint32_t times = 0; + + char tcp_address[] = "172.16.2.3"; + std::string tcp_port_str = "23456"; + + transport_manager::transport_adapter::TransportConfig configs; + configs[transport_manager::transport_adapter::tc_enabled] = + std::string("false"); + configs[transport_manager::transport_adapter::tc_tcp_port] = tcp_port_str; + configs[transport_manager::transport_adapter::tc_tcp_ip_address] = + std::string(tcp_address); + + transport_manager::ConnectionUID device1_primary_connection_id = 100; + transport_manager::ConnectionUID device1_secondary_connection_id = 150; + transport_manager::ConnectionUID device2_primary_connection_id = 101; + transport_manager::ConnectionUID device3_primary_connection_id = 102; + transport_manager::ConnectionUID device3_secondary_connection_id = 151; + + SessionTransports st1 = {device1_primary_connection_id, + device1_secondary_connection_id}; + SessionTransports st2 = {device2_primary_connection_id, kDisabledSecondary}; + SessionTransports st3 = {device3_primary_connection_id, + device3_secondary_connection_id}; + session_connection_map_[0x11] = st1; + session_connection_map_[0x22] = st2; + session_connection_map_[0x33] = st3; + + EXPECT_CALL(connection_handler_mock, session_connection_map()) + .WillOnce(Return(DataAccessor<SessionConnectionMap>( + session_connection_map_, session_connection_map_lock_ptr_))); + + EXPECT_CALL(session_observer_mock, ProtocolVersionUsed(_, _, _)) + .WillRepeatedly( + DoAll(SetArgReferee<2>(PROTOCOL_VERSION_5), Return(true))); + + BsonObject expected_obj; + bson_object_initialize_default(&expected_obj); + // IP address + char empty_ip_address[] = ""; + bson_object_put_string(&expected_obj, + protocol_handler::strings::tcp_ip_address, + empty_ip_address); + // TCP port number should be omitted + + std::vector<uint8_t> expected_param = + CreateVectorFromBsonObject(&expected_obj); + + bson_object_deinitialize(&expected_obj); + + // both device 1 and device 3 should receive TransportEventUpdate frames + EXPECT_CALL( + transport_manager_mock, + SendMessageToDevice(ControlMessage(FRAME_DATA_TRANSPORT_EVENT_UPDATE, + PROTECTION_OFF, + device1_primary_connection_id, + Eq(expected_param)))) + .WillOnce(DoAll(NotifyTestAsyncWaiter(&waiter), Return(E_SUCCESS))); + times++; + EXPECT_CALL( + transport_manager_mock, + SendMessageToDevice(ControlMessage(FRAME_DATA_TRANSPORT_EVENT_UPDATE, + PROTECTION_OFF, + device3_primary_connection_id, + Eq(expected_param)))) + .WillOnce(DoAll(NotifyTestAsyncWaiter(&waiter), Return(E_SUCCESS))); + times++; + + tm_listener->OnTransportConfigUpdated(configs); + + EXPECT_TRUE(waiter.WaitFor(times, kAsyncExpectationsTimeout)); +} + +TEST_F(ProtocolHandlerImplTest, RegisterSecondaryTransport_SUCCESS) { + AddConnection(); + + TestAsyncWaiter waiter; + uint32_t times = 0; + + transport_manager::ConnectionUID primary_connection_id = 123; + + EXPECT_CALL(session_observer_mock, + ProtocolVersionUsed(primary_connection_id, _, _)) + .WillRepeatedly( + DoAll(SetArgReferee<2>(PROTOCOL_VERSION_5), Return(true))); + + EXPECT_CALL(connection_handler_mock, + OnSecondaryTransportStarted(_, connection_id, session_id)) + .WillOnce(DoAll(SetArgReferee<0>(primary_connection_id), Return(true))); + + EXPECT_CALL(transport_manager_mock, + SendMessageToDevice( + ControlMessage(FRAME_DATA_REGISTER_SECONDARY_TRANSPORT_ACK, + PROTECTION_OFF, + connection_id, + _))) + .WillOnce(DoAll(NotifyTestAsyncWaiter(&waiter), Return(E_SUCCESS))); + times++; + + SendControlMessage(PROTECTION_OFF, + kControl, + session_id, + FRAME_DATA_REGISTER_SECONDARY_TRANSPORT, + PROTOCOL_VERSION_5); + + EXPECT_TRUE(waiter.WaitFor(times, kAsyncExpectationsTimeout)); +} + +TEST_F(ProtocolHandlerImplTest, RegisterSecondaryTransport_FAILURE) { + AddConnection(); + + TestAsyncWaiter waiter; + uint32_t times = 0; + + transport_manager::ConnectionUID primary_connection_id = 123; + + EXPECT_CALL(session_observer_mock, + ProtocolVersionUsed(primary_connection_id, _, _)) + .WillRepeatedly( + DoAll(SetArgReferee<2>(PROTOCOL_VERSION_5), Return(true))); + + // check the behavior when OnSecondaryTransportStarted() returns false + EXPECT_CALL(connection_handler_mock, + OnSecondaryTransportStarted(_, connection_id, session_id)) + .WillOnce(DoAll(SetArgReferee<0>(primary_connection_id), Return(false))); + + EXPECT_CALL(transport_manager_mock, + SendMessageToDevice( + ControlMessage(FRAME_DATA_REGISTER_SECONDARY_TRANSPORT_NACK, + PROTECTION_OFF, + connection_id, + _))) + .WillOnce(DoAll(NotifyTestAsyncWaiter(&waiter), Return(E_SUCCESS))); + times++; + + SendControlMessage(PROTECTION_OFF, + kControl, + session_id, + FRAME_DATA_REGISTER_SECONDARY_TRANSPORT, + PROTOCOL_VERSION_5); + + EXPECT_TRUE(waiter.WaitFor(times, kAsyncExpectationsTimeout)); +} + TEST_F(ProtocolHandlerImplTest, DISABLED_FloodVerification) { const size_t period_msec = 10000; const size_t max_messages = 1000; @@ -1955,7 +3152,8 @@ TEST_F(ProtocolHandlerImplTest, times++; // Act - protocol_handler_impl->SendEndService(connection_id, session_id, kControl); + protocol_handler_impl->SendEndService( + connection_id, connection_id, session_id, kControl); EXPECT_TRUE(waiter->WaitFor(times, kAsyncExpectationsTimeout)); } @@ -1989,7 +3187,8 @@ TEST_F(ProtocolHandlerImplTest, SendHeartBeat_Successful) { transport_manager_mock, SendMessageToDevice(ExpectedMessage( FRAME_TYPE_CONTROL, FRAME_DATA_HEART_BEAT, PROTECTION_OFF, kControl))) - .WillOnce(Return(E_SUCCESS)); + .WillOnce(DoAll(NotifyTestAsyncWaiter(waiter), Return(E_SUCCESS))); + times++; // Act protocol_handler_impl->SendHeartBeat(connection_id, session_id); @@ -2086,7 +3285,8 @@ TEST_F(ProtocolHandlerImplTest, transport_manager_mock, SendMessageToDevice(ExpectedMessage( FRAME_TYPE_SINGLE, FRAME_DATA_SINGLE, PROTECTION_OFF, kControl))) - .WillOnce(Return(E_SUCCESS)); + .WillOnce(DoAll(NotifyTestAsyncWaiter(waiter), Return(E_SUCCESS))); + times++; // Act protocol_handler_impl->SendMessageToMobileApp(message, is_final); diff --git a/src/components/protocol_handler/test/protocol_header_validator_test.cc b/src/components/protocol_handler/test/protocol_header_validator_test.cc index e42ba96251..5554bfd280 100644 --- a/src/components/protocol_handler/test/protocol_header_validator_test.cc +++ b/src/components/protocol_handler/test/protocol_header_validator_test.cc @@ -230,12 +230,12 @@ TEST_F(ProtocolHeaderValidatorTest, Malformed_FrameType) { } } -// For Control frames Frame info value shall be from 0x00 to 0x06 or 0xFE(Data -// Ack), 0xFF(HB Ack) +// For Control frames Frame info value shall be from 0x00 to 0x09 or 0xFD +// (Transport Update Event), 0xFE(Data Ack), 0xFF(HB Ack) TEST_F(ProtocolHeaderValidatorTest, Malformed_ControlFrame) { std::vector<uint8_t> malformed_frame_data; - for (uint8_t frame_type = FRAME_DATA_END_SERVICE_NACK + 1; - frame_type < FRAME_DATA_SERVICE_DATA_ACK; + for (uint8_t frame_type = FRAME_DATA_REGISTER_SECONDARY_TRANSPORT_NACK + 1; + frame_type < FRAME_DATA_TRANSPORT_EVENT_UPDATE; ++frame_type) { malformed_frame_data.push_back(frame_type); } |