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_proxy.h"
      6 
      7 #include "base/compiler_specific.h"
      8 #include "base/logging.h"
      9 #include "base/process/process_handle.h"
     10 #include "base/memory/shared_memory.h"
     11 #include "base/single_thread_task_runner.h"
     12 #include "ipc/ipc_channel_proxy.h"
     13 #include "ipc/ipc_message_macros.h"
     14 #include "remoting/base/capabilities.h"
     15 #include "remoting/host/chromoting_messages.h"
     16 #include "remoting/host/client_session.h"
     17 #include "remoting/host/client_session_control.h"
     18 #include "remoting/host/desktop_session_connector.h"
     19 #include "remoting/host/ipc_audio_capturer.h"
     20 #include "remoting/host/ipc_input_injector.h"
     21 #include "remoting/host/ipc_screen_controls.h"
     22 #include "remoting/host/ipc_video_frame_capturer.h"
     23 #include "remoting/proto/audio.pb.h"
     24 #include "remoting/proto/control.pb.h"
     25 #include "remoting/proto/event.pb.h"
     26 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
     27 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
     28 #include "third_party/webrtc/modules/desktop_capture/shared_memory.h"
     29 
     30 #if defined(OS_WIN)
     31 #include "base/win/scoped_handle.h"
     32 #endif  // defined(OS_WIN)
     33 
     34 const bool kReadOnly = true;
     35 const char kSendInitialResolution[] = "sendInitialResolution";
     36 const char kRateLimitResizeRequests[] = "rateLimitResizeRequests";
     37 
     38 namespace remoting {
     39 
     40 class DesktopSessionProxy::IpcSharedBufferCore
     41     : public base::RefCountedThreadSafe<IpcSharedBufferCore> {
     42  public:
     43   IpcSharedBufferCore(int id,
     44                       base::SharedMemoryHandle handle,
     45                       base::ProcessHandle process,
     46                       size_t size)
     47       : id_(id),
     48 #if defined(OS_WIN)
     49         shared_memory_(handle, kReadOnly, process),
     50 #else  // !defined(OS_WIN)
     51         shared_memory_(handle, kReadOnly),
     52 #endif  // !defined(OS_WIN)
     53         size_(size) {
     54     if (!shared_memory_.Map(size)) {
     55       LOG(ERROR) << "Failed to map a shared buffer: id=" << id
     56 #if defined(OS_WIN)
     57                  << ", handle=" << handle
     58 #else
     59                  << ", handle.fd=" << handle.fd
     60 #endif
     61                  << ", size=" << size;
     62     }
     63   }
     64 
     65   int id() { return id_; }
     66   size_t size() { return size_; }
     67   void* memory() { return shared_memory_.memory(); }
     68   webrtc::SharedMemory::Handle handle() {
     69 #if defined(OS_WIN)
     70     return shared_memory_.handle();
     71 #else
     72     return shared_memory_.handle().fd;
     73 #endif
     74   }
     75 
     76  private:
     77   virtual ~IpcSharedBufferCore() {}
     78   friend class base::RefCountedThreadSafe<IpcSharedBufferCore>;
     79 
     80   int id_;
     81   base::SharedMemory shared_memory_;
     82   size_t size_;
     83 
     84   DISALLOW_COPY_AND_ASSIGN(IpcSharedBufferCore);
     85 };
     86 
     87 class DesktopSessionProxy::IpcSharedBuffer : public webrtc::SharedMemory {
     88  public:
     89   IpcSharedBuffer(scoped_refptr<IpcSharedBufferCore> core)
     90       : SharedMemory(core->memory(), core->size(),
     91                      core->handle(), core->id()),
     92         core_(core) {
     93   }
     94 
     95  private:
     96   scoped_refptr<IpcSharedBufferCore> core_;
     97 
     98   DISALLOW_COPY_AND_ASSIGN(IpcSharedBuffer);
     99 };
    100 
    101 DesktopSessionProxy::DesktopSessionProxy(
    102     scoped_refptr<base::SingleThreadTaskRunner> audio_capture_task_runner,
    103     scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
    104     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
    105     scoped_refptr<base::SingleThreadTaskRunner> video_capture_task_runner,
    106     base::WeakPtr<ClientSessionControl> client_session_control,
    107     base::WeakPtr<DesktopSessionConnector> desktop_session_connector,
    108     bool virtual_terminal)
    109     : audio_capture_task_runner_(audio_capture_task_runner),
    110       caller_task_runner_(caller_task_runner),
    111       io_task_runner_(io_task_runner),
    112       video_capture_task_runner_(video_capture_task_runner),
    113       client_session_control_(client_session_control),
    114       desktop_session_connector_(desktop_session_connector),
    115       desktop_process_(base::kNullProcessHandle),
    116       pending_capture_frame_requests_(0),
    117       is_desktop_session_connected_(false),
    118       virtual_terminal_(virtual_terminal) {
    119   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    120 }
    121 
    122 scoped_ptr<AudioCapturer> DesktopSessionProxy::CreateAudioCapturer() {
    123   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    124 
    125   return scoped_ptr<AudioCapturer>(new IpcAudioCapturer(this));
    126 }
    127 
    128 scoped_ptr<InputInjector> DesktopSessionProxy::CreateInputInjector() {
    129   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    130 
    131   return scoped_ptr<InputInjector>(new IpcInputInjector(this));
    132 }
    133 
    134 scoped_ptr<ScreenControls> DesktopSessionProxy::CreateScreenControls() {
    135   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    136 
    137   return scoped_ptr<ScreenControls>(new IpcScreenControls(this));
    138 }
    139 
    140 scoped_ptr<webrtc::ScreenCapturer> DesktopSessionProxy::CreateVideoCapturer() {
    141   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    142 
    143   return scoped_ptr<webrtc::ScreenCapturer>(new IpcVideoFrameCapturer(this));
    144 }
    145 
    146 std::string DesktopSessionProxy::GetCapabilities() const {
    147   std::string result = kRateLimitResizeRequests;
    148   // Ask the client to send its resolution unconditionally.
    149   if (virtual_terminal_)
    150     result = result + " " + kSendInitialResolution;
    151   return result;
    152 }
    153 
    154 void DesktopSessionProxy::SetCapabilities(const std::string& capabilities) {
    155   // Delay creation of the desktop session until the client screen resolution is
    156   // received if the desktop session requires the initial screen resolution
    157   // (when |virtual_terminal_| is true) and the client is expected to
    158   // sent its screen resolution (the 'sendInitialResolution' capability is
    159   // supported).
    160   if (virtual_terminal_ &&
    161       HasCapability(capabilities, kSendInitialResolution)) {
    162     VLOG(1) << "Waiting for the client screen resolution.";
    163     return;
    164   }
    165 
    166   // Connect to the desktop session.
    167   if (!is_desktop_session_connected_) {
    168     is_desktop_session_connected_ = true;
    169     if (desktop_session_connector_.get()) {
    170       desktop_session_connector_->ConnectTerminal(
    171           this, screen_resolution_, virtual_terminal_);
    172     }
    173   }
    174 }
    175 
    176 bool DesktopSessionProxy::OnMessageReceived(const IPC::Message& message) {
    177   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    178 
    179   bool handled = true;
    180   IPC_BEGIN_MESSAGE_MAP(DesktopSessionProxy, message)
    181     IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_AudioPacket,
    182                         OnAudioPacket)
    183     IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_CaptureCompleted,
    184                         OnCaptureCompleted)
    185     IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_CursorShapeChanged,
    186                         OnCursorShapeChanged)
    187     IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_CreateSharedBuffer,
    188                         OnCreateSharedBuffer)
    189     IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_ReleaseSharedBuffer,
    190                         OnReleaseSharedBuffer)
    191     IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_InjectClipboardEvent,
    192                         OnInjectClipboardEvent)
    193     IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_DisconnectSession,
    194                         DisconnectSession);
    195   IPC_END_MESSAGE_MAP()
    196 
    197   CHECK(handled) << "Received unexpected IPC type: " << message.type();
    198   return handled;
    199 }
    200 
    201 void DesktopSessionProxy::OnChannelConnected(int32 peer_pid) {
    202   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    203 
    204   VLOG(1) << "IPC: network <- desktop (" << peer_pid << ")";
    205 }
    206 
    207 void DesktopSessionProxy::OnChannelError() {
    208   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    209 
    210   DetachFromDesktop();
    211 }
    212 
    213 bool DesktopSessionProxy::AttachToDesktop(
    214     base::ProcessHandle desktop_process,
    215     IPC::PlatformFileForTransit desktop_pipe) {
    216   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    217   DCHECK(!desktop_channel_);
    218   DCHECK_EQ(desktop_process_, base::kNullProcessHandle);
    219 
    220   // Ignore the attach notification if the client session has been disconnected
    221   // already.
    222   if (!client_session_control_.get()) {
    223     base::CloseProcessHandle(desktop_process);
    224     return false;
    225   }
    226 
    227   desktop_process_ = desktop_process;
    228 
    229 #if defined(OS_WIN)
    230   // On Windows: |desktop_process| is a valid handle, but |desktop_pipe| needs
    231   // to be duplicated from the desktop process.
    232   HANDLE temp_handle;
    233   if (!DuplicateHandle(desktop_process_, desktop_pipe, GetCurrentProcess(),
    234                        &temp_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
    235     PLOG(ERROR) << "Failed to duplicate the desktop-to-network pipe handle";
    236 
    237     desktop_process_ = base::kNullProcessHandle;
    238     base::CloseProcessHandle(desktop_process);
    239     return false;
    240   }
    241   base::win::ScopedHandle pipe(temp_handle);
    242 
    243   IPC::ChannelHandle desktop_channel_handle(pipe);
    244 
    245 #elif defined(OS_POSIX)
    246   // On posix: |desktop_pipe| is a valid file descriptor.
    247   DCHECK(desktop_pipe.auto_close);
    248 
    249   IPC::ChannelHandle desktop_channel_handle(std::string(), desktop_pipe);
    250 
    251 #else
    252 #error Unsupported platform.
    253 #endif
    254 
    255   // Connect to the desktop process.
    256   desktop_channel_ = IPC::ChannelProxy::Create(desktop_channel_handle,
    257                                                IPC::Channel::MODE_CLIENT,
    258                                                this,
    259                                                io_task_runner_.get());
    260 
    261   // Pass ID of the client (which is authenticated at this point) to the desktop
    262   // session agent and start the agent.
    263   SendToDesktop(new ChromotingNetworkDesktopMsg_StartSessionAgent(
    264       client_session_control_->client_jid(),
    265       screen_resolution_,
    266       virtual_terminal_));
    267 
    268   return true;
    269 }
    270 
    271 void DesktopSessionProxy::DetachFromDesktop() {
    272   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    273 
    274   desktop_channel_.reset();
    275 
    276   if (desktop_process_ != base::kNullProcessHandle) {
    277     base::CloseProcessHandle(desktop_process_);
    278     desktop_process_ = base::kNullProcessHandle;
    279   }
    280 
    281   shared_buffers_.clear();
    282 
    283   // Generate fake responses to keep the video capturer in sync.
    284   while (pending_capture_frame_requests_) {
    285     --pending_capture_frame_requests_;
    286     PostCaptureCompleted(scoped_ptr<webrtc::DesktopFrame>());
    287   }
    288 }
    289 
    290 void DesktopSessionProxy::SetAudioCapturer(
    291     const base::WeakPtr<IpcAudioCapturer>& audio_capturer) {
    292   DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
    293 
    294   audio_capturer_ = audio_capturer;
    295 }
    296 
    297 void DesktopSessionProxy::CaptureFrame() {
    298   if (!caller_task_runner_->BelongsToCurrentThread()) {
    299     caller_task_runner_->PostTask(
    300         FROM_HERE, base::Bind(&DesktopSessionProxy::CaptureFrame, this));
    301     return;
    302   }
    303 
    304   if (desktop_channel_) {
    305     ++pending_capture_frame_requests_;
    306     SendToDesktop(new ChromotingNetworkDesktopMsg_CaptureFrame());
    307   } else {
    308     PostCaptureCompleted(scoped_ptr<webrtc::DesktopFrame>());
    309   }
    310 }
    311 
    312 void DesktopSessionProxy::SetVideoCapturer(
    313     const base::WeakPtr<IpcVideoFrameCapturer> video_capturer) {
    314   DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
    315 
    316   video_capturer_ = video_capturer;
    317 }
    318 
    319 void DesktopSessionProxy::DisconnectSession() {
    320   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    321 
    322   // Disconnect the client session if it hasn't been disconnected yet.
    323   if (client_session_control_.get())
    324     client_session_control_->DisconnectSession();
    325 }
    326 
    327 void DesktopSessionProxy::InjectClipboardEvent(
    328     const protocol::ClipboardEvent& event) {
    329   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    330 
    331   std::string serialized_event;
    332   if (!event.SerializeToString(&serialized_event)) {
    333     LOG(ERROR) << "Failed to serialize protocol::ClipboardEvent.";
    334     return;
    335   }
    336 
    337   SendToDesktop(
    338       new ChromotingNetworkDesktopMsg_InjectClipboardEvent(serialized_event));
    339 }
    340 
    341 void DesktopSessionProxy::InjectKeyEvent(const protocol::KeyEvent& event) {
    342   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    343 
    344   std::string serialized_event;
    345   if (!event.SerializeToString(&serialized_event)) {
    346     LOG(ERROR) << "Failed to serialize protocol::KeyEvent.";
    347     return;
    348   }
    349 
    350   SendToDesktop(
    351       new ChromotingNetworkDesktopMsg_InjectKeyEvent(serialized_event));
    352 }
    353 
    354 void DesktopSessionProxy::InjectTextEvent(const protocol::TextEvent& event) {
    355   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    356 
    357   std::string serialized_event;
    358   if (!event.SerializeToString(&serialized_event)) {
    359     LOG(ERROR) << "Failed to serialize protocol::TextEvent.";
    360     return;
    361   }
    362 
    363   SendToDesktop(
    364       new ChromotingNetworkDesktopMsg_InjectTextEvent(serialized_event));
    365 }
    366 
    367 void DesktopSessionProxy::InjectMouseEvent(const protocol::MouseEvent& event) {
    368   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    369 
    370   std::string serialized_event;
    371   if (!event.SerializeToString(&serialized_event)) {
    372     LOG(ERROR) << "Failed to serialize protocol::MouseEvent.";
    373     return;
    374   }
    375 
    376   SendToDesktop(
    377       new ChromotingNetworkDesktopMsg_InjectMouseEvent(serialized_event));
    378 }
    379 
    380 void DesktopSessionProxy::StartInputInjector(
    381     scoped_ptr<protocol::ClipboardStub> client_clipboard) {
    382   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    383 
    384   client_clipboard_ = client_clipboard.Pass();
    385 }
    386 
    387 void DesktopSessionProxy::SetScreenResolution(
    388     const ScreenResolution& resolution) {
    389   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    390 
    391   if (resolution.IsEmpty())
    392     return;
    393 
    394   screen_resolution_ = resolution;
    395 
    396   // Connect to the desktop session if it is not done yet.
    397   if (!is_desktop_session_connected_) {
    398     is_desktop_session_connected_ = true;
    399     if (desktop_session_connector_.get()) {
    400       desktop_session_connector_->ConnectTerminal(
    401           this, screen_resolution_, virtual_terminal_);
    402     }
    403     return;
    404   }
    405 
    406   // Pass the client's resolution to both daemon and desktop session agent.
    407   // Depending on the session kind the screen resolution can be set by either
    408   // the daemon (for example RDP sessions on Windows) or by the desktop session
    409   // agent (when sharing the physical console).
    410   if (desktop_session_connector_.get())
    411     desktop_session_connector_->SetScreenResolution(this, screen_resolution_);
    412   SendToDesktop(
    413       new ChromotingNetworkDesktopMsg_SetScreenResolution(screen_resolution_));
    414 }
    415 
    416 DesktopSessionProxy::~DesktopSessionProxy() {
    417   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    418 
    419   if (desktop_session_connector_.get() && is_desktop_session_connected_)
    420     desktop_session_connector_->DisconnectTerminal(this);
    421 
    422   if (desktop_process_ != base::kNullProcessHandle) {
    423     base::CloseProcessHandle(desktop_process_);
    424     desktop_process_ = base::kNullProcessHandle;
    425   }
    426 }
    427 
    428 scoped_refptr<DesktopSessionProxy::IpcSharedBufferCore>
    429 DesktopSessionProxy::GetSharedBufferCore(int id) {
    430   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    431 
    432   SharedBuffers::const_iterator i = shared_buffers_.find(id);
    433   if (i != shared_buffers_.end()) {
    434     return i->second;
    435   } else {
    436     LOG(ERROR) << "Failed to find the shared buffer " << id;
    437     return NULL;
    438   }
    439 }
    440 
    441 void DesktopSessionProxy::OnAudioPacket(const std::string& serialized_packet) {
    442   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    443 
    444   // Parse a serialized audio packet. No further validation is done since
    445   // the message was sent by more privileged process.
    446   scoped_ptr<AudioPacket> packet(new AudioPacket());
    447   if (!packet->ParseFromString(serialized_packet)) {
    448     LOG(ERROR) << "Failed to parse AudioPacket.";
    449     return;
    450   }
    451 
    452   // Pass a captured audio packet to |audio_capturer_|.
    453   audio_capture_task_runner_->PostTask(
    454       FROM_HERE, base::Bind(&IpcAudioCapturer::OnAudioPacket, audio_capturer_,
    455                             base::Passed(&packet)));
    456 }
    457 
    458 void DesktopSessionProxy::OnCreateSharedBuffer(
    459     int id,
    460     IPC::PlatformFileForTransit handle,
    461     uint32 size) {
    462   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    463 
    464   scoped_refptr<IpcSharedBufferCore> shared_buffer =
    465       new IpcSharedBufferCore(id, handle, desktop_process_, size);
    466 
    467   if (shared_buffer->memory() != NULL &&
    468       !shared_buffers_.insert(std::make_pair(id, shared_buffer)).second) {
    469     LOG(ERROR) << "Duplicate shared buffer id " << id << " encountered";
    470   }
    471 }
    472 
    473 void DesktopSessionProxy::OnReleaseSharedBuffer(int id) {
    474   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    475 
    476   // Drop the cached reference to the buffer.
    477   shared_buffers_.erase(id);
    478 }
    479 
    480 void DesktopSessionProxy::OnCaptureCompleted(
    481     const SerializedDesktopFrame& serialized_frame) {
    482   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    483 
    484   // Assume that |serialized_frame| is well-formed because it was received from
    485   // a more privileged process.
    486   scoped_refptr<IpcSharedBufferCore> shared_buffer_core =
    487       GetSharedBufferCore(serialized_frame.shared_buffer_id);
    488   CHECK(shared_buffer_core.get());
    489 
    490   scoped_ptr<webrtc::DesktopFrame> frame(
    491       new webrtc::SharedMemoryDesktopFrame(
    492           serialized_frame.dimensions, serialized_frame.bytes_per_row,
    493           new IpcSharedBuffer(shared_buffer_core)));
    494   frame->set_capture_time_ms(serialized_frame.capture_time_ms);
    495   frame->set_dpi(serialized_frame.dpi);
    496 
    497   for (size_t i = 0; i < serialized_frame.dirty_region.size(); ++i) {
    498     frame->mutable_updated_region()->AddRect(serialized_frame.dirty_region[i]);
    499   }
    500 
    501   --pending_capture_frame_requests_;
    502   PostCaptureCompleted(frame.Pass());
    503 }
    504 
    505 void DesktopSessionProxy::OnCursorShapeChanged(
    506     const webrtc::MouseCursorShape& cursor_shape) {
    507   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    508   PostCursorShape(scoped_ptr<webrtc::MouseCursorShape>(
    509       new webrtc::MouseCursorShape(cursor_shape)));
    510 }
    511 
    512 void DesktopSessionProxy::OnInjectClipboardEvent(
    513     const std::string& serialized_event) {
    514   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    515 
    516   if (client_clipboard_) {
    517     protocol::ClipboardEvent event;
    518     if (!event.ParseFromString(serialized_event)) {
    519       LOG(ERROR) << "Failed to parse protocol::ClipboardEvent.";
    520       return;
    521     }
    522 
    523     client_clipboard_->InjectClipboardEvent(event);
    524   }
    525 }
    526 
    527 void DesktopSessionProxy::PostCaptureCompleted(
    528     scoped_ptr<webrtc::DesktopFrame> frame) {
    529   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    530 
    531   video_capture_task_runner_->PostTask(
    532       FROM_HERE,
    533       base::Bind(&IpcVideoFrameCapturer::OnCaptureCompleted, video_capturer_,
    534                  base::Passed(&frame)));
    535 }
    536 
    537 void DesktopSessionProxy::PostCursorShape(
    538     scoped_ptr<webrtc::MouseCursorShape> cursor_shape) {
    539   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    540 
    541   video_capture_task_runner_->PostTask(
    542       FROM_HERE,
    543       base::Bind(&IpcVideoFrameCapturer::OnCursorShapeChanged, video_capturer_,
    544                  base::Passed(&cursor_shape)));
    545 }
    546 
    547 void DesktopSessionProxy::SendToDesktop(IPC::Message* message) {
    548   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    549 
    550   if (desktop_channel_) {
    551     desktop_channel_->Send(message);
    552   } else {
    553     delete message;
    554   }
    555 }
    556 
    557 // static
    558 void DesktopSessionProxyTraits::Destruct(
    559     const DesktopSessionProxy* desktop_session_proxy) {
    560   desktop_session_proxy->caller_task_runner_->DeleteSoon(FROM_HERE,
    561                                                          desktop_session_proxy);
    562 }
    563 
    564 }  // namespace remoting
    565