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