OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 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. |
| 4 |
| 5 #include "remoting/ios/bridge/client_instance.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/logging.h" |
| 9 #include "base/synchronization/waitable_event.h" |
| 10 #include "net/socket/client_socket_factory.h" |
| 11 #include "remoting/base/url_request_context.h" |
| 12 #include "remoting/client/audio_player.h" |
| 13 #include "remoting/client/plugin/delegating_signal_strategy.h" |
| 14 #include "remoting/ios/bridge/client_proxy.h" |
| 15 #include "remoting/jingle_glue/chromium_port_allocator.h" |
| 16 #include "remoting/protocol/host_stub.h" |
| 17 #include "remoting/protocol/libjingle_transport_factory.h" |
| 18 |
| 19 namespace { |
| 20 const char* const kXmppServer = "talk.google.com"; |
| 21 const int kXmppPort = 5222; |
| 22 const bool kXmppUseTls = true; |
| 23 |
| 24 void DoNothing() {} |
| 25 } // namespace |
| 26 |
| 27 namespace remoting { |
| 28 |
| 29 ClientInstance::ClientInstance(const base::WeakPtr<ClientProxy>& proxy, |
| 30 const std::string& username, |
| 31 const std::string& auth_token, |
| 32 const std::string& host_jid, |
| 33 const std::string& host_id, |
| 34 const std::string& host_pubkey, |
| 35 const std::string& pairing_id, |
| 36 const std::string& pairing_secret) |
| 37 : proxyToClient_(proxy), host_id_(host_id), create_pairing_(false) { |
| 38 |
| 39 if (!base::MessageLoop::current()) { |
| 40 VLOG(1) << "Starting main message loop"; |
| 41 ui_loop_ = new base::MessageLoopForUI(); |
| 42 ui_loop_->Attach(); |
| 43 } else { |
| 44 VLOG(1) << "Using existing main message loop"; |
| 45 ui_loop_ = base::MessageLoopForUI::current(); |
| 46 } |
| 47 |
| 48 VLOG(1) << "Spawning additional threads"; |
| 49 |
| 50 // |ui_loop_| runs on the main thread, so |ui_task_runner_| will run on the |
| 51 // main thread. We can not kill the main thread when the message loop becomes |
| 52 // idle so the callback function does nothing (as opposed to the typical |
| 53 // base::MessageLoop::QuitClosure()) |
| 54 ui_task_runner_ = new AutoThreadTaskRunner(ui_loop_->message_loop_proxy(), |
| 55 base::Bind(&::DoNothing)); |
| 56 |
| 57 network_task_runner_ = AutoThread::CreateWithType( |
| 58 "native_net", ui_task_runner_, base::MessageLoop::TYPE_IO); |
| 59 |
| 60 url_requester_ = new URLRequestContextGetter(network_task_runner_); |
| 61 |
| 62 client_context_.reset(new ClientContext(network_task_runner_)); |
| 63 |
| 64 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
| 65 |
| 66 // Initialize XMPP config. |
| 67 xmpp_config_.host = kXmppServer; |
| 68 xmpp_config_.port = kXmppPort; |
| 69 xmpp_config_.use_tls = kXmppUseTls; |
| 70 xmpp_config_.username = username; |
| 71 xmpp_config_.auth_token = auth_token; |
| 72 xmpp_config_.auth_service = "oauth2"; |
| 73 |
| 74 // Initialize ClientConfig. |
| 75 client_config_.host_jid = host_jid; |
| 76 client_config_.host_public_key = host_pubkey; |
| 77 client_config_.authentication_tag = host_id_; |
| 78 client_config_.client_pairing_id = pairing_id; |
| 79 client_config_.client_paired_secret = pairing_secret; |
| 80 client_config_.authentication_methods.push_back( |
| 81 protocol::AuthenticationMethod::FromString("spake2_pair")); |
| 82 client_config_.authentication_methods.push_back( |
| 83 protocol::AuthenticationMethod::FromString("spake2_hmac")); |
| 84 client_config_.authentication_methods.push_back( |
| 85 protocol::AuthenticationMethod::FromString("spake2_plain")); |
| 86 } |
| 87 |
| 88 ClientInstance::~ClientInstance() {} |
| 89 |
| 90 void ClientInstance::Start() { |
| 91 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
| 92 |
| 93 // Creates a reference to |this|, so don't want to bind during constructor |
| 94 client_config_.fetch_secret_callback = |
| 95 base::Bind(&ClientInstance::FetchSecret, this); |
| 96 |
| 97 view_.reset(new FrameConsumerBridge( |
| 98 base::Bind(&ClientProxy::RedrawCanvas, proxyToClient_))); |
| 99 |
| 100 // |consumer_proxy| must be created on the UI thread to proxy calls from the |
| 101 // network or decode thread to the UI thread, but ownership will belong to a |
| 102 // SoftwareVideoRenderer which runs on the network thread. |
| 103 scoped_refptr<FrameConsumerProxy> consumer_proxy = |
| 104 new FrameConsumerProxy(ui_task_runner_, view_->AsWeakPtr()); |
| 105 |
| 106 // Post a task to start connection |
| 107 base::WaitableEvent done_event(true, false); |
| 108 network_task_runner_->PostTask( |
| 109 FROM_HERE, |
| 110 base::Bind(&ClientInstance::ConnectToHostOnNetworkThread, |
| 111 this, |
| 112 consumer_proxy, |
| 113 base::Bind(&base::WaitableEvent::Signal, |
| 114 base::Unretained(&done_event)))); |
| 115 // Wait until initialization completes before continuing |
| 116 done_event.Wait(); |
| 117 } |
| 118 |
| 119 void ClientInstance::Cleanup() { |
| 120 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
| 121 |
| 122 client_config_.fetch_secret_callback.Reset(); // Release ref to this |
| 123 // |view_| must be destroyed on the UI thread before the producer is gone. |
| 124 view_.reset(); |
| 125 |
| 126 base::WaitableEvent done_event(true, false); |
| 127 network_task_runner_->PostTask( |
| 128 FROM_HERE, |
| 129 base::Bind(&ClientInstance::DisconnectFromHostOnNetworkThread, |
| 130 this, |
| 131 base::Bind(&base::WaitableEvent::Signal, |
| 132 base::Unretained(&done_event)))); |
| 133 // Wait until we are fully disconnected before continuing |
| 134 done_event.Wait(); |
| 135 } |
| 136 |
| 137 // HOST attempts to continue automatically with previously supplied credentials, |
| 138 // if it can't it requests the user's PIN. |
| 139 void ClientInstance::FetchSecret( |
| 140 bool pairable, |
| 141 const protocol::SecretFetchedCallback& callback) { |
| 142 if (!ui_task_runner_->BelongsToCurrentThread()) { |
| 143 ui_task_runner_->PostTask( |
| 144 FROM_HERE, |
| 145 base::Bind(&ClientInstance::FetchSecret, this, pairable, callback)); |
| 146 return; |
| 147 } |
| 148 |
| 149 pin_callback_ = callback; |
| 150 |
| 151 if (proxyToClient_) { |
| 152 if (!client_config_.client_pairing_id.empty()) { |
| 153 // We attempted to connect using an existing pairing that was rejected. |
| 154 // Unless we forget about the stale credentials, we'll continue trying |
| 155 // them. |
| 156 VLOG(1) << "Deleting rejected pairing credentials"; |
| 157 |
| 158 proxyToClient_->CommitPairingCredentials(host_id_, "", ""); |
| 159 } |
| 160 proxyToClient_->DisplayAuthenticationPrompt(pairable); |
| 161 } |
| 162 } |
| 163 |
| 164 void ClientInstance::ProvideSecret(const std::string& pin, |
| 165 bool create_pairing) { |
| 166 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
| 167 create_pairing_ = create_pairing; |
| 168 |
| 169 // Before this function can complete, FetchSecret must be called |
| 170 DCHECK(!pin_callback_.is_null()); |
| 171 network_task_runner_->PostTask(FROM_HERE, base::Bind(pin_callback_, pin)); |
| 172 } |
| 173 |
| 174 void ClientInstance::PerformMouseAction( |
| 175 const webrtc::DesktopVector& position, |
| 176 const webrtc::DesktopVector& wheel_delta, |
| 177 int /* protocol::MouseEvent_MouseButton */ whichButton, |
| 178 bool button_down) { |
| 179 if (!network_task_runner_->BelongsToCurrentThread()) { |
| 180 network_task_runner_->PostTask( |
| 181 FROM_HERE, |
| 182 base::Bind(&ClientInstance::PerformMouseAction, |
| 183 this, |
| 184 position, |
| 185 wheel_delta, |
| 186 whichButton, |
| 187 button_down)); |
| 188 return; |
| 189 } |
| 190 |
| 191 protocol::MouseEvent_MouseButton mButton; |
| 192 |
| 193 // Button must be within the bounds of the MouseEvent_MouseButton enum. |
| 194 switch (whichButton) { |
| 195 case protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_LEFT: |
| 196 mButton = |
| 197 protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_LEFT; |
| 198 break; |
| 199 case protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_MAX: |
| 200 mButton = |
| 201 protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_MAX; |
| 202 break; |
| 203 case protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_MIDDLE: |
| 204 mButton = protocol::MouseEvent_MouseButton:: |
| 205 MouseEvent_MouseButton_BUTTON_MIDDLE; |
| 206 break; |
| 207 case protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_RIGHT: |
| 208 mButton = |
| 209 protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_RIGHT; |
| 210 break; |
| 211 case protocol::MouseEvent_MouseButton:: |
| 212 MouseEvent_MouseButton_BUTTON_UNDEFINED: |
| 213 mButton = protocol::MouseEvent_MouseButton:: |
| 214 MouseEvent_MouseButton_BUTTON_UNDEFINED; |
| 215 break; |
| 216 default: |
| 217 LOG(FATAL) << "Invalid constant for MouseEvent_MouseButton"; |
| 218 mButton = protocol::MouseEvent_MouseButton:: |
| 219 MouseEvent_MouseButton_BUTTON_UNDEFINED; |
| 220 break; |
| 221 } |
| 222 |
| 223 protocol::MouseEvent action; |
| 224 action.set_x(position.x()); |
| 225 action.set_y(position.y()); |
| 226 action.set_wheel_delta_x(wheel_delta.x()); |
| 227 action.set_wheel_delta_y(wheel_delta.y()); |
| 228 action.set_button(mButton); |
| 229 if (mButton != protocol::MouseEvent::BUTTON_UNDEFINED) |
| 230 action.set_button_down(button_down); |
| 231 |
| 232 connection_->input_stub()->InjectMouseEvent(action); |
| 233 } |
| 234 |
| 235 void ClientInstance::PerformKeyboardAction(int key_code, bool key_down) { |
| 236 if (!network_task_runner_->BelongsToCurrentThread()) { |
| 237 network_task_runner_->PostTask( |
| 238 FROM_HERE, |
| 239 base::Bind( |
| 240 &ClientInstance::PerformKeyboardAction, this, key_code, key_down)); |
| 241 return; |
| 242 } |
| 243 |
| 244 protocol::KeyEvent action; |
| 245 action.set_usb_keycode(key_code); |
| 246 action.set_pressed(key_down); |
| 247 connection_->input_stub()->InjectKeyEvent(action); |
| 248 } |
| 249 |
| 250 void ClientInstance::OnConnectionState(protocol::ConnectionToHost::State state, |
| 251 protocol::ErrorCode error) { |
| 252 if (!ui_task_runner_->BelongsToCurrentThread()) { |
| 253 ui_task_runner_->PostTask( |
| 254 FROM_HERE, |
| 255 base::Bind(&ClientInstance::OnConnectionState, this, state, error)); |
| 256 return; |
| 257 } |
| 258 |
| 259 // TODO (aboone) This functionality is not scheduled for QA yet. |
| 260 // if (create_pairing_ && state == protocol::ConnectionToHost::CONNECTED) { |
| 261 // VLOG(1) << "Attempting to pair with host"; |
| 262 // protocol::PairingRequest request; |
| 263 // request.set_client_name("iOS"); |
| 264 // connection_->host_stub()->RequestPairing(request); |
| 265 // } |
| 266 |
| 267 if (proxyToClient_) |
| 268 proxyToClient_->ReportConnectionStatus(state, error); |
| 269 } |
| 270 |
| 271 void ClientInstance::OnConnectionReady(bool ready) { |
| 272 // We ignore this message, since OnConnectionState tells us the same thing. |
| 273 } |
| 274 |
| 275 void ClientInstance::OnRouteChanged(const std::string& channel_name, |
| 276 const protocol::TransportRoute& route) { |
| 277 VLOG(1) << "Using " << protocol::TransportRoute::GetTypeString(route.type) |
| 278 << " connection for " << channel_name << " channel"; |
| 279 } |
| 280 |
| 281 void ClientInstance::SetCapabilities(const std::string& capabilities) { |
| 282 DCHECK(video_renderer_); |
| 283 DCHECK(connection_); |
| 284 DCHECK(connection_->state() == protocol::ConnectionToHost::CONNECTED); |
| 285 video_renderer_->Initialize(connection_->config()); |
| 286 } |
| 287 |
| 288 void ClientInstance::SetPairingResponse( |
| 289 const protocol::PairingResponse& response) { |
| 290 if (!ui_task_runner_->BelongsToCurrentThread()) { |
| 291 ui_task_runner_->PostTask( |
| 292 FROM_HERE, |
| 293 base::Bind(&ClientInstance::SetPairingResponse, this, response)); |
| 294 return; |
| 295 } |
| 296 |
| 297 VLOG(1) << "Successfully established pairing with host"; |
| 298 |
| 299 if (proxyToClient_) |
| 300 proxyToClient_->CommitPairingCredentials( |
| 301 host_id_, response.client_id(), response.shared_secret()); |
| 302 } |
| 303 |
| 304 void ClientInstance::DeliverHostMessage( |
| 305 const protocol::ExtensionMessage& message) { |
| 306 NOTIMPLEMENTED(); |
| 307 } |
| 308 |
| 309 // Returning interface of protocol::ClipboardStub |
| 310 protocol::ClipboardStub* ClientInstance::GetClipboardStub() { return this; } |
| 311 |
| 312 // Returning interface of protocol::CursorShapeStub |
| 313 protocol::CursorShapeStub* ClientInstance::GetCursorShapeStub() { return this; } |
| 314 |
| 315 scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher> |
| 316 ClientInstance::GetTokenFetcher(const std::string& host_public_key) { |
| 317 // Returns null when third-party authentication is unsupported. |
| 318 return scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher>(); |
| 319 } |
| 320 |
| 321 void ClientInstance::InjectClipboardEvent( |
| 322 const protocol::ClipboardEvent& event) { |
| 323 NOTIMPLEMENTED(); |
| 324 } |
| 325 |
| 326 void ClientInstance::SetCursorShape(const protocol::CursorShapeInfo& shape) { |
| 327 if (!ui_task_runner_->BelongsToCurrentThread()) { |
| 328 ui_task_runner_->PostTask( |
| 329 FROM_HERE, base::Bind(&ClientInstance::SetCursorShape, this, shape)); |
| 330 return; |
| 331 } |
| 332 if (proxyToClient_) |
| 333 proxyToClient_->UpdateCursorShape(shape); |
| 334 } |
| 335 |
| 336 void ClientInstance::ConnectToHostOnNetworkThread( |
| 337 scoped_refptr<FrameConsumerProxy> consumer_proxy, |
| 338 const base::Closure& done) { |
| 339 DCHECK(network_task_runner_->BelongsToCurrentThread()); |
| 340 |
| 341 client_context_->Start(); |
| 342 |
| 343 video_renderer_.reset( |
| 344 new SoftwareVideoRenderer(client_context_->main_task_runner(), |
| 345 client_context_->decode_task_runner(), |
| 346 consumer_proxy)); |
| 347 |
| 348 view_->Initialize(video_renderer_.get()); |
| 349 |
| 350 connection_.reset(new protocol::ConnectionToHost(true)); |
| 351 |
| 352 client_.reset(new ChromotingClient(client_config_, |
| 353 client_context_.get(), |
| 354 connection_.get(), |
| 355 this, |
| 356 video_renderer_.get(), |
| 357 scoped_ptr<AudioPlayer>())); |
| 358 |
| 359 signaling_.reset( |
| 360 new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(), |
| 361 url_requester_, |
| 362 xmpp_config_)); |
| 363 |
| 364 NetworkSettings network_settings(NetworkSettings::NAT_TRAVERSAL_ENABLED); |
| 365 |
| 366 scoped_ptr<ChromiumPortAllocator> port_allocator( |
| 367 ChromiumPortAllocator::Create(url_requester_, network_settings)); |
| 368 |
| 369 scoped_ptr<protocol::TransportFactory> transport_factory( |
| 370 new protocol::LibjingleTransportFactory( |
| 371 signaling_.get(), |
| 372 port_allocator.PassAs<cricket::HttpPortAllocatorBase>(), |
| 373 network_settings)); |
| 374 |
| 375 client_->Start(signaling_.get(), transport_factory.Pass()); |
| 376 |
| 377 if (!done.is_null()) |
| 378 done.Run(); |
| 379 } |
| 380 |
| 381 void ClientInstance::DisconnectFromHostOnNetworkThread( |
| 382 const base::Closure& done) { |
| 383 DCHECK(network_task_runner_->BelongsToCurrentThread()); |
| 384 |
| 385 host_id_.clear(); |
| 386 |
| 387 // |client_| must be torn down before |signaling_|. |
| 388 connection_.reset(); |
| 389 client_.reset(); |
| 390 signaling_.reset(); |
| 391 video_renderer_.reset(); |
| 392 client_context_->Stop(); |
| 393 if (!done.is_null()) |
| 394 done.Run(); |
| 395 } |
| 396 |
| 397 } // namespace remoting |
OLD | NEW |