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