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_instance.h" 6 7 #include <android/log.h> 8 9 #include "base/bind.h" 10 #include "base/logging.h" 11 #include "jingle/glue/thread_wrapper.h" 12 #include "net/socket/client_socket_factory.h" 13 #include "remoting/client/audio_player.h" 14 #include "remoting/client/jni/android_keymap.h" 15 #include "remoting/client/jni/chromoting_jni_runtime.h" 16 #include "remoting/client/log_to_server.h" 17 #include "remoting/client/software_video_renderer.h" 18 #include "remoting/client/token_fetcher_proxy.h" 19 #include "remoting/jingle_glue/chromium_port_allocator.h" 20 #include "remoting/jingle_glue/chromium_socket_factory.h" 21 #include "remoting/jingle_glue/network_settings.h" 22 #include "remoting/jingle_glue/server_log_entry.h" 23 #include "remoting/protocol/host_stub.h" 24 #include "remoting/protocol/libjingle_transport_factory.h" 25 26 namespace remoting { 27 28 namespace { 29 30 // TODO(solb) Move into location shared with client plugin. 31 const char* const kXmppServer = "talk.google.com"; 32 const int kXmppPort = 5222; 33 const bool kXmppUseTls = true; 34 35 // Interval at which to log performance statistics, if enabled. 36 const int kPerfStatsIntervalMs = 60000; 37 38 } 39 40 ChromotingJniInstance::ChromotingJniInstance(ChromotingJniRuntime* jni_runtime, 41 const char* username, 42 const char* auth_token, 43 const char* host_jid, 44 const char* host_id, 45 const char* host_pubkey, 46 const char* pairing_id, 47 const char* pairing_secret) 48 : jni_runtime_(jni_runtime), 49 host_id_(host_id), 50 create_pairing_(false), 51 stats_logging_enabled_(false), 52 weak_factory_(this) { 53 DCHECK(jni_runtime_->ui_task_runner()->BelongsToCurrentThread()); 54 55 // Intialize XMPP config. 56 xmpp_config_.host = kXmppServer; 57 xmpp_config_.port = kXmppPort; 58 xmpp_config_.use_tls = kXmppUseTls; 59 xmpp_config_.username = username; 60 xmpp_config_.auth_token = auth_token; 61 xmpp_config_.auth_service = "oauth2"; 62 63 // Initialize ClientConfig. 64 client_config_.host_jid = host_jid; 65 client_config_.host_public_key = host_pubkey; 66 67 client_config_.fetch_secret_callback = 68 base::Bind(&ChromotingJniInstance::FetchSecret, this); 69 client_config_.authentication_tag = host_id_; 70 71 client_config_.client_pairing_id = pairing_id; 72 client_config_.client_paired_secret = pairing_secret; 73 74 client_config_.authentication_methods.push_back( 75 protocol::AuthenticationMethod::FromString("spake2_pair")); 76 client_config_.authentication_methods.push_back( 77 protocol::AuthenticationMethod::FromString("spake2_hmac")); 78 client_config_.authentication_methods.push_back( 79 protocol::AuthenticationMethod::FromString("spake2_plain")); 80 client_config_.authentication_methods.push_back( 81 protocol::AuthenticationMethod::FromString("third_party")); 82 83 // Post a task to start connection 84 jni_runtime_->display_task_runner()->PostTask( 85 FROM_HERE, 86 base::Bind(&ChromotingJniInstance::ConnectToHostOnDisplayThread, 87 this)); 88 } 89 90 ChromotingJniInstance::~ChromotingJniInstance() {} 91 92 void ChromotingJniInstance::Cleanup() { 93 if (!jni_runtime_->display_task_runner()->BelongsToCurrentThread()) { 94 jni_runtime_->display_task_runner()->PostTask( 95 FROM_HERE, 96 base::Bind(&ChromotingJniInstance::Cleanup, this)); 97 return; 98 } 99 100 // This must be destroyed on the display thread before the producer is gone. 101 view_.reset(); 102 103 // The weak pointers must be invalidated on the same thread they were used. 104 view_weak_factory_->InvalidateWeakPtrs(); 105 106 jni_runtime_->network_task_runner()->PostTask( 107 FROM_HERE, 108 base::Bind(&ChromotingJniInstance::DisconnectFromHostOnNetworkThread, 109 this)); 110 } 111 112 void ChromotingJniInstance::FetchThirdPartyToken( 113 const GURL& token_url, 114 const std::string& client_id, 115 const std::string& scope, 116 base::WeakPtr<TokenFetcherProxy> token_fetcher_proxy) { 117 DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread()); 118 DCHECK(!token_fetcher_proxy_.get()); 119 120 __android_log_print(ANDROID_LOG_INFO, 121 "ThirdPartyAuth", 122 "Fetching Third Party Token from user."); 123 124 token_fetcher_proxy_ = token_fetcher_proxy; 125 jni_runtime_->ui_task_runner()->PostTask( 126 FROM_HERE, 127 base::Bind(&ChromotingJniRuntime::FetchThirdPartyToken, 128 base::Unretained(jni_runtime_), 129 token_url, 130 client_id, 131 scope)); 132 } 133 134 void ChromotingJniInstance::HandleOnThirdPartyTokenFetched( 135 const std::string& token, 136 const std::string& shared_secret) { 137 DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread()); 138 139 __android_log_print( 140 ANDROID_LOG_INFO, "ThirdPartyAuth", "Third Party Token Fetched."); 141 142 if (token_fetcher_proxy_.get()) { 143 token_fetcher_proxy_->OnTokenFetched(token, shared_secret); 144 token_fetcher_proxy_.reset(); 145 } else { 146 __android_log_print( 147 ANDROID_LOG_WARN, 148 "ThirdPartyAuth", 149 "Ignored OnThirdPartyTokenFetched() without a pending fetch."); 150 } 151 } 152 153 void ChromotingJniInstance::ProvideSecret(const std::string& pin, 154 bool create_pairing, 155 const std::string& device_name) { 156 DCHECK(jni_runtime_->ui_task_runner()->BelongsToCurrentThread()); 157 DCHECK(!pin_callback_.is_null()); 158 159 create_pairing_ = create_pairing; 160 161 if (create_pairing) 162 SetDeviceName(device_name); 163 164 jni_runtime_->network_task_runner()->PostTask(FROM_HERE, 165 base::Bind(pin_callback_, pin)); 166 } 167 168 void ChromotingJniInstance::RedrawDesktop() { 169 if (!jni_runtime_->display_task_runner()->BelongsToCurrentThread()) { 170 jni_runtime_->display_task_runner()->PostTask( 171 FROM_HERE, 172 base::Bind(&ChromotingJniInstance::RedrawDesktop, this)); 173 return; 174 } 175 176 jni_runtime_->RedrawCanvas(); 177 } 178 179 void ChromotingJniInstance::SendMouseEvent( 180 int x, int y, 181 protocol::MouseEvent_MouseButton button, 182 bool button_down) { 183 if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) { 184 jni_runtime_->network_task_runner()->PostTask( 185 FROM_HERE, base::Bind(&ChromotingJniInstance::SendMouseEvent, 186 this, x, y, button, button_down)); 187 return; 188 } 189 190 protocol::MouseEvent event; 191 event.set_x(x); 192 event.set_y(y); 193 event.set_button(button); 194 if (button != protocol::MouseEvent::BUTTON_UNDEFINED) 195 event.set_button_down(button_down); 196 197 connection_->input_stub()->InjectMouseEvent(event); 198 } 199 200 void ChromotingJniInstance::SendMouseWheelEvent(int delta_x, int delta_y) { 201 if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) { 202 jni_runtime_->network_task_runner()->PostTask( 203 FROM_HERE, 204 base::Bind(&ChromotingJniInstance::SendMouseWheelEvent, this, 205 delta_x, delta_y)); 206 return; 207 } 208 209 protocol::MouseEvent event; 210 event.set_wheel_delta_x(delta_x); 211 event.set_wheel_delta_y(delta_y); 212 connection_->input_stub()->InjectMouseEvent(event); 213 } 214 215 bool ChromotingJniInstance::SendKeyEvent(int key_code, bool key_down) { 216 uint32 usb_key_code = AndroidKeycodeToUsbKeycode(key_code); 217 if (!usb_key_code) { 218 LOG(WARNING) << "Ignoring unknown keycode: " << key_code; 219 return false; 220 } 221 222 SendKeyEventInternal(usb_key_code, key_down); 223 return true; 224 } 225 226 void ChromotingJniInstance::SendTextEvent(const std::string& text) { 227 if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) { 228 jni_runtime_->network_task_runner()->PostTask( 229 FROM_HERE, 230 base::Bind(&ChromotingJniInstance::SendTextEvent, this, text)); 231 return; 232 } 233 234 protocol::TextEvent event; 235 event.set_text(text); 236 connection_->input_stub()->InjectTextEvent(event); 237 } 238 239 void ChromotingJniInstance::RecordPaintTime(int64 paint_time_ms) { 240 if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) { 241 jni_runtime_->network_task_runner()->PostTask( 242 FROM_HERE, base::Bind(&ChromotingJniInstance::RecordPaintTime, this, 243 paint_time_ms)); 244 return; 245 } 246 247 if (stats_logging_enabled_) 248 video_renderer_->GetStats()->video_paint_ms()->Record(paint_time_ms); 249 } 250 251 void ChromotingJniInstance::OnConnectionState( 252 protocol::ConnectionToHost::State state, 253 protocol::ErrorCode error) { 254 DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread()); 255 256 EnableStatsLogging(state == protocol::ConnectionToHost::CONNECTED); 257 258 log_to_server_->LogSessionStateChange(state, error); 259 260 if (create_pairing_ && state == protocol::ConnectionToHost::CONNECTED) { 261 protocol::PairingRequest request; 262 DCHECK(!device_name_.empty()); 263 request.set_client_name(device_name_); 264 connection_->host_stub()->RequestPairing(request); 265 } 266 267 jni_runtime_->ui_task_runner()->PostTask( 268 FROM_HERE, 269 base::Bind(&ChromotingJniRuntime::ReportConnectionStatus, 270 base::Unretained(jni_runtime_), 271 state, 272 error)); 273 } 274 275 void ChromotingJniInstance::OnConnectionReady(bool ready) { 276 // We ignore this message, since OnConnectionState tells us the same thing. 277 } 278 279 void ChromotingJniInstance::OnRouteChanged( 280 const std::string& channel_name, 281 const protocol::TransportRoute& route) { 282 std::string message = "Channel " + channel_name + " using " + 283 protocol::TransportRoute::GetTypeString(route.type) + " connection."; 284 __android_log_print(ANDROID_LOG_INFO, "route", "%s", message.c_str()); 285 } 286 287 void ChromotingJniInstance::SetCapabilities(const std::string& capabilities) { 288 NOTIMPLEMENTED(); 289 } 290 291 void ChromotingJniInstance::SetPairingResponse( 292 const protocol::PairingResponse& response) { 293 294 jni_runtime_->ui_task_runner()->PostTask( 295 FROM_HERE, 296 base::Bind(&ChromotingJniRuntime::CommitPairingCredentials, 297 base::Unretained(jni_runtime_), 298 host_id_, response.client_id(), response.shared_secret())); 299 } 300 301 void ChromotingJniInstance::DeliverHostMessage( 302 const protocol::ExtensionMessage& message) { 303 NOTIMPLEMENTED(); 304 } 305 306 protocol::ClipboardStub* ChromotingJniInstance::GetClipboardStub() { 307 return this; 308 } 309 310 protocol::CursorShapeStub* ChromotingJniInstance::GetCursorShapeStub() { 311 return this; 312 } 313 314 scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher> 315 ChromotingJniInstance::GetTokenFetcher(const std::string& host_public_key) { 316 return scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher>( 317 new TokenFetcherProxy( 318 base::Bind(&ChromotingJniInstance::FetchThirdPartyToken, 319 weak_factory_.GetWeakPtr()), 320 host_public_key)); 321 } 322 323 void ChromotingJniInstance::InjectClipboardEvent( 324 const protocol::ClipboardEvent& event) { 325 NOTIMPLEMENTED(); 326 } 327 328 void ChromotingJniInstance::SetCursorShape( 329 const protocol::CursorShapeInfo& shape) { 330 if (!jni_runtime_->display_task_runner()->BelongsToCurrentThread()) { 331 jni_runtime_->display_task_runner()->PostTask( 332 FROM_HERE, 333 base::Bind(&ChromotingJniInstance::SetCursorShape, this, shape)); 334 return; 335 } 336 337 jni_runtime_->UpdateCursorShape(shape); 338 } 339 340 void ChromotingJniInstance::ConnectToHostOnDisplayThread() { 341 DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread()); 342 343 view_.reset(new JniFrameConsumer(jni_runtime_, this)); 344 view_weak_factory_.reset(new base::WeakPtrFactory<JniFrameConsumer>( 345 view_.get())); 346 frame_consumer_ = new FrameConsumerProxy(jni_runtime_->display_task_runner(), 347 view_weak_factory_->GetWeakPtr()); 348 349 jni_runtime_->network_task_runner()->PostTask( 350 FROM_HERE, 351 base::Bind(&ChromotingJniInstance::ConnectToHostOnNetworkThread, 352 this)); 353 } 354 355 void ChromotingJniInstance::ConnectToHostOnNetworkThread() { 356 DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread()); 357 358 jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop(); 359 360 client_context_.reset(new ClientContext( 361 jni_runtime_->network_task_runner().get())); 362 client_context_->Start(); 363 364 connection_.reset(new protocol::ConnectionToHost(true)); 365 366 SoftwareVideoRenderer* renderer = 367 new SoftwareVideoRenderer(client_context_->main_task_runner(), 368 client_context_->decode_task_runner(), 369 frame_consumer_); 370 view_->set_frame_producer(renderer); 371 video_renderer_.reset(renderer); 372 373 client_.reset(new ChromotingClient( 374 client_config_, client_context_.get(), connection_.get(), 375 this, video_renderer_.get(), scoped_ptr<AudioPlayer>())); 376 377 378 signaling_.reset(new XmppSignalStrategy( 379 net::ClientSocketFactory::GetDefaultFactory(), 380 jni_runtime_->url_requester(), xmpp_config_)); 381 382 log_to_server_.reset(new client::LogToServer(ServerLogEntry::ME2ME, 383 signaling_.get(), 384 "remoting (at) bot.talk.google.com")); 385 386 NetworkSettings network_settings(NetworkSettings::NAT_TRAVERSAL_FULL); 387 388 // Use Chrome's network stack to allocate ports for peer-to-peer channels. 389 scoped_ptr<ChromiumPortAllocator> port_allocator( 390 ChromiumPortAllocator::Create(jni_runtime_->url_requester(), 391 network_settings)); 392 393 scoped_ptr<protocol::TransportFactory> transport_factory( 394 new protocol::LibjingleTransportFactory( 395 signaling_.get(), 396 port_allocator.PassAs<cricket::HttpPortAllocatorBase>(), 397 network_settings)); 398 399 client_->Start(signaling_.get(), transport_factory.Pass()); 400 } 401 402 void ChromotingJniInstance::DisconnectFromHostOnNetworkThread() { 403 DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread()); 404 405 host_id_.clear(); 406 407 stats_logging_enabled_ = false; 408 409 // |client_| must be torn down before |signaling_|. 410 connection_.reset(); 411 client_.reset(); 412 log_to_server_.reset(); 413 } 414 415 void ChromotingJniInstance::FetchSecret( 416 bool pairable, 417 const protocol::SecretFetchedCallback& callback) { 418 if (!jni_runtime_->ui_task_runner()->BelongsToCurrentThread()) { 419 jni_runtime_->ui_task_runner()->PostTask( 420 FROM_HERE, base::Bind(&ChromotingJniInstance::FetchSecret, 421 this, pairable, callback)); 422 return; 423 } 424 425 if (!client_config_.client_pairing_id.empty()) { 426 // We attempted to connect using an existing pairing that was rejected. 427 // Unless we forget about the stale credentials, we'll continue trying them. 428 jni_runtime_->CommitPairingCredentials(host_id_, "", ""); 429 } 430 431 pin_callback_ = callback; 432 jni_runtime_->DisplayAuthenticationPrompt(pairable); 433 } 434 435 void ChromotingJniInstance::SetDeviceName(const std::string& device_name) { 436 if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) { 437 jni_runtime_->network_task_runner()->PostTask( 438 FROM_HERE, base::Bind(&ChromotingJniInstance::SetDeviceName, this, 439 device_name)); 440 return; 441 } 442 443 device_name_ = device_name; 444 } 445 446 void ChromotingJniInstance::SendKeyEventInternal(int usb_key_code, 447 bool key_down) { 448 if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) { 449 jni_runtime_->network_task_runner()->PostTask( 450 FROM_HERE, base::Bind(&ChromotingJniInstance::SendKeyEventInternal, 451 this, usb_key_code, key_down)); 452 return; 453 } 454 455 456 protocol::KeyEvent event; 457 event.set_usb_keycode(usb_key_code); 458 event.set_pressed(key_down); 459 connection_->input_stub()->InjectKeyEvent(event); 460 } 461 462 void ChromotingJniInstance::EnableStatsLogging(bool enabled) { 463 DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread()); 464 465 if (enabled && !stats_logging_enabled_) { 466 jni_runtime_->network_task_runner()->PostDelayedTask( 467 FROM_HERE, base::Bind(&ChromotingJniInstance::LogPerfStats, this), 468 base::TimeDelta::FromMilliseconds(kPerfStatsIntervalMs)); 469 } 470 stats_logging_enabled_ = enabled; 471 } 472 473 void ChromotingJniInstance::LogPerfStats() { 474 DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread()); 475 476 if (!stats_logging_enabled_) 477 return; 478 479 ChromotingStats* stats = video_renderer_->GetStats(); 480 __android_log_print(ANDROID_LOG_INFO, "stats", 481 "Bandwidth:%.0f FrameRate:%.1f Capture:%.1f Encode:%.1f " 482 "Decode:%.1f Render:%.1f Latency:%.0f", 483 stats->video_bandwidth()->Rate(), 484 stats->video_frame_rate()->Rate(), 485 stats->video_capture_ms()->Average(), 486 stats->video_encode_ms()->Average(), 487 stats->video_decode_ms()->Average(), 488 stats->video_paint_ms()->Average(), 489 stats->round_trip_ms()->Average()); 490 491 log_to_server_->LogStatistics(stats); 492 493 jni_runtime_->network_task_runner()->PostDelayedTask( 494 FROM_HERE, base::Bind(&ChromotingJniInstance::LogPerfStats, this), 495 base::TimeDelta::FromMilliseconds(kPerfStatsIntervalMs)); 496 } 497 498 } // namespace remoting 499