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