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/base/logging.h" 12 #include "remoting/codec/audio_encoder.h" 13 #include "remoting/codec/audio_encoder_opus.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_vpx.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 if (message.has_type()) { 193 if (message.type() == "test-echo") { 194 protocol::ExtensionMessage reply; 195 reply.set_type("test-echo-reply"); 196 if (message.has_data()) 197 reply.set_data(message.data().substr(0, 16)); 198 connection_->client_stub()->DeliverHostMessage(reply); 199 return; 200 } 201 } 202 // No messages are currently supported. 203 HOST_LOG << "Unexpected message received: " 204 << message.type() << ": " << message.data(); 205 } 206 207 void ClientSession::OnConnectionAuthenticated( 208 protocol::ConnectionToClient* connection) { 209 DCHECK(CalledOnValidThread()); 210 DCHECK_EQ(connection_.get(), connection); 211 DCHECK(!audio_scheduler_.get()); 212 DCHECK(!desktop_environment_); 213 DCHECK(!input_injector_); 214 DCHECK(!screen_controls_); 215 DCHECK(!video_scheduler_.get()); 216 217 auth_input_filter_.set_enabled(true); 218 auth_clipboard_filter_.set_enabled(true); 219 220 clipboard_echo_filter_.set_client_stub(connection_->client_stub()); 221 mouse_clamping_filter_.set_video_stub(connection_->video_stub()); 222 223 if (max_duration_ > base::TimeDelta()) { 224 // TODO(simonmorris): Let Disconnect() tell the client that the 225 // disconnection was caused by the session exceeding its maximum duration. 226 max_duration_timer_.Start(FROM_HERE, max_duration_, 227 this, &ClientSession::DisconnectSession); 228 } 229 230 // Disconnect the session if the connection was rejected by the host. 231 if (!event_handler_->OnSessionAuthenticated(this)) { 232 DisconnectSession(); 233 return; 234 } 235 236 // Create the desktop environment. Drop the connection if it could not be 237 // created for any reason (for instance the curtain could not initialize). 238 desktop_environment_ = 239 desktop_environment_factory_->Create(control_factory_.GetWeakPtr()); 240 if (!desktop_environment_) { 241 DisconnectSession(); 242 return; 243 } 244 245 host_capabilities_ = desktop_environment_->GetCapabilities(); 246 247 // Ignore protocol::Capabilities messages from the client if it does not 248 // support any capabilities. 249 if (!connection_->session()->config().SupportsCapabilities()) { 250 VLOG(1) << "The client does not support any capabilities."; 251 252 client_capabilities_ = make_scoped_ptr(new std::string()); 253 desktop_environment_->SetCapabilities(*client_capabilities_); 254 } 255 256 // Create the object that controls the screen resolution. 257 screen_controls_ = desktop_environment_->CreateScreenControls(); 258 259 // Create the event executor. 260 input_injector_ = desktop_environment_->CreateInputInjector(); 261 262 // Connect the host clipboard and input stubs. 263 host_input_filter_.set_input_stub(input_injector_.get()); 264 clipboard_echo_filter_.set_host_stub(input_injector_.get()); 265 266 // Create a VideoEncoder based on the session's video channel configuration. 267 scoped_ptr<VideoEncoder> video_encoder = 268 CreateVideoEncoder(connection_->session()->config()); 269 270 // Create a VideoScheduler to pump frames from the capturer to the client. 271 video_scheduler_ = new VideoScheduler( 272 video_capture_task_runner_, 273 video_encode_task_runner_, 274 network_task_runner_, 275 desktop_environment_->CreateVideoCapturer(), 276 video_encoder.Pass(), 277 connection_->client_stub(), 278 &mouse_clamping_filter_); 279 280 // Create an AudioScheduler if audio is enabled, to pump audio samples. 281 if (connection_->session()->config().is_audio_enabled()) { 282 scoped_ptr<AudioEncoder> audio_encoder = 283 CreateAudioEncoder(connection_->session()->config()); 284 audio_scheduler_ = new AudioScheduler( 285 audio_task_runner_, 286 network_task_runner_, 287 desktop_environment_->CreateAudioCapturer(), 288 audio_encoder.Pass(), 289 connection_->audio_stub()); 290 } 291 } 292 293 void ClientSession::OnConnectionChannelsConnected( 294 protocol::ConnectionToClient* connection) { 295 DCHECK(CalledOnValidThread()); 296 DCHECK_EQ(connection_.get(), connection); 297 298 // Negotiate capabilities with the client. 299 if (connection_->session()->config().SupportsCapabilities()) { 300 VLOG(1) << "Host capabilities: " << host_capabilities_; 301 302 protocol::Capabilities capabilities; 303 capabilities.set_capabilities(host_capabilities_); 304 connection_->client_stub()->SetCapabilities(capabilities); 305 } 306 307 // Start the event executor. 308 input_injector_->Start(CreateClipboardProxy()); 309 SetDisableInputs(false); 310 311 // Start capturing the screen. 312 video_scheduler_->Start(); 313 314 // Start recording audio. 315 if (connection_->session()->config().is_audio_enabled()) 316 audio_scheduler_->Start(); 317 318 // Notify the event handler that all our channels are now connected. 319 event_handler_->OnSessionChannelsConnected(this); 320 } 321 322 void ClientSession::OnConnectionClosed( 323 protocol::ConnectionToClient* connection, 324 protocol::ErrorCode error) { 325 DCHECK(CalledOnValidThread()); 326 DCHECK_EQ(connection_.get(), connection); 327 328 // Ignore any further callbacks. 329 control_factory_.InvalidateWeakPtrs(); 330 331 // If the client never authenticated then the session failed. 332 if (!auth_input_filter_.enabled()) 333 event_handler_->OnSessionAuthenticationFailed(this); 334 335 // Block any further input events from the client. 336 // TODO(wez): Fix ChromotingHost::OnSessionClosed not to check our 337 // is_authenticated(), so that we can disable |auth_*_filter_| here. 338 disable_input_filter_.set_enabled(false); 339 disable_clipboard_filter_.set_enabled(false); 340 341 // Ensure that any pressed keys or buttons are released. 342 input_tracker_.ReleaseAll(); 343 344 // Stop components access the client, audio or video stubs, which are no 345 // longer valid once ConnectionToClient calls OnConnectionClosed(). 346 if (audio_scheduler_.get()) { 347 audio_scheduler_->Stop(); 348 audio_scheduler_ = NULL; 349 } 350 if (video_scheduler_.get()) { 351 video_scheduler_->Stop(); 352 video_scheduler_ = NULL; 353 } 354 355 client_clipboard_factory_.InvalidateWeakPtrs(); 356 input_injector_.reset(); 357 screen_controls_.reset(); 358 desktop_environment_.reset(); 359 360 // Notify the ChromotingHost that this client is disconnected. 361 // TODO(sergeyu): Log failure reason? 362 event_handler_->OnSessionClosed(this); 363 } 364 365 void ClientSession::OnSequenceNumberUpdated( 366 protocol::ConnectionToClient* connection, int64 sequence_number) { 367 DCHECK(CalledOnValidThread()); 368 DCHECK_EQ(connection_.get(), connection); 369 370 if (video_scheduler_.get()) 371 video_scheduler_->UpdateSequenceNumber(sequence_number); 372 373 event_handler_->OnSessionSequenceNumber(this, sequence_number); 374 } 375 376 void ClientSession::OnRouteChange( 377 protocol::ConnectionToClient* connection, 378 const std::string& channel_name, 379 const protocol::TransportRoute& route) { 380 DCHECK(CalledOnValidThread()); 381 DCHECK_EQ(connection_.get(), connection); 382 event_handler_->OnSessionRouteChange(this, channel_name, route); 383 } 384 385 const std::string& ClientSession::client_jid() const { 386 return client_jid_; 387 } 388 389 void ClientSession::DisconnectSession() { 390 DCHECK(CalledOnValidThread()); 391 DCHECK(connection_.get()); 392 393 max_duration_timer_.Stop(); 394 395 // This triggers OnConnectionClosed(), and the session may be destroyed 396 // as the result, so this call must be the last in this method. 397 connection_->Disconnect(); 398 } 399 400 void ClientSession::OnLocalMouseMoved(const webrtc::DesktopVector& position) { 401 DCHECK(CalledOnValidThread()); 402 remote_input_filter_.LocalMouseMoved(position); 403 } 404 405 void ClientSession::SetDisableInputs(bool disable_inputs) { 406 DCHECK(CalledOnValidThread()); 407 408 if (disable_inputs) 409 input_tracker_.ReleaseAll(); 410 411 disable_input_filter_.set_enabled(!disable_inputs); 412 disable_clipboard_filter_.set_enabled(!disable_inputs); 413 } 414 415 scoped_ptr<protocol::ClipboardStub> ClientSession::CreateClipboardProxy() { 416 DCHECK(CalledOnValidThread()); 417 418 return scoped_ptr<protocol::ClipboardStub>( 419 new protocol::ClipboardThreadProxy( 420 client_clipboard_factory_.GetWeakPtr(), 421 base::MessageLoopProxy::current())); 422 } 423 424 // TODO(sergeyu): Move this to SessionManager? 425 // static 426 scoped_ptr<VideoEncoder> ClientSession::CreateVideoEncoder( 427 const protocol::SessionConfig& config) { 428 const protocol::ChannelConfig& video_config = config.video_config(); 429 430 if (video_config.codec == protocol::ChannelConfig::CODEC_VP8) { 431 return remoting::VideoEncoderVpx::CreateForVP8().PassAs<VideoEncoder>(); 432 } 433 434 NOTREACHED(); 435 return scoped_ptr<VideoEncoder>(); 436 } 437 438 // static 439 scoped_ptr<AudioEncoder> ClientSession::CreateAudioEncoder( 440 const protocol::SessionConfig& config) { 441 const protocol::ChannelConfig& audio_config = config.audio_config(); 442 443 if (audio_config.codec == protocol::ChannelConfig::CODEC_VERBATIM) { 444 return scoped_ptr<AudioEncoder>(new AudioEncoderVerbatim()); 445 } else if (audio_config.codec == protocol::ChannelConfig::CODEC_OPUS) { 446 return scoped_ptr<AudioEncoder>(new AudioEncoderOpus()); 447 } 448 449 NOTREACHED(); 450 return scoped_ptr<AudioEncoder>(); 451 } 452 453 } // namespace remoting 454