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       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