blob: 48f49cfb2f8bee88656028260e0a57dd5069abf0 [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.
#ifndef CHROME_BROWSER_MEDIA_ROUTER_MOJO_MEDIA_ROUTER_MOJO_IMPL_H_
#define CHROME_BROWSER_MEDIA_ROUTER_MOJO_MEDIA_ROUTER_MOJO_IMPL_H_
#include <stdint.h>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <unordered_map>
#include <vector>
#include "base/containers/hash_tables.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/optional.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/media/router/issue_manager.h"
#include "chrome/browser/media/router/media_router_base.h"
#include "chrome/browser/media/router/media_routes_observer.h"
#include "chrome/common/media_router/issue.h"
#include "chrome/common/media_router/mojo/media_router.mojom.h"
#include "chrome/common/media_router/route_request_result.h"
#include "content/public/browser/browser_thread.h"
#include "mojo/public/cpp/bindings/binding.h"
namespace content {
class BrowserContext;
}
namespace media_router {
enum class MediaRouteProviderWakeReason;
class CastMediaSinkService;
class DialMediaSinkServiceProxy;
// MediaRouter implementation that delegates calls to a MediaRouteProvider.
class MediaRouterMojoImpl : public MediaRouterBase,
public mojom::MediaRouter {
public:
~MediaRouterMojoImpl() override;
// MediaRouter implementation.
void CreateRoute(const MediaSource::Id& source_id,
const MediaSink::Id& sink_id,
const url::Origin& origin,
content::WebContents* web_contents,
std::vector<MediaRouteResponseCallback> callbacks,
base::TimeDelta timeout,
bool incognito) final;
void JoinRoute(const MediaSource::Id& source_id,
const std::string& presentation_id,
const url::Origin& origin,
content::WebContents* web_contents,
std::vector<MediaRouteResponseCallback> callbacks,
base::TimeDelta timeout,
bool incognito) final;
void ConnectRouteByRouteId(const MediaSource::Id& source,
const MediaRoute::Id& route_id,
const url::Origin& origin,
content::WebContents* web_contents,
std::vector<MediaRouteResponseCallback> callbacks,
base::TimeDelta timeout,
bool incognito) final;
void TerminateRoute(const MediaRoute::Id& route_id) final;
void DetachRoute(const MediaRoute::Id& route_id) final;
void SendRouteMessage(const MediaRoute::Id& route_id,
const std::string& message,
SendRouteMessageCallback callback) final;
void SendRouteBinaryMessage(const MediaRoute::Id& route_id,
std::unique_ptr<std::vector<uint8_t>> data,
SendRouteMessageCallback callback) final;
void AddIssue(const IssueInfo& issue_info) override;
void ClearIssue(const Issue::Id& issue_id) override;
void OnUserGesture() override;
void SearchSinks(const MediaSink::Id& sink_id,
const MediaSource::Id& source_id,
const std::string& search_input,
const std::string& domain,
MediaSinkSearchResponseCallback sink_callback) final;
void ProvideSinks(const std::string& provider_name,
std::vector<MediaSinkInternal> sinks) final;
scoped_refptr<MediaRouteController> GetRouteController(
const MediaRoute::Id& route_id) final;
void RegisterMediaRouteProvider(
mojom::MediaRouteProviderPtr media_route_provider_ptr,
mojom::MediaRouter::RegisterMediaRouteProviderCallback callback) override;
// Issues 0+ calls to |media_route_provider_| to ensure its state is in sync
// with MediaRouter on a best-effort basis.
virtual void SyncStateToMediaRouteProvider();
const std::string& instance_id() const { return instance_id_; }
void set_instance_id_for_test(const std::string& instance_id) {
instance_id_ = instance_id;
}
void set_media_route_provider_for_test(
mojom::MediaRouteProviderPtr media_route_provider) {
media_route_provider_ = std::move(media_route_provider);
}
protected:
// Standard constructor, used by
// MediaRouterMojoImplFactory::GetApiForBrowserContext.
explicit MediaRouterMojoImpl(content::BrowserContext* context);
// Error handler callback for |media_route_provider_|.
virtual void OnConnectionError();
// Requests MRPM to update media sinks. This allows MRPs that only do
// discovery on sink queries an opportunity to update discovery results
// even if the MRP SinkAvailability is marked UNAVAILABLE.
void UpdateMediaSinks(const MediaSource::Id& source_id);
// Callback called by MRP's CreateMediaRouteController().
void OnMediaControllerCreated(const MediaRoute::Id& route_id, bool success);
// Mojo proxy object for the Media Route Provider Manager.
// Set to null initially, and later set to the Provider Manager proxy object
// passed in via |RegisterMediaRouteProvider()|.
// This is set to null again when the component extension is suspended
// if or a Mojo channel error occured.
mojom::MediaRouteProviderPtr media_route_provider_;
// Stores route controllers that can be used to send media commands.
std::unordered_map<MediaRoute::Id, MediaRouteController*> route_controllers_;
private:
friend class MediaRouterFactory;
friend class MediaRouterMojoImplTest;
friend class MediaRouterMojoTest;
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest, JoinRoute);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest, JoinRouteTimedOutFails);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
JoinRouteIncognitoMismatchFails);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
IncognitoRoutesTerminatedOnProfileShutdown);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
RegisterAndUnregisterMediaSinksObserver);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
RegisterMediaSinksObserverWithAvailabilityChange);
FRIEND_TEST_ALL_PREFIXES(
MediaRouterMojoImplTest,
RegisterAndUnregisterMediaSinksObserverWithAvailabilityChange);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
RegisterAndUnregisterMediaRoutesObserver);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
RouteMessagesSingleObserver);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
RouteMessagesMultipleObservers);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest, HandleIssue);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest, GetRouteController);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
GetRouteControllerMultipleTimes);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
GetRouteControllerAfterInvalidation);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
GetRouteControllerAfterRouteInvalidation);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
FailToCreateRouteController);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
RegisterMediaRoutesObserver_DedupingWithCache);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
PresentationConnectionStateChangedCallback);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
PresentationConnectionStateChangedCallbackRemoved);
FRIEND_TEST_ALL_PREFIXES(MediaRouterDesktopTest,
SyncStateToMediaRouteProvider);
FRIEND_TEST_ALL_PREFIXES(ExtensionMediaRouteProviderProxyTest,
StartAndStopObservingMediaSinks);
// Represents a query to the MRPM for media sinks and holds observers for the
// query.
struct MediaSinksQuery {
public:
MediaSinksQuery();
~MediaSinksQuery();
// True if the query has been sent to the MRPM.
bool is_active = false;
// Cached list of sinks and origins for the query.
base::Optional<std::vector<MediaSink>> cached_sink_list;
std::vector<url::Origin> origins;
base::ObserverList<MediaSinksObserver> observers;
private:
DISALLOW_COPY_AND_ASSIGN(MediaSinksQuery);
};
struct MediaRoutesQuery {
public:
MediaRoutesQuery();
~MediaRoutesQuery();
// Cached list of routes and joinable route IDs for the query.
base::Optional<std::vector<MediaRoute>> cached_route_list;
std::vector<std::string> joinable_route_ids;
base::ObserverList<MediaRoutesObserver> observers;
private:
DISALLOW_COPY_AND_ASSIGN(MediaRoutesQuery);
};
// MediaRouter implementation.
bool RegisterMediaSinksObserver(MediaSinksObserver* observer) override;
void UnregisterMediaSinksObserver(MediaSinksObserver* observer) override;
void RegisterMediaRoutesObserver(MediaRoutesObserver* observer) override;
void UnregisterMediaRoutesObserver(MediaRoutesObserver* observer) override;
void RegisterIssuesObserver(IssuesObserver* observer) override;
void UnregisterIssuesObserver(IssuesObserver* observer) override;
void RegisterRouteMessageObserver(RouteMessageObserver* observer) override;
void UnregisterRouteMessageObserver(RouteMessageObserver* observer) override;
void DetachRouteController(const MediaRoute::Id& route_id,
MediaRouteController* controller) override;
// Notifies |observer| of any existing cached routes, if it is still
// registered.
void NotifyOfExistingRoutesIfRegistered(const MediaSource::Id& source_id,
MediaRoutesObserver* observer) const;
// mojom::MediaRouter implementation.
void OnIssue(const IssueInfo& issue) override;
void OnSinksReceived(const std::string& media_source,
const std::vector<MediaSinkInternal>& internal_sinks,
const std::vector<url::Origin>& origins) override;
void OnRoutesUpdated(
const std::vector<MediaRoute>& routes,
const std::string& media_source,
const std::vector<std::string>& joinable_route_ids) override;
void OnSinkAvailabilityUpdated(
mojom::MediaRouter::SinkAvailability availability) override;
void OnPresentationConnectionStateChanged(
const std::string& route_id,
content::PresentationConnectionState state) override;
void OnPresentationConnectionClosed(
const std::string& route_id,
content::PresentationConnectionCloseReason reason,
const std::string& message) override;
void OnRouteMessagesReceived(
const std::string& route_id,
const std::vector<content::PresentationConnectionMessage>& messages)
override;
void OnMediaRemoterCreated(
int32_t tab_id,
media::mojom::MirrorServiceRemoterPtr remoter,
media::mojom::MirrorServiceRemotingSourceRequest source_request) override;
// Result callback when Mojo terminateRoute is invoked. |route_id| is bound
// to the ID of the route that was terminated.
void OnTerminateRouteResult(const MediaRoute::Id& route_id,
const base::Optional<std::string>& error_text,
RouteRequestResult::ResultCode result_code);
// Converts the callback result of calling Mojo CreateRoute()/JoinRoute()
// into a local callback.
void RouteResponseReceived(const std::string& presentation_id,
bool is_incognito,
std::vector<MediaRouteResponseCallback> callbacks,
bool is_join,
const base::Optional<MediaRoute>& media_route,
const base::Optional<std::string>& error_text,
RouteRequestResult::ResultCode result_code);
// Start browser side sink discovery.
void StartDiscovery();
// Invalidates and removes controllers from |route_controllers_| whose media
// routes do not appear in |routes|.
void RemoveInvalidRouteControllers(const std::vector<MediaRoute>& routes);
std::unordered_map<MediaSource::Id, std::unique_ptr<MediaSinksQuery>>
sinks_queries_;
std::unordered_map<MediaSource::Id, std::unique_ptr<MediaRoutesQuery>>
routes_queries_;
std::unordered_map<MediaRoute::Id,
std::unique_ptr<base::ObserverList<RouteMessageObserver>>>
message_observers_;
IssueManager issue_manager_;
// GUID unique to each browser run. Component extension uses this to detect
// when its persisted state was written by an older browser instance, and is
// therefore stale.
std::string instance_id_;
// The last reported sink availability from the media route provider manager.
mojom::MediaRouter::SinkAvailability availability_;
// Media sink service for DIAL devices.
// TODO(takumif): Move this to MediaRouterDesktop.
scoped_refptr<DialMediaSinkServiceProxy> dial_media_sink_service_proxy_;
// Media sink service for CAST devices.
// TODO(takumif): Move this to MediaRouterDesktop.
scoped_refptr<CastMediaSinkService> cast_media_sink_service_;
content::BrowserContext* const context_;
base::WeakPtrFactory<MediaRouterMojoImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(MediaRouterMojoImpl);
};
} // namespace media_router
#endif // CHROME_BROWSER_MEDIA_ROUTER_MOJO_MEDIA_ROUTER_MOJO_IMPL_H_