Upstream version 7.36.149.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.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   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   remoting::ChromotingJniRuntime::GetInstance()->ConnectToHost(
82       ConvertJavaStringToUTF8(env, username).c_str(),
83       ConvertJavaStringToUTF8(env, authToken).c_str(),
84       ConvertJavaStringToUTF8(env, hostJid).c_str(),
85       ConvertJavaStringToUTF8(env, hostId).c_str(),
86       ConvertJavaStringToUTF8(env, hostPubkey).c_str(),
87       ConvertJavaStringToUTF8(env, pairId).c_str(),
88       ConvertJavaStringToUTF8(env, pairSecret).c_str());
89 }
90
91 static void Disconnect(JNIEnv* env, jclass clazz) {
92   remoting::ChromotingJniRuntime::GetInstance()->DisconnectFromHost();
93 }
94
95 static void AuthenticationResponse(JNIEnv* env,
96                                    jclass clazz,
97                                    jstring pin,
98                                    jboolean createPair,
99                                    jstring deviceName) {
100   remoting::ChromotingJniRuntime::GetInstance()->session()->ProvideSecret(
101       ConvertJavaStringToUTF8(env, pin).c_str(), createPair,
102       ConvertJavaStringToUTF8(env, deviceName));
103 }
104
105 static void ScheduleRedraw(JNIEnv* env, jclass clazz) {
106   remoting::ChromotingJniRuntime::GetInstance()->session()->RedrawDesktop();
107 }
108
109 static void SendMouseEvent(JNIEnv* env,
110                            jclass clazz,
111                            jint x,
112                            jint y,
113                            jint whichButton,
114                            jboolean buttonDown) {
115   // Button must be within the bounds of the MouseEvent_MouseButton enum.
116   DCHECK(whichButton >= 0 && whichButton < 5);
117
118   remoting::ChromotingJniRuntime::GetInstance()->session()->SendMouseEvent(
119       x, y,
120       static_cast<remoting::protocol::MouseEvent_MouseButton>(whichButton),
121       buttonDown);
122 }
123
124 static void SendMouseWheelEvent(JNIEnv* env,
125                                 jclass clazz,
126                                 jint delta_x,
127                                 jint delta_y) {
128   remoting::ChromotingJniRuntime::GetInstance()->session()->SendMouseWheelEvent(
129       delta_x, delta_y);
130 }
131
132 static void SendKeyEvent(JNIEnv* env,
133                          jclass clazz,
134                          jint keyCode,
135                          jboolean keyDown) {
136   remoting::ChromotingJniRuntime::GetInstance()->session()->SendKeyEvent(
137       keyCode, keyDown);
138 }
139
140 static void SendTextEvent(JNIEnv* env,
141                           jclass clazz,
142                           jstring text) {
143   remoting::ChromotingJniRuntime::GetInstance()->session()->SendTextEvent(
144       ConvertJavaStringToUTF8(env, text));
145 }
146
147 // ChromotingJniRuntime implementation.
148
149 // static
150 ChromotingJniRuntime* ChromotingJniRuntime::GetInstance() {
151   return Singleton<ChromotingJniRuntime>::get();
152 }
153
154 ChromotingJniRuntime::ChromotingJniRuntime() {
155   at_exit_manager_.reset(new base::AtExitManager());
156
157   // On Android, the UI thread is managed by Java, so we need to attach and
158   // start a special type of message loop to allow Chromium code to run tasks.
159   ui_loop_.reset(new base::MessageLoopForUI());
160   ui_loop_->Start();
161
162   // TODO(solb) Stop pretending to control the managed UI thread's lifetime.
163   ui_task_runner_ = new AutoThreadTaskRunner(ui_loop_->message_loop_proxy(),
164                                              base::MessageLoop::QuitClosure());
165   network_task_runner_ = AutoThread::CreateWithType("native_net",
166                                                     ui_task_runner_,
167                                                     base::MessageLoop::TYPE_IO);
168   display_task_runner_ = AutoThread::Create("native_disp",
169                                             ui_task_runner_);
170
171   url_requester_ = new URLRequestContextGetter(network_task_runner_);
172
173   // Allows later decoding of video frames.
174   media::InitializeCPUSpecificYUVConversions();
175 }
176
177 ChromotingJniRuntime::~ChromotingJniRuntime() {
178   // The singleton should only ever be destroyed on the main thread.
179   DCHECK(ui_task_runner_->BelongsToCurrentThread());
180
181   // The session must be shut down first, since it depends on our other
182   // components' still being alive.
183   DisconnectFromHost();
184
185   base::WaitableEvent done_event(false, false);
186   network_task_runner_->PostTask(FROM_HERE, base::Bind(
187       &ChromotingJniRuntime::DetachFromVmAndSignal,
188       base::Unretained(this),
189       &done_event));
190   done_event.Wait();
191   display_task_runner_->PostTask(FROM_HERE, base::Bind(
192       &ChromotingJniRuntime::DetachFromVmAndSignal,
193       base::Unretained(this),
194       &done_event));
195   done_event.Wait();
196   base::android::DetachFromVM();
197 }
198
199 void ChromotingJniRuntime::ConnectToHost(const char* username,
200                                   const char* auth_token,
201                                   const char* host_jid,
202                                   const char* host_id,
203                                   const char* host_pubkey,
204                                   const char* pairing_id,
205                                   const char* pairing_secret) {
206   DCHECK(ui_task_runner_->BelongsToCurrentThread());
207   DCHECK(!session_);
208   session_ = new ChromotingJniInstance(this,
209                                        username,
210                                        auth_token,
211                                        host_jid,
212                                        host_id,
213                                        host_pubkey,
214                                        pairing_id,
215                                        pairing_secret);
216 }
217
218 void ChromotingJniRuntime::DisconnectFromHost() {
219   DCHECK(ui_task_runner_->BelongsToCurrentThread());
220   if (session_) {
221     session_->Cleanup();
222     session_ = NULL;
223   }
224 }
225
226 void ChromotingJniRuntime::ReportConnectionStatus(
227     protocol::ConnectionToHost::State state,
228     protocol::ErrorCode error) {
229   DCHECK(ui_task_runner_->BelongsToCurrentThread());
230
231   JNIEnv* env = base::android::AttachCurrentThread();
232   Java_JniInterface_reportConnectionStatus(env, state, error);
233 }
234
235 void ChromotingJniRuntime::DisplayAuthenticationPrompt(bool pairing_supported) {
236   DCHECK(ui_task_runner_->BelongsToCurrentThread());
237
238   JNIEnv* env = base::android::AttachCurrentThread();
239   Java_JniInterface_displayAuthenticationPrompt(env, pairing_supported);
240 }
241
242 void ChromotingJniRuntime::CommitPairingCredentials(const std::string& host,
243                                                     const std::string& id,
244                                                     const std::string& secret) {
245   DCHECK(ui_task_runner_->BelongsToCurrentThread());
246
247   JNIEnv* env = base::android::AttachCurrentThread();
248   ScopedJavaLocalRef<jstring> j_host = ConvertUTF8ToJavaString(env, host);
249   ScopedJavaLocalRef<jbyteArray> j_id = ToJavaByteArray(
250       env, reinterpret_cast<const uint8*>(id.data()), id.size());
251   ScopedJavaLocalRef<jbyteArray> j_secret = ToJavaByteArray(
252       env, reinterpret_cast<const uint8*>(secret.data()), secret.size());
253
254   Java_JniInterface_commitPairingCredentials(
255       env, j_host.obj(), j_id.obj(), j_secret.obj());
256 }
257
258 base::android::ScopedJavaLocalRef<jobject> ChromotingJniRuntime::NewBitmap(
259     webrtc::DesktopSize size) {
260   JNIEnv* env = base::android::AttachCurrentThread();
261   return Java_JniInterface_newBitmap(env, size.width(), size.height());
262 }
263
264 void ChromotingJniRuntime::UpdateFrameBitmap(jobject bitmap) {
265   DCHECK(display_task_runner_->BelongsToCurrentThread());
266
267   JNIEnv* env = base::android::AttachCurrentThread();
268   Java_JniInterface_setVideoFrame(env, bitmap);
269 }
270
271 void ChromotingJniRuntime::UpdateCursorShape(
272     const protocol::CursorShapeInfo& cursor_shape) {
273   DCHECK(display_task_runner_->BelongsToCurrentThread());
274
275   // const_cast<> is safe as long as the Java updateCursorShape() method copies
276   // the data out of the buffer without mutating it, and doesn't keep any
277   // reference to the buffer afterwards. Unfortunately, there seems to be no way
278   // to create a read-only ByteBuffer from a pointer-to-const.
279   char* data = string_as_array(const_cast<std::string*>(&cursor_shape.data()));
280   int cursor_total_bytes =
281       cursor_shape.width() * cursor_shape.height() * kBytesPerPixel;
282
283   JNIEnv* env = base::android::AttachCurrentThread();
284   base::android::ScopedJavaLocalRef<jobject> buffer(env,
285       env->NewDirectByteBuffer(data, cursor_total_bytes));
286   Java_JniInterface_updateCursorShape(env,
287                                       cursor_shape.width(),
288                                       cursor_shape.height(),
289                                       cursor_shape.hotspot_x(),
290                                       cursor_shape.hotspot_y(),
291                                       buffer.obj());
292 }
293
294 void ChromotingJniRuntime::RedrawCanvas() {
295   DCHECK(display_task_runner_->BelongsToCurrentThread());
296
297   JNIEnv* env = base::android::AttachCurrentThread();
298   Java_JniInterface_redrawGraphicsInternal(env);
299 }
300
301 void ChromotingJniRuntime::DetachFromVmAndSignal(base::WaitableEvent* waiter) {
302   base::android::DetachFromVM();
303   waiter->Signal();
304 }
305
306 }  // namespace remoting