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 desktop_pipe_(IPC::InvalidPlatformFileForTransit()), 124 next_shared_buffer_id_(1), 125 shared_buffers_(0), 126 started_(false) { 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_InjectMouseEvent, 143 OnInjectMouseEvent) 144 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_SetScreenResolution, 145 SetScreenResolution) 146 IPC_MESSAGE_UNHANDLED(handled = false) 147 IPC_END_MESSAGE_MAP() 148 } else { 149 IPC_BEGIN_MESSAGE_MAP(DesktopSessionAgent, message) 150 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_StartSessionAgent, 151 OnStartSessionAgent) 152 IPC_MESSAGE_UNHANDLED(handled = false) 153 IPC_END_MESSAGE_MAP() 154 } 155 156 CHECK(handled) << "Received unexpected IPC type: " << message.type(); 157 return handled; 158 } 159 160 void DesktopSessionAgent::OnChannelConnected(int32 peer_pid) { 161 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 162 163 VLOG(1) << "IPC: desktop <- network (" << peer_pid << ")"; 164 165 CloseDesktopPipeHandle(); 166 } 167 168 void DesktopSessionAgent::OnChannelError() { 169 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 170 171 // Make sure the channel is closed. 172 network_channel_.reset(); 173 CloseDesktopPipeHandle(); 174 175 // Notify the caller that the channel has been disconnected. 176 if (delegate_.get()) 177 delegate_->OnNetworkProcessDisconnected(); 178 } 179 180 webrtc::SharedMemory* DesktopSessionAgent::CreateSharedMemory(size_t size) { 181 DCHECK(video_capture_task_runner_->BelongsToCurrentThread()); 182 183 scoped_ptr<SharedBuffer> buffer = 184 SharedBuffer::Create(this, size, next_shared_buffer_id_); 185 if (buffer) { 186 shared_buffers_++; 187 188 // |next_shared_buffer_id_| starts from 1 and incrementing it by 2 makes 189 // sure it is always odd and therefore zero is never used as a valid buffer 190 // ID. 191 // 192 // It is very unlikely (though theoretically possible) to allocate the same 193 // ID for two different buffers due to integer overflow. It should take 194 // about a year of allocating 100 new buffers every second. Practically 195 // speaking it never happens. 196 next_shared_buffer_id_ += 2; 197 198 IPC::PlatformFileForTransit handle; 199 #if defined(OS_WIN) 200 handle = buffer->handle(); 201 #else 202 handle = base::FileDescriptor(buffer->handle(), false); 203 #endif 204 SendToNetwork(new ChromotingDesktopNetworkMsg_CreateSharedBuffer( 205 buffer->id(), handle, buffer->size())); 206 } 207 208 return buffer.release(); 209 } 210 211 DesktopSessionAgent::~DesktopSessionAgent() { 212 DCHECK(!audio_capturer_); 213 DCHECK(!desktop_environment_); 214 DCHECK(!network_channel_); 215 DCHECK(!screen_controls_); 216 DCHECK(!video_capturer_); 217 218 CloseDesktopPipeHandle(); 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::OnStartSessionAgent( 244 const std::string& authenticated_jid, 245 const ScreenResolution& resolution, 246 bool virtual_terminal) { 247 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 248 DCHECK(!started_); 249 DCHECK(!audio_capturer_); 250 DCHECK(!desktop_environment_); 251 DCHECK(!input_injector_); 252 DCHECK(!screen_controls_); 253 DCHECK(!video_capturer_); 254 255 started_ = true; 256 client_jid_ = authenticated_jid; 257 258 // Enable the curtain mode. 259 delegate_->desktop_environment_factory().SetEnableCurtaining( 260 virtual_terminal); 261 262 // Create a desktop environment for the new session. 263 desktop_environment_ = delegate_->desktop_environment_factory().Create( 264 control_factory_.GetWeakPtr()); 265 266 // Create the session controller and set the initial screen resolution. 267 screen_controls_ = desktop_environment_->CreateScreenControls(); 268 SetScreenResolution(resolution); 269 270 // Create the input injector. 271 input_injector_ = desktop_environment_->CreateInputInjector(); 272 273 // Hook up the input filter. 274 input_tracker_.reset(new protocol::InputEventTracker(input_injector_.get())); 275 remote_input_filter_.reset(new RemoteInputFilter(input_tracker_.get())); 276 277 #if defined(OS_WIN) 278 // LocalInputMonitorWin filters out an echo of the injected input before it 279 // reaches |remote_input_filter_|. 280 remote_input_filter_->SetExpectLocalEcho(false); 281 #endif // defined(OS_WIN) 282 283 // Start the input injector. 284 scoped_ptr<protocol::ClipboardStub> clipboard_stub( 285 new DesktopSesssionClipboardStub(this)); 286 input_injector_->Start(clipboard_stub.Pass()); 287 288 // Start the audio capturer. 289 if (delegate_->desktop_environment_factory().SupportsAudioCapture()) { 290 audio_capturer_ = desktop_environment_->CreateAudioCapturer(); 291 audio_capture_task_runner_->PostTask( 292 FROM_HERE, base::Bind(&DesktopSessionAgent::StartAudioCapturer, this)); 293 } 294 295 // Start the video capturer. 296 video_capturer_ = desktop_environment_->CreateVideoCapturer(); 297 video_capture_task_runner_->PostTask( 298 FROM_HERE, base::Bind(&DesktopSessionAgent::StartVideoCapturer, this)); 299 } 300 301 void DesktopSessionAgent::OnCaptureCompleted(webrtc::DesktopFrame* frame) { 302 DCHECK(video_capture_task_runner_->BelongsToCurrentThread()); 303 304 last_frame_.reset(frame); 305 306 current_size_ = frame->size(); 307 308 // Serialize webrtc::DesktopFrame. 309 SerializedDesktopFrame serialized_frame; 310 serialized_frame.shared_buffer_id = frame->shared_memory()->id(); 311 serialized_frame.bytes_per_row = frame->stride(); 312 serialized_frame.dimensions = frame->size(); 313 serialized_frame.capture_time_ms = frame->capture_time_ms(); 314 serialized_frame.dpi = frame->dpi(); 315 for (webrtc::DesktopRegion::Iterator i(frame->updated_region()); 316 !i.IsAtEnd(); i.Advance()) { 317 serialized_frame.dirty_region.push_back(i.rect()); 318 } 319 320 SendToNetwork( 321 new ChromotingDesktopNetworkMsg_CaptureCompleted(serialized_frame)); 322 } 323 324 void DesktopSessionAgent::OnCursorShapeChanged( 325 webrtc::MouseCursorShape* cursor_shape) { 326 DCHECK(video_capture_task_runner_->BelongsToCurrentThread()); 327 328 scoped_ptr<webrtc::MouseCursorShape> owned_cursor(cursor_shape); 329 330 SendToNetwork(new ChromotingDesktopNetworkMsg_CursorShapeChanged( 331 *cursor_shape)); 332 } 333 334 void DesktopSessionAgent::InjectClipboardEvent( 335 const protocol::ClipboardEvent& event) { 336 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 337 338 std::string serialized_event; 339 if (!event.SerializeToString(&serialized_event)) { 340 LOG(ERROR) << "Failed to serialize protocol::ClipboardEvent."; 341 return; 342 } 343 344 SendToNetwork( 345 new ChromotingDesktopNetworkMsg_InjectClipboardEvent(serialized_event)); 346 } 347 348 void DesktopSessionAgent::ProcessAudioPacket(scoped_ptr<AudioPacket> packet) { 349 DCHECK(audio_capture_task_runner_->BelongsToCurrentThread()); 350 351 std::string serialized_packet; 352 if (!packet->SerializeToString(&serialized_packet)) { 353 LOG(ERROR) << "Failed to serialize AudioPacket."; 354 return; 355 } 356 357 SendToNetwork(new ChromotingDesktopNetworkMsg_AudioPacket(serialized_packet)); 358 } 359 360 bool DesktopSessionAgent::Start(const base::WeakPtr<Delegate>& delegate, 361 IPC::PlatformFileForTransit* desktop_pipe_out) { 362 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 363 DCHECK(delegate_.get() == NULL); 364 365 delegate_ = delegate; 366 367 // Create an IPC channel to communicate with the network process. 368 bool result = CreateConnectedIpcChannel(io_task_runner_, 369 this, 370 &desktop_pipe_, 371 &network_channel_); 372 *desktop_pipe_out = desktop_pipe_; 373 return result; 374 } 375 376 void DesktopSessionAgent::Stop() { 377 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 378 379 delegate_.reset(); 380 381 // Make sure the channel is closed. 382 network_channel_.reset(); 383 384 if (started_) { 385 started_ = false; 386 387 // Ignore any further callbacks. 388 control_factory_.InvalidateWeakPtrs(); 389 client_jid_.clear(); 390 391 remote_input_filter_.reset(); 392 393 // Ensure that any pressed keys or buttons are released. 394 input_tracker_->ReleaseAll(); 395 input_tracker_.reset(); 396 397 desktop_environment_.reset(); 398 input_injector_.reset(); 399 screen_controls_.reset(); 400 401 // Stop the audio capturer. 402 audio_capture_task_runner_->PostTask( 403 FROM_HERE, base::Bind(&DesktopSessionAgent::StopAudioCapturer, this)); 404 405 // Stop the video capturer. 406 video_capture_task_runner_->PostTask( 407 FROM_HERE, base::Bind(&DesktopSessionAgent::StopVideoCapturer, this)); 408 } 409 } 410 411 void DesktopSessionAgent::OnCaptureFrame() { 412 if (!video_capture_task_runner_->BelongsToCurrentThread()) { 413 video_capture_task_runner_->PostTask( 414 FROM_HERE, 415 base::Bind(&DesktopSessionAgent::OnCaptureFrame, this)); 416 return; 417 } 418 419 // webrtc::ScreenCapturer supports a very few (currently 2) outstanding 420 // capture requests. The requests are serialized on 421 // |video_capture_task_runner()| task runner. If the client issues more 422 // requests, pixel data in captured frames will likely be corrupted but 423 // stability of webrtc::ScreenCapturer will not be affected. 424 video_capturer_->Capture(webrtc::DesktopRegion()); 425 } 426 427 void DesktopSessionAgent::OnInjectClipboardEvent( 428 const std::string& serialized_event) { 429 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 430 431 protocol::ClipboardEvent event; 432 if (!event.ParseFromString(serialized_event)) { 433 LOG(ERROR) << "Failed to parse protocol::ClipboardEvent."; 434 return; 435 } 436 437 // InputStub implementations must verify events themselves, so we don't need 438 // verification here. This matches HostEventDispatcher. 439 input_injector_->InjectClipboardEvent(event); 440 } 441 442 void DesktopSessionAgent::OnInjectKeyEvent( 443 const std::string& serialized_event) { 444 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 445 446 protocol::KeyEvent event; 447 if (!event.ParseFromString(serialized_event)) { 448 LOG(ERROR) << "Failed to parse protocol::KeyEvent."; 449 return; 450 } 451 452 // InputStub implementations must verify events themselves, so we need only 453 // basic verification here. This matches HostEventDispatcher. 454 if (!event.has_usb_keycode() || !event.has_pressed()) { 455 LOG(ERROR) << "Received invalid key event."; 456 return; 457 } 458 459 remote_input_filter_->InjectKeyEvent(event); 460 } 461 462 void DesktopSessionAgent::OnInjectMouseEvent( 463 const std::string& serialized_event) { 464 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 465 466 protocol::MouseEvent event; 467 if (!event.ParseFromString(serialized_event)) { 468 LOG(ERROR) << "Failed to parse protocol::MouseEvent."; 469 return; 470 } 471 472 // InputStub implementations must verify events themselves, so we don't need 473 // verification here. This matches HostEventDispatcher. 474 remote_input_filter_->InjectMouseEvent(event); 475 } 476 477 void DesktopSessionAgent::SetScreenResolution( 478 const ScreenResolution& resolution) { 479 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 480 481 if (screen_controls_ && resolution.IsEmpty()) 482 screen_controls_->SetScreenResolution(resolution); 483 } 484 485 void DesktopSessionAgent::SendToNetwork(IPC::Message* message) { 486 if (!caller_task_runner_->BelongsToCurrentThread()) { 487 caller_task_runner_->PostTask( 488 FROM_HERE, 489 base::Bind(&DesktopSessionAgent::SendToNetwork, this, message)); 490 return; 491 } 492 493 if (network_channel_) { 494 network_channel_->Send(message); 495 } else { 496 delete message; 497 } 498 } 499 500 void DesktopSessionAgent::StartAudioCapturer() { 501 DCHECK(audio_capture_task_runner_->BelongsToCurrentThread()); 502 503 if (audio_capturer_) { 504 audio_capturer_->Start(base::Bind(&DesktopSessionAgent::ProcessAudioPacket, 505 this)); 506 } 507 } 508 509 void DesktopSessionAgent::StopAudioCapturer() { 510 DCHECK(audio_capture_task_runner_->BelongsToCurrentThread()); 511 512 audio_capturer_.reset(); 513 } 514 515 void DesktopSessionAgent::StartVideoCapturer() { 516 DCHECK(video_capture_task_runner_->BelongsToCurrentThread()); 517 518 if (video_capturer_) { 519 video_capturer_->SetMouseShapeObserver(this); 520 video_capturer_->Start(this); 521 } 522 } 523 524 void DesktopSessionAgent::StopVideoCapturer() { 525 DCHECK(video_capture_task_runner_->BelongsToCurrentThread()); 526 527 video_capturer_.reset(); 528 last_frame_.reset(); 529 530 // Video capturer must delete all buffers. 531 DCHECK_EQ(shared_buffers_, 0); 532 } 533 534 void DesktopSessionAgent::OnSharedBufferDeleted(int id) { 535 DCHECK(video_capture_task_runner_->BelongsToCurrentThread()); 536 DCHECK(id != 0); 537 538 shared_buffers_--; 539 DCHECK_GE(shared_buffers_, 0); 540 SendToNetwork(new ChromotingDesktopNetworkMsg_ReleaseSharedBuffer(id)); 541 } 542 543 void DesktopSessionAgent::CloseDesktopPipeHandle() { 544 if (!(desktop_pipe_ == IPC::InvalidPlatformFileForTransit())) { 545 #if defined(OS_WIN) 546 base::ClosePlatformFile(desktop_pipe_); 547 #elif defined(OS_POSIX) 548 base::ClosePlatformFile(desktop_pipe_.fd); 549 #else // !defined(OS_POSIX) 550 #error Unsupported platform. 551 #endif // !defined(OS_POSIX) 552 553 desktop_pipe_ = IPC::InvalidPlatformFileForTransit(); 554 } 555 } 556 557 } // namespace remoting 558