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