Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / media / android / router / media_router_android.cc
bloba89f961c716c0d30e5d303b0bd902175d134807a
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/android/router/media_router_android.h"
7 #include "base/android/jni_android.h"
8 #include "base/android/jni_string.h"
9 #include "base/guid.h"
10 #include "base/logging.h"
11 #include "base/memory/scoped_vector.h"
12 #include "base/strings/stringprintf.h"
13 #include "chrome/browser/media/router/media_routes_observer.h"
14 #include "chrome/browser/media/router/media_sinks_observer.h"
15 #include "chrome/browser/media/router/presentation_session_messages_observer.h"
16 #include "jni/ChromeMediaRouter_jni.h"
17 #include "url/gurl.h"
19 using base::android::ConvertUTF8ToJavaString;
20 using base::android::ConvertJavaStringToUTF8;
21 using base::android::ScopedJavaLocalRef;
22 using base::android::AttachCurrentThread;
24 namespace media_router {
26 MediaRouterAndroid::CreateMediaRouteRequest::CreateMediaRouteRequest(
27 const MediaSource& source,
28 const MediaSink& sink,
29 const std::string& presentation_id,
30 const std::vector<MediaRouteResponseCallback>& callbacks)
31 : media_source(source),
32 media_sink(sink),
33 presentation_id(presentation_id),
34 callbacks(callbacks) {}
35 MediaRouterAndroid::CreateMediaRouteRequest::~CreateMediaRouteRequest() {}
37 MediaRouterAndroid::MediaRouterAndroid(content::BrowserContext*) {
38 JNIEnv* env = base::android::AttachCurrentThread();
39 java_media_router_.Reset(Java_ChromeMediaRouter_create(
40 env,
41 reinterpret_cast<jlong>(this),
42 base::android::GetApplicationContext()));
45 MediaRouterAndroid::~MediaRouterAndroid() {
48 // static
49 bool MediaRouterAndroid::Register(JNIEnv* env) {
50 bool ret = RegisterNativesImpl(env);
51 DCHECK(g_ChromeMediaRouter_clazz);
52 return ret;
55 void MediaRouterAndroid::CreateRoute(
56 const MediaSource::Id& source_id,
57 const MediaSink::Id& sink_id,
58 const GURL& origin,
59 int tab_id,
60 const std::vector<MediaRouteResponseCallback>& callbacks) {
61 if (!origin.is_valid()) {
62 for (const MediaRouteResponseCallback& callback : callbacks)
63 callback.Run(nullptr, "", "Invalid origin");
64 return;
67 // TODO(avayvod): unify presentation id generation code between platforms.
68 // https://crbug.com/522239
69 std::string presentation_id("mr_");
70 presentation_id += base::GenerateGUID();
72 CreateMediaRouteRequest* request = new CreateMediaRouteRequest(
73 MediaSource(source_id),
74 MediaSink(sink_id, std::string(), MediaSink::GENERIC),
75 presentation_id,
76 callbacks);
77 int create_route_request_id = create_route_requests_.Add(request);
79 JNIEnv* env = base::android::AttachCurrentThread();
80 ScopedJavaLocalRef<jstring> jsource_id =
81 base::android::ConvertUTF8ToJavaString(env, source_id);
82 ScopedJavaLocalRef<jstring> jsink_id =
83 base::android::ConvertUTF8ToJavaString(env, sink_id);
84 ScopedJavaLocalRef<jstring> jpresentation_id =
85 base::android::ConvertUTF8ToJavaString(env, presentation_id);
87 Java_ChromeMediaRouter_createRoute(
88 env,
89 java_media_router_.obj(),
90 jsource_id.obj(),
91 jsink_id.obj(),
92 jpresentation_id.obj(),
93 create_route_request_id);
96 void MediaRouterAndroid::JoinRoute(
97 const MediaSource::Id& source,
98 const std::string& presentation_id,
99 const GURL& origin,
100 int tab_id,
101 const std::vector<MediaRouteResponseCallback>& callbacks) {
102 GURL source_url(source);
103 DCHECK(source_url.is_valid());
105 const MediaRoute* matching_route = nullptr;
106 for (const auto& route : active_routes_) {
107 GURL route_source_url(route.media_source().id());
108 DCHECK(route_source_url.is_valid());
110 if (route_source_url.scheme() != source_url.scheme() ||
111 route_source_url.username() != source_url.username() ||
112 route_source_url.password() != source_url.password() ||
113 route_source_url.host() != source_url.host() ||
114 route_source_url.port() != source_url.port() ||
115 route_source_url.path() != source_url.path() ||
116 route_source_url.query() != source_url.query()) {
117 // Allow the ref() to be different.
118 continue;
121 // Since ref() could be different, use the existing route's source id.
122 const MediaSink::Id& sink_id = route.media_sink_id();
123 const std::string& potential_route_id = base::StringPrintf(
124 "route:%s/%s/%s",
125 presentation_id.c_str(),
126 sink_id.c_str(),
127 route.media_source().id().c_str());
128 if (potential_route_id == route.media_route_id()) {
129 matching_route = &route;
130 break;
134 if (!matching_route) {
135 for (const auto& callback : callbacks)
136 callback.Run(nullptr, std::string(), "No routes found");
137 return;
140 for (const auto& callback : callbacks)
141 callback.Run(matching_route, presentation_id, std::string());
144 void MediaRouterAndroid::CloseRoute(const MediaRoute::Id& route_id) {
145 JNIEnv* env = base::android::AttachCurrentThread();
146 ScopedJavaLocalRef<jstring> jroute_id =
147 base::android::ConvertUTF8ToJavaString(env, route_id);
148 Java_ChromeMediaRouter_closeRoute(
149 env, java_media_router_.obj(), jroute_id.obj());
152 void MediaRouterAndroid::SendRouteMessage(
153 const MediaRoute::Id& route_id,
154 const std::string& message,
155 const SendRouteMessageCallback& callback) {
156 int callback_id = message_callbacks_.Add(
157 new SendRouteMessageCallback(callback));
158 JNIEnv* env = base::android::AttachCurrentThread();
159 ScopedJavaLocalRef<jstring> jroute_id =
160 base::android::ConvertUTF8ToJavaString(env, route_id);
161 ScopedJavaLocalRef<jstring> jmessage =
162 base::android::ConvertUTF8ToJavaString(env, message);
163 Java_ChromeMediaRouter_sendStringMessage(
164 env,
165 java_media_router_.obj(),
166 jroute_id.obj(),
167 jmessage.obj(),
168 callback_id);
171 void MediaRouterAndroid::SendRouteBinaryMessage(
172 const MediaRoute::Id& route_id,
173 scoped_ptr<std::vector<uint8>> data,
174 const SendRouteMessageCallback& callback) {
175 NOTIMPLEMENTED();
178 void MediaRouterAndroid::AddIssue(const Issue& issue) {
179 NOTIMPLEMENTED();
182 void MediaRouterAndroid::ClearIssue(const Issue::Id& issue_id) {
183 NOTIMPLEMENTED();
186 void MediaRouterAndroid::OnPresentationSessionDetached(
187 const MediaRoute::Id& route_id) {
188 NOTIMPLEMENTED();
191 void MediaRouterAndroid::RegisterMediaSinksObserver(
192 MediaSinksObserver* observer) {
193 const std::string& source_id = observer->source().id();
194 base::ObserverList<MediaSinksObserver>* observer_list =
195 sinks_observers_.get(source_id);
196 if (!observer_list) {
197 observer_list = new base::ObserverList<MediaSinksObserver>;
198 sinks_observers_.add(source_id, make_scoped_ptr(observer_list));
199 } else {
200 DCHECK(!observer_list->HasObserver(observer));
203 observer_list->AddObserver(observer);
204 JNIEnv* env = base::android::AttachCurrentThread();
205 ScopedJavaLocalRef<jstring> jsource_id =
206 base::android::ConvertUTF8ToJavaString(env, source_id);
207 Java_ChromeMediaRouter_startObservingMediaSinks(
208 env, java_media_router_.obj(), jsource_id.obj());
211 void MediaRouterAndroid::UnregisterMediaSinksObserver(
212 MediaSinksObserver* observer) {
213 const std::string& source_id = observer->source().id();
214 auto* observer_list = sinks_observers_.get(source_id);
215 if (!observer_list || !observer_list->HasObserver(observer))
216 return;
218 // If we are removing the final observer for the source, then stop
219 // observing sinks for it.
220 // might_have_observers() is reliable here on the assumption that this call
221 // is not inside the ObserverList iteration.
222 observer_list->RemoveObserver(observer);
223 if (!observer_list->might_have_observers()) {
224 sinks_observers_.erase(source_id);
225 JNIEnv* env = base::android::AttachCurrentThread();
226 ScopedJavaLocalRef<jstring> jsource_id =
227 base::android::ConvertUTF8ToJavaString(env, source_id);
228 Java_ChromeMediaRouter_stopObservingMediaSinks(
229 env, java_media_router_.obj(), jsource_id.obj());
233 void MediaRouterAndroid::RegisterMediaRoutesObserver(
234 MediaRoutesObserver* observer) {
235 DVLOG(2) << "Added MediaRoutesObserver: " << observer;
236 routes_observers_.AddObserver(observer);
239 void MediaRouterAndroid::UnregisterMediaRoutesObserver(
240 MediaRoutesObserver* observer) {
241 if (!routes_observers_.HasObserver(observer))
242 return;
243 routes_observers_.RemoveObserver(observer);
246 void MediaRouterAndroid::RegisterIssuesObserver(IssuesObserver* observer) {
247 NOTIMPLEMENTED();
250 void MediaRouterAndroid::UnregisterIssuesObserver(IssuesObserver* observer) {
251 NOTIMPLEMENTED();
254 void MediaRouterAndroid::RegisterPresentationSessionMessagesObserver(
255 PresentationSessionMessagesObserver* observer) {
256 const MediaRoute::Id& route_id = observer->route_id();
257 auto* observer_list = messages_observers_.get(route_id);
258 if (!observer_list) {
259 observer_list = new base::ObserverList<PresentationSessionMessagesObserver>;
260 messages_observers_.add(route_id, make_scoped_ptr(observer_list));
261 } else {
262 DCHECK(!observer_list->HasObserver(observer));
265 observer_list->AddObserver(observer);
268 void MediaRouterAndroid::UnregisterPresentationSessionMessagesObserver(
269 PresentationSessionMessagesObserver* observer) {
270 const MediaRoute::Id& route_id = observer->route_id();
271 auto* observer_list = messages_observers_.get(route_id);
272 DCHECK(observer_list->HasObserver(observer));
274 observer_list->RemoveObserver(observer);
275 if (!observer_list->might_have_observers())
276 messages_observers_.erase(route_id);
279 void MediaRouterAndroid::OnSinksReceived(
280 JNIEnv* env,
281 jobject obj,
282 jstring jsource_urn,
283 jint jcount) {
284 std::vector<MediaSink> sinks_converted;
285 sinks_converted.reserve(jcount);
286 for (int i = 0; i < jcount; ++i) {
287 ScopedJavaLocalRef<jstring> jsink_urn =
288 Java_ChromeMediaRouter_getSinkUrn(
289 env, java_media_router_.obj(), jsource_urn, i);
290 ScopedJavaLocalRef<jstring> jsink_name =
291 Java_ChromeMediaRouter_getSinkName(
292 env, java_media_router_.obj(), jsource_urn, i);
293 sinks_converted.push_back(
294 MediaSink(ConvertJavaStringToUTF8(env, jsink_urn.obj()),
295 ConvertJavaStringToUTF8(env, jsink_name.obj()),
296 MediaSink::GENERIC));
299 std::string source_urn = ConvertJavaStringToUTF8(env, jsource_urn);
300 auto it = sinks_observers_.find(source_urn);
301 if (it != sinks_observers_.end()) {
302 FOR_EACH_OBSERVER(MediaSinksObserver, *it->second,
303 OnSinksReceived(sinks_converted));
307 void MediaRouterAndroid::OnRouteCreated(
308 JNIEnv* env,
309 jobject obj,
310 jstring jmedia_route_id,
311 jint jcreate_route_request_id,
312 jboolean jis_local) {
313 CreateMediaRouteRequest* request =
314 create_route_requests_.Lookup(jcreate_route_request_id);
315 if (!request)
316 return;
318 MediaRoute route(ConvertJavaStringToUTF8(env, jmedia_route_id),
319 request->media_source, request->media_sink.id(),
320 std::string(), jis_local, std::string(),
321 true); // TODO(avayvod): Populate for_display.
323 for (const MediaRouteResponseCallback& callback : request->callbacks)
324 callback.Run(&route, request->presentation_id, std::string());
326 create_route_requests_.Remove(jcreate_route_request_id);
328 active_routes_.push_back(route);
329 FOR_EACH_OBSERVER(MediaRoutesObserver, routes_observers_,
330 OnRoutesUpdated(active_routes_));
333 void MediaRouterAndroid::OnRouteCreationError(
334 JNIEnv* env,
335 jobject obj,
336 jstring jerror_text,
337 jint jcreate_route_request_id) {
338 CreateMediaRouteRequest* request =
339 create_route_requests_.Lookup(jcreate_route_request_id);
340 if (!request)
341 return;
343 std::string error_text = ConvertJavaStringToUTF8(env, jerror_text);
345 for (const MediaRouteResponseCallback& callback : request->callbacks)
346 callback.Run(nullptr, std::string(), error_text);
348 create_route_requests_.Remove(jcreate_route_request_id);
351 void MediaRouterAndroid::OnRouteClosed(JNIEnv* env,
352 jobject obj,
353 jstring jmedia_route_id) {
354 MediaRoute::Id route_id = ConvertJavaStringToUTF8(env, jmedia_route_id);
355 for (auto it = active_routes_.begin(); it != active_routes_.end(); ++it)
356 if (it->media_route_id() == route_id) {
357 active_routes_.erase(it);
358 break;
361 FOR_EACH_OBSERVER(MediaRoutesObserver, routes_observers_,
362 OnRoutesUpdated(active_routes_));
365 void MediaRouterAndroid::OnMessageSentResult(
366 JNIEnv* env, jobject obj, jboolean jsuccess, jint jcallback_id) {
367 SendRouteMessageCallback* callback = message_callbacks_.Lookup(jcallback_id);
368 callback->Run(jsuccess);
369 message_callbacks_.Remove(jcallback_id);
372 // Notifies the media router about a message received from the media route.
373 void MediaRouterAndroid::OnMessage(
374 JNIEnv* env, jobject obj, jstring jmedia_route_id, jstring jmessage) {
375 MediaRoute::Id route_id = ConvertJavaStringToUTF8(env, jmedia_route_id);
376 auto* observer_list = messages_observers_.get(route_id);
377 if (!observer_list)
378 return;
380 ScopedVector<content::PresentationSessionMessage> session_messages;
381 scoped_ptr<content::PresentationSessionMessage> message(
382 new content::PresentationSessionMessage(content::TEXT));
383 message->message = ConvertJavaStringToUTF8(env, jmessage);
384 session_messages.push_back(message.Pass());
386 FOR_EACH_OBSERVER(PresentationSessionMessagesObserver, *observer_list,
387 OnMessagesReceived(session_messages, true));
390 } // namespace media_router