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.
5 #include "remoting/client/jni/chromoting_jni_instance.h"
7 #include <android/log.h>
10 #include "base/logging.h"
11 #include "jingle/glue/thread_wrapper.h"
12 #include "net/socket/client_socket_factory.h"
13 #include "remoting/base/service_urls.h"
14 #include "remoting/client/audio_player.h"
15 #include "remoting/client/client_status_logger.h"
16 #include "remoting/client/jni/android_keymap.h"
17 #include "remoting/client/jni/chromoting_jni_runtime.h"
18 #include "remoting/client/jni/jni_frame_consumer.h"
19 #include "remoting/client/software_video_renderer.h"
20 #include "remoting/client/token_fetcher_proxy.h"
21 #include "remoting/protocol/chromium_port_allocator.h"
22 #include "remoting/protocol/chromium_socket_factory.h"
23 #include "remoting/protocol/host_stub.h"
24 #include "remoting/protocol/libjingle_transport_factory.h"
25 #include "remoting/protocol/negotiating_client_authenticator.h"
26 #include "remoting/protocol/network_settings.h"
27 #include "remoting/protocol/performance_tracker.h"
28 #include "remoting/signaling/server_log_entry.h"
29 #include "ui/events/keycodes/dom/keycode_converter.h"
35 // TODO(solb) Move into location shared with client plugin.
36 const char* const kXmppServer
= "talk.google.com";
37 const int kXmppPort
= 5222;
38 const bool kXmppUseTls
= true;
40 // Interval at which to log performance statistics, if enabled.
41 const int kPerfStatsIntervalMs
= 60000;
45 ChromotingJniInstance::ChromotingJniInstance(ChromotingJniRuntime
* jni_runtime
,
47 const char* auth_token
,
50 const char* host_pubkey
,
51 const char* pairing_id
,
52 const char* pairing_secret
,
53 const char* capabilities
)
54 : jni_runtime_(jni_runtime
),
57 create_pairing_(false),
58 stats_logging_enabled_(false),
59 capabilities_(capabilities
),
61 DCHECK(jni_runtime_
->ui_task_runner()->BelongsToCurrentThread());
63 // Intialize XMPP config.
64 xmpp_config_
.host
= kXmppServer
;
65 xmpp_config_
.port
= kXmppPort
;
66 xmpp_config_
.use_tls
= kXmppUseTls
;
67 xmpp_config_
.username
= username
;
68 xmpp_config_
.auth_token
= auth_token
;
70 // Initialize |authenticator_|.
71 scoped_ptr
<protocol::ThirdPartyClientAuthenticator::TokenFetcher
>
72 token_fetcher(new TokenFetcherProxy(
73 base::Bind(&ChromotingJniInstance::FetchThirdPartyToken
,
74 weak_factory_
.GetWeakPtr()),
77 std::vector
<protocol::AuthenticationMethod
> auth_methods
;
78 auth_methods
.push_back(protocol::AuthenticationMethod::Spake2Pair());
79 auth_methods
.push_back(protocol::AuthenticationMethod::Spake2(
80 protocol::AuthenticationMethod::HMAC_SHA256
));
81 auth_methods
.push_back(protocol::AuthenticationMethod::Spake2(
82 protocol::AuthenticationMethod::NONE
));
83 auth_methods
.push_back(protocol::AuthenticationMethod::ThirdParty());
85 authenticator_
.reset(new protocol::NegotiatingClientAuthenticator(
86 pairing_id
, pairing_secret
, host_id_
,
87 base::Bind(&ChromotingJniInstance::FetchSecret
, this),
88 token_fetcher
.Pass(), auth_methods
));
90 // Post a task to start connection
91 jni_runtime_
->network_task_runner()->PostTask(
93 base::Bind(&ChromotingJniInstance::ConnectToHostOnNetworkThread
, this));
96 ChromotingJniInstance::~ChromotingJniInstance() {
97 // This object is ref-counted, so this dtor can execute on any thread.
98 // Ensure that all these objects have been freed already, so they are not
99 // destroyed on some random thread.
101 DCHECK(!client_context_
);
102 DCHECK(!video_renderer_
);
103 DCHECK(!authenticator_
);
106 DCHECK(!client_status_logger_
);
109 void ChromotingJniInstance::Disconnect() {
110 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
111 jni_runtime_
->network_task_runner()->PostTask(
113 base::Bind(&ChromotingJniInstance::Disconnect
, this));
119 stats_logging_enabled_
= false;
121 // |client_| must be torn down before |signaling_|.
123 client_status_logger_
.reset();
124 video_renderer_
.reset();
126 authenticator_
.reset();
128 perf_tracker_
.reset();
129 client_context_
.reset();
132 void ChromotingJniInstance::FetchThirdPartyToken(
133 const GURL
& token_url
,
134 const std::string
& client_id
,
135 const std::string
& scope
,
136 base::WeakPtr
<TokenFetcherProxy
> token_fetcher_proxy
) {
137 DCHECK(jni_runtime_
->network_task_runner()->BelongsToCurrentThread());
138 DCHECK(!token_fetcher_proxy_
.get());
140 __android_log_print(ANDROID_LOG_INFO
,
142 "Fetching Third Party Token from user.");
144 token_fetcher_proxy_
= token_fetcher_proxy
;
145 jni_runtime_
->ui_task_runner()->PostTask(
147 base::Bind(&ChromotingJniRuntime::FetchThirdPartyToken
,
148 base::Unretained(jni_runtime_
),
154 void ChromotingJniInstance::HandleOnThirdPartyTokenFetched(
155 const std::string
& token
,
156 const std::string
& shared_secret
) {
157 DCHECK(jni_runtime_
->network_task_runner()->BelongsToCurrentThread());
160 ANDROID_LOG_INFO
, "ThirdPartyAuth", "Third Party Token Fetched.");
162 if (token_fetcher_proxy_
.get()) {
163 token_fetcher_proxy_
->OnTokenFetched(token
, shared_secret
);
164 token_fetcher_proxy_
.reset();
169 "Ignored OnThirdPartyTokenFetched() without a pending fetch.");
173 void ChromotingJniInstance::ProvideSecret(const std::string
& pin
,
175 const std::string
& device_name
) {
176 DCHECK(jni_runtime_
->ui_task_runner()->BelongsToCurrentThread());
177 DCHECK(!pin_callback_
.is_null());
179 create_pairing_
= create_pairing
;
182 SetDeviceName(device_name
);
184 jni_runtime_
->network_task_runner()->PostTask(FROM_HERE
,
185 base::Bind(pin_callback_
, pin
));
188 void ChromotingJniInstance::RedrawDesktop() {
189 if (!jni_runtime_
->display_task_runner()->BelongsToCurrentThread()) {
190 jni_runtime_
->display_task_runner()->PostTask(
192 base::Bind(&ChromotingJniInstance::RedrawDesktop
, this));
196 jni_runtime_
->RedrawCanvas();
199 void ChromotingJniInstance::SendMouseEvent(
201 protocol::MouseEvent_MouseButton button
,
203 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
204 jni_runtime_
->network_task_runner()->PostTask(
205 FROM_HERE
, base::Bind(&ChromotingJniInstance::SendMouseEvent
,
206 this, x
, y
, button
, button_down
));
210 protocol::MouseEvent event
;
213 event
.set_button(button
);
214 if (button
!= protocol::MouseEvent::BUTTON_UNDEFINED
)
215 event
.set_button_down(button_down
);
217 client_
->input_stub()->InjectMouseEvent(event
);
220 void ChromotingJniInstance::SendMouseWheelEvent(int delta_x
, int delta_y
) {
221 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
222 jni_runtime_
->network_task_runner()->PostTask(
224 base::Bind(&ChromotingJniInstance::SendMouseWheelEvent
, this,
229 protocol::MouseEvent event
;
230 event
.set_wheel_delta_x(delta_x
);
231 event
.set_wheel_delta_y(delta_y
);
232 client_
->input_stub()->InjectMouseEvent(event
);
235 bool ChromotingJniInstance::SendKeyEvent(int scan_code
,
238 // For software keyboards |scan_code| is set to 0, in which case the
239 // |key_code| is used instead.
240 uint32_t usb_key_code
=
241 scan_code
? ui::KeycodeConverter::NativeKeycodeToUsbKeycode(scan_code
)
242 : AndroidKeycodeToUsbKeycode(key_code
);
244 LOG(WARNING
) << "Ignoring unknown key code: " << key_code
245 << " scan code: " << scan_code
;
249 SendKeyEventInternal(usb_key_code
, key_down
);
253 void ChromotingJniInstance::SendTextEvent(const std::string
& text
) {
254 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
255 jni_runtime_
->network_task_runner()->PostTask(
257 base::Bind(&ChromotingJniInstance::SendTextEvent
, this, text
));
261 protocol::TextEvent event
;
262 event
.set_text(text
);
263 client_
->input_stub()->InjectTextEvent(event
);
266 void ChromotingJniInstance::EnableVideoChannel(bool enable
) {
267 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
268 jni_runtime_
->network_task_runner()->PostTask(
270 base::Bind(&ChromotingJniInstance::EnableVideoChannel
, this, enable
));
274 protocol::VideoControl video_control
;
275 video_control
.set_enable(enable
);
276 client_
->host_stub()->ControlVideo(video_control
);
279 void ChromotingJniInstance::SendClientMessage(const std::string
& type
,
280 const std::string
& data
) {
281 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
282 jni_runtime_
->network_task_runner()->PostTask(
285 &ChromotingJniInstance::SendClientMessage
, this, type
, data
));
289 protocol::ExtensionMessage extension_message
;
290 extension_message
.set_type(type
);
291 extension_message
.set_data(data
);
292 client_
->host_stub()->DeliverClientMessage(extension_message
);
295 void ChromotingJniInstance::RecordPaintTime(int64 paint_time_ms
) {
296 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
297 jni_runtime_
->network_task_runner()->PostTask(
298 FROM_HERE
, base::Bind(&ChromotingJniInstance::RecordPaintTime
, this,
303 if (stats_logging_enabled_
)
304 perf_tracker_
->RecordPaintTime(paint_time_ms
);
307 void ChromotingJniInstance::OnConnectionState(
308 protocol::ConnectionToHost::State state
,
309 protocol::ErrorCode error
) {
310 DCHECK(jni_runtime_
->network_task_runner()->BelongsToCurrentThread());
312 EnableStatsLogging(state
== protocol::ConnectionToHost::CONNECTED
);
314 client_status_logger_
->LogSessionStateChange(state
, error
);
316 if (create_pairing_
&& state
== protocol::ConnectionToHost::CONNECTED
) {
317 protocol::PairingRequest request
;
318 DCHECK(!device_name_
.empty());
319 request
.set_client_name(device_name_
);
320 client_
->host_stub()->RequestPairing(request
);
323 jni_runtime_
->ui_task_runner()->PostTask(
325 base::Bind(&ChromotingJniRuntime::OnConnectionState
,
326 base::Unretained(jni_runtime_
),
331 void ChromotingJniInstance::OnConnectionReady(bool ready
) {
332 // We ignore this message, since OnConnectionState tells us the same thing.
335 void ChromotingJniInstance::OnRouteChanged(
336 const std::string
& channel_name
,
337 const protocol::TransportRoute
& route
) {
338 std::string message
= "Channel " + channel_name
+ " using " +
339 protocol::TransportRoute::GetTypeString(route
.type
) + " connection.";
340 __android_log_print(ANDROID_LOG_INFO
, "route", "%s", message
.c_str());
343 void ChromotingJniInstance::SetCapabilities(const std::string
& capabilities
) {
344 jni_runtime_
->ui_task_runner()->PostTask(
346 base::Bind(&ChromotingJniRuntime::SetCapabilities
,
347 base::Unretained(jni_runtime_
),
351 void ChromotingJniInstance::SetPairingResponse(
352 const protocol::PairingResponse
& response
) {
354 jni_runtime_
->ui_task_runner()->PostTask(
356 base::Bind(&ChromotingJniRuntime::CommitPairingCredentials
,
357 base::Unretained(jni_runtime_
),
358 host_id_
, response
.client_id(), response
.shared_secret()));
361 void ChromotingJniInstance::DeliverHostMessage(
362 const protocol::ExtensionMessage
& message
) {
363 jni_runtime_
->ui_task_runner()->PostTask(
365 base::Bind(&ChromotingJniRuntime::HandleExtensionMessage
,
366 base::Unretained(jni_runtime_
),
371 protocol::ClipboardStub
* ChromotingJniInstance::GetClipboardStub() {
375 protocol::CursorShapeStub
* ChromotingJniInstance::GetCursorShapeStub() {
379 void ChromotingJniInstance::InjectClipboardEvent(
380 const protocol::ClipboardEvent
& event
) {
384 void ChromotingJniInstance::SetCursorShape(
385 const protocol::CursorShapeInfo
& shape
) {
386 if (!jni_runtime_
->display_task_runner()->BelongsToCurrentThread()) {
387 jni_runtime_
->display_task_runner()->PostTask(
389 base::Bind(&ChromotingJniInstance::SetCursorShape
, this, shape
));
393 jni_runtime_
->UpdateCursorShape(shape
);
396 void ChromotingJniInstance::ConnectToHostOnNetworkThread() {
397 DCHECK(jni_runtime_
->network_task_runner()->BelongsToCurrentThread());
399 jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
401 client_context_
.reset(new ClientContext(jni_runtime_
->network_task_runner()));
402 client_context_
->Start();
404 perf_tracker_
.reset(new protocol::PerformanceTracker());
406 view_
.reset(new JniFrameConsumer(jni_runtime_
));
407 video_renderer_
.reset(new SoftwareVideoRenderer(
408 client_context_
->decode_task_runner(), view_
.get(), perf_tracker_
.get()));
410 client_
.reset(new ChromotingClient(
411 client_context_
.get(), this, video_renderer_
.get(), nullptr));
413 signaling_
.reset(new XmppSignalStrategy(
414 net::ClientSocketFactory::GetDefaultFactory(),
415 jni_runtime_
->url_requester(), xmpp_config_
));
417 client_status_logger_
.reset(
418 new ClientStatusLogger(ServerLogEntry::ME2ME
,
420 ServiceUrls::GetInstance()->directory_bot_jid()));
422 protocol::NetworkSettings
network_settings(
423 protocol::NetworkSettings::NAT_TRAVERSAL_FULL
);
425 // Use Chrome's network stack to allocate ports for peer-to-peer channels.
426 scoped_ptr
<protocol::ChromiumPortAllocator
> port_allocator(
427 protocol::ChromiumPortAllocator::Create(jni_runtime_
->url_requester(),
430 scoped_ptr
<protocol::TransportFactory
> transport_factory(
431 new protocol::LibjingleTransportFactory(
432 signaling_
.get(), port_allocator
.Pass(), network_settings
,
433 protocol::TransportRole::CLIENT
));
435 client_
->Start(signaling_
.get(), authenticator_
.Pass(),
436 transport_factory
.Pass(), host_jid_
, capabilities_
);
439 void ChromotingJniInstance::FetchSecret(
441 const protocol::SecretFetchedCallback
& callback
) {
442 if (!jni_runtime_
->ui_task_runner()->BelongsToCurrentThread()) {
443 jni_runtime_
->ui_task_runner()->PostTask(
444 FROM_HERE
, base::Bind(&ChromotingJniInstance::FetchSecret
,
445 this, pairable
, callback
));
449 // Delete pairing credentials if they exist.
450 jni_runtime_
->CommitPairingCredentials(host_id_
, "", "");
452 pin_callback_
= callback
;
453 jni_runtime_
->DisplayAuthenticationPrompt(pairable
);
456 void ChromotingJniInstance::SetDeviceName(const std::string
& device_name
) {
457 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
458 jni_runtime_
->network_task_runner()->PostTask(
459 FROM_HERE
, base::Bind(&ChromotingJniInstance::SetDeviceName
, this,
464 device_name_
= device_name
;
467 void ChromotingJniInstance::SendKeyEventInternal(int usb_key_code
,
469 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
470 jni_runtime_
->network_task_runner()->PostTask(
471 FROM_HERE
, base::Bind(&ChromotingJniInstance::SendKeyEventInternal
,
472 this, usb_key_code
, key_down
));
476 protocol::KeyEvent event
;
477 event
.set_usb_keycode(usb_key_code
);
478 event
.set_pressed(key_down
);
479 client_
->input_stub()->InjectKeyEvent(event
);
482 void ChromotingJniInstance::EnableStatsLogging(bool enabled
) {
483 DCHECK(jni_runtime_
->network_task_runner()->BelongsToCurrentThread());
485 if (enabled
&& !stats_logging_enabled_
) {
486 jni_runtime_
->network_task_runner()->PostDelayedTask(
487 FROM_HERE
, base::Bind(&ChromotingJniInstance::LogPerfStats
, this),
488 base::TimeDelta::FromMilliseconds(kPerfStatsIntervalMs
));
490 stats_logging_enabled_
= enabled
;
493 void ChromotingJniInstance::LogPerfStats() {
494 DCHECK(jni_runtime_
->network_task_runner()->BelongsToCurrentThread());
496 if (!stats_logging_enabled_
)
500 ANDROID_LOG_INFO
, "stats",
501 "Bandwidth:%.0f FrameRate:%.1f Capture:%.1f Encode:%.1f "
502 "Decode:%.1f Render:%.1f Latency:%.0f",
503 perf_tracker_
->video_bandwidth(), perf_tracker_
->video_frame_rate(),
504 perf_tracker_
->video_capture_ms(), perf_tracker_
->video_encode_ms(),
505 perf_tracker_
->video_decode_ms(), perf_tracker_
->video_paint_ms(),
506 perf_tracker_
->round_trip_ms());
508 client_status_logger_
->LogStatistics(perf_tracker_
.get());
510 jni_runtime_
->network_task_runner()->PostDelayedTask(
511 FROM_HERE
, base::Bind(&ChromotingJniInstance::LogPerfStats
, this),
512 base::TimeDelta::FromMilliseconds(kPerfStatsIntervalMs
));
515 } // namespace remoting