Commit 44aa3c0d authored by Derek Cheng's avatar Derek Cheng Committed by Commit Bot

[Cast MRP] Add a flag to connect to (test) devices with fixed ip.

This flag is needed for the E2E tests to continue functioning as Cast
discovery is moved to the browser side.

The flag name is media-router-cast-device-ips, and the value is a
comma-separated list of IP endpoints of the Cast devices. The port
number can be omitted, and will default to 8009 in that case.

Bug: 698940
Change-Id: I1cf9b4ebc7c63f53ed811002eb917af137e62b74
Reviewed-on: https://chromium-review.googlesource.com/1024237
Commit-Queue: Derek Cheng <imcheng@chromium.org>
Reviewed-by: default avatarBrandon Tolsch <btolsch@chromium.org>
Cr-Commit-Position: refs/heads/master@{#553659}
parent 2153a8d7
......@@ -55,6 +55,8 @@ static_library("discovery") {
"mdns/dns_sd_device_lister.h",
"mdns/dns_sd_registry.cc",
"mdns/dns_sd_registry.h",
"mdns/media_sink_util.cc",
"mdns/media_sink_util.h",
"media_sink_discovery_metrics.cc",
"media_sink_discovery_metrics.h",
]
......
......@@ -5,94 +5,18 @@
#include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service.h"
#include "base/bind.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/media/router/discovery/discovery_network_monitor.h"
#include "chrome/browser/media/router/discovery/mdns/media_sink_util.h"
#include "chrome/browser/media/router/media_router_feature.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/media_router/media_sink.h"
#include "components/cast_channel/cast_socket_service.h"
#include "components/prefs/pref_service.h"
#include "net/base/host_port_pair.h"
#include "net/base/ip_address.h"
namespace media_router {
namespace {
enum ErrorType {
NONE,
NOT_CAST_DEVICE,
MISSING_ID,
MISSING_FRIENDLY_NAME,
MISSING_OR_INVALID_IP_ADDRESS,
MISSING_OR_INVALID_PORT,
};
ErrorType CreateCastMediaSink(const DnsSdService& service,
MediaSinkInternal* cast_sink) {
DCHECK(cast_sink);
if (service.service_name.find(CastMediaSinkService::kCastServiceType) ==
std::string::npos)
return ErrorType::NOT_CAST_DEVICE;
net::IPAddress ip_address;
if (!ip_address.AssignFromIPLiteral(service.ip_address))
return ErrorType::MISSING_OR_INVALID_IP_ADDRESS;
std::map<std::string, std::string> service_data;
for (const auto& item : service.service_data) {
// |item| format should be "id=xxxxxx", etc.
size_t split_idx = item.find('=');
if (split_idx == std::string::npos)
continue;
std::string key = item.substr(0, split_idx);
std::string val =
split_idx < item.length() ? item.substr(split_idx + 1) : "";
service_data[key] = val;
}
// When use this "sink" within browser, please note it will have a different
// ID when it is sent to the extension, because it derives a different sink ID
// using the given sink ID.
std::string unique_id = service_data["id"];
if (unique_id.empty())
return ErrorType::MISSING_ID;
std::string friendly_name = service_data["fn"];
if (friendly_name.empty())
return ErrorType::MISSING_FRIENDLY_NAME;
CastSinkExtraData extra_data;
extra_data.ip_endpoint =
net::IPEndPoint(ip_address, service.service_host_port.port());
extra_data.model_name = service_data["md"];
extra_data.capabilities = cast_channel::CastDeviceCapability::NONE;
unsigned capacities;
if (base::StringToUint(service_data["ca"], &capacities))
extra_data.capabilities = capacities;
std::string processed_uuid = MediaSinkInternal::ProcessDeviceUUID(unique_id);
std::string sink_id = base::StringPrintf("cast:<%s>", processed_uuid.c_str());
MediaSink sink(
sink_id, friendly_name,
CastMediaSinkServiceImpl::GetCastSinkIconType(extra_data.capabilities),
MediaRouteProviderId::CAST);
cast_sink->set_sink(sink);
cast_sink->set_cast_data(extra_data);
return ErrorType::NONE;
}
} // namespace
// static
const char CastMediaSinkService::kCastServiceType[] = "_googlecast._tcp.local";
CastMediaSinkService::CastMediaSinkService()
: impl_(nullptr, base::OnTaskRunnerDeleter(nullptr)),
weak_ptr_factory_(this) {}
......@@ -208,9 +132,9 @@ void CastMediaSinkService::OnDnsSdEvent(
for (const auto& service : services) {
// Create Cast sink from mDNS service description.
MediaSinkInternal cast_sink;
ErrorType error = CreateCastMediaSink(service, &cast_sink);
if (error != ErrorType::NONE) {
DVLOG(2) << "Fail to create Cast device [error]: " << error;
CreateCastMediaSinkResult result = CreateCastMediaSink(service, &cast_sink);
if (result != CreateCastMediaSinkResult::kOk) {
DVLOG(2) << "Fail to create Cast device [error]: " << result;
continue;
}
......
......@@ -28,9 +28,6 @@ namespace media_router {
// This class is not thread safe. All methods must be invoked on the UI thread.
class CastMediaSinkService : public DnsSdRegistry::DnsSdObserver {
public:
// mDNS service types.
static const char kCastServiceType[];
CastMediaSinkService();
~CastMediaSinkService() override;
......
......@@ -8,6 +8,7 @@
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/default_clock.h"
#include "chrome/browser/media/router/discovery/mdns/media_sink_util.h"
#include "chrome/browser/media/router/media_router_feature.h"
#include "chrome/common/media_router/discovery/media_sink_internal.h"
#include "chrome/common/media_router/media_sink.h"
......@@ -39,8 +40,7 @@ MediaSinkInternal CreateCastSinkFromDialSink(
CastSinkExtraData extra_data;
extra_data.ip_endpoint =
net::IPEndPoint(dial_sink.dial_data().ip_address,
CastMediaSinkServiceImpl::kCastControlPort);
net::IPEndPoint(dial_sink.dial_data().ip_address, kCastControlPort);
extra_data.model_name = dial_sink.dial_data().model_name;
extra_data.discovered_by_dial = true;
extra_data.capabilities = cast_channel::CastDeviceCapability::NONE;
......@@ -164,17 +164,6 @@ bool IsNetworkIdUnknownOrDisconnected(const std::string& network_id) {
// static
constexpr int CastMediaSinkServiceImpl::kMaxDialSinkFailureCount;
// static
SinkIconType CastMediaSinkServiceImpl::GetCastSinkIconType(
uint8_t capabilities) {
if (capabilities & cast_channel::CastDeviceCapability::VIDEO_OUT)
return SinkIconType::CAST;
return capabilities & cast_channel::CastDeviceCapability::MULTIZONE_GROUP
? SinkIconType::CAST_AUDIO_GROUP
: SinkIconType::CAST_AUDIO;
}
CastMediaSinkServiceImpl::CastMediaSinkServiceImpl(
const OnSinksDiscoveredCallback& callback,
Observer* observer,
......@@ -252,6 +241,10 @@ void CastMediaSinkServiceImpl::Start() {
network_monitor_->GetNetworkId(base::BindOnce(
&CastMediaSinkServiceImpl::OnNetworksChanged, GetWeakPtr()));
network_monitor_->AddObserver(this);
std::vector<MediaSinkInternal> test_sinks = GetFixedIPSinksFromCommandLine();
if (!test_sinks.empty())
OpenChannels(test_sinks, SinkSource::kMdns);
}
void CastMediaSinkServiceImpl::OnDiscoveryComplete() {
......
......@@ -54,17 +54,10 @@ class CastMediaSinkServiceImpl
using SinkSource = CastDeviceCountMetrics::SinkSource;
// Default Cast control port to open Cast Socket from DIAL sink.
static constexpr int kCastControlPort = 8009;
// The max number of cast channel open failure for a DIAL-discovered sink
// before we can say confidently that it is unlikely to be a Cast device.
static constexpr int kMaxDialSinkFailureCount = 10;
// Returns the icon type to use according to |capabilities|. |capabilities| is
// a bit set of cast_channel::CastDeviceCapabilities in CastSinkExtraData.
static SinkIconType GetCastSinkIconType(uint8_t capabilities);
// |callback|: Callback passed to MediaSinkServiceBase.
// |observer|: Observer to invoke on sink updates. Can be nullptr.
// |cast_socket_service|: CastSocketService to use to open Cast channels to
......
......@@ -11,6 +11,7 @@
#include "base/test/simple_test_clock.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/timer/mock_timer.h"
#include "chrome/browser/media/router/discovery/mdns/media_sink_util.h"
#include "chrome/browser/media/router/media_router_feature.h"
#include "chrome/browser/media/router/test/test_helper.h"
#include "components/cast_channel/cast_socket.h"
......@@ -562,9 +563,9 @@ TEST_F(CastMediaSinkServiceImplTest, TestOnDialSinkAdded) {
MediaSinkInternal dial_sink1 = CreateDialSink(1);
MediaSinkInternal dial_sink2 = CreateDialSink(2);
net::IPEndPoint ip_endpoint1(dial_sink1.dial_data().ip_address,
CastMediaSinkServiceImpl::kCastControlPort);
kCastControlPort);
net::IPEndPoint ip_endpoint2(dial_sink2.dial_data().ip_address,
CastMediaSinkServiceImpl::kCastControlPort);
kCastControlPort);
cast_channel::MockCastSocket socket1;
cast_channel::MockCastSocket socket2;
......@@ -607,7 +608,7 @@ TEST_F(CastMediaSinkServiceImplTest, TestOnDialSinkAdded) {
TEST_F(CastMediaSinkServiceImplTest, TestOnDialSinkAddedSkipsIfNonCastDevice) {
MediaSinkInternal dial_sink1 = CreateDialSink(1);
net::IPEndPoint ip_endpoint1(dial_sink1.dial_data().ip_address,
CastMediaSinkServiceImpl::kCastControlPort);
kCastControlPort);
cast_channel::MockCastSocket socket1;
socket1.set_id(1);
......@@ -1111,7 +1112,7 @@ TEST_F(CastMediaSinkServiceImplTest, CacheDialDiscoveredSinks) {
MediaSinkInternal sink2_dial = CreateDialSink(2);
net::IPEndPoint ip_endpoint1 = CreateIPEndPoint(1);
net::IPEndPoint ip_endpoint2(sink2_dial.dial_data().ip_address,
CastMediaSinkServiceImpl::kCastControlPort);
kCastControlPort);
std::vector<MediaSinkInternal> sink_list1{sink1_cast};
// Resolution will succeed for both sinks.
......@@ -1153,7 +1154,7 @@ TEST_F(CastMediaSinkServiceImplTest, CacheDialDiscoveredSinks) {
MediaSinkInternal sink4_dial = CreateDialSink(4);
net::IPEndPoint ip_endpoint3 = CreateIPEndPoint(3);
net::IPEndPoint ip_endpoint4(sink4_dial.dial_data().ip_address,
CastMediaSinkServiceImpl::kCastControlPort);
kCastControlPort);
std::vector<MediaSinkInternal> sink_list2{sink3_cast};
cast_channel::MockCastSocket socket3;
......@@ -1200,7 +1201,7 @@ TEST_F(CastMediaSinkServiceImplTest, DualDiscoveryDoesntDuplicateCacheItems) {
MediaSinkInternal sink1_dial = CreateDialSink(0);
net::IPEndPoint ip_endpoint1_cast = CreateIPEndPoint(0);
net::IPEndPoint ip_endpoint1_dial(sink1_dial.dial_data().ip_address,
CastMediaSinkServiceImpl::kCastControlPort);
kCastControlPort);
std::vector<MediaSinkInternal> sink_list1{sink1_cast};
// Dial discovery will succeed first.
......
......@@ -9,6 +9,7 @@
#include "base/test/test_simple_task_runner.h"
#include "base/timer/mock_timer.h"
#include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h"
#include "chrome/browser/media/router/discovery/mdns/media_sink_util.h"
#include "chrome/browser/media/router/test/mock_dns_sd_registry.h"
#include "chrome/browser/media/router/test/test_helper.h"
#include "components/cast_channel/cast_socket.h"
......@@ -38,8 +39,7 @@ media_router::DnsSdService CreateDnsService(int num, int capabilities) {
net::IPEndPoint ip_endpoint = CreateIPEndPoint(num);
media_router::DnsSdService service;
service.service_name =
"_myDevice." +
std::string(media_router::CastMediaSinkService::kCastServiceType);
"_myDevice." + std::string(media_router::kCastServiceType);
service.ip_address = ip_endpoint.address().ToString();
service.service_host_port = net::HostPortPair::FromIPEndPoint(ip_endpoint);
service.service_data.push_back(base::StringPrintf("id=service %d", num));
......@@ -179,8 +179,7 @@ TEST_F(CastMediaSinkServiceTest, TestOnDnsSdEvent) {
DnsSdRegistry::DnsSdServiceList service_list{service1, service2, service3};
// Invoke CastSocketService::OpenSocket on the IO thread.
media_sink_service_->OnDnsSdEvent(CastMediaSinkService::kCastServiceType,
service_list);
media_sink_service_->OnDnsSdEvent(kCastServiceType, service_list);
std::vector<MediaSinkInternal> sinks;
EXPECT_CALL(*mock_impl_, OpenChannels(_, _)).WillOnce(SaveArg<0>(&sinks));
......
// 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 "chrome/browser/media/router/discovery/mdns/media_sink_util.h"
#include <map>
#include "base/command_line.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/media/router/discovery/mdns/dns_sd_delegate.h"
#include "components/cast_channel/cast_socket.h"
#include "net/base/host_port_pair.h"
#include "net/base/ip_address.h"
#include "net/base/url_util.h"
namespace media_router {
SinkIconType GetCastSinkIconType(uint8_t capabilities) {
if (capabilities & cast_channel::CastDeviceCapability::VIDEO_OUT)
return SinkIconType::CAST;
return capabilities & cast_channel::CastDeviceCapability::MULTIZONE_GROUP
? SinkIconType::CAST_AUDIO_GROUP
: SinkIconType::CAST_AUDIO;
}
CreateCastMediaSinkResult CreateCastMediaSink(const DnsSdService& service,
MediaSinkInternal* cast_sink) {
DCHECK(cast_sink);
if (service.service_name.find(kCastServiceType) == std::string::npos)
return CreateCastMediaSinkResult::kNotCastDevice;
net::IPAddress ip_address;
if (!ip_address.AssignFromIPLiteral(service.ip_address))
return CreateCastMediaSinkResult::kMissingOrInvalidIPAddress;
std::map<std::string, std::string> service_data;
for (const auto& item : service.service_data) {
// |item| format should be "id=xxxxxx", etc.
size_t split_idx = item.find('=');
if (split_idx == std::string::npos)
continue;
std::string key = item.substr(0, split_idx);
std::string val =
split_idx < item.length() ? item.substr(split_idx + 1) : "";
service_data[key] = val;
}
// When use this "sink" within browser, please note it will have a different
// ID when it is sent to the extension, because it derives a different sink ID
// using the given sink ID.
std::string unique_id = service_data["id"];
if (unique_id.empty())
return CreateCastMediaSinkResult::kMissingID;
std::string friendly_name = service_data["fn"];
if (friendly_name.empty())
return CreateCastMediaSinkResult::kMissingFriendlyName;
CastSinkExtraData extra_data;
extra_data.ip_endpoint =
net::IPEndPoint(ip_address, service.service_host_port.port());
extra_data.model_name = service_data["md"];
extra_data.capabilities = cast_channel::CastDeviceCapability::NONE;
unsigned capacities;
if (base::StringToUint(service_data["ca"], &capacities))
extra_data.capabilities = capacities;
std::string processed_uuid = MediaSinkInternal::ProcessDeviceUUID(unique_id);
std::string sink_id = base::StringPrintf("cast:<%s>", processed_uuid.c_str());
MediaSink sink(sink_id, friendly_name,
GetCastSinkIconType(extra_data.capabilities),
MediaRouteProviderId::CAST);
cast_sink->set_sink(sink);
cast_sink->set_cast_data(extra_data);
return CreateCastMediaSinkResult::kOk;
}
std::vector<MediaSinkInternal> GetFixedIPSinksFromCommandLine() {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
std::string ips_string =
command_line->GetSwitchValueASCII(kFixedCastDeviceIps);
if (ips_string.empty())
return std::vector<MediaSinkInternal>();
std::vector<MediaSinkInternal> sinks;
std::vector<std::string> ips = base::SplitString(
ips_string, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
for (const auto& ip : ips) {
std::string host;
int port = -1;
if (!net::ParseHostAndPort(ip, &host, &port))
continue;
net::IPAddress ip_address;
if (!ip_address.AssignFromIPLiteral(host))
continue;
if (port == -1)
port = kCastControlPort;
std::string instance_name;
base::ReplaceChars(host, ".", "_", &instance_name);
instance_name = "Receiver-" + instance_name;
DnsSdService service;
service.service_name = instance_name + "._googlecast._tcp.local";
service.service_host_port = net::HostPortPair(host, port);
service.ip_address = host;
service.service_data = {
"id=mdns:" + instance_name, "ve=02", "ca=5", "st=1",
"fn=" + instance_name, "md=Chromecast"};
MediaSinkInternal sink;
CreateCastMediaSinkResult result = CreateCastMediaSink(service, &sink);
if (result == CreateCastMediaSinkResult::kOk) {
sinks.push_back(sink);
} else {
DVLOG(2) << "Failed to create sink from " << ip;
}
}
return sinks;
}
} // namespace media_router
// 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 CHROME_BROWSER_MEDIA_ROUTER_DISCOVERY_MDNS_MEDIA_SINK_UTIL_H_
#define CHROME_BROWSER_MEDIA_ROUTER_DISCOVERY_MDNS_MEDIA_SINK_UTIL_H_
#include <string>
#include <vector>
#include "chrome/common/media_router/discovery/media_sink_internal.h"
namespace media_router {
struct DnsSdService;
// The DNS-SD service type for Cast devices.
static constexpr char kCastServiceType[] = "_googlecast._tcp.local";
// Default Cast control port to open Cast Socket.
static constexpr int kCastControlPort = 8009;
// Returns the icon type to use according to |capabilities|. |capabilities| is
// a bit set of cast_channel::CastDeviceCapabilities in CastSinkExtraData.
SinkIconType GetCastSinkIconType(uint8_t capabilities);
enum CreateCastMediaSinkResult {
kOk,
kNotCastDevice,
kMissingID,
kMissingFriendlyName,
kMissingOrInvalidIPAddress,
kMissingOrInvalidPort,
};
// Creates a MediaSinkInternal from |service| and assigns the result to
// |cast_sink|. |cast_sink| is only valid if the returned result is |kOk|.
CreateCastMediaSinkResult CreateCastMediaSink(const DnsSdService& service,
MediaSinkInternal* cast_sink);
// Command line flag for a list of Cast device IPs to connect to at startup.
// The value should be a comma-separated list of IP endpoints.
static constexpr char kFixedCastDeviceIps[] = "media-router-cast-device-ips";
// Returns a list of Cast sinks whose IPs were specified in the command line
// flag |kFixedCastDeviceIps|.
std::vector<MediaSinkInternal> GetFixedIPSinksFromCommandLine();
} // namespace media_router
#endif // CHROME_BROWSER_MEDIA_ROUTER_DISCOVERY_MDNS_MEDIA_SINK_UTIL_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment