Commit 23a0140b authored by Derek Cheng's avatar Derek Cheng Committed by Commit Bot

[Cast MRP] In-browser Cast MRP part 2a.

Add utility class that handles messages between a Cast SDK client and
the Cast MRP ("internal messages"). The types of internal messages are:
- new session (mrp -> client)
- receiver action (mrp -> client)
- client connect (client -> mrp)
- app message (client <-> mrp)

In particular, client connect should be going away soon (with the use
of PresentationConnection in MRP). app message is bi-directional between
the client and the receiver device, where MRP acts as a pass through.


Also added a utility method to generate a stop session cast channel
message for a given receiver device and session id.


Bug: 809249
Change-Id: I62169dd5b163060464cc6d389c8d93fc4bb2425f
Reviewed-on: https://chromium-review.googlesource.com/1085768Reviewed-by: default avatarBernhard Bauer <bauerb@chromium.org>
Reviewed-by: default avatarBrandon Tolsch <btolsch@chromium.org>
Commit-Queue: Derek Cheng <imcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#577506}
parent 34cab752
......@@ -14,6 +14,7 @@ static_library("router") {
"//components/keyed_service/core",
"//content/public/browser",
"//content/public/common",
"//crypto",
"//net",
"//third_party/icu",
"//url",
......@@ -93,6 +94,8 @@ static_library("router") {
"providers/cast/cast_app_availability_tracker.h",
"providers/cast/cast_app_discovery_service.cc",
"providers/cast/cast_app_discovery_service.h",
"providers/cast/cast_internal_message_util.cc",
"providers/cast/cast_internal_message_util.h",
"providers/cast/cast_media_route_provider.cc",
"providers/cast/cast_media_route_provider.h",
"providers/cast/cast_media_route_provider_metrics.cc",
......
......@@ -4,10 +4,13 @@
#include "chrome/browser/media/router/media_router_feature.h"
#include "base/base64.h"
#include "base/feature_list.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/browser_context.h"
#include "crypto/random.h"
#include "extensions/buildflags/buildflags.h"
#include "ui/base/ui_features.h"
......@@ -73,6 +76,12 @@ void RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
PrefRegistry::PUBLIC);
}
void RegisterProfilePrefs(PrefRegistrySimple* registry) {
// TODO(imcheng): Migrate existing Media Router prefs to here.
registry->RegisterStringPref(prefs::kMediaRouterReceiverIdHashToken, "",
PrefRegistry::PUBLIC);
}
const base::Feature kCastAllowAllIPsFeature{"CastAllowAllIPs",
base::FEATURE_DISABLED_BY_DEFAULT};
......@@ -90,6 +99,19 @@ bool GetCastAllowAllIPsPref(PrefService* pref_service) {
return allow_all_ips;
}
std::string GetReceiverIdHashToken(PrefService* pref_service) {
static constexpr size_t kHashTokenSize = 64;
std::string token =
pref_service->GetString(prefs::kMediaRouterReceiverIdHashToken);
if (token.empty()) {
crypto::RandBytes(base::WriteInto(&token, kHashTokenSize + 1),
kHashTokenSize);
base::Base64Encode(token, &token);
pref_service->SetString(prefs::kMediaRouterReceiverIdHashToken, token);
}
return token;
}
bool DialMediaRouteProviderEnabled() {
return base::FeatureList::IsEnabled(kDialMediaRouteProvider);
}
......
......@@ -25,11 +25,18 @@ namespace prefs {
// Pref name for the enterprise policy for allowing Cast devices on all IPs.
constexpr char kMediaRouterCastAllowAllIPs[] =
"media_router.cast_allow_all_ips";
// Pref name for the per-profile randomly generated token to include with the
// hash when externalizing MediaSink IDs.
constexpr char kMediaRouterReceiverIdHashToken[] =
"media_router.receiver_id_hash_token";
} // namespace prefs
// Registers |kMediaRouterCastAllowAllIPs| with local state pref |registry|.
void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
// Registers Media Router related preferences with per-profile pref |registry|.
void RegisterProfilePrefs(PrefRegistrySimple* registry);
// If enabled, allows Media Router to connect to Cast devices on all IP
// addresses, not just RFC1918/RFC4913 private addresses. Workaround for
// https://crbug.com/813974.
......@@ -39,6 +46,11 @@ extern const base::Feature kCastAllowAllIPsFeature;
// all IPs, as determined by local state |pref_service| / feature flag.
bool GetCastAllowAllIPsPref(PrefService* pref_service);
// Returns the hash token to use for externalizing MediaSink IDs from
// |pref_service|. If the token does not exist, the token will be created from a
// randomly generated string and stored in |pref_service|.
std::string GetReceiverIdHashToken(PrefService* pref_service);
extern const base::Feature kEnableDialSinkQuery;
extern const base::Feature kEnableCastDiscovery;
extern const base::Feature kCastMediaRouteProvider;
......
......@@ -30,4 +30,16 @@ TEST(MediaRouterFeatureTest, GetCastAllowAllIPsPref) {
EXPECT_FALSE(GetCastAllowAllIPsPref(pref_service.get()));
}
TEST(MediaRouterFeatureTest, GetReceiverIdHashToken) {
auto pref_service = std::make_unique<TestingPrefServiceSimple>();
pref_service->registry()->RegisterStringPref(
prefs::kMediaRouterReceiverIdHashToken, "");
std::string token = GetReceiverIdHashToken(pref_service.get());
EXPECT_FALSE(token.empty());
// Token stays the same on subsequent invocation.
EXPECT_EQ(token, GetReceiverIdHashToken(pref_service.get()));
}
} // namespace media_router
......@@ -76,7 +76,7 @@ TEST_F(CastAppAvailabilityTrackerTest, RegisterSourceReturnsMultipleAppIds) {
TEST_F(CastAppAvailabilityTrackerTest, MultipleAppIdsAlreadyTrackingOne) {
// One of the mirroring app IDs.
auto source1 = CastMediaSource::From("cast:0F5096E8");
auto source1 = CastMediaSource::From("cast:0F5096E8?clientId=123");
ASSERT_TRUE(source1);
base::flat_set<std::string> new_app_ids = {"0F5096E8"};
......
// 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_PROVIDERS_CAST_CAST_INTERNAL_MESSAGE_UTIL_H_
#define CHROME_BROWSER_MEDIA_ROUTER_PROVIDERS_CAST_CAST_INTERNAL_MESSAGE_UTIL_H_
#include "base/containers/flat_set.h"
#include "base/macros.h"
#include "base/values.h"
#include "third_party/blink/public/platform/modules/presentation/presentation.mojom.h"
namespace cast_channel {
class CastMessage;
}
namespace media_router {
class MediaSinkInternal;
// Represents a message sent or received by the Cast SDK via a
// PresentationConnection.
struct CastInternalMessage {
// TODO(crbug.com/809249): Add other types of messages.
enum class Type {
kClientConnect, // Initial message sent by SDK client to connect to MRP.
kAppMessage, // App messages to pass through between SDK client and the
// receiver.
kReceiverAction, // Message sent by MRP to inform SDK client of action.
kNewSession, // Message sent by MRP to inform SDK client of new
// session.
kOther // All other types of messages which are not considered
// part of communication with Cast SDK.
};
// Returns a CastInternalMessage for |message|, or nullptr is |message| is not
// a valid Cast internal message.
static std::unique_ptr<CastInternalMessage> From(base::Value message);
CastInternalMessage(Type type, const std::string& client_id);
~CastInternalMessage();
Type type;
std::string client_id;
int sequence_number = -1;
// The following are set if |type| is |kAppMessage|.
std::string app_message_namespace;
std::string app_message_session_id;
base::Value app_message_body;
DISALLOW_COPY_AND_ASSIGN(CastInternalMessage);
};
// Represents a Cast session on a Cast device. Cast sessions are derived from
// RECEIVER_STATUS messages sent by Cast devices.
class CastSession {
public:
// Returns a CastSession from |receiver_status| message sent by |sink|, or
// nullptr if |receiver_status| is not a valid RECEIVER_STATUS message.
// |hash_token| is a per-profile value that is used to hash the sink ID.
static std::unique_ptr<CastSession> From(const MediaSinkInternal& sink,
const std::string& hash_token,
const base::Value& receiver_status);
// Returns a string that can be used as the description of the MediaRoute
// associated with this session.
static std::string GetRouteDescription(const CastSession& session);
CastSession();
~CastSession();
// ID of the session.
std::string session_id;
// ID of the app in the session.
std::string app_id;
// ID used for communicating with the session over the Cast channel.
std::string transport_id;
// The set of accepted message namespaces. Must be non-empty, unless the
// session represents a multizone leader.
base::flat_set<std::string> message_namespaces;
// The human-readable name of the Cast application, for example, "YouTube".
// Mandatory.
std::string display_name;
// Descriptive text for the current application content, for example “My
// Wedding Slideshow”. May be empty.
std::string status;
// The dictionary representing this session, derived from |receiver_status|.
// For convenience, this is used for generating messages sent to the SDK that
// include the session value.
base::Value value;
};
// Utility methods for generating messages sent to the SDK.
// |hash_token| is a per-profile value that is used to hash the sink ID.
blink::mojom::PresentationConnectionMessagePtr CreateReceiverActionCastMessage(
const std::string& client_id,
const MediaSinkInternal& sink,
const std::string& hash_token);
blink::mojom::PresentationConnectionMessagePtr CreateReceiverActionStopMessage(
const std::string& client_id,
const MediaSinkInternal& sink,
const std::string& hash_token);
blink::mojom::PresentationConnectionMessagePtr CreateNewSessionMessage(
const CastSession& session,
const std::string& client_id);
blink::mojom::PresentationConnectionMessagePtr CreateAppMessageAck(
const std::string& client_id,
int sequence_number);
blink::mojom::PresentationConnectionMessagePtr CreateAppMessage(
const std::string& session_id,
const std::string& client_id,
const cast_channel::CastMessage& cast_message);
} // namespace media_router
#endif // CHROME_BROWSER_MEDIA_ROUTER_PROVIDERS_CAST_CAST_INTERNAL_MESSAGE_UTIL_H_
......@@ -64,7 +64,7 @@ TEST_F(CastMediaRouteProviderTest, StartObservingMediaSinks) {
EXPECT_CALL(app_discovery_service_, DoStartObservingMediaSinks(_)).Times(0);
provider_->StartObservingMediaSinks(non_cast_source);
MediaSource::Id cast_source("cast:ABCDEFGH");
MediaSource::Id cast_source("cast:ABCDEFGH?clientId=123");
EXPECT_CALL(app_discovery_service_, DoStartObservingMediaSinks(_));
provider_->StartObservingMediaSinks(cast_source);
EXPECT_FALSE(app_discovery_service_.callbacks().empty());
......@@ -78,6 +78,7 @@ TEST_F(CastMediaRouteProviderTest, BroadcastRequest) {
media_sink_service_.AddOrUpdateSink(CreateCastSink(2));
MediaSource::Id source_id(
"cast:ABCDEFAB?capabilities=video_out,audio_out"
"&clientId=123"
"&broadcastNamespace=namespace"
"&broadcastMessage=message");
......
......@@ -28,6 +28,7 @@
#include "chrome/browser/media/media_device_id_salt.h"
#include "chrome/browser/media/media_engagement_service.h"
#include "chrome/browser/media/media_storage_id_salt.h"
#include "chrome/browser/media/router/media_router_feature.h"
#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
#include "chrome/browser/media/webrtc/media_stream_devices_controller.h"
#include "chrome/browser/metrics/chrome_metrics_service_client.h"
......@@ -651,6 +652,7 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
InstantService::RegisterProfilePrefs(registry);
gcm::GCMChannelStatusSyncer::RegisterProfilePrefs(registry);
gcm::RegisterProfilePrefs(registry);
media_router::RegisterProfilePrefs(registry);
ntp_tiles::CustomLinksManagerImpl::RegisterProfilePrefs(registry);
StartupBrowserCreator::RegisterProfilePrefs(registry);
#endif
......
......@@ -4,6 +4,7 @@
#include "chrome/common/media_router/providers/cast/cast_media_source.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
......@@ -18,18 +19,19 @@ namespace media_router {
namespace {
constexpr char kMirroringAppId[] = "0F5096E8";
constexpr char kAudioMirroringAppId[] = "85CDB22F";
// Parameter keys used by new Cast URLs.
constexpr char kCapabilitiesKey[] = "capabilities";
constexpr char kBroadcastNamespaceKey[] = "broadcastNamespace";
constexpr char kBroadcastMessageKey[] = "broadcastMessage";
constexpr char kClientIdKey[] = "clientId";
constexpr char kLaunchTimeoutKey[] = "launchTimeout";
// Parameter keys used by legacy Cast URLs.
constexpr char kLegacyAppIdKey[] = "__castAppId__";
constexpr char kLegacyBroadcastNamespaceKey[] = "__castBroadcastNamespace__";
constexpr char kLegacyBroadcastMessageKey[] = "__castBroadcastMessage__";
constexpr char kLegacyClientIdKey[] = "__castClientId__";
constexpr char kLegacyLaunchTimeoutKey[] = "__castLaunchTimeout__";
// TODO(imcheng): Move to common utils?
std::string DecodeURLComponent(const std::string& encoded) {
......@@ -61,8 +63,9 @@ cast_channel::CastDeviceCapability CastDeviceCapabilityFromString(
std::unique_ptr<CastMediaSource> CastMediaSourceForTabMirroring(
const MediaSource::Id& source_id) {
return std::make_unique<CastMediaSource>(
source_id, std::vector<CastAppInfo>({CastAppInfo(kMirroringAppId),
CastAppInfo(kAudioMirroringAppId)}));
source_id,
std::vector<CastAppInfo>({CastAppInfo(kCastStreamingAppId),
CastAppInfo(kCastStreamingAudioAppId)}));
}
std::unique_ptr<CastMediaSource> CastMediaSourceForDesktopMirroring(
......@@ -70,28 +73,33 @@ std::unique_ptr<CastMediaSource> CastMediaSourceForDesktopMirroring(
// Desktop audio mirroring is only supported on some platforms.
#if defined(OS_WIN) || defined(OS_CHROMEOS)
return std::make_unique<CastMediaSource>(
source_id, std::vector<CastAppInfo>({CastAppInfo(kMirroringAppId),
CastAppInfo(kAudioMirroringAppId)}));
source_id,
std::vector<CastAppInfo>({CastAppInfo(kCastStreamingAppId),
CastAppInfo(kCastStreamingAudioAppId)}));
#else
return std::make_unique<CastMediaSource>(
source_id, std::vector<CastAppInfo>({CastAppInfo(kMirroringAppId)}));
source_id, std::vector<CastAppInfo>({CastAppInfo(kCastStreamingAppId)}));
#endif
}
std::unique_ptr<CastMediaSource> CreateFromURLParams(
const MediaSource::Id& source_id,
const std::vector<CastAppInfo>& app_infos,
const std::string& client_id,
const std::string& broadcast_namespace,
const std::string& broadcast_message) {
const std::string& broadcast_message,
base::TimeDelta launch_timeout) {
if (app_infos.empty())
return nullptr;
auto cast_source = std::make_unique<CastMediaSource>(source_id, app_infos);
cast_source->set_client_id(client_id);
if (!broadcast_namespace.empty() && !broadcast_message.empty()) {
cast_source->set_broadcast_request(
BroadcastRequest(broadcast_namespace, broadcast_message));
}
if (launch_timeout > base::TimeDelta())
cast_source->set_launch_timeout(launch_timeout);
return cast_source;
}
......@@ -103,6 +111,8 @@ std::unique_ptr<CastMediaSource> ParseCastUrl(const MediaSource::Id& source_id,
return nullptr;
std::string broadcast_namespace, broadcast_message, capabilities;
std::string client_id;
int launch_timeout_millis = 0;
for (net::QueryIterator query_it(url); !query_it.IsAtEnd();
query_it.Advance()) {
std::string key = query_it.GetKey();
......@@ -116,6 +126,12 @@ std::unique_ptr<CastMediaSource> ParseCastUrl(const MediaSource::Id& source_id,
broadcast_message = DecodeURLComponent(value);
} else if (key == kCapabilitiesKey) {
capabilities = value;
} else if (key == kClientIdKey) {
client_id = value;
} else if (key == kLaunchTimeoutKey) {
if (!base::StringToInt(value, &launch_timeout_millis) ||
launch_timeout_millis < 0)
launch_timeout_millis = 0;
}
}
......@@ -129,8 +145,9 @@ std::unique_ptr<CastMediaSource> ParseCastUrl(const MediaSource::Id& source_id,
}
}
return CreateFromURLParams(source_id, {app_info}, broadcast_namespace,
broadcast_message);
return CreateFromURLParams(
source_id, {app_info}, client_id, broadcast_namespace, broadcast_message,
base::TimeDelta::FromMilliseconds(launch_timeout_millis));
}
std::unique_ptr<CastMediaSource> ParseLegacyCastUrl(
......@@ -141,6 +158,8 @@ std::unique_ptr<CastMediaSource> ParseLegacyCastUrl(
// Legacy URLs can specify multiple apps.
std::vector<std::string> app_id_params;
std::string broadcast_namespace, broadcast_message;
std::string client_id;
int launch_timeout_millis = 0;
for (const auto& key_value : parameters) {
const auto& key = key_value.first;
const auto& value = key_value.second;
......@@ -151,6 +170,12 @@ std::unique_ptr<CastMediaSource> ParseLegacyCastUrl(
} else if (key == kLegacyBroadcastMessageKey) {
// The broadcast message is URL-encoded.
broadcast_message = DecodeURLComponent(value);
} else if (key == kLegacyClientIdKey) {
client_id = value;
} else if (key == kLegacyLaunchTimeoutKey) {
if (!base::StringToInt(value, &launch_timeout_millis) ||
launch_timeout_millis < 0)
launch_timeout_millis = 0;
}
}
......@@ -187,8 +212,9 @@ std::unique_ptr<CastMediaSource> ParseLegacyCastUrl(
if (app_infos.empty())
return nullptr;
return CreateFromURLParams(source_id, app_infos, broadcast_namespace,
broadcast_message);
return CreateFromURLParams(
source_id, app_infos, client_id, broadcast_namespace, broadcast_message,
base::TimeDelta::FromMilliseconds(launch_timeout_millis));
}
} // namespace
......@@ -225,9 +251,6 @@ std::unique_ptr<CastMediaSource> CastMediaSource::From(
return nullptr;
}
CastMediaSource::CastMediaSource(const MediaSource::Id& source_id,
const CastAppInfo& app_info)
: source_id_(source_id), app_infos_({app_info}) {}
CastMediaSource::CastMediaSource(const MediaSource::Id& source_id,
const std::vector<CastAppInfo>& app_infos)
: source_id_(source_id), app_infos_(app_infos) {}
......
......@@ -16,6 +16,16 @@
namespace media_router {
static constexpr char kCastStreamingAppId[] = "0F5096E8";
static constexpr char kCastStreamingAudioAppId[] = "85CDB22F";
// Placeholder app ID advertised by the multizone leader in a receiver status
// message.
static constexpr char kMultizoneLeaderAppId[] = "MultizoneLeader";
static constexpr base::TimeDelta kDefaultLaunchTimeout =
base::TimeDelta::FromSeconds(60);
// Represents a Cast app and its capabilitity requirements.
struct CastAppInfo {
explicit CastAppInfo(const std::string& app_id);
......@@ -39,9 +49,7 @@ class CastMediaSource {
// Returns the parsed form of |source|, or nullptr if it cannot be parsed.
static std::unique_ptr<CastMediaSource> From(const MediaSource::Id& source);
explicit CastMediaSource(const MediaSource::Id& source_id,
const CastAppInfo& app_info);
explicit CastMediaSource(const MediaSource::Id& source_id,
CastMediaSource(const MediaSource::Id& source_id,
const std::vector<CastAppInfo>& app_infos);
CastMediaSource(const CastMediaSource& other);
~CastMediaSource();
......@@ -55,6 +63,12 @@ class CastMediaSource {
const MediaSource::Id& source_id() const { return source_id_; }
const std::vector<CastAppInfo>& app_infos() const { return app_infos_; }
const std::string& client_id() const { return client_id_; }
void set_client_id(const std::string& client_id) { client_id_ = client_id; }
base::TimeDelta launch_timeout() const { return launch_timeout_; }
void set_launch_timeout(base::TimeDelta launch_timeout) {
launch_timeout_ = launch_timeout;
}
const base::Optional<cast_channel::BroadcastRequest>& broadcast_request()
const {
return broadcast_request_;
......@@ -68,6 +82,9 @@ class CastMediaSource {
// TODO(imcheng): Fill in other parameters.
MediaSource::Id source_id_;
std::vector<CastAppInfo> app_infos_;
base::TimeDelta launch_timeout_ = kDefaultLaunchTimeout;
// Empty if not set.
std::string client_id_;
base::Optional<cast_channel::BroadcastRequest> broadcast_request_;
};
......
......@@ -13,7 +13,9 @@ TEST(CastMediaSourceTest, FromCastURL) {
MediaSource::Id source_id(
"cast:ABCDEFAB?capabilities=video_out,audio_out"
"&broadcastNamespace=namespace"
"&broadcastMessage=message");
"&broadcastMessage=message"
"&clientId=12345"
"&launchTimeout=30000");
std::unique_ptr<CastMediaSource> source = CastMediaSource::From(source_id);
ASSERT_TRUE(source);
EXPECT_EQ(source_id, source->source_id());
......@@ -27,13 +29,17 @@ TEST(CastMediaSourceTest, FromCastURL) {
ASSERT_TRUE(broadcast_request);
EXPECT_EQ("namespace", broadcast_request->broadcast_namespace);
EXPECT_EQ("message", broadcast_request->message);
EXPECT_EQ("12345", source->client_id());
EXPECT_EQ(base::TimeDelta::FromMilliseconds(30000), source->launch_timeout());
}
TEST(CastMediaSourceTest, FromLegacyCastURL) {
MediaSource::Id source_id(
"https://google.com/cast#__castAppId__=ABCDEFAB(video_out,audio_out)"
"/__castBroadcastNamespace__=namespace"
"/__castBroadcastMessage__=message");
"/__castBroadcastMessage__=message"
"/__castClientId__=12345"
"/__castLaunchTimeout__=30000");
std::unique_ptr<CastMediaSource> source = CastMediaSource::From(source_id);
ASSERT_TRUE(source);
EXPECT_EQ(source_id, source->source_id());
......@@ -47,6 +53,8 @@ TEST(CastMediaSourceTest, FromLegacyCastURL) {
ASSERT_TRUE(broadcast_request);
EXPECT_EQ("namespace", broadcast_request->broadcast_namespace);
EXPECT_EQ("message", broadcast_request->message);
EXPECT_EQ("12345", source->client_id());
EXPECT_EQ(base::TimeDelta::FromMilliseconds(30000), source->launch_timeout());
}
TEST(CastMediaSourceTest, FromPresentationURL) {
......@@ -57,6 +65,8 @@ TEST(CastMediaSourceTest, FromPresentationURL) {
ASSERT_EQ(2u, source->app_infos().size());
EXPECT_EQ("0F5096E8", source->app_infos()[0].app_id);
EXPECT_EQ("85CDB22F", source->app_infos()[1].app_id);
EXPECT_TRUE(source->client_id().empty());
EXPECT_EQ(kDefaultLaunchTimeout, source->launch_timeout());
}
TEST(CastMediaSourceTest, FromMirroringURN) {
......@@ -67,6 +77,8 @@ TEST(CastMediaSourceTest, FromMirroringURN) {
ASSERT_EQ(2u, source->app_infos().size());
EXPECT_EQ("0F5096E8", source->app_infos()[0].app_id);
EXPECT_EQ("85CDB22F", source->app_infos()[1].app_id);
EXPECT_TRUE(source->client_id().empty());
EXPECT_EQ(kDefaultLaunchTimeout, source->launch_timeout());
}
TEST(CastMediaSourceTest, FromInvalidSource) {
......
......@@ -3201,6 +3201,7 @@ test("unit_tests") {
"../browser/media/router/mojo/media_sink_service_status_unittest.cc",
"../browser/media/router/providers/cast/cast_app_availability_tracker_unittest.cc",
"../browser/media/router/providers/cast/cast_app_discovery_service_unittest.cc",
"../browser/media/router/providers/cast/cast_internal_message_util_unittest.cc",
"../browser/media/router/providers/cast/cast_media_route_provider_metrics_unittest.cc",
"../browser/media/router/providers/cast/cast_media_route_provider_unittest.cc",
"../browser/media/router/providers/cast/dual_media_sink_service_unittest.cc",
......
......@@ -34,13 +34,14 @@ constexpr char kBroadcastNamespace[] = "urn:x-cast:com.google.cast.broadcast";
constexpr char kTypeNodeId[] = "type";
constexpr char kRequestIdNodeId[] = "requestId";
// Cast application protocol message types.
// Cast application protocol message types. Keep in sync with CastMessageType.
constexpr char kKeepAlivePingType[] = "PING";
constexpr char kKeepAlivePongType[] = "PONG";
constexpr char kGetAppAvailabilityRequestType[] = "GET_APP_AVAILABILITY";
constexpr char kConnectionRequestType[] = "CONNECT";
constexpr char kBroadcastRequestType[] = "APPLICATION_BROADCAST";
constexpr char kLaunchRequestType[] = "LAUNCH";
constexpr char kStopRequestType[] = "STOP";
constexpr char kReceiverStatusType[] = "RECEIVER_STATUS";
constexpr char kLaunchErrorType[] = "LAUNCH_ERROR";
......@@ -107,6 +108,7 @@ std::unique_ptr<base::DictionaryValue> GetDictionaryFromCastMessage(
if (!message.has_payload_utf8())
return nullptr;
// TODO(https://crbug.com/809249): Parse JSON using data_decoder service.
return base::DictionaryValue::From(
base::JSONReader::Read(message.payload_utf8()));
}
......@@ -146,6 +148,8 @@ const char* CastMessageTypeToString(CastMessageType message_type) {
return kBroadcastRequestType;
case CastMessageType::kLaunch:
return kLaunchRequestType;
case CastMessageType::kStop:
return kStopRequestType;
case CastMessageType::kReceiverStatus:
return kReceiverStatusType;
case CastMessageType::kLaunchError:
......@@ -170,6 +174,8 @@ CastMessageType CastMessageTypeFromString(const std::string& type) {
return CastMessageType::kBroadcast;
if (type == kLaunchRequestType)
return CastMessageType::kLaunch;
if (type == kStopRequestType)
return CastMessageType::kStop;
if (type == kReceiverStatusType)
return CastMessageType::kReceiverStatus;
if (type == kLaunchErrorType)
......@@ -347,6 +353,17 @@ CastMessage CreateLaunchRequest(const std::string& source_id,
kPlatformReceiverId);
}
CastMessage CreateStopRequest(const std::string& source_id,
int request_id,
const std::string& session_id) {
Value dict(Value::Type::DICTIONARY);
dict.SetKey(kTypeNodeId, Value(kStopRequestType));
dict.SetKey(kRequestIdNodeId, Value(request_id));
dict.SetKey("sessionId", Value(session_id));
return CreateCastMessage(kReceiverNamespace, dict, source_id,
kPlatformReceiverId);
}
CastMessage CreateCastMessage(const std::string& message_namespace,
const base::Value& message,
const std::string& source_id,
......@@ -406,8 +423,7 @@ LaunchSessionResponse::LaunchSessionResponse(LaunchSessionResponse&& other) =
default;
LaunchSessionResponse::~LaunchSessionResponse() = default;
LaunchSessionResponse GetLaunchSessionResponse(
const base::DictionaryValue& payload) {
LaunchSessionResponse GetLaunchSessionResponse(const base::Value& payload) {
const Value* type_value =
payload.FindKeyOfType(kTypeNodeId, Value::Type::STRING);
if (!type_value)
......
......@@ -27,6 +27,7 @@ enum class CastMessageType {
kConnect, // Virtual connection request
kBroadcast, // Application broadcast / precache
kLaunch, // Session launch request
kStop, // Session stop request
kReceiverStatus,
kLaunchError,
kOther // Add new types above |kOther|.
......@@ -132,6 +133,10 @@ CastMessage CreateLaunchRequest(const std::string& source_id,
const std::string& app_id,
const std::string& locale);
CastMessage CreateStopRequest(const std::string& source_id,
int request_id,
const std::string& session_id);
// Creates a generic CastMessage with |message| as the string payload. Used for
// app messages.
CastMessage CreateCastMessage(const std::string& message_namespace,
......@@ -168,16 +173,15 @@ struct LaunchSessionResponse {
~LaunchSessionResponse();
Result result = Result::kUnknown;
// Populated if |result| is |kOk|.
base::Optional<base::Value> receiver_status;
};
// Parses |payload| into a LaunchSessionResponse. Returns an empty
// LaunchSessionResponse if |payload| is not a properly formatted launch
// response. |payload| must be from the string payload of a CastMessage.
LaunchSessionResponse GetLaunchSessionResponse(
const base::DictionaryValue& payload);
// response. |payload| must be a dictionary from the string payload of a
// CastMessage.
LaunchSessionResponse GetLaunchSessionResponse(const base::Value& payload);
} // namespace cast_channel
......
......@@ -6,6 +6,7 @@
#include "base/json/json_reader.h"
#include "base/values.h"
#include "components/cast_channel/proto/cast_channel.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cast_channel {
......@@ -34,8 +35,7 @@ TEST(CastMessageUtilTest, GetLaunchSessionResponseOk) {
}
)";
std::unique_ptr<base::DictionaryValue> value =
base::DictionaryValue::From(base::JSONReader::Read(payload));
std::unique_ptr<base::Value> value = base::JSONReader::Read(payload);
ASSERT_TRUE(value);
LaunchSessionResponse response = GetLaunchSessionResponse(*value);
......@@ -51,8 +51,7 @@ TEST(CastMessageUtilTest, GetLaunchSessionResponseError) {
}
)";
std::unique_ptr<base::DictionaryValue> value =
base::DictionaryValue::From(base::JSONReader::Read(payload));
std::unique_ptr<base::Value> value = base::JSONReader::Read(payload);
ASSERT_TRUE(value);
LaunchSessionResponse response = GetLaunchSessionResponse(*value);
......@@ -70,8 +69,7 @@ TEST(CastMessageUtilTest, GetLaunchSessionResponseUnknown) {
}
)";
std::unique_ptr<base::DictionaryValue> value =
base::DictionaryValue::From(base::JSONReader::Read(payload));
std::unique_ptr<base::Value> value = base::JSONReader::Read(payload);
ASSERT_TRUE(value);
LaunchSessionResponse response = GetLaunchSessionResponse(*value);
......@@ -79,4 +77,26 @@ TEST(CastMessageUtilTest, GetLaunchSessionResponseUnknown) {
EXPECT_FALSE(response.receiver_status);
}
TEST(CastMessageUtilTest, CreateStopRequest) {
std::string expected_message = R"(
{
"type": "STOP",
"requestId": 123,
"sessionId": "sessionId"
}
)";
std::unique_ptr<base::Value> expected_value =
base::JSONReader::Read(expected_message);
ASSERT_TRUE(expected_value);
CastMessage message = CreateStopRequest("sourceId", 123, "sessionId");
ASSERT_TRUE(IsCastMessageValid(message));
std::unique_ptr<base::Value> actual_value =
base::JSONReader::Read(message.payload_utf8());
ASSERT_TRUE(actual_value);
EXPECT_EQ(*expected_value, *actual_value);
}
} // namespace cast_channel
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