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/desktop_session_agent.h" 6 7 #include "base/files/file_util.h" 8 #include "base/logging.h" 9 #include "base/memory/shared_memory.h" 10 #include "ipc/ipc_channel_proxy.h" 11 #include "ipc/ipc_message.h" 12 #include "ipc/ipc_message_macros.h" 13 #include "remoting/base/auto_thread_task_runner.h" 14 #include "remoting/base/constants.h" 15 #include "remoting/host/audio_capturer.h" 16 #include "remoting/host/chromoting_messages.h" 17 #include "remoting/host/desktop_environment.h" 18 #include "remoting/host/input_injector.h" 19 #include "remoting/host/ipc_util.h" 20 #include "remoting/host/remote_input_filter.h" 21 #include "remoting/host/screen_controls.h" 22 #include "remoting/host/screen_resolution.h" 23 #include "remoting/proto/audio.pb.h" 24 #include "remoting/proto/control.pb.h" 25 #include "remoting/proto/event.pb.h" 26 #include "remoting/protocol/clipboard_stub.h" 27 #include "remoting/protocol/input_event_tracker.h" 28 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" 29 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" 30 #include "third_party/webrtc/modules/desktop_capture/mouse_cursor.h" 31 #include "third_party/webrtc/modules/desktop_capture/shared_memory.h" 32 33 namespace remoting { 34 35 namespace { 36 37 // Routes local clipboard events though the IPC channel to the network process. 38 class DesktopSesssionClipboardStub : public protocol::ClipboardStub { 39 public: 40 explicit DesktopSesssionClipboardStub( 41 scoped_refptr<DesktopSessionAgent> desktop_session_agent); 42 virtual ~DesktopSesssionClipboardStub(); 43 44 // protocol::ClipboardStub implementation. 45 virtual void InjectClipboardEvent( 46 const protocol::ClipboardEvent& event) OVERRIDE; 47 48 private: 49 scoped_refptr<DesktopSessionAgent> desktop_session_agent_; 50 51 DISALLOW_COPY_AND_ASSIGN(DesktopSesssionClipboardStub); 52 }; 53 54 DesktopSesssionClipboardStub::DesktopSesssionClipboardStub( 55 scoped_refptr<DesktopSessionAgent> desktop_session_agent) 56 : desktop_session_agent_(desktop_session_agent) { 57 } 58 59 DesktopSesssionClipboardStub::~DesktopSesssionClipboardStub() { 60 } 61 62 void DesktopSesssionClipboardStub::InjectClipboardEvent( 63 const protocol::ClipboardEvent& event) { 64 desktop_session_agent_->InjectClipboardEvent(event); 65 } 66 67 } // namespace 68 69 // webrtc::SharedMemory implementation that notifies creating 70 // DesktopSessionAgent when it's deleted. 71 class DesktopSessionAgent::SharedBuffer : public webrtc::SharedMemory { 72 public: 73 static scoped_ptr<SharedBuffer> Create(DesktopSessionAgent* agent, 74 size_t size, 75 int id) { 76 scoped_ptr<base::SharedMemory> memory(new base::SharedMemory()); 77 if (!memory->CreateAndMapAnonymous(size)) 78 return scoped_ptr<SharedBuffer>(); 79 return scoped_ptr<SharedBuffer>( 80 new SharedBuffer(agent, memory.Pass(), size, id)); 81 } 82 83 virtual ~SharedBuffer() { 84 agent_->OnSharedBufferDeleted(id()); 85 } 86 87 private: 88 SharedBuffer(DesktopSessionAgent* agent, 89 scoped_ptr<base::SharedMemory> memory, 90 size_t size, 91 int id) 92 : SharedMemory(memory->memory(), size, 93 #if defined(OS_WIN) 94 memory->handle(), 95 #else 96 memory->handle().fd, 97 #endif 98 id), 99 agent_(agent), 100 shared_memory_(memory.Pass()) { 101 } 102 103 DesktopSessionAgent* agent_; 104 scoped_ptr<base::SharedMemory> shared_memory_; 105 106 DISALLOW_COPY_AND_ASSIGN(SharedBuffer); 107 }; 108 109 DesktopSessionAgent::Delegate::~Delegate() { 110 } 111 112 DesktopSessionAgent::DesktopSessionAgent( 113 scoped_refptr<AutoThreadTaskRunner> audio_capture_task_runner, 114 scoped_refptr<AutoThreadTaskRunner> caller_task_runner, 115 scoped_refptr<AutoThreadTaskRunner> input_task_runner, 116 scoped_refptr<AutoThreadTaskRunner> io_task_runner, 117 scoped_refptr<AutoThreadTaskRunner> video_capture_task_runner) 118 : audio_capture_task_runner_(audio_capture_task_runner), 119 caller_task_runner_(caller_task_runner), 120 input_task_runner_(input_task_runner), 121 io_task_runner_(io_task_runner), 122 video_capture_task_runner_(video_capture_task_runner), 123 next_shared_buffer_id_(1), 124 shared_buffers_(0), 125 started_(false), 126 weak_factory_(this) { 127 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 128 } 129 130 bool DesktopSessionAgent::OnMessageReceived(const IPC::Message& message) { 131 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 132 133 bool handled = true; 134 if (started_) { 135 IPC_BEGIN_MESSAGE_MAP(DesktopSessionAgent, message) 136 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_CaptureFrame, 137 OnCaptureFrame) 138 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectClipboardEvent, 139 OnInjectClipboardEvent) 140 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectKeyEvent, 141 OnInjectKeyEvent) 142 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectTextEvent, 143 OnInjectTextEvent) 144 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectMouseEvent, 145 OnInjectMouseEvent) 146 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_SetScreenResolution, 147 SetScreenResolution) 148 IPC_MESSAGE_UNHANDLED(handled = false) 149 IPC_END_MESSAGE_MAP() 150 } else { 151 IPC_BEGIN_MESSAGE_MAP(DesktopSessionAgent, message) 152 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_StartSessionAgent, 153 OnStartSessionAgent) 154 IPC_MESSAGE_UNHANDLED(handled = false) 155 IPC_END_MESSAGE_MAP() 156 } 157 158 CHECK(handled) << "Received unexpected IPC type: " << message.type(); 159 return handled; 160 } 161 162 void DesktopSessionAgent::OnChannelConnected(int32 peer_pid) { 163 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 164 165 VLOG(1) << "IPC: desktop <- network (" << peer_pid << ")"; 166 167 desktop_pipe_.Close(); 168 } 169 170 void DesktopSessionAgent::OnChannelError() { 171 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 172 173 // Make sure the channel is closed. 174 network_channel_.reset(); 175 desktop_pipe_.Close(); 176 177 // Notify the caller that the channel has been disconnected. 178 if (delegate_.get()) 179 delegate_->OnNetworkProcessDisconnected(); 180 } 181 182 webrtc::SharedMemory* DesktopSessionAgent::CreateSharedMemory(size_t size) { 183 DCHECK(video_capture_task_runner_->BelongsToCurrentThread()); 184 185 scoped_ptr<SharedBuffer> buffer = 186 SharedBuffer::Create(this, size, next_shared_buffer_id_); 187 if (buffer) { 188 shared_buffers_++; 189 190 // |next_shared_buffer_id_| starts from 1 and incrementing it by 2 makes 191 // sure it is always odd and therefore zero is never used as a valid buffer 192 // ID. 193 // 194 // It is very unlikely (though theoretically possible) to allocate the same 195 // ID for two different buffers due to integer overflow. It should take 196 // about a year of allocating 100 new buffers every second. Practically 197 // speaking it never happens. 198 next_shared_buffer_id_ += 2; 199 200 IPC::PlatformFileForTransit handle; 201 #if defined(OS_WIN) 202 handle = buffer->handle(); 203 #else 204 handle = base::FileDescriptor(buffer->handle(), false); 205 #endif 206 SendToNetwork(new ChromotingDesktopNetworkMsg_CreateSharedBuffer( 207 buffer->id(), handle, buffer->size())); 208 } 209 210 return buffer.release(); 211 } 212 213 DesktopSessionAgent::~DesktopSessionAgent() { 214 DCHECK(!audio_capturer_); 215 DCHECK(!desktop_environment_); 216 DCHECK(!network_channel_); 217 DCHECK(!screen_controls_); 218 DCHECK(!video_capturer_); 219 } 220 221 const std::string& DesktopSessionAgent::client_jid() const { 222 return client_jid_; 223 } 224 225 void DesktopSessionAgent::DisconnectSession() { 226 SendToNetwork(new ChromotingDesktopNetworkMsg_DisconnectSession()); 227 } 228 229 void DesktopSessionAgent::OnLocalMouseMoved( 230 const webrtc::DesktopVector& new_pos) { 231 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 232 233 remote_input_filter_->LocalMouseMoved(new_pos); 234 } 235 236 void DesktopSessionAgent::SetDisableInputs(bool disable_inputs) { 237 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 238 239 // Do not expect this method to be called because it is only used by It2Me. 240 NOTREACHED(); 241 } 242 243 void DesktopSessionAgent::ResetVideoPipeline() { 244 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 245 246 // This method is only used by HostExtensionSessions in the network process. 247 NOTREACHED(); 248 } 249 250 void DesktopSessionAgent::OnStartSessionAgent( 251 const std::string& authenticated_jid, 252 const ScreenResolution& resolution, 253 bool virtual_terminal) { 254 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 255 DCHECK(!started_); 256 DCHECK(!audio_capturer_); 257 DCHECK(!desktop_environment_); 258 DCHECK(!input_injector_); 259 DCHECK(!screen_controls_); 260 DCHECK(!video_capturer_); 261 262 started_ = true; 263 client_jid_ = authenticated_jid; 264 265 // Enable the curtain mode. 266 delegate_->desktop_environment_factory().SetEnableCurtaining( 267 virtual_terminal); 268 269 // Create a desktop environment for the new session. 270 desktop_environment_ = delegate_->desktop_environment_factory().Create( 271 weak_factory_.GetWeakPtr()); 272 273 // Create the session controller and set the initial screen resolution. 274 screen_controls_ = desktop_environment_->CreateScreenControls(); 275 SetScreenResolution(resolution); 276 277 // Create the input injector. 278 input_injector_ = desktop_environment_->CreateInputInjector(); 279 280 // Hook up the input filter. 281 input_tracker_.reset(new protocol::InputEventTracker(input_injector_.get())); 282 remote_input_filter_.reset(new RemoteInputFilter(input_tracker_.get())); 283 284 #if defined(OS_WIN) 285 // LocalInputMonitorWin filters out an echo of the injected input before it 286 // reaches |remote_input_filter_|. 287 remote_input_filter_->SetExpectLocalEcho(false); 288 #endif // defined(OS_WIN) 289 290 // Start the input injector. 291 scoped_ptr<protocol::ClipboardStub> clipboard_stub( 292 new DesktopSesssionClipboardStub(this)); 293 input_injector_->Start(clipboard_stub.Pass()); 294 295 // Start the audio capturer. 296 if (delegate_->desktop_environment_factory().SupportsAudioCapture()) { 297 audio_capturer_ = desktop_environment_->CreateAudioCapturer(); 298 audio_capture_task_runner_->PostTask( 299 FROM_HERE, base::Bind(&DesktopSessionAgent::StartAudioCapturer, this)); 300 } 301 302 // Start the video capturer and mouse cursor monitor. 303 video_capturer_ = desktop_environment_->CreateVideoCapturer(); 304 mouse_cursor_monitor_ = desktop_environment_->CreateMouseCursorMonitor(); 305 video_capture_task_runner_->PostTask( 306 FROM_HERE, base::Bind( 307 &DesktopSessionAgent::StartVideoCapturerAndMouseMonitor, this)); 308 } 309 310 void DesktopSessionAgent::OnCaptureCompleted(webrtc::DesktopFrame* frame) { 311 DCHECK(video_capture_task_runner_->BelongsToCurrentThread()); 312 313 last_frame_.reset(frame); 314 315 current_size_ = frame->size(); 316 317 // Serialize webrtc::DesktopFrame. 318 SerializedDesktopFrame serialized_frame; 319 serialized_frame.shared_buffer_id = frame->shared_memory()->id(); 320 serialized_frame.bytes_per_row = frame->stride(); 321 serialized_frame.dimensions = frame->size(); 322 serialized_frame.capture_time_ms = frame->capture_time_ms(); 323 serialized_frame.dpi = frame->dpi(); 324 for (webrtc::DesktopRegion::Iterator i(frame->updated_region()); 325 !i.IsAtEnd(); i.Advance()) { 326 serialized_frame.dirty_region.push_back(i.rect()); 327 } 328 329 SendToNetwork( 330 new ChromotingDesktopNetworkMsg_CaptureCompleted(serialized_frame)); 331 } 332 333 void DesktopSessionAgent::OnMouseCursor(webrtc::MouseCursor* cursor) { 334 DCHECK(video_capture_task_runner_->BelongsToCurrentThread()); 335 336 scoped_ptr<webrtc::MouseCursor> owned_cursor(cursor); 337 338 SendToNetwork( 339 new ChromotingDesktopNetworkMsg_MouseCursor(*owned_cursor)); 340 } 341 342 void DesktopSessionAgent::OnMouseCursorPosition( 343 webrtc::MouseCursorMonitor::CursorState state, 344 const webrtc::DesktopVector& position) { 345 // We're not subscribing to mouse position changes. 346 NOTREACHED(); 347 } 348 349 void DesktopSessionAgent::InjectClipboardEvent( 350 const protocol::ClipboardEvent& event) { 351 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 352 353 std::string serialized_event; 354 if (!event.SerializeToString(&serialized_event)) { 355 LOG(ERROR) << "Failed to serialize protocol::ClipboardEvent."; 356 return; 357 } 358 359 SendToNetwork( 360 new ChromotingDesktopNetworkMsg_InjectClipboardEvent(serialized_event)); 361 } 362 363 void DesktopSessionAgent::ProcessAudioPacket(scoped_ptr<AudioPacket> packet) { 364 DCHECK(audio_capture_task_runner_->BelongsToCurrentThread()); 365 366 std::string serialized_packet; 367 if (!packet->SerializeToString(&serialized_packet)) { 368 LOG(ERROR) << "Failed to serialize AudioPacket."; 369 return; 370 } 371 372 SendToNetwork(new ChromotingDesktopNetworkMsg_AudioPacket(serialized_packet)); 373 } 374 375 bool DesktopSessionAgent::Start(const base::WeakPtr<Delegate>& delegate, 376 IPC::PlatformFileForTransit* desktop_pipe_out) { 377 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 378 DCHECK(delegate_.get() == NULL); 379 380 delegate_ = delegate; 381 382 // Create an IPC channel to communicate with the network process. 383 bool result = CreateConnectedIpcChannel(io_task_runner_, 384 this, 385 &desktop_pipe_, 386 &network_channel_); 387 base::PlatformFile raw_desktop_pipe = desktop_pipe_.GetPlatformFile(); 388 #if defined(OS_WIN) 389 *desktop_pipe_out = IPC::PlatformFileForTransit(raw_desktop_pipe); 390 #elif defined(OS_POSIX) 391 *desktop_pipe_out = IPC::PlatformFileForTransit(raw_desktop_pipe, false); 392 #else 393 #error Unsupported platform. 394 #endif 395 return result; 396 } 397 398 void DesktopSessionAgent::Stop() { 399 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 400 401 delegate_.reset(); 402 403 // Make sure the channel is closed. 404 network_channel_.reset(); 405 406 if (started_) { 407 started_ = false; 408 409 // Ignore any further callbacks. 410 weak_factory_.InvalidateWeakPtrs(); 411 client_jid_.clear(); 412 413 remote_input_filter_.reset(); 414 415 // Ensure that any pressed keys or buttons are released. 416 input_tracker_->ReleaseAll(); 417 input_tracker_.reset(); 418 419 desktop_environment_.reset(); 420 input_injector_.reset(); 421 screen_controls_.reset(); 422 423 // Stop the audio capturer. 424 audio_capture_task_runner_->PostTask( 425 FROM_HERE, base::Bind(&DesktopSessionAgent::StopAudioCapturer, this)); 426 427 // Stop the video capturer. 428 video_capture_task_runner_->PostTask( 429 FROM_HERE, base::Bind( 430 &DesktopSessionAgent::StopVideoCapturerAndMouseMonitor, this)); 431 } 432 } 433 434 void DesktopSessionAgent::OnCaptureFrame() { 435 if (!video_capture_task_runner_->BelongsToCurrentThread()) { 436 video_capture_task_runner_->PostTask( 437 FROM_HERE, 438 base::Bind(&DesktopSessionAgent::OnCaptureFrame, this)); 439 return; 440 } 441 442 mouse_cursor_monitor_->Capture(); 443 444 // webrtc::DesktopCapturer supports a very few (currently 2) outstanding 445 // capture requests. The requests are serialized on 446 // |video_capture_task_runner()| task runner. If the client issues more 447 // requests, pixel data in captured frames will likely be corrupted but 448 // stability of webrtc::DesktopCapturer will not be affected. 449 video_capturer_->Capture(webrtc::DesktopRegion()); 450 } 451 452 void DesktopSessionAgent::OnInjectClipboardEvent( 453 const std::string& serialized_event) { 454 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 455 456 protocol::ClipboardEvent event; 457 if (!event.ParseFromString(serialized_event)) { 458 LOG(ERROR) << "Failed to parse protocol::ClipboardEvent."; 459 return; 460 } 461 462 // InputStub implementations must verify events themselves, so we don't need 463 // verification here. This matches HostEventDispatcher. 464 input_injector_->InjectClipboardEvent(event); 465 } 466 467 void DesktopSessionAgent::OnInjectKeyEvent( 468 const std::string& serialized_event) { 469 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 470 471 protocol::KeyEvent event; 472 if (!event.ParseFromString(serialized_event)) { 473 LOG(ERROR) << "Failed to parse protocol::KeyEvent."; 474 return; 475 } 476 477 // InputStub implementations must verify events themselves, so we need only 478 // basic verification here. This matches HostEventDispatcher. 479 if (!event.has_usb_keycode() || !event.has_pressed()) { 480 LOG(ERROR) << "Received invalid key event."; 481 return; 482 } 483 484 remote_input_filter_->InjectKeyEvent(event); 485 } 486 487 void DesktopSessionAgent::OnInjectTextEvent( 488 const std::string& serialized_event) { 489 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 490 491 protocol::TextEvent event; 492 if (!event.ParseFromString(serialized_event)) { 493 LOG(ERROR) << "Failed to parse protocol::TextEvent."; 494 return; 495 } 496 497 // InputStub implementations must verify events themselves, so we need only 498 // basic verification here. This matches HostEventDispatcher. 499 if (!event.has_text()) { 500 LOG(ERROR) << "Received invalid TextEvent."; 501 return; 502 } 503 504 remote_input_filter_->InjectTextEvent(event); 505 } 506 507 void DesktopSessionAgent::OnInjectMouseEvent( 508 const std::string& serialized_event) { 509 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 510 511 protocol::MouseEvent event; 512 if (!event.ParseFromString(serialized_event)) { 513 LOG(ERROR) << "Failed to parse protocol::MouseEvent."; 514 return; 515 } 516 517 // InputStub implementations must verify events themselves, so we don't need 518 // verification here. This matches HostEventDispatcher. 519 remote_input_filter_->InjectMouseEvent(event); 520 } 521 522 void DesktopSessionAgent::SetScreenResolution( 523 const ScreenResolution& resolution) { 524 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 525 526 if (screen_controls_ && resolution.IsEmpty()) 527 screen_controls_->SetScreenResolution(resolution); 528 } 529 530 void DesktopSessionAgent::SendToNetwork(IPC::Message* message) { 531 if (!caller_task_runner_->BelongsToCurrentThread()) { 532 caller_task_runner_->PostTask( 533 FROM_HERE, 534 base::Bind(&DesktopSessionAgent::SendToNetwork, this, message)); 535 return; 536 } 537 538 if (network_channel_) { 539 network_channel_->Send(message); 540 } else { 541 delete message; 542 } 543 } 544 545 void DesktopSessionAgent::StartAudioCapturer() { 546 DCHECK(audio_capture_task_runner_->BelongsToCurrentThread()); 547 548 if (audio_capturer_) { 549 audio_capturer_->Start(base::Bind(&DesktopSessionAgent::ProcessAudioPacket, 550 this)); 551 } 552 } 553 554 void DesktopSessionAgent::StopAudioCapturer() { 555 DCHECK(audio_capture_task_runner_->BelongsToCurrentThread()); 556 557 audio_capturer_.reset(); 558 } 559 560 void DesktopSessionAgent::StartVideoCapturerAndMouseMonitor() { 561 DCHECK(video_capture_task_runner_->BelongsToCurrentThread()); 562 563 if (video_capturer_) { 564 video_capturer_->Start(this); 565 } 566 567 if (mouse_cursor_monitor_) { 568 mouse_cursor_monitor_->Init(this, webrtc::MouseCursorMonitor::SHAPE_ONLY); 569 } 570 } 571 572 void DesktopSessionAgent::StopVideoCapturerAndMouseMonitor() { 573 DCHECK(video_capture_task_runner_->BelongsToCurrentThread()); 574 575 video_capturer_.reset(); 576 last_frame_.reset(); 577 mouse_cursor_monitor_.reset(); 578 579 // Video capturer must delete all buffers. 580 DCHECK_EQ(shared_buffers_, 0); 581 } 582 583 void DesktopSessionAgent::OnSharedBufferDeleted(int id) { 584 DCHECK(video_capture_task_runner_->BelongsToCurrentThread()); 585 DCHECK(id != 0); 586 587 shared_buffers_--; 588 DCHECK_GE(shared_buffers_, 0); 589 SendToNetwork(new ChromotingDesktopNetworkMsg_ReleaseSharedBuffer(id)); 590 } 591 592 } // namespace remoting 593