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