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