Chromium Code Reviews| 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 |