blob: 492450b18e44f9a621fdf2a860db472f0112c2d8 [file] [log] [blame]
// Copyright 2019 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/app_activity.h"
#include <algorithm>
#include <memory>
#include <vector>
#include "base/bind.h"
#include "base/containers/contains.h"
#include "chrome/browser/media/router/providers/cast/cast_activity_manager.h"
#include "chrome/browser/media/router/providers/cast/cast_session_client.h"
#include "components/cast_channel/enum_table.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/origin.h"
using blink::mojom::PresentationConnectionCloseReason;
using blink::mojom::PresentationConnectionMessagePtr;
namespace media_router {
AppActivity::AppActivity(const MediaRoute& route,
const std::string& app_id,
cast_channel::CastMessageHandler* message_handler,
CastSessionTracker* session_tracker)
: CastActivity(route, app_id, message_handler, session_tracker) {}
AppActivity::~AppActivity() = default;
void AppActivity::OnSessionSet(const CastSession& session) {
if (media_controller_)
media_controller_->SetSession(session);
}
void AppActivity::OnSessionUpdated(const CastSession& session,
const std::string& hash_token) {
for (auto& client : connected_clients_) {
client.second->SendMessageToClient(
CreateUpdateSessionMessage(session, client.first, sink_, hash_token));
}
if (media_controller_)
media_controller_->SetSession(session);
}
cast_channel::Result AppActivity::SendAppMessageToReceiver(
const CastInternalMessage& cast_message) {
CastSessionClient* client = GetClient(cast_message.client_id());
const CastSession* session = GetSession();
if (!session) {
if (client && cast_message.sequence_number()) {
client->SendErrorCodeToClient(
*cast_message.sequence_number(),
CastInternalMessage::ErrorCode::kSessionError,
"Invalid session ID: " + session_id_.value_or("<missing>"));
}
return cast_channel::Result::kFailed;
}
const std::string& message_namespace = cast_message.app_message_namespace();
if (!base::Contains(session->message_namespaces(), message_namespace)) {
DLOG(ERROR) << "Disallowed message namespace: " << message_namespace;
if (client && cast_message.sequence_number()) {
client->SendErrorCodeToClient(
*cast_message.sequence_number(),
CastInternalMessage::ErrorCode::kInvalidParameter,
"Invalid namespace: " + message_namespace);
}
return cast_channel::Result::kFailed;
}
return message_handler_->SendAppMessage(
cast_channel_id(),
cast_channel::CreateCastMessage(
message_namespace, cast_message.app_message_body(),
cast_message.client_id(), session->transport_id()));
}
absl::optional<int> AppActivity::SendMediaRequestToReceiver(
const CastInternalMessage& cast_message) {
CastSession* session = GetSession();
if (!session)
return absl::nullopt;
return message_handler_->SendMediaRequest(
cast_channel_id(), cast_message.v2_message_body(),
cast_message.client_id(), session->transport_id());
}
void AppActivity::SendSetVolumeRequestToReceiver(
const CastInternalMessage& cast_message,
cast_channel::ResultCallback callback) {
message_handler_->SendSetVolumeRequest(
cast_channel_id(), cast_message.v2_message_body(),
cast_message.client_id(), std::move(callback));
}
void AppActivity::SendMediaStatusToClients(const base::Value& media_status,
absl::optional<int> request_id) {
CastActivity::SendMediaStatusToClients(media_status, request_id);
if (media_controller_)
media_controller_->SetMediaStatus(media_status);
}
void AppActivity::CreateMediaController(
mojo::PendingReceiver<mojom::MediaController> media_controller,
mojo::PendingRemote<mojom::MediaStatusObserver> observer) {
media_controller_ = std::make_unique<CastMediaController>(
this, std::move(media_controller), std::move(observer));
if (session_id_) {
CastSession* session = GetSession();
if (session) {
media_controller_->SetSession(*session);
base::Value status_request(base::Value::Type::DICTIONARY);
status_request.SetStringKey(
"type", cast_util::EnumToString<
cast_channel::V2MessageType,
cast_channel::V2MessageType::kMediaGetStatus>());
message_handler_->SendMediaRequest(cast_channel_id(), status_request,
media_controller_->sender_id(),
session->transport_id());
}
}
}
void AppActivity::OnAppMessage(const cast::channel::CastMessage& message) {
if (!session_id_) {
DVLOG(2) << "No session associated with activity!";
return;
}
const std::string& client_id = message.destination_id();
if (client_id == "*") {
for (const auto& client : connected_clients_) {
SendMessageToClient(
client.first, CreateAppMessage(*session_id_, client.first, message));
}
} else {
SendMessageToClient(client_id,
CreateAppMessage(*session_id_, client_id, message));
}
}
void AppActivity::OnInternalMessage(
const cast_channel::InternalMessage& message) {}
bool AppActivity::CanJoinSession(const CastMediaSource& cast_source,
bool off_the_record) const {
if (!cast_source.ContainsApp(app_id()))
return false;
if (base::Contains(connected_clients_, cast_source.client_id()))
return false;
if (route().is_off_the_record() != off_the_record)
return false;
return true;
}
bool AppActivity::HasJoinableClient(AutoJoinPolicy policy,
const url::Origin& origin,
int tab_id) const {
return std::any_of(connected_clients_.begin(), connected_clients_.end(),
[policy, &origin, tab_id](const auto& client) {
return IsAutoJoinAllowed(policy, origin, tab_id,
client.second->origin(),
client.second->tab_id());
});
}
} // namespace media_router