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 #include "chrome/browser/media/router/media_router_mojo_impl.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/guid.h" | |
9 #include "base/logging.h" | |
10 #include "base/observer_list.h" | |
11 #include "chrome/browser/media/router/media_router_impl.h" | |
12 #include "chrome/browser/media/router/media_router_impl_factory.h" | |
13 #include "chrome/browser/media/router/media_router_mojo_impl_factory.h" | |
14 #include "chrome/browser/media/router/media_router_type_converters.h" | |
15 #include "chrome/browser/media/router/media_routes_observer.h" | |
16 #include "chrome/browser/media/router/media_sinks_observer.h" | |
17 #include "extensions/browser/process_manager.h" | |
18 | |
19 #define DVLOG_WITH_INSTANCE(level) \ | |
20 DVLOG(level) << "MR #" << instance_id_ << ": " | |
21 | |
22 #define DVLOG_WITH_INSTANCE(level) DLOG(level) << "MR #" << instance_id_ << ": " | |
23 | |
24 namespace media_router { | |
25 namespace { | |
26 | |
27 // Converts the callback result of calling Mojo CreateRoute() into a local | |
28 // callback. | |
29 void CreateRouteFinished(const MediaSinkId& sink_id, | |
30 const MediaRouteResponseCallback& callback, | |
31 interfaces::MediaRoutePtr media_route, | |
32 const mojo::String& error_text) { | |
33 if (media_route.is_null()) { | |
34 // An error occurred. | |
35 DCHECK(!error_text.is_null()); | |
36 callback.Run(nullptr, !error_text.get().empty() ? error_text.get() | |
37 : "Unknown error."); | |
38 return; | |
39 } | |
40 callback.Run(make_scoped_ptr(new MediaRoute(media_route.To<MediaRoute>())), | |
41 ""); | |
42 } | |
43 | |
44 void EventPageWakeComplete(bool success) { | |
45 if (!success) | |
46 LOG(ERROR) << "An error encountered while waking the event page."; | |
47 } | |
48 | |
49 } // namespace | |
50 | |
51 MediaRouterMojoImpl::MediaRouterMojoImpl() | |
52 : event_page_tracker_(nullptr), instance_id_(base::GenerateGUID()) { | |
53 } | |
54 | |
55 MediaRouterMojoImpl::MediaRouterMojoImpl( | |
56 const std::string& mrpm_extension_id, | |
57 extensions::EventPageTracker* event_page_tracker_for_test) | |
58 : MediaRouterMojoImpl() { | |
59 DCHECK(!mrpm_extension_id.empty()); | |
60 DCHECK(event_page_tracker_for_test); | |
61 mrpm_extension_id_ = mrpm_extension_id; | |
62 event_page_tracker_ = event_page_tracker_for_test; | |
63 } | |
64 | |
65 MediaRouterMojoImpl::~MediaRouterMojoImpl() { | |
66 DCHECK(thread_checker_.CalledOnValidThread()); | |
67 } | |
68 | |
69 // static | |
70 void MediaRouterMojoImpl::BindToRequest( | |
71 const std::string& extension_id, | |
72 content::BrowserContext* context, | |
73 mojo::InterfaceRequest<interfaces::MediaRouterObserver> request) { | |
74 MediaRouterMojoImpl* impl = | |
75 MediaRouterMojoImplFactory::GetApiForBrowserContext(context); | |
76 DCHECK(impl); | |
77 impl->Bind(request.Pass()); | |
78 impl->MonitorExtension(extension_id, context); | |
79 } | |
80 | |
81 void MediaRouterMojoImpl::Bind( | |
82 mojo::InterfaceRequest<interfaces::MediaRouterObserver> request) { | |
83 DCHECK(thread_checker_.CalledOnValidThread()); | |
84 binding_.reset( | |
85 new mojo::Binding<interfaces::MediaRouterObserver>(this, request.Pass())); | |
86 binding_->set_error_handler(this); | |
87 } | |
88 | |
89 void MediaRouterMojoImpl::OnConnectionError() { | |
90 DCHECK(thread_checker_.CalledOnValidThread()); | |
91 mojo_media_router_.reset(); | |
92 binding_.reset(); | |
93 } | |
94 | |
95 void MediaRouterMojoImpl::ProvideMediaRouter( | |
96 interfaces::MediaRouterPtr media_router_ptr, | |
97 const interfaces::MediaRouterObserver::ProvideMediaRouterCallback& | |
98 callback) { | |
99 DCHECK(thread_checker_.CalledOnValidThread()); | |
100 mojo_media_router_ = media_router_ptr.Pass(); | |
101 mojo_media_router_.set_error_handler(this); | |
102 callback.Run(instance_id_); | |
103 ExecutePendingRequests(); | |
imcheng (use chromium acct)
2015/05/14 21:30:13
Consider inlining this since there's only 1 caller
Kevin M
2015/05/14 22:46:35
I added an "inline" keyword. I think that organizi
| |
104 } | |
105 | |
106 void MediaRouterMojoImpl::OnMessage(const mojo::String& route_id, | |
107 const mojo::String& message) { | |
108 DCHECK(thread_checker_.CalledOnValidThread()); | |
109 NOTIMPLEMENTED(); | |
110 } | |
111 | |
112 void MediaRouterMojoImpl::OnIssue(const interfaces::IssuePtr issue) { | |
113 DCHECK(thread_checker_.CalledOnValidThread()); | |
114 NOTIMPLEMENTED(); | |
115 } | |
116 | |
117 void MediaRouterMojoImpl::OnSinksReceived( | |
118 const mojo::String& media_source, | |
119 mojo::Array<interfaces::MediaSinkPtr> sinks) { | |
120 DCHECK(thread_checker_.CalledOnValidThread()); | |
121 DVLOG_WITH_INSTANCE(1) << "OnSinksReceived"; | |
122 std::vector<MediaSink> sinks_converted; | |
123 sinks_converted.reserve(sinks.size()); | |
124 | |
125 for (size_t i = 0; i < sinks.size(); ++i) { | |
126 sinks_converted.push_back(sinks[i].To<MediaSink>()); | |
127 } | |
128 | |
129 auto it = sinks_observers_.find(media_source); | |
130 if (it == sinks_observers_.end()) { | |
131 DVLOG_WITH_INSTANCE(1) | |
132 << "Received sink list without any active observers: " << media_source; | |
133 } else { | |
134 FOR_EACH_OBSERVER(MediaSinksObserver, *it->second, | |
135 OnSinksReceived(sinks_converted)); | |
136 } | |
137 } | |
138 | |
139 void MediaRouterMojoImpl::OnRoutesUpdated( | |
140 mojo::Array<interfaces::MediaRoutePtr> routes) { | |
141 DCHECK(thread_checker_.CalledOnValidThread()); | |
142 DVLOG_WITH_INSTANCE(1) << "OnRoutesUpdated"; | |
143 | |
144 std::vector<MediaRoute> routes_converted; | |
145 routes_converted.reserve(routes.size()); | |
146 | |
147 for (size_t i = 0; i < routes.size(); ++i) { | |
148 routes_converted.push_back(routes[i].To<MediaRoute>()); | |
149 } | |
150 | |
151 FOR_EACH_OBSERVER(MediaRoutesObserver, routes_observers_, | |
152 OnRoutesUpdated(routes_converted)); | |
153 } | |
154 | |
155 // ---------------------------------------------------------------------------- | |
156 // MediaRouter methods. | |
157 | |
158 void MediaRouterMojoImpl::CreateRoute( | |
159 const MediaSourceId& source_id, | |
160 const MediaSinkId& sink_id, | |
161 const MediaRouteResponseCallback& callback) { | |
162 DCHECK(thread_checker_.CalledOnValidThread()); | |
163 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoCreateRoute, | |
164 base::Unretained(this), source_id, sink_id, callback)); | |
165 } | |
166 | |
167 void MediaRouterMojoImpl::CloseRoute(const MediaRouteId& route_id) { | |
168 DCHECK(thread_checker_.CalledOnValidThread()); | |
169 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoCloseRoute, | |
170 base::Unretained(this), route_id)); | |
171 } | |
172 | |
173 void MediaRouterMojoImpl::PostMessage(const MediaRouteId& route_id, | |
174 const std::string& message) { | |
175 DCHECK(thread_checker_.CalledOnValidThread()); | |
176 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoPostMessage, | |
177 base::Unretained(this), route_id, message)); | |
178 } | |
179 | |
180 void MediaRouterMojoImpl::ClearIssue(const Issue::IssueId& issue_id) { | |
181 DCHECK(thread_checker_.CalledOnValidThread()); | |
182 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoClearIssue, | |
183 base::Unretained(this), issue_id)); | |
184 } | |
185 | |
186 bool MediaRouterMojoImpl::RegisterMediaSinksObserver( | |
187 MediaSinksObserver* observer) { | |
188 DCHECK(thread_checker_.CalledOnValidThread()); | |
189 | |
190 // Create an observer list for the media source and add |observer| | |
191 // to it. Fail if |observer| is already registered. | |
192 const std::string& source_id = observer->source().id(); | |
193 linked_ptr<ObserverList<MediaSinksObserver>> observer_list = | |
194 sinks_observers_[source_id]; | |
195 if (!observer_list.get()) { | |
196 observer_list = make_linked_ptr(new ObserverList<MediaSinksObserver>); | |
197 sinks_observers_[source_id] = observer_list; | |
198 } else { | |
199 if (observer_list->HasObserver(observer)) { | |
200 DLOG_WITH_INSTANCE(FATAL) | |
201 << "Redundant RegisterMediaSinksObserver call detected."; | |
202 return true; | |
imcheng (use chromium acct)
2015/05/14 21:30:13
return false instead?
Kevin M
2015/05/14 22:46:35
Done
| |
203 } | |
204 } | |
205 | |
206 // Turn on sink observation if this is the first time we've started observing | |
207 // sinks for the media source. | |
208 if (!observer_list->might_have_observers()) { | |
imcheng (use chromium acct)
2015/05/14 21:30:13
Because we don't have caching anymore, we should a
Kevin M
2015/05/14 22:46:35
Done.
| |
209 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStartObservingMediaSinks, | |
210 base::Unretained(this), source_id)); | |
211 } | |
212 observer_list->AddObserver(observer); | |
213 | |
214 return true; | |
215 } | |
216 | |
217 void MediaRouterMojoImpl::UnregisterMediaSinksObserver( | |
218 MediaSinksObserver* observer) { | |
219 DCHECK(thread_checker_.CalledOnValidThread()); | |
220 | |
221 const std::string& source_id = observer->source().id(); | |
222 const auto observer_list_it = sinks_observers_.find(source_id); | |
223 if (observer_list_it == sinks_observers_.end() || | |
224 !observer_list_it->second->HasObserver(observer)) { | |
225 return; | |
226 } | |
227 observer_list_it->second->RemoveObserver(observer); | |
228 | |
229 // If we are removing the final observer for the source, then stop | |
230 // observing sinks for it. | |
231 if (!observer_list_it->second->might_have_observers()) { | |
232 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStopObservingMediaSinks, | |
233 base::Unretained(this), source_id)); | |
234 sinks_observers_.erase(observer_list_it); | |
235 } | |
236 } | |
237 | |
238 bool MediaRouterMojoImpl::RegisterMediaRoutesObserver( | |
239 MediaRoutesObserver* observer) { | |
240 DCHECK(thread_checker_.CalledOnValidThread()); | |
241 if (routes_observers_.HasObserver(observer)) { | |
242 DLOG(FATAL) << "Redundant RegisterMediaRoutesObserver() call detected."; | |
243 return true; | |
244 } | |
245 | |
246 // TODO(kmarshall): add result caching and max limits. | |
imcheng (use chromium acct)
2015/05/14 21:30:13
ditto on caching
Kevin M
2015/05/14 22:46:35
Done.
| |
247 if (!routes_observers_.might_have_observers()) { | |
248 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStartObservingMediaRoutes, | |
249 base::Unretained(this))); | |
250 } | |
251 routes_observers_.AddObserver(observer); | |
252 return true; | |
253 } | |
254 | |
255 void MediaRouterMojoImpl::UnregisterMediaRoutesObserver( | |
256 MediaRoutesObserver* observer) { | |
257 if (!routes_observers_.HasObserver(observer)) { | |
imcheng (use chromium acct)
2015/05/14 21:30:13
curly brace not needed for a single line
Kevin M
2015/05/14 22:46:35
Done.
| |
258 return; | |
259 } | |
260 routes_observers_.RemoveObserver(observer); | |
261 if (!routes_observers_.might_have_observers()) { | |
262 RunOrDefer(base::Bind(&MediaRouterMojoImpl::DoStopObservingMediaRoutes, | |
263 base::Unretained(this))); | |
264 } | |
265 } | |
266 | |
267 void MediaRouterMojoImpl::AddIssuesObserver(IssuesObserver* observer) { | |
268 NOTIMPLEMENTED(); | |
269 } | |
270 | |
271 void MediaRouterMojoImpl::RemoveIssuesObserver(IssuesObserver* observer) { | |
272 NOTIMPLEMENTED(); | |
273 } | |
274 | |
275 void MediaRouterMojoImpl::DoCreateRoute( | |
276 const MediaSourceId& source_id, | |
277 const MediaSinkId& sink_id, | |
278 const MediaRouteResponseCallback& callback) { | |
279 DCHECK(thread_checker_.CalledOnValidThread()); | |
280 DCHECK(mojo_media_router_); | |
281 DVLOG_WITH_INSTANCE(1) << "CreateRoute " << source_id << "=>" << sink_id; | |
282 mojo_media_router_->CreateRoute( | |
283 source_id, sink_id, base::Bind(&CreateRouteFinished, sink_id, callback)); | |
284 } | |
285 | |
286 void MediaRouterMojoImpl::DoCloseRoute(const MediaRouteId& route_id) { | |
287 DCHECK(thread_checker_.CalledOnValidThread()); | |
288 DCHECK(mojo_media_router_); | |
289 DVLOG_WITH_INSTANCE(1) << "CloseRoute " << route_id; | |
290 mojo_media_router_->CloseRoute(route_id); | |
291 } | |
292 | |
293 void MediaRouterMojoImpl::DoPostMessage(const MediaRouteId& route_id, | |
294 const std::string& message) { | |
295 DCHECK(thread_checker_.CalledOnValidThread()); | |
296 DCHECK(mojo_media_router_); | |
297 DVLOG_WITH_INSTANCE(1) << "PostMessage " << route_id; | |
298 mojo_media_router_->PostMessage(route_id, message); | |
299 } | |
300 | |
301 void MediaRouterMojoImpl::DoClearIssue(const Issue::IssueId& issue_id) { | |
302 DCHECK(thread_checker_.CalledOnValidThread()); | |
303 DCHECK(mojo_media_router_); | |
304 DVLOG_WITH_INSTANCE(1) << "ClearIssue " << issue_id; | |
305 mojo_media_router_->ClearIssue(issue_id); | |
306 } | |
307 | |
308 void MediaRouterMojoImpl::DoStartObservingMediaSinks( | |
309 const std::string& source_id) { | |
310 DCHECK(thread_checker_.CalledOnValidThread()); | |
311 DCHECK(mojo_media_router_); | |
312 DVLOG_WITH_INSTANCE(1) << "StartObservingMediaSinks: " << source_id; | |
313 mojo_media_router_->StartObservingMediaSinks(source_id); | |
314 } | |
315 | |
316 void MediaRouterMojoImpl::DoStopObservingMediaSinks( | |
317 const std::string& source_id) { | |
318 DCHECK(thread_checker_.CalledOnValidThread()); | |
319 DCHECK(mojo_media_router_); | |
320 DVLOG_WITH_INSTANCE(1) << "StopObservingMediaSinks: " << source_id; | |
321 mojo_media_router_->StopObservingMediaSinks(source_id); | |
322 } | |
323 | |
324 void MediaRouterMojoImpl::DoStartObservingMediaRoutes() { | |
325 DCHECK(thread_checker_.CalledOnValidThread()); | |
326 DCHECK(mojo_media_router_); | |
327 DVLOG_WITH_INSTANCE(1) << "StartObservingMediaRoutes"; | |
328 mojo_media_router_->StartObservingMediaRoutes(); | |
329 } | |
330 | |
331 void MediaRouterMojoImpl::DoStopObservingMediaRoutes() { | |
332 DCHECK(thread_checker_.CalledOnValidThread()); | |
333 DCHECK(mojo_media_router_); | |
334 DVLOG_WITH_INSTANCE(1) << "StopObservingMediaRoutes"; | |
335 mojo_media_router_->StopObservingMediaRoutes(); | |
336 } | |
337 | |
338 void MediaRouterMojoImpl::EnqueueTask(const base::Closure& closure) { | |
339 DVLOG_WITH_INSTANCE(2) << "EnqueueTask (size=" << pending_requests_.size() | |
340 << ")"; | |
341 pending_requests_.push_back(closure); | |
342 } | |
343 | |
344 void MediaRouterMojoImpl::MonitorExtension(const std::string& extension_id, | |
345 content::BrowserContext* context) { | |
346 DCHECK(thread_checker_.CalledOnValidThread()); | |
347 DCHECK(context); | |
348 mrpm_extension_id_ = extension_id; | |
349 event_page_tracker_ = extensions::ProcessManager::Get(context); | |
350 } | |
351 | |
352 void MediaRouterMojoImpl::RunOrDefer(const base::Closure& request_cb) { | |
353 DCHECK(event_page_tracker_); | |
354 if (event_page_tracker_->IsEventPageSuspended(mrpm_extension_id_)) { | |
355 DVLOG_WITH_INSTANCE(1) << "Waking event page."; | |
356 pending_requests_.push_back(request_cb); | |
357 if (!event_page_tracker_->WakeEventPage( | |
358 mrpm_extension_id_, base::Bind(&EventPageWakeComplete))) { | |
359 LOG(ERROR) << "An error encountered while waking the event page."; | |
360 } | |
361 mojo_media_router_.reset(); | |
362 } else if (!mojo_media_router_) { | |
363 DVLOG_WITH_INSTANCE(1) << "Extension is awake, awaiting MRPM connection."; | |
364 pending_requests_.push_back(request_cb); | |
365 } else { | |
366 request_cb.Run(); | |
imcheng (use chromium acct)
2015/05/14 21:30:13
I wonder if we can add a DCHECK(mojo_media_router_
Kevin M
2015/05/14 22:46:35
No need to have a DCHECK on mojo_media_router_ bec
| |
367 } | |
368 } | |
369 | |
370 void MediaRouterMojoImpl::ExecutePendingRequests() { | |
371 DCHECK(thread_checker_.CalledOnValidThread()); | |
372 DCHECK(mojo_media_router_); | |
373 DCHECK(event_page_tracker_); | |
374 if (event_page_tracker_->IsEventPageSuspended(mrpm_extension_id_)) { | |
imcheng (use chromium acct)
2015/05/14 21:30:13
In what conditions will that happen? Should it be
Kevin M
2015/05/14 22:46:35
If the extension shuts down immediately after Prov
| |
375 DVLOG_WITH_INSTANCE(1) | |
376 << "ExecutePendingRequests was called while extension is suspended."; | |
377 return; | |
378 } | |
379 | |
380 for (const auto& next_request : pending_requests_) { | |
imcheng (use chromium acct)
2015/05/14 21:30:13
curly braces not needed
Kevin M
2015/05/14 22:46:35
Done.
| |
381 next_request.Run(); | |
382 } | |
383 pending_requests_.clear(); | |
384 } | |
385 | |
386 } // namespace media_router | |
OLD | NEW |