OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "remoting/client/jni/chromoting_jni_runtime.h" | 5 #include "remoting/client/jni/chromoting_jni_runtime.h" |
6 | 6 |
7 #include "base/android/jni_android.h" | 7 #include "base/android/jni_android.h" |
8 #include "base/android/jni_array.h" | 8 #include "base/android/jni_array.h" |
9 #include "base/android/jni_string.h" | 9 #include "base/android/jni_string.h" |
10 #include "base/android/library_loader/library_loader_hooks.h" | 10 #include "base/android/library_loader/library_loader_hooks.h" |
11 #include "base/android/scoped_java_ref.h" | 11 #include "base/android/scoped_java_ref.h" |
12 #include "base/command_line.h" | 12 #include "base/command_line.h" |
13 #include "base/memory/ptr_util.h" | 13 #include "base/memory/ptr_util.h" |
14 #include "base/memory/singleton.h" | 14 #include "base/memory/singleton.h" |
15 #include "base/stl_util.h" | 15 #include "base/stl_util.h" |
16 #include "base/synchronization/waitable_event.h" | 16 #include "base/synchronization/waitable_event.h" |
17 #include "jni/JniInterface_jni.h" | 17 #include "jni/JniInterface_jni.h" |
18 #include "remoting/base/chromium_url_request.h" | 18 #include "remoting/base/chromium_url_request.h" |
19 #include "remoting/base/url_request_context_getter.h" | 19 #include "remoting/base/url_request_context_getter.h" |
20 #include "remoting/client/jni/jni_touch_event_data.h" | 20 #include "remoting/client/jni/jni_touch_event_data.h" |
21 | 21 |
22 using base::android::ConvertJavaStringToUTF8; | 22 using base::android::ConvertJavaStringToUTF8; |
23 using base::android::ConvertUTF8ToJavaString; | 23 using base::android::ConvertUTF8ToJavaString; |
24 using base::android::ToJavaByteArray; | 24 using base::android::ToJavaByteArray; |
25 | 25 |
26 namespace { | 26 namespace { |
27 | 27 |
28 const int kBytesPerPixel = 4; | |
29 | |
30 const char kTelemetryBaseUrl[] = "https://remoting-pa.googleapis.com/v1/events"; | 28 const char kTelemetryBaseUrl[] = "https://remoting-pa.googleapis.com/v1/events"; |
31 | 29 |
32 } // namespace | 30 } // namespace |
33 | 31 |
34 namespace remoting { | 32 namespace remoting { |
35 | 33 |
36 bool RegisterChromotingJniRuntime(JNIEnv* env) { | 34 bool RegisterChromotingJniRuntime(JNIEnv* env) { |
37 return remoting::RegisterNativesImpl(env); | 35 return remoting::RegisterNativesImpl(env); |
38 } | 36 } |
39 | 37 |
40 // Implementation of stubs defined in JniInterface_jni.h. These are the entry | 38 // Implementation of stubs defined in JniInterface_jni.h. These are the entry |
41 // points for JNI calls from Java into C++. | 39 // points for JNI calls from Java into C++. |
42 | 40 |
43 static void LoadNative(JNIEnv* env, const JavaParamRef<jclass>& clazz) { | 41 static void LoadNative(JNIEnv* env, const JavaParamRef<jclass>& clazz) { |
44 base::CommandLine::Init(0, nullptr); | 42 base::CommandLine::Init(0, nullptr); |
45 | 43 |
46 // Create the singleton now so that the Chromoting threads will be set up. | 44 // Create the singleton now so that the Chromoting threads will be set up. |
47 remoting::ChromotingJniRuntime::GetInstance(); | 45 remoting::ChromotingJniRuntime::GetInstance(); |
48 } | 46 } |
49 | 47 |
50 static void Connect(JNIEnv* env, | |
51 const JavaParamRef<jclass>& clazz, | |
52 const JavaParamRef<jstring>& username, | |
53 const JavaParamRef<jstring>& authToken, | |
54 const JavaParamRef<jstring>& hostJid, | |
55 const JavaParamRef<jstring>& hostId, | |
56 const JavaParamRef<jstring>& hostPubkey, | |
57 const JavaParamRef<jstring>& pairId, | |
58 const JavaParamRef<jstring>& pairSecret, | |
59 const JavaParamRef<jstring>& capabilities, | |
60 const JavaParamRef<jstring>& flags) { | |
61 remoting::ChromotingJniRuntime::GetInstance()->ConnectToHost( | |
62 ConvertJavaStringToUTF8(env, username), | |
63 ConvertJavaStringToUTF8(env, authToken), | |
64 ConvertJavaStringToUTF8(env, hostJid), | |
65 ConvertJavaStringToUTF8(env, hostId), | |
66 ConvertJavaStringToUTF8(env, hostPubkey), | |
67 ConvertJavaStringToUTF8(env, pairId), | |
68 ConvertJavaStringToUTF8(env, pairSecret), | |
69 ConvertJavaStringToUTF8(env, capabilities), | |
70 ConvertJavaStringToUTF8(env, flags)); | |
71 } | |
72 | |
73 static void Disconnect(JNIEnv* env, const JavaParamRef<jclass>& clazz) { | |
74 remoting::ChromotingJniRuntime::GetInstance()->DisconnectFromHost(); | |
75 } | |
76 | |
77 static void AuthenticationResponse(JNIEnv* env, | |
78 const JavaParamRef<jclass>& clazz, | |
79 const JavaParamRef<jstring>& pin, | |
80 jboolean createPair, | |
81 const JavaParamRef<jstring>& deviceName) { | |
82 remoting::ChromotingJniRuntime::GetInstance()->session()->ProvideSecret( | |
83 ConvertJavaStringToUTF8(env, pin).c_str(), createPair, | |
84 ConvertJavaStringToUTF8(env, deviceName)); | |
85 } | |
86 | |
87 static void ScheduleRedraw(JNIEnv* env, const JavaParamRef<jclass>& clazz) { | |
88 remoting::ChromotingJniRuntime::GetInstance()->session()->RedrawDesktop(); | |
89 } | |
90 | |
91 static void SendMouseEvent(JNIEnv* env, | |
92 const JavaParamRef<jclass>& clazz, | |
93 jint x, | |
94 jint y, | |
95 jint whichButton, | |
96 jboolean buttonDown) { | |
97 // Button must be within the bounds of the MouseEvent_MouseButton enum. | |
98 DCHECK(whichButton >= 0 && whichButton < 5); | |
99 | |
100 remoting::ChromotingJniRuntime::GetInstance()->session()->SendMouseEvent( | |
101 x, y, | |
102 static_cast<remoting::protocol::MouseEvent_MouseButton>(whichButton), | |
103 buttonDown); | |
104 } | |
105 | |
106 static void SendMouseWheelEvent(JNIEnv* env, | |
107 const JavaParamRef<jclass>& clazz, | |
108 jint delta_x, | |
109 jint delta_y) { | |
110 remoting::ChromotingJniRuntime::GetInstance()->session()->SendMouseWheelEvent( | |
111 delta_x, delta_y); | |
112 } | |
113 | |
114 static jboolean SendKeyEvent(JNIEnv* env, | |
115 const JavaParamRef<jclass>& clazz, | |
116 jint scanCode, | |
117 jint keyCode, | |
118 jboolean keyDown) { | |
119 return remoting::ChromotingJniRuntime::GetInstance()->session()->SendKeyEvent( | |
120 scanCode, keyCode, keyDown); | |
121 } | |
122 | |
123 static void SendTextEvent(JNIEnv* env, | |
124 const JavaParamRef<jclass>& clazz, | |
125 const JavaParamRef<jstring>& text) { | |
126 remoting::ChromotingJniRuntime::GetInstance()->session()->SendTextEvent( | |
127 ConvertJavaStringToUTF8(env, text)); | |
128 } | |
129 | |
130 static void SendTouchEvent( | |
131 JNIEnv* env, | |
132 const JavaParamRef<jclass>& clazz, | |
133 jint eventType, | |
134 const JavaParamRef<jobjectArray>& touchEventObjectArray) { | |
135 protocol::TouchEvent touch_event; | |
136 touch_event.set_event_type( | |
137 static_cast<protocol::TouchEvent::TouchEventType>(eventType)); | |
138 | |
139 // Iterate over the elements in the object array and transfer the data from | |
140 // the java object to a native event object. | |
141 jsize length = env->GetArrayLength(touchEventObjectArray); | |
142 DCHECK_GE(length, 0); | |
143 for (jsize i = 0; i < length; ++i) { | |
144 protocol::TouchEventPoint* touch_point = touch_event.add_touch_points(); | |
145 | |
146 ScopedJavaLocalRef<jobject> java_touch_event( | |
147 env, env->GetObjectArrayElement(touchEventObjectArray, i)); | |
148 | |
149 JniTouchEventData::CopyTouchPointData(env, java_touch_event, touch_point); | |
150 } | |
151 | |
152 remoting::ChromotingJniRuntime::GetInstance()->session()->SendTouchEvent( | |
153 touch_event); | |
154 } | |
155 | |
156 static void EnableVideoChannel(JNIEnv* env, | |
157 const JavaParamRef<jclass>& clazz, | |
158 jboolean enable) { | |
159 remoting::ChromotingJniRuntime::GetInstance()->session()->EnableVideoChannel( | |
160 enable); | |
161 } | |
162 | |
163 static void OnThirdPartyTokenFetched( | |
164 JNIEnv* env, | |
165 const JavaParamRef<jclass>& clazz, | |
166 const JavaParamRef<jstring>& token, | |
167 const JavaParamRef<jstring>& shared_secret) { | |
168 ChromotingJniRuntime* runtime = remoting::ChromotingJniRuntime::GetInstance(); | |
169 runtime->network_task_runner()->PostTask(FROM_HERE, base::Bind( | |
170 &ChromotingJniInstance::HandleOnThirdPartyTokenFetched, | |
171 runtime->session(), | |
172 ConvertJavaStringToUTF8(env, token), | |
173 ConvertJavaStringToUTF8(env, shared_secret))); | |
174 } | |
175 | |
176 static void SendExtensionMessage(JNIEnv* env, | |
177 const JavaParamRef<jclass>& clazz, | |
178 const JavaParamRef<jstring>& type, | |
179 const JavaParamRef<jstring>& data) { | |
180 remoting::ChromotingJniRuntime::GetInstance()->session()->SendClientMessage( | |
181 ConvertJavaStringToUTF8(env, type), | |
182 ConvertJavaStringToUTF8(env, data)); | |
183 } | |
184 | |
185 static void HandleAuthTokenOnNetworkThread(const std::string& token) { | 48 static void HandleAuthTokenOnNetworkThread(const std::string& token) { |
186 ChromotingJniRuntime* runtime = remoting::ChromotingJniRuntime::GetInstance(); | 49 ChromotingJniRuntime* runtime = remoting::ChromotingJniRuntime::GetInstance(); |
187 DCHECK(runtime->network_task_runner()->BelongsToCurrentThread()); | 50 DCHECK(runtime->network_task_runner()->BelongsToCurrentThread()); |
188 runtime->logger()->SetAuthToken(token); | 51 runtime->logger()->SetAuthToken(token); |
189 } | 52 } |
190 | 53 |
191 static void OnAuthTokenFetched(JNIEnv* env, | 54 static void OnAuthTokenFetched(JNIEnv* env, |
192 const JavaParamRef<jclass>& clazz, | 55 const JavaParamRef<jclass>& clazz, |
193 const JavaParamRef<jstring>& token) { | 56 const JavaParamRef<jstring>& token) { |
194 ChromotingJniRuntime* runtime = remoting::ChromotingJniRuntime::GetInstance(); | 57 ChromotingJniRuntime* runtime = remoting::ChromotingJniRuntime::GetInstance(); |
(...skipping 29 matching lines...) Expand all Loading... |
224 runtime_ = ChromotingClientRuntime::Create(ui_loop_.get()); | 87 runtime_ = ChromotingClientRuntime::Create(ui_loop_.get()); |
225 network_task_runner()->PostTask( | 88 network_task_runner()->PostTask( |
226 FROM_HERE, base::Bind(&ChromotingJniRuntime::StartLoggerOnNetworkThread, | 89 FROM_HERE, base::Bind(&ChromotingJniRuntime::StartLoggerOnNetworkThread, |
227 base::Unretained(this))); | 90 base::Unretained(this))); |
228 } | 91 } |
229 | 92 |
230 ChromotingJniRuntime::~ChromotingJniRuntime() { | 93 ChromotingJniRuntime::~ChromotingJniRuntime() { |
231 // The singleton should only ever be destroyed on the main thread. | 94 // The singleton should only ever be destroyed on the main thread. |
232 DCHECK(ui_task_runner()->BelongsToCurrentThread()); | 95 DCHECK(ui_task_runner()->BelongsToCurrentThread()); |
233 | 96 |
234 // The session must be shut down first, since it depends on our other | |
235 // components' still being alive. | |
236 DisconnectFromHost(); | |
237 | |
238 base::WaitableEvent done_event(false, false); | 97 base::WaitableEvent done_event(false, false); |
239 network_task_runner()->PostTask( | 98 network_task_runner()->PostTask( |
240 FROM_HERE, base::Bind(&ChromotingJniRuntime::DetachFromVmAndSignal, | 99 FROM_HERE, base::Bind(&ChromotingJniRuntime::DetachFromVmAndSignal, |
241 base::Unretained(this), &done_event)); | 100 base::Unretained(this), &done_event)); |
242 done_event.Wait(); | 101 done_event.Wait(); |
243 display_task_runner()->PostTask( | 102 display_task_runner()->PostTask( |
244 FROM_HERE, base::Bind(&ChromotingJniRuntime::DetachFromVmAndSignal, | 103 FROM_HERE, base::Bind(&ChromotingJniRuntime::DetachFromVmAndSignal, |
245 base::Unretained(this), &done_event)); | 104 base::Unretained(this), &done_event)); |
246 done_event.Wait(); | 105 done_event.Wait(); |
247 base::android::LibraryLoaderExitHook(); | 106 base::android::LibraryLoaderExitHook(); |
248 base::android::DetachFromVM(); | 107 base::android::DetachFromVM(); |
249 } | 108 } |
250 | 109 |
251 void ChromotingJniRuntime::ConnectToHost(const std::string& username, | |
252 const std::string& auth_token, | |
253 const std::string& host_jid, | |
254 const std::string& host_id, | |
255 const std::string& host_pubkey, | |
256 const std::string& pairing_id, | |
257 const std::string& pairing_secret, | |
258 const std::string& capabilities, | |
259 const std::string& flags) { | |
260 DCHECK(ui_task_runner()->BelongsToCurrentThread()); | |
261 DCHECK(!session_.get()); | |
262 session_ = new ChromotingJniInstance(this, username, auth_token, host_jid, | |
263 host_id, host_pubkey, pairing_id, | |
264 pairing_secret, capabilities, flags); | |
265 session_->Connect(); | |
266 } | |
267 | |
268 void ChromotingJniRuntime::DisconnectFromHost() { | |
269 DCHECK(ui_task_runner()->BelongsToCurrentThread()); | |
270 if (session_.get()) { | |
271 session_->Disconnect(); | |
272 session_ = nullptr; | |
273 } | |
274 } | |
275 | |
276 void ChromotingJniRuntime::OnConnectionState( | |
277 protocol::ConnectionToHost::State state, | |
278 protocol::ErrorCode error) { | |
279 DCHECK(ui_task_runner()->BelongsToCurrentThread()); | |
280 | |
281 JNIEnv* env = base::android::AttachCurrentThread(); | |
282 Java_JniInterface_onConnectionState(env, state, error); | |
283 } | |
284 | |
285 void ChromotingJniRuntime::DisplayAuthenticationPrompt(bool pairing_supported) { | |
286 DCHECK(ui_task_runner()->BelongsToCurrentThread()); | |
287 | |
288 JNIEnv* env = base::android::AttachCurrentThread(); | |
289 Java_JniInterface_displayAuthenticationPrompt(env, pairing_supported); | |
290 } | |
291 | |
292 void ChromotingJniRuntime::CommitPairingCredentials(const std::string& host, | |
293 const std::string& id, | |
294 const std::string& secret) { | |
295 DCHECK(ui_task_runner()->BelongsToCurrentThread()); | |
296 | |
297 JNIEnv* env = base::android::AttachCurrentThread(); | |
298 ScopedJavaLocalRef<jstring> j_host = ConvertUTF8ToJavaString(env, host); | |
299 ScopedJavaLocalRef<jstring> j_id = ConvertUTF8ToJavaString(env, id); | |
300 ScopedJavaLocalRef<jstring> j_secret = ConvertUTF8ToJavaString(env,secret); | |
301 | |
302 Java_JniInterface_commitPairingCredentials( | |
303 env, j_host.obj(), j_id.obj(), j_secret.obj()); | |
304 } | |
305 | |
306 void ChromotingJniRuntime::FetchAuthToken() { | 110 void ChromotingJniRuntime::FetchAuthToken() { |
307 if (!ui_task_runner()->BelongsToCurrentThread()) { | 111 if (!ui_task_runner()->BelongsToCurrentThread()) { |
308 ui_task_runner()->PostTask( | 112 ui_task_runner()->PostTask( |
309 FROM_HERE, base::Bind(&ChromotingJniRuntime::FetchAuthToken, | 113 FROM_HERE, base::Bind(&ChromotingJniRuntime::FetchAuthToken, |
310 base::Unretained(this))); | 114 base::Unretained(this))); |
311 return; | 115 return; |
312 } | 116 } |
313 JNIEnv* env = base::android::AttachCurrentThread(); | 117 JNIEnv* env = base::android::AttachCurrentThread(); |
314 | 118 |
315 Java_JniInterface_fetchAuthToken(env); | 119 Java_JniInterface_fetchAuthToken(env); |
316 } | 120 } |
317 | 121 |
318 void ChromotingJniRuntime::FetchThirdPartyToken(const std::string& token_url, | |
319 const std::string& client_id, | |
320 const std::string& scope) { | |
321 DCHECK(ui_task_runner()->BelongsToCurrentThread()); | |
322 JNIEnv* env = base::android::AttachCurrentThread(); | |
323 | |
324 ScopedJavaLocalRef<jstring> j_url = ConvertUTF8ToJavaString(env, token_url); | |
325 ScopedJavaLocalRef<jstring> j_client_id = | |
326 ConvertUTF8ToJavaString(env, client_id); | |
327 ScopedJavaLocalRef<jstring> j_scope = ConvertUTF8ToJavaString(env, scope); | |
328 | |
329 Java_JniInterface_fetchThirdPartyToken( | |
330 env, j_url.obj(), j_client_id.obj(), j_scope.obj()); | |
331 } | |
332 | |
333 void ChromotingJniRuntime::SetCapabilities(const std::string& capabilities) { | |
334 DCHECK(ui_task_runner()->BelongsToCurrentThread()); | |
335 JNIEnv* env = base::android::AttachCurrentThread(); | |
336 | |
337 ScopedJavaLocalRef<jstring> j_cap = | |
338 ConvertUTF8ToJavaString(env, capabilities); | |
339 | |
340 Java_JniInterface_setCapabilities(env, j_cap.obj()); | |
341 } | |
342 | |
343 void ChromotingJniRuntime::HandleExtensionMessage(const std::string& type, | |
344 const std::string& message) { | |
345 DCHECK(ui_task_runner()->BelongsToCurrentThread()); | |
346 JNIEnv* env = base::android::AttachCurrentThread(); | |
347 | |
348 ScopedJavaLocalRef<jstring> j_type = ConvertUTF8ToJavaString(env, type); | |
349 ScopedJavaLocalRef<jstring> j_message = ConvertUTF8ToJavaString(env, message); | |
350 | |
351 Java_JniInterface_handleExtensionMessage(env, j_type.obj(), j_message.obj()); | |
352 } | |
353 | |
354 base::android::ScopedJavaLocalRef<jobject> ChromotingJniRuntime::NewBitmap( | |
355 int width, int height) { | |
356 JNIEnv* env = base::android::AttachCurrentThread(); | |
357 return Java_JniInterface_newBitmap(env, width, height); | |
358 } | |
359 | |
360 void ChromotingJniRuntime::UpdateFrameBitmap(jobject bitmap) { | |
361 DCHECK(display_task_runner()->BelongsToCurrentThread()); | |
362 | |
363 JNIEnv* env = base::android::AttachCurrentThread(); | |
364 Java_JniInterface_setVideoFrame(env, bitmap); | |
365 } | |
366 | |
367 void ChromotingJniRuntime::UpdateCursorShape( | |
368 const protocol::CursorShapeInfo& cursor_shape) { | |
369 DCHECK(display_task_runner()->BelongsToCurrentThread()); | |
370 | |
371 // const_cast<> is safe as long as the Java updateCursorShape() method copies | |
372 // the data out of the buffer without mutating it, and doesn't keep any | |
373 // reference to the buffer afterwards. Unfortunately, there seems to be no way | |
374 // to create a read-only ByteBuffer from a pointer-to-const. | |
375 char* data = string_as_array(const_cast<std::string*>(&cursor_shape.data())); | |
376 int cursor_total_bytes = | |
377 cursor_shape.width() * cursor_shape.height() * kBytesPerPixel; | |
378 | |
379 JNIEnv* env = base::android::AttachCurrentThread(); | |
380 base::android::ScopedJavaLocalRef<jobject> buffer(env, | |
381 env->NewDirectByteBuffer(data, cursor_total_bytes)); | |
382 Java_JniInterface_updateCursorShape(env, | |
383 cursor_shape.width(), | |
384 cursor_shape.height(), | |
385 cursor_shape.hotspot_x(), | |
386 cursor_shape.hotspot_y(), | |
387 buffer.obj()); | |
388 } | |
389 | |
390 void ChromotingJniRuntime::RedrawCanvas() { | |
391 DCHECK(display_task_runner()->BelongsToCurrentThread()); | |
392 | |
393 JNIEnv* env = base::android::AttachCurrentThread(); | |
394 Java_JniInterface_redrawGraphicsInternal(env); | |
395 } | |
396 | |
397 void ChromotingJniRuntime::DetachFromVmAndSignal(base::WaitableEvent* waiter) { | 122 void ChromotingJniRuntime::DetachFromVmAndSignal(base::WaitableEvent* waiter) { |
398 base::android::DetachFromVM(); | 123 base::android::DetachFromVM(); |
399 waiter->Signal(); | 124 waiter->Signal(); |
400 } | 125 } |
401 | 126 |
402 void ChromotingJniRuntime::StartLoggerOnNetworkThread() { | 127 void ChromotingJniRuntime::StartLoggerOnNetworkThread() { |
403 DCHECK(network_task_runner()->BelongsToCurrentThread()); | 128 DCHECK(network_task_runner()->BelongsToCurrentThread()); |
404 logger_.reset(new ClientTelemetryLogger(ChromotingEvent::Mode::ME2ME)); | 129 logger_.reset(new ClientTelemetryLogger(ChromotingEvent::Mode::ME2ME)); |
405 logger_->Start( | 130 logger_->Start( |
406 base::WrapUnique( | 131 base::WrapUnique( |
407 new ChromiumUrlRequestFactory(runtime_->url_requester())), | 132 new ChromiumUrlRequestFactory(runtime_->url_requester())), |
408 kTelemetryBaseUrl); | 133 kTelemetryBaseUrl); |
409 logger_->SetAuthClosure( | 134 logger_->SetAuthClosure( |
410 base::Bind(&ChromotingJniRuntime::FetchAuthToken, | 135 base::Bind(&ChromotingJniRuntime::FetchAuthToken, |
411 base::Unretained(this))); | 136 base::Unretained(this))); |
412 } | 137 } |
413 | 138 |
414 } // namespace remoting | 139 } // namespace remoting |
OLD | NEW |