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   HANDLE temp_handle;
    234   if (!DuplicateHandle(desktop_process_, desktop_pipe, GetCurrentProcess(),
    235                        &temp_handle, 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   base::win::ScopedHandle pipe(temp_handle);
    244 
    245   IPC::ChannelHandle desktop_channel_handle(pipe);
    246 
    247 #elif defined(OS_POSIX)
    248   // On posix: |desktop_pipe| is a valid file descriptor.
    249   DCHECK(desktop_pipe.auto_close);
    250 
    251   IPC::ChannelHandle desktop_channel_handle(std::string(), desktop_pipe);
    252 
    253 #else
    254 #error Unsupported platform.
    255 #endif
    256 
    257   // Connect to the desktop process.
    258   desktop_channel_.reset(new IPC::ChannelProxy(desktop_channel_handle,
    259                                                IPC::Channel::MODE_CLIENT,
    260                                                this,
    261                                                io_task_runner_.get()));
    262 
    263   // Pass ID of the client (which is authenticated at this point) to the desktop
    264   // session agent and start the agent.
    265   SendToDesktop(new ChromotingNetworkDesktopMsg_StartSessionAgent(
    266       client_session_control_->client_jid(),
    267       screen_resolution_,
    268       virtual_terminal_));
    269 
    270   return true;
    271 }
    272 
    273 void DesktopSessionProxy::DetachFromDesktop() {
    274   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    275 
    276   desktop_channel_.reset();
    277 
    278   if (desktop_process_ != base::kNullProcessHandle) {
    279     base::CloseProcessHandle(desktop_process_);
    280     desktop_process_ = base::kNullProcessHandle;
    281   }
    282 
    283   shared_buffers_.clear();
    284 
    285   // Generate fake responses to keep the video capturer in sync.
    286   while (pending_capture_frame_requests_) {
    287     --pending_capture_frame_requests_;
    288     PostCaptureCompleted(scoped_ptr<webrtc::DesktopFrame>());
    289   }
    290 }
    291 
    292 void DesktopSessionProxy::SetAudioCapturer(
    293     const base::WeakPtr<IpcAudioCapturer>& audio_capturer) {
    294   DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
    295 
    296   audio_capturer_ = audio_capturer;
    297 }
    298 
    299 void DesktopSessionProxy::CaptureFrame() {
    300   if (!caller_task_runner_->BelongsToCurrentThread()) {
    301     caller_task_runner_->PostTask(
    302         FROM_HERE, base::Bind(&DesktopSessionProxy::CaptureFrame, this));
    303     return;
    304   }
    305 
    306   if (desktop_channel_) {
    307     ++pending_capture_frame_requests_;
    308     SendToDesktop(new ChromotingNetworkDesktopMsg_CaptureFrame());
    309   } else {
    310     PostCaptureCompleted(scoped_ptr<webrtc::DesktopFrame>());
    311   }
    312 }
    313 
    314 void DesktopSessionProxy::SetVideoCapturer(
    315     const base::WeakPtr<IpcVideoFrameCapturer> video_capturer) {
    316   DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
    317 
    318   video_capturer_ = video_capturer;
    319 }
    320 
    321 void DesktopSessionProxy::DisconnectSession() {
    322   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    323 
    324   // Disconnect the client session if it hasn't been disconnected yet.
    325   if (client_session_control_.get())
    326     client_session_control_->DisconnectSession();
    327 }
    328 
    329 void DesktopSessionProxy::InjectClipboardEvent(
    330     const protocol::ClipboardEvent& event) {
    331   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    332 
    333   std::string serialized_event;
    334   if (!event.SerializeToString(&serialized_event)) {
    335     LOG(ERROR) << "Failed to serialize protocol::ClipboardEvent.";
    336     return;
    337   }
    338 
    339   SendToDesktop(
    340       new ChromotingNetworkDesktopMsg_InjectClipboardEvent(serialized_event));
    341 }
    342 
    343 void DesktopSessionProxy::InjectKeyEvent(const protocol::KeyEvent& event) {
    344   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    345 
    346   std::string serialized_event;
    347   if (!event.SerializeToString(&serialized_event)) {
    348     LOG(ERROR) << "Failed to serialize protocol::KeyEvent.";
    349     return;
    350   }
    351 
    352   SendToDesktop(
    353       new ChromotingNetworkDesktopMsg_InjectKeyEvent(serialized_event));
    354 }
    355 
    356 void DesktopSessionProxy::InjectMouseEvent(const protocol::MouseEvent& event) {
    357   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    358 
    359   std::string serialized_event;
    360   if (!event.SerializeToString(&serialized_event)) {
    361     LOG(ERROR) << "Failed to serialize protocol::MouseEvent.";
    362     return;
    363   }
    364 
    365   SendToDesktop(
    366       new ChromotingNetworkDesktopMsg_InjectMouseEvent(serialized_event));
    367 }
    368 
    369 void DesktopSessionProxy::StartInputInjector(
    370     scoped_ptr<protocol::ClipboardStub> client_clipboard) {
    371   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    372 
    373   client_clipboard_ = client_clipboard.Pass();
    374 }
    375 
    376 void DesktopSessionProxy::SetScreenResolution(
    377     const ScreenResolution& resolution) {
    378   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    379 
    380   if (resolution.IsEmpty())
    381     return;
    382 
    383   screen_resolution_ = resolution;
    384 
    385   // Connect to the desktop session if it is not done yet.
    386   if (!is_desktop_session_connected_) {
    387     is_desktop_session_connected_ = true;
    388     if (desktop_session_connector_.get()) {
    389       desktop_session_connector_->ConnectTerminal(
    390           this, screen_resolution_, virtual_terminal_);
    391     }
    392     return;
    393   }
    394 
    395   // Pass the client's resolution to both daemon and desktop session agent.
    396   // Depending on the session kind the screen resolution can be set by either
    397   // the daemon (for example RDP sessions on Windows) or by the desktop session
    398   // agent (when sharing the physical console).
    399   if (desktop_session_connector_.get())
    400     desktop_session_connector_->SetScreenResolution(this, screen_resolution_);
    401   SendToDesktop(
    402       new ChromotingNetworkDesktopMsg_SetScreenResolution(screen_resolution_));
    403 }
    404 
    405 DesktopSessionProxy::~DesktopSessionProxy() {
    406   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    407 
    408   if (desktop_session_connector_.get() && is_desktop_session_connected_)
    409     desktop_session_connector_->DisconnectTerminal(this);
    410 
    411   if (desktop_process_ != base::kNullProcessHandle) {
    412     base::CloseProcessHandle(desktop_process_);
    413     desktop_process_ = base::kNullProcessHandle;
    414   }
    415 }
    416 
    417 scoped_refptr<DesktopSessionProxy::IpcSharedBufferCore>
    418 DesktopSessionProxy::GetSharedBufferCore(int id) {
    419   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    420 
    421   SharedBuffers::const_iterator i = shared_buffers_.find(id);
    422   if (i != shared_buffers_.end()) {
    423     return i->second;
    424   } else {
    425     LOG(ERROR) << "Failed to find the shared buffer " << id;
    426     return NULL;
    427   }
    428 }
    429 
    430 void DesktopSessionProxy::OnAudioPacket(const std::string& serialized_packet) {
    431   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    432 
    433   // Parse a serialized audio packet. No further validation is done since
    434   // the message was sent by more privileged process.
    435   scoped_ptr<AudioPacket> packet(new AudioPacket());
    436   if (!packet->ParseFromString(serialized_packet)) {
    437     LOG(ERROR) << "Failed to parse AudioPacket.";
    438     return;
    439   }
    440 
    441   // Pass a captured audio packet to |audio_capturer_|.
    442   audio_capture_task_runner_->PostTask(
    443       FROM_HERE, base::Bind(&IpcAudioCapturer::OnAudioPacket, audio_capturer_,
    444                             base::Passed(&packet)));
    445 }
    446 
    447 void DesktopSessionProxy::OnCreateSharedBuffer(
    448     int id,
    449     IPC::PlatformFileForTransit handle,
    450     uint32 size) {
    451   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    452 
    453   scoped_refptr<IpcSharedBufferCore> shared_buffer =
    454       new IpcSharedBufferCore(id, handle, desktop_process_, size);
    455 
    456   if (shared_buffer->memory() != NULL &&
    457       !shared_buffers_.insert(std::make_pair(id, shared_buffer)).second) {
    458     LOG(ERROR) << "Duplicate shared buffer id " << id << " encountered";
    459   }
    460 }
    461 
    462 void DesktopSessionProxy::OnReleaseSharedBuffer(int id) {
    463   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    464 
    465   // Drop the cached reference to the buffer.
    466   shared_buffers_.erase(id);
    467 }
    468 
    469 void DesktopSessionProxy::OnCaptureCompleted(
    470     const SerializedDesktopFrame& serialized_frame) {
    471   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    472 
    473   // Assume that |serialized_frame| is well-formed because it was received from
    474   // a more privileged process.
    475   scoped_refptr<IpcSharedBufferCore> shared_buffer_core =
    476       GetSharedBufferCore(serialized_frame.shared_buffer_id);
    477   CHECK(shared_buffer_core.get());
    478 
    479   scoped_ptr<webrtc::DesktopFrame> frame(
    480       new webrtc::SharedMemoryDesktopFrame(
    481           serialized_frame.dimensions, serialized_frame.bytes_per_row,
    482           new IpcSharedBuffer(shared_buffer_core)));
    483   frame->set_capture_time_ms(serialized_frame.capture_time_ms);
    484   frame->set_dpi(serialized_frame.dpi);
    485 
    486   for (size_t i = 0; i < serialized_frame.dirty_region.size(); ++i) {
    487     frame->mutable_updated_region()->AddRect(serialized_frame.dirty_region[i]);
    488   }
    489 
    490   --pending_capture_frame_requests_;
    491   PostCaptureCompleted(frame.Pass());
    492 }
    493 
    494 void DesktopSessionProxy::OnCursorShapeChanged(
    495     const webrtc::MouseCursorShape& cursor_shape) {
    496   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    497   PostCursorShape(scoped_ptr<webrtc::MouseCursorShape>(
    498       new webrtc::MouseCursorShape(cursor_shape)));
    499 }
    500 
    501 void DesktopSessionProxy::OnInjectClipboardEvent(
    502     const std::string& serialized_event) {
    503   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    504 
    505   if (client_clipboard_) {
    506     protocol::ClipboardEvent event;
    507     if (!event.ParseFromString(serialized_event)) {
    508       LOG(ERROR) << "Failed to parse protocol::ClipboardEvent.";
    509       return;
    510     }
    511 
    512     client_clipboard_->InjectClipboardEvent(event);
    513   }
    514 }
    515 
    516 void DesktopSessionProxy::PostCaptureCompleted(
    517     scoped_ptr<webrtc::DesktopFrame> frame) {
    518   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    519 
    520   video_capture_task_runner_->PostTask(
    521       FROM_HERE,
    522       base::Bind(&IpcVideoFrameCapturer::OnCaptureCompleted, video_capturer_,
    523                  base::Passed(&frame)));
    524 }
    525 
    526 void DesktopSessionProxy::PostCursorShape(
    527     scoped_ptr<webrtc::MouseCursorShape> cursor_shape) {
    528   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    529 
    530   video_capture_task_runner_->PostTask(
    531       FROM_HERE,
    532       base::Bind(&IpcVideoFrameCapturer::OnCursorShapeChanged, video_capturer_,
    533                  base::Passed(&cursor_shape)));
    534 }
    535 
    536 void DesktopSessionProxy::SendToDesktop(IPC::Message* message) {
    537   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    538 
    539   if (desktop_channel_) {
    540     desktop_channel_->Send(message);
    541   } else {
    542     delete message;
    543   }
    544 }
    545 
    546 // static
    547 void DesktopSessionProxyTraits::Destruct(
    548     const DesktopSessionProxy* desktop_session_proxy) {
    549   desktop_session_proxy->caller_task_runner_->DeleteSoon(FROM_HERE,
    550                                                          desktop_session_proxy);
    551 }
    552 
    553 }  // namespace remoting
    554