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