• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ChromotingJniInstance(ChromotingJniRuntime * jni_runtime,const char * username,const char * auth_token,const char * host_jid,const char * host_id,const char * host_pubkey,const char * pairing_id,const char * pairing_secret)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 
~ChromotingJniInstance()82 ChromotingJniInstance::~ChromotingJniInstance() {}
83 
Cleanup()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 
ProvideSecret(const std::string & pin,bool create_pairing)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 
RedrawDesktop()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 
PerformMouseAction(int x,int y,protocol::MouseEvent_MouseButton button,bool button_down)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 
PerformMouseWheelDeltaAction(int delta_x,int delta_y)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 
PerformKeyboardAction(int key_code,bool key_down)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 
RecordPaintTime(int64 paint_time_ms)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 
OnConnectionState(protocol::ConnectionToHost::State state,protocol::ErrorCode error)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 
OnConnectionReady(bool ready)215 void ChromotingJniInstance::OnConnectionReady(bool ready) {
216   // We ignore this message, since OnConnectionState tells us the same thing.
217 }
218 
SetCapabilities(const std::string & capabilities)219 void ChromotingJniInstance::SetCapabilities(const std::string& capabilities) {
220   NOTIMPLEMENTED();
221 }
222 
SetPairingResponse(const protocol::PairingResponse & response)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 
DeliverHostMessage(const protocol::ExtensionMessage & message)233 void ChromotingJniInstance::DeliverHostMessage(
234     const protocol::ExtensionMessage& message) {
235   NOTIMPLEMENTED();
236 }
237 
GetClipboardStub()238 protocol::ClipboardStub* ChromotingJniInstance::GetClipboardStub() {
239   return this;
240 }
241 
GetCursorShapeStub()242 protocol::CursorShapeStub* ChromotingJniInstance::GetCursorShapeStub() {
243   return this;
244 }
245 
246 scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher>
GetTokenFetcher(const std::string & host_public_key)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 
InjectClipboardEvent(const protocol::ClipboardEvent & event)252 void ChromotingJniInstance::InjectClipboardEvent(
253     const protocol::ClipboardEvent& event) {
254   NOTIMPLEMENTED();
255 }
256 
SetCursorShape(const protocol::CursorShapeInfo & shape)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 
ConnectToHostOnDisplayThread()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 
ConnectToHostOnNetworkThread()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 
DisconnectFromHostOnNetworkThread()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 
FetchSecret(bool pairable,const protocol::SecretFetchedCallback & callback)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 
EnableStatsLogging(bool enabled)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 
LogPerfStats()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