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 "net/socket/client_socket_factory.h"
     12 #include "remoting/client/audio_player.h"
     13 #include "remoting/client/jni/android_keymap.h"
     14 #include "remoting/client/jni/chromoting_jni_runtime.h"
     15 #include "remoting/jingle_glue/chromium_port_allocator.h"
     16 #include "remoting/jingle_glue/chromium_socket_factory.h"
     17 #include "remoting/jingle_glue/network_settings.h"
     18 #include "remoting/protocol/host_stub.h"
     19 #include "remoting/protocol/libjingle_transport_factory.h"
     20 
     21 namespace remoting {
     22 
     23 namespace {
     24 
     25 // TODO(solb) Move into location shared with client plugin.
     26 const char* const kXmppServer = "talk.google.com";
     27 const int kXmppPort = 5222;
     28 const bool kXmppUseTls = true;
     29 
     30 // Interval at which to log performance statistics, if enabled.
     31 const int kPerfStatsIntervalMs = 10000;
     32 
     33 }
     34 
     35 ChromotingJniInstance::ChromotingJniInstance(ChromotingJniRuntime* jni_runtime,
     36                                              const char* username,
     37                                              const char* auth_token,
     38                                              const char* host_jid,
     39                                              const char* host_id,
     40                                              const char* host_pubkey,
     41                                              const char* pairing_id,
     42                                              const char* pairing_secret)
     43     : jni_runtime_(jni_runtime),
     44       host_id_(host_id),
     45       create_pairing_(false),
     46       stats_logging_enabled_(false) {
     47   DCHECK(jni_runtime_->ui_task_runner()->BelongsToCurrentThread());
     48 
     49   // Intialize XMPP config.
     50   xmpp_config_.host = kXmppServer;
     51   xmpp_config_.port = kXmppPort;
     52   xmpp_config_.use_tls = kXmppUseTls;
     53   xmpp_config_.username = username;
     54   xmpp_config_.auth_token = auth_token;
     55   xmpp_config_.auth_service = "oauth2";
     56 
     57   // Initialize ClientConfig.
     58   client_config_.host_jid = host_jid;
     59   client_config_.host_public_key = host_pubkey;
     60 
     61   client_config_.fetch_secret_callback =
     62       base::Bind(&ChromotingJniInstance::FetchSecret, this);
     63   client_config_.authentication_tag = host_id_;
     64 
     65   client_config_.client_pairing_id = pairing_id;
     66   client_config_.client_paired_secret = pairing_secret;
     67 
     68   client_config_.authentication_methods.push_back(
     69       protocol::AuthenticationMethod::FromString("spake2_pair"));
     70   client_config_.authentication_methods.push_back(
     71       protocol::AuthenticationMethod::FromString("spake2_hmac"));
     72   client_config_.authentication_methods.push_back(
     73       protocol::AuthenticationMethod::FromString("spake2_plain"));
     74 
     75   // Post a task to start connection
     76   jni_runtime_->display_task_runner()->PostTask(
     77       FROM_HERE,
     78       base::Bind(&ChromotingJniInstance::ConnectToHostOnDisplayThread,
     79                  this));
     80 }
     81 
     82 ChromotingJniInstance::~ChromotingJniInstance() {}
     83 
     84 void ChromotingJniInstance::Cleanup() {
     85   if (!jni_runtime_->display_task_runner()->BelongsToCurrentThread()) {
     86     jni_runtime_->display_task_runner()->PostTask(
     87         FROM_HERE,
     88         base::Bind(&ChromotingJniInstance::Cleanup, this));
     89     return;
     90   }
     91 
     92   // This must be destroyed on the display thread before the producer is gone.
     93   view_.reset();
     94 
     95   // The weak pointers must be invalidated on the same thread they were used.
     96   view_weak_factory_->InvalidateWeakPtrs();
     97 
     98   jni_runtime_->network_task_runner()->PostTask(
     99       FROM_HERE,
    100       base::Bind(&ChromotingJniInstance::DisconnectFromHostOnNetworkThread,
    101                  this));
    102 }
    103 
    104 void ChromotingJniInstance::ProvideSecret(const std::string& pin,
    105                                           bool create_pairing) {
    106   DCHECK(jni_runtime_->ui_task_runner()->BelongsToCurrentThread());
    107   DCHECK(!pin_callback_.is_null());
    108 
    109   create_pairing_ = create_pairing;
    110 
    111   jni_runtime_->network_task_runner()->PostTask(FROM_HERE,
    112                                                 base::Bind(pin_callback_, pin));
    113 }
    114 
    115 void ChromotingJniInstance::RedrawDesktop() {
    116   if (!jni_runtime_->display_task_runner()->BelongsToCurrentThread()) {
    117     jni_runtime_->display_task_runner()->PostTask(
    118         FROM_HERE,
    119         base::Bind(&ChromotingJniInstance::RedrawDesktop, this));
    120     return;
    121   }
    122 
    123   jni_runtime_->RedrawCanvas();
    124 }
    125 
    126 void ChromotingJniInstance::PerformMouseAction(
    127     int x, int y,
    128     protocol::MouseEvent_MouseButton button,
    129     bool button_down) {
    130   if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) {
    131     jni_runtime_->network_task_runner()->PostTask(
    132         FROM_HERE, base::Bind(&ChromotingJniInstance::PerformMouseAction,
    133                               this, x, y, button, button_down));
    134     return;
    135   }
    136 
    137   protocol::MouseEvent action;
    138   action.set_x(x);
    139   action.set_y(y);
    140   action.set_button(button);
    141   if (button != protocol::MouseEvent::BUTTON_UNDEFINED)
    142     action.set_button_down(button_down);
    143 
    144   connection_->input_stub()->InjectMouseEvent(action);
    145 }
    146 
    147 void ChromotingJniInstance::PerformMouseWheelDeltaAction(int delta_x,
    148                                                          int delta_y) {
    149   if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) {
    150     jni_runtime_->network_task_runner()->PostTask(
    151         FROM_HERE,
    152         base::Bind(&ChromotingJniInstance::PerformMouseWheelDeltaAction, this,
    153                    delta_x, delta_y));
    154     return;
    155   }
    156 
    157   protocol::MouseEvent action;
    158   action.set_wheel_delta_x(delta_x);
    159   action.set_wheel_delta_y(delta_y);
    160   connection_->input_stub()->InjectMouseEvent(action);
    161 }
    162 
    163 void ChromotingJniInstance::PerformKeyboardAction(int key_code, bool key_down) {
    164   if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) {
    165     jni_runtime_->network_task_runner()->PostTask(
    166         FROM_HERE, base::Bind(&ChromotingJniInstance::PerformKeyboardAction,
    167                               this, key_code, key_down));
    168     return;
    169   }
    170 
    171   uint32 usb_code = AndroidKeycodeToUsbKeycode(key_code);
    172   if (usb_code) {
    173     protocol::KeyEvent action;
    174     action.set_usb_keycode(usb_code);
    175     action.set_pressed(key_down);
    176     connection_->input_stub()->InjectKeyEvent(action);
    177   } else {
    178     LOG(WARNING) << "Ignoring unknown keycode: " << key_code;
    179   }
    180 }
    181 
    182 void ChromotingJniInstance::RecordPaintTime(int64 paint_time_ms) {
    183   if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) {
    184     jni_runtime_->network_task_runner()->PostTask(
    185         FROM_HERE, base::Bind(&ChromotingJniInstance::RecordPaintTime, this,
    186                               paint_time_ms));
    187     return;
    188   }
    189 
    190   if (stats_logging_enabled_)
    191     client_->GetStats()->video_paint_ms()->Record(paint_time_ms);
    192 }
    193 
    194 void ChromotingJniInstance::OnConnectionState(
    195     protocol::ConnectionToHost::State state,
    196     protocol::ErrorCode error) {
    197   DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread());
    198 
    199   EnableStatsLogging(state == protocol::ConnectionToHost::CONNECTED);
    200 
    201   if (create_pairing_ && state == protocol::ConnectionToHost::CONNECTED) {
    202     protocol::PairingRequest request;
    203     request.set_client_name("Android");
    204     connection_->host_stub()->RequestPairing(request);
    205   }
    206 
    207   jni_runtime_->ui_task_runner()->PostTask(
    208       FROM_HERE,
    209       base::Bind(&ChromotingJniRuntime::ReportConnectionStatus,
    210                  base::Unretained(jni_runtime_),
    211                  state,
    212                  error));
    213 }
    214 
    215 void ChromotingJniInstance::OnConnectionReady(bool ready) {
    216   // We ignore this message, since OnConnectionState tells us the same thing.
    217 }
    218 
    219 void ChromotingJniInstance::SetCapabilities(const std::string& capabilities) {
    220   NOTIMPLEMENTED();
    221 }
    222 
    223 void ChromotingJniInstance::SetPairingResponse(
    224     const protocol::PairingResponse& response) {
    225 
    226   jni_runtime_->ui_task_runner()->PostTask(
    227       FROM_HERE,
    228       base::Bind(&ChromotingJniRuntime::CommitPairingCredentials,
    229                  base::Unretained(jni_runtime_),
    230                  host_id_, response.client_id(), response.shared_secret()));
    231 }
    232 
    233 void ChromotingJniInstance::DeliverHostMessage(
    234     const protocol::ExtensionMessage& message) {
    235   NOTIMPLEMENTED();
    236 }
    237 
    238 protocol::ClipboardStub* ChromotingJniInstance::GetClipboardStub() {
    239   return this;
    240 }
    241 
    242 protocol::CursorShapeStub* ChromotingJniInstance::GetCursorShapeStub() {
    243   return this;
    244 }
    245 
    246 scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher>
    247     ChromotingJniInstance::GetTokenFetcher(const std::string& host_public_key) {
    248   // Return null to indicate that third-party authentication is unsupported.
    249   return scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher>();
    250 }
    251 
    252 void ChromotingJniInstance::InjectClipboardEvent(
    253     const protocol::ClipboardEvent& event) {
    254   NOTIMPLEMENTED();
    255 }
    256 
    257 void ChromotingJniInstance::SetCursorShape(
    258     const protocol::CursorShapeInfo& shape) {
    259   if (!jni_runtime_->display_task_runner()->BelongsToCurrentThread()) {
    260     jni_runtime_->display_task_runner()->PostTask(
    261         FROM_HERE,
    262         base::Bind(&ChromotingJniInstance::SetCursorShape, this, shape));
    263     return;
    264   }
    265 
    266   jni_runtime_->UpdateCursorShape(shape);
    267 }
    268 
    269 void ChromotingJniInstance::ConnectToHostOnDisplayThread() {
    270   DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread());
    271 
    272   view_.reset(new JniFrameConsumer(jni_runtime_, this));
    273   view_weak_factory_.reset(new base::WeakPtrFactory<JniFrameConsumer>(
    274       view_.get()));
    275   frame_consumer_ = new FrameConsumerProxy(jni_runtime_->display_task_runner(),
    276                                            view_weak_factory_->GetWeakPtr());
    277 
    278   jni_runtime_->network_task_runner()->PostTask(
    279       FROM_HERE,
    280       base::Bind(&ChromotingJniInstance::ConnectToHostOnNetworkThread,
    281                  this));
    282 }
    283 
    284 void ChromotingJniInstance::ConnectToHostOnNetworkThread() {
    285   DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread());
    286 
    287   client_context_.reset(new ClientContext(
    288       jni_runtime_->network_task_runner().get()));
    289   client_context_->Start();
    290 
    291   connection_.reset(new protocol::ConnectionToHost(true));
    292 
    293   client_.reset(new ChromotingClient(
    294       client_config_, client_context_.get(), connection_.get(),
    295       this, frame_consumer_, scoped_ptr<AudioPlayer>()));
    296 
    297   view_->set_frame_producer(client_->GetFrameProducer());
    298 
    299   signaling_.reset(new XmppSignalStrategy(
    300       net::ClientSocketFactory::GetDefaultFactory(),
    301       jni_runtime_->url_requester(), xmpp_config_));
    302 
    303   NetworkSettings network_settings(NetworkSettings::NAT_TRAVERSAL_ENABLED);
    304 
    305   // Use Chrome's network stack to allocate ports for peer-to-peer channels.
    306   scoped_ptr<ChromiumPortAllocator> port_allocator(
    307       ChromiumPortAllocator::Create(jni_runtime_->url_requester(),
    308                                     network_settings));
    309 
    310   scoped_ptr<protocol::TransportFactory> transport_factory(
    311       new protocol::LibjingleTransportFactory(
    312           signaling_.get(),
    313           port_allocator.PassAs<cricket::HttpPortAllocatorBase>(),
    314           network_settings));
    315 
    316   client_->Start(signaling_.get(), transport_factory.Pass());
    317 }
    318 
    319 void ChromotingJniInstance::DisconnectFromHostOnNetworkThread() {
    320   DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread());
    321 
    322   host_id_.clear();
    323 
    324   stats_logging_enabled_ = false;
    325 
    326   // |client_| must be torn down before |signaling_|.
    327   connection_.reset();
    328   client_.reset();
    329 }
    330 
    331 void ChromotingJniInstance::FetchSecret(
    332     bool pairable,
    333     const protocol::SecretFetchedCallback& callback) {
    334   if (!jni_runtime_->ui_task_runner()->BelongsToCurrentThread()) {
    335     jni_runtime_->ui_task_runner()->PostTask(
    336         FROM_HERE, base::Bind(&ChromotingJniInstance::FetchSecret,
    337                               this, pairable, callback));
    338     return;
    339   }
    340 
    341   if (!client_config_.client_pairing_id.empty()) {
    342     // We attempted to connect using an existing pairing that was rejected.
    343     // Unless we forget about the stale credentials, we'll continue trying them.
    344     jni_runtime_->CommitPairingCredentials(host_id_, "", "");
    345   }
    346 
    347   pin_callback_ = callback;
    348   jni_runtime_->DisplayAuthenticationPrompt(pairable);
    349 }
    350 
    351 void ChromotingJniInstance::EnableStatsLogging(bool enabled) {
    352   DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread());
    353 
    354   if (enabled && !stats_logging_enabled_) {
    355     jni_runtime_->network_task_runner()->PostDelayedTask(
    356         FROM_HERE, base::Bind(&ChromotingJniInstance::LogPerfStats, this),
    357         base::TimeDelta::FromMilliseconds(kPerfStatsIntervalMs));
    358   }
    359   stats_logging_enabled_ = enabled;
    360 }
    361 
    362 void ChromotingJniInstance::LogPerfStats() {
    363   DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread());
    364 
    365   if (!stats_logging_enabled_)
    366     return;
    367 
    368   ChromotingStats* stats = client_->GetStats();
    369   __android_log_print(ANDROID_LOG_INFO, "stats",
    370                       "Bandwidth:%.0f FrameRate:%.1f Capture:%.1f Encode:%.1f "
    371                       "Decode:%.1f Render:%.1f Latency:%.0f",
    372                       stats->video_bandwidth()->Rate(),
    373                       stats->video_frame_rate()->Rate(),
    374                       stats->video_capture_ms()->Average(),
    375                       stats->video_encode_ms()->Average(),
    376                       stats->video_decode_ms()->Average(),
    377                       stats->video_paint_ms()->Average(),
    378                       stats->round_trip_ms()->Average());
    379 
    380   jni_runtime_->network_task_runner()->PostDelayedTask(
    381       FROM_HERE, base::Bind(&ChromotingJniInstance::LogPerfStats, this),
    382       base::TimeDelta::FromMilliseconds(kPerfStatsIntervalMs));
    383 }
    384 
    385 }  // namespace remoting
    386