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/software_video_renderer.h"
19 #include "remoting/client/token_fetcher_proxy.h"
20 #include "remoting/protocol/chromium_port_allocator.h"
21 #include "remoting/protocol/chromium_socket_factory.h"
22 #include "remoting/protocol/host_stub.h"
23 #include "remoting/protocol/libjingle_transport_factory.h"
24 #include "remoting/protocol/negotiating_client_authenticator.h"
25 #include "remoting/protocol/network_settings.h"
26 #include "remoting/signaling/server_log_entry.h"
32 // TODO(solb) Move into location shared with client plugin.
33 const char* const kXmppServer
= "talk.google.com";
34 const int kXmppPort
= 5222;
35 const bool kXmppUseTls
= true;
37 // Interval at which to log performance statistics, if enabled.
38 const int kPerfStatsIntervalMs
= 60000;
42 ChromotingJniInstance::ChromotingJniInstance(ChromotingJniRuntime
* jni_runtime
,
44 const char* auth_token
,
47 const char* host_pubkey
,
48 const char* pairing_id
,
49 const char* pairing_secret
,
50 const char* capabilities
)
51 : jni_runtime_(jni_runtime
),
54 create_pairing_(false),
55 stats_logging_enabled_(false),
56 capabilities_(capabilities
),
58 DCHECK(jni_runtime_
->ui_task_runner()->BelongsToCurrentThread());
60 // Intialize XMPP config.
61 xmpp_config_
.host
= kXmppServer
;
62 xmpp_config_
.port
= kXmppPort
;
63 xmpp_config_
.use_tls
= kXmppUseTls
;
64 xmpp_config_
.username
= username
;
65 xmpp_config_
.auth_token
= auth_token
;
66 xmpp_config_
.auth_service
= "oauth2";
68 // Initialize |authenticator_|.
69 scoped_ptr
<protocol::ThirdPartyClientAuthenticator::TokenFetcher
>
70 token_fetcher(new TokenFetcherProxy(
71 base::Bind(&ChromotingJniInstance::FetchThirdPartyToken
,
72 weak_factory_
.GetWeakPtr()),
75 std::vector
<protocol::AuthenticationMethod
> auth_methods
;
76 auth_methods
.push_back(protocol::AuthenticationMethod::Spake2Pair());
77 auth_methods
.push_back(protocol::AuthenticationMethod::Spake2(
78 protocol::AuthenticationMethod::HMAC_SHA256
));
79 auth_methods
.push_back(protocol::AuthenticationMethod::Spake2(
80 protocol::AuthenticationMethod::NONE
));
81 auth_methods
.push_back(protocol::AuthenticationMethod::ThirdParty());
83 authenticator_
.reset(new protocol::NegotiatingClientAuthenticator(
84 pairing_id
, pairing_secret
, host_id_
,
85 base::Bind(&ChromotingJniInstance::FetchSecret
, this),
86 token_fetcher
.Pass(), auth_methods
));
88 // Post a task to start connection
89 jni_runtime_
->display_task_runner()->PostTask(
91 base::Bind(&ChromotingJniInstance::ConnectToHostOnDisplayThread
,
95 ChromotingJniInstance::~ChromotingJniInstance() {
96 // This object is ref-counted, so this dtor can execute on any thread.
97 // Ensure that all these objects have been freed already, so they are not
98 // destroyed on some random thread.
100 DCHECK(!client_context_
);
101 DCHECK(!video_renderer_
);
102 DCHECK(!authenticator_
);
105 DCHECK(!client_status_logger_
);
108 void ChromotingJniInstance::Disconnect() {
109 if (!jni_runtime_
->display_task_runner()->BelongsToCurrentThread()) {
110 jni_runtime_
->display_task_runner()->PostTask(
112 base::Bind(&ChromotingJniInstance::Disconnect
, this));
116 // This must be destroyed on the display thread before the producer is gone.
119 // The weak pointers must be invalidated on the same thread they were used.
120 view_weak_factory_
->InvalidateWeakPtrs();
122 jni_runtime_
->network_task_runner()->PostTask(
124 base::Bind(&ChromotingJniInstance::DisconnectFromHostOnNetworkThread
,
128 void ChromotingJniInstance::FetchThirdPartyToken(
129 const GURL
& token_url
,
130 const std::string
& client_id
,
131 const std::string
& scope
,
132 base::WeakPtr
<TokenFetcherProxy
> token_fetcher_proxy
) {
133 DCHECK(jni_runtime_
->network_task_runner()->BelongsToCurrentThread());
134 DCHECK(!token_fetcher_proxy_
.get());
136 __android_log_print(ANDROID_LOG_INFO
,
138 "Fetching Third Party Token from user.");
140 token_fetcher_proxy_
= token_fetcher_proxy
;
141 jni_runtime_
->ui_task_runner()->PostTask(
143 base::Bind(&ChromotingJniRuntime::FetchThirdPartyToken
,
144 base::Unretained(jni_runtime_
),
150 void ChromotingJniInstance::HandleOnThirdPartyTokenFetched(
151 const std::string
& token
,
152 const std::string
& shared_secret
) {
153 DCHECK(jni_runtime_
->network_task_runner()->BelongsToCurrentThread());
156 ANDROID_LOG_INFO
, "ThirdPartyAuth", "Third Party Token Fetched.");
158 if (token_fetcher_proxy_
.get()) {
159 token_fetcher_proxy_
->OnTokenFetched(token
, shared_secret
);
160 token_fetcher_proxy_
.reset();
165 "Ignored OnThirdPartyTokenFetched() without a pending fetch.");
169 void ChromotingJniInstance::ProvideSecret(const std::string
& pin
,
171 const std::string
& device_name
) {
172 DCHECK(jni_runtime_
->ui_task_runner()->BelongsToCurrentThread());
173 DCHECK(!pin_callback_
.is_null());
175 create_pairing_
= create_pairing
;
178 SetDeviceName(device_name
);
180 jni_runtime_
->network_task_runner()->PostTask(FROM_HERE
,
181 base::Bind(pin_callback_
, pin
));
184 void ChromotingJniInstance::RedrawDesktop() {
185 if (!jni_runtime_
->display_task_runner()->BelongsToCurrentThread()) {
186 jni_runtime_
->display_task_runner()->PostTask(
188 base::Bind(&ChromotingJniInstance::RedrawDesktop
, this));
192 jni_runtime_
->RedrawCanvas();
195 void ChromotingJniInstance::SendMouseEvent(
197 protocol::MouseEvent_MouseButton button
,
199 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
200 jni_runtime_
->network_task_runner()->PostTask(
201 FROM_HERE
, base::Bind(&ChromotingJniInstance::SendMouseEvent
,
202 this, x
, y
, button
, button_down
));
206 protocol::MouseEvent event
;
209 event
.set_button(button
);
210 if (button
!= protocol::MouseEvent::BUTTON_UNDEFINED
)
211 event
.set_button_down(button_down
);
213 client_
->input_stub()->InjectMouseEvent(event
);
216 void ChromotingJniInstance::SendMouseWheelEvent(int delta_x
, int delta_y
) {
217 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
218 jni_runtime_
->network_task_runner()->PostTask(
220 base::Bind(&ChromotingJniInstance::SendMouseWheelEvent
, this,
225 protocol::MouseEvent event
;
226 event
.set_wheel_delta_x(delta_x
);
227 event
.set_wheel_delta_y(delta_y
);
228 client_
->input_stub()->InjectMouseEvent(event
);
231 bool ChromotingJniInstance::SendKeyEvent(int key_code
, bool key_down
) {
232 uint32 usb_key_code
= AndroidKeycodeToUsbKeycode(key_code
);
234 LOG(WARNING
) << "Ignoring unknown keycode: " << key_code
;
238 SendKeyEventInternal(usb_key_code
, key_down
);
242 void ChromotingJniInstance::SendTextEvent(const std::string
& text
) {
243 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
244 jni_runtime_
->network_task_runner()->PostTask(
246 base::Bind(&ChromotingJniInstance::SendTextEvent
, this, text
));
250 protocol::TextEvent event
;
251 event
.set_text(text
);
252 client_
->input_stub()->InjectTextEvent(event
);
255 void ChromotingJniInstance::SendClientMessage(const std::string
& type
,
256 const std::string
& data
) {
257 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
258 jni_runtime_
->network_task_runner()->PostTask(
261 &ChromotingJniInstance::SendClientMessage
, this, type
, data
));
265 protocol::ExtensionMessage extension_message
;
266 extension_message
.set_type(type
);
267 extension_message
.set_data(data
);
268 client_
->host_stub()->DeliverClientMessage(extension_message
);
271 void ChromotingJniInstance::RecordPaintTime(int64 paint_time_ms
) {
272 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
273 jni_runtime_
->network_task_runner()->PostTask(
274 FROM_HERE
, base::Bind(&ChromotingJniInstance::RecordPaintTime
, this,
279 if (stats_logging_enabled_
)
280 video_renderer_
->GetStats()->video_paint_ms()->Record(paint_time_ms
);
283 void ChromotingJniInstance::OnConnectionState(
284 protocol::ConnectionToHost::State state
,
285 protocol::ErrorCode error
) {
286 DCHECK(jni_runtime_
->network_task_runner()->BelongsToCurrentThread());
288 EnableStatsLogging(state
== protocol::ConnectionToHost::CONNECTED
);
290 client_status_logger_
->LogSessionStateChange(state
, error
);
292 if (create_pairing_
&& state
== protocol::ConnectionToHost::CONNECTED
) {
293 protocol::PairingRequest request
;
294 DCHECK(!device_name_
.empty());
295 request
.set_client_name(device_name_
);
296 client_
->host_stub()->RequestPairing(request
);
299 jni_runtime_
->ui_task_runner()->PostTask(
301 base::Bind(&ChromotingJniRuntime::OnConnectionState
,
302 base::Unretained(jni_runtime_
),
307 void ChromotingJniInstance::OnConnectionReady(bool ready
) {
308 // We ignore this message, since OnConnectionState tells us the same thing.
311 void ChromotingJniInstance::OnRouteChanged(
312 const std::string
& channel_name
,
313 const protocol::TransportRoute
& route
) {
314 std::string message
= "Channel " + channel_name
+ " using " +
315 protocol::TransportRoute::GetTypeString(route
.type
) + " connection.";
316 __android_log_print(ANDROID_LOG_INFO
, "route", "%s", message
.c_str());
319 void ChromotingJniInstance::SetCapabilities(const std::string
& capabilities
) {
320 jni_runtime_
->ui_task_runner()->PostTask(
322 base::Bind(&ChromotingJniRuntime::SetCapabilities
,
323 base::Unretained(jni_runtime_
),
327 void ChromotingJniInstance::SetPairingResponse(
328 const protocol::PairingResponse
& response
) {
330 jni_runtime_
->ui_task_runner()->PostTask(
332 base::Bind(&ChromotingJniRuntime::CommitPairingCredentials
,
333 base::Unretained(jni_runtime_
),
334 host_id_
, response
.client_id(), response
.shared_secret()));
337 void ChromotingJniInstance::DeliverHostMessage(
338 const protocol::ExtensionMessage
& message
) {
339 jni_runtime_
->ui_task_runner()->PostTask(
341 base::Bind(&ChromotingJniRuntime::HandleExtensionMessage
,
342 base::Unretained(jni_runtime_
),
347 protocol::ClipboardStub
* ChromotingJniInstance::GetClipboardStub() {
351 protocol::CursorShapeStub
* ChromotingJniInstance::GetCursorShapeStub() {
355 void ChromotingJniInstance::InjectClipboardEvent(
356 const protocol::ClipboardEvent
& event
) {
360 void ChromotingJniInstance::SetCursorShape(
361 const protocol::CursorShapeInfo
& shape
) {
362 if (!jni_runtime_
->display_task_runner()->BelongsToCurrentThread()) {
363 jni_runtime_
->display_task_runner()->PostTask(
365 base::Bind(&ChromotingJniInstance::SetCursorShape
, this, shape
));
369 jni_runtime_
->UpdateCursorShape(shape
);
372 void ChromotingJniInstance::ConnectToHostOnDisplayThread() {
373 DCHECK(jni_runtime_
->display_task_runner()->BelongsToCurrentThread());
375 view_
.reset(new JniFrameConsumer(jni_runtime_
, this));
376 view_weak_factory_
.reset(new base::WeakPtrFactory
<JniFrameConsumer
>(
378 frame_consumer_
= new FrameConsumerProxy(jni_runtime_
->display_task_runner(),
379 view_weak_factory_
->GetWeakPtr());
381 jni_runtime_
->network_task_runner()->PostTask(
383 base::Bind(&ChromotingJniInstance::ConnectToHostOnNetworkThread
,
387 void ChromotingJniInstance::ConnectToHostOnNetworkThread() {
388 DCHECK(jni_runtime_
->network_task_runner()->BelongsToCurrentThread());
390 jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
392 client_context_
.reset(new ClientContext(
393 jni_runtime_
->network_task_runner().get()));
394 client_context_
->Start();
396 SoftwareVideoRenderer
* renderer
=
397 new SoftwareVideoRenderer(client_context_
->main_task_runner(),
398 client_context_
->decode_task_runner(),
400 view_
->set_frame_producer(renderer
);
401 video_renderer_
.reset(renderer
);
403 client_
.reset(new ChromotingClient(
404 client_context_
.get(), this, video_renderer_
.get(), nullptr));
406 signaling_
.reset(new XmppSignalStrategy(
407 net::ClientSocketFactory::GetDefaultFactory(),
408 jni_runtime_
->url_requester(), xmpp_config_
));
410 client_status_logger_
.reset(
411 new ClientStatusLogger(ServerLogEntry::ME2ME
,
413 ServiceUrls::GetInstance()->directory_bot_jid()));
415 protocol::NetworkSettings
network_settings(
416 protocol::NetworkSettings::NAT_TRAVERSAL_FULL
);
418 // Use Chrome's network stack to allocate ports for peer-to-peer channels.
419 scoped_ptr
<protocol::ChromiumPortAllocator
> port_allocator(
420 protocol::ChromiumPortAllocator::Create(jni_runtime_
->url_requester(),
423 scoped_ptr
<protocol::TransportFactory
> transport_factory(
424 new protocol::LibjingleTransportFactory(
425 signaling_
.get(), port_allocator
.Pass(), network_settings
));
427 client_
->Start(signaling_
.get(), authenticator_
.Pass(),
428 transport_factory
.Pass(), host_jid_
, capabilities_
);
431 void ChromotingJniInstance::DisconnectFromHostOnNetworkThread() {
432 DCHECK(jni_runtime_
->network_task_runner()->BelongsToCurrentThread());
436 stats_logging_enabled_
= false;
438 // |client_| must be torn down before |signaling_|.
440 client_status_logger_
.reset();
441 client_context_
.reset();
442 video_renderer_
.reset();
443 authenticator_
.reset();
447 void ChromotingJniInstance::FetchSecret(
449 const protocol::SecretFetchedCallback
& callback
) {
450 if (!jni_runtime_
->ui_task_runner()->BelongsToCurrentThread()) {
451 jni_runtime_
->ui_task_runner()->PostTask(
452 FROM_HERE
, base::Bind(&ChromotingJniInstance::FetchSecret
,
453 this, pairable
, callback
));
457 // Delete pairing credentials if they exist.
458 jni_runtime_
->CommitPairingCredentials(host_id_
, "", "");
460 pin_callback_
= callback
;
461 jni_runtime_
->DisplayAuthenticationPrompt(pairable
);
464 void ChromotingJniInstance::SetDeviceName(const std::string
& device_name
) {
465 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
466 jni_runtime_
->network_task_runner()->PostTask(
467 FROM_HERE
, base::Bind(&ChromotingJniInstance::SetDeviceName
, this,
472 device_name_
= device_name
;
475 void ChromotingJniInstance::SendKeyEventInternal(int usb_key_code
,
477 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
478 jni_runtime_
->network_task_runner()->PostTask(
479 FROM_HERE
, base::Bind(&ChromotingJniInstance::SendKeyEventInternal
,
480 this, usb_key_code
, key_down
));
485 protocol::KeyEvent event
;
486 event
.set_usb_keycode(usb_key_code
);
487 event
.set_pressed(key_down
);
488 client_
->input_stub()->InjectKeyEvent(event
);
491 void ChromotingJniInstance::EnableStatsLogging(bool enabled
) {
492 DCHECK(jni_runtime_
->network_task_runner()->BelongsToCurrentThread());
494 if (enabled
&& !stats_logging_enabled_
) {
495 jni_runtime_
->network_task_runner()->PostDelayedTask(
496 FROM_HERE
, base::Bind(&ChromotingJniInstance::LogPerfStats
, this),
497 base::TimeDelta::FromMilliseconds(kPerfStatsIntervalMs
));
499 stats_logging_enabled_
= enabled
;
502 void ChromotingJniInstance::LogPerfStats() {
503 DCHECK(jni_runtime_
->network_task_runner()->BelongsToCurrentThread());
505 if (!stats_logging_enabled_
)
508 ChromotingStats
* stats
= video_renderer_
->GetStats();
509 __android_log_print(ANDROID_LOG_INFO
, "stats",
510 "Bandwidth:%.0f FrameRate:%.1f Capture:%.1f Encode:%.1f "
511 "Decode:%.1f Render:%.1f Latency:%.0f",
512 stats
->video_bandwidth()->Rate(),
513 stats
->video_frame_rate()->Rate(),
514 stats
->video_capture_ms()->Average(),
515 stats
->video_encode_ms()->Average(),
516 stats
->video_decode_ms()->Average(),
517 stats
->video_paint_ms()->Average(),
518 stats
->round_trip_ms()->Average());
520 client_status_logger_
->LogStatistics(stats
);
522 jni_runtime_
->network_task_runner()->PostDelayedTask(
523 FROM_HERE
, base::Bind(&ChromotingJniInstance::LogPerfStats
, this),
524 base::TimeDelta::FromMilliseconds(kPerfStatsIntervalMs
));
527 } // namespace remoting