Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / remoting / client / jni / chromoting_jni_runtime.cc
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
3 // found in the LICENSE file.
4
5 #include "remoting/client/jni/chromoting_jni_runtime.h"
6
7 #include "base/android/jni_android.h"
8 #include "base/android/jni_array.h"
9 #include "base/android/jni_string.h"
10 #include "base/android/scoped_java_ref.h"
11 #include "base/basictypes.h"
12 #include "base/command_line.h"
13 #include "base/memory/singleton.h"
14 #include "base/stl_util.h"
15 #include "base/synchronization/waitable_event.h"
16 #include "google_apis/google_api_keys.h"
17 #include "jni/JniInterface_jni.h"
18 #include "media/base/yuv_convert.h"
19 #include "remoting/base/url_request_context_getter.h"
20 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
21
22 using base::android::ConvertJavaStringToUTF8;
23 using base::android::ConvertUTF8ToJavaString;
24 using base::android::ToJavaByteArray;
25
26 namespace {
27
28 const int kBytesPerPixel = 4;
29
30 }  // namespace
31
32 namespace remoting {
33
34 bool RegisterJni(JNIEnv* env) {
35   return remoting::RegisterNativesImpl(env);
36 }
37
38 // Implementation of stubs defined in JniInterface_jni.h. These are the entry
39 // points for JNI calls from Java into C++.
40
41 static void LoadNative(JNIEnv* env, jclass clazz, jobject context) {
42   base::android::ScopedJavaLocalRef<jobject> context_activity(env, context);
43   base::android::InitApplicationContext(env, context_activity);
44
45   // The google_apis functions check the command-line arguments to make sure no
46   // runtime API keys have been specified by the environment. Unfortunately, we
47   // neither launch Chromium nor have a command line, so we need to prevent
48   // them from DCHECKing out when they go looking.
49   base::CommandLine::Init(0, NULL);
50
51   // Create the singleton now so that the Chromoting threads will be set up.
52   remoting::ChromotingJniRuntime::GetInstance();
53 }
54
55 static jstring GetApiKey(JNIEnv* env, jclass clazz) {
56   return ConvertUTF8ToJavaString(
57       env, google_apis::GetAPIKey().c_str()).Release();
58 }
59
60 static jstring GetClientId(JNIEnv* env, jclass clazz) {
61   return ConvertUTF8ToJavaString(
62       env, google_apis::GetOAuth2ClientID(
63           google_apis::CLIENT_REMOTING).c_str()).Release();
64 }
65
66 static jstring GetClientSecret(JNIEnv* env, jclass clazz) {
67   return ConvertUTF8ToJavaString(
68       env, google_apis::GetOAuth2ClientSecret(
69           google_apis::CLIENT_REMOTING).c_str()).Release();
70 }
71
72 static void Connect(JNIEnv* env,
73                     jclass clazz,
74                     jstring username,
75                     jstring authToken,
76                     jstring hostJid,
77                     jstring hostId,
78                     jstring hostPubkey,
79                     jstring pairId,
80                     jstring pairSecret,
81                     jstring capabilities) {
82   remoting::ChromotingJniRuntime::GetInstance()->ConnectToHost(
83       ConvertJavaStringToUTF8(env, username).c_str(),
84       ConvertJavaStringToUTF8(env, authToken).c_str(),
85       ConvertJavaStringToUTF8(env, hostJid).c_str(),
86       ConvertJavaStringToUTF8(env, hostId).c_str(),
87       ConvertJavaStringToUTF8(env, hostPubkey).c_str(),
88       ConvertJavaStringToUTF8(env, pairId).c_str(),
89       ConvertJavaStringToUTF8(env, pairSecret).c_str(),
90       ConvertJavaStringToUTF8(env, capabilities).c_str());
91 }
92
93 static void Disconnect(JNIEnv* env, jclass clazz) {
94   remoting::ChromotingJniRuntime::GetInstance()->DisconnectFromHost();
95 }
96
97 static void AuthenticationResponse(JNIEnv* env,
98                                    jclass clazz,
99                                    jstring pin,
100                                    jboolean createPair,
101                                    jstring deviceName) {
102   remoting::ChromotingJniRuntime::GetInstance()->session()->ProvideSecret(
103       ConvertJavaStringToUTF8(env, pin).c_str(), createPair,
104       ConvertJavaStringToUTF8(env, deviceName));
105 }
106
107 static void ScheduleRedraw(JNIEnv* env, jclass clazz) {
108   remoting::ChromotingJniRuntime::GetInstance()->session()->RedrawDesktop();
109 }
110
111 static void SendMouseEvent(JNIEnv* env,
112                            jclass clazz,
113                            jint x,
114                            jint y,
115                            jint whichButton,
116                            jboolean buttonDown) {
117   // Button must be within the bounds of the MouseEvent_MouseButton enum.
118   DCHECK(whichButton >= 0 && whichButton < 5);
119
120   remoting::ChromotingJniRuntime::GetInstance()->session()->SendMouseEvent(
121       x, y,
122       static_cast<remoting::protocol::MouseEvent_MouseButton>(whichButton),
123       buttonDown);
124 }
125
126 static void SendMouseWheelEvent(JNIEnv* env,
127                                 jclass clazz,
128                                 jint delta_x,
129                                 jint delta_y) {
130   remoting::ChromotingJniRuntime::GetInstance()->session()->SendMouseWheelEvent(
131       delta_x, delta_y);
132 }
133
134 static jboolean SendKeyEvent(JNIEnv* env,
135                          jclass clazz,
136                          jint keyCode,
137                          jboolean keyDown) {
138   return remoting::ChromotingJniRuntime::GetInstance()->session()->SendKeyEvent(
139       keyCode, keyDown);
140 }
141
142 static void SendTextEvent(JNIEnv* env,
143                           jclass clazz,
144                           jstring text) {
145   remoting::ChromotingJniRuntime::GetInstance()->session()->SendTextEvent(
146       ConvertJavaStringToUTF8(env, text));
147 }
148
149 static void OnThirdPartyTokenFetched(JNIEnv* env,
150                                      jclass clazz,
151                                      jstring token,
152                                      jstring shared_secret) {
153   ChromotingJniRuntime* runtime = remoting::ChromotingJniRuntime::GetInstance();
154   runtime->network_task_runner()->PostTask(FROM_HERE, base::Bind(
155       &ChromotingJniInstance::HandleOnThirdPartyTokenFetched,
156       runtime->session(),
157       ConvertJavaStringToUTF8(env, token),
158       ConvertJavaStringToUTF8(env, shared_secret)));
159 }
160
161 static void SendExtensionMessage(JNIEnv* env,
162                                  jclass clazz,
163                                  jstring type,
164                                  jstring data) {
165   remoting::ChromotingJniRuntime::GetInstance()->session()->SendClientMessage(
166       ConvertJavaStringToUTF8(env, type),
167       ConvertJavaStringToUTF8(env, data));
168 }
169
170 // ChromotingJniRuntime implementation.
171
172 // static
173 ChromotingJniRuntime* ChromotingJniRuntime::GetInstance() {
174   return Singleton<ChromotingJniRuntime>::get();
175 }
176
177 ChromotingJniRuntime::ChromotingJniRuntime() {
178   at_exit_manager_.reset(new base::AtExitManager());
179
180   // On Android, the UI thread is managed by Java, so we need to attach and
181   // start a special type of message loop to allow Chromium code to run tasks.
182   ui_loop_.reset(new base::MessageLoopForUI());
183   ui_loop_->Start();
184
185   // TODO(solb) Stop pretending to control the managed UI thread's lifetime.
186   ui_task_runner_ = new AutoThreadTaskRunner(ui_loop_->message_loop_proxy(),
187                                              base::MessageLoop::QuitClosure());
188   network_task_runner_ = AutoThread::CreateWithType("native_net",
189                                                     ui_task_runner_,
190                                                     base::MessageLoop::TYPE_IO);
191   display_task_runner_ = AutoThread::Create("native_disp",
192                                             ui_task_runner_);
193
194   url_requester_ = new URLRequestContextGetter(network_task_runner_);
195
196   // Allows later decoding of video frames.
197   media::InitializeCPUSpecificYUVConversions();
198 }
199
200 ChromotingJniRuntime::~ChromotingJniRuntime() {
201   // The singleton should only ever be destroyed on the main thread.
202   DCHECK(ui_task_runner_->BelongsToCurrentThread());
203
204   // The session must be shut down first, since it depends on our other
205   // components' still being alive.
206   DisconnectFromHost();
207
208   base::WaitableEvent done_event(false, false);
209   network_task_runner_->PostTask(FROM_HERE, base::Bind(
210       &ChromotingJniRuntime::DetachFromVmAndSignal,
211       base::Unretained(this),
212       &done_event));
213   done_event.Wait();
214   display_task_runner_->PostTask(FROM_HERE, base::Bind(
215       &ChromotingJniRuntime::DetachFromVmAndSignal,
216       base::Unretained(this),
217       &done_event));
218   done_event.Wait();
219   base::android::DetachFromVM();
220 }
221
222 void ChromotingJniRuntime::ConnectToHost(const char* username,
223                                   const char* auth_token,
224                                   const char* host_jid,
225                                   const char* host_id,
226                                   const char* host_pubkey,
227                                   const char* pairing_id,
228                                   const char* pairing_secret,
229                                   const char* capabilities) {
230   DCHECK(ui_task_runner_->BelongsToCurrentThread());
231   DCHECK(!session_);
232   session_ = new ChromotingJniInstance(this,
233                                        username,
234                                        auth_token,
235                                        host_jid,
236                                        host_id,
237                                        host_pubkey,
238                                        pairing_id,
239                                        pairing_secret,
240                                        capabilities);
241 }
242
243 void ChromotingJniRuntime::DisconnectFromHost() {
244   DCHECK(ui_task_runner_->BelongsToCurrentThread());
245   if (session_) {
246     session_->Disconnect();
247     session_ = NULL;
248   }
249 }
250
251 void ChromotingJniRuntime::OnConnectionState(
252     protocol::ConnectionToHost::State state,
253     protocol::ErrorCode error) {
254   DCHECK(ui_task_runner_->BelongsToCurrentThread());
255
256   JNIEnv* env = base::android::AttachCurrentThread();
257   Java_JniInterface_onConnectionState(env, state, error);
258 }
259
260 void ChromotingJniRuntime::DisplayAuthenticationPrompt(bool pairing_supported) {
261   DCHECK(ui_task_runner_->BelongsToCurrentThread());
262
263   JNIEnv* env = base::android::AttachCurrentThread();
264   Java_JniInterface_displayAuthenticationPrompt(env, pairing_supported);
265 }
266
267 void ChromotingJniRuntime::CommitPairingCredentials(const std::string& host,
268                                                     const std::string& id,
269                                                     const std::string& secret) {
270   DCHECK(ui_task_runner_->BelongsToCurrentThread());
271
272   JNIEnv* env = base::android::AttachCurrentThread();
273   ScopedJavaLocalRef<jstring> j_host = ConvertUTF8ToJavaString(env, host);
274   ScopedJavaLocalRef<jstring> j_id = ConvertUTF8ToJavaString(env, id);
275   ScopedJavaLocalRef<jstring> j_secret = ConvertUTF8ToJavaString(env,secret);
276
277   Java_JniInterface_commitPairingCredentials(
278       env, j_host.obj(), j_id.obj(), j_secret.obj());
279 }
280
281 void ChromotingJniRuntime::FetchThirdPartyToken(const GURL& token_url,
282                                                 const std::string& client_id,
283                                                 const std::string& scope) {
284   DCHECK(ui_task_runner_->BelongsToCurrentThread());
285   JNIEnv* env = base::android::AttachCurrentThread();
286
287   ScopedJavaLocalRef<jstring> j_url =
288       ConvertUTF8ToJavaString(env, token_url.spec());
289   ScopedJavaLocalRef<jstring> j_client_id =
290       ConvertUTF8ToJavaString(env, client_id);
291   ScopedJavaLocalRef<jstring> j_scope = ConvertUTF8ToJavaString(env, scope);
292
293   Java_JniInterface_fetchThirdPartyToken(
294       env, j_url.obj(), j_client_id.obj(), j_scope.obj());
295 }
296
297 void ChromotingJniRuntime::SetCapabilities(const std::string& capabilities) {
298   DCHECK(ui_task_runner_->BelongsToCurrentThread());
299   JNIEnv* env = base::android::AttachCurrentThread();
300
301   ScopedJavaLocalRef<jstring> j_cap =
302       ConvertUTF8ToJavaString(env, capabilities);
303
304   Java_JniInterface_setCapabilities(env, j_cap.obj());
305 }
306
307 void ChromotingJniRuntime::HandleExtensionMessage(const std::string& type,
308                                                   const std::string& message) {
309   DCHECK(ui_task_runner_->BelongsToCurrentThread());
310   JNIEnv* env = base::android::AttachCurrentThread();
311
312   ScopedJavaLocalRef<jstring> j_type = ConvertUTF8ToJavaString(env, type);
313   ScopedJavaLocalRef<jstring> j_message = ConvertUTF8ToJavaString(env, message);
314
315   Java_JniInterface_handleExtensionMessage(env, j_type.obj(), j_message.obj());
316 }
317
318 base::android::ScopedJavaLocalRef<jobject> ChromotingJniRuntime::NewBitmap(
319     webrtc::DesktopSize size) {
320   JNIEnv* env = base::android::AttachCurrentThread();
321   return Java_JniInterface_newBitmap(env, size.width(), size.height());
322 }
323
324 void ChromotingJniRuntime::UpdateFrameBitmap(jobject bitmap) {
325   DCHECK(display_task_runner_->BelongsToCurrentThread());
326
327   JNIEnv* env = base::android::AttachCurrentThread();
328   Java_JniInterface_setVideoFrame(env, bitmap);
329 }
330
331 void ChromotingJniRuntime::UpdateCursorShape(
332     const protocol::CursorShapeInfo& cursor_shape) {
333   DCHECK(display_task_runner_->BelongsToCurrentThread());
334
335   // const_cast<> is safe as long as the Java updateCursorShape() method copies
336   // the data out of the buffer without mutating it, and doesn't keep any
337   // reference to the buffer afterwards. Unfortunately, there seems to be no way
338   // to create a read-only ByteBuffer from a pointer-to-const.
339   char* data = string_as_array(const_cast<std::string*>(&cursor_shape.data()));
340   int cursor_total_bytes =
341       cursor_shape.width() * cursor_shape.height() * kBytesPerPixel;
342
343   JNIEnv* env = base::android::AttachCurrentThread();
344   base::android::ScopedJavaLocalRef<jobject> buffer(env,
345       env->NewDirectByteBuffer(data, cursor_total_bytes));
346   Java_JniInterface_updateCursorShape(env,
347                                       cursor_shape.width(),
348                                       cursor_shape.height(),
349                                       cursor_shape.hotspot_x(),
350                                       cursor_shape.hotspot_y(),
351                                       buffer.obj());
352 }
353
354 void ChromotingJniRuntime::RedrawCanvas() {
355   DCHECK(display_task_runner_->BelongsToCurrentThread());
356
357   JNIEnv* env = base::android::AttachCurrentThread();
358   Java_JniInterface_redrawGraphicsInternal(env);
359 }
360
361 void ChromotingJniRuntime::DetachFromVmAndSignal(base::WaitableEvent* waiter) {
362   base::android::DetachFromVM();
363   waiter->Signal();
364 }
365 }  // namespace remoting