blob: 87b6febe67fcb7b284d8f13e708491cf8dffb9e9 [file] [log] [blame]
// 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/providers/cast/cast_media_route_provider.h"
#include "base/stl_util.h"
#include "chrome/browser/media/router/data_decoder_util.h"
#include "chrome/browser/media/router/providers/cast/cast_activity_manager.h"
#include "chrome/browser/media/router/providers/cast/cast_internal_message_util.h"
#include "chrome/common/media_router/mojo/media_router.mojom.h"
#include "chrome/common/media_router/providers/cast/cast_media_source.h"
#include "components/cast_channel/cast_message_handler.h"
#include "url/origin.h"
namespace media_router {
namespace {
// Returns a list of origins that are valid for |source_id|. An empty list
// means all origins are valid.
std::vector<url::Origin> GetOrigins(const MediaSource::Id& source_id) {
// Use of the mirroring app as a Cast URL is permitted for Slides as a
// temporary workaround only. The eventual goal is to support their usecase
// using generic Presentation API.
// See also cast_media_source.cc.
static const char kMirroringAppPrefix[] = "cast:0F5096E8";
return base::StartsWith(source_id, kMirroringAppPrefix,
base::CompareCase::SENSITIVE)
? std::vector<url::Origin>(
{url::Origin::Create(GURL("https://docs.google.com"))})
: std::vector<url::Origin>();
}
} // namespace
CastMediaRouteProvider::CastMediaRouteProvider(
mojom::MediaRouteProviderRequest request,
mojom::MediaRouterPtrInfo media_router,
MediaSinkServiceBase* media_sink_service,
CastAppDiscoveryService* app_discovery_service,
cast_channel::CastMessageHandler* message_handler,
service_manager::Connector* connector,
const std::string& hash_token,
const scoped_refptr<base::SequencedTaskRunner>& task_runner)
: binding_(this),
media_sink_service_(media_sink_service),
app_discovery_service_(app_discovery_service),
message_handler_(message_handler) {
DETACH_FROM_SEQUENCE(sequence_checker_);
DCHECK(media_sink_service_);
DCHECK(app_discovery_service_);
DCHECK(message_handler_);
task_runner->PostTask(
FROM_HERE,
base::BindOnce(&CastMediaRouteProvider::Init, base::Unretained(this),
std::move(request), std::move(media_router),
CastSessionTracker::GetInstance(),
std::make_unique<DataDecoder>(connector), hash_token));
}
void CastMediaRouteProvider::Init(mojom::MediaRouteProviderRequest request,
mojom::MediaRouterPtrInfo media_router,
CastSessionTracker* session_tracker,
std::unique_ptr<DataDecoder> data_decoder,
const std::string& hash_token) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
binding_.Bind(std::move(request));
media_router_.Bind(std::move(media_router));
activity_manager_ = std::make_unique<CastActivityManager>(
media_sink_service_, session_tracker, message_handler_,
media_router_.get(), std::move(data_decoder), hash_token);
// TODO(crbug.com/816702): This needs to be set properly according to sinks
// discovered.
media_router_->OnSinkAvailabilityUpdated(
MediaRouteProviderId::CAST,
mojom::MediaRouter::SinkAvailability::PER_SOURCE);
}
CastMediaRouteProvider::~CastMediaRouteProvider() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(sink_queries_.empty());
}
void CastMediaRouteProvider::CreateRoute(const std::string& media_source,
const std::string& sink_id,
const std::string& presentation_id,
const url::Origin& origin,
int32_t tab_id,
base::TimeDelta timeout,
bool incognito,
CreateRouteCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// TODO(https://crbug.com/809249): Handle mirroring routes, including
// mirror-to-Cast transitions.
const MediaSinkInternal* sink = media_sink_service_->GetSinkById(sink_id);
if (!sink) {
std::move(callback).Run(base::nullopt, nullptr,
std::string("Sink not found"),
RouteRequestResult::ResultCode::SINK_NOT_FOUND);
return;
}
std::unique_ptr<CastMediaSource> cast_source =
CastMediaSource::FromMediaSourceId(media_source);
if (!cast_source) {
std::move(callback).Run(
base::nullopt, nullptr, std::string("Invalid source"),
RouteRequestResult::ResultCode::NO_SUPPORTED_PROVIDER);
return;
}
activity_manager_->LaunchSession(*cast_source, *sink, presentation_id, origin,
tab_id, incognito, std::move(callback));
}
void CastMediaRouteProvider::JoinRoute(const std::string& media_source,
const std::string& presentation_id,
const url::Origin& origin,
int32_t tab_id,
base::TimeDelta timeout,
bool incognito,
JoinRouteCallback callback) {
NOTIMPLEMENTED();
std::move(callback).Run(
base::nullopt, nullptr, std::string("Not implemented"),
RouteRequestResult::ResultCode::NO_SUPPORTED_PROVIDER);
}
void CastMediaRouteProvider::ConnectRouteByRouteId(
const std::string& media_source,
const std::string& route_id,
const std::string& presentation_id,
const url::Origin& origin,
int32_t tab_id,
base::TimeDelta timeout,
bool incognito,
ConnectRouteByRouteIdCallback callback) {
NOTIMPLEMENTED();
std::move(callback).Run(
base::nullopt, nullptr, std::string("Not implemented"),
RouteRequestResult::ResultCode::NO_SUPPORTED_PROVIDER);
}
void CastMediaRouteProvider::TerminateRoute(const std::string& route_id,
TerminateRouteCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
activity_manager_->TerminateSession(route_id, std::move(callback));
}
void CastMediaRouteProvider::SendRouteMessage(const std::string& media_route_id,
const std::string& message) {
NOTIMPLEMENTED();
}
void CastMediaRouteProvider::SendRouteBinaryMessage(
const std::string& media_route_id,
const std::vector<uint8_t>& data) {
NOTIMPLEMENTED();
}
void CastMediaRouteProvider::StartObservingMediaSinks(
const std::string& media_source) {
DVLOG(1) << __func__ << ", media_source: " << media_source;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (base::ContainsKey(sink_queries_, media_source))
return;
std::unique_ptr<CastMediaSource> cast_source =
CastMediaSource::FromMediaSourceId(media_source);
if (!cast_source)
return;
// A broadcast request is not an actual sink query; it is used to send a
// app precache message to receivers.
if (cast_source->broadcast_request()) {
// TODO(imcheng): Add metric to record broadcast usage.
BroadcastMessageToSinks(cast_source->GetAppIds(),
*cast_source->broadcast_request());
return;
}
sink_queries_[media_source] =
app_discovery_service_->StartObservingMediaSinks(
*cast_source,
base::BindRepeating(&CastMediaRouteProvider::OnSinkQueryUpdated,
base::Unretained(this)));
}
void CastMediaRouteProvider::StopObservingMediaSinks(
const std::string& media_source) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
sink_queries_.erase(media_source);
}
void CastMediaRouteProvider::StartObservingMediaRoutes(
const std::string& media_source) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
activity_manager_->AddRouteQuery(media_source);
}
void CastMediaRouteProvider::StopObservingMediaRoutes(
const std::string& media_source) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
activity_manager_->RemoveRouteQuery(media_source);
}
void CastMediaRouteProvider::StartListeningForRouteMessages(
const std::string& route_id) {
NOTIMPLEMENTED();
}
void CastMediaRouteProvider::StopListeningForRouteMessages(
const std::string& route_id) {
NOTIMPLEMENTED();
}
void CastMediaRouteProvider::DetachRoute(const std::string& route_id) {
NOTIMPLEMENTED();
}
void CastMediaRouteProvider::EnableMdnsDiscovery() {
NOTIMPLEMENTED();
}
void CastMediaRouteProvider::UpdateMediaSinks(const std::string& media_source) {
app_discovery_service_->Refresh();
}
void CastMediaRouteProvider::SearchSinks(
const std::string& sink_id,
const std::string& media_source,
mojom::SinkSearchCriteriaPtr search_criteria,
SearchSinksCallback callback) {
std::move(callback).Run(std::string());
}
void CastMediaRouteProvider::ProvideSinks(
const std::string& provider_name,
const std::vector<media_router::MediaSinkInternal>& sinks) {
NOTIMPLEMENTED();
}
void CastMediaRouteProvider::CreateMediaRouteController(
const std::string& route_id,
mojom::MediaControllerRequest media_controller,
mojom::MediaStatusObserverPtr observer,
CreateMediaRouteControllerCallback callback) {
NOTIMPLEMENTED();
std::move(callback).Run(false);
}
void CastMediaRouteProvider::OnSinkQueryUpdated(
const MediaSource::Id& source_id,
const std::vector<MediaSinkInternal>& sinks) {
DVLOG(1) << __func__ << ", source_id: " << source_id
<< ", #sinks: " << sinks.size();
media_router_->OnSinksReceived(MediaRouteProviderId::CAST, source_id, sinks,
GetOrigins(source_id));
}
void CastMediaRouteProvider::BroadcastMessageToSinks(
const std::vector<std::string>& app_ids,
const cast_channel::BroadcastRequest& request) {
for (const auto& id_and_sink : media_sink_service_->GetSinks()) {
const MediaSinkInternal& sink = id_and_sink.second;
message_handler_->SendBroadcastMessage(sink.cast_data().cast_channel_id,
app_ids, request);
}
}
} // namespace media_router