| 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 |