OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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/jni_client.h" | 5 #include "remoting/client/jni/jni_client.h" |
6 | 6 |
| 7 #include "base/android/jni_android.h" |
| 8 #include "base/android/jni_string.h" |
| 9 #include "base/logging.h" |
7 #include "jni/Client_jni.h" | 10 #include "jni/Client_jni.h" |
| 11 #include "remoting/client/jni/chromoting_jni_instance.h" |
| 12 #include "remoting/client/jni/chromoting_jni_runtime.h" |
| 13 #include "remoting/client/jni/jni_touch_event_data.h" |
| 14 |
| 15 using base::android::ConvertJavaStringToUTF8; |
| 16 using base::android::ConvertUTF8ToJavaString; |
| 17 |
| 18 namespace { |
| 19 |
| 20 const int kBytesPerPixel = 4; |
| 21 } |
8 | 22 |
9 namespace remoting { | 23 namespace remoting { |
10 | 24 |
11 JniClient::JniClient() { | 25 JniClient::JniClient(jobject java_client) |
| 26 : java_client_(java_client), weak_factory_(this) {} |
| 27 |
| 28 JniClient::~JniClient() { |
| 29 DCHECK(runtime()->ui_task_runner()->BelongsToCurrentThread()); |
12 } | 30 } |
13 | 31 |
14 JniClient::~JniClient() { | 32 void JniClient::ConnectToHost(const std::string& username, |
| 33 const std::string& auth_token, |
| 34 const std::string& host_jid, |
| 35 const std::string& host_id, |
| 36 const std::string& host_pubkey, |
| 37 const std::string& pairing_id, |
| 38 const std::string& pairing_secret, |
| 39 const std::string& capabilities, |
| 40 const std::string& flags) { |
| 41 DCHECK(runtime()->ui_task_runner()->BelongsToCurrentThread()); |
| 42 DCHECK(!session_); |
| 43 session_ = new ChromotingJniInstance( |
| 44 runtime(), this, username, auth_token, host_jid, host_id, host_pubkey, |
| 45 pairing_id, pairing_secret, capabilities, flags); |
| 46 } |
| 47 |
| 48 void JniClient::DisconnectFromHost() { |
| 49 DCHECK(runtime()->ui_task_runner()->BelongsToCurrentThread()); |
| 50 if (session_) { |
| 51 session_->Disconnect(); |
| 52 session_ = nullptr; |
| 53 } |
| 54 } |
| 55 |
| 56 void JniClient::OnConnectionState(protocol::ConnectionToHost::State state, |
| 57 protocol::ErrorCode error) { |
| 58 DCHECK(runtime()->ui_task_runner()->BelongsToCurrentThread()); |
| 59 |
| 60 JNIEnv* env = base::android::AttachCurrentThread(); |
| 61 Java_Client_onConnectionState(env, java_client_, state, error); |
| 62 } |
| 63 |
| 64 void JniClient::DisplayAuthenticationPrompt(bool pairing_supported) { |
| 65 DCHECK(runtime()->ui_task_runner()->BelongsToCurrentThread()); |
| 66 |
| 67 JNIEnv* env = base::android::AttachCurrentThread(); |
| 68 Java_Client_displayAuthenticationPrompt(env, java_client_, pairing_supported); |
| 69 } |
| 70 |
| 71 void JniClient::CommitPairingCredentials(const std::string& host, |
| 72 const std::string& id, |
| 73 const std::string& secret) { |
| 74 DCHECK(runtime()->ui_task_runner()->BelongsToCurrentThread()); |
| 75 |
| 76 JNIEnv* env = base::android::AttachCurrentThread(); |
| 77 ScopedJavaLocalRef<jstring> j_host = ConvertUTF8ToJavaString(env, host); |
| 78 ScopedJavaLocalRef<jstring> j_id = ConvertUTF8ToJavaString(env, id); |
| 79 ScopedJavaLocalRef<jstring> j_secret = ConvertUTF8ToJavaString(env, secret); |
| 80 |
| 81 Java_Client_commitPairingCredentials(env, java_client_, j_host.obj(), |
| 82 j_id.obj(), j_secret.obj()); |
| 83 } |
| 84 |
| 85 void JniClient::FetchThirdPartyToken(const std::string& token_url, |
| 86 const std::string& client_id, |
| 87 const std::string& scope) { |
| 88 DCHECK(runtime()->ui_task_runner()->BelongsToCurrentThread()); |
| 89 JNIEnv* env = base::android::AttachCurrentThread(); |
| 90 |
| 91 ScopedJavaLocalRef<jstring> j_url = ConvertUTF8ToJavaString(env, token_url); |
| 92 ScopedJavaLocalRef<jstring> j_client_id = |
| 93 ConvertUTF8ToJavaString(env, client_id); |
| 94 ScopedJavaLocalRef<jstring> j_scope = ConvertUTF8ToJavaString(env, scope); |
| 95 |
| 96 Java_Client_fetchThirdPartyToken(env, java_client_, j_url.obj(), |
| 97 j_client_id.obj(), j_scope.obj()); |
| 98 } |
| 99 |
| 100 void JniClient::SetCapabilities(const std::string& capabilities) { |
| 101 DCHECK(runtime()->ui_task_runner()->BelongsToCurrentThread()); |
| 102 JNIEnv* env = base::android::AttachCurrentThread(); |
| 103 |
| 104 ScopedJavaLocalRef<jstring> j_cap = |
| 105 ConvertUTF8ToJavaString(env, capabilities); |
| 106 |
| 107 Java_Client_setCapabilities(env, java_client_, j_cap.obj()); |
| 108 } |
| 109 |
| 110 void JniClient::HandleExtensionMessage(const std::string& type, |
| 111 const std::string& message) { |
| 112 DCHECK(runtime()->ui_task_runner()->BelongsToCurrentThread()); |
| 113 JNIEnv* env = base::android::AttachCurrentThread(); |
| 114 |
| 115 ScopedJavaLocalRef<jstring> j_type = ConvertUTF8ToJavaString(env, type); |
| 116 ScopedJavaLocalRef<jstring> j_message = ConvertUTF8ToJavaString(env, message); |
| 117 |
| 118 Java_Client_handleExtensionMessage(env, java_client_, j_type.obj(), |
| 119 j_message.obj()); |
| 120 } |
| 121 |
| 122 base::android::ScopedJavaLocalRef<jobject> JniClient::NewBitmap(int width, |
| 123 int height) { |
| 124 JNIEnv* env = base::android::AttachCurrentThread(); |
| 125 return Java_Client_newBitmap(env, width, height); |
| 126 } |
| 127 |
| 128 void JniClient::UpdateFrameBitmap(jobject bitmap) { |
| 129 DCHECK(runtime()->display_task_runner()->BelongsToCurrentThread()); |
| 130 |
| 131 JNIEnv* env = base::android::AttachCurrentThread(); |
| 132 Java_Client_setVideoFrame(env, java_client_, bitmap); |
| 133 } |
| 134 |
| 135 void JniClient::UpdateCursorShape( |
| 136 const protocol::CursorShapeInfo& cursor_shape) { |
| 137 DCHECK(runtime()->display_task_runner()->BelongsToCurrentThread()); |
| 138 |
| 139 // const_cast<> is safe as long as the Java updateCursorShape() method copies |
| 140 // the data out of the buffer without mutating it, and doesn't keep any |
| 141 // reference to the buffer afterwards. Unfortunately, there seems to be no way |
| 142 // to create a read-only ByteBuffer from a pointer-to-const. |
| 143 char* data = string_as_array(const_cast<std::string*>(&cursor_shape.data())); |
| 144 int cursor_total_bytes = |
| 145 cursor_shape.width() * cursor_shape.height() * kBytesPerPixel; |
| 146 |
| 147 JNIEnv* env = base::android::AttachCurrentThread(); |
| 148 base::android::ScopedJavaLocalRef<jobject> buffer( |
| 149 env, env->NewDirectByteBuffer(data, cursor_total_bytes)); |
| 150 Java_Client_updateCursorShape(env, java_client_, cursor_shape.width(), |
| 151 cursor_shape.height(), cursor_shape.hotspot_x(), |
| 152 cursor_shape.hotspot_y(), buffer.obj()); |
| 153 } |
| 154 |
| 155 void JniClient::RedrawCanvas() { |
| 156 DCHECK(runtime()->display_task_runner()->BelongsToCurrentThread()); |
| 157 |
| 158 JNIEnv* env = base::android::AttachCurrentThread(); |
| 159 Java_Client_redrawGraphicsInternal(env, java_client_); |
15 } | 160 } |
16 | 161 |
17 // static | 162 // static |
18 bool JniClient::RegisterJni(JNIEnv* env) { | 163 bool JniClient::RegisterJni(JNIEnv* env) { |
19 return RegisterNativesImpl(env); | 164 return RegisterNativesImpl(env); |
20 } | 165 } |
21 | 166 |
| 167 void JniClient::Connect( |
| 168 JNIEnv* env, |
| 169 const base::android::JavaParamRef<jobject>& caller, |
| 170 const base::android::JavaParamRef<jstring>& username, |
| 171 const base::android::JavaParamRef<jstring>& authToken, |
| 172 const base::android::JavaParamRef<jstring>& hostJid, |
| 173 const base::android::JavaParamRef<jstring>& hostId, |
| 174 const base::android::JavaParamRef<jstring>& hostPubkey, |
| 175 const base::android::JavaParamRef<jstring>& pairId, |
| 176 const base::android::JavaParamRef<jstring>& pairSecret, |
| 177 const base::android::JavaParamRef<jstring>& capabilities, |
| 178 const base::android::JavaParamRef<jstring>& flags) { |
| 179 ConnectToHost(ConvertJavaStringToUTF8(env, username), |
| 180 ConvertJavaStringToUTF8(env, authToken), |
| 181 ConvertJavaStringToUTF8(env, hostJid), |
| 182 ConvertJavaStringToUTF8(env, hostId), |
| 183 ConvertJavaStringToUTF8(env, hostPubkey), |
| 184 ConvertJavaStringToUTF8(env, pairId), |
| 185 ConvertJavaStringToUTF8(env, pairSecret), |
| 186 ConvertJavaStringToUTF8(env, capabilities), |
| 187 ConvertJavaStringToUTF8(env, flags)); |
| 188 } |
| 189 |
| 190 void JniClient::Disconnect(JNIEnv* env, |
| 191 const base::android::JavaParamRef<jobject>& caller) { |
| 192 DisconnectFromHost(); |
| 193 } |
| 194 |
| 195 void JniClient::AuthenticationResponse( |
| 196 JNIEnv* env, |
| 197 const JavaParamRef<jobject>& caller, |
| 198 const JavaParamRef<jstring>& pin, |
| 199 jboolean createPair, |
| 200 const JavaParamRef<jstring>& deviceName) { |
| 201 session_->ProvideSecret(ConvertJavaStringToUTF8(env, pin).c_str(), createPair, |
| 202 ConvertJavaStringToUTF8(env, deviceName)); |
| 203 } |
| 204 |
| 205 void JniClient::ScheduleRedraw( |
| 206 JNIEnv* env, |
| 207 const base::android::JavaParamRef<jobject>& caller) { |
| 208 session_->RedrawDesktop(); |
| 209 } |
| 210 |
| 211 void JniClient::SendMouseEvent( |
| 212 JNIEnv* env, |
| 213 const base::android::JavaParamRef<jobject>& caller, |
| 214 jint x, |
| 215 jint y, |
| 216 jint whichButton, |
| 217 jboolean buttonDown) { |
| 218 // Button must be within the bounds of the MouseEvent_MouseButton enum. |
| 219 DCHECK(whichButton >= 0 && whichButton < 5); |
| 220 |
| 221 session_->SendMouseEvent( |
| 222 x, y, |
| 223 static_cast<remoting::protocol::MouseEvent_MouseButton>(whichButton), |
| 224 buttonDown); |
| 225 } |
| 226 |
| 227 void JniClient::SendMouseWheelEvent( |
| 228 JNIEnv* env, |
| 229 const base::android::JavaParamRef<jobject>& caller, |
| 230 jint delta_x, |
| 231 jint delta_y) { |
| 232 session_->SendMouseWheelEvent(delta_x, delta_y); |
| 233 } |
| 234 |
| 235 jboolean JniClient::SendKeyEvent( |
| 236 JNIEnv* env, |
| 237 const base::android::JavaParamRef<jobject>& caller, |
| 238 jint scanCode, |
| 239 jint keyCode, |
| 240 jboolean keyDown) { |
| 241 return session_->SendKeyEvent(scanCode, keyCode, keyDown); |
| 242 } |
| 243 |
| 244 void JniClient::SendTextEvent( |
| 245 JNIEnv* env, |
| 246 const base::android::JavaParamRef<jobject>& caller, |
| 247 const JavaParamRef<jstring>& text) { |
| 248 session_->SendTextEvent(ConvertJavaStringToUTF8(env, text)); |
| 249 } |
| 250 |
| 251 void JniClient::SendTouchEvent( |
| 252 JNIEnv* env, |
| 253 const base::android::JavaParamRef<jobject>& caller, |
| 254 jint eventType, |
| 255 const JavaParamRef<jobjectArray>& touchEventObjectArray) { |
| 256 protocol::TouchEvent touch_event; |
| 257 touch_event.set_event_type( |
| 258 static_cast<protocol::TouchEvent::TouchEventType>(eventType)); |
| 259 |
| 260 // Iterate over the elements in the object array and transfer the data from |
| 261 // the java object to a native event object. |
| 262 jsize length = env->GetArrayLength(touchEventObjectArray); |
| 263 DCHECK_GE(length, 0); |
| 264 for (jsize i = 0; i < length; ++i) { |
| 265 protocol::TouchEventPoint* touch_point = touch_event.add_touch_points(); |
| 266 |
| 267 ScopedJavaLocalRef<jobject> java_touch_event( |
| 268 env, env->GetObjectArrayElement(touchEventObjectArray, i)); |
| 269 |
| 270 JniTouchEventData::CopyTouchPointData(env, java_touch_event, touch_point); |
| 271 } |
| 272 |
| 273 session_->SendTouchEvent(touch_event); |
| 274 } |
| 275 |
| 276 void JniClient::EnableVideoChannel( |
| 277 JNIEnv* env, |
| 278 const base::android::JavaParamRef<jobject>& caller, |
| 279 jboolean enable) { |
| 280 session_->EnableVideoChannel(enable); |
| 281 } |
| 282 |
| 283 void JniClient::OnThirdPartyTokenFetched( |
| 284 JNIEnv* env, |
| 285 const base::android::JavaParamRef<jobject>& caller, |
| 286 const JavaParamRef<jstring>& token, |
| 287 const JavaParamRef<jstring>& shared_secret) { |
| 288 runtime()->network_task_runner()->PostTask( |
| 289 FROM_HERE, |
| 290 base::Bind(&ChromotingJniInstance::HandleOnThirdPartyTokenFetched, |
| 291 session_, ConvertJavaStringToUTF8(env, token), |
| 292 ConvertJavaStringToUTF8(env, shared_secret))); |
| 293 } |
| 294 |
| 295 void JniClient::SendExtensionMessage( |
| 296 JNIEnv* env, |
| 297 const base::android::JavaParamRef<jobject>& caller, |
| 298 const JavaParamRef<jstring>& type, |
| 299 const JavaParamRef<jstring>& data) { |
| 300 session_->SendClientMessage(ConvertJavaStringToUTF8(env, type), |
| 301 ConvertJavaStringToUTF8(env, data)); |
| 302 } |
| 303 |
22 void JniClient::Destroy(JNIEnv* env, const JavaParamRef<jobject>& caller) { | 304 void JniClient::Destroy(JNIEnv* env, const JavaParamRef<jobject>& caller) { |
| 305 // The session must be shut down first, since it depends on our other |
| 306 // components' still being alive. |
| 307 DisconnectFromHost(); |
| 308 |
| 309 env->DeleteGlobalRef(java_client_); |
23 delete this; | 310 delete this; |
24 } | 311 } |
25 | 312 |
| 313 base::WeakPtr<JniClient> JniClient::GetWeakPtr() { |
| 314 return weak_factory_.GetWeakPtr(); |
| 315 } |
| 316 |
| 317 // static |
| 318 ChromotingJniRuntime* JniClient::runtime() { |
| 319 return ChromotingJniRuntime::GetInstance(); |
| 320 } |
| 321 |
26 static jlong Init(JNIEnv* env, const JavaParamRef<jobject>& caller) { | 322 static jlong Init(JNIEnv* env, const JavaParamRef<jobject>& caller) { |
27 return reinterpret_cast<intptr_t>(new JniClient()); | 323 jobject caller_ref = env->NewGlobalRef(caller); |
| 324 return reinterpret_cast<intptr_t>(new JniClient(caller_ref)); |
28 } | 325 } |
29 | 326 |
30 } // namespace remoting | 327 } // namespace remoting |
OLD | NEW |