Chromium Code Reviews| 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 |