summaryrefslogtreecommitdiffstats
path: root/remoting/ios/bridge/client_instance.cc
diff options
context:
space:
mode:
Diffstat (limited to 'remoting/ios/bridge/client_instance.cc')
-rw-r--r--remoting/ios/bridge/client_instance.cc397
1 files changed, 397 insertions, 0 deletions
diff --git a/remoting/ios/bridge/client_instance.cc b/remoting/ios/bridge/client_instance.cc
new file mode 100644
index 0000000..6066f95
--- /dev/null
+++ b/remoting/ios/bridge/client_instance.cc
@@ -0,0 +1,397 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/ios/bridge/client_instance.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/synchronization/waitable_event.h"
+#include "net/socket/client_socket_factory.h"
+#include "remoting/base/url_request_context.h"
+#include "remoting/client/audio_player.h"
+#include "remoting/client/plugin/delegating_signal_strategy.h"
+#include "remoting/ios/bridge/client_proxy.h"
+#include "remoting/jingle_glue/chromium_port_allocator.h"
+#include "remoting/protocol/host_stub.h"
+#include "remoting/protocol/libjingle_transport_factory.h"
+
+namespace {
+const char* const kXmppServer = "talk.google.com";
+const int kXmppPort = 5222;
+const bool kXmppUseTls = true;
+
+void DoNothing() {}
+} // namespace
+
+namespace remoting {
+
+ClientInstance::ClientInstance(const base::WeakPtr<ClientProxy>& proxy,
+ const std::string& username,
+ const std::string& auth_token,
+ const std::string& host_jid,
+ const std::string& host_id,
+ const std::string& host_pubkey,
+ const std::string& pairing_id,
+ const std::string& pairing_secret)
+ : proxyToClient_(proxy), host_id_(host_id), create_pairing_(false) {
+
+ if (!base::MessageLoop::current()) {
+ VLOG(1) << "Starting main message loop";
+ ui_loop_ = new base::MessageLoopForUI();
+ ui_loop_->Attach();
+ } else {
+ VLOG(1) << "Using existing main message loop";
+ ui_loop_ = base::MessageLoopForUI::current();
+ }
+
+ VLOG(1) << "Spawning additional threads";
+
+ // |ui_loop_| runs on the main thread, so |ui_task_runner_| will run on the
+ // main thread. We can not kill the main thread when the message loop becomes
+ // idle so the callback function does nothing (as opposed to the typical
+ // base::MessageLoop::QuitClosure())
+ ui_task_runner_ = new AutoThreadTaskRunner(ui_loop_->message_loop_proxy(),
+ base::Bind(&::DoNothing));
+
+ network_task_runner_ = AutoThread::CreateWithType(
+ "native_net", ui_task_runner_, base::MessageLoop::TYPE_IO);
+
+ url_requester_ = new URLRequestContextGetter(network_task_runner_);
+
+ client_context_.reset(new ClientContext(network_task_runner_));
+
+ DCHECK(ui_task_runner_->BelongsToCurrentThread());
+
+ // Initialize XMPP config.
+ xmpp_config_.host = kXmppServer;
+ xmpp_config_.port = kXmppPort;
+ xmpp_config_.use_tls = kXmppUseTls;
+ xmpp_config_.username = username;
+ 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_.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"));
+}
+
+ClientInstance::~ClientInstance() {}
+
+void ClientInstance::Start() {
+ DCHECK(ui_task_runner_->BelongsToCurrentThread());
+
+ // Creates a reference to |this|, so don't want to bind during constructor
+ client_config_.fetch_secret_callback =
+ base::Bind(&ClientInstance::FetchSecret, this);
+
+ view_.reset(new FrameConsumerBridge(
+ base::Bind(&ClientProxy::RedrawCanvas, proxyToClient_)));
+
+ // |consumer_proxy| must be created on the UI thread to proxy calls from the
+ // network or decode thread to the UI thread, but ownership will belong to a
+ // SoftwareVideoRenderer which runs on the network thread.
+ scoped_refptr<FrameConsumerProxy> consumer_proxy =
+ new FrameConsumerProxy(ui_task_runner_, view_->AsWeakPtr());
+
+ // Post a task to start connection
+ base::WaitableEvent done_event(true, false);
+ network_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&ClientInstance::ConnectToHostOnNetworkThread,
+ this,
+ consumer_proxy,
+ base::Bind(&base::WaitableEvent::Signal,
+ base::Unretained(&done_event))));
+ // Wait until initialization completes before continuing
+ done_event.Wait();
+}
+
+void ClientInstance::Cleanup() {
+ DCHECK(ui_task_runner_->BelongsToCurrentThread());
+
+ client_config_.fetch_secret_callback.Reset(); // Release ref to this
+ // |view_| must be destroyed on the UI thread before the producer is gone.
+ view_.reset();
+
+ base::WaitableEvent done_event(true, false);
+ network_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&ClientInstance::DisconnectFromHostOnNetworkThread,
+ this,
+ base::Bind(&base::WaitableEvent::Signal,
+ base::Unretained(&done_event))));
+ // Wait until we are fully disconnected before continuing
+ done_event.Wait();
+}
+
+// HOST attempts to continue automatically with previously supplied credentials,
+// if it can't it requests the user's PIN.
+void ClientInstance::FetchSecret(
+ bool pairable,
+ const protocol::SecretFetchedCallback& callback) {
+ if (!ui_task_runner_->BelongsToCurrentThread()) {
+ ui_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&ClientInstance::FetchSecret, this, pairable, callback));
+ return;
+ }
+
+ pin_callback_ = callback;
+
+ if (proxyToClient_) {
+ 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.
+ VLOG(1) << "Deleting rejected pairing credentials";
+
+ proxyToClient_->CommitPairingCredentials(host_id_, "", "");
+ }
+ proxyToClient_->DisplayAuthenticationPrompt(pairable);
+ }
+}
+
+void ClientInstance::ProvideSecret(const std::string& pin,
+ bool create_pairing) {
+ DCHECK(ui_task_runner_->BelongsToCurrentThread());
+ create_pairing_ = create_pairing;
+
+ // Before this function can complete, FetchSecret must be called
+ DCHECK(!pin_callback_.is_null());
+ network_task_runner_->PostTask(FROM_HERE, base::Bind(pin_callback_, pin));
+}
+
+void ClientInstance::PerformMouseAction(
+ const webrtc::DesktopVector& position,
+ const webrtc::DesktopVector& wheel_delta,
+ int /* protocol::MouseEvent_MouseButton */ whichButton,
+ bool button_down) {
+ if (!network_task_runner_->BelongsToCurrentThread()) {
+ network_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&ClientInstance::PerformMouseAction,
+ this,
+ position,
+ wheel_delta,
+ whichButton,
+ button_down));
+ return;
+ }
+
+ protocol::MouseEvent_MouseButton mButton;
+
+ // Button must be within the bounds of the MouseEvent_MouseButton enum.
+ switch (whichButton) {
+ case protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_LEFT:
+ mButton =
+ protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_LEFT;
+ break;
+ case protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_MAX:
+ mButton =
+ protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_MAX;
+ break;
+ case protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_MIDDLE:
+ mButton = protocol::MouseEvent_MouseButton::
+ MouseEvent_MouseButton_BUTTON_MIDDLE;
+ break;
+ case protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_RIGHT:
+ mButton =
+ protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_RIGHT;
+ break;
+ case protocol::MouseEvent_MouseButton::
+ MouseEvent_MouseButton_BUTTON_UNDEFINED:
+ mButton = protocol::MouseEvent_MouseButton::
+ MouseEvent_MouseButton_BUTTON_UNDEFINED;
+ break;
+ default:
+ LOG(FATAL) << "Invalid constant for MouseEvent_MouseButton";
+ mButton = protocol::MouseEvent_MouseButton::
+ MouseEvent_MouseButton_BUTTON_UNDEFINED;
+ break;
+ }
+
+ protocol::MouseEvent action;
+ action.set_x(position.x());
+ action.set_y(position.y());
+ action.set_wheel_delta_x(wheel_delta.x());
+ action.set_wheel_delta_y(wheel_delta.y());
+ action.set_button(mButton);
+ if (mButton != protocol::MouseEvent::BUTTON_UNDEFINED)
+ action.set_button_down(button_down);
+
+ connection_->input_stub()->InjectMouseEvent(action);
+}
+
+void ClientInstance::PerformKeyboardAction(int key_code, bool key_down) {
+ if (!network_task_runner_->BelongsToCurrentThread()) {
+ network_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &ClientInstance::PerformKeyboardAction, this, key_code, key_down));
+ return;
+ }
+
+ protocol::KeyEvent action;
+ action.set_usb_keycode(key_code);
+ action.set_pressed(key_down);
+ connection_->input_stub()->InjectKeyEvent(action);
+}
+
+void ClientInstance::OnConnectionState(protocol::ConnectionToHost::State state,
+ protocol::ErrorCode error) {
+ if (!ui_task_runner_->BelongsToCurrentThread()) {
+ ui_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&ClientInstance::OnConnectionState, this, state, error));
+ return;
+ }
+
+ // TODO (aboone) This functionality is not scheduled for QA yet.
+ // if (create_pairing_ && state == protocol::ConnectionToHost::CONNECTED) {
+ // VLOG(1) << "Attempting to pair with host";
+ // protocol::PairingRequest request;
+ // request.set_client_name("iOS");
+ // connection_->host_stub()->RequestPairing(request);
+ // }
+
+ if (proxyToClient_)
+ proxyToClient_->ReportConnectionStatus(state, error);
+}
+
+void ClientInstance::OnConnectionReady(bool ready) {
+ // We ignore this message, since OnConnectionState tells us the same thing.
+}
+
+void ClientInstance::OnRouteChanged(const std::string& channel_name,
+ const protocol::TransportRoute& route) {
+ VLOG(1) << "Using " << protocol::TransportRoute::GetTypeString(route.type)
+ << " connection for " << channel_name << " channel";
+}
+
+void ClientInstance::SetCapabilities(const std::string& capabilities) {
+ DCHECK(video_renderer_);
+ DCHECK(connection_);
+ DCHECK(connection_->state() == protocol::ConnectionToHost::CONNECTED);
+ video_renderer_->Initialize(connection_->config());
+}
+
+void ClientInstance::SetPairingResponse(
+ const protocol::PairingResponse& response) {
+ if (!ui_task_runner_->BelongsToCurrentThread()) {
+ ui_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&ClientInstance::SetPairingResponse, this, response));
+ return;
+ }
+
+ VLOG(1) << "Successfully established pairing with host";
+
+ if (proxyToClient_)
+ proxyToClient_->CommitPairingCredentials(
+ host_id_, response.client_id(), response.shared_secret());
+}
+
+void ClientInstance::DeliverHostMessage(
+ const protocol::ExtensionMessage& message) {
+ NOTIMPLEMENTED();
+}
+
+// Returning interface of protocol::ClipboardStub
+protocol::ClipboardStub* ClientInstance::GetClipboardStub() { return this; }
+
+// Returning interface of protocol::CursorShapeStub
+protocol::CursorShapeStub* ClientInstance::GetCursorShapeStub() { return this; }
+
+scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher>
+ClientInstance::GetTokenFetcher(const std::string& host_public_key) {
+ // Returns null when third-party authentication is unsupported.
+ return scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher>();
+}
+
+void ClientInstance::InjectClipboardEvent(
+ const protocol::ClipboardEvent& event) {
+ NOTIMPLEMENTED();
+}
+
+void ClientInstance::SetCursorShape(const protocol::CursorShapeInfo& shape) {
+ if (!ui_task_runner_->BelongsToCurrentThread()) {
+ ui_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&ClientInstance::SetCursorShape, this, shape));
+ return;
+ }
+ if (proxyToClient_)
+ proxyToClient_->UpdateCursorShape(shape);
+}
+
+void ClientInstance::ConnectToHostOnNetworkThread(
+ scoped_refptr<FrameConsumerProxy> consumer_proxy,
+ const base::Closure& done) {
+ DCHECK(network_task_runner_->BelongsToCurrentThread());
+
+ client_context_->Start();
+
+ video_renderer_.reset(
+ new SoftwareVideoRenderer(client_context_->main_task_runner(),
+ client_context_->decode_task_runner(),
+ consumer_proxy));
+
+ view_->Initialize(video_renderer_.get());
+
+ connection_.reset(new protocol::ConnectionToHost(true));
+
+ client_.reset(new ChromotingClient(client_config_,
+ client_context_.get(),
+ connection_.get(),
+ this,
+ video_renderer_.get(),
+ scoped_ptr<AudioPlayer>()));
+
+ signaling_.reset(
+ new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(),
+ url_requester_,
+ xmpp_config_));
+
+ NetworkSettings network_settings(NetworkSettings::NAT_TRAVERSAL_ENABLED);
+
+ scoped_ptr<ChromiumPortAllocator> port_allocator(
+ ChromiumPortAllocator::Create(url_requester_, network_settings));
+
+ scoped_ptr<protocol::TransportFactory> transport_factory(
+ new protocol::LibjingleTransportFactory(
+ signaling_.get(),
+ port_allocator.PassAs<cricket::HttpPortAllocatorBase>(),
+ network_settings));
+
+ client_->Start(signaling_.get(), transport_factory.Pass());
+
+ if (!done.is_null())
+ done.Run();
+}
+
+void ClientInstance::DisconnectFromHostOnNetworkThread(
+ const base::Closure& done) {
+ DCHECK(network_task_runner_->BelongsToCurrentThread());
+
+ host_id_.clear();
+
+ // |client_| must be torn down before |signaling_|.
+ connection_.reset();
+ client_.reset();
+ signaling_.reset();
+ video_renderer_.reset();
+ client_context_->Stop();
+ if (!done.is_null())
+ done.Run();
+}
+
+} // namespace remoting