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