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