#include "base/bind.h"
#include "base/logging.h"
+#include "jingle/glue/thread_wrapper.h"
#include "net/socket/client_socket_factory.h"
+#include "remoting/base/service_urls.h"
#include "remoting/client/audio_player.h"
+#include "remoting/client/client_status_logger.h"
#include "remoting/client/jni/android_keymap.h"
#include "remoting/client/jni/chromoting_jni_runtime.h"
#include "remoting/client/software_video_renderer.h"
-#include "remoting/jingle_glue/chromium_port_allocator.h"
-#include "remoting/jingle_glue/chromium_socket_factory.h"
-#include "remoting/jingle_glue/network_settings.h"
+#include "remoting/client/token_fetcher_proxy.h"
+#include "remoting/protocol/chromium_port_allocator.h"
+#include "remoting/protocol/chromium_socket_factory.h"
#include "remoting/protocol/host_stub.h"
#include "remoting/protocol/libjingle_transport_factory.h"
+#include "remoting/protocol/negotiating_client_authenticator.h"
+#include "remoting/protocol/network_settings.h"
+#include "remoting/signaling/server_log_entry.h"
namespace remoting {
const bool kXmppUseTls = true;
// Interval at which to log performance statistics, if enabled.
-const int kPerfStatsIntervalMs = 10000;
+const int kPerfStatsIntervalMs = 60000;
}
const char* host_id,
const char* host_pubkey,
const char* pairing_id,
- const char* pairing_secret)
+ const char* pairing_secret,
+ const char* capabilities)
: jni_runtime_(jni_runtime),
host_id_(host_id),
+ host_jid_(host_jid),
create_pairing_(false),
- stats_logging_enabled_(false) {
+ stats_logging_enabled_(false),
+ capabilities_(capabilities),
+ weak_factory_(this) {
DCHECK(jni_runtime_->ui_task_runner()->BelongsToCurrentThread());
// Intialize XMPP config.
xmpp_config_.auth_token = auth_token;
xmpp_config_.auth_service = "oauth2";
- // Initialize ClientConfig.
- client_config_.host_jid = host_jid;
- client_config_.host_public_key = host_pubkey;
-
- client_config_.fetch_secret_callback =
- base::Bind(&ChromotingJniInstance::FetchSecret, this);
- client_config_.authentication_tag = host_id_;
-
- client_config_.client_pairing_id = pairing_id;
- client_config_.client_paired_secret = pairing_secret;
-
- client_config_.authentication_methods.push_back(
- protocol::AuthenticationMethod::FromString("spake2_pair"));
- client_config_.authentication_methods.push_back(
- protocol::AuthenticationMethod::FromString("spake2_hmac"));
- client_config_.authentication_methods.push_back(
- protocol::AuthenticationMethod::FromString("spake2_plain"));
+ // Initialize |authenticator_|.
+ scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher>
+ token_fetcher(new TokenFetcherProxy(
+ base::Bind(&ChromotingJniInstance::FetchThirdPartyToken,
+ weak_factory_.GetWeakPtr()),
+ host_pubkey));
+
+ std::vector<protocol::AuthenticationMethod> auth_methods;
+ auth_methods.push_back(protocol::AuthenticationMethod::Spake2Pair());
+ auth_methods.push_back(protocol::AuthenticationMethod::Spake2(
+ protocol::AuthenticationMethod::HMAC_SHA256));
+ auth_methods.push_back(protocol::AuthenticationMethod::Spake2(
+ protocol::AuthenticationMethod::NONE));
+ auth_methods.push_back(protocol::AuthenticationMethod::ThirdParty());
+
+ authenticator_.reset(new protocol::NegotiatingClientAuthenticator(
+ pairing_id, pairing_secret, host_id_,
+ base::Bind(&ChromotingJniInstance::FetchSecret, this),
+ token_fetcher.Pass(), auth_methods));
// Post a task to start connection
jni_runtime_->display_task_runner()->PostTask(
this));
}
-ChromotingJniInstance::~ChromotingJniInstance() {}
+ChromotingJniInstance::~ChromotingJniInstance() {
+ // This object is ref-counted, so this dtor can execute on any thread.
+ // Ensure that all these objects have been freed already, so they are not
+ // destroyed on some random thread.
+ DCHECK(!view_);
+ DCHECK(!client_context_);
+ DCHECK(!video_renderer_);
+ DCHECK(!authenticator_);
+ DCHECK(!client_);
+ DCHECK(!signaling_);
+ DCHECK(!client_status_logger_);
+}
-void ChromotingJniInstance::Cleanup() {
+void ChromotingJniInstance::Disconnect() {
if (!jni_runtime_->display_task_runner()->BelongsToCurrentThread()) {
jni_runtime_->display_task_runner()->PostTask(
FROM_HERE,
- base::Bind(&ChromotingJniInstance::Cleanup, this));
+ base::Bind(&ChromotingJniInstance::Disconnect, this));
return;
}
this));
}
+void ChromotingJniInstance::FetchThirdPartyToken(
+ const GURL& token_url,
+ const std::string& client_id,
+ const std::string& scope,
+ base::WeakPtr<TokenFetcherProxy> token_fetcher_proxy) {
+ DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread());
+ DCHECK(!token_fetcher_proxy_.get());
+
+ __android_log_print(ANDROID_LOG_INFO,
+ "ThirdPartyAuth",
+ "Fetching Third Party Token from user.");
+
+ token_fetcher_proxy_ = token_fetcher_proxy;
+ jni_runtime_->ui_task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&ChromotingJniRuntime::FetchThirdPartyToken,
+ base::Unretained(jni_runtime_),
+ token_url,
+ client_id,
+ scope));
+}
+
+void ChromotingJniInstance::HandleOnThirdPartyTokenFetched(
+ const std::string& token,
+ const std::string& shared_secret) {
+ DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread());
+
+ __android_log_print(
+ ANDROID_LOG_INFO, "ThirdPartyAuth", "Third Party Token Fetched.");
+
+ if (token_fetcher_proxy_.get()) {
+ token_fetcher_proxy_->OnTokenFetched(token, shared_secret);
+ token_fetcher_proxy_.reset();
+ } else {
+ __android_log_print(
+ ANDROID_LOG_WARN,
+ "ThirdPartyAuth",
+ "Ignored OnThirdPartyTokenFetched() without a pending fetch.");
+ }
+}
+
void ChromotingJniInstance::ProvideSecret(const std::string& pin,
- bool create_pairing) {
+ bool create_pairing,
+ const std::string& device_name) {
DCHECK(jni_runtime_->ui_task_runner()->BelongsToCurrentThread());
DCHECK(!pin_callback_.is_null());
create_pairing_ = create_pairing;
+ if (create_pairing)
+ SetDeviceName(device_name);
+
jni_runtime_->network_task_runner()->PostTask(FROM_HERE,
base::Bind(pin_callback_, pin));
}
jni_runtime_->RedrawCanvas();
}
-void ChromotingJniInstance::PerformMouseAction(
+void ChromotingJniInstance::SendMouseEvent(
int x, int y,
protocol::MouseEvent_MouseButton button,
bool button_down) {
if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) {
jni_runtime_->network_task_runner()->PostTask(
- FROM_HERE, base::Bind(&ChromotingJniInstance::PerformMouseAction,
+ FROM_HERE, base::Bind(&ChromotingJniInstance::SendMouseEvent,
this, x, y, button, button_down));
return;
}
- protocol::MouseEvent action;
- action.set_x(x);
- action.set_y(y);
- action.set_button(button);
+ protocol::MouseEvent event;
+ event.set_x(x);
+ event.set_y(y);
+ event.set_button(button);
if (button != protocol::MouseEvent::BUTTON_UNDEFINED)
- action.set_button_down(button_down);
+ event.set_button_down(button_down);
- connection_->input_stub()->InjectMouseEvent(action);
+ client_->input_stub()->InjectMouseEvent(event);
}
-void ChromotingJniInstance::PerformMouseWheelDeltaAction(int delta_x,
- int delta_y) {
+void ChromotingJniInstance::SendMouseWheelEvent(int delta_x, int delta_y) {
if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) {
jni_runtime_->network_task_runner()->PostTask(
FROM_HERE,
- base::Bind(&ChromotingJniInstance::PerformMouseWheelDeltaAction, this,
+ base::Bind(&ChromotingJniInstance::SendMouseWheelEvent, this,
delta_x, delta_y));
return;
}
- protocol::MouseEvent action;
- action.set_wheel_delta_x(delta_x);
- action.set_wheel_delta_y(delta_y);
- connection_->input_stub()->InjectMouseEvent(action);
+ protocol::MouseEvent event;
+ event.set_wheel_delta_x(delta_x);
+ event.set_wheel_delta_y(delta_y);
+ client_->input_stub()->InjectMouseEvent(event);
}
-void ChromotingJniInstance::PerformKeyboardAction(int key_code, bool key_down) {
+bool ChromotingJniInstance::SendKeyEvent(int key_code, bool key_down) {
+ uint32 usb_key_code = AndroidKeycodeToUsbKeycode(key_code);
+ if (!usb_key_code) {
+ LOG(WARNING) << "Ignoring unknown keycode: " << key_code;
+ return false;
+ }
+
+ SendKeyEventInternal(usb_key_code, key_down);
+ return true;
+}
+
+void ChromotingJniInstance::SendTextEvent(const std::string& text) {
if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) {
jni_runtime_->network_task_runner()->PostTask(
- FROM_HERE, base::Bind(&ChromotingJniInstance::PerformKeyboardAction,
- this, key_code, key_down));
+ FROM_HERE,
+ base::Bind(&ChromotingJniInstance::SendTextEvent, this, text));
return;
}
- uint32 usb_code = AndroidKeycodeToUsbKeycode(key_code);
- if (usb_code) {
- protocol::KeyEvent action;
- action.set_usb_keycode(usb_code);
- action.set_pressed(key_down);
- connection_->input_stub()->InjectKeyEvent(action);
- } else {
- LOG(WARNING) << "Ignoring unknown keycode: " << key_code;
+ protocol::TextEvent event;
+ event.set_text(text);
+ client_->input_stub()->InjectTextEvent(event);
+}
+
+void ChromotingJniInstance::SendClientMessage(const std::string& type,
+ const std::string& data) {
+ if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) {
+ jni_runtime_->network_task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &ChromotingJniInstance::SendClientMessage, this, type, data));
+ return;
}
+
+ protocol::ExtensionMessage extension_message;
+ extension_message.set_type(type);
+ extension_message.set_data(data);
+ client_->host_stub()->DeliverClientMessage(extension_message);
}
void ChromotingJniInstance::RecordPaintTime(int64 paint_time_ms) {
EnableStatsLogging(state == protocol::ConnectionToHost::CONNECTED);
+ client_status_logger_->LogSessionStateChange(state, error);
+
if (create_pairing_ && state == protocol::ConnectionToHost::CONNECTED) {
protocol::PairingRequest request;
- request.set_client_name("Android");
- connection_->host_stub()->RequestPairing(request);
+ DCHECK(!device_name_.empty());
+ request.set_client_name(device_name_);
+ client_->host_stub()->RequestPairing(request);
}
jni_runtime_->ui_task_runner()->PostTask(
FROM_HERE,
- base::Bind(&ChromotingJniRuntime::ReportConnectionStatus,
+ base::Bind(&ChromotingJniRuntime::OnConnectionState,
base::Unretained(jni_runtime_),
state,
error));
}
void ChromotingJniInstance::SetCapabilities(const std::string& capabilities) {
- NOTIMPLEMENTED();
+ jni_runtime_->ui_task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&ChromotingJniRuntime::SetCapabilities,
+ base::Unretained(jni_runtime_),
+ capabilities));
}
void ChromotingJniInstance::SetPairingResponse(
void ChromotingJniInstance::DeliverHostMessage(
const protocol::ExtensionMessage& message) {
- NOTIMPLEMENTED();
+ jni_runtime_->ui_task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&ChromotingJniRuntime::HandleExtensionMessage,
+ base::Unretained(jni_runtime_),
+ message.type(),
+ message.data()));
}
protocol::ClipboardStub* ChromotingJniInstance::GetClipboardStub() {
return this;
}
-scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher>
- ChromotingJniInstance::GetTokenFetcher(const std::string& host_public_key) {
- // Return null to indicate that third-party authentication is unsupported.
- return scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher>();
-}
-
void ChromotingJniInstance::InjectClipboardEvent(
const protocol::ClipboardEvent& event) {
NOTIMPLEMENTED();
void ChromotingJniInstance::ConnectToHostOnNetworkThread() {
DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread());
+ jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
+
client_context_.reset(new ClientContext(
jni_runtime_->network_task_runner().get()));
client_context_->Start();
- connection_.reset(new protocol::ConnectionToHost(true));
-
SoftwareVideoRenderer* renderer =
new SoftwareVideoRenderer(client_context_->main_task_runner(),
client_context_->decode_task_runner(),
video_renderer_.reset(renderer);
client_.reset(new ChromotingClient(
- client_config_, client_context_.get(), connection_.get(),
- this, video_renderer_.get(), scoped_ptr<AudioPlayer>()));
-
+ client_context_.get(), this, video_renderer_.get(), nullptr));
signaling_.reset(new XmppSignalStrategy(
net::ClientSocketFactory::GetDefaultFactory(),
jni_runtime_->url_requester(), xmpp_config_));
- NetworkSettings network_settings(NetworkSettings::NAT_TRAVERSAL_ENABLED);
+ client_status_logger_.reset(
+ new ClientStatusLogger(ServerLogEntry::ME2ME,
+ signaling_.get(),
+ ServiceUrls::GetInstance()->directory_bot_jid()));
+
+ protocol::NetworkSettings network_settings(
+ protocol::NetworkSettings::NAT_TRAVERSAL_FULL);
// Use Chrome's network stack to allocate ports for peer-to-peer channels.
- scoped_ptr<ChromiumPortAllocator> port_allocator(
- ChromiumPortAllocator::Create(jni_runtime_->url_requester(),
- network_settings));
+ scoped_ptr<protocol::ChromiumPortAllocator> port_allocator(
+ protocol::ChromiumPortAllocator::Create(jni_runtime_->url_requester(),
+ network_settings));
scoped_ptr<protocol::TransportFactory> transport_factory(
new protocol::LibjingleTransportFactory(
- signaling_.get(),
- port_allocator.PassAs<cricket::HttpPortAllocatorBase>(),
- network_settings));
+ signaling_.get(), port_allocator.Pass(), network_settings));
- client_->Start(signaling_.get(), transport_factory.Pass());
+ client_->Start(signaling_.get(), authenticator_.Pass(),
+ transport_factory.Pass(), host_jid_, capabilities_);
}
void ChromotingJniInstance::DisconnectFromHostOnNetworkThread() {
stats_logging_enabled_ = false;
// |client_| must be torn down before |signaling_|.
- connection_.reset();
client_.reset();
+ client_status_logger_.reset();
+ client_context_.reset();
+ video_renderer_.reset();
+ authenticator_.reset();
+ signaling_.reset();
}
void ChromotingJniInstance::FetchSecret(
return;
}
- if (!client_config_.client_pairing_id.empty()) {
- // We attempted to connect using an existing pairing that was rejected.
- // Unless we forget about the stale credentials, we'll continue trying them.
- jni_runtime_->CommitPairingCredentials(host_id_, "", "");
- }
+ // Delete pairing credentials if they exist.
+ jni_runtime_->CommitPairingCredentials(host_id_, "", "");
pin_callback_ = callback;
jni_runtime_->DisplayAuthenticationPrompt(pairable);
}
+void ChromotingJniInstance::SetDeviceName(const std::string& device_name) {
+ if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) {
+ jni_runtime_->network_task_runner()->PostTask(
+ FROM_HERE, base::Bind(&ChromotingJniInstance::SetDeviceName, this,
+ device_name));
+ return;
+ }
+
+ device_name_ = device_name;
+}
+
+void ChromotingJniInstance::SendKeyEventInternal(int usb_key_code,
+ bool key_down) {
+ if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) {
+ jni_runtime_->network_task_runner()->PostTask(
+ FROM_HERE, base::Bind(&ChromotingJniInstance::SendKeyEventInternal,
+ this, usb_key_code, key_down));
+ return;
+ }
+
+
+ protocol::KeyEvent event;
+ event.set_usb_keycode(usb_key_code);
+ event.set_pressed(key_down);
+ client_->input_stub()->InjectKeyEvent(event);
+}
+
void ChromotingJniInstance::EnableStatsLogging(bool enabled) {
DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread());
stats->video_paint_ms()->Average(),
stats->round_trip_ms()->Average());
+ client_status_logger_->LogStatistics(stats);
+
jni_runtime_->network_task_runner()->PostDelayedTask(
FROM_HERE, base::Bind(&ChromotingJniInstance::LogPerfStats, this),
base::TimeDelta::FromMilliseconds(kPerfStatsIntervalMs));