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 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 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 55 static jstring GetApiKey(JNIEnv* env, jclass clazz) { 56 return env->NewStringUTF(google_apis::GetAPIKey().c_str()); 57 } 58 59 static jstring GetClientId(JNIEnv* env, jclass clazz) { 60 return env->NewStringUTF( 61 google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING).c_str()); 62 } 63 64 static jstring GetClientSecret(JNIEnv* env, jclass clazz) { 65 return env->NewStringUTF( 66 google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING).c_str()); 67 } 68 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 88 static void Disconnect(JNIEnv* env, jclass clazz) { 89 remoting::ChromotingJniRuntime::GetInstance()->DisconnectFromHost(); 90 } 91 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 100 static void ScheduleRedraw(JNIEnv* env, jclass clazz) { 101 remoting::ChromotingJniRuntime::GetInstance()->session()->RedrawDesktop(); 102 } 103 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 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 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 141 ChromotingJniRuntime* ChromotingJniRuntime::GetInstance() { 142 return Singleton<ChromotingJniRuntime>::get(); 143 } 144 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 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 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 209 void ChromotingJniRuntime::DisconnectFromHost() { 210 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 211 if (session_) { 212 session_->Cleanup(); 213 session_ = NULL; 214 } 215 } 216 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 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 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 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 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 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 285 void ChromotingJniRuntime::RedrawCanvas() { 286 DCHECK(display_task_runner_->BelongsToCurrentThread()); 287 288 JNIEnv* env = base::android::AttachCurrentThread(); 289 Java_JniInterface_redrawGraphicsInternal(env); 290 } 291 292 void ChromotingJniRuntime::DetachFromVmAndSignal(base::WaitableEvent* waiter) { 293 base::android::DetachFromVM(); 294 waiter->Signal(); 295 } 296 297 } // namespace remoting 298