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
;
67 // Initialize |authenticator_|.
68 scoped_ptr
<protocol::ThirdPartyClientAuthenticator::TokenFetcher
>
69 token_fetcher(new TokenFetcherProxy(
70 base::Bind(&ChromotingJniInstance::FetchThirdPartyToken
,
71 weak_factory_
.GetWeakPtr()),
74 std::vector
<protocol::AuthenticationMethod
> auth_methods
;
75 auth_methods
.push_back(protocol::AuthenticationMethod::Spake2Pair());
76 auth_methods
.push_back(protocol::AuthenticationMethod::Spake2(
77 protocol::AuthenticationMethod::HMAC_SHA256
));
78 auth_methods
.push_back(protocol::AuthenticationMethod::Spake2(
79 protocol::AuthenticationMethod::NONE
));
80 auth_methods
.push_back(protocol::AuthenticationMethod::ThirdParty());
82 authenticator_
.reset(new protocol::NegotiatingClientAuthenticator(
83 pairing_id
, pairing_secret
, host_id_
,
84 base::Bind(&ChromotingJniInstance::FetchSecret
, this),
85 token_fetcher
.Pass(), auth_methods
));
87 // Post a task to start connection
88 jni_runtime_
->display_task_runner()->PostTask(
90 base::Bind(&ChromotingJniInstance::ConnectToHostOnDisplayThread
,
94 ChromotingJniInstance::~ChromotingJniInstance() {
95 // This object is ref-counted, so this dtor can execute on any thread.
96 // Ensure that all these objects have been freed already, so they are not
97 // destroyed on some random thread.
99 DCHECK(!client_context_
);
100 DCHECK(!video_renderer_
);
101 DCHECK(!authenticator_
);
104 DCHECK(!client_status_logger_
);
107 void ChromotingJniInstance::Disconnect() {
108 if (!jni_runtime_
->display_task_runner()->BelongsToCurrentThread()) {
109 jni_runtime_
->display_task_runner()->PostTask(
111 base::Bind(&ChromotingJniInstance::Disconnect
, this));
115 // This must be destroyed on the display thread before the producer is gone.
118 // The weak pointers must be invalidated on the same thread they were used.
119 view_weak_factory_
->InvalidateWeakPtrs();
121 jni_runtime_
->network_task_runner()->PostTask(
123 base::Bind(&ChromotingJniInstance::DisconnectFromHostOnNetworkThread
,
127 void ChromotingJniInstance::FetchThirdPartyToken(
128 const GURL
& token_url
,
129 const std::string
& client_id
,
130 const std::string
& scope
,
131 base::WeakPtr
<TokenFetcherProxy
> token_fetcher_proxy
) {
132 DCHECK(jni_runtime_
->network_task_runner()->BelongsToCurrentThread());
133 DCHECK(!token_fetcher_proxy_
.get());
135 __android_log_print(ANDROID_LOG_INFO
,
137 "Fetching Third Party Token from user.");
139 token_fetcher_proxy_
= token_fetcher_proxy
;
140 jni_runtime_
->ui_task_runner()->PostTask(
142 base::Bind(&ChromotingJniRuntime::FetchThirdPartyToken
,
143 base::Unretained(jni_runtime_
),
149 void ChromotingJniInstance::HandleOnThirdPartyTokenFetched(
150 const std::string
& token
,
151 const std::string
& shared_secret
) {
152 DCHECK(jni_runtime_
->network_task_runner()->BelongsToCurrentThread());
155 ANDROID_LOG_INFO
, "ThirdPartyAuth", "Third Party Token Fetched.");
157 if (token_fetcher_proxy_
.get()) {
158 token_fetcher_proxy_
->OnTokenFetched(token
, shared_secret
);
159 token_fetcher_proxy_
.reset();
164 "Ignored OnThirdPartyTokenFetched() without a pending fetch.");
168 void ChromotingJniInstance::ProvideSecret(const std::string
& pin
,
170 const std::string
& device_name
) {
171 DCHECK(jni_runtime_
->ui_task_runner()->BelongsToCurrentThread());
172 DCHECK(!pin_callback_
.is_null());
174 create_pairing_
= create_pairing
;
177 SetDeviceName(device_name
);
179 jni_runtime_
->network_task_runner()->PostTask(FROM_HERE
,
180 base::Bind(pin_callback_
, pin
));
183 void ChromotingJniInstance::RedrawDesktop() {
184 if (!jni_runtime_
->display_task_runner()->BelongsToCurrentThread()) {
185 jni_runtime_
->display_task_runner()->PostTask(
187 base::Bind(&ChromotingJniInstance::RedrawDesktop
, this));
191 jni_runtime_
->RedrawCanvas();
194 void ChromotingJniInstance::SendMouseEvent(
196 protocol::MouseEvent_MouseButton button
,
198 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
199 jni_runtime_
->network_task_runner()->PostTask(
200 FROM_HERE
, base::Bind(&ChromotingJniInstance::SendMouseEvent
,
201 this, x
, y
, button
, button_down
));
205 protocol::MouseEvent event
;
208 event
.set_button(button
);
209 if (button
!= protocol::MouseEvent::BUTTON_UNDEFINED
)
210 event
.set_button_down(button_down
);
212 client_
->input_stub()->InjectMouseEvent(event
);
215 void ChromotingJniInstance::SendMouseWheelEvent(int delta_x
, int delta_y
) {
216 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
217 jni_runtime_
->network_task_runner()->PostTask(
219 base::Bind(&ChromotingJniInstance::SendMouseWheelEvent
, this,
224 protocol::MouseEvent event
;
225 event
.set_wheel_delta_x(delta_x
);
226 event
.set_wheel_delta_y(delta_y
);
227 client_
->input_stub()->InjectMouseEvent(event
);
230 bool ChromotingJniInstance::SendKeyEvent(int key_code
, bool key_down
) {
231 uint32 usb_key_code
= AndroidKeycodeToUsbKeycode(key_code
);
233 LOG(WARNING
) << "Ignoring unknown keycode: " << key_code
;
237 SendKeyEventInternal(usb_key_code
, key_down
);
241 void ChromotingJniInstance::SendTextEvent(const std::string
& text
) {
242 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
243 jni_runtime_
->network_task_runner()->PostTask(
245 base::Bind(&ChromotingJniInstance::SendTextEvent
, this, text
));
249 protocol::TextEvent event
;
250 event
.set_text(text
);
251 client_
->input_stub()->InjectTextEvent(event
);
254 void ChromotingJniInstance::EnableVideoChannel(bool enable
) {
255 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
256 jni_runtime_
->network_task_runner()->PostTask(
258 base::Bind(&ChromotingJniInstance::EnableVideoChannel
, this, enable
));
262 protocol::VideoControl video_control
;
263 video_control
.set_enable(enable
);
264 client_
->host_stub()->ControlVideo(video_control
);
267 void ChromotingJniInstance::SendClientMessage(const std::string
& type
,
268 const std::string
& data
) {
269 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
270 jni_runtime_
->network_task_runner()->PostTask(
273 &ChromotingJniInstance::SendClientMessage
, this, type
, data
));
277 protocol::ExtensionMessage extension_message
;
278 extension_message
.set_type(type
);
279 extension_message
.set_data(data
);
280 client_
->host_stub()->DeliverClientMessage(extension_message
);
283 void ChromotingJniInstance::RecordPaintTime(int64 paint_time_ms
) {
284 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
285 jni_runtime_
->network_task_runner()->PostTask(
286 FROM_HERE
, base::Bind(&ChromotingJniInstance::RecordPaintTime
, this,
291 if (stats_logging_enabled_
)
292 video_renderer_
->GetStats()->video_paint_ms()->Record(paint_time_ms
);
295 void ChromotingJniInstance::OnConnectionState(
296 protocol::ConnectionToHost::State state
,
297 protocol::ErrorCode error
) {
298 DCHECK(jni_runtime_
->network_task_runner()->BelongsToCurrentThread());
300 EnableStatsLogging(state
== protocol::ConnectionToHost::CONNECTED
);
302 client_status_logger_
->LogSessionStateChange(state
, error
);
304 if (create_pairing_
&& state
== protocol::ConnectionToHost::CONNECTED
) {
305 protocol::PairingRequest request
;
306 DCHECK(!device_name_
.empty());
307 request
.set_client_name(device_name_
);
308 client_
->host_stub()->RequestPairing(request
);
311 jni_runtime_
->ui_task_runner()->PostTask(
313 base::Bind(&ChromotingJniRuntime::OnConnectionState
,
314 base::Unretained(jni_runtime_
),
319 void ChromotingJniInstance::OnConnectionReady(bool ready
) {
320 // We ignore this message, since OnConnectionState tells us the same thing.
323 void ChromotingJniInstance::OnRouteChanged(
324 const std::string
& channel_name
,
325 const protocol::TransportRoute
& route
) {
326 std::string message
= "Channel " + channel_name
+ " using " +
327 protocol::TransportRoute::GetTypeString(route
.type
) + " connection.";
328 __android_log_print(ANDROID_LOG_INFO
, "route", "%s", message
.c_str());
331 void ChromotingJniInstance::SetCapabilities(const std::string
& capabilities
) {
332 jni_runtime_
->ui_task_runner()->PostTask(
334 base::Bind(&ChromotingJniRuntime::SetCapabilities
,
335 base::Unretained(jni_runtime_
),
339 void ChromotingJniInstance::SetPairingResponse(
340 const protocol::PairingResponse
& response
) {
342 jni_runtime_
->ui_task_runner()->PostTask(
344 base::Bind(&ChromotingJniRuntime::CommitPairingCredentials
,
345 base::Unretained(jni_runtime_
),
346 host_id_
, response
.client_id(), response
.shared_secret()));
349 void ChromotingJniInstance::DeliverHostMessage(
350 const protocol::ExtensionMessage
& message
) {
351 jni_runtime_
->ui_task_runner()->PostTask(
353 base::Bind(&ChromotingJniRuntime::HandleExtensionMessage
,
354 base::Unretained(jni_runtime_
),
359 protocol::ClipboardStub
* ChromotingJniInstance::GetClipboardStub() {
363 protocol::CursorShapeStub
* ChromotingJniInstance::GetCursorShapeStub() {
367 void ChromotingJniInstance::InjectClipboardEvent(
368 const protocol::ClipboardEvent
& event
) {
372 void ChromotingJniInstance::SetCursorShape(
373 const protocol::CursorShapeInfo
& shape
) {
374 if (!jni_runtime_
->display_task_runner()->BelongsToCurrentThread()) {
375 jni_runtime_
->display_task_runner()->PostTask(
377 base::Bind(&ChromotingJniInstance::SetCursorShape
, this, shape
));
381 jni_runtime_
->UpdateCursorShape(shape
);
384 void ChromotingJniInstance::ConnectToHostOnDisplayThread() {
385 DCHECK(jni_runtime_
->display_task_runner()->BelongsToCurrentThread());
387 view_
.reset(new JniFrameConsumer(jni_runtime_
, this));
388 view_weak_factory_
.reset(new base::WeakPtrFactory
<JniFrameConsumer
>(
390 frame_consumer_
= new FrameConsumerProxy(jni_runtime_
->display_task_runner(),
391 view_weak_factory_
->GetWeakPtr());
393 jni_runtime_
->network_task_runner()->PostTask(
395 base::Bind(&ChromotingJniInstance::ConnectToHostOnNetworkThread
,
399 void ChromotingJniInstance::ConnectToHostOnNetworkThread() {
400 DCHECK(jni_runtime_
->network_task_runner()->BelongsToCurrentThread());
402 jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
404 client_context_
.reset(new ClientContext(
405 jni_runtime_
->network_task_runner().get()));
406 client_context_
->Start();
408 SoftwareVideoRenderer
* renderer
=
409 new SoftwareVideoRenderer(client_context_
->main_task_runner(),
410 client_context_
->decode_task_runner(),
412 view_
->set_frame_producer(renderer
);
413 video_renderer_
.reset(renderer
);
415 client_
.reset(new ChromotingClient(
416 client_context_
.get(), this, video_renderer_
.get(), nullptr));
418 signaling_
.reset(new XmppSignalStrategy(
419 net::ClientSocketFactory::GetDefaultFactory(),
420 jni_runtime_
->url_requester(), xmpp_config_
));
422 client_status_logger_
.reset(
423 new ClientStatusLogger(ServerLogEntry::ME2ME
,
425 ServiceUrls::GetInstance()->directory_bot_jid()));
427 protocol::NetworkSettings
network_settings(
428 protocol::NetworkSettings::NAT_TRAVERSAL_FULL
);
430 // Use Chrome's network stack to allocate ports for peer-to-peer channels.
431 scoped_ptr
<protocol::ChromiumPortAllocator
> port_allocator(
432 protocol::ChromiumPortAllocator::Create(jni_runtime_
->url_requester(),
435 scoped_ptr
<protocol::TransportFactory
> transport_factory(
436 new protocol::LibjingleTransportFactory(
437 signaling_
.get(), port_allocator
.Pass(), network_settings
,
438 protocol::TransportRole::CLIENT
));
440 client_
->Start(signaling_
.get(), authenticator_
.Pass(),
441 transport_factory
.Pass(), host_jid_
, capabilities_
);
444 void ChromotingJniInstance::DisconnectFromHostOnNetworkThread() {
445 DCHECK(jni_runtime_
->network_task_runner()->BelongsToCurrentThread());
449 stats_logging_enabled_
= false;
451 // |client_| must be torn down before |signaling_|.
453 client_status_logger_
.reset();
454 video_renderer_
.reset();
455 authenticator_
.reset();
457 client_context_
.reset();
460 void ChromotingJniInstance::FetchSecret(
462 const protocol::SecretFetchedCallback
& callback
) {
463 if (!jni_runtime_
->ui_task_runner()->BelongsToCurrentThread()) {
464 jni_runtime_
->ui_task_runner()->PostTask(
465 FROM_HERE
, base::Bind(&ChromotingJniInstance::FetchSecret
,
466 this, pairable
, callback
));
470 // Delete pairing credentials if they exist.
471 jni_runtime_
->CommitPairingCredentials(host_id_
, "", "");
473 pin_callback_
= callback
;
474 jni_runtime_
->DisplayAuthenticationPrompt(pairable
);
477 void ChromotingJniInstance::SetDeviceName(const std::string
& device_name
) {
478 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
479 jni_runtime_
->network_task_runner()->PostTask(
480 FROM_HERE
, base::Bind(&ChromotingJniInstance::SetDeviceName
, this,
485 device_name_
= device_name
;
488 void ChromotingJniInstance::SendKeyEventInternal(int usb_key_code
,
490 if (!jni_runtime_
->network_task_runner()->BelongsToCurrentThread()) {
491 jni_runtime_
->network_task_runner()->PostTask(
492 FROM_HERE
, base::Bind(&ChromotingJniInstance::SendKeyEventInternal
,
493 this, usb_key_code
, key_down
));
498 protocol::KeyEvent event
;
499 event
.set_usb_keycode(usb_key_code
);
500 event
.set_pressed(key_down
);
501 client_
->input_stub()->InjectKeyEvent(event
);
504 void ChromotingJniInstance::EnableStatsLogging(bool enabled
) {
505 DCHECK(jni_runtime_
->network_task_runner()->BelongsToCurrentThread());
507 if (enabled
&& !stats_logging_enabled_
) {
508 jni_runtime_
->network_task_runner()->PostDelayedTask(
509 FROM_HERE
, base::Bind(&ChromotingJniInstance::LogPerfStats
, this),
510 base::TimeDelta::FromMilliseconds(kPerfStatsIntervalMs
));
512 stats_logging_enabled_
= enabled
;
515 void ChromotingJniInstance::LogPerfStats() {
516 DCHECK(jni_runtime_
->network_task_runner()->BelongsToCurrentThread());
518 if (!stats_logging_enabled_
)
521 ChromotingStats
* stats
= video_renderer_
->GetStats();
522 __android_log_print(ANDROID_LOG_INFO
, "stats",
523 "Bandwidth:%.0f FrameRate:%.1f Capture:%.1f Encode:%.1f "
524 "Decode:%.1f Render:%.1f Latency:%.0f",
525 stats
->video_bandwidth()->Rate(),
526 stats
->video_frame_rate()->Rate(),
527 stats
->video_capture_ms()->Average(),
528 stats
->video_encode_ms()->Average(),
529 stats
->video_decode_ms()->Average(),
530 stats
->video_paint_ms()->Average(),
531 stats
->round_trip_ms()->Average());
533 client_status_logger_
->LogStatistics(stats
);
535 jni_runtime_
->network_task_runner()->PostDelayedTask(
536 FROM_HERE
, base::Bind(&ChromotingJniInstance::LogPerfStats
, this),
537 base::TimeDelta::FromMilliseconds(kPerfStatsIntervalMs
));
540 } // namespace remoting