Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(219)

Side by Side Diff: chrome/browser/media/router/media_router_mojo_impl.cc

Issue 1826403002: [Media Router] Moves mojo-specific code into mojo/ folder (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Revert change to media_router.mojom to fix compile Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 #include "chrome/browser/media/router/media_router_mojo_impl.h"
6
7 #include <stddef.h>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/guid.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_vector.h"
14 #include "base/observer_list.h"
15 #include "base/stl_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "chrome/browser/media/router/issues_observer.h"
18 #include "chrome/browser/media/router/media_route_provider_util_win.h"
19 #include "chrome/browser/media/router/media_router_factory.h"
20 #include "chrome/browser/media/router/media_router_metrics.h"
21 #include "chrome/browser/media/router/media_router_type_converters.h"
22 #include "chrome/browser/media/router/media_routes_observer.h"
23 #include "chrome/browser/media/router/media_sinks_observer.h"
24 #include "chrome/browser/media/router/presentation_session_messages_observer.h"
25 #include "chrome/browser/sessions/session_tab_helper.h"
26 #include "extensions/browser/process_manager.h"
27
28 #define DVLOG_WITH_INSTANCE(level) \
29 DVLOG(level) << "MR #" << instance_id_ << ": "
30
31 #define DLOG_WITH_INSTANCE(level) DLOG(level) << "MR #" << instance_id_ << ": "
32
33 namespace media_router {
34
35 using SinkAvailability = interfaces::MediaRouter::SinkAvailability;
36
37 namespace {
38
39 scoped_ptr<content::PresentationSessionMessage>
40 ConvertToPresentationSessionMessage(interfaces::RouteMessagePtr input) {
41 DCHECK(!input.is_null());
42 scoped_ptr<content::PresentationSessionMessage> output;
43 switch (input->type) {
44 case interfaces::RouteMessage::Type::TEXT: {
45 DCHECK(!input->message.is_null());
46 DCHECK(input->data.is_null());
47 output.reset(new content::PresentationSessionMessage(
48 content::PresentationMessageType::TEXT));
49 input->message.Swap(&output->message);
50 return output;
51 }
52 case interfaces::RouteMessage::Type::BINARY: {
53 DCHECK(!input->data.is_null());
54 DCHECK(input->message.is_null());
55 output.reset(new content::PresentationSessionMessage(
56 content::PresentationMessageType::ARRAY_BUFFER));
57 output->data.reset(new std::vector<uint8_t>);
58 input->data.Swap(output->data.get());
59 return output;
60 }
61 }
62
63 NOTREACHED() << "Invalid route message type " << input->type;
64 return output;
65 }
66
67 } // namespace
68
69 MediaRouterMojoImpl::MediaRoutesQuery::MediaRoutesQuery() = default;
70
71 MediaRouterMojoImpl::MediaRoutesQuery::~MediaRoutesQuery() = default;
72
73 MediaRouterMojoImpl::MediaSinksQuery::MediaSinksQuery() = default;
74
75 MediaRouterMojoImpl::MediaSinksQuery::~MediaSinksQuery() = default;
76
77 MediaRouterMojoImpl::MediaRouterMojoImpl(
78 extensions::EventPageTracker* event_page_tracker)
79 : event_page_tracker_(event_page_tracker),
80 instance_id_(base::GenerateGUID()),
81 availability_(interfaces::MediaRouter::SinkAvailability::UNAVAILABLE),
82 current_wake_reason_(MediaRouteProviderWakeReason::TOTAL_COUNT),
83 weak_factory_(this) {
84 DCHECK(event_page_tracker_);
85 #if defined(OS_WIN)
86 CanFirewallUseLocalPorts(
87 base::Bind(&MediaRouterMojoImpl::OnFirewallCheckComplete,
88 weak_factory_.GetWeakPtr()));
89 #endif
90 }
91
92 MediaRouterMojoImpl::~MediaRouterMojoImpl() {
93 DCHECK(thread_checker_.CalledOnValidThread());
94 }
95
96 // static
97 void MediaRouterMojoImpl::BindToRequest(
98 const extensions::Extension* extension,
99 content::BrowserContext* context,
100 mojo::InterfaceRequest<interfaces::MediaRouter> request) {
101 MediaRouterMojoImpl* impl = static_cast<MediaRouterMojoImpl*>(
102 MediaRouterFactory::GetApiForBrowserContext(context));
103 DCHECK(impl);
104
105 impl->BindToMojoRequest(std::move(request), *extension);
106 }
107
108 void MediaRouterMojoImpl::BindToMojoRequest(
109 mojo::InterfaceRequest<interfaces::MediaRouter> request,
110 const extensions::Extension& extension) {
111 DCHECK(thread_checker_.CalledOnValidThread());
112
113 binding_.reset(
114 new mojo::Binding<interfaces::MediaRouter>(this, std::move(request)));
115 binding_->set_connection_error_handler(base::Bind(
116 &MediaRouterMojoImpl::OnConnectionError, base::Unretained(this)));
117
118 media_route_provider_extension_id_ = extension.id();
119 if (!provider_version_was_recorded_) {
120 MediaRouterMetrics::RecordMediaRouteProviderVersion(extension);
121 provider_version_was_recorded_ = true;
122 }
123 }
124
125 void MediaRouterMojoImpl::OnConnectionError() {
126 DCHECK(thread_checker_.CalledOnValidThread());
127
128 media_route_provider_.reset();
129 binding_.reset();
130
131 // If |OnConnectionError| is invoked while there are pending requests, then
132 // it means we tried to wake the extension, but weren't able to complete the
133 // connection to media route provider. Since we do not know whether the error
134 // is transient, reattempt the wakeup.
135 if (!pending_requests_.empty()) {
136 DLOG_WITH_INSTANCE(ERROR) << "A connection error while there are pending "
137 "requests.";
138 SetWakeReason(MediaRouteProviderWakeReason::CONNECTION_ERROR);
139 AttemptWakeEventPage();
140 }
141 }
142
143 void MediaRouterMojoImpl::RegisterMediaRouteProvider(
144 interfaces::MediaRouteProviderPtr media_route_provider_ptr,
145 const interfaces::MediaRouter::RegisterMediaRouteProviderCallback&
146 callback) {
147 DCHECK(thread_checker_.CalledOnValidThread());
148
149 #if defined(OS_WIN)
150 // The MRPM may have been upgraded or otherwise reload such that we could be
151 // seeing an MRPM that doesn't know mDNS is enabled, even if we've told a
152 // previously registered MRPM it should be enabled. Furthermore, there may be
153 // a pending request to enable mDNS, so don't clear this flag after
154 // ExecutePendingRequests().
155 is_mdns_enabled_ = false;
156 #endif
157 if (event_page_tracker_->IsEventPageSuspended(
158 media_route_provider_extension_id_)) {
159 DVLOG_WITH_INSTANCE(1)
160 << "RegisterMediaRouteProvider was called while extension is "
161 "suspended.";
162 media_route_provider_.reset();
163 SetWakeReason(MediaRouteProviderWakeReason::REGISTER_MEDIA_ROUTE_PROVIDER);
164 AttemptWakeEventPage();
165 return;
166 }
167
168 media_route_provider_ = std::move(media_route_provider_ptr);
169 media_route_provider_.set_connection_error_handler(base::Bind(
170 &MediaRouterMojoImpl::OnConnectionError, base::Unretained(this)));
171 callback.Run(instance_id_);
172 ExecutePendingRequests();
173 wakeup_attempt_count_ = 0;
174 #if defined(OS_WIN)
175 // The MRPM extension already turns on mDNS discovery for platforms other than
176 // Windows. It only relies on this signalling from MR on Windows to avoid
177 // triggering a firewall prompt out of the context of MR from the user's
178 // perspective. This particular call reminds the extension to enable mDNS
179 // discovery when it wakes up, has been upgraded, etc.
180 if (should_enable_mdns_discovery_) {
181 DoEnsureMdnsDiscoveryEnabled();
182 }
183 #endif
184 }
185
186 void MediaRouterMojoImpl::OnIssue(const interfaces::IssuePtr issue) {
187 DCHECK(thread_checker_.CalledOnValidThread());
188 DVLOG_WITH_INSTANCE(1) << "OnIssue " << issue->title;
189 const Issue& issue_converted = issue.To<Issue>();
190 issue_manager_.AddIssue(issue_converted);
191 }
192
193 void MediaRouterMojoImpl::OnSinksReceived(
194 const mojo::String& media_source,
195 mojo::Array<interfaces::MediaSinkPtr> sinks,
196 mojo::Array<mojo::String> origins) {
197 DCHECK(thread_checker_.CalledOnValidThread());
198 DVLOG_WITH_INSTANCE(1) << "OnSinksReceived";
199 auto it = sinks_queries_.find(media_source);
200 if (it == sinks_queries_.end()) {
201 DVLOG_WITH_INSTANCE(1) << "Received sink list without MediaSinksQuery.";
202 return;
203 }
204
205 std::vector<GURL> origin_list;
206 origin_list.reserve(origins.size());
207 for (size_t i = 0; i < origins.size(); ++i) {
208 GURL origin(origins[i].get());
209 if (!origin.is_valid()) {
210 LOG(WARNING) << "Received invalid origin: " << origin
211 << ". Dropping result.";
212 return;
213 }
214 origin_list.push_back(origin);
215 }
216
217 std::vector<MediaSink> sink_list;
218 sink_list.reserve(sinks.size());
219 for (size_t i = 0; i < sinks.size(); ++i)
220 sink_list.push_back(sinks[i].To<MediaSink>());
221
222 auto* sinks_query = it->second;
223 sinks_query->has_cached_result = true;
224 sinks_query->origins.swap(origin_list);
225 sinks_query->cached_sink_list.swap(sink_list);
226
227 if (!sinks_query->observers.might_have_observers()) {
228 DVLOG_WITH_INSTANCE(1)
229 << "Received sink list without any active observers: " << media_source;
230 } else {
231 FOR_EACH_OBSERVER(
232 MediaSinksObserver, sinks_query->observers,
233 OnSinksUpdated(sinks_query->cached_sink_list, sinks_query->origins));
234 }
235 }
236
237 void MediaRouterMojoImpl::OnRoutesUpdated(
238 mojo::Array<interfaces::MediaRoutePtr> routes,
239 const mojo::String& media_source,
240 mojo::Array<mojo::String> joinable_route_ids) {
241 DCHECK(thread_checker_.CalledOnValidThread());
242
243 DVLOG_WITH_INSTANCE(1) << "OnRoutesUpdated";
244 auto it = routes_queries_.find(media_source);
245 if (it == routes_queries_.end() ||
246 !(it->second->observers.might_have_observers())) {
247 DVLOG_WITH_INSTANCE(1)
248 << "Received route list without any active observers: " << media_source;
249 return;
250 }
251
252 std::vector<MediaRoute> routes_converted;
253 routes_converted.reserve(routes.size());
254
255 for (size_t i = 0; i < routes.size(); ++i)
256 routes_converted.push_back(routes[i].To<MediaRoute>());
257
258 std::vector<MediaRoute::Id> joinable_routes_converted =
259 joinable_route_ids.To<std::vector<std::string>>();
260
261 FOR_EACH_OBSERVER(MediaRoutesObserver, it->second->observers,
262 OnRoutesUpdated(routes_converted,
263 joinable_routes_converted));
264 }
265
266 void MediaRouterMojoImpl::RouteResponseReceived(
267 const std::string& presentation_id,
268 bool off_the_record,
269 const std::vector<MediaRouteResponseCallback>& callbacks,
270 interfaces::MediaRoutePtr media_route,
271 const mojo::String& error_text,
272 interfaces::RouteRequestResultCode result_code) {
273 scoped_ptr<RouteRequestResult> result;
274 if (media_route.is_null()) {
275 // An error occurred.
276 DCHECK(!error_text.is_null());
277 std::string error =
278 !error_text.get().empty() ? error_text.get() : "Unknown error.";
279 result = RouteRequestResult::FromError(
280 error, mojo::RouteRequestResultCodeFromMojo(result_code));
281 } else if (media_route->off_the_record != off_the_record) {
282 std::string error = base::StringPrintf(
283 "Mismatch in off the record status: request = %d, response = %d",
284 off_the_record, media_route->off_the_record);
285 result = RouteRequestResult::FromError(
286 error, RouteRequestResult::OFF_THE_RECORD_MISMATCH);
287 } else {
288 result = RouteRequestResult::FromSuccess(
289 media_route.To<scoped_ptr<MediaRoute>>(), presentation_id);
290 if (result->route()->off_the_record())
291 OnOffTheRecordRouteCreated(result->route()->media_route_id());
292 }
293
294 // TODO(imcheng): Add UMA histogram based on result code (crbug.com/583044).
295 for (const MediaRouteResponseCallback& callback : callbacks)
296 callback.Run(*result);
297 }
298
299 void MediaRouterMojoImpl::CreateRoute(
300 const MediaSource::Id& source_id,
301 const MediaSink::Id& sink_id,
302 const GURL& origin,
303 content::WebContents* web_contents,
304 const std::vector<MediaRouteResponseCallback>& callbacks,
305 base::TimeDelta timeout,
306 bool off_the_record) {
307 DCHECK(thread_checker_.CalledOnValidThread());
308
309 if (!origin.is_valid()) {
310 DVLOG_WITH_INSTANCE(1) << "Invalid origin: " << origin;
311 scoped_ptr<RouteRequestResult> result = RouteRequestResult::FromError(
312 "Invalid origin", RouteRequestResult::INVALID_ORIGIN);
313 for (const MediaRouteResponseCallback& callback : callbacks)
314 callback.Run(*result);
315 return;
316 }
317
318 SetWakeReason(MediaRouteProviderWakeReason::CREATE_ROUTE);
319 int tab_id = SessionTabHelper::IdForTab(web_contents);
320 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoCreateRoute,
321 base::Unretained(this), source_id, sink_id,
322 origin.is_empty() ? "" : origin.spec(), tab_id,
323 callbacks, timeout, off_the_record));
324 }
325
326 void MediaRouterMojoImpl::JoinRoute(
327 const MediaSource::Id& source_id,
328 const std::string& presentation_id,
329 const GURL& origin,
330 content::WebContents* web_contents,
331 const std::vector<MediaRouteResponseCallback>& callbacks,
332 base::TimeDelta timeout,
333 bool off_the_record) {
334 DCHECK(thread_checker_.CalledOnValidThread());
335
336 if (!origin.is_valid()) {
337 DVLOG_WITH_INSTANCE(1) << "Invalid origin: " << origin;
338 scoped_ptr<RouteRequestResult> result = RouteRequestResult::FromError(
339 "Invalid origin", RouteRequestResult::INVALID_ORIGIN);
340 for (const MediaRouteResponseCallback& callback : callbacks)
341 callback.Run(*result);
342 return;
343 }
344
345 SetWakeReason(MediaRouteProviderWakeReason::JOIN_ROUTE);
346 int tab_id = SessionTabHelper::IdForTab(web_contents);
347 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoJoinRoute,
348 base::Unretained(this), source_id, presentation_id,
349 origin.is_empty() ? "" : origin.spec(), tab_id,
350 callbacks, timeout, off_the_record));
351 }
352
353 void MediaRouterMojoImpl::ConnectRouteByRouteId(
354 const MediaSource::Id& source_id,
355 const MediaRoute::Id& route_id,
356 const GURL& origin,
357 content::WebContents* web_contents,
358 const std::vector<MediaRouteResponseCallback>& callbacks,
359 base::TimeDelta timeout,
360 bool off_the_record) {
361 DCHECK(thread_checker_.CalledOnValidThread());
362
363 if (!origin.is_valid()) {
364 DVLOG_WITH_INSTANCE(1) << "Invalid origin: " << origin;
365 scoped_ptr<RouteRequestResult> result = RouteRequestResult::FromError(
366 "Invalid origin", RouteRequestResult::INVALID_ORIGIN);
367 for (const MediaRouteResponseCallback& callback : callbacks)
368 callback.Run(*result);
369 return;
370 }
371
372 SetWakeReason(MediaRouteProviderWakeReason::CONNECT_ROUTE_BY_ROUTE_ID);
373 int tab_id = SessionTabHelper::IdForTab(web_contents);
374 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoConnectRouteByRouteId,
375 base::Unretained(this), source_id, route_id,
376 origin.is_empty() ? "" : origin.spec(), tab_id,
377 callbacks, timeout, off_the_record));
378 }
379
380 void MediaRouterMojoImpl::TerminateRoute(const MediaRoute::Id& route_id) {
381 DCHECK(thread_checker_.CalledOnValidThread());
382 DVLOG(2) << "TerminateRoute " << route_id;
383 SetWakeReason(MediaRouteProviderWakeReason::TERMINATE_ROUTE);
384 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoTerminateRoute,
385 base::Unretained(this), route_id));
386 }
387
388 void MediaRouterMojoImpl::DetachRoute(const MediaRoute::Id& route_id) {
389 DCHECK(thread_checker_.CalledOnValidThread());
390
391 SetWakeReason(MediaRouteProviderWakeReason::DETACH_ROUTE);
392 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoDetachRoute,
393 base::Unretained(this), route_id));
394 }
395
396 void MediaRouterMojoImpl::SendRouteMessage(
397 const MediaRoute::Id& route_id,
398 const std::string& message,
399 const SendRouteMessageCallback& callback) {
400 DCHECK(thread_checker_.CalledOnValidThread());
401
402 SetWakeReason(MediaRouteProviderWakeReason::SEND_SESSION_MESSAGE);
403 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoSendSessionMessage,
404 base::Unretained(this), route_id, message, callback));
405 }
406
407 void MediaRouterMojoImpl::SendRouteBinaryMessage(
408 const MediaRoute::Id& route_id,
409 scoped_ptr<std::vector<uint8_t>> data,
410 const SendRouteMessageCallback& callback) {
411 DCHECK(thread_checker_.CalledOnValidThread());
412
413 SetWakeReason(MediaRouteProviderWakeReason::SEND_SESSION_BINARY_MESSAGE);
414 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoSendSessionBinaryMessage,
415 base::Unretained(this), route_id,
416 base::Passed(std::move(data)), callback));
417 }
418
419 void MediaRouterMojoImpl::AddIssue(const Issue& issue) {
420 DCHECK(thread_checker_.CalledOnValidThread());
421 issue_manager_.AddIssue(issue);
422 }
423
424 void MediaRouterMojoImpl::ClearIssue(const Issue::Id& issue_id) {
425 DCHECK(thread_checker_.CalledOnValidThread());
426 issue_manager_.ClearIssue(issue_id);
427 }
428
429 void MediaRouterMojoImpl::OnUserGesture() {
430 #if defined(OS_WIN)
431 EnsureMdnsDiscoveryEnabled();
432 #endif
433 }
434
435 bool MediaRouterMojoImpl::RegisterMediaSinksObserver(
436 MediaSinksObserver* observer) {
437 DCHECK(thread_checker_.CalledOnValidThread());
438
439 // Create an observer list for the media source and add |observer|
440 // to it. Fail if |observer| is already registered.
441 const std::string& source_id = observer->source().id();
442 auto* sinks_query = sinks_queries_.get(source_id);
443 bool new_query = false;
444 if (!sinks_query) {
445 new_query = true;
446 sinks_query = new MediaSinksQuery;
447 sinks_queries_.add(source_id, make_scoped_ptr(sinks_query));
448 } else {
449 DCHECK(!sinks_query->observers.HasObserver(observer));
450 }
451
452 // If sink availability is UNAVAILABLE, then there is no need to call MRPM.
453 // |observer| can be immediately notified with an empty list.
454 sinks_query->observers.AddObserver(observer);
455 if (availability_ == interfaces::MediaRouter::SinkAvailability::UNAVAILABLE) {
456 observer->OnSinksUpdated(std::vector<MediaSink>(), std::vector<GURL>());
457 } else {
458 // Need to call MRPM to start observing sinks if the query is new.
459 if (new_query) {
460 SetWakeReason(MediaRouteProviderWakeReason::START_OBSERVING_MEDIA_SINKS);
461 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStartObservingMediaSinks,
462 base::Unretained(this), source_id));
463 } else if (sinks_query->has_cached_result) {
464 observer->OnSinksUpdated(sinks_query->cached_sink_list,
465 sinks_query->origins);
466 }
467 }
468 return true;
469 }
470
471 void MediaRouterMojoImpl::UnregisterMediaSinksObserver(
472 MediaSinksObserver* observer) {
473 DCHECK(thread_checker_.CalledOnValidThread());
474
475 const MediaSource::Id& source_id = observer->source().id();
476 auto* sinks_query = sinks_queries_.get(source_id);
477 if (!sinks_query || !sinks_query->observers.HasObserver(observer)) {
478 return;
479 }
480
481 // If we are removing the final observer for the source, then stop
482 // observing sinks for it.
483 // might_have_observers() is reliable here on the assumption that this call
484 // is not inside the ObserverList iteration.
485 sinks_query->observers.RemoveObserver(observer);
486 if (!sinks_query->observers.might_have_observers()) {
487 // Only ask MRPM to stop observing media sinks if the availability is not
488 // UNAVAILABLE.
489 // Otherwise, the MRPM would have discarded the queries already.
490 if (availability_ !=
491 interfaces::MediaRouter::SinkAvailability::UNAVAILABLE) {
492 SetWakeReason(MediaRouteProviderWakeReason::STOP_OBSERVING_MEDIA_SINKS);
493 // The |sinks_queries_| entry will be removed in the immediate or deferred
494 // |DoStopObservingMediaSinks| call.
495 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStopObservingMediaSinks,
496 base::Unretained(this), source_id));
497 } else {
498 sinks_queries_.erase(source_id);
499 }
500 }
501 }
502
503 void MediaRouterMojoImpl::RegisterMediaRoutesObserver(
504 MediaRoutesObserver* observer) {
505 DCHECK(thread_checker_.CalledOnValidThread());
506 const MediaSource::Id source_id = observer->source_id();
507 auto* routes_query = routes_queries_.get(source_id);
508 if (!routes_query) {
509 routes_query = new MediaRoutesQuery;
510 routes_queries_.add(source_id, make_scoped_ptr(routes_query));
511 } else {
512 DCHECK(!routes_query->observers.HasObserver(observer));
513 }
514
515 routes_query->observers.AddObserver(observer);
516 SetWakeReason(MediaRouteProviderWakeReason::START_OBSERVING_MEDIA_ROUTES);
517 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStartObservingMediaRoutes,
518 base::Unretained(this), source_id));
519 }
520
521 void MediaRouterMojoImpl::UnregisterMediaRoutesObserver(
522 MediaRoutesObserver* observer) {
523 const MediaSource::Id source_id = observer->source_id();
524 auto* routes_query = routes_queries_.get(source_id);
525 if (!routes_query || !routes_query->observers.HasObserver(observer)) {
526 return;
527 }
528
529 // If we are removing the final observer for the source, then stop
530 // observing routes for it.
531 // might_have_observers() is reliable here on the assumption that this call
532 // is not inside the ObserverList iteration.
533 routes_query->observers.RemoveObserver(observer);
534 if (!routes_query->observers.might_have_observers()) {
535 SetWakeReason(MediaRouteProviderWakeReason::STOP_OBSERVING_MEDIA_ROUTES);
536 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStopObservingMediaRoutes,
537 base::Unretained(this), source_id));
538 }
539 }
540
541 void MediaRouterMojoImpl::RegisterIssuesObserver(IssuesObserver* observer) {
542 DCHECK(thread_checker_.CalledOnValidThread());
543 issue_manager_.RegisterObserver(observer);
544 }
545
546 void MediaRouterMojoImpl::UnregisterIssuesObserver(IssuesObserver* observer) {
547 DCHECK(thread_checker_.CalledOnValidThread());
548 issue_manager_.UnregisterObserver(observer);
549 }
550
551 void MediaRouterMojoImpl::RegisterPresentationSessionMessagesObserver(
552 PresentationSessionMessagesObserver* observer) {
553 DCHECK(thread_checker_.CalledOnValidThread());
554 DCHECK(observer);
555 const MediaRoute::Id& route_id = observer->route_id();
556 auto* observer_list = messages_observers_.get(route_id);
557 if (!observer_list) {
558 observer_list = new base::ObserverList<PresentationSessionMessagesObserver>;
559 messages_observers_.add(route_id, make_scoped_ptr(observer_list));
560 } else {
561 DCHECK(!observer_list->HasObserver(observer));
562 }
563
564 bool should_listen = !observer_list->might_have_observers();
565 observer_list->AddObserver(observer);
566 if (should_listen) {
567 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoListenForRouteMessages,
568 base::Unretained(this), route_id));
569 }
570 }
571
572 void MediaRouterMojoImpl::UnregisterPresentationSessionMessagesObserver(
573 PresentationSessionMessagesObserver* observer) {
574 DCHECK(thread_checker_.CalledOnValidThread());
575 DCHECK(observer);
576
577 const MediaRoute::Id& route_id = observer->route_id();
578 auto* observer_list = messages_observers_.get(route_id);
579 if (!observer_list || !observer_list->HasObserver(observer))
580 return;
581
582 observer_list->RemoveObserver(observer);
583 if (!observer_list->might_have_observers()) {
584 messages_observers_.erase(route_id);
585 SetWakeReason(
586 MediaRouteProviderWakeReason::STOP_LISTENING_FOR_ROUTE_MESSAGES);
587 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStopListeningForRouteMessages,
588 base::Unretained(this), route_id));
589 }
590 }
591
592 void MediaRouterMojoImpl::DoCreateRoute(
593 const MediaSource::Id& source_id,
594 const MediaSink::Id& sink_id,
595 const std::string& origin,
596 int tab_id,
597 const std::vector<MediaRouteResponseCallback>& callbacks,
598 base::TimeDelta timeout,
599 bool off_the_record) {
600 std::string presentation_id("mr_");
601 presentation_id += base::GenerateGUID();
602 DVLOG_WITH_INSTANCE(1) << "DoCreateRoute " << source_id << "=>" << sink_id
603 << ", presentation ID: " << presentation_id;
604
605 media_route_provider_->CreateRoute(
606 source_id, sink_id, presentation_id, origin, tab_id,
607 timeout > base::TimeDelta() ? timeout.InMilliseconds() : 0,
608 off_the_record, base::Bind(&MediaRouterMojoImpl::RouteResponseReceived,
609 base::Unretained(this), presentation_id,
610 off_the_record, callbacks));
611 }
612
613 void MediaRouterMojoImpl::DoJoinRoute(
614 const MediaSource::Id& source_id,
615 const std::string& presentation_id,
616 const std::string& origin,
617 int tab_id,
618 const std::vector<MediaRouteResponseCallback>& callbacks,
619 base::TimeDelta timeout,
620 bool off_the_record) {
621 DVLOG_WITH_INSTANCE(1) << "DoJoinRoute " << source_id
622 << ", presentation ID: " << presentation_id;
623
624 media_route_provider_->JoinRoute(
625 source_id, presentation_id, origin, tab_id,
626 timeout > base::TimeDelta() ? timeout.InMilliseconds() : 0,
627 off_the_record, base::Bind(&MediaRouterMojoImpl::RouteResponseReceived,
628 base::Unretained(this), presentation_id,
629 off_the_record, callbacks));
630 }
631
632 void MediaRouterMojoImpl::DoConnectRouteByRouteId(
633 const MediaSource::Id& source_id,
634 const MediaRoute::Id& route_id,
635 const std::string& origin,
636 int tab_id,
637 const std::vector<MediaRouteResponseCallback>& callbacks,
638 base::TimeDelta timeout,
639 bool off_the_record) {
640 std::string presentation_id("mr_");
641 presentation_id += base::GenerateGUID();
642 DVLOG_WITH_INSTANCE(1) << "DoConnectRouteByRouteId " << source_id
643 << ", route ID: " << route_id
644 << ", presentation ID: " << presentation_id;
645
646 media_route_provider_->ConnectRouteByRouteId(
647 source_id, route_id, presentation_id, origin, tab_id,
648 timeout > base::TimeDelta() ? timeout.InMilliseconds() : 0,
649 off_the_record, base::Bind(&MediaRouterMojoImpl::RouteResponseReceived,
650 base::Unretained(this), presentation_id,
651 off_the_record, callbacks));
652 }
653
654 void MediaRouterMojoImpl::DoTerminateRoute(const MediaRoute::Id& route_id) {
655 DVLOG_WITH_INSTANCE(1) << "DoTerminateRoute " << route_id;
656 media_route_provider_->TerminateRoute(route_id);
657 OnRouteTerminated(route_id);
658 }
659
660 void MediaRouterMojoImpl::DoDetachRoute(const MediaRoute::Id& route_id) {
661 DVLOG_WITH_INSTANCE(1) << "DoDetachRoute " << route_id;
662 media_route_provider_->DetachRoute(route_id);
663 }
664
665 void MediaRouterMojoImpl::DoSendSessionMessage(
666 const MediaRoute::Id& route_id,
667 const std::string& message,
668 const SendRouteMessageCallback& callback) {
669 DVLOG_WITH_INSTANCE(1) << "SendRouteMessage " << route_id;
670 media_route_provider_->SendRouteMessage(route_id, message, callback);
671 }
672
673 void MediaRouterMojoImpl::DoSendSessionBinaryMessage(
674 const MediaRoute::Id& route_id,
675 scoped_ptr<std::vector<uint8_t>> data,
676 const SendRouteMessageCallback& callback) {
677 DVLOG_WITH_INSTANCE(1) << "SendRouteBinaryMessage " << route_id;
678 mojo::Array<uint8_t> mojo_array;
679 mojo_array.Swap(data.get());
680 media_route_provider_->SendRouteBinaryMessage(route_id, std::move(mojo_array),
681 callback);
682 }
683
684 void MediaRouterMojoImpl::DoListenForRouteMessages(
685 const MediaRoute::Id& route_id) {
686 DVLOG_WITH_INSTANCE(1) << "ListenForRouteMessages";
687 if (!ContainsValue(route_ids_listening_for_messages_, route_id)) {
688 route_ids_listening_for_messages_.insert(route_id);
689 media_route_provider_->ListenForRouteMessages(
690 route_id, base::Bind(&MediaRouterMojoImpl::OnRouteMessagesReceived,
691 base::Unretained(this), route_id));
692 }
693 }
694
695 void MediaRouterMojoImpl::DoStopListeningForRouteMessages(
696 const MediaRoute::Id& route_id) {
697 DVLOG_WITH_INSTANCE(1) << "StopListeningForRouteMessages";
698
699 // No need to erase |route_ids_listening_for_messages_| entry here.
700 // It will be removed when there are no more observers by the time
701 // |OnRouteMessagesReceived| is invoked.
702 media_route_provider_->StopListeningForRouteMessages(route_id);
703 }
704
705 void MediaRouterMojoImpl::OnRouteMessagesReceived(
706 const MediaRoute::Id& route_id,
707 mojo::Array<interfaces::RouteMessagePtr> messages,
708 bool error) {
709 DVLOG(1) << "OnRouteMessagesReceived";
710
711 // If |messages| is null, then no more messages will come from this route.
712 // We can stop listening.
713 if (error) {
714 DVLOG(2) << "Encountered error in OnRouteMessagesReceived for " << route_id;
715 route_ids_listening_for_messages_.erase(route_id);
716 return;
717 }
718
719 // Check if there are any observers remaining. If not, the messages
720 // can be discarded and we can stop listening for the next batch of messages.
721 auto* observer_list = messages_observers_.get(route_id);
722 if (!observer_list) {
723 route_ids_listening_for_messages_.erase(route_id);
724 return;
725 }
726
727 // If |messages| is empty, then |StopListeningForRouteMessages| was invoked
728 // but we have added back an observer since. Keep listening for more messages,
729 // but do not notify observers with empty list.
730 if (!messages.storage().empty()) {
731 ScopedVector<content::PresentationSessionMessage> session_messages;
732 session_messages.reserve(messages.size());
733 for (size_t i = 0; i < messages.size(); ++i) {
734 session_messages.push_back(
735 ConvertToPresentationSessionMessage(std::move(messages[i])));
736 }
737 base::ObserverList<PresentationSessionMessagesObserver>::Iterator
738 observer_it(observer_list);
739 bool single_observer =
740 observer_it.GetNext() != nullptr && observer_it.GetNext() == nullptr;
741 FOR_EACH_OBSERVER(PresentationSessionMessagesObserver, *observer_list,
742 OnMessagesReceived(session_messages, single_observer));
743 }
744
745 // Listen for more messages.
746 media_route_provider_->ListenForRouteMessages(
747 route_id, base::Bind(&MediaRouterMojoImpl::OnRouteMessagesReceived,
748 base::Unretained(this), route_id));
749 }
750
751 void MediaRouterMojoImpl::OnSinkAvailabilityUpdated(
752 SinkAvailability availability) {
753 if (availability_ == availability)
754 return;
755
756 availability_ = availability;
757 if (availability_ == interfaces::MediaRouter::SinkAvailability::UNAVAILABLE) {
758 // Sinks are no longer available. MRPM has already removed all sink queries.
759 for (auto& source_and_query : sinks_queries_) {
760 auto* query = source_and_query.second;
761 query->is_active = false;
762 query->has_cached_result = false;
763 query->cached_sink_list.clear();
764 query->origins.clear();
765 }
766 } else {
767 // Sinks are now available. Tell MRPM to start all sink queries again.
768 for (const auto& source_and_query : sinks_queries_) {
769 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStartObservingMediaSinks,
770 base::Unretained(this), source_and_query.first));
771 }
772 }
773 }
774
775 void MediaRouterMojoImpl::OnPresentationConnectionStateChanged(
776 const mojo::String& route_id,
777 interfaces::MediaRouter::PresentationConnectionState state) {
778 NotifyPresentationConnectionStateChange(
779 route_id, mojo::PresentationConnectionStateFromMojo(state));
780 }
781
782 void MediaRouterMojoImpl::OnPresentationConnectionClosed(
783 const mojo::String& route_id,
784 interfaces::MediaRouter::PresentationConnectionCloseReason reason,
785 const mojo::String& message) {
786 NotifyPresentationConnectionClose(
787 route_id, mojo::PresentationConnectionCloseReasonFromMojo(reason),
788 message);
789 }
790
791 void MediaRouterMojoImpl::DoStartObservingMediaSinks(
792 const MediaSource::Id& source_id) {
793 DVLOG_WITH_INSTANCE(1) << "DoStartObservingMediaSinks: " << source_id;
794 // No need to call MRPM if there are no sinks available.
795 if (availability_ == interfaces::MediaRouter::SinkAvailability::UNAVAILABLE)
796 return;
797
798 // No need to call MRPM if all observers have been removed in the meantime.
799 auto* sinks_query = sinks_queries_.get(source_id);
800 if (!sinks_query || !sinks_query->observers.might_have_observers())
801 return;
802
803 DVLOG_WITH_INSTANCE(1) << "MRPM.StartObservingMediaSinks: " << source_id;
804 media_route_provider_->StartObservingMediaSinks(source_id);
805 sinks_query->is_active = true;
806 }
807
808 void MediaRouterMojoImpl::DoStopObservingMediaSinks(
809 const MediaSource::Id& source_id) {
810 DVLOG_WITH_INSTANCE(1) << "DoStopObservingMediaSinks: " << source_id;
811
812 auto* sinks_query = sinks_queries_.get(source_id);
813 // No need to call MRPM if observers have been added in the meantime,
814 // or StopObservingMediaSinks has already been called.
815 if (!sinks_query || !sinks_query->is_active ||
816 sinks_query->observers.might_have_observers()) {
817 return;
818 }
819
820 DVLOG_WITH_INSTANCE(1) << "MRPM.StopObservingMediaSinks: " << source_id;
821 media_route_provider_->StopObservingMediaSinks(source_id);
822 sinks_queries_.erase(source_id);
823 }
824
825 void MediaRouterMojoImpl::DoStartObservingMediaRoutes(
826 const MediaSource::Id& source_id) {
827 DVLOG_WITH_INSTANCE(1) << "DoStartObservingMediaRoutes";
828
829 // No need to call MRPM if all observers have been removed in the meantime.
830 auto* routes_query = routes_queries_.get(source_id);
831 if (!routes_query || !routes_query->observers.might_have_observers())
832 return;
833
834 DVLOG_WITH_INSTANCE(1) << "MRPM.StartObservingMediaRoutes: " << source_id;
835 media_route_provider_->StartObservingMediaRoutes(source_id);
836 routes_query->is_active = true;
837 }
838
839 void MediaRouterMojoImpl::DoStopObservingMediaRoutes(
840 const MediaSource::Id& source_id) {
841 DVLOG_WITH_INSTANCE(1) << "DoStopObservingMediaRoutes";
842
843 // No need to call MRPM if observers have been added in the meantime,
844 // or StopObservingMediaRoutes has already been called.
845 auto* routes_query = routes_queries_.get(source_id);
846 if (!routes_query || !routes_query->is_active ||
847 routes_query->observers.might_have_observers()) {
848 return;
849 }
850
851 DVLOG_WITH_INSTANCE(1) << "MRPM.StopObservingMediaRoutes: " << source_id;
852 media_route_provider_->StopObservingMediaRoutes(source_id);
853 routes_queries_.erase(source_id);
854 }
855
856 void MediaRouterMojoImpl::EnqueueTask(const base::Closure& closure) {
857 pending_requests_.push_back(closure);
858 if (pending_requests_.size() > kMaxPendingRequests) {
859 DLOG_WITH_INSTANCE(ERROR) << "Reached max queue size. Dropping oldest "
860 << "request.";
861 pending_requests_.pop_front();
862 }
863 DVLOG_WITH_INSTANCE(2) << "EnqueueTask (queue-length="
864 << pending_requests_.size() << ")";
865 }
866
867 void MediaRouterMojoImpl::RunOrDefer(const base::Closure& request) {
868 DCHECK(event_page_tracker_);
869
870 if (media_route_provider_extension_id_.empty()) {
871 DVLOG_WITH_INSTANCE(1) << "Extension ID not known yet.";
872 EnqueueTask(request);
873 } else if (event_page_tracker_->IsEventPageSuspended(
874 media_route_provider_extension_id_)) {
875 DVLOG_WITH_INSTANCE(1) << "Waking event page.";
876 EnqueueTask(request);
877 AttemptWakeEventPage();
878 media_route_provider_.reset();
879 } else if (!media_route_provider_) {
880 DVLOG_WITH_INSTANCE(1) << "Extension is awake, awaiting ProvideMediaRouter "
881 " to be called.";
882 EnqueueTask(request);
883 } else {
884 request.Run();
885 }
886 }
887
888 void MediaRouterMojoImpl::AttemptWakeEventPage() {
889 ++wakeup_attempt_count_;
890 if (wakeup_attempt_count_ > kMaxWakeupAttemptCount) {
891 DLOG_WITH_INSTANCE(ERROR) << "Attempted too many times to wake up event "
892 << "page.";
893 DrainPendingRequests();
894 wakeup_attempt_count_ = 0;
895 MediaRouterMetrics::RecordMediaRouteProviderWakeup(
896 MediaRouteProviderWakeup::ERROR_TOO_MANY_RETRIES);
897 return;
898 }
899
900 DVLOG_WITH_INSTANCE(1) << "Attempting to wake up event page: attempt "
901 << wakeup_attempt_count_;
902
903 // This return false if the extension is already awake.
904 // Callback is bound using WeakPtr because |event_page_tracker_| outlives
905 // |this|.
906 if (!event_page_tracker_->WakeEventPage(
907 media_route_provider_extension_id_,
908 base::Bind(&MediaRouterMojoImpl::EventPageWakeComplete,
909 weak_factory_.GetWeakPtr()))) {
910 DLOG_WITH_INSTANCE(ERROR) << "Failed to schedule a wakeup for event page.";
911 }
912 }
913
914 void MediaRouterMojoImpl::ExecutePendingRequests() {
915 DCHECK(thread_checker_.CalledOnValidThread());
916 DCHECK(media_route_provider_);
917 DCHECK(event_page_tracker_);
918 DCHECK(!media_route_provider_extension_id_.empty());
919
920 for (const auto& next_request : pending_requests_)
921 next_request.Run();
922
923 pending_requests_.clear();
924 }
925
926 void MediaRouterMojoImpl::EventPageWakeComplete(bool success) {
927 if (success) {
928 MediaRouterMetrics::RecordMediaRouteProviderWakeReason(
929 current_wake_reason_);
930 ClearWakeReason();
931 MediaRouterMetrics::RecordMediaRouteProviderWakeup(
932 MediaRouteProviderWakeup::SUCCESS);
933 return;
934 }
935
936 // This is likely an non-retriable error. Drop the pending requests.
937 DLOG_WITH_INSTANCE(ERROR)
938 << "An error encountered while waking the event page.";
939 ClearWakeReason();
940 DrainPendingRequests();
941 MediaRouterMetrics::RecordMediaRouteProviderWakeup(
942 MediaRouteProviderWakeup::ERROR_UNKNOWN);
943 }
944
945 void MediaRouterMojoImpl::DrainPendingRequests() {
946 DLOG_WITH_INSTANCE(ERROR)
947 << "Draining request queue. (queue-length=" << pending_requests_.size()
948 << ")";
949 pending_requests_.clear();
950 }
951
952 void MediaRouterMojoImpl::SetWakeReason(MediaRouteProviderWakeReason reason) {
953 DCHECK(reason != MediaRouteProviderWakeReason::TOTAL_COUNT);
954 if (current_wake_reason_ == MediaRouteProviderWakeReason::TOTAL_COUNT)
955 current_wake_reason_ = reason;
956 }
957
958 void MediaRouterMojoImpl::ClearWakeReason() {
959 DCHECK(current_wake_reason_ != MediaRouteProviderWakeReason::TOTAL_COUNT);
960 current_wake_reason_ = MediaRouteProviderWakeReason::TOTAL_COUNT;
961 }
962
963 #if defined(OS_WIN)
964 void MediaRouterMojoImpl::EnsureMdnsDiscoveryEnabled() {
965 if (is_mdns_enabled_)
966 return;
967
968 SetWakeReason(MediaRouteProviderWakeReason::ENABLE_MDNS_DISCOVERY);
969 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoEnsureMdnsDiscoveryEnabled,
970 base::Unretained(this)));
971 should_enable_mdns_discovery_ = true;
972 }
973
974 void MediaRouterMojoImpl::DoEnsureMdnsDiscoveryEnabled() {
975 DVLOG_WITH_INSTANCE(1) << "DoEnsureMdnsDiscoveryEnabled";
976 if (!is_mdns_enabled_) {
977 media_route_provider_->EnableMdnsDiscovery();
978 is_mdns_enabled_ = true;
979 }
980 }
981
982 void MediaRouterMojoImpl::OnFirewallCheckComplete(
983 bool firewall_can_use_local_ports) {
984 if (firewall_can_use_local_ports)
985 EnsureMdnsDiscoveryEnabled();
986 }
987 #endif
988
989 } // namespace media_router
OLDNEW
« no previous file with comments | « chrome/browser/media/router/media_router_mojo_impl.h ('k') | chrome/browser/media/router/media_router_mojo_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698