blob: bc0ddcbfe0832483593d608201a2ae7a5d1eb2b2 [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"
#include "mojo/public/cpp/bindings/binding_set.h"
namespace content {
class BrowserContext;
}
namespace media_router {
enum class MediaRouteProviderWakeReason;
// 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 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;
scoped_refptr<MediaRouteController> GetRouteController(
const MediaRoute::Id& route_id) final;
void RegisterMediaRouteProvider(
MediaRouteProviderId provider_id,
mojom::MediaRouteProviderPtr media_route_provider_ptr,
mojom::MediaRouter::RegisterMediaRouteProviderCallback callback) override;
// Issues 0+ calls to the provider given by |provider_id| to ensure its state
// is in sync with MediaRouter on a best-effort basis.
virtual void SyncStateToMediaRouteProvider(MediaRouteProviderId provider_id);
const std::string& instance_id() const { return instance_id_; }
void set_instance_id_for_test(const std::string& instance_id) {
instance_id_ = instance_id;
}
protected:
// Standard constructor, used by
// MediaRouterMojoImplFactory::GetApiForBrowserContext.
explicit MediaRouterMojoImpl(content::BrowserContext* context);
// Requests MRPs 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);
// Requests the creation of a MediaRouteController implementation by passing
// the interface request to |media_route_provider_|. Does not take ownership
// of |route_controller|.
void InitMediaRouteController(MediaRouteController* route_controller);
// Called when the Mojo pointer for |provider_id| has a connection error.
// Removes the pointer from |media_route_providers_|.
void OnProviderConnectionError(MediaRouteProviderId provider_id);
// Creates a binding between |this| and |request|.
void BindToMojoRequest(mojo::InterfaceRequest<mojom::MediaRouter> request);
// Returns the ID of the provider associated with the presentation ID, or
// nullopt if not found.
virtual base::Optional<MediaRouteProviderId> GetProviderIdForPresentation(
const std::string& presentation_id);
content::BrowserContext* context() const { return context_; }
// mojom::MediaRouter implementation.
void OnSinksReceived(MediaRouteProviderId provider_id,
const std::string& media_source,
const std::vector<MediaSinkInternal>& internal_sinks,
const std::vector<url::Origin>& origins) override;
// Mojo pointers to media route providers. Providers are added via
// RegisterMediaRouteProvider().
base::flat_map<MediaRouteProviderId, mojom::MediaRouteProviderPtr>
media_route_providers_;
// Stores route controllers that can be used to send media commands.
base::flat_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(MediaRouterMojoImplTest,
SendSinkRequestsToMultipleProviders);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
SendRouteRequestsToMultipleProviders);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
ObserveSinksFromMultipleProviders);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
ObserveRoutesFromMultipleProviders);
FRIEND_TEST_ALL_PREFIXES(MediaRouterDesktopTest,
SyncStateToMediaRouteProvider);
FRIEND_TEST_ALL_PREFIXES(ExtensionMediaRouteProviderProxyTest,
StartAndStopObservingMediaSinks);
// Represents a query to the MediaRouteProviders for media sinks and caches
// media sinks returned by MRPs. Holds observers for the query.
class MediaSinksQuery {
public:
MediaSinksQuery();
~MediaSinksQuery();
// Caches the list of sinks for the provider returned from the query.
void SetSinksForProvider(MediaRouteProviderId provider_id,
const std::vector<MediaSink>& sinks);
// Resets the internal state, including the cache for all the providers.
void Reset();
void AddObserver(MediaSinksObserver* observer);
void RemoveObserver(MediaSinksObserver* observer);
void NotifyObservers();
bool HasObserver(MediaSinksObserver* observer) const;
bool HasObservers() const;
const std::vector<MediaSink>& cached_sink_list() const {
return cached_sink_list_;
}
void set_origins(const std::vector<url::Origin>& origins) {
origins_ = origins;
}
private:
// Cached list of sinks for the query.
std::vector<MediaSink> cached_sink_list_;
// Cached list of origins for the query.
// TODO(takumif): The list of supported origins may differ between MRPs, so
// we need more fine-grained associations between sinks and origins.
std::vector<url::Origin> origins_;
base::ObserverList<MediaSinksObserver> observers_;
DISALLOW_COPY_AND_ASSIGN(MediaSinksQuery);
};
// Represents a query to the MediaRouteProviders for media routes and caches
// media routes returned by MRPs. Holds observers for the query.
class MediaRoutesQuery {
public:
MediaRoutesQuery();
~MediaRoutesQuery();
// Caches the list of routes and joinable route IDs for the provider
// returned from the query.
void SetRoutesForProvider(
MediaRouteProviderId provider_id,
const std::vector<MediaRoute>& routes,
const std::vector<MediaRoute::Id>& joinable_route_ids);
// Adds |route| to the list of routes managed by the provider and returns
// true, if it hasn't been added already. Returns false otherwise.
bool AddRouteForProvider(MediaRouteProviderId provider_id,
const MediaRoute& route);
// Re-constructs |cached_route_list_| by merging route lists in
// |providers_to_routes_|.
void UpdateCachedRouteList();
void AddObserver(MediaRoutesObserver* observer);
void RemoveObserver(MediaRoutesObserver* observer);
void NotifyObservers();
bool HasObserver(MediaRoutesObserver* observer) const;
bool HasObservers() const;
const base::Optional<std::vector<MediaRoute>>& cached_route_list() const {
return cached_route_list_;
}
const std::vector<MediaRoute::Id>& joinable_route_ids() const {
return joinable_route_ids_;
}
const base::flat_map<MediaRouteProviderId, std::vector<MediaRoute>>&
providers_to_routes() const {
return providers_to_routes_;
}
private:
// Cached list of routes and joinable route IDs for the query.
base::Optional<std::vector<MediaRoute>> cached_route_list_;
std::vector<MediaRoute::Id> joinable_route_ids_;
// Per-MRP lists of routes and joinable route IDs for the query.
// TODO(crbug.com/761493): Consider making MRP ID an attribute
// of MediaRoute, so that we can simplify these into vectors.
base::flat_map<MediaRouteProviderId, std::vector<MediaRoute>>
providers_to_routes_;
base::flat_map<MediaRouteProviderId, std::vector<MediaRoute::Id>>
providers_to_joinable_routes_;
base::ObserverList<MediaRoutesObserver> observers_;
DISALLOW_COPY_AND_ASSIGN(MediaRoutesQuery);
};
class ProviderSinkAvailability {
public:
ProviderSinkAvailability();
~ProviderSinkAvailability();
// Sets the sink availability for |provider_id|. Returns true if
// |availability| is different from that already recorded.
bool SetAvailabilityForProvider(MediaRouteProviderId provider_id,
SinkAvailability availability);
// Returns true if the availability for the provider is not UNAVAILABLE.
bool IsAvailableForProvider(MediaRouteProviderId provider_id) const;
// Returns true if there is a provider whose sink availability isn't
// UNAVAILABLE.
bool IsAvailable() const;
private:
void UpdateOverallAvailability();
base::flat_map<MediaRouteProviderId, SinkAvailability> availabilities_;
SinkAvailability overall_availability_ = SinkAvailability::UNAVAILABLE;
};
// MediaRouter implementation.
bool RegisterMediaSinksObserver(MediaSinksObserver* observer) override;
void UnregisterMediaSinksObserver(MediaSinksObserver* observer) override;
void RegisterMediaRoutesObserver(MediaRoutesObserver* observer) override;
void UnregisterMediaRoutesObserver(MediaRoutesObserver* 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 OnRoutesUpdated(
MediaRouteProviderId provider_id,
const std::vector<MediaRoute>& routes,
const std::string& media_source,
const std::vector<std::string>& joinable_route_ids) override;
void OnSinkAvailabilityUpdated(MediaRouteProviderId provider_id,
SinkAvailability availability) override;
void OnPresentationConnectionStateChanged(
const std::string& route_id,
media_router::mojom::MediaRouter::PresentationConnectionState state)
override;
void OnPresentationConnectionClosed(
const std::string& route_id,
media_router::mojom::MediaRouter::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;
void GetMediaSinkServiceStatus(
mojom::MediaRouter::GetMediaSinkServiceStatusCallback callback) override;
// Result callback when Mojo TerminateRoute is invoked.
// |route_id|: ID of MediaRoute passed to the TerminateRoute request.
// |provider_id|: ID of MediaRouteProvider that handled the request.
// |error_text|: Error message if an error occurred.
// |result_code|: The result of the request.
void OnTerminateRouteResult(const MediaRoute::Id& route_id,
MediaRouteProviderId provider_id,
const base::Optional<std::string>& error_text,
RouteRequestResult::ResultCode result_code);
// Adds |route| to the list of routes. Called in the callback for
// CreateRoute() etc. so that even if the callback is called before
// OnRoutesUpdated(), MediaRouter is still aware of the route.
void OnRouteAdded(MediaRouteProviderId provider_id, const MediaRoute& route);
// Converts the callback result of calling Mojo CreateRoute()/JoinRoute()
// into a local callback.
void RouteResponseReceived(const std::string& presentation_id,
MediaRouteProviderId provider_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);
// Callback called by MRP's CreateMediaRouteController().
void OnMediaControllerCreated(const MediaRoute::Id& route_id, bool success);
// Invalidates and removes controllers from |route_controllers_| whose media
// routes do not appear in |routes|.
void RemoveInvalidRouteControllers(const std::vector<MediaRoute>& routes);
// Methods for obtaining a pointer to the provider associated with the given
// object. They return a nullopt when such a provider is not found.
base::Optional<MediaRouteProviderId> GetProviderIdForRoute(
const MediaRoute::Id& route_id);
base::Optional<MediaRouteProviderId> GetProviderIdForSink(
const MediaSink::Id& sink_id);
// Gets the sink with the given ID from lists of sinks held by sink queries.
// Returns a nullptr if none is found.
const MediaSink* GetSinkById(const MediaSink::Id& sink_id) const;
base::flat_map<MediaSource::Id, std::unique_ptr<MediaSinksQuery>>
sinks_queries_;
base::flat_map<MediaSource::Id, std::unique_ptr<MediaRoutesQuery>>
routes_queries_;
base::flat_map<MediaRoute::Id,
std::unique_ptr<base::ObserverList<RouteMessageObserver>>>
message_observers_;
// 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 providers.
ProviderSinkAvailability sink_availability_;
// Bindings for Mojo pointers to |this| held by media route providers.
mojo::BindingSet<mojom::MediaRouter> bindings_;
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_