Home | History | Annotate | Download | only in jni
      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 
     28 namespace remoting {
     29 
     30 namespace {
     31 
     32 // TODO(solb) Move into location shared with client plugin.
     33 const char* const kXmppServer = "talk.google.com";
     34 const int kXmppPort = 5222;
     35 const bool kXmppUseTls = true;
     36 
     37 // Interval at which to log performance statistics, if enabled.
     38 const int kPerfStatsIntervalMs = 60000;
     39 
     40 }
     41 
     42 ChromotingJniInstance::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 
     95 ChromotingJniInstance::~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 
    108 void 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 
    128 void 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 
    150 void 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 
    169 void 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 
    184 void 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 
    195 void 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 
    216 void 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 
    231 bool 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 
    242 void 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 
    255 void 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 
    271 void 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 
    283 void 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 
    307 void ChromotingJniInstance::OnConnectionReady(bool ready) {
    308   // We ignore this message, since OnConnectionState tells us the same thing.
    309 }
    310 
    311 void 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 
    319 void 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 
    327 void 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 
    337 void 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 
    347 protocol::ClipboardStub* ChromotingJniInstance::GetClipboardStub() {
    348   return this;
    349 }
    350 
    351 protocol::CursorShapeStub* ChromotingJniInstance::GetCursorShapeStub() {
    352   return this;
    353 }
    354 
    355 void ChromotingJniInstance::InjectClipboardEvent(
    356     const protocol::ClipboardEvent& event) {
    357   NOTIMPLEMENTED();
    358 }
    359 
    360 void 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 
    372 void 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 
    387 void 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 
    435 void 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 
    451 void 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 
    468 void 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 
    479 void 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 
    495 void 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 
    506 void 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