Home | History | Annotate | Download | only in host
      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