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 "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"
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;
33 // Interval at which to log performance statistics, if enabled.
34 const int kPerfStatsIntervalMs = 60000;
38 ChromotingJniInstance::ChromotingJniInstance(ChromotingJniRuntime* jni_runtime,
40 const char* auth_token,
43 const char* host_pubkey,
44 const char* pairing_id,
45 const char* pairing_secret)
46 : jni_runtime_(jni_runtime),
48 create_pairing_(false),
49 stats_logging_enabled_(false) {
50 DCHECK(jni_runtime_->ui_task_runner()->BelongsToCurrentThread());
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";
60 // Initialize ClientConfig.
61 client_config_.host_jid = host_jid;
62 client_config_.host_public_key = host_pubkey;
64 client_config_.fetch_secret_callback =
65 base::Bind(&ChromotingJniInstance::FetchSecret, this);
66 client_config_.authentication_tag = host_id_;
68 client_config_.client_pairing_id = pairing_id;
69 client_config_.client_paired_secret = pairing_secret;
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"));
78 // Post a task to start connection
79 jni_runtime_->display_task_runner()->PostTask(
81 base::Bind(&ChromotingJniInstance::ConnectToHostOnDisplayThread,
85 ChromotingJniInstance::~ChromotingJniInstance() {}
87 void ChromotingJniInstance::Cleanup() {
88 if (!jni_runtime_->display_task_runner()->BelongsToCurrentThread()) {
89 jni_runtime_->display_task_runner()->PostTask(
91 base::Bind(&ChromotingJniInstance::Cleanup, this));
95 // This must be destroyed on the display thread before the producer is gone.
98 // The weak pointers must be invalidated on the same thread they were used.
99 view_weak_factory_->InvalidateWeakPtrs();
101 jni_runtime_->network_task_runner()->PostTask(
103 base::Bind(&ChromotingJniInstance::DisconnectFromHostOnNetworkThread,
107 void ChromotingJniInstance::ProvideSecret(const std::string& pin,
109 const std::string& device_name) {
110 DCHECK(jni_runtime_->ui_task_runner()->BelongsToCurrentThread());
111 DCHECK(!pin_callback_.is_null());
113 create_pairing_ = create_pairing;
116 SetDeviceName(device_name);
118 jni_runtime_->network_task_runner()->PostTask(FROM_HERE,
119 base::Bind(pin_callback_, pin));
122 void ChromotingJniInstance::RedrawDesktop() {
123 if (!jni_runtime_->display_task_runner()->BelongsToCurrentThread()) {
124 jni_runtime_->display_task_runner()->PostTask(
126 base::Bind(&ChromotingJniInstance::RedrawDesktop, this));
130 jni_runtime_->RedrawCanvas();
133 void ChromotingJniInstance::SendMouseEvent(
135 protocol::MouseEvent_MouseButton button,
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));
144 protocol::MouseEvent event;
147 event.set_button(button);
148 if (button != protocol::MouseEvent::BUTTON_UNDEFINED)
149 event.set_button_down(button_down);
151 connection_->input_stub()->InjectMouseEvent(event);
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(
158 base::Bind(&ChromotingJniInstance::SendMouseWheelEvent, this,
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);
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));
177 uint32 usb_code = AndroidKeycodeToUsbKeycode(key_code);
179 protocol::KeyEvent event;
180 event.set_usb_keycode(usb_code);
181 event.set_pressed(key_down);
182 connection_->input_stub()->InjectKeyEvent(event);
184 LOG(WARNING) << "Ignoring unknown keycode: " << key_code;
188 void ChromotingJniInstance::SendTextEvent(const std::string& text) {
189 if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) {
190 jni_runtime_->network_task_runner()->PostTask(
192 base::Bind(&ChromotingJniInstance::SendTextEvent, this, text));
196 protocol::TextEvent event;
197 event.set_text(text);
198 connection_->input_stub()->InjectTextEvent(event);
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,
209 if (stats_logging_enabled_)
210 video_renderer_->GetStats()->video_paint_ms()->Record(paint_time_ms);
213 void ChromotingJniInstance::OnConnectionState(
214 protocol::ConnectionToHost::State state,
215 protocol::ErrorCode error) {
216 DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread());
218 EnableStatsLogging(state == protocol::ConnectionToHost::CONNECTED);
220 log_to_server_->LogSessionStateChange(state, error);
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);
229 jni_runtime_->ui_task_runner()->PostTask(
231 base::Bind(&ChromotingJniRuntime::ReportConnectionStatus,
232 base::Unretained(jni_runtime_),
237 void ChromotingJniInstance::OnConnectionReady(bool ready) {
238 // We ignore this message, since OnConnectionState tells us the same thing.
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());
249 void ChromotingJniInstance::SetCapabilities(const std::string& capabilities) {
253 void ChromotingJniInstance::SetPairingResponse(
254 const protocol::PairingResponse& response) {
256 jni_runtime_->ui_task_runner()->PostTask(
258 base::Bind(&ChromotingJniRuntime::CommitPairingCredentials,
259 base::Unretained(jni_runtime_),
260 host_id_, response.client_id(), response.shared_secret()));
263 void ChromotingJniInstance::DeliverHostMessage(
264 const protocol::ExtensionMessage& message) {
268 protocol::ClipboardStub* ChromotingJniInstance::GetClipboardStub() {
272 protocol::CursorShapeStub* ChromotingJniInstance::GetCursorShapeStub() {
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>();
282 void ChromotingJniInstance::InjectClipboardEvent(
283 const protocol::ClipboardEvent& event) {
287 void ChromotingJniInstance::SetCursorShape(
288 const protocol::CursorShapeInfo& shape) {
289 if (!jni_runtime_->display_task_runner()->BelongsToCurrentThread()) {
290 jni_runtime_->display_task_runner()->PostTask(
292 base::Bind(&ChromotingJniInstance::SetCursorShape, this, shape));
296 jni_runtime_->UpdateCursorShape(shape);
299 void ChromotingJniInstance::ConnectToHostOnDisplayThread() {
300 DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread());
302 view_.reset(new JniFrameConsumer(jni_runtime_, this));
303 view_weak_factory_.reset(new base::WeakPtrFactory<JniFrameConsumer>(
305 frame_consumer_ = new FrameConsumerProxy(jni_runtime_->display_task_runner(),
306 view_weak_factory_->GetWeakPtr());
308 jni_runtime_->network_task_runner()->PostTask(
310 base::Bind(&ChromotingJniInstance::ConnectToHostOnNetworkThread,
314 void ChromotingJniInstance::ConnectToHostOnNetworkThread() {
315 DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread());
317 client_context_.reset(new ClientContext(
318 jni_runtime_->network_task_runner().get()));
319 client_context_->Start();
321 connection_.reset(new protocol::ConnectionToHost(true));
323 SoftwareVideoRenderer* renderer =
324 new SoftwareVideoRenderer(client_context_->main_task_runner(),
325 client_context_->decode_task_runner(),
327 view_->set_frame_producer(renderer);
328 video_renderer_.reset(renderer);
330 client_.reset(new ChromotingClient(
331 client_config_, client_context_.get(), connection_.get(),
332 this, video_renderer_.get(), scoped_ptr<AudioPlayer>()));
335 signaling_.reset(new XmppSignalStrategy(
336 net::ClientSocketFactory::GetDefaultFactory(),
337 jni_runtime_->url_requester(), xmpp_config_));
339 log_to_server_.reset(new client::LogToServer(client::ServerLogEntry::ME2ME,
341 "remoting@bot.talk.google.com"));
343 NetworkSettings network_settings(NetworkSettings::NAT_TRAVERSAL_FULL);
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(),
350 scoped_ptr<protocol::TransportFactory> transport_factory(
351 new protocol::LibjingleTransportFactory(
353 port_allocator.PassAs<cricket::HttpPortAllocatorBase>(),
356 client_->Start(signaling_.get(), transport_factory.Pass());
359 void ChromotingJniInstance::DisconnectFromHostOnNetworkThread() {
360 DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread());
364 stats_logging_enabled_ = false;
366 // |client_| must be torn down before |signaling_|.
369 log_to_server_.reset();
372 void ChromotingJniInstance::FetchSecret(
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));
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_, "", "");
388 pin_callback_ = callback;
389 jni_runtime_->DisplayAuthenticationPrompt(pairable);
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,
400 device_name_ = device_name;
403 void ChromotingJniInstance::EnableStatsLogging(bool enabled) {
404 DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread());
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));
411 stats_logging_enabled_ = enabled;
414 void ChromotingJniInstance::LogPerfStats() {
415 DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread());
417 if (!stats_logging_enabled_)
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());
432 log_to_server_->LogStatistics(stats);
434 jni_runtime_->network_task_runner()->PostDelayedTask(
435 FROM_HERE, base::Bind(&ChromotingJniInstance::LogPerfStats, this),
436 base::TimeDelta::FromMilliseconds(kPerfStatsIntervalMs));
439 } // namespace remoting