blob: e08ec6c96e0a651787573cd609065d71bdccc444 [file] [log] [blame]
// Copyright 2015 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/media_router_base.h"
#include <memory>
#include "base/bind.h"
#include "base/guid.h"
#include "base/stl_util.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/browser_thread.h"
#if !defined(OS_ANDROID)
#include "chrome/browser/media/router/mojo/media_route_controller.h"
#endif // !defined(OS_ANDROID)
using blink::mojom::PresentationConnectionState;
namespace media_router {
// A MediaRoutesObserver that maintains state about the current set of media
// routes.
class MediaRouterBase::InternalMediaRoutesObserver
: public MediaRoutesObserver {
public:
explicit InternalMediaRoutesObserver(MediaRouter* router)
: MediaRoutesObserver(router), has_route(false) {}
~InternalMediaRoutesObserver() override {}
// MediaRoutesObserver
void OnRoutesUpdated(
const std::vector<MediaRoute>& routes,
const std::vector<MediaRoute::Id>& joinable_route_ids) override {
current_routes = routes;
incognito_route_ids.clear();
// TODO(crbug.com/611486): Have the MRPM pass a list of joinable route ids
// via |joinable_route_ids|, and check here if it is non-empty.
has_route = !routes.empty();
for (const auto& route : routes) {
if (route.is_incognito())
incognito_route_ids.push_back(route.media_route_id());
}
}
bool has_route;
std::vector<MediaRoute> current_routes;
std::vector<MediaRoute::Id> incognito_route_ids;
private:
DISALLOW_COPY_AND_ASSIGN(InternalMediaRoutesObserver);
};
MediaRouterBase::~MediaRouterBase() {
CHECK(!internal_routes_observer_);
}
std::unique_ptr<PresentationConnectionStateSubscription>
MediaRouterBase::AddPresentationConnectionStateChangedCallback(
const MediaRoute::Id& route_id,
const content::PresentationConnectionStateChangedCallback& callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
auto& callbacks = presentation_connection_state_callbacks_[route_id];
if (!callbacks) {
callbacks = std::make_unique<PresentationConnectionStateChangedCallbacks>();
callbacks->set_removal_callback(base::Bind(
&MediaRouterBase::OnPresentationConnectionStateCallbackRemoved,
base::Unretained(this), route_id));
}
return callbacks->Add(callback);
}
void MediaRouterBase::OnIncognitoProfileShutdown() {
for (const auto& route_id : internal_routes_observer_->incognito_route_ids)
TerminateRoute(route_id);
}
IssueManager* MediaRouterBase::GetIssueManager() {
return &issue_manager_;
}
std::vector<MediaRoute> MediaRouterBase::GetCurrentRoutes() const {
return internal_routes_observer_->current_routes;
}
std::unique_ptr<media::FlingingController>
MediaRouterBase::GetFlingingController(const MediaRoute::Id& route_id) {
return nullptr;
}
#if !defined(OS_ANDROID)
scoped_refptr<MediaRouteController> MediaRouterBase::GetRouteController(
const MediaRoute::Id& route_id) {
return nullptr;
}
#endif // !defined(OS_ANDROID)
MediaRouterBase::MediaRouterBase() : initialized_(false) {}
// static
std::string MediaRouterBase::CreatePresentationId() {
return "mr_" + base::GenerateGUID();
}
void MediaRouterBase::NotifyPresentationConnectionStateChange(
const MediaRoute::Id& route_id,
PresentationConnectionState state) {
// We should call NotifyPresentationConnectionClose() for the CLOSED state.
DCHECK_NE(state, PresentationConnectionState::CLOSED);
auto it = presentation_connection_state_callbacks_.find(route_id);
if (it == presentation_connection_state_callbacks_.end())
return;
it->second->Notify(content::PresentationConnectionStateChangeInfo(state));
}
void MediaRouterBase::NotifyPresentationConnectionClose(
const MediaRoute::Id& route_id,
blink::mojom::PresentationConnectionCloseReason reason,
const std::string& message) {
auto it = presentation_connection_state_callbacks_.find(route_id);
if (it == presentation_connection_state_callbacks_.end())
return;
content::PresentationConnectionStateChangeInfo info(
PresentationConnectionState::CLOSED);
info.close_reason = reason;
info.message = message;
it->second->Notify(info);
}
bool MediaRouterBase::HasJoinableRoute() const {
return internal_routes_observer_->has_route;
}
const MediaRoute* MediaRouterBase::GetRoute(
const MediaRoute::Id& route_id) const {
const auto& routes = internal_routes_observer_->current_routes;
auto it = std::find_if(routes.begin(), routes.end(),
[&route_id](const MediaRoute& route) {
return route.media_route_id() == route_id;
});
return it == routes.end() ? nullptr : &*it;
}
void MediaRouterBase::Initialize() {
DCHECK(!initialized_);
// The observer calls virtual methods on MediaRouter; it must be created
// outside of the ctor
internal_routes_observer_.reset(new InternalMediaRoutesObserver(this));
initialized_ = true;
}
void MediaRouterBase::OnPresentationConnectionStateCallbackRemoved(
const MediaRoute::Id& route_id) {
auto it = presentation_connection_state_callbacks_.find(route_id);
if (it != presentation_connection_state_callbacks_.end() &&
it->second->empty()) {
presentation_connection_state_callbacks_.erase(route_id);
}
}
void MediaRouterBase::Shutdown() {
// The observer calls virtual methods on MediaRouter; it must be destroyed
// outside of the dtor
internal_routes_observer_.reset();
}
#if !defined(OS_ANDROID)
void MediaRouterBase::DetachRouteController(const MediaRoute::Id& route_id,
MediaRouteController* controller) {}
#endif // !defined(OS_ANDROID)
void MediaRouterBase::RegisterRemotingSource(
SessionID tab_id,
CastRemotingConnector* remoting_source) {
auto it = remoting_sources_.find(tab_id);
if (it != remoting_sources_.end()) {
DCHECK(remoting_source == it->second);
return;
}
remoting_sources_.emplace(tab_id, remoting_source);
}
void MediaRouterBase::UnregisterRemotingSource(SessionID tab_id) {
auto it = remoting_sources_.find(tab_id);
DCHECK(it != remoting_sources_.end());
remoting_sources_.erase(it);
}
base::Value MediaRouterBase::GetState() const {
NOTREACHED() << "Should not invoke MediaRouterBase::GetState()";
return base::Value(base::Value::Type::DICTIONARY);
}
} // namespace media_router