Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / media / android / router / media_router_android.cc
blobb28b25c5de8a635dc344063a6e36b2f6a82b2a40
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::ClearIssue(const Issue::Id& issue_id) {
179 NOTIMPLEMENTED();
182 void MediaRouterAndroid::OnPresentationSessionDetached(
183 const MediaRoute::Id& route_id) {
184 NOTIMPLEMENTED();
187 void MediaRouterAndroid::RegisterMediaSinksObserver(
188 MediaSinksObserver* observer) {
189 const std::string& source_id = observer->source().id();
190 base::ObserverList<MediaSinksObserver>* observer_list =
191 sinks_observers_.get(source_id);
192 if (!observer_list) {
193 observer_list = new base::ObserverList<MediaSinksObserver>;
194 sinks_observers_.add(source_id, make_scoped_ptr(observer_list));
195 } else {
196 DCHECK(!observer_list->HasObserver(observer));
199 observer_list->AddObserver(observer);
200 JNIEnv* env = base::android::AttachCurrentThread();
201 ScopedJavaLocalRef<jstring> jsource_id =
202 base::android::ConvertUTF8ToJavaString(env, source_id);
203 Java_ChromeMediaRouter_startObservingMediaSinks(
204 env, java_media_router_.obj(), jsource_id.obj());
207 void MediaRouterAndroid::UnregisterMediaSinksObserver(
208 MediaSinksObserver* observer) {
209 const std::string& source_id = observer->source().id();
210 auto* observer_list = sinks_observers_.get(source_id);
211 if (!observer_list || !observer_list->HasObserver(observer))
212 return;
214 // If we are removing the final observer for the source, then stop
215 // observing sinks for it.
216 // might_have_observers() is reliable here on the assumption that this call
217 // is not inside the ObserverList iteration.
218 observer_list->RemoveObserver(observer);
219 if (!observer_list->might_have_observers()) {
220 sinks_observers_.erase(source_id);
221 JNIEnv* env = base::android::AttachCurrentThread();
222 ScopedJavaLocalRef<jstring> jsource_id =
223 base::android::ConvertUTF8ToJavaString(env, source_id);
224 Java_ChromeMediaRouter_stopObservingMediaSinks(
225 env, java_media_router_.obj(), jsource_id.obj());
229 void MediaRouterAndroid::RegisterMediaRoutesObserver(
230 MediaRoutesObserver* observer) {
231 DVLOG(2) << "Added MediaRoutesObserver: " << observer;
232 routes_observers_.AddObserver(observer);
235 void MediaRouterAndroid::UnregisterMediaRoutesObserver(
236 MediaRoutesObserver* observer) {
237 if (!routes_observers_.HasObserver(observer))
238 return;
239 routes_observers_.RemoveObserver(observer);
242 void MediaRouterAndroid::RegisterIssuesObserver(IssuesObserver* observer) {
243 NOTIMPLEMENTED();
246 void MediaRouterAndroid::UnregisterIssuesObserver(IssuesObserver* observer) {
247 NOTIMPLEMENTED();
250 void MediaRouterAndroid::RegisterPresentationSessionMessagesObserver(
251 PresentationSessionMessagesObserver* observer) {
252 const MediaRoute::Id& route_id = observer->route_id();
253 auto* observer_list = messages_observers_.get(route_id);
254 if (!observer_list) {
255 observer_list = new base::ObserverList<PresentationSessionMessagesObserver>;
256 messages_observers_.add(route_id, make_scoped_ptr(observer_list));
257 } else {
258 DCHECK(!observer_list->HasObserver(observer));
261 observer_list->AddObserver(observer);
264 void MediaRouterAndroid::UnregisterPresentationSessionMessagesObserver(
265 PresentationSessionMessagesObserver* observer) {
266 const MediaRoute::Id& route_id = observer->route_id();
267 auto* observer_list = messages_observers_.get(route_id);
268 DCHECK(observer_list->HasObserver(observer));
270 observer_list->RemoveObserver(observer);
271 if (!observer_list->might_have_observers())
272 messages_observers_.erase(route_id);
275 void MediaRouterAndroid::OnSinksReceived(
276 JNIEnv* env,
277 jobject obj,
278 jstring jsource_urn,
279 jint jcount) {
280 std::vector<MediaSink> sinks_converted;
281 sinks_converted.reserve(jcount);
282 for (int i = 0; i < jcount; ++i) {
283 ScopedJavaLocalRef<jstring> jsink_urn =
284 Java_ChromeMediaRouter_getSinkUrn(
285 env, java_media_router_.obj(), jsource_urn, i);
286 ScopedJavaLocalRef<jstring> jsink_name =
287 Java_ChromeMediaRouter_getSinkName(
288 env, java_media_router_.obj(), jsource_urn, i);
289 sinks_converted.push_back(
290 MediaSink(ConvertJavaStringToUTF8(env, jsink_urn.obj()),
291 ConvertJavaStringToUTF8(env, jsink_name.obj()),
292 MediaSink::GENERIC));
295 std::string source_urn = ConvertJavaStringToUTF8(env, jsource_urn);
296 auto it = sinks_observers_.find(source_urn);
297 if (it != sinks_observers_.end()) {
298 FOR_EACH_OBSERVER(MediaSinksObserver, *it->second,
299 OnSinksReceived(sinks_converted));
303 void MediaRouterAndroid::OnRouteCreated(
304 JNIEnv* env,
305 jobject obj,
306 jstring jmedia_route_id,
307 jint jcreate_route_request_id,
308 jboolean jis_local) {
309 CreateMediaRouteRequest* request =
310 create_route_requests_.Lookup(jcreate_route_request_id);
311 if (!request)
312 return;
314 MediaRoute route(ConvertJavaStringToUTF8(env, jmedia_route_id),
315 request->media_source, request->media_sink.id(),
316 std::string(), jis_local, std::string(),
317 true); // TODO(avayvod): Populate for_display.
319 for (const MediaRouteResponseCallback& callback : request->callbacks)
320 callback.Run(&route, request->presentation_id, std::string());
322 create_route_requests_.Remove(jcreate_route_request_id);
324 active_routes_.push_back(route);
325 FOR_EACH_OBSERVER(MediaRoutesObserver, routes_observers_,
326 OnRoutesUpdated(active_routes_));
329 void MediaRouterAndroid::OnRouteCreationError(
330 JNIEnv* env,
331 jobject obj,
332 jstring jerror_text,
333 jint jcreate_route_request_id) {
334 CreateMediaRouteRequest* request =
335 create_route_requests_.Lookup(jcreate_route_request_id);
336 if (!request)
337 return;
339 std::string error_text = ConvertJavaStringToUTF8(env, jerror_text);
341 for (const MediaRouteResponseCallback& callback : request->callbacks)
342 callback.Run(nullptr, std::string(), error_text);
344 create_route_requests_.Remove(jcreate_route_request_id);
347 void MediaRouterAndroid::OnRouteClosed(JNIEnv* env,
348 jobject obj,
349 jstring jmedia_route_id) {
350 MediaRoute::Id route_id = ConvertJavaStringToUTF8(env, jmedia_route_id);
351 for (auto it = active_routes_.begin(); it != active_routes_.end(); ++it)
352 if (it->media_route_id() == route_id) {
353 active_routes_.erase(it);
354 break;
357 FOR_EACH_OBSERVER(MediaRoutesObserver, routes_observers_,
358 OnRoutesUpdated(active_routes_));
361 void MediaRouterAndroid::OnMessageSentResult(
362 JNIEnv* env, jobject obj, jboolean jsuccess, jint jcallback_id) {
363 SendRouteMessageCallback* callback = message_callbacks_.Lookup(jcallback_id);
364 callback->Run(jsuccess);
365 message_callbacks_.Remove(jcallback_id);
368 // Notifies the media router about a message received from the media route.
369 void MediaRouterAndroid::OnMessage(
370 JNIEnv* env, jobject obj, jstring jmedia_route_id, jstring jmessage) {
371 MediaRoute::Id route_id = ConvertJavaStringToUTF8(env, jmedia_route_id);
372 auto* observer_list = messages_observers_.get(route_id);
373 if (!observer_list)
374 return;
376 ScopedVector<content::PresentationSessionMessage> session_messages;
377 scoped_ptr<content::PresentationSessionMessage> message(
378 new content::PresentationSessionMessage(content::TEXT));
379 message->message = ConvertJavaStringToUTF8(env, jmessage);
380 session_messages.push_back(message.Pass());
382 FOR_EACH_OBSERVER(PresentationSessionMessagesObserver, *observer_list,
383 OnMessagesReceived(session_messages, true));
386 } // namespace media_router