1 // Copyright (c) 2012 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/host/client_session.h" 6 7 #include <algorithm> 8 9 #include "base/message_loop/message_loop_proxy.h" 10 #include "remoting/base/capabilities.h" 11 #include "remoting/codec/audio_encoder.h" 12 #include "remoting/codec/audio_encoder_opus.h" 13 #include "remoting/codec/audio_encoder_speex.h" 14 #include "remoting/codec/audio_encoder_verbatim.h" 15 #include "remoting/codec/video_encoder.h" 16 #include "remoting/codec/video_encoder_verbatim.h" 17 #include "remoting/codec/video_encoder_vp8.h" 18 #include "remoting/host/audio_capturer.h" 19 #include "remoting/host/audio_scheduler.h" 20 #include "remoting/host/desktop_environment.h" 21 #include "remoting/host/input_injector.h" 22 #include "remoting/host/screen_controls.h" 23 #include "remoting/host/screen_resolution.h" 24 #include "remoting/host/video_scheduler.h" 25 #include "remoting/proto/control.pb.h" 26 #include "remoting/proto/event.pb.h" 27 #include "remoting/protocol/client_stub.h" 28 #include "remoting/protocol/clipboard_thread_proxy.h" 29 #include "remoting/protocol/pairing_registry.h" 30 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" 31 32 // Default DPI to assume for old clients that use notifyClientDimensions. 33 const int kDefaultDPI = 96; 34 35 namespace remoting { 36 37 ClientSession::ClientSession( 38 EventHandler* event_handler, 39 scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner, 40 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner, 41 scoped_refptr<base::SingleThreadTaskRunner> video_capture_task_runner, 42 scoped_refptr<base::SingleThreadTaskRunner> video_encode_task_runner, 43 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, 44 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, 45 scoped_ptr<protocol::ConnectionToClient> connection, 46 DesktopEnvironmentFactory* desktop_environment_factory, 47 const base::TimeDelta& max_duration, 48 scoped_refptr<protocol::PairingRegistry> pairing_registry) 49 : event_handler_(event_handler), 50 connection_(connection.Pass()), 51 client_jid_(connection_->session()->jid()), 52 control_factory_(this), 53 desktop_environment_factory_(desktop_environment_factory), 54 input_tracker_(&host_input_filter_), 55 remote_input_filter_(&input_tracker_), 56 mouse_clamping_filter_(&remote_input_filter_), 57 disable_input_filter_(mouse_clamping_filter_.input_filter()), 58 disable_clipboard_filter_(clipboard_echo_filter_.host_filter()), 59 auth_input_filter_(&disable_input_filter_), 60 auth_clipboard_filter_(&disable_clipboard_filter_), 61 client_clipboard_factory_(clipboard_echo_filter_.client_filter()), 62 max_duration_(max_duration), 63 audio_task_runner_(audio_task_runner), 64 input_task_runner_(input_task_runner), 65 video_capture_task_runner_(video_capture_task_runner), 66 video_encode_task_runner_(video_encode_task_runner), 67 network_task_runner_(network_task_runner), 68 ui_task_runner_(ui_task_runner), 69 pairing_registry_(pairing_registry) { 70 connection_->SetEventHandler(this); 71 72 // TODO(sergeyu): Currently ConnectionToClient expects stubs to be 73 // set before channels are connected. Make it possible to set stubs 74 // later and set them only when connection is authenticated. 75 connection_->set_clipboard_stub(&auth_clipboard_filter_); 76 connection_->set_host_stub(this); 77 connection_->set_input_stub(&auth_input_filter_); 78 79 // |auth_*_filter_|'s states reflect whether the session is authenticated. 80 auth_input_filter_.set_enabled(false); 81 auth_clipboard_filter_.set_enabled(false); 82 83 #if defined(OS_WIN) 84 // LocalInputMonitorWin filters out an echo of the injected input before it 85 // reaches |remote_input_filter_|. 86 remote_input_filter_.SetExpectLocalEcho(false); 87 #endif // defined(OS_WIN) 88 } 89 90 ClientSession::~ClientSession() { 91 DCHECK(CalledOnValidThread()); 92 DCHECK(!audio_scheduler_.get()); 93 DCHECK(!desktop_environment_); 94 DCHECK(!input_injector_); 95 DCHECK(!screen_controls_); 96 DCHECK(!video_scheduler_.get()); 97 98 connection_.reset(); 99 } 100 101 void ClientSession::NotifyClientResolution( 102 const protocol::ClientResolution& resolution) { 103 DCHECK(CalledOnValidThread()); 104 105 // TODO(sergeyu): Move these checks to protocol layer. 106 if (!resolution.has_dips_width() || !resolution.has_dips_height() || 107 resolution.dips_width() < 0 || resolution.dips_height() < 0 || 108 resolution.width() <= 0 || resolution.height() <= 0) { 109 LOG(ERROR) << "Received invalid ClientResolution message."; 110 return; 111 } 112 113 VLOG(1) << "Received ClientResolution (dips_width=" 114 << resolution.dips_width() << ", dips_height=" 115 << resolution.dips_height() << ")"; 116 117 if (!screen_controls_) 118 return; 119 120 ScreenResolution client_resolution( 121 webrtc::DesktopSize(resolution.dips_width(), resolution.dips_height()), 122 webrtc::DesktopVector(kDefaultDPI, kDefaultDPI)); 123 124 // Try to match the client's resolution. 125 screen_controls_->SetScreenResolution(client_resolution); 126 } 127 128 void ClientSession::ControlVideo(const protocol::VideoControl& video_control) { 129 DCHECK(CalledOnValidThread()); 130 131 if (video_control.has_enable()) { 132 VLOG(1) << "Received VideoControl (enable=" 133 << video_control.enable() << ")"; 134 video_scheduler_->Pause(!video_control.enable()); 135 } 136 } 137 138 void ClientSession::ControlAudio(const protocol::AudioControl& audio_control) { 139 DCHECK(CalledOnValidThread()); 140 141 if (audio_control.has_enable()) { 142 VLOG(1) << "Received AudioControl (enable=" 143 << audio_control.enable() << ")"; 144 if (audio_scheduler_.get()) 145 audio_scheduler_->Pause(!audio_control.enable()); 146 } 147 } 148 149 void ClientSession::SetCapabilities( 150 const protocol::Capabilities& capabilities) { 151 DCHECK(CalledOnValidThread()); 152 153 // The client should not send protocol::Capabilities if it is not supported by 154 // the config channel. 155 if (!connection_->session()->config().SupportsCapabilities()) { 156 LOG(ERROR) << "Unexpected protocol::Capabilities has been received."; 157 return; 158 } 159 160 // Ignore all the messages but the 1st one. 161 if (client_capabilities_) { 162 LOG(WARNING) << "protocol::Capabilities has been received already."; 163 return; 164 } 165 166 client_capabilities_ = make_scoped_ptr(new std::string()); 167 if (capabilities.has_capabilities()) 168 *client_capabilities_ = capabilities.capabilities(); 169 170 VLOG(1) << "Client capabilities: " << *client_capabilities_; 171 172 // Calculate the set of capabilities enabled by both client and host and 173 // pass it to the desktop environment if it is available. 174 desktop_environment_->SetCapabilities( 175 IntersectCapabilities(*client_capabilities_, host_capabilities_)); 176 } 177 178 void ClientSession::RequestPairing( 179 const protocol::PairingRequest& pairing_request) { 180 if (pairing_registry_ && pairing_request.has_client_name()) { 181 protocol::PairingRegistry::Pairing pairing = 182 pairing_registry_->CreatePairing(pairing_request.client_name()); 183 protocol::PairingResponse pairing_response; 184 pairing_response.set_client_id(pairing.client_id()); 185 pairing_response.set_shared_secret(pairing.shared_secret()); 186 connection_->client_stub()->SetPairingResponse(pairing_response); 187 } 188 } 189 190 void ClientSession::DeliverClientMessage( 191 const protocol::ExtensionMessage& message) { 192 // No messages are currently supported. 193 LOG(INFO) << "Unexpected message received: " 194 << message.type() << ": " << message.data(); 195 } 196 197 void ClientSession::OnConnectionAuthenticated( 198 protocol::ConnectionToClient* connection) { 199 DCHECK(CalledOnValidThread()); 200 DCHECK_EQ(connection_.get(), connection); 201 DCHECK(!audio_scheduler_.get()); 202 DCHECK(!desktop_environment_); 203 DCHECK(!input_injector_); 204 DCHECK(!screen_controls_); 205 DCHECK(!video_scheduler_.get()); 206 207 auth_input_filter_.set_enabled(true); 208 auth_clipboard_filter_.set_enabled(true); 209 210 clipboard_echo_filter_.set_client_stub(connection_->client_stub()); 211 mouse_clamping_filter_.set_video_stub(connection_->video_stub()); 212 213 if (max_duration_ > base::TimeDelta()) { 214 // TODO(simonmorris): Let Disconnect() tell the client that the 215 // disconnection was caused by the session exceeding its maximum duration. 216 max_duration_timer_.Start(FROM_HERE, max_duration_, 217 this, &ClientSession::DisconnectSession); 218 } 219 220 // Disconnect the session if the connection was rejected by the host. 221 if (!event_handler_->OnSessionAuthenticated(this)) { 222 DisconnectSession(); 223 return; 224 } 225 226 // Create the desktop environment. Drop the connection if it could not be 227 // created for any reason (for instance the curtain could not initialize). 228 desktop_environment_ = 229 desktop_environment_factory_->Create(control_factory_.GetWeakPtr()); 230 if (!desktop_environment_) { 231 DisconnectSession(); 232 return; 233 } 234 235 host_capabilities_ = desktop_environment_->GetCapabilities(); 236 237 // Ignore protocol::Capabilities messages from the client if it does not 238 // support any capabilities. 239 if (!connection_->session()->config().SupportsCapabilities()) { 240 VLOG(1) << "The client does not support any capabilities."; 241 242 client_capabilities_ = make_scoped_ptr(new std::string()); 243 desktop_environment_->SetCapabilities(*client_capabilities_); 244 } 245 246 // Create the object that controls the screen resolution. 247 screen_controls_ = desktop_environment_->CreateScreenControls(); 248 249 // Create the event executor. 250 input_injector_ = desktop_environment_->CreateInputInjector(); 251 252 // Connect the host clipboard and input stubs. 253 host_input_filter_.set_input_stub(input_injector_.get()); 254 clipboard_echo_filter_.set_host_stub(input_injector_.get()); 255 256 // Create a VideoEncoder based on the session's video channel configuration. 257 scoped_ptr<VideoEncoder> video_encoder = 258 CreateVideoEncoder(connection_->session()->config()); 259 260 // Create a VideoScheduler to pump frames from the capturer to the client. 261 video_scheduler_ = new VideoScheduler( 262 video_capture_task_runner_, 263 video_encode_task_runner_, 264 network_task_runner_, 265 desktop_environment_->CreateVideoCapturer(), 266 video_encoder.Pass(), 267 connection_->client_stub(), 268 &mouse_clamping_filter_); 269 270 // Create an AudioScheduler if audio is enabled, to pump audio samples. 271 if (connection_->session()->config().is_audio_enabled()) { 272 scoped_ptr<AudioEncoder> audio_encoder = 273 CreateAudioEncoder(connection_->session()->config()); 274 audio_scheduler_ = new AudioScheduler( 275 audio_task_runner_, 276 network_task_runner_, 277 desktop_environment_->CreateAudioCapturer(), 278 audio_encoder.Pass(), 279 connection_->audio_stub()); 280 } 281 } 282 283 void ClientSession::OnConnectionChannelsConnected( 284 protocol::ConnectionToClient* connection) { 285 DCHECK(CalledOnValidThread()); 286 DCHECK_EQ(connection_.get(), connection); 287 288 // Negotiate capabilities with the client. 289 if (connection_->session()->config().SupportsCapabilities()) { 290 VLOG(1) << "Host capabilities: " << host_capabilities_; 291 292 protocol::Capabilities capabilities; 293 capabilities.set_capabilities(host_capabilities_); 294 connection_->client_stub()->SetCapabilities(capabilities); 295 } 296 297 // Start the event executor. 298 input_injector_->Start(CreateClipboardProxy()); 299 SetDisableInputs(false); 300 301 // Start capturing the screen. 302 video_scheduler_->Start(); 303 304 // Start recording audio. 305 if (connection_->session()->config().is_audio_enabled()) 306 audio_scheduler_->Start(); 307 308 // Notify the event handler that all our channels are now connected. 309 event_handler_->OnSessionChannelsConnected(this); 310 } 311 312 void ClientSession::OnConnectionClosed( 313 protocol::ConnectionToClient* connection, 314 protocol::ErrorCode error) { 315 DCHECK(CalledOnValidThread()); 316 DCHECK_EQ(connection_.get(), connection); 317 318 // Ignore any further callbacks. 319 control_factory_.InvalidateWeakPtrs(); 320 321 // If the client never authenticated then the session failed. 322 if (!auth_input_filter_.enabled()) 323 event_handler_->OnSessionAuthenticationFailed(this); 324 325 // Block any further input events from the client. 326 // TODO(wez): Fix ChromotingHost::OnSessionClosed not to check our 327 // is_authenticated(), so that we can disable |auth_*_filter_| here. 328 disable_input_filter_.set_enabled(false); 329 disable_clipboard_filter_.set_enabled(false); 330 331 // Ensure that any pressed keys or buttons are released. 332 input_tracker_.ReleaseAll(); 333 334 // Stop components access the client, audio or video stubs, which are no 335 // longer valid once ConnectionToClient calls OnConnectionClosed(). 336 if (audio_scheduler_.get()) { 337 audio_scheduler_->Stop(); 338 audio_scheduler_ = NULL; 339 } 340 if (video_scheduler_.get()) { 341 video_scheduler_->Stop(); 342 video_scheduler_ = NULL; 343 } 344 345 client_clipboard_factory_.InvalidateWeakPtrs(); 346 input_injector_.reset(); 347 screen_controls_.reset(); 348 desktop_environment_.reset(); 349 350 // Notify the ChromotingHost that this client is disconnected. 351 // TODO(sergeyu): Log failure reason? 352 event_handler_->OnSessionClosed(this); 353 } 354 355 void ClientSession::OnSequenceNumberUpdated( 356 protocol::ConnectionToClient* connection, int64 sequence_number) { 357 DCHECK(CalledOnValidThread()); 358 DCHECK_EQ(connection_.get(), connection); 359 360 if (video_scheduler_.get()) 361 video_scheduler_->UpdateSequenceNumber(sequence_number); 362 363 event_handler_->OnSessionSequenceNumber(this, sequence_number); 364 } 365 366 void ClientSession::OnRouteChange( 367 protocol::ConnectionToClient* connection, 368 const std::string& channel_name, 369 const protocol::TransportRoute& route) { 370 DCHECK(CalledOnValidThread()); 371 DCHECK_EQ(connection_.get(), connection); 372 event_handler_->OnSessionRouteChange(this, channel_name, route); 373 } 374 375 const std::string& ClientSession::client_jid() const { 376 return client_jid_; 377 } 378 379 void ClientSession::DisconnectSession() { 380 DCHECK(CalledOnValidThread()); 381 DCHECK(connection_.get()); 382 383 max_duration_timer_.Stop(); 384 385 // This triggers OnConnectionClosed(), and the session may be destroyed 386 // as the result, so this call must be the last in this method. 387 connection_->Disconnect(); 388 } 389 390 void ClientSession::OnLocalMouseMoved(const SkIPoint& position) { 391 DCHECK(CalledOnValidThread()); 392 remote_input_filter_.LocalMouseMoved(position); 393 } 394 395 void ClientSession::SetDisableInputs(bool disable_inputs) { 396 DCHECK(CalledOnValidThread()); 397 398 if (disable_inputs) 399 input_tracker_.ReleaseAll(); 400 401 disable_input_filter_.set_enabled(!disable_inputs); 402 disable_clipboard_filter_.set_enabled(!disable_inputs); 403 } 404 405 scoped_ptr<protocol::ClipboardStub> ClientSession::CreateClipboardProxy() { 406 DCHECK(CalledOnValidThread()); 407 408 return scoped_ptr<protocol::ClipboardStub>( 409 new protocol::ClipboardThreadProxy( 410 client_clipboard_factory_.GetWeakPtr(), 411 base::MessageLoopProxy::current())); 412 } 413 414 // TODO(sergeyu): Move this to SessionManager? 415 // static 416 scoped_ptr<VideoEncoder> ClientSession::CreateVideoEncoder( 417 const protocol::SessionConfig& config) { 418 const protocol::ChannelConfig& video_config = config.video_config(); 419 420 if (video_config.codec == protocol::ChannelConfig::CODEC_VERBATIM) { 421 return scoped_ptr<VideoEncoder>(new remoting::VideoEncoderVerbatim()); 422 } else if (video_config.codec == protocol::ChannelConfig::CODEC_VP8) { 423 return scoped_ptr<VideoEncoder>(new remoting::VideoEncoderVp8()); 424 } 425 426 NOTIMPLEMENTED(); 427 return scoped_ptr<VideoEncoder>(); 428 } 429 430 // static 431 scoped_ptr<AudioEncoder> ClientSession::CreateAudioEncoder( 432 const protocol::SessionConfig& config) { 433 const protocol::ChannelConfig& audio_config = config.audio_config(); 434 435 if (audio_config.codec == protocol::ChannelConfig::CODEC_VERBATIM) { 436 return scoped_ptr<AudioEncoder>(new AudioEncoderVerbatim()); 437 } else if (audio_config.codec == protocol::ChannelConfig::CODEC_SPEEX) { 438 return scoped_ptr<AudioEncoder>(new AudioEncoderSpeex()); 439 } else if (audio_config.codec == protocol::ChannelConfig::CODEC_OPUS) { 440 return scoped_ptr<AudioEncoder>(new AudioEncoderOpus()); 441 } 442 443 NOTIMPLEMENTED(); 444 return scoped_ptr<AudioEncoder>(); 445 } 446 447 } // namespace remoting 448