| // 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 |