summaryrefslogtreecommitdiff
path: root/chromium/components/cast_channel
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-05-03 13:42:47 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-05-15 10:27:51 +0000
commit8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec (patch)
treed29d987c4d7b173cf853279b79a51598f104b403 /chromium/components/cast_channel
parent830c9e163d31a9180fadca926b3e1d7dfffb5021 (diff)
downloadqtwebengine-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')
-rw-r--r--chromium/components/cast_channel/BUILD.gn5
-rw-r--r--chromium/components/cast_channel/cast_message_handler.cc234
-rw-r--r--chromium/components/cast_channel/cast_message_handler.h150
-rw-r--r--chromium/components/cast_channel/cast_message_handler_unittest.cc180
-rw-r--r--chromium/components/cast_channel/cast_message_util.cc82
-rw-r--r--chromium/components/cast_channel/cast_message_util.h25
-rw-r--r--chromium/components/cast_channel/cast_socket.cc39
-rw-r--r--chromium/components/cast_channel/cast_socket_service.h1
-rw-r--r--chromium/components/cast_channel/cast_socket_service_unittest.cc8
-rw-r--r--chromium/components/cast_channel/cast_socket_unittest.cc2
-rw-r--r--chromium/components/cast_channel/cast_test_util.cc6
-rw-r--r--chromium/components/cast_channel/cast_test_util.h21
-rw-r--r--chromium/components/cast_channel/keep_alive_delegate.cc27
-rw-r--r--chromium/components/cast_channel/keep_alive_delegate_unittest.cc4
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),