Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / media / router / media_router_mojo_impl.cc
blobcfdd2b8d355f2b346a4fab1c63550b3c26781c9b
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.
5 #include "chrome/browser/media/router/media_router_mojo_impl.h"
7 #include "base/bind.h"
8 #include "base/guid.h"
9 #include "base/logging.h"
10 #include "base/memory/scoped_vector.h"
11 #include "base/observer_list.h"
12 #include "base/strings/stringprintf.h"
13 #include "chrome/browser/media/router/issues_observer.h"
14 #include "chrome/browser/media/router/media_router_factory.h"
15 #include "chrome/browser/media/router/media_router_type_converters.h"
16 #include "chrome/browser/media/router/media_routes_observer.h"
17 #include "chrome/browser/media/router/media_sinks_observer.h"
18 #include "chrome/browser/media/router/presentation_session_messages_observer.h"
19 #include "extensions/browser/process_manager.h"
21 #define DVLOG_WITH_INSTANCE(level) \
22 DVLOG(level) << "MR #" << instance_id_ << ": "
24 #define DLOG_WITH_INSTANCE(level) DLOG(level) << "MR #" << instance_id_ << ": "
26 namespace media_router {
27 namespace {
29 // Converts the callback result of calling Mojo CreateRoute()/JoinRoute()
30 // into a local callback.
31 void RouteResponseReceived(
32 const std::string& presentation_id,
33 const std::vector<MediaRouteResponseCallback>& callbacks,
34 interfaces::MediaRoutePtr media_route,
35 const mojo::String& error_text) {
36 scoped_ptr<MediaRoute> route;
37 std::string actual_presentation_id;
38 std::string error;
39 if (media_route.is_null()) {
40 // An error occurred.
41 DCHECK(!error_text.is_null());
42 error = !error_text.get().empty() ? error_text.get() : "Unknown error.";
43 } else {
44 route = media_route.To<scoped_ptr<MediaRoute>>();
45 actual_presentation_id = presentation_id;
48 for (const MediaRouteResponseCallback& callback : callbacks)
49 callback.Run(route.get(), actual_presentation_id, error);
52 // TODO(imcheng): We should handle failure in this case. One way is to invoke
53 // all pending requests with failure. (crbug.com/490787)
54 void EventPageWakeComplete(bool success) {
55 if (!success)
56 LOG(ERROR) << "An error encountered while waking the event page.";
59 scoped_ptr<content::PresentationSessionMessage>
60 ConvertToPresentationSessionMessage(interfaces::RouteMessagePtr input) {
61 DCHECK(!input.is_null());
62 scoped_ptr<content::PresentationSessionMessage> output;
63 switch (input->type) {
64 case interfaces::RouteMessage::Type::TYPE_TEXT: {
65 DCHECK(!input->message.is_null());
66 DCHECK(input->data.is_null());
67 output.reset(new content::PresentationSessionMessage(
68 content::PresentationMessageType::TEXT));
69 input->message.Swap(&output->message);
70 return output.Pass();
72 case interfaces::RouteMessage::Type::TYPE_BINARY: {
73 DCHECK(!input->data.is_null());
74 DCHECK(input->message.is_null());
75 output.reset(new content::PresentationSessionMessage(
76 content::PresentationMessageType::ARRAY_BUFFER));
77 output->data.reset(new std::vector<uint8_t>);
78 input->data.Swap(output->data.get());
79 return output.Pass();
83 NOTREACHED() << "Invalid route message type " << input->type;
84 return output.Pass();
87 } // namespace
89 MediaRouterMojoImpl::MediaRouterMojoImpl(
90 extensions::EventPageTracker* event_page_tracker)
91 : event_page_tracker_(event_page_tracker),
92 instance_id_(base::GenerateGUID()) {
93 DCHECK(event_page_tracker_);
96 MediaRouterMojoImpl::~MediaRouterMojoImpl() {
97 DCHECK(thread_checker_.CalledOnValidThread());
100 // static
101 void MediaRouterMojoImpl::BindToRequest(
102 const std::string& extension_id,
103 content::BrowserContext* context,
104 mojo::InterfaceRequest<interfaces::MediaRouter> request) {
105 MediaRouterMojoImpl* impl = static_cast<MediaRouterMojoImpl*>(
106 MediaRouterFactory::GetApiForBrowserContext(context));
107 DCHECK(impl);
109 impl->BindToMojoRequest(request.Pass(), extension_id);
112 void MediaRouterMojoImpl::BindToMojoRequest(
113 mojo::InterfaceRequest<interfaces::MediaRouter> request,
114 const std::string& extension_id) {
115 DCHECK(thread_checker_.CalledOnValidThread());
117 binding_.reset(
118 new mojo::Binding<interfaces::MediaRouter>(this, request.Pass()));
119 binding_->set_connection_error_handler(base::Bind(
120 &MediaRouterMojoImpl::OnConnectionError, base::Unretained(this)));
122 media_route_provider_extension_id_ = extension_id;
125 // TODO(imcheng): If this occurs while there are pending requests, we should
126 // probably invoke them with failure. (crbug.com/490787)
127 void MediaRouterMojoImpl::OnConnectionError() {
128 DCHECK(thread_checker_.CalledOnValidThread());
130 media_route_provider_.reset();
131 binding_.reset();
134 void MediaRouterMojoImpl::RegisterMediaRouteProvider(
135 interfaces::MediaRouteProviderPtr media_route_provider_ptr,
136 const interfaces::MediaRouter::RegisterMediaRouteProviderCallback&
137 callback) {
138 DCHECK(thread_checker_.CalledOnValidThread());
140 media_route_provider_ = media_route_provider_ptr.Pass();
141 media_route_provider_.set_connection_error_handler(base::Bind(
142 &MediaRouterMojoImpl::OnConnectionError, base::Unretained(this)));
143 callback.Run(instance_id_);
144 ExecutePendingRequests();
147 void MediaRouterMojoImpl::OnIssue(const interfaces::IssuePtr issue) {
148 DCHECK(thread_checker_.CalledOnValidThread());
149 DVLOG_WITH_INSTANCE(1) << "OnIssue " << issue->title;
150 const Issue& issue_converted = issue.To<Issue>();
151 issue_manager_.AddIssue(issue_converted);
154 void MediaRouterMojoImpl::OnSinksReceived(
155 const mojo::String& media_source,
156 mojo::Array<interfaces::MediaSinkPtr> sinks) {
157 DCHECK(thread_checker_.CalledOnValidThread());
159 DVLOG_WITH_INSTANCE(1) << "OnSinksReceived";
160 std::vector<MediaSink> sinks_converted;
161 sinks_converted.reserve(sinks.size());
163 for (size_t i = 0; i < sinks.size(); ++i) {
164 sinks_converted.push_back(sinks[i].To<MediaSink>());
167 auto it = sinks_observers_.find(media_source);
168 if (it == sinks_observers_.end()) {
169 DVLOG_WITH_INSTANCE(1)
170 << "Received sink list without any active observers: " << media_source;
171 } else {
172 FOR_EACH_OBSERVER(MediaSinksObserver, *it->second,
173 OnSinksReceived(sinks_converted));
177 void MediaRouterMojoImpl::OnRoutesUpdated(
178 mojo::Array<interfaces::MediaRoutePtr> routes) {
179 DCHECK(thread_checker_.CalledOnValidThread());
181 DVLOG_WITH_INSTANCE(1) << "OnRoutesUpdated";
183 std::vector<MediaRoute> routes_converted;
184 routes_converted.reserve(routes.size());
186 for (size_t i = 0; i < routes.size(); ++i) {
187 routes_converted.push_back(routes[i].To<MediaRoute>());
190 FOR_EACH_OBSERVER(MediaRoutesObserver, routes_observers_,
191 OnRoutesUpdated(routes_converted));
194 void MediaRouterMojoImpl::CreateRoute(
195 const MediaSource::Id& source_id,
196 const MediaSink::Id& sink_id,
197 const GURL& origin,
198 int tab_id,
199 const std::vector<MediaRouteResponseCallback>& callbacks) {
200 DCHECK(thread_checker_.CalledOnValidThread());
202 if (!origin.is_valid()) {
203 DVLOG_WITH_INSTANCE(1) << "Invalid origin: " << origin;
204 for (const MediaRouteResponseCallback& callback : callbacks)
205 callback.Run(nullptr, "", "Invalid origin");
206 return;
208 RunOrDefer(base::Bind(
209 &MediaRouterMojoImpl::DoCreateRoute, base::Unretained(this), source_id,
210 sink_id, origin.is_empty() ? "" : origin.spec(), tab_id, callbacks));
213 void MediaRouterMojoImpl::JoinRoute(
214 const MediaSource::Id& source_id,
215 const std::string& presentation_id,
216 const GURL& origin,
217 int tab_id,
218 const std::vector<MediaRouteResponseCallback>& callbacks) {
219 DCHECK(thread_checker_.CalledOnValidThread());
221 if (!origin.is_valid()) {
222 DVLOG_WITH_INSTANCE(1) << "Invalid origin: " << origin;
223 for (const MediaRouteResponseCallback& callback : callbacks)
224 callback.Run(nullptr, "", "Invalid origin");
225 return;
227 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoJoinRoute,
228 base::Unretained(this), source_id, presentation_id,
229 origin.is_empty() ? "" : origin.spec(), tab_id,
230 callbacks));
233 void MediaRouterMojoImpl::CloseRoute(const MediaRoute::Id& route_id) {
234 DCHECK(thread_checker_.CalledOnValidThread());
236 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoCloseRoute,
237 base::Unretained(this), route_id));
240 void MediaRouterMojoImpl::SendRouteMessage(
241 const MediaRoute::Id& route_id,
242 const std::string& message,
243 const SendRouteMessageCallback& callback) {
244 DCHECK(thread_checker_.CalledOnValidThread());
246 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoSendSessionMessage,
247 base::Unretained(this), route_id, message, callback));
250 void MediaRouterMojoImpl::SendRouteBinaryMessage(
251 const MediaRoute::Id& route_id,
252 scoped_ptr<std::vector<uint8>> data,
253 const SendRouteMessageCallback& callback) {
254 DCHECK(thread_checker_.CalledOnValidThread());
256 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoSendSessionBinaryMessage,
257 base::Unretained(this), route_id,
258 base::Passed(data.Pass()), callback));
261 void MediaRouterMojoImpl::AddIssue(const Issue& issue) {
262 DCHECK(thread_checker_.CalledOnValidThread());
263 issue_manager_.AddIssue(issue);
266 void MediaRouterMojoImpl::ClearIssue(const Issue::Id& issue_id) {
267 DCHECK(thread_checker_.CalledOnValidThread());
268 issue_manager_.ClearIssue(issue_id);
271 void MediaRouterMojoImpl::OnPresentationSessionDetached(
272 const MediaRoute::Id& route_id) {
273 DCHECK(thread_checker_.CalledOnValidThread());
274 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoOnPresentationSessionDetached,
275 base::Unretained(this), route_id));
278 void MediaRouterMojoImpl::RegisterMediaSinksObserver(
279 MediaSinksObserver* observer) {
280 DCHECK(thread_checker_.CalledOnValidThread());
282 // Create an observer list for the media source and add |observer|
283 // to it. Fail if |observer| is already registered.
284 const std::string& source_id = observer->source().id();
285 base::ObserverList<MediaSinksObserver>* observer_list =
286 sinks_observers_.get(source_id);
287 if (!observer_list) {
288 observer_list = new base::ObserverList<MediaSinksObserver>;
289 sinks_observers_.add(source_id, make_scoped_ptr(observer_list));
290 } else {
291 DCHECK(!observer_list->HasObserver(observer));
294 // We need to call DoStartObservingMediaSinks every time an observer is
295 // added to ensure the observer will be notified with a fresh set of results.
296 // TODO(imcheng): Implement caching. (crbug.com/492451)
297 observer_list->AddObserver(observer);
298 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStartObservingMediaSinks,
299 base::Unretained(this), source_id));
302 void MediaRouterMojoImpl::UnregisterMediaSinksObserver(
303 MediaSinksObserver* observer) {
304 DCHECK(thread_checker_.CalledOnValidThread());
306 const MediaSource::Id& source_id = observer->source().id();
307 auto* observer_list = sinks_observers_.get(source_id);
308 if (!observer_list || !observer_list->HasObserver(observer)) {
309 return;
312 // If we are removing the final observer for the source, then stop
313 // observing sinks for it.
314 // might_have_observers() is reliable here on the assumption that this call
315 // is not inside the ObserverList iteration.
316 observer_list->RemoveObserver(observer);
317 if (!observer_list->might_have_observers()) {
318 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStopObservingMediaSinks,
319 base::Unretained(this), source_id));
320 sinks_observers_.erase(source_id);
324 void MediaRouterMojoImpl::RegisterMediaRoutesObserver(
325 MediaRoutesObserver* observer) {
326 DCHECK(thread_checker_.CalledOnValidThread());
327 DCHECK(!routes_observers_.HasObserver(observer));
329 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStartObservingMediaRoutes,
330 base::Unretained(this)));
331 routes_observers_.AddObserver(observer);
334 void MediaRouterMojoImpl::UnregisterMediaRoutesObserver(
335 MediaRoutesObserver* observer) {
336 if (!routes_observers_.HasObserver(observer))
337 return;
339 routes_observers_.RemoveObserver(observer);
340 if (!routes_observers_.might_have_observers()) {
341 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStopObservingMediaRoutes,
342 base::Unretained(this)));
346 void MediaRouterMojoImpl::RegisterIssuesObserver(IssuesObserver* observer) {
347 DCHECK(thread_checker_.CalledOnValidThread());
348 issue_manager_.RegisterObserver(observer);
351 void MediaRouterMojoImpl::UnregisterIssuesObserver(IssuesObserver* observer) {
352 DCHECK(thread_checker_.CalledOnValidThread());
353 issue_manager_.UnregisterObserver(observer);
356 void MediaRouterMojoImpl::RegisterPresentationSessionMessagesObserver(
357 PresentationSessionMessagesObserver* observer) {
358 DCHECK(thread_checker_.CalledOnValidThread());
359 DCHECK(observer);
360 const MediaRoute::Id& route_id = observer->route_id();
361 auto* observer_list = messages_observers_.get(route_id);
362 if (!observer_list) {
363 observer_list = new base::ObserverList<PresentationSessionMessagesObserver>;
364 messages_observers_.add(route_id, make_scoped_ptr(observer_list));
365 } else {
366 DCHECK(!observer_list->HasObserver(observer));
369 bool should_listen = !observer_list->might_have_observers();
370 observer_list->AddObserver(observer);
371 if (should_listen) {
372 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoListenForRouteMessages,
373 base::Unretained(this), route_id));
377 void MediaRouterMojoImpl::UnregisterPresentationSessionMessagesObserver(
378 PresentationSessionMessagesObserver* observer) {
379 DCHECK(thread_checker_.CalledOnValidThread());
380 DCHECK(observer);
382 const MediaRoute::Id& route_id = observer->route_id();
383 auto* observer_list = messages_observers_.get(route_id);
384 if (!observer_list || !observer_list->HasObserver(observer))
385 return;
387 observer_list->RemoveObserver(observer);
388 if (!observer_list->might_have_observers()) {
389 messages_observers_.erase(route_id);
390 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStopListeningForRouteMessages,
391 base::Unretained(this), route_id));
395 void MediaRouterMojoImpl::DoCreateRoute(
396 const MediaSource::Id& source_id,
397 const MediaSink::Id& sink_id,
398 const std::string& origin,
399 int tab_id,
400 const std::vector<MediaRouteResponseCallback>& callbacks) {
401 std::string presentation_id("mr_");
402 presentation_id += base::GenerateGUID();
403 DVLOG_WITH_INSTANCE(1) << "DoCreateRoute " << source_id << "=>" << sink_id
404 << ", presentation ID: " << presentation_id;
405 media_route_provider_->CreateRoute(
406 source_id, sink_id, presentation_id, origin, tab_id,
407 base::Bind(&RouteResponseReceived, presentation_id, callbacks));
410 void MediaRouterMojoImpl::DoJoinRoute(
411 const MediaSource::Id& source_id,
412 const std::string& presentation_id,
413 const std::string& origin,
414 int tab_id,
415 const std::vector<MediaRouteResponseCallback>& callbacks) {
416 DVLOG_WITH_INSTANCE(1) << "DoJoinRoute " << source_id
417 << ", presentation ID: " << presentation_id;
418 media_route_provider_->JoinRoute(
419 source_id, presentation_id, origin, tab_id,
420 base::Bind(&RouteResponseReceived, presentation_id, callbacks));
423 void MediaRouterMojoImpl::DoCloseRoute(const MediaRoute::Id& route_id) {
424 DVLOG_WITH_INSTANCE(1) << "DoCloseRoute " << route_id;
425 media_route_provider_->CloseRoute(route_id);
428 void MediaRouterMojoImpl::DoSendSessionMessage(
429 const MediaRoute::Id& route_id,
430 const std::string& message,
431 const SendRouteMessageCallback& callback) {
432 DVLOG_WITH_INSTANCE(1) << "SendRouteMessage " << route_id;
433 media_route_provider_->SendRouteMessage(route_id, message, callback);
436 void MediaRouterMojoImpl::DoSendSessionBinaryMessage(
437 const MediaRoute::Id& route_id,
438 scoped_ptr<std::vector<uint8>> data,
439 const SendRouteMessageCallback& callback) {
440 DVLOG_WITH_INSTANCE(1) << "SendRouteBinaryMessage " << route_id;
441 mojo::Array<uint8> mojo_array;
442 mojo_array.Swap(data.get());
443 media_route_provider_->SendRouteBinaryMessage(route_id, mojo_array.Pass(),
444 callback);
447 void MediaRouterMojoImpl::DoListenForRouteMessages(
448 const MediaRoute::Id& route_id) {
449 DVLOG_WITH_INSTANCE(1) << "ListenForRouteMessages";
450 if (!ContainsValue(route_ids_listening_for_messages_, route_id)) {
451 route_ids_listening_for_messages_.insert(route_id);
452 media_route_provider_->ListenForRouteMessages(
453 route_id, base::Bind(&MediaRouterMojoImpl::OnRouteMessagesReceived,
454 base::Unretained(this), route_id));
458 void MediaRouterMojoImpl::DoStopListeningForRouteMessages(
459 const MediaRoute::Id& route_id) {
460 DVLOG_WITH_INSTANCE(1) << "StopListeningForRouteMessages";
462 // No need to erase |route_ids_listening_for_messages_| entry here.
463 // It will be removed when there are no more observers by the time
464 // |OnRouteMessagesReceived| is invoked.
465 media_route_provider_->StopListeningForRouteMessages(route_id);
468 void MediaRouterMojoImpl::OnRouteMessagesReceived(
469 const MediaRoute::Id& route_id,
470 mojo::Array<interfaces::RouteMessagePtr> messages,
471 bool error) {
472 DVLOG(1) << "OnRouteMessagesReceived";
474 // If |messages| is null, then no more messages will come from this route.
475 // We can stop listening.
476 if (error) {
477 DVLOG(2) << "Encountered error in OnRouteMessagesReceived for " << route_id;
478 route_ids_listening_for_messages_.erase(route_id);
479 return;
482 // Check if there are any observers remaining. If not, the messages
483 // can be discarded and we can stop listening for the next batch of messages.
484 auto* observer_list = messages_observers_.get(route_id);
485 if (!observer_list) {
486 route_ids_listening_for_messages_.erase(route_id);
487 return;
490 // If |messages| is empty, then |StopListeningForRouteMessages| was invoked
491 // but we have added back an observer since. Keep listening for more messages,
492 // but do not notify observers with empty list.
493 if (!messages.storage().empty()) {
494 ScopedVector<content::PresentationSessionMessage> session_messages;
495 session_messages.reserve(messages.size());
496 for (size_t i = 0; i < messages.size(); ++i) {
497 session_messages.push_back(
498 ConvertToPresentationSessionMessage(messages[i].Pass()).Pass());
500 base::ObserverList<PresentationSessionMessagesObserver>::Iterator
501 observer_it(observer_list);
502 bool single_observer =
503 observer_it.GetNext() != nullptr && observer_it.GetNext() == nullptr;
504 FOR_EACH_OBSERVER(PresentationSessionMessagesObserver, *observer_list,
505 OnMessagesReceived(session_messages, single_observer));
508 // Listen for more messages.
509 media_route_provider_->ListenForRouteMessages(
510 route_id, base::Bind(&MediaRouterMojoImpl::OnRouteMessagesReceived,
511 base::Unretained(this), route_id));
514 void MediaRouterMojoImpl::DoOnPresentationSessionDetached(
515 const MediaRoute::Id& route_id) {
516 DVLOG_WITH_INSTANCE(1) << "DoOnPresentationSessionDetached " << route_id;
517 media_route_provider_->OnPresentationSessionDetached(route_id);
520 void MediaRouterMojoImpl::DoStartObservingMediaSinks(
521 const MediaSource::Id& source_id) {
522 DVLOG_WITH_INSTANCE(1) << "DoStartObservingMediaSinks: " << source_id;
523 media_route_provider_->StartObservingMediaSinks(source_id);
526 void MediaRouterMojoImpl::DoStopObservingMediaSinks(
527 const MediaSource::Id& source_id) {
528 DVLOG_WITH_INSTANCE(1) << "DoStopObservingMediaSinks: " << source_id;
529 media_route_provider_->StopObservingMediaSinks(source_id);
532 void MediaRouterMojoImpl::DoStartObservingMediaRoutes() {
533 DVLOG_WITH_INSTANCE(1) << "DoStartObservingMediaRoutes";
534 media_route_provider_->StartObservingMediaRoutes();
537 void MediaRouterMojoImpl::DoStopObservingMediaRoutes() {
538 DVLOG_WITH_INSTANCE(1) << "DoStopObservingMediaRoutes";
539 media_route_provider_->StopObservingMediaRoutes();
542 void MediaRouterMojoImpl::EnqueueTask(const base::Closure& closure) {
543 pending_requests_.push_back(closure);
544 DVLOG_WITH_INSTANCE(2) << "EnqueueTask (queue-length="
545 << pending_requests_.size() << ")";
548 void MediaRouterMojoImpl::RunOrDefer(const base::Closure& request) {
549 DCHECK(event_page_tracker_);
551 if (media_route_provider_extension_id_.empty()) {
552 DVLOG_WITH_INSTANCE(1) << "Extension ID not known yet.";
553 EnqueueTask(request);
554 } else if (event_page_tracker_->IsEventPageSuspended(
555 media_route_provider_extension_id_)) {
556 DVLOG_WITH_INSTANCE(1) << "Waking event page.";
557 EnqueueTask(request);
558 if (!event_page_tracker_->WakeEventPage(
559 media_route_provider_extension_id_,
560 base::Bind(&EventPageWakeComplete))) {
561 LOG(ERROR) << "An error encountered while waking the event page.";
563 media_route_provider_.reset();
564 } else if (!media_route_provider_) {
565 DVLOG_WITH_INSTANCE(1) << "Extension is awake, awaiting ProvideMediaRouter "
566 " to be called.";
567 EnqueueTask(request);
568 } else {
569 request.Run();
573 void MediaRouterMojoImpl::ExecutePendingRequests() {
574 DCHECK(thread_checker_.CalledOnValidThread());
575 DCHECK(media_route_provider_);
576 DCHECK(event_page_tracker_);
577 DCHECK(!media_route_provider_extension_id_.empty());
579 if (event_page_tracker_->IsEventPageSuspended(
580 media_route_provider_extension_id_)) {
581 DVLOG_WITH_INSTANCE(1)
582 << "ExecutePendingRequests was called while extension is suspended.";
583 return;
586 for (const auto& next_request : pending_requests_)
587 next_request.Run();
589 pending_requests_.clear();
592 } // namespace media_router