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