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/base_jni_registrar.h" | |
8 #include "base/android/jni_android.h" | 7 #include "base/android/jni_android.h" |
| 8 #include "base/android/jni_string.h" |
9 #include "base/android/scoped_java_ref.h" | 9 #include "base/android/scoped_java_ref.h" |
| 10 #include "base/basictypes.h" |
| 11 #include "base/command_line.h" |
10 #include "base/memory/singleton.h" | 12 #include "base/memory/singleton.h" |
11 #include "base/stl_util.h" | 13 #include "base/stl_util.h" |
12 #include "base/synchronization/waitable_event.h" | 14 #include "base/synchronization/waitable_event.h" |
| 15 #include "google_apis/google_api_keys.h" |
| 16 #include "jni/JniInterface_jni.h" |
13 #include "media/base/yuv_convert.h" | 17 #include "media/base/yuv_convert.h" |
14 #include "net/android/net_jni_registrar.h" | |
15 #include "remoting/base/url_request_context.h" | 18 #include "remoting/base/url_request_context.h" |
16 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" | 19 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" |
17 | 20 |
| 21 using base::android::ConvertJavaStringToUTF8; |
| 22 |
18 namespace { | 23 namespace { |
19 | 24 |
20 // Class and package name of the Java class supporting the methods we call. | |
21 const char* const kJavaClass = "org/chromium/chromoting/jni/JniInterface"; | |
22 | |
23 const int kBytesPerPixel = 4; | 25 const int kBytesPerPixel = 4; |
24 | 26 |
25 } // namespace | 27 } // namespace |
26 | 28 |
27 namespace remoting { | 29 namespace remoting { |
28 | 30 |
| 31 bool RegisterJni(JNIEnv* env) { |
| 32 return remoting::RegisterNativesImpl(env); |
| 33 } |
| 34 |
| 35 // Implementation of stubs defined in JniInterface_jni.h. These are the entry |
| 36 // points for JNI calls from Java into C++. |
| 37 |
| 38 static void LoadNative(JNIEnv* env, jclass clazz, jobject context) { |
| 39 base::android::ScopedJavaLocalRef<jobject> context_activity(env, context); |
| 40 base::android::InitApplicationContext(context_activity); |
| 41 |
| 42 // The google_apis functions check the command-line arguments to make sure no |
| 43 // runtime API keys have been specified by the environment. Unfortunately, we |
| 44 // neither launch Chromium nor have a command line, so we need to prevent |
| 45 // them from DCHECKing out when they go looking. |
| 46 CommandLine::Init(0, NULL); |
| 47 |
| 48 // Create the singleton now so that the Chromoting threads will be set up. |
| 49 remoting::ChromotingJniRuntime::GetInstance(); |
| 50 } |
| 51 |
| 52 static jstring GetApiKey(JNIEnv* env, jclass clazz) { |
| 53 return env->NewStringUTF(google_apis::GetAPIKey().c_str()); |
| 54 } |
| 55 |
| 56 static jstring GetClientId(JNIEnv* env, jclass clazz) { |
| 57 return env->NewStringUTF( |
| 58 google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING).c_str()); |
| 59 } |
| 60 |
| 61 static jstring GetClientSecret(JNIEnv* env, jclass clazz) { |
| 62 return env->NewStringUTF( |
| 63 google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING).c_str()); |
| 64 } |
| 65 |
| 66 static void Connect(JNIEnv* env, |
| 67 jclass clazz, |
| 68 jstring username, |
| 69 jstring authToken, |
| 70 jstring hostJid, |
| 71 jstring hostId, |
| 72 jstring hostPubkey, |
| 73 jstring pairId, |
| 74 jstring pairSecret) { |
| 75 remoting::ChromotingJniRuntime::GetInstance()->ConnectToHost( |
| 76 ConvertJavaStringToUTF8(env, username).c_str(), |
| 77 ConvertJavaStringToUTF8(env, authToken).c_str(), |
| 78 ConvertJavaStringToUTF8(env, hostJid).c_str(), |
| 79 ConvertJavaStringToUTF8(env, hostId).c_str(), |
| 80 ConvertJavaStringToUTF8(env, hostPubkey).c_str(), |
| 81 ConvertJavaStringToUTF8(env, pairId).c_str(), |
| 82 ConvertJavaStringToUTF8(env, pairSecret).c_str()); |
| 83 } |
| 84 |
| 85 static void Disconnect(JNIEnv* env, jclass clazz) { |
| 86 remoting::ChromotingJniRuntime::GetInstance()->DisconnectFromHost(); |
| 87 } |
| 88 |
| 89 static void AuthenticationResponse(JNIEnv* env, |
| 90 jclass clazz, |
| 91 jstring pin, |
| 92 jboolean createPair) { |
| 93 remoting::ChromotingJniRuntime::GetInstance()->session()->ProvideSecret( |
| 94 ConvertJavaStringToUTF8(env, pin).c_str(), createPair); |
| 95 } |
| 96 |
| 97 static void ScheduleRedraw(JNIEnv* env, jclass clazz) { |
| 98 remoting::ChromotingJniRuntime::GetInstance()->session()->RedrawDesktop(); |
| 99 } |
| 100 |
| 101 static void MouseAction(JNIEnv* env, |
| 102 jclass clazz, |
| 103 jint x, |
| 104 jint y, |
| 105 jint whichButton, |
| 106 jboolean buttonDown) { |
| 107 // Button must be within the bounds of the MouseEvent_MouseButton enum. |
| 108 DCHECK(whichButton >= 0 && whichButton < 5); |
| 109 |
| 110 remoting::ChromotingJniRuntime::GetInstance()->session()->PerformMouseAction( |
| 111 x, |
| 112 y, |
| 113 static_cast<remoting::protocol::MouseEvent_MouseButton>(whichButton), |
| 114 buttonDown); |
| 115 } |
| 116 |
| 117 static void KeyboardAction(JNIEnv* env, |
| 118 jclass clazz, |
| 119 jint keyCode, |
| 120 jboolean keyDown) { |
| 121 remoting::ChromotingJniRuntime::GetInstance() |
| 122 ->session() |
| 123 ->PerformKeyboardAction(keyCode, keyDown); |
| 124 } |
| 125 |
| 126 // ChromotingJniRuntime implementation. |
| 127 |
29 // static | 128 // static |
30 ChromotingJniRuntime* ChromotingJniRuntime::GetInstance() { | 129 ChromotingJniRuntime* ChromotingJniRuntime::GetInstance() { |
31 return Singleton<ChromotingJniRuntime>::get(); | 130 return Singleton<ChromotingJniRuntime>::get(); |
32 } | 131 } |
33 | 132 |
34 ChromotingJniRuntime::ChromotingJniRuntime() { | 133 ChromotingJniRuntime::ChromotingJniRuntime() { |
35 // Obtain a reference to the Java environment. (Future calls to this function | |
36 // made from the same thread return the same stored reference instead of | |
37 // repeating the work of attaching to the JVM.) | |
38 JNIEnv* env = base::android::AttachCurrentThread(); | |
39 | |
40 // The base and networks stacks must be registered with JNI in order to work | |
41 // on Android. An AtExitManager cleans this up at process exit. | |
42 at_exit_manager_.reset(new base::AtExitManager()); | 134 at_exit_manager_.reset(new base::AtExitManager()); |
43 base::android::RegisterJni(env); | |
44 net::android::RegisterJni(env); | |
45 | 135 |
46 // On Android, the UI thread is managed by Java, so we need to attach and | 136 // On Android, the UI thread is managed by Java, so we need to attach and |
47 // start a special type of message loop to allow Chromium code to run tasks. | 137 // start a special type of message loop to allow Chromium code to run tasks. |
48 LOG(INFO) << "Starting main message loop"; | 138 LOG(INFO) << "Starting main message loop"; |
49 ui_loop_.reset(new base::MessageLoopForUI()); | 139 ui_loop_.reset(new base::MessageLoopForUI()); |
50 ui_loop_->Start(); | 140 ui_loop_->Start(); |
51 | 141 |
52 LOG(INFO) << "Spawning additional threads"; | 142 LOG(INFO) << "Spawning additional threads"; |
53 // TODO(solb) Stop pretending to control the managed UI thread's lifetime. | 143 // TODO(solb) Stop pretending to control the managed UI thread's lifetime. |
54 ui_task_runner_ = new AutoThreadTaskRunner(ui_loop_->message_loop_proxy(), | 144 ui_task_runner_ = new AutoThreadTaskRunner(ui_loop_->message_loop_proxy(), |
55 base::MessageLoop::QuitClosure()); | 145 base::MessageLoop::QuitClosure()); |
56 network_task_runner_ = AutoThread::CreateWithType("native_net", | 146 network_task_runner_ = AutoThread::CreateWithType("native_net", |
57 ui_task_runner_, | 147 ui_task_runner_, |
58 base::MessageLoop::TYPE_IO); | 148 base::MessageLoop::TYPE_IO); |
59 display_task_runner_ = AutoThread::Create("native_disp", | 149 display_task_runner_ = AutoThread::Create("native_disp", |
60 ui_task_runner_); | 150 ui_task_runner_); |
61 | 151 |
62 url_requester_ = new URLRequestContextGetter(network_task_runner_); | 152 url_requester_ = new URLRequestContextGetter(network_task_runner_); |
63 | 153 |
64 // Allows later decoding of video frames. | 154 // Allows later decoding of video frames. |
65 media::InitializeCPUSpecificYUVConversions(); | 155 media::InitializeCPUSpecificYUVConversions(); |
66 | |
67 class_ = static_cast<jclass>(env->NewGlobalRef(env->FindClass(kJavaClass))); | |
68 } | 156 } |
69 | 157 |
70 ChromotingJniRuntime::~ChromotingJniRuntime() { | 158 ChromotingJniRuntime::~ChromotingJniRuntime() { |
71 // The singleton should only ever be destroyed on the main thread. | 159 // The singleton should only ever be destroyed on the main thread. |
72 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 160 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
73 | 161 |
74 // The session must be shut down first, since it depends on our other | 162 // The session must be shut down first, since it depends on our other |
75 // components' still being alive. | 163 // components' still being alive. |
76 DisconnectFromHost(); | 164 DisconnectFromHost(); |
77 | 165 |
78 JNIEnv* env = base::android::AttachCurrentThread(); | |
79 env->DeleteGlobalRef(class_); | |
80 | |
81 base::WaitableEvent done_event(false, false); | 166 base::WaitableEvent done_event(false, false); |
82 network_task_runner_->PostTask(FROM_HERE, base::Bind( | 167 network_task_runner_->PostTask(FROM_HERE, base::Bind( |
83 &ChromotingJniRuntime::DetachFromVmAndSignal, | 168 &ChromotingJniRuntime::DetachFromVmAndSignal, |
84 base::Unretained(this), | 169 base::Unretained(this), |
85 &done_event)); | 170 &done_event)); |
86 done_event.Wait(); | 171 done_event.Wait(); |
87 display_task_runner_->PostTask(FROM_HERE, base::Bind( | 172 display_task_runner_->PostTask(FROM_HERE, base::Bind( |
88 &ChromotingJniRuntime::DetachFromVmAndSignal, | 173 &ChromotingJniRuntime::DetachFromVmAndSignal, |
89 base::Unretained(this), | 174 base::Unretained(this), |
90 &done_event)); | 175 &done_event)); |
(...skipping 27 matching lines...) Expand all Loading... |
118 session_ = NULL; | 203 session_ = NULL; |
119 } | 204 } |
120 } | 205 } |
121 | 206 |
122 void ChromotingJniRuntime::ReportConnectionStatus( | 207 void ChromotingJniRuntime::ReportConnectionStatus( |
123 protocol::ConnectionToHost::State state, | 208 protocol::ConnectionToHost::State state, |
124 protocol::ErrorCode error) { | 209 protocol::ErrorCode error) { |
125 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 210 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
126 | 211 |
127 JNIEnv* env = base::android::AttachCurrentThread(); | 212 JNIEnv* env = base::android::AttachCurrentThread(); |
128 env->CallStaticVoidMethod( | 213 Java_JniInterface_reportConnectionStatus(env, state, error); |
129 class_, | |
130 env->GetStaticMethodID(class_, "reportConnectionStatus", "(II)V"), | |
131 state, | |
132 error); | |
133 } | 214 } |
134 | 215 |
135 void ChromotingJniRuntime::DisplayAuthenticationPrompt(bool pairing_supported) { | 216 void ChromotingJniRuntime::DisplayAuthenticationPrompt(bool pairing_supported) { |
136 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 217 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
137 | 218 |
138 JNIEnv* env = base::android::AttachCurrentThread(); | 219 JNIEnv* env = base::android::AttachCurrentThread(); |
139 env->CallStaticVoidMethod( | 220 Java_JniInterface_displayAuthenticationPrompt(env, pairing_supported); |
140 class_, | |
141 env->GetStaticMethodID(class_, "displayAuthenticationPrompt", "(Z)V"), | |
142 pairing_supported); | |
143 } | 221 } |
144 | 222 |
145 void ChromotingJniRuntime::CommitPairingCredentials(const std::string& host, | 223 void ChromotingJniRuntime::CommitPairingCredentials(const std::string& host, |
146 const std::string& id, | 224 const std::string& id, |
147 const std::string& secret) { | 225 const std::string& secret) { |
148 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 226 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
149 | 227 |
150 JNIEnv* env = base::android::AttachCurrentThread(); | 228 JNIEnv* env = base::android::AttachCurrentThread(); |
151 jstring host_jstr = env->NewStringUTF(host.c_str()); | 229 jstring host_jstr = env->NewStringUTF(host.c_str()); |
152 jbyteArray id_arr = env->NewByteArray(id.size()); | 230 jbyteArray id_arr = env->NewByteArray(id.size()); |
153 env->SetByteArrayRegion(id_arr, 0, id.size(), | 231 env->SetByteArrayRegion(id_arr, 0, id.size(), |
154 reinterpret_cast<const jbyte*>(id.c_str())); | 232 reinterpret_cast<const jbyte*>(id.c_str())); |
155 jbyteArray secret_arr = env->NewByteArray(secret.size()); | 233 jbyteArray secret_arr = env->NewByteArray(secret.size()); |
156 env->SetByteArrayRegion(secret_arr, 0, secret.size(), | 234 env->SetByteArrayRegion(secret_arr, 0, secret.size(), |
157 reinterpret_cast<const jbyte*>(secret.c_str())); | 235 reinterpret_cast<const jbyte*>(secret.c_str())); |
158 | 236 |
159 env->CallStaticVoidMethod( | 237 Java_JniInterface_commitPairingCredentials(env, host_jstr, id_arr, |
160 class_, | 238 secret_arr); |
161 env->GetStaticMethodID( | |
162 class_, | |
163 "commitPairingCredentials", | |
164 "(Ljava/lang/String;[B[B)V"), | |
165 host_jstr, | |
166 id_arr, | |
167 secret_arr); | |
168 | 239 |
169 // Because we passed them as arguments, their corresponding Java objects were | 240 // Because we passed them as arguments, their corresponding Java objects were |
170 // GCd as soon as the managed method returned, so we mustn't release it here. | 241 // GCd as soon as the managed method returned, so we mustn't release it here. |
171 } | 242 } |
172 | 243 |
173 base::android::ScopedJavaLocalRef<jobject> ChromotingJniRuntime::NewBitmap( | 244 base::android::ScopedJavaLocalRef<jobject> ChromotingJniRuntime::NewBitmap( |
174 webrtc::DesktopSize size) { | 245 webrtc::DesktopSize size) { |
175 JNIEnv* env = base::android::AttachCurrentThread(); | 246 JNIEnv* env = base::android::AttachCurrentThread(); |
176 | 247 return Java_JniInterface_newBitmap(env, size.width(), size.height()); |
177 jobject bitmap = env->CallStaticObjectMethod( | |
178 class_, | |
179 env->GetStaticMethodID( | |
180 class_, | |
181 "newBitmap", | |
182 "(II)Landroid/graphics/Bitmap;"), | |
183 size.width(), | |
184 size.height()); | |
185 return base::android::ScopedJavaLocalRef<jobject>(env, bitmap); | |
186 } | 248 } |
187 | 249 |
188 void ChromotingJniRuntime::UpdateFrameBitmap(jobject bitmap) { | 250 void ChromotingJniRuntime::UpdateFrameBitmap(jobject bitmap) { |
189 DCHECK(display_task_runner_->BelongsToCurrentThread()); | 251 DCHECK(display_task_runner_->BelongsToCurrentThread()); |
190 | 252 |
191 JNIEnv* env = base::android::AttachCurrentThread(); | 253 JNIEnv* env = base::android::AttachCurrentThread(); |
192 | 254 Java_JniInterface_setVideoFrame(env, bitmap); |
193 env->CallStaticVoidMethod( | |
194 class_, | |
195 env->GetStaticMethodID( | |
196 class_, | |
197 "setVideoFrame", | |
198 "(Landroid/graphics/Bitmap;)V"), | |
199 bitmap); | |
200 } | 255 } |
201 | 256 |
202 void ChromotingJniRuntime::UpdateCursorShape( | 257 void ChromotingJniRuntime::UpdateCursorShape( |
203 const protocol::CursorShapeInfo& cursor_shape) { | 258 const protocol::CursorShapeInfo& cursor_shape) { |
204 DCHECK(display_task_runner_->BelongsToCurrentThread()); | 259 DCHECK(display_task_runner_->BelongsToCurrentThread()); |
205 | 260 |
206 // const_cast<> is safe as long as the Java updateCursorShape() method copies | 261 // const_cast<> is safe as long as the Java updateCursorShape() method copies |
207 // the data out of the buffer without mutating it, and doesn't keep any | 262 // the data out of the buffer without mutating it, and doesn't keep any |
208 // reference to the buffer afterwards. Unfortunately, there seems to be no way | 263 // reference to the buffer afterwards. Unfortunately, there seems to be no way |
209 // to create a read-only ByteBuffer from a pointer-to-const. | 264 // to create a read-only ByteBuffer from a pointer-to-const. |
210 char* data = string_as_array(const_cast<std::string*>(&cursor_shape.data())); | 265 char* data = string_as_array(const_cast<std::string*>(&cursor_shape.data())); |
211 int cursor_total_bytes = | 266 int cursor_total_bytes = |
212 cursor_shape.width() * cursor_shape.height() * kBytesPerPixel; | 267 cursor_shape.width() * cursor_shape.height() * kBytesPerPixel; |
213 | 268 |
214 JNIEnv* env = base::android::AttachCurrentThread(); | 269 JNIEnv* env = base::android::AttachCurrentThread(); |
215 base::android::ScopedJavaLocalRef<jobject> buffer(env, | 270 base::android::ScopedJavaLocalRef<jobject> buffer(env, |
216 env->NewDirectByteBuffer(data, cursor_total_bytes)); | 271 env->NewDirectByteBuffer(data, cursor_total_bytes)); |
217 env->CallStaticVoidMethod( | 272 Java_JniInterface_updateCursorShape(env, |
218 class_, | 273 cursor_shape.width(), |
219 env->GetStaticMethodID( | 274 cursor_shape.height(), |
220 class_, | 275 cursor_shape.hotspot_x(), |
221 "updateCursorShape", | 276 cursor_shape.hotspot_y(), |
222 "(IIIILjava/nio/ByteBuffer;)V"), | 277 buffer.obj()); |
223 cursor_shape.width(), | |
224 cursor_shape.height(), | |
225 cursor_shape.hotspot_x(), | |
226 cursor_shape.hotspot_y(), | |
227 buffer.obj()); | |
228 } | 278 } |
229 | 279 |
230 void ChromotingJniRuntime::RedrawCanvas() { | 280 void ChromotingJniRuntime::RedrawCanvas() { |
231 DCHECK(display_task_runner_->BelongsToCurrentThread()); | 281 DCHECK(display_task_runner_->BelongsToCurrentThread()); |
232 | 282 |
233 JNIEnv* env = base::android::AttachCurrentThread(); | 283 JNIEnv* env = base::android::AttachCurrentThread(); |
234 env->CallStaticVoidMethod( | 284 Java_JniInterface_redrawGraphicsInternal(env); |
235 class_, | |
236 env->GetStaticMethodID(class_, "redrawGraphicsInternal", "()V")); | |
237 } | 285 } |
238 | 286 |
239 void ChromotingJniRuntime::DetachFromVmAndSignal(base::WaitableEvent* waiter) { | 287 void ChromotingJniRuntime::DetachFromVmAndSignal(base::WaitableEvent* waiter) { |
240 base::android::DetachFromVM(); | 288 base::android::DetachFromVM(); |
241 waiter->Signal(); | 289 waiter->Signal(); |
242 } | 290 } |
243 | 291 |
244 } // namespace remoting | 292 } // namespace remoting |
OLD | NEW |