diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-03 13:42:47 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-15 10:27:51 +0000 |
commit | 8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec (patch) | |
tree | d29d987c4d7b173cf853279b79a51598f104b403 /chromium/components/cast_channel | |
parent | 830c9e163d31a9180fadca926b3e1d7dfffb5021 (diff) | |
download | qtwebengine-chromium-8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec.tar.gz |
BASELINE: Update Chromium to 66.0.3359.156
Change-Id: I0c9831ad39911a086b6377b16f995ad75a51e441
Reviewed-by: Michal Klocek <michal.klocek@qt.io>
Diffstat (limited to 'chromium/components/cast_channel')
14 files changed, 749 insertions, 35 deletions
diff --git a/chromium/components/cast_channel/BUILD.gn b/chromium/components/cast_channel/BUILD.gn index 2ca9b39b344..64a0bdcbcfc 100644 --- a/chromium/components/cast_channel/BUILD.gn +++ b/chromium/components/cast_channel/BUILD.gn @@ -12,6 +12,8 @@ static_library("cast_channel") { "cast_channel_util.h", "cast_framer.cc", "cast_framer.h", + "cast_message_handler.cc", + "cast_message_handler.h", "cast_message_util.cc", "cast_message_util.h", "cast_socket.cc", @@ -31,7 +33,9 @@ static_library("cast_channel") { "//components/cast_channel/proto:cast_channel_proto", "//components/keyed_service/content", "//components/keyed_service/core", + "//components/version_info", "//content/public/browser", + "//content/public/common", "//crypto", "//net", ] @@ -59,6 +63,7 @@ source_set("unit_tests") { sources = [ "cast_auth_util_unittest.cc", "cast_framer_unittest.cc", + "cast_message_handler_unittest.cc", "cast_socket_service_unittest.cc", "cast_socket_unittest.cc", "cast_transport_unittest.cc", diff --git a/chromium/components/cast_channel/cast_message_handler.cc b/chromium/components/cast_channel/cast_message_handler.cc new file mode 100644 index 00000000000..86a154b51de --- /dev/null +++ b/chromium/components/cast_channel/cast_message_handler.cc @@ -0,0 +1,234 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/cast_channel/cast_message_handler.h" + +#include <tuple> + +#include "base/rand_util.h" +#include "base/strings/stringprintf.h" +#include "base/time/default_tick_clock.h" +#include "components/cast_channel/cast_socket_service.h" +#include "net/traffic_annotation/network_traffic_annotation.h" + +namespace cast_channel { + +namespace { + +constexpr net::NetworkTrafficAnnotationTag kMessageTrafficAnnotation = + net::DefineNetworkTrafficAnnotation("cast_message_handler", R"( + semantics { + sender: "Cast Message Handler" + description: + "A Cast protocol or application-level message sent to a Cast " + "device." + trigger: + "Triggered by user gesture from using Cast functionality, or " + "a webpage using the Presentation API, or " + "Cast device discovery internal logic." + data: + "A serialized Cast protocol or application-level protobuf message. " + "A non-exhaustive list of Cast protocol messages:\n" + "- Virtual connection requests,\n" + "- App availability / media status / receiver status requests,\n" + "- Launch / stop Cast session requests,\n" + "- Media commands, such as play/pause.\n" + "Application-level messages may contain data specific to the Cast " + "application." + destination: OTHER + destination_other: + "Data will be sent to a Cast device in local network." + } + policy { + cookies_allowed: NO + setting: + "This request cannot be disabled, but it would not be sent if user " + "does not connect a Cast device to the local network." + policy_exception_justification: "Not implemented." + })"); + +} // namespace + +GetAppAvailabilityRequest::GetAppAvailabilityRequest( + int channel_id, + const std::string& app_id, + GetAppAvailabilityCallback callback, + base::TickClock* clock) + : channel_id(channel_id), + app_id(app_id), + callback(std::move(callback)), + timeout_timer(clock) {} + +GetAppAvailabilityRequest::~GetAppAvailabilityRequest() = default; + +VirtualConnection::VirtualConnection(int channel_id, + const std::string& source_id, + const std::string& destination_id) + : channel_id(channel_id), + source_id(source_id), + destination_id(destination_id) {} +VirtualConnection::~VirtualConnection() = default; + +bool VirtualConnection::operator<(const VirtualConnection& other) const { + return std::tie(channel_id, source_id, destination_id) < + std::tie(other.channel_id, other.source_id, other.destination_id); +} + +CastMessageHandler::CastMessageHandler(CastSocketService* socket_service, + const std::string& user_agent, + const std::string& browser_version) + : sender_id_(base::StringPrintf("sender-%d", base::RandInt(0, 1000000))), + user_agent_(user_agent), + browser_version_(browser_version), + socket_service_(socket_service), + clock_(base::DefaultTickClock::GetInstance()), + weak_ptr_factory_(this) { + DETACH_FROM_SEQUENCE(sequence_checker_); + socket_service_->task_runner()->PostTask( + FROM_HERE, base::BindOnce(&CastSocketService::AddObserver, + base::Unretained(socket_service_), + base::Unretained(this))); +} + +CastMessageHandler::~CastMessageHandler() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + socket_service_->RemoveObserver(this); +} + +void CastMessageHandler::RequestAppAvailability( + CastSocket* socket, + const std::string& app_id, + GetAppAvailabilityCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + int channel_id = socket->id(); + auto pending_it = std::find_if( + pending_app_availability_requests_.begin(), + pending_app_availability_requests_.end(), + [&channel_id, &app_id]( + const std::pair<int, std::unique_ptr<GetAppAvailabilityRequest>>& + entry) { + const auto& request = entry.second; + return request->channel_id == channel_id && app_id == request->app_id; + }); + if (pending_it != pending_app_availability_requests_.end()) + return; + + int request_id = NextRequestId(); + + DVLOG(2) << __func__ << ", socket_id: " << socket->id() + << ", app_id: " << app_id << ", request_id: " << request_id; + CastMessage message = + CreateGetAppAvailabilityRequest(sender_id_, request_id, app_id); + + auto request = std::make_unique<GetAppAvailabilityRequest>( + channel_id, app_id, std::move(callback), clock_); + request->timeout_timer.Start( + FROM_HERE, base::TimeDelta::FromSeconds(5), + base::Bind(&CastMessageHandler::AppAvailabilityTimedOut, + base::Unretained(this), request_id)); + pending_app_availability_requests_.emplace(request_id, std::move(request)); + + SendCastMessage(socket, message); +} + +void CastMessageHandler::AppAvailabilityTimedOut(int request_id) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DVLOG(1) << __func__ << ", request_id: " << request_id; + auto it = pending_app_availability_requests_.find(request_id); + if (it == pending_app_availability_requests_.end()) + return; + + std::move(it->second->callback) + .Run(it->second->app_id, GetAppAvailabilityResult::kUnknown); + pending_app_availability_requests_.erase(it); +} + +void CastMessageHandler::OnError(const CastSocket& socket, + ChannelError error_state) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + base::EraseIf(virtual_connections_, + [&socket](const VirtualConnection& connection) { + return connection.channel_id == socket.id(); + }); + + auto it = pending_app_availability_requests_.begin(); + while (it != pending_app_availability_requests_.end()) { + if (it->second->channel_id == socket.id()) { + std::move(it->second->callback) + .Run(it->second->app_id, GetAppAvailabilityResult::kUnknown); + it = pending_app_availability_requests_.erase(it); + } else { + ++it; + } + } +} + +void CastMessageHandler::OnMessage(const CastSocket& socket, + const CastMessage& message) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // TODO(crbug.com/698940): Handle other messages. + DVLOG(2) << __func__ << ", socket_id: " << socket.id() + << ", message: " << CastMessageToString(message); + if (!IsReceiverMessage(message) || message.destination_id() != sender_id_) + return; + + std::unique_ptr<base::DictionaryValue> payload = + GetDictionaryFromCastMessage(message); + if (!payload) + return; + + int request_id = 0; + if (!GetRequestIdFromResponse(*payload, &request_id)) + return; + + auto it = pending_app_availability_requests_.find(request_id); + if (it != pending_app_availability_requests_.end()) { + GetAppAvailabilityResult result = + GetAppAvailabilityResultFromResponse(*payload, it->second->app_id); + std::move(it->second->callback).Run(it->second->app_id, result); + pending_app_availability_requests_.erase(it); + } +} + +void CastMessageHandler::SendCastMessage(CastSocket* socket, + const CastMessage& message) { + // A virtual connection must be opened to the receiver before other messages + // can be sent. + VirtualConnection connection(socket->id(), message.source_id(), + message.destination_id()); + if (virtual_connections_.find(connection) == virtual_connections_.end()) { + DVLOG(1) << "Creating VC for channel: " << connection.channel_id + << ", source: " << connection.source_id + << ", dest: " << connection.destination_id; + CastMessage virtual_connection_request = CreateVirtualConnectionRequest( + connection.source_id, connection.destination_id, + connection.destination_id == kPlatformReceiverId + ? VirtualConnectionType::kStrong + : VirtualConnectionType::kInvisible, + user_agent_, browser_version_); + socket->transport()->SendMessage( + virtual_connection_request, + base::Bind(&CastMessageHandler::OnMessageSent, + weak_ptr_factory_.GetWeakPtr()), + kMessageTrafficAnnotation); + + // We assume the virtual connection request will succeed; otherwise this + // will eventually self-correct. + virtual_connections_.insert(connection); + } + socket->transport()->SendMessage( + message, + base::Bind(&CastMessageHandler::OnMessageSent, + weak_ptr_factory_.GetWeakPtr()), + kMessageTrafficAnnotation); +} + +void CastMessageHandler::OnMessageSent(int result) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DVLOG_IF(2, result < 0) << "SendMessage failed with code: " << result; +} + +} // namespace cast_channel diff --git a/chromium/components/cast_channel/cast_message_handler.h b/chromium/components/cast_channel/cast_message_handler.h new file mode 100644 index 00000000000..8c1cc334e48 --- /dev/null +++ b/chromium/components/cast_channel/cast_message_handler.h @@ -0,0 +1,150 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_CAST_CHANNEL_CAST_MESSAGE_HANDLER_H_ +#define COMPONENTS_CAST_CHANNEL_CAST_MESSAGE_HANDLER_H_ + +#include "base/containers/flat_map.h" +#include "base/containers/flat_set.h" +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/sequence_checker.h" +#include "base/time/tick_clock.h" +#include "base/values.h" +#include "components/cast_channel/cast_message_util.h" +#include "components/cast_channel/cast_socket.h" + +namespace cast_channel { + +class CastSocketService; + +// |app_id|: ID of app the result is for. +// |result|: Availability result from the receiver. +using GetAppAvailabilityCallback = + base::OnceCallback<void(const std::string& app_id, + GetAppAvailabilityResult result)>; + +// Represents an app availability request to a Cast sink. +struct GetAppAvailabilityRequest { + GetAppAvailabilityRequest(int channel_id, + const std::string& app_id, + GetAppAvailabilityCallback callback, + base::TickClock* clock); + ~GetAppAvailabilityRequest(); + + // ID of channel the request is sent over. + int channel_id; + + // App ID of the request. + std::string app_id; + + // Callback to invoke when availability has been determined. + GetAppAvailabilityCallback callback; + + // Timer to fail the request on timeout. + base::OneShotTimer timeout_timer; +}; + +// Represents a virtual connection on a cast channel. A virtual connection is +// given by a source and destination ID pair, and must be created before +// messages can be sent. Virtual connections are managed by CastMessageHandler. +struct VirtualConnection { + VirtualConnection(int channel_id, + const std::string& source_id, + const std::string& destination_id); + ~VirtualConnection(); + + bool operator<(const VirtualConnection& other) const; + + // ID of cast channel. + int channel_id; + + // Source ID (e.g. sender-0). + std::string source_id; + + // Destination ID (e.g. receiver-0). + std::string destination_id; +}; + +// Handles messages that are sent between this browser instance and the Cast +// devices connected to it. This class also manages virtual connections (VCs) +// with each connected Cast device and ensures a proper VC exists before the +// message is sent. This makes the concept of VC transparent to the client. +// This class currently provides only supports requesting app availability from +// a device, but will be expanded to support additional types of messages. +// This class may be created on any sequence, but other methods (including +// destructor) must be run on the same sequence that CastSocketService runs on. +class CastMessageHandler : public CastSocket::Observer { + public: + explicit CastMessageHandler(CastSocketService* socket_service, + const std::string& user_agent, + const std::string& browser_version); + ~CastMessageHandler() override; + + // Sends an app availability for |app_id| to the device given by |socket|. + // |callback| is always invoked asynchronously, and will be invoked when a + // response is received, or if the request timed out. No-ops if there is + // already a pending request with the same socket and app ID. + virtual void RequestAppAvailability(CastSocket* socket, + const std::string& app_id, + GetAppAvailabilityCallback callback); + + const std::string& sender_id() const { return sender_id_; } + + private: + friend class CastMessageHandlerTest; + FRIEND_TEST_ALL_PREFIXES(CastMessageHandlerTest, RequestAppAvailability); + FRIEND_TEST_ALL_PREFIXES(CastMessageHandlerTest, + RecreateVirtualConnectionAfterError); + + // Used internally to generate the next ID to use in a request type message. + int NextRequestId() { return ++next_request_id_; } + + // CastSocket::Observer implementation. + void OnError(const CastSocket& socket, ChannelError error_state) override; + void OnMessage(const CastSocket& socket, const CastMessage& message) override; + + // Sends |message| over |socket|. This also ensures the necessary virtual + // connection exists before sending the message. + void SendCastMessage(CastSocket* socket, const CastMessage& message); + + // Callback for CastTransport::SendMessage. + void OnMessageSent(int result); + + // Invokes the pending callback associated with |request_id| with kUnknown. + void AppAvailabilityTimedOut(int request_id); + + // App availability requests pending responses, keyed by request ID. + base::flat_map<int, std::unique_ptr<GetAppAvailabilityRequest>> + pending_app_availability_requests_; + + // Source ID used for platform messages. The suffix is randomized to + // distinguish it from other Cast senders on the same network. + const std::string sender_id_; + + // User agent and browser version strings included in virtual connection + // messages. + const std::string user_agent_; + const std::string browser_version_; + + int next_request_id_ = 0; + + // Set of virtual connections opened to receivers. + base::flat_set<VirtualConnection> virtual_connections_; + + CastSocketService* const socket_service_; + + // Non-owned pointer to TickClock used for request timeouts. + base::TickClock* const clock_; + + SEQUENCE_CHECKER(sequence_checker_); + base::WeakPtrFactory<CastMessageHandler> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(CastMessageHandler); +}; + +} // namespace cast_channel + +#endif // COMPONENTS_CAST_CHANNEL_CAST_MESSAGE_HANDLER_H_ diff --git a/chromium/components/cast_channel/cast_message_handler_unittest.cc b/chromium/components/cast_channel/cast_message_handler_unittest.cc new file mode 100644 index 00000000000..c528bf83e1e --- /dev/null +++ b/chromium/components/cast_channel/cast_message_handler_unittest.cc @@ -0,0 +1,180 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/cast_channel/cast_message_handler.h" + +#include "base/run_loop.h" +#include "base/strings/stringprintf.h" +#include "base/test/scoped_task_environment.h" +#include "base/test/test_simple_task_runner.h" +#include "components/cast_channel/cast_test_util.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::SaveArg; + +namespace cast_channel { + +namespace { + +constexpr char kTestUserAgentString[] = + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/66.0.3331.0 Safari/537.36"; + +std::string GetMessageType(const CastMessage& message) { + std::unique_ptr<base::Value> dict = GetDictionaryFromCastMessage(message); + if (!dict) + return std::string(); + const base::Value* message_type = + dict->FindKeyOfType("type", base::Value::Type::STRING); + if (!message_type) + return std::string(); + return message_type->GetString(); +} + +} // namespace + +class CastMessageHandlerTest : public testing::Test { + public: + CastMessageHandlerTest() + : environment_( + base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME), + cast_socket_service_(new base::TestSimpleTaskRunner()), + handler_(&cast_socket_service_, kTestUserAgentString, "66.0.3331.0") {} + ~CastMessageHandlerTest() override {} + + MOCK_METHOD2(OnAppAvailability, + void(const std::string& app_id, + GetAppAvailabilityResult result)); + + protected: + base::test::ScopedTaskEnvironment environment_; + MockCastSocketService cast_socket_service_; + CastMessageHandler handler_; + MockCastSocket cast_socket_; + + private: + DISALLOW_COPY_AND_ASSIGN(CastMessageHandlerTest); +}; + +TEST_F(CastMessageHandlerTest, VirtualConnectionCreatedOnlyOnce) { + CastMessage virtual_connection_request; + CastMessage app_availability_request1; + CastMessage app_availability_request2; + EXPECT_CALL(*cast_socket_.mock_transport(), SendMessage(_, _, _)) + .WillOnce(SaveArg<0>(&virtual_connection_request)) + .WillOnce(SaveArg<0>(&app_availability_request1)) + .WillOnce(SaveArg<0>(&app_availability_request2)); + + handler_.RequestAppAvailability( + &cast_socket_, "AAAAAAAA", + base::BindOnce(&CastMessageHandlerTest::OnAppAvailability, + base::Unretained(this))); + handler_.RequestAppAvailability( + &cast_socket_, "BBBBBBBB", + base::BindOnce(&CastMessageHandlerTest::OnAppAvailability, + base::Unretained(this))); + + EXPECT_EQ("CONNECT", GetMessageType(virtual_connection_request)); + EXPECT_EQ("GET_APP_AVAILABILITY", GetMessageType(app_availability_request1)); + EXPECT_EQ("GET_APP_AVAILABILITY", GetMessageType(app_availability_request2)); +} + +TEST_F(CastMessageHandlerTest, RecreateVirtualConnectionAfterError) { + CastMessage virtual_connection_request; + CastMessage app_availability_request; + EXPECT_CALL(*cast_socket_.mock_transport(), SendMessage(_, _, _)) + .WillOnce(SaveArg<0>(&virtual_connection_request)) + .WillOnce(SaveArg<0>(&app_availability_request)); + + handler_.RequestAppAvailability( + &cast_socket_, "AAAAAAAA", + base::BindOnce(&CastMessageHandlerTest::OnAppAvailability, + base::Unretained(this))); + EXPECT_EQ("CONNECT", GetMessageType(virtual_connection_request)); + EXPECT_EQ("GET_APP_AVAILABILITY", GetMessageType(app_availability_request)); + + EXPECT_CALL( + *this, OnAppAvailability("AAAAAAAA", GetAppAvailabilityResult::kUnknown)); + handler_.OnError(cast_socket_, ChannelError::TRANSPORT_ERROR); + + EXPECT_CALL(*cast_socket_.mock_transport(), SendMessage(_, _, _)) + .WillOnce(SaveArg<0>(&virtual_connection_request)) + .WillOnce(SaveArg<0>(&app_availability_request)); + + handler_.RequestAppAvailability( + &cast_socket_, "BBBBBBBB", + base::BindOnce(&CastMessageHandlerTest::OnAppAvailability, + base::Unretained(this))); + EXPECT_EQ("CONNECT", GetMessageType(virtual_connection_request)); + EXPECT_EQ("GET_APP_AVAILABILITY", GetMessageType(app_availability_request)); +} + +TEST_F(CastMessageHandlerTest, RequestAppAvailability) { + CastMessage virtual_connection_request; + CastMessage app_availability_request; + EXPECT_CALL(*cast_socket_.mock_transport(), SendMessage(_, _, _)) + .WillOnce(SaveArg<0>(&virtual_connection_request)) + .WillOnce(SaveArg<0>(&app_availability_request)); + + handler_.RequestAppAvailability( + &cast_socket_, "ABCDEFAB", + base::BindOnce(&CastMessageHandlerTest::OnAppAvailability, + base::Unretained(this))); + + EXPECT_EQ("CONNECT", GetMessageType(virtual_connection_request)); + EXPECT_EQ("GET_APP_AVAILABILITY", GetMessageType(app_availability_request)); + + std::unique_ptr<base::Value> dict = + GetDictionaryFromCastMessage(app_availability_request); + ASSERT_TRUE(dict); + const base::Value* request_id_value = + dict->FindKeyOfType("requestId", base::Value::Type::INTEGER); + ASSERT_TRUE(request_id_value); + int request_id = request_id_value->GetInt(); + EXPECT_GT(request_id, 0); + + CastMessage response; + response.set_namespace_("urn:x-cast:com.google.cast.receiver"); + response.set_source_id("receiver-0"); + response.set_destination_id(handler_.sender_id()); + response.set_payload_type( + CastMessage::PayloadType::CastMessage_PayloadType_STRING); + response.set_payload_utf8( + base::StringPrintf("{\"requestId\": %d, \"availability\": {\"ABCDEFAB\": " + "\"APP_AVAILABLE\"}}", + request_id)); + + EXPECT_CALL(*this, OnAppAvailability("ABCDEFAB", + GetAppAvailabilityResult::kAvailable)); + handler_.OnMessage(cast_socket_, response); +} + +TEST_F(CastMessageHandlerTest, RequestAppAvailabilityTimesOut) { + EXPECT_CALL(*cast_socket_.mock_transport(), SendMessage(_, _, _)).Times(2); + handler_.RequestAppAvailability( + &cast_socket_, "ABCDEFAB", + base::BindOnce(&CastMessageHandlerTest::OnAppAvailability, + base::Unretained(this))); + EXPECT_CALL( + *this, OnAppAvailability("ABCDEFAB", GetAppAvailabilityResult::kUnknown)); + environment_.FastForwardBy(base::TimeDelta::FromSeconds(5)); +} + +TEST_F(CastMessageHandlerTest, AppAvailabilitySentOnlyOnceWhilePending) { + EXPECT_CALL(*cast_socket_.mock_transport(), SendMessage(_, _, _)).Times(2); + handler_.RequestAppAvailability( + &cast_socket_, "ABCDEFAB", + base::BindOnce(&CastMessageHandlerTest::OnAppAvailability, + base::Unretained(this))); + + EXPECT_CALL(*cast_socket_.mock_transport(), SendMessage(_, _, _)).Times(0); + handler_.RequestAppAvailability( + &cast_socket_, "ABCDEFAB", + base::BindOnce(&CastMessageHandlerTest::OnAppAvailability, + base::Unretained(this))); +} + +} // namespace cast_channel diff --git a/chromium/components/cast_channel/cast_message_util.cc b/chromium/components/cast_channel/cast_message_util.cc index acd9848dbc7..415556f50b1 100644 --- a/chromium/components/cast_channel/cast_message_util.cc +++ b/chromium/components/cast_channel/cast_message_util.cc @@ -10,6 +10,7 @@ #include "base/json/json_writer.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" +#include "build/build_config.h" #include "components/cast_channel/cast_auth_util.h" #include "components/cast_channel/proto/cast_channel.pb.h" @@ -26,10 +27,6 @@ constexpr char kConnectionNamespace[] = "urn:x-cast:com.google.cast.tp.connection"; constexpr char kReceiverNamespace[] = "urn:x-cast:com.google.cast.receiver"; -// Sender and receiver IDs to use for platform messages. -constexpr char kPlatformSenderId[] = "sender-0"; -constexpr char kPlatformReceiverId[] = "receiver-0"; - // Text payload keys. constexpr char kTypeNodeId[] = "type"; constexpr char kRequestIdNodeId[] = "requestId"; @@ -40,6 +37,14 @@ constexpr char kKeepAlivePongType[] = "PONG"; constexpr char kGetAppAvailabilityRequestType[] = "GET_APP_AVAILABILITY"; constexpr char kConnectionRequestType[] = "CONNECT"; +// The value used for "sdkType" in a virtual connect request. Historically, this +// value is used in the Media Router extension, but here it is reused in Chrome. +constexpr int kVirtualConnectSdkType = 2; + +// The value used for "connectionType" in a virtual connect request. This value +// stands for CONNECTION_TYPE_LOCAL, which is the only type used in Chrome. +constexpr int kVirtualConnectTypeLocal = 1; + void FillCommonCastMessageFields(CastMessage* message, const std::string& source_id, const std::string& destination_id, @@ -63,6 +68,23 @@ CastMessage CreateKeepAliveMessage(const char* keep_alive_type) { return output; } +// Returns the value to be set as the "platform" value in a virtual connect +// request. The value is platform-dependent and is taken from the Platform enum +// defined in third_party/metrics_proto/cast_logs.proto. +int GetVirtualConnectPlatformValue() { +#if defined(OS_WIN) + return 3; +#elif defined(OS_MACOSX) + return 4; +#elif defined(OS_CHROMEOS) + return 5; +#elif defined(OS_LINUX) + return 6; +#else + return 0; +#endif +} + } // namespace bool IsCastMessageValid(const CastMessage& message_proto) { @@ -76,26 +98,23 @@ bool IsCastMessageValid(const CastMessage& message_proto) { message_proto.has_payload_binary()); } -std::unique_ptr<Value> GetDictionaryFromCastMessage( +std::unique_ptr<base::DictionaryValue> GetDictionaryFromCastMessage( const CastMessage& message) { if (!message.has_payload_utf8()) return nullptr; - std::unique_ptr<Value> parsed_payload( + return base::DictionaryValue::From( base::JSONReader::Read(message.payload_utf8())); - if (!parsed_payload || !parsed_payload->is_dict()) - return nullptr; - - return parsed_payload; } CastMessageType ParseMessageType(const CastMessage& message) { - std::unique_ptr<Value> dictionary = GetDictionaryFromCastMessage(message); + std::unique_ptr<base::DictionaryValue> dictionary = + GetDictionaryFromCastMessage(message); if (!dictionary) return CastMessageType::kOther; - const base::Value* type_string = - dictionary->FindKeyOfType(kTypeNodeId, base::Value::Type::STRING); + const Value* type_string = + dictionary->FindKeyOfType(kTypeNodeId, Value::Type::STRING); if (!type_string) return CastMessageType::kOther; @@ -193,17 +212,48 @@ CastMessage CreateKeepAlivePongMessage() { return CreateKeepAliveMessage(kKeepAlivePongType); } -CastMessage CreateVirtualConnectionRequest(const std::string& source_id, - const std::string& destination_id) { +CastMessage CreateVirtualConnectionRequest( + const std::string& source_id, + const std::string& destination_id, + VirtualConnectionType connection_type, + const std::string& user_agent, + const std::string& browser_version) { + DCHECK(destination_id != kPlatformReceiverId || connection_type == kStrong); + CastMessage output; FillCommonCastMessageFields(&output, source_id, destination_id, kConnectionNamespace); output.set_payload_type( CastMessage::PayloadType::CastMessage_PayloadType_STRING); + // Parse system_version from user agent string. It contains platform, OS and + // CPU info and is contained in the first set of parentheses of the user + // agent string (e.g., X11; Linux x86_64). + std::string system_version; + size_t start_index = user_agent.find('('); + if (start_index != std::string::npos) { + size_t end_index = user_agent.find(')', start_index + 1); + if (end_index != std::string::npos) { + system_version = + user_agent.substr(start_index + 1, end_index - start_index - 1); + } + } + Value dict(Value::Type::DICTIONARY); dict.SetKey(kTypeNodeId, Value(kConnectionRequestType)); - // TODO(crbug.com/698940): Populate other optional fields. + dict.SetKey("userAgent", Value(user_agent)); + dict.SetKey("connType", Value(connection_type)); + + Value sender_info(Value::Type::DICTIONARY); + sender_info.SetKey("sdkType", Value(kVirtualConnectSdkType)); + sender_info.SetKey("version", Value(browser_version)); + sender_info.SetKey("browserVersion", Value(browser_version)); + sender_info.SetKey("platform", Value(GetVirtualConnectPlatformValue())); + sender_info.SetKey("connectionType", Value(kVirtualConnectTypeLocal)); + if (!system_version.empty()) + sender_info.SetKey("systemVersion", Value(system_version)); + + dict.SetKey("senderInfo", std::move(sender_info)); CHECK(base::JSONWriter::Write(dict, output.mutable_payload_utf8())); return output; } diff --git a/chromium/components/cast_channel/cast_message_util.h b/chromium/components/cast_channel/cast_message_util.h index 343d2f9e640..c0882cc52ed 100644 --- a/chromium/components/cast_channel/cast_message_util.h +++ b/chromium/components/cast_channel/cast_message_util.h @@ -15,6 +15,10 @@ class AuthContext; class CastMessage; class DeviceAuthMessage; +// Sender and receiver IDs to use for platform messages. +constexpr char kPlatformSenderId[] = "sender-0"; +constexpr char kPlatformReceiverId[] = "receiver-0"; + // Cast application protocol message types. enum class CastMessageType { kOther, kPing, kPong }; @@ -23,7 +27,7 @@ bool IsCastMessageValid(const CastMessage& message_proto); // Parses and returns the UTF-8 payload from |message|. Returns nullptr // if the UTF-8 payload doesn't exist, or if it is not a dictionary. -std::unique_ptr<base::Value> GetDictionaryFromCastMessage( +std::unique_ptr<base::DictionaryValue> GetDictionaryFromCastMessage( const CastMessage& message); // Parses the JSON-encoded payload of |message| and returns the value in the @@ -58,10 +62,23 @@ bool IsPlatformSenderMessage(const CastMessage& message); CastMessage CreateKeepAlivePingMessage(); CastMessage CreateKeepAlivePongMessage(); +enum VirtualConnectionType { + kStrong = 0, + // kWeak = 1 not used. + kInvisible = 2 +}; + // Creates a virtual connection request message for |source_id| and -// |destination_id|. -CastMessage CreateVirtualConnectionRequest(const std::string& source_id, - const std::string& destination_id); +// |destination_id|. |user_agent| and |browser_version| will be included with +// the request. +// If |destination_id| is kPlatformReceiverId, then |connection_type| must be +// kStrong. Otherwise |connection_type| can be either kStrong or kInvisible. +CastMessage CreateVirtualConnectionRequest( + const std::string& source_id, + const std::string& destination_id, + VirtualConnectionType connection_type, + const std::string& user_agent, + const std::string& browser_version); // Creates an app availability request for |app_id| from |source_id| with // ID |request_id|. diff --git a/chromium/components/cast_channel/cast_socket.cc b/chromium/components/cast_channel/cast_socket.cc index 93995871461..ccfc0049afe 100644 --- a/chromium/components/cast_channel/cast_socket.cc +++ b/chromium/components/cast_channel/cast_socket.cc @@ -7,6 +7,7 @@ #include <stdlib.h> #include <string.h> +#include <memory> #include <utility> #include "base/bind.h" @@ -47,6 +48,7 @@ #include "net/socket/tcp_client_socket.h" #include "net/ssl/ssl_config_service.h" #include "net/ssl/ssl_info.h" +#include "net/traffic_annotation/network_traffic_annotation.h" // Helper for logging data with remote host IP and authentication state. // Assumes |ip_endpoint_| of type net::IPEndPoint and |channel_auth_| of enum @@ -105,9 +107,10 @@ CastSocketImpl::CastSocketImpl(const CastSocketOpenParams& open_params, ready_state_(ReadyState::NONE), auth_delegate_(nullptr) { DCHECK(open_params.ip_endpoint.address().IsValid()); - DCHECK(open_params_.net_log); - net_log_source_.type = net::NetLogSourceType::SOCKET; - net_log_source_.id = open_params_.net_log->NextID(); + if (open_params_.net_log) { + net_log_source_.type = net::NetLogSourceType::SOCKET; + net_log_source_.id = open_params_.net_log->NextID(); + } } CastSocketImpl::~CastSocketImpl() { @@ -251,7 +254,7 @@ void CastSocketImpl::Connect() { DCHECK_EQ(ReadyState::NONE, ready_state_); DCHECK_EQ(ConnectionState::START_CONNECT, connect_state_); - delegate_ = base::MakeUnique<CastSocketMessageDelegate>(this); + delegate_ = std::make_unique<CastSocketMessageDelegate>(this); SetReadyState(ReadyState::CONNECTING); SetConnectState(ConnectionState::TCP_CONNECT); @@ -455,9 +458,33 @@ int CastSocketImpl::DoAuthChallengeSend() { << CastMessageToString(challenge_message); ResetConnectLoopCallback(); - // TODO(https://crbug.com/656607): Add proper annotation. + + net::NetworkTrafficAnnotationTag traffic_annotation = + net::DefineNetworkTrafficAnnotation("cast_socket", R"( + semantics { + sender: "Cast Socket" + description: + "An auth challenge request sent to a Cast device as a part of " + "establishing a connection to it." + trigger: + "A new Cast device has been discovered via mDNS in the local " + "network." + data: + "A serialized protobuf message containing the nonce challenge. No " + "user data." + destination: OTHER + destination_other: + "Data will be sent to a Cast device in local network." + } + policy { + cookies_allowed: NO + setting: + "This request cannot be disabled, but it would not be sent if user " + "does not connect a Cast device to the local network." + policy_exception_justification: "Not implemented." + })"); transport_->SendMessage(challenge_message, connect_loop_callback_.callback(), - NO_TRAFFIC_ANNOTATION_BUG_656607); + traffic_annotation); // Always return IO_PENDING since the result is always asynchronous. return net::ERR_IO_PENDING; diff --git a/chromium/components/cast_channel/cast_socket_service.h b/chromium/components/cast_channel/cast_socket_service.h index 94bbf434485..64fd9d4f419 100644 --- a/chromium/components/cast_channel/cast_socket_service.h +++ b/chromium/components/cast_channel/cast_socket_service.h @@ -11,6 +11,7 @@ #include "base/macros.h" #include "base/observer_list.h" #include "base/sequence_checker.h" +#include "base/single_thread_task_runner.h" #include "components/cast_channel/cast_socket.h" namespace cast_channel { diff --git a/chromium/components/cast_channel/cast_socket_service_unittest.cc b/chromium/components/cast_channel/cast_socket_service_unittest.cc index d0a519bef1c..3aafc5eebb5 100644 --- a/chromium/components/cast_channel/cast_socket_service_unittest.cc +++ b/chromium/components/cast_channel/cast_socket_service_unittest.cc @@ -41,18 +41,18 @@ class CastSocketServiceTest : public testing::Test { }; TEST_F(CastSocketServiceTest, TestAddSocket) { - auto socket1 = base::MakeUnique<MockCastSocket>(); + auto socket1 = std::make_unique<MockCastSocket>(); auto* socket_ptr1 = AddSocket(std::move(socket1)); EXPECT_NE(0, socket_ptr1->id()); - auto socket2 = base::MakeUnique<MockCastSocket>(); + auto socket2 = std::make_unique<MockCastSocket>(); auto* socket_ptr2 = AddSocket(std::move(socket2)); EXPECT_NE(socket_ptr1->id(), socket_ptr2->id()); auto removed_socket = cast_socket_service_->RemoveSocket(socket_ptr2->id()); EXPECT_EQ(socket_ptr2, removed_socket.get()); - auto socket3 = base::MakeUnique<MockCastSocket>(); + auto socket3 = std::make_unique<MockCastSocket>(); auto* socket_ptr3 = AddSocket(std::move(socket3)); EXPECT_NE(socket_ptr1->id(), socket_ptr3->id()); EXPECT_NE(socket_ptr2->id(), socket_ptr3->id()); @@ -65,7 +65,7 @@ TEST_F(CastSocketServiceTest, TestRemoveAndGetSocket) { auto socket = cast_socket_service_->RemoveSocket(channel_id); EXPECT_FALSE(socket); - auto mock_socket = base::MakeUnique<MockCastSocket>(); + auto mock_socket = std::make_unique<MockCastSocket>(); auto* mock_socket_ptr = AddSocket(std::move(mock_socket)); channel_id = mock_socket_ptr->id(); diff --git a/chromium/components/cast_channel/cast_socket_unittest.cc b/chromium/components/cast_channel/cast_socket_unittest.cc index 37626eb1fdd..9951586d912 100644 --- a/chromium/components/cast_channel/cast_socket_unittest.cc +++ b/chromium/components/cast_channel/cast_socket_unittest.cc @@ -538,7 +538,7 @@ class SslCastSocketTest : public CastSocketTestBase { net::TestCompletionCallback write_callback; int write_result = write_callback.GetResult(server_socket_->Write( draining_buffer.get(), draining_buffer->BytesRemaining(), - write_callback.callback())); + write_callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS)); EXPECT_GT(write_result, 0); draining_buffer->DidConsume(write_result); } diff --git a/chromium/components/cast_channel/cast_test_util.cc b/chromium/components/cast_channel/cast_test_util.cc index cabff1b3d88..f90d13d3ce7 100644 --- a/chromium/components/cast_channel/cast_test_util.cc +++ b/chromium/components/cast_channel/cast_test_util.cc @@ -48,4 +48,10 @@ net::IPEndPoint CreateIPEndPointForTest() { return net::IPEndPoint(net::IPAddress(192, 168, 1, 1), 8009); } +MockCastMessageHandler::MockCastMessageHandler( + MockCastSocketService* socket_service) + : CastMessageHandler(socket_service, "userAgent", "1.2.3.4") {} + +MockCastMessageHandler::~MockCastMessageHandler() = default; + } // namespace cast_channel diff --git a/chromium/components/cast_channel/cast_test_util.h b/chromium/components/cast_channel/cast_test_util.h index 4fff1ad89c1..87813250760 100644 --- a/chromium/components/cast_channel/cast_test_util.h +++ b/chromium/components/cast_channel/cast_test_util.h @@ -10,6 +10,7 @@ #include "base/macros.h" #include "base/threading/thread_task_runner_handle.h" +#include "components/cast_channel/cast_message_handler.h" #include "components/cast_channel/cast_socket.h" #include "components/cast_channel/cast_socket_service.h" #include "components/cast_channel/cast_transport.h" @@ -143,6 +144,26 @@ class MockCastSocket : public CastSocket { DISALLOW_COPY_AND_ASSIGN(MockCastSocket); }; +class MockCastMessageHandler : public CastMessageHandler { + public: + explicit MockCastMessageHandler(MockCastSocketService* socket_service); + ~MockCastMessageHandler() override; + + void RequestAppAvailability(CastSocket* socket, + const std::string& app_id, + GetAppAvailabilityCallback callback) override { + DoRequestAppAvailability(socket, app_id, callback); + } + + MOCK_METHOD3(DoRequestAppAvailability, + void(CastSocket*, + const std::string&, + GetAppAvailabilityCallback&)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockCastMessageHandler); +}; + // Creates the IPEndpoint 192.168.1.1. net::IPEndPoint CreateIPEndPointForTest(); diff --git a/chromium/components/cast_channel/keep_alive_delegate.cc b/chromium/components/cast_channel/keep_alive_delegate.cc index cce03044d05..082ecc838d8 100644 --- a/chromium/components/cast_channel/keep_alive_delegate.cc +++ b/chromium/components/cast_channel/keep_alive_delegate.cc @@ -12,6 +12,7 @@ #include "components/cast_channel/logger.h" #include "components/cast_channel/proto/cast_channel.pb.h" #include "net/base/net_errors.h" +#include "net/traffic_annotation/network_traffic_annotation.h" namespace cast_channel { @@ -85,12 +86,34 @@ void KeepAliveDelegate::SendKeepAliveMessage(const CastMessage& message, CastMessageType message_type) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DVLOG(2) << "Sending " << CastMessageTypeToString(message_type); - // TODO(https://crbug.com/656607): Add proper annotation. + + net::NetworkTrafficAnnotationTag traffic_annotation = + net::DefineNetworkTrafficAnnotation("cast_keep_alive_delegate", R"( + semantics { + sender: "Cast Socket Keep Alive Delegate" + description: + "A ping/pong message sent periodically to a Cast device to keep " + "the connection alive." + trigger: + "Periodically while a connection to a Cast device is established." + data: + "A protobuf message representing a ping/pong message. No user data." + destination: OTHER + destination_other: + "Data will be sent to a Cast device in local network." + } + policy { + cookies_allowed: NO + setting: + "This request cannot be disabled, but it would not be sent if user " + "does not connect to a Cast device." + policy_exception_justification: "Not implemented." + })"); socket_->transport()->SendMessage( message, base::Bind(&KeepAliveDelegate::SendKeepAliveMessageComplete, base::Unretained(this), message_type), - NO_TRAFFIC_ANNOTATION_BUG_656607); + traffic_annotation); } void KeepAliveDelegate::SendKeepAliveMessageComplete( diff --git a/chromium/components/cast_channel/keep_alive_delegate_unittest.cc b/chromium/components/cast_channel/keep_alive_delegate_unittest.cc index 284560697fa..feb23dd726d 100644 --- a/chromium/components/cast_channel/keep_alive_delegate_unittest.cc +++ b/chromium/components/cast_channel/keep_alive_delegate_unittest.cc @@ -236,9 +236,9 @@ TEST_F(KeepAliveDelegateTest, TestLivenessTimerResetAfterSendingMessage) { mock_time_task_runner->GetMockTickClock(); std::unique_ptr<base::Timer> liveness_timer = - base::MakeUnique<base::Timer>(true, false, tick_clock.get()); + std::make_unique<base::Timer>(true, false, tick_clock.get()); std::unique_ptr<base::Timer> ping_timer = - base::MakeUnique<base::Timer>(true, false, tick_clock.get()); + std::make_unique<base::Timer>(true, false, tick_clock.get()); ping_timer->SetTaskRunner(mock_time_task_runner); liveness_timer->SetTaskRunner(mock_time_task_runner); keep_alive_->SetTimersForTest(std::move(ping_timer), |