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