Index: remoting/ios/bridge/client_instance.cc |
diff --git a/remoting/ios/bridge/client_instance.cc b/remoting/ios/bridge/client_instance.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..423a01a72417bc71d54e850b6848f22dab9288fc |
--- /dev/null |
+++ b/remoting/ios/bridge/client_instance.cc |
@@ -0,0 +1,326 @@ |
+// 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 "net/socket/client_socket_factory.h" |
+#include "remoting/client/audio_player.h" |
+#include "remoting/client/software_video_renderer.h" |
+#include "remoting/client/plugin/delegating_signal_strategy.h" |
+#include "remoting/jingle_glue/chromium_port_allocator.h" |
+#include "remoting/protocol/host_stub.h" |
+#include "remoting/protocol/libjingle_transport_factory.h" |
+ |
+#include "remoting/ios/bridge/client_bridge.h" |
+ |
+namespace { |
+const char* const kXmppServer = "talk.google.com"; |
+const int kXmppPort = 5222; |
+const bool kXmppUseTls = true; |
+} // namespace |
+ |
+namespace remoting { |
+ |
+ClientInstance::ClientInstance(ClientBridge* bridge, |
+ const char* username, |
+ const char* auth_token, |
+ const char* host_jid, |
+ const char* host_id, |
+ const char* host_pubkey, |
+ const char* pairing_id, |
+ const char* pairing_secret) |
+ : bridge_(bridge), |
+ host_id_(host_id), |
+ client_context_(bridge_->network_task_runner().get()), |
+ create_pairing_(false) { |
+ DCHECK(bridge_->ui_task_runner()->BelongsToCurrentThread()); |
+ |
+ // Intialize 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_.fetch_secret_callback = |
+ base::Bind(&ClientInstance::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")); |
+ |
+ // Post a task to start connection |
+ bridge_->network_task_runner()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&ClientInstance::ConnectToHostOnNetworkThread, this)); |
+} |
+ |
+ClientInstance::~ClientInstance() {} |
+ |
+void ClientInstance::Cleanup() { |
+ DCHECK(bridge_->ui_task_runner()->BelongsToCurrentThread()); |
+ |
+ bridge_->network_task_runner()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&ClientInstance::DisconnectFromHostOnNetworkThread, this)); |
+} |
+ |
+// CLIENT reporting user provided PIN |
+void ClientInstance::ProvideSecret(const std::string& pin, |
+ bool create_pairing) { |
+ DCHECK(bridge_->ui_task_runner()->BelongsToCurrentThread()); |
+ DCHECK(!pin_callback_.is_null()); |
+ |
+ create_pairing_ = create_pairing; |
+ |
+ bridge_->network_task_runner()->PostTask(FROM_HERE, |
+ base::Bind(pin_callback_, pin)); |
+} |
+ |
+// CLIENT invoked mouse input |
+void ClientInstance::PerformMouseAction( |
+ const webrtc::DesktopVector& position, |
+ const webrtc::DesktopVector& wheel_delta, |
+ int /* protocol::MouseEvent_MouseButton */ whichButton, |
+ bool button_down) { |
+ // Button must be within the bounds of the MouseEvent_MouseButton enum. |
+ DCHECK(whichButton >= 0 && whichButton < 5); |
+ |
+ protocol::MouseEvent_MouseButton mButton = |
+ static_cast<remoting::protocol::MouseEvent_MouseButton>(whichButton); |
+ |
+ if (!bridge_->network_task_runner()->BelongsToCurrentThread()) { |
dcaiafa
2014/03/19 01:14:15
Move this to the top of the function.
aboone
2014/03/21 16:42:07
Done.
|
+ bridge_->network_task_runner()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&ClientInstance::PerformMouseAction, |
+ this, |
+ position, |
+ wheel_delta, |
+ mButton, |
+ button_down)); |
+ return; |
+ } |
+ |
+ 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); |
+} |
+ |
+// CLIENT invoked keyboard input |
+void ClientInstance::PerformKeyboardAction(int key_code, bool key_down) { |
+ if (!bridge_->network_task_runner()->BelongsToCurrentThread()) { |
+ bridge_->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); |
+} |
+ |
+// HOST reporting Connections State and/or Error |
+void ClientInstance::OnConnectionState(protocol::ConnectionToHost::State state, |
+ protocol::ErrorCode error) { |
+ if (!bridge_->ui_task_runner()->BelongsToCurrentThread()) { |
+ bridge_->ui_task_runner()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&ClientInstance::OnConnectionState, this, state, error)); |
+ return; |
+ } |
+ |
+ if (create_pairing_ && state == protocol::ConnectionToHost::CONNECTED) { |
+ VLOG(1) << "Attempting to pair with host"; |
+ protocol::PairingRequest request; |
+ request.set_client_name("iPad"); |
+ connection_->host_stub()->RequestPairing(request); |
+ } |
+ |
+ bridge_->ui_task_runner()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&ClientBridge::ReportConnectionStatus, |
+ base::Unretained(bridge_.get()), |
dcaiafa
2014/03/19 01:14:15
ClientBridge is currently RefCounted, so there sho
aboone
2014/03/21 16:42:07
Done.
|
+ state, |
+ error)); |
+} |
+ |
+void ClientInstance::OnConnectionReady(bool ready) { |
+ // We ignore this message, since OnConnectoinState 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) { |
+ video_renderer_->Initialize(connection_->config()); |
+} |
+ |
+// HOST accepted client creditials, and response |
+void ClientInstance::SetPairingResponse( |
+ const protocol::PairingResponse& response) { |
+ VLOG(1) << "Successfully established pairing with host"; |
+ |
+ bridge_->ui_task_runner()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&ClientBridge::CommitPairingCredentials, |
+ base::Unretained(bridge_.get()), |
dcaiafa
2014/03/19 01:14:15
Same as OnConnectionState().
aboone
2014/03/21 16:42:07
Done.
|
+ host_id_, |
+ response.client_id(), |
+ response.shared_secret())); |
+} |
+ |
+void ClientInstance::DeliverHostMessage( |
+ const protocol::ExtensionMessage& message) { |
+ NOTIMPLEMENTED(); |
+} |
+ |
+// THIS inhereits protocol::ClipboardStub, returning a view of THIS as a |
+// protocol::ClipboardStub |
+protocol::ClipboardStub* ClientInstance::GetClipboardStub() { return this; } |
+ |
+// THIS inhereits protocol::CursorShapeStub, returning a view of THIS as a |
+// protocol::CursorShapeStub |
+protocol::CursorShapeStub* ClientInstance::GetCursorShapeStub() { return this; } |
+ |
+scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher> |
+ClientInstance::GetTokenFetcher(const std::string& host_public_key) { |
+ // Return null to indicate that third-party authentication is unsupported. |
+ return scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher>(); |
+} |
+ |
+void ClientInstance::InjectClipboardEvent( |
+ const protocol::ClipboardEvent& event) { |
+ NOTIMPLEMENTED(); |
+} |
+ |
+// HOST delivering a Cursor (mouse) update |
+void ClientInstance::SetCursorShape(const protocol::CursorShapeInfo& shape) { |
+ if (!bridge_->ui_task_runner()->BelongsToCurrentThread()) { |
+ bridge_->ui_task_runner()->PostTask( |
+ FROM_HERE, base::Bind(&ClientInstance::SetCursorShape, this, shape)); |
+ return; |
+ } |
+ |
+ bridge_->UpdateCursorShape(shape); |
+} |
+ |
+// Create a thread to receive and package Canvas (desktop) updates |
+void ClientInstance::ConnectToHostOnNetworkThread() { |
+ DCHECK(bridge_->network_task_runner()->BelongsToCurrentThread()); |
+ |
+ view_.reset( |
+ new FrameConsumerBridge(bridge_->BindToFrameConsumerBridgeCallback())); |
dcaiafa
2014/03/19 01:14:15
Create the binding on site instead of using a help
aboone
2014/03/21 16:42:07
Done.
|
+ view_weak_factory_.reset( |
dcaiafa
2014/03/19 01:14:15
Move the WeakPtrFactory into FrameConsumerBridge,
aboone
2014/03/21 16:42:07
Done.
|
+ new base::WeakPtrFactory<FrameConsumer>(view_.get())); |
+ |
+ client_context_.Start(); |
+ |
+ scoped_refptr<FrameConsumerProxy> consumer_proxy = new FrameConsumerProxy( |
+ bridge_->network_task_runner(), view_weak_factory_->GetWeakPtr()); |
+ |
+ SoftwareVideoRenderer* renderer = |
+ new SoftwareVideoRenderer(client_context_.main_task_runner(), |
+ client_context_.decode_task_runner(), |
+ consumer_proxy); |
+ |
+ view_->Initialize(renderer); |
+ video_renderer_.reset(renderer); |
+ |
+ connection_.reset(new protocol::ConnectionToHost(true)); |
+ |
+ client_.reset(new ChromotingClient(client_config_, |
+ &client_context_, |
+ connection_.get(), |
+ this, |
+ video_renderer_.get(), |
+ scoped_ptr<AudioPlayer>())); |
+ |
+ signaling_.reset( |
+ new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(), |
+ bridge_->url_requester(), |
+ xmpp_config_)); |
+ |
+ NetworkSettings network_settings(NetworkSettings::NAT_TRAVERSAL_ENABLED); |
+ |
+ scoped_ptr<ChromiumPortAllocator> port_allocator( |
+ ChromiumPortAllocator::Create(bridge_->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()); |
+} |
+ |
+void ClientInstance::DisconnectFromHostOnNetworkThread() { |
+ DCHECK(bridge_->network_task_runner()->BelongsToCurrentThread()); |
+ |
+ // This must be destroyed on the network thread before the producer is gone. |
+ view_.reset(); |
+ |
+ // The weak pointers must be invalidated on the same thread they were used. |
+ view_weak_factory_->InvalidateWeakPtrs(); |
+ |
+ host_id_.clear(); |
+ |
+ // |client_| must be torn down before |signaling_|. |
+ connection_.reset(); |
+ client_.reset(); |
+ client_context_.Stop(); |
+} |
+ |
+// 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 (!bridge_->ui_task_runner()->BelongsToCurrentThread()) { |
+ bridge_->ui_task_runner()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&ClientInstance::FetchSecret, this, pairable, callback)); |
+ 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. |
+ VLOG(1) << "Deleting rejected pairing credentials"; |
+ bridge_->CommitPairingCredentials(host_id_, "", ""); |
+ } |
+ |
+ pin_callback_ = callback; |
+ bridge_->DisplayAuthenticationPrompt(pairable); |
+} |
+ |
+} // namespace remoting |