| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #ifndef CHROME_BROWSER_MEDIA_ROUTER_MEDIA_ROUTER_MOJO_IMPL_H_ | |
| 6 #define CHROME_BROWSER_MEDIA_ROUTER_MEDIA_ROUTER_MOJO_IMPL_H_ | |
| 7 | |
| 8 #include <stdint.h> | |
| 9 | |
| 10 #include <deque> | |
| 11 #include <map> | |
| 12 #include <set> | |
| 13 #include <string> | |
| 14 #include <vector> | |
| 15 | |
| 16 #include "base/containers/hash_tables.h" | |
| 17 #include "base/containers/scoped_ptr_hash_map.h" | |
| 18 #include "base/gtest_prod_util.h" | |
| 19 #include "base/macros.h" | |
| 20 #include "base/memory/scoped_ptr.h" | |
| 21 #include "base/memory/weak_ptr.h" | |
| 22 #include "base/observer_list.h" | |
| 23 #include "base/thread_task_runner_handle.h" | |
| 24 #include "chrome/browser/media/router/issue.h" | |
| 25 #include "chrome/browser/media/router/issue_manager.h" | |
| 26 #include "chrome/browser/media/router/media_router.mojom.h" | |
| 27 #include "chrome/browser/media/router/media_router_base.h" | |
| 28 #include "chrome/browser/media/router/media_routes_observer.h" | |
| 29 #include "mojo/public/cpp/bindings/binding.h" | |
| 30 | |
| 31 namespace content { | |
| 32 class BrowserContext; | |
| 33 } | |
| 34 | |
| 35 namespace extensions { | |
| 36 class EventPageTracker; | |
| 37 class Extension; | |
| 38 } | |
| 39 | |
| 40 namespace media_router { | |
| 41 | |
| 42 enum class MediaRouteProviderWakeReason; | |
| 43 | |
| 44 // MediaRouter implementation that delegates calls to the component extension. | |
| 45 // Also handles the suspension and wakeup of the component extension. | |
| 46 class MediaRouterMojoImpl : public MediaRouterBase, | |
| 47 public interfaces::MediaRouter { | |
| 48 public: | |
| 49 ~MediaRouterMojoImpl() override; | |
| 50 | |
| 51 // Sets up the MediaRouterMojoImpl instance owned by |context| to handle | |
| 52 // MediaRouterObserver requests from the component extension given by | |
| 53 // |extension|. Creates the MediaRouterMojoImpl instance if it does not | |
| 54 // exist. | |
| 55 // Called by the Mojo module registry. | |
| 56 // |extension|: The component extension, used for querying | |
| 57 // suspension state. | |
| 58 // |context|: The BrowserContext which owns the extension process. | |
| 59 // |request|: The Mojo connection request used for binding. | |
| 60 static void BindToRequest( | |
| 61 const extensions::Extension* extension, | |
| 62 content::BrowserContext* context, | |
| 63 mojo::InterfaceRequest<interfaces::MediaRouter> request); | |
| 64 | |
| 65 // MediaRouter implementation. | |
| 66 // Execution of the requests is delegated to the Do* methods, which can be | |
| 67 // enqueued for later use if the extension is temporarily suspended. | |
| 68 void CreateRoute(const MediaSource::Id& source_id, | |
| 69 const MediaSink::Id& sink_id, | |
| 70 const GURL& origin, | |
| 71 content::WebContents* web_contents, | |
| 72 const std::vector<MediaRouteResponseCallback>& callbacks, | |
| 73 base::TimeDelta timeout, | |
| 74 bool off_the_record) override; | |
| 75 void JoinRoute(const MediaSource::Id& source_id, | |
| 76 const std::string& presentation_id, | |
| 77 const GURL& origin, | |
| 78 content::WebContents* web_contents, | |
| 79 const std::vector<MediaRouteResponseCallback>& callbacks, | |
| 80 base::TimeDelta timeout, | |
| 81 bool off_the_record) override; | |
| 82 void ConnectRouteByRouteId( | |
| 83 const MediaSource::Id& source, | |
| 84 const MediaRoute::Id& route_id, | |
| 85 const GURL& origin, | |
| 86 content::WebContents* web_contents, | |
| 87 const std::vector<MediaRouteResponseCallback>& callbacks, | |
| 88 base::TimeDelta timeout, | |
| 89 bool off_the_record) override; | |
| 90 void TerminateRoute(const MediaRoute::Id& route_id) override; | |
| 91 void DetachRoute(const MediaRoute::Id& route_id) override; | |
| 92 void SendRouteMessage(const MediaRoute::Id& route_id, | |
| 93 const std::string& message, | |
| 94 const SendRouteMessageCallback& callback) override; | |
| 95 void SendRouteBinaryMessage( | |
| 96 const MediaRoute::Id& route_id, | |
| 97 scoped_ptr<std::vector<uint8_t>> data, | |
| 98 const SendRouteMessageCallback& callback) override; | |
| 99 void AddIssue(const Issue& issue) override; | |
| 100 void ClearIssue(const Issue::Id& issue_id) override; | |
| 101 | |
| 102 const std::string& media_route_provider_extension_id() const { | |
| 103 return media_route_provider_extension_id_; | |
| 104 } | |
| 105 | |
| 106 void set_instance_id_for_test(const std::string& instance_id) { | |
| 107 instance_id_ = instance_id; | |
| 108 } | |
| 109 | |
| 110 private: | |
| 111 friend class MediaRouterFactory; | |
| 112 friend class MediaRouterMojoExtensionTest; | |
| 113 friend class MediaRouterMojoTest; | |
| 114 FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest, | |
| 115 RegisterAndUnregisterMediaSinksObserver); | |
| 116 FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest, | |
| 117 RegisterMediaSinksObserverWithAvailabilityChange); | |
| 118 FRIEND_TEST_ALL_PREFIXES( | |
| 119 MediaRouterMojoImplTest, | |
| 120 RegisterAndUnregisterMediaSinksObserverWithAvailabilityChange); | |
| 121 FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest, | |
| 122 RegisterAndUnregisterMediaRoutesObserver); | |
| 123 FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest, HandleIssue); | |
| 124 FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoExtensionTest, | |
| 125 DeferredBindingAndSuspension); | |
| 126 FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoExtensionTest, | |
| 127 DrainPendingRequestQueue); | |
| 128 FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoExtensionTest, | |
| 129 DropOldestPendingRequest); | |
| 130 FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoExtensionTest, | |
| 131 AttemptedWakeupTooManyTimes); | |
| 132 FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoExtensionTest, | |
| 133 WakeupFailedDrainsQueue); | |
| 134 | |
| 135 // The max number of pending requests allowed. When number of pending requests | |
| 136 // exceeds this number, the oldest request will be dropped. | |
| 137 static const int kMaxPendingRequests = 30; | |
| 138 | |
| 139 // Max consecutive attempts to wake up the component extension before | |
| 140 // giving up and draining the pending request queue. | |
| 141 static const int kMaxWakeupAttemptCount = 3; | |
| 142 | |
| 143 // Represents a query to the MRPM for media sinks and holds observers for the | |
| 144 // query. | |
| 145 struct MediaSinksQuery { | |
| 146 public: | |
| 147 MediaSinksQuery(); | |
| 148 ~MediaSinksQuery(); | |
| 149 | |
| 150 // True if the query has been sent to the MRPM. | |
| 151 bool is_active = false; | |
| 152 | |
| 153 // True if cached result is available. | |
| 154 bool has_cached_result = false; | |
| 155 | |
| 156 // Cached list of sinks for the query, if |has_cached_result| is true. | |
| 157 // Empty otherwise. | |
| 158 std::vector<MediaSink> cached_sink_list; | |
| 159 std::vector<GURL> origins; | |
| 160 base::ObserverList<MediaSinksObserver> observers; | |
| 161 | |
| 162 private: | |
| 163 DISALLOW_COPY_AND_ASSIGN(MediaSinksQuery); | |
| 164 }; | |
| 165 | |
| 166 struct MediaRoutesQuery { | |
| 167 public: | |
| 168 MediaRoutesQuery(); | |
| 169 ~MediaRoutesQuery(); | |
| 170 | |
| 171 // True if the query has been sent to the MRPM. False otherwise. | |
| 172 bool is_active = false; | |
| 173 base::ObserverList<MediaRoutesObserver> observers; | |
| 174 | |
| 175 private: | |
| 176 DISALLOW_COPY_AND_ASSIGN(MediaRoutesQuery); | |
| 177 }; | |
| 178 | |
| 179 // Standard constructor, used by | |
| 180 // MediaRouterMojoImplFactory::GetApiForBrowserContext. | |
| 181 explicit MediaRouterMojoImpl( | |
| 182 extensions::EventPageTracker* event_page_tracker); | |
| 183 | |
| 184 // Binds |this| to a Mojo interface request, so that clients can acquire a | |
| 185 // handle to a MediaRouterMojoImpl instance via the Mojo service connector. | |
| 186 // Stores the ID of |extension| in |media_route_provider_extension_id_|. | |
| 187 void BindToMojoRequest( | |
| 188 mojo::InterfaceRequest<interfaces::MediaRouter> request, | |
| 189 const extensions::Extension& extension); | |
| 190 | |
| 191 // Enqueues a closure for later execution by ExecutePendingRequests(). | |
| 192 void EnqueueTask(const base::Closure& closure); | |
| 193 | |
| 194 // Runs a closure if the extension monitored by |extension_monitor_| is | |
| 195 // active, or defers it for later execution if the extension is suspended. | |
| 196 void RunOrDefer(const base::Closure& request); | |
| 197 | |
| 198 // Dispatches the Mojo requests queued in |pending_requests_|. | |
| 199 void ExecutePendingRequests(); | |
| 200 | |
| 201 // Drops all pending requests. Called when we have a connection error to | |
| 202 // component extension and further reattempts are unlikely to help. | |
| 203 void DrainPendingRequests(); | |
| 204 | |
| 205 // MediaRouter implementation. | |
| 206 bool RegisterMediaSinksObserver(MediaSinksObserver* observer) override; | |
| 207 void UnregisterMediaSinksObserver(MediaSinksObserver* observer) override; | |
| 208 void RegisterMediaRoutesObserver(MediaRoutesObserver* observer) override; | |
| 209 void UnregisterMediaRoutesObserver(MediaRoutesObserver* observer) override; | |
| 210 void RegisterIssuesObserver(IssuesObserver* observer) override; | |
| 211 void UnregisterIssuesObserver(IssuesObserver* observer) override; | |
| 212 void RegisterPresentationSessionMessagesObserver( | |
| 213 PresentationSessionMessagesObserver* observer) override; | |
| 214 void UnregisterPresentationSessionMessagesObserver( | |
| 215 PresentationSessionMessagesObserver* observer) override; | |
| 216 | |
| 217 // These calls invoke methods in the component extension via Mojo. | |
| 218 void DoCreateRoute(const MediaSource::Id& source_id, | |
| 219 const MediaSink::Id& sink_id, | |
| 220 const std::string& origin, | |
| 221 int tab_id, | |
| 222 const std::vector<MediaRouteResponseCallback>& callbacks, | |
| 223 base::TimeDelta timeout, | |
| 224 bool off_the_record); | |
| 225 void DoJoinRoute(const MediaSource::Id& source_id, | |
| 226 const std::string& presentation_id, | |
| 227 const std::string& origin, | |
| 228 int tab_id, | |
| 229 const std::vector<MediaRouteResponseCallback>& callbacks, | |
| 230 base::TimeDelta timeout, | |
| 231 bool off_the_record); | |
| 232 void DoConnectRouteByRouteId( | |
| 233 const MediaSource::Id& source_id, | |
| 234 const MediaRoute::Id& route_id, | |
| 235 const std::string& origin, | |
| 236 int tab_id, | |
| 237 const std::vector<MediaRouteResponseCallback>& callbacks, | |
| 238 base::TimeDelta timeout, | |
| 239 bool off_the_record); | |
| 240 void DoTerminateRoute(const MediaRoute::Id& route_id); | |
| 241 void DoDetachRoute(const MediaRoute::Id& route_id); | |
| 242 void DoSendSessionMessage(const MediaRoute::Id& route_id, | |
| 243 const std::string& message, | |
| 244 const SendRouteMessageCallback& callback); | |
| 245 void DoSendSessionBinaryMessage(const MediaRoute::Id& route_id, | |
| 246 scoped_ptr<std::vector<uint8_t>> data, | |
| 247 const SendRouteMessageCallback& callback); | |
| 248 void DoListenForRouteMessages(const MediaRoute::Id& route_id); | |
| 249 void DoStopListeningForRouteMessages(const MediaRoute::Id& route_id); | |
| 250 void DoStartObservingMediaSinks(const MediaSource::Id& source_id); | |
| 251 void DoStopObservingMediaSinks(const MediaSource::Id& source_id); | |
| 252 void DoStartObservingMediaRoutes(const MediaSource::Id& source_id); | |
| 253 void DoStopObservingMediaRoutes(const MediaSource::Id& source_id); | |
| 254 | |
| 255 // Invoked when the next batch of messages arrives. | |
| 256 // |route_id|: ID of route of the messages. | |
| 257 // |messages|: A list of messages received. | |
| 258 // |error|: true if an error occurred. | |
| 259 void OnRouteMessagesReceived( | |
| 260 const MediaRoute::Id& route_id, | |
| 261 mojo::Array<interfaces::RouteMessagePtr> messages, | |
| 262 bool error); | |
| 263 | |
| 264 // Error handler callback for |binding_| and |media_route_provider_|. | |
| 265 void OnConnectionError(); | |
| 266 | |
| 267 // interfaces::MediaRouter implementation. | |
| 268 void RegisterMediaRouteProvider( | |
| 269 interfaces::MediaRouteProviderPtr media_route_provider_ptr, | |
| 270 const interfaces::MediaRouter::RegisterMediaRouteProviderCallback& | |
| 271 callback) override; | |
| 272 void OnIssue(interfaces::IssuePtr issue) override; | |
| 273 void OnSinksReceived(const mojo::String& media_source, | |
| 274 mojo::Array<interfaces::MediaSinkPtr> sinks, | |
| 275 mojo::Array<mojo::String> origins) override; | |
| 276 void OnRoutesUpdated(mojo::Array<interfaces::MediaRoutePtr> routes, | |
| 277 const mojo::String& media_source, | |
| 278 mojo::Array<mojo::String> joinable_route_ids) override; | |
| 279 void OnSinkAvailabilityUpdated( | |
| 280 interfaces::MediaRouter::SinkAvailability availability) override; | |
| 281 void OnPresentationConnectionStateChanged( | |
| 282 const mojo::String& route_id, | |
| 283 interfaces::MediaRouter::PresentationConnectionState state) override; | |
| 284 void OnPresentationConnectionClosed( | |
| 285 const mojo::String& route_id, | |
| 286 interfaces::MediaRouter::PresentationConnectionCloseReason reason, | |
| 287 const mojo::String& message) override; | |
| 288 | |
| 289 // Converts the callback result of calling Mojo CreateRoute()/JoinRoute() | |
| 290 // into a local callback. | |
| 291 void RouteResponseReceived( | |
| 292 const std::string& presentation_id, | |
| 293 bool off_the_record, | |
| 294 const std::vector<MediaRouteResponseCallback>& callbacks, | |
| 295 interfaces::MediaRoutePtr media_route, | |
| 296 const mojo::String& error_text, | |
| 297 interfaces::RouteRequestResultCode result_code); | |
| 298 | |
| 299 // Callback invoked by |event_page_tracker_| after an attempt to wake the | |
| 300 // component extension. If |success| is false, the pending request queue is | |
| 301 // drained. | |
| 302 void EventPageWakeComplete(bool success); | |
| 303 | |
| 304 // Removes all requests from the pending requests queue. Called when there is | |
| 305 // a permanent error connecting to component extension. | |
| 306 void DrainRequestQueue(); | |
| 307 | |
| 308 // Calls to |event_page_tracker_| to wake the component extension. | |
| 309 // |media_route_provider_extension_id_| must not be empty and the extension | |
| 310 // should be currently suspended. | |
| 311 // If there have already been too many wakeup attempts, give up and drain | |
| 312 // the pending request queue. | |
| 313 void AttemptWakeEventPage(); | |
| 314 | |
| 315 // Sets the reason why we are attempting to wake the extension. Since | |
| 316 // multiple tasks may be enqueued for execution each time the extension runs, | |
| 317 // we record the first such reason. | |
| 318 void SetWakeReason(MediaRouteProviderWakeReason reason); | |
| 319 | |
| 320 // Clears the wake reason after the extension has been awoken. | |
| 321 void ClearWakeReason(); | |
| 322 | |
| 323 // Pending requests queued to be executed once component extension | |
| 324 // becomes ready. | |
| 325 std::deque<base::Closure> pending_requests_; | |
| 326 | |
| 327 base::ScopedPtrHashMap<MediaSource::Id, scoped_ptr<MediaSinksQuery>> | |
| 328 sinks_queries_; | |
| 329 | |
| 330 base::ScopedPtrHashMap<MediaSource::Id, scoped_ptr<MediaRoutesQuery>> | |
| 331 routes_queries_; | |
| 332 | |
| 333 using PresentationSessionMessagesObserverList = | |
| 334 base::ObserverList<PresentationSessionMessagesObserver>; | |
| 335 base::ScopedPtrHashMap<MediaRoute::Id, | |
| 336 scoped_ptr<PresentationSessionMessagesObserverList>> | |
| 337 messages_observers_; | |
| 338 | |
| 339 // IDs of MediaRoutes being listened for messages. Note that this is | |
| 340 // different from |message_observers_| because we might be waiting for | |
| 341 // |OnRouteMessagesReceived()| to be invoked after all observers for that | |
| 342 // route have been removed. | |
| 343 std::set<MediaRoute::Id> route_ids_listening_for_messages_; | |
| 344 | |
| 345 IssueManager issue_manager_; | |
| 346 | |
| 347 // Binds |this| to a Mojo connection stub for interfaces::MediaRouter. | |
| 348 scoped_ptr<mojo::Binding<interfaces::MediaRouter>> binding_; | |
| 349 | |
| 350 // Mojo proxy object for the Media Route Provider Manager. | |
| 351 // Set to null initially, and later set to the Provider Manager proxy object | |
| 352 // passed in via |RegisterMediaRouteProvider()|. | |
| 353 // This is set to null again when the component extension is suspended | |
| 354 // if or a Mojo channel error occured. | |
| 355 interfaces::MediaRouteProviderPtr media_route_provider_; | |
| 356 | |
| 357 // Id of the component extension. Used for managing its suspend/wake state | |
| 358 // via event_page_tracker_. | |
| 359 std::string media_route_provider_extension_id_; | |
| 360 | |
| 361 // Allows the extension to be monitored for suspend, and woken. | |
| 362 // This is a reference to a BrowserContext keyed service that outlives this | |
| 363 // instance. | |
| 364 extensions::EventPageTracker* event_page_tracker_; | |
| 365 | |
| 366 // GUID unique to each browser run. Component extension uses this to detect | |
| 367 // when its persisted state was written by an older browser instance, and is | |
| 368 // therefore stale. | |
| 369 std::string instance_id_; | |
| 370 | |
| 371 // The last reported sink availability from the media route provider manager. | |
| 372 interfaces::MediaRouter::SinkAvailability availability_; | |
| 373 | |
| 374 int wakeup_attempt_count_ = 0; | |
| 375 | |
| 376 // Records the current reason the extension is being woken up. Is set to | |
| 377 // MediaRouteProviderWakeReason::TOTAL_COUNT if there is no pending reason. | |
| 378 MediaRouteProviderWakeReason current_wake_reason_; | |
| 379 | |
| 380 // A flag to ensure that we record the provider version once, during the | |
| 381 // initial event page wakeup attempt. | |
| 382 bool provider_version_was_recorded_ = false; | |
| 383 | |
| 384 base::WeakPtrFactory<MediaRouterMojoImpl> weak_factory_; | |
| 385 | |
| 386 DISALLOW_COPY_AND_ASSIGN(MediaRouterMojoImpl); | |
| 387 }; | |
| 388 | |
| 389 } // namespace media_router | |
| 390 | |
| 391 #endif // CHROME_BROWSER_MEDIA_ROUTER_MEDIA_ROUTER_MOJO_IMPL_H_ | |
| OLD | NEW |