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/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