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