Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / remoting / host / desktop_session_agent.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_agent.h"
6
7 #include "base/file_util.h"
8 #include "base/logging.h"
9 #include "base/memory/shared_memory.h"
10 #include "ipc/ipc_channel_proxy.h"
11 #include "ipc/ipc_message.h"
12 #include "ipc/ipc_message_macros.h"
13 #include "remoting/base/auto_thread_task_runner.h"
14 #include "remoting/base/constants.h"
15 #include "remoting/host/audio_capturer.h"
16 #include "remoting/host/chromoting_messages.h"
17 #include "remoting/host/desktop_environment.h"
18 #include "remoting/host/input_injector.h"
19 #include "remoting/host/ipc_util.h"
20 #include "remoting/host/remote_input_filter.h"
21 #include "remoting/host/screen_controls.h"
22 #include "remoting/host/screen_resolution.h"
23 #include "remoting/proto/audio.pb.h"
24 #include "remoting/proto/control.pb.h"
25 #include "remoting/proto/event.pb.h"
26 #include "remoting/protocol/clipboard_stub.h"
27 #include "remoting/protocol/input_event_tracker.h"
28 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
29 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
30 #include "third_party/webrtc/modules/desktop_capture/mouse_cursor.h"
31 #include "third_party/webrtc/modules/desktop_capture/shared_memory.h"
32
33 namespace remoting {
34
35 namespace {
36
37 // Routes local clipboard events though the IPC channel to the network process.
38 class DesktopSesssionClipboardStub : public protocol::ClipboardStub {
39  public:
40   explicit DesktopSesssionClipboardStub(
41       scoped_refptr<DesktopSessionAgent> desktop_session_agent);
42   virtual ~DesktopSesssionClipboardStub();
43
44   // protocol::ClipboardStub implementation.
45   virtual void InjectClipboardEvent(
46       const protocol::ClipboardEvent& event) OVERRIDE;
47
48  private:
49   scoped_refptr<DesktopSessionAgent> desktop_session_agent_;
50
51   DISALLOW_COPY_AND_ASSIGN(DesktopSesssionClipboardStub);
52 };
53
54 DesktopSesssionClipboardStub::DesktopSesssionClipboardStub(
55     scoped_refptr<DesktopSessionAgent> desktop_session_agent)
56     : desktop_session_agent_(desktop_session_agent) {
57 }
58
59 DesktopSesssionClipboardStub::~DesktopSesssionClipboardStub() {
60 }
61
62 void DesktopSesssionClipboardStub::InjectClipboardEvent(
63     const protocol::ClipboardEvent& event) {
64   desktop_session_agent_->InjectClipboardEvent(event);
65 }
66
67 }  // namespace
68
69 // webrtc::SharedMemory implementation that notifies creating
70 // DesktopSessionAgent when it's deleted.
71 class DesktopSessionAgent::SharedBuffer : public webrtc::SharedMemory {
72  public:
73   static scoped_ptr<SharedBuffer> Create(DesktopSessionAgent* agent,
74                                          size_t size,
75                                          int id) {
76     scoped_ptr<base::SharedMemory> memory(new base::SharedMemory());
77     if (!memory->CreateAndMapAnonymous(size))
78       return scoped_ptr<SharedBuffer>();
79     return scoped_ptr<SharedBuffer>(
80         new SharedBuffer(agent, memory.Pass(), size, id));
81   }
82
83   virtual ~SharedBuffer() {
84     agent_->OnSharedBufferDeleted(id());
85   }
86
87  private:
88   SharedBuffer(DesktopSessionAgent* agent,
89                scoped_ptr<base::SharedMemory> memory,
90                size_t size,
91                int id)
92       : SharedMemory(memory->memory(), size,
93 #if defined(OS_WIN)
94                      memory->handle(),
95 #else
96                      memory->handle().fd,
97 #endif
98                      id),
99         agent_(agent),
100         shared_memory_(memory.Pass()) {
101   }
102
103   DesktopSessionAgent* agent_;
104   scoped_ptr<base::SharedMemory> shared_memory_;
105
106   DISALLOW_COPY_AND_ASSIGN(SharedBuffer);
107 };
108
109 DesktopSessionAgent::Delegate::~Delegate() {
110 }
111
112 DesktopSessionAgent::DesktopSessionAgent(
113     scoped_refptr<AutoThreadTaskRunner> audio_capture_task_runner,
114     scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
115     scoped_refptr<AutoThreadTaskRunner> input_task_runner,
116     scoped_refptr<AutoThreadTaskRunner> io_task_runner,
117     scoped_refptr<AutoThreadTaskRunner> video_capture_task_runner)
118     : audio_capture_task_runner_(audio_capture_task_runner),
119       caller_task_runner_(caller_task_runner),
120       input_task_runner_(input_task_runner),
121       io_task_runner_(io_task_runner),
122       video_capture_task_runner_(video_capture_task_runner),
123       control_factory_(this),
124       next_shared_buffer_id_(1),
125       shared_buffers_(0),
126       started_(false) {
127   DCHECK(caller_task_runner_->BelongsToCurrentThread());
128 }
129
130 bool DesktopSessionAgent::OnMessageReceived(const IPC::Message& message) {
131   DCHECK(caller_task_runner_->BelongsToCurrentThread());
132
133   bool handled = true;
134   if (started_) {
135     IPC_BEGIN_MESSAGE_MAP(DesktopSessionAgent, message)
136       IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_CaptureFrame,
137                           OnCaptureFrame)
138       IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectClipboardEvent,
139                           OnInjectClipboardEvent)
140       IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectKeyEvent,
141                           OnInjectKeyEvent)
142       IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectTextEvent,
143                           OnInjectTextEvent)
144       IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectMouseEvent,
145                           OnInjectMouseEvent)
146       IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_SetScreenResolution,
147                           SetScreenResolution)
148       IPC_MESSAGE_UNHANDLED(handled = false)
149     IPC_END_MESSAGE_MAP()
150   } else {
151     IPC_BEGIN_MESSAGE_MAP(DesktopSessionAgent, message)
152       IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_StartSessionAgent,
153                           OnStartSessionAgent)
154       IPC_MESSAGE_UNHANDLED(handled = false)
155     IPC_END_MESSAGE_MAP()
156   }
157
158   CHECK(handled) << "Received unexpected IPC type: " << message.type();
159   return handled;
160 }
161
162 void DesktopSessionAgent::OnChannelConnected(int32 peer_pid) {
163   DCHECK(caller_task_runner_->BelongsToCurrentThread());
164
165   VLOG(1) << "IPC: desktop <- network (" << peer_pid << ")";
166
167   desktop_pipe_.Close();
168 }
169
170 void DesktopSessionAgent::OnChannelError() {
171   DCHECK(caller_task_runner_->BelongsToCurrentThread());
172
173   // Make sure the channel is closed.
174   network_channel_.reset();
175   desktop_pipe_.Close();
176
177   // Notify the caller that the channel has been disconnected.
178   if (delegate_.get())
179     delegate_->OnNetworkProcessDisconnected();
180 }
181
182 webrtc::SharedMemory* DesktopSessionAgent::CreateSharedMemory(size_t size) {
183   DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
184
185   scoped_ptr<SharedBuffer> buffer =
186       SharedBuffer::Create(this, size, next_shared_buffer_id_);
187   if (buffer) {
188     shared_buffers_++;
189
190     // |next_shared_buffer_id_| starts from 1 and incrementing it by 2 makes
191     // sure it is always odd and therefore zero is never used as a valid buffer
192     // ID.
193     //
194     // It is very unlikely (though theoretically possible) to allocate the same
195     // ID for two different buffers due to integer overflow. It should take
196     // about a year of allocating 100 new buffers every second. Practically
197     // speaking it never happens.
198     next_shared_buffer_id_ += 2;
199
200     IPC::PlatformFileForTransit handle;
201 #if defined(OS_WIN)
202     handle = buffer->handle();
203 #else
204     handle = base::FileDescriptor(buffer->handle(), false);
205 #endif
206     SendToNetwork(new ChromotingDesktopNetworkMsg_CreateSharedBuffer(
207         buffer->id(), handle, buffer->size()));
208   }
209
210   return buffer.release();
211 }
212
213 DesktopSessionAgent::~DesktopSessionAgent() {
214   DCHECK(!audio_capturer_);
215   DCHECK(!desktop_environment_);
216   DCHECK(!network_channel_);
217   DCHECK(!screen_controls_);
218   DCHECK(!video_capturer_);
219 }
220
221 const std::string& DesktopSessionAgent::client_jid() const {
222   return client_jid_;
223 }
224
225 void DesktopSessionAgent::DisconnectSession() {
226   SendToNetwork(new ChromotingDesktopNetworkMsg_DisconnectSession());
227 }
228
229 void DesktopSessionAgent::OnLocalMouseMoved(
230     const webrtc::DesktopVector& new_pos) {
231   DCHECK(caller_task_runner_->BelongsToCurrentThread());
232
233   remote_input_filter_->LocalMouseMoved(new_pos);
234 }
235
236 void DesktopSessionAgent::SetDisableInputs(bool disable_inputs) {
237   DCHECK(caller_task_runner_->BelongsToCurrentThread());
238
239   // Do not expect this method to be called because it is only used by It2Me.
240   NOTREACHED();
241 }
242
243 void DesktopSessionAgent::ResetVideoPipeline() {
244   DCHECK(caller_task_runner_->BelongsToCurrentThread());
245
246   // This method is only used by HostExtensionSessions in the network process.
247   NOTREACHED();
248 }
249
250 void DesktopSessionAgent::OnStartSessionAgent(
251     const std::string& authenticated_jid,
252     const ScreenResolution& resolution,
253     bool virtual_terminal) {
254   DCHECK(caller_task_runner_->BelongsToCurrentThread());
255   DCHECK(!started_);
256   DCHECK(!audio_capturer_);
257   DCHECK(!desktop_environment_);
258   DCHECK(!input_injector_);
259   DCHECK(!screen_controls_);
260   DCHECK(!video_capturer_);
261
262   started_ = true;
263   client_jid_ = authenticated_jid;
264
265   // Enable the curtain mode.
266   delegate_->desktop_environment_factory().SetEnableCurtaining(
267       virtual_terminal);
268
269   // Create a desktop environment for the new session.
270   desktop_environment_ = delegate_->desktop_environment_factory().Create(
271       control_factory_.GetWeakPtr());
272
273   // Create the session controller and set the initial screen resolution.
274   screen_controls_ = desktop_environment_->CreateScreenControls();
275   SetScreenResolution(resolution);
276
277   // Create the input injector.
278   input_injector_ = desktop_environment_->CreateInputInjector();
279
280   // Hook up the input filter.
281   input_tracker_.reset(new protocol::InputEventTracker(input_injector_.get()));
282   remote_input_filter_.reset(new RemoteInputFilter(input_tracker_.get()));
283
284 #if defined(OS_WIN)
285   // LocalInputMonitorWin filters out an echo of the injected input before it
286   // reaches |remote_input_filter_|.
287   remote_input_filter_->SetExpectLocalEcho(false);
288 #endif  // defined(OS_WIN)
289
290   // Start the input injector.
291   scoped_ptr<protocol::ClipboardStub> clipboard_stub(
292       new DesktopSesssionClipboardStub(this));
293   input_injector_->Start(clipboard_stub.Pass());
294
295   // Start the audio capturer.
296   if (delegate_->desktop_environment_factory().SupportsAudioCapture()) {
297     audio_capturer_ = desktop_environment_->CreateAudioCapturer();
298     audio_capture_task_runner_->PostTask(
299         FROM_HERE, base::Bind(&DesktopSessionAgent::StartAudioCapturer, this));
300   }
301
302   // Start the video capturer and mouse cursor monitor.
303   video_capturer_ = desktop_environment_->CreateVideoCapturer();
304   mouse_cursor_monitor_ = desktop_environment_->CreateMouseCursorMonitor();
305   video_capture_task_runner_->PostTask(
306       FROM_HERE, base::Bind(
307           &DesktopSessionAgent::StartVideoCapturerAndMouseMonitor, this));
308 }
309
310 void DesktopSessionAgent::OnCaptureCompleted(webrtc::DesktopFrame* frame) {
311   DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
312
313   last_frame_.reset(frame);
314
315   current_size_ = frame->size();
316
317   // Serialize webrtc::DesktopFrame.
318   SerializedDesktopFrame serialized_frame;
319   serialized_frame.shared_buffer_id = frame->shared_memory()->id();
320   serialized_frame.bytes_per_row = frame->stride();
321   serialized_frame.dimensions = frame->size();
322   serialized_frame.capture_time_ms = frame->capture_time_ms();
323   serialized_frame.dpi = frame->dpi();
324   for (webrtc::DesktopRegion::Iterator i(frame->updated_region());
325        !i.IsAtEnd(); i.Advance()) {
326     serialized_frame.dirty_region.push_back(i.rect());
327   }
328
329   SendToNetwork(
330       new ChromotingDesktopNetworkMsg_CaptureCompleted(serialized_frame));
331 }
332
333 void DesktopSessionAgent::OnMouseCursor(webrtc::MouseCursor* cursor) {
334   DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
335
336   scoped_ptr<webrtc::MouseCursor> owned_cursor(cursor);
337
338   SendToNetwork(
339       new ChromotingDesktopNetworkMsg_MouseCursor(*owned_cursor));
340 }
341
342 void DesktopSessionAgent::OnMouseCursorPosition(
343     webrtc::MouseCursorMonitor::CursorState state,
344     const webrtc::DesktopVector& position) {
345   // We're not subscribing to mouse position changes.
346   NOTREACHED();
347 }
348
349 void DesktopSessionAgent::InjectClipboardEvent(
350     const protocol::ClipboardEvent& event) {
351   DCHECK(caller_task_runner_->BelongsToCurrentThread());
352
353   std::string serialized_event;
354   if (!event.SerializeToString(&serialized_event)) {
355     LOG(ERROR) << "Failed to serialize protocol::ClipboardEvent.";
356     return;
357   }
358
359   SendToNetwork(
360       new ChromotingDesktopNetworkMsg_InjectClipboardEvent(serialized_event));
361 }
362
363 void DesktopSessionAgent::ProcessAudioPacket(scoped_ptr<AudioPacket> packet) {
364   DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
365
366   std::string serialized_packet;
367   if (!packet->SerializeToString(&serialized_packet)) {
368     LOG(ERROR) << "Failed to serialize AudioPacket.";
369     return;
370   }
371
372   SendToNetwork(new ChromotingDesktopNetworkMsg_AudioPacket(serialized_packet));
373 }
374
375 bool DesktopSessionAgent::Start(const base::WeakPtr<Delegate>& delegate,
376                                 IPC::PlatformFileForTransit* desktop_pipe_out) {
377   DCHECK(caller_task_runner_->BelongsToCurrentThread());
378   DCHECK(delegate_.get() == NULL);
379
380   delegate_ = delegate;
381
382   // Create an IPC channel to communicate with the network process.
383   bool result = CreateConnectedIpcChannel(io_task_runner_,
384                                           this,
385                                           &desktop_pipe_,
386                                           &network_channel_);
387   base::PlatformFile raw_desktop_pipe = desktop_pipe_.GetPlatformFile();
388 #if defined(OS_WIN)
389   *desktop_pipe_out = IPC::PlatformFileForTransit(raw_desktop_pipe);
390 #elif defined(OS_POSIX)
391   *desktop_pipe_out = IPC::PlatformFileForTransit(raw_desktop_pipe, false);
392 #else
393 #error Unsupported platform.
394 #endif
395   return result;
396 }
397
398 void DesktopSessionAgent::Stop() {
399   DCHECK(caller_task_runner_->BelongsToCurrentThread());
400
401   delegate_.reset();
402
403   // Make sure the channel is closed.
404   network_channel_.reset();
405
406   if (started_) {
407     started_ = false;
408
409     // Ignore any further callbacks.
410     control_factory_.InvalidateWeakPtrs();
411     client_jid_.clear();
412
413     remote_input_filter_.reset();
414
415     // Ensure that any pressed keys or buttons are released.
416     input_tracker_->ReleaseAll();
417     input_tracker_.reset();
418
419     desktop_environment_.reset();
420     input_injector_.reset();
421     screen_controls_.reset();
422
423     // Stop the audio capturer.
424     audio_capture_task_runner_->PostTask(
425         FROM_HERE, base::Bind(&DesktopSessionAgent::StopAudioCapturer, this));
426
427     // Stop the video capturer.
428     video_capture_task_runner_->PostTask(
429         FROM_HERE, base::Bind(
430             &DesktopSessionAgent::StopVideoCapturerAndMouseMonitor, this));
431   }
432 }
433
434 void DesktopSessionAgent::OnCaptureFrame() {
435   if (!video_capture_task_runner_->BelongsToCurrentThread()) {
436     video_capture_task_runner_->PostTask(
437         FROM_HERE,
438         base::Bind(&DesktopSessionAgent::OnCaptureFrame, this));
439     return;
440   }
441
442   mouse_cursor_monitor_->Capture();
443
444   // webrtc::DesktopCapturer supports a very few (currently 2) outstanding
445   // capture requests. The requests are serialized on
446   // |video_capture_task_runner()| task runner. If the client issues more
447   // requests, pixel data in captured frames will likely be corrupted but
448   // stability of webrtc::DesktopCapturer will not be affected.
449   video_capturer_->Capture(webrtc::DesktopRegion());
450 }
451
452 void DesktopSessionAgent::OnInjectClipboardEvent(
453     const std::string& serialized_event) {
454   DCHECK(caller_task_runner_->BelongsToCurrentThread());
455
456   protocol::ClipboardEvent event;
457   if (!event.ParseFromString(serialized_event)) {
458     LOG(ERROR) << "Failed to parse protocol::ClipboardEvent.";
459     return;
460   }
461
462   // InputStub implementations must verify events themselves, so we don't need
463   // verification here. This matches HostEventDispatcher.
464   input_injector_->InjectClipboardEvent(event);
465 }
466
467 void DesktopSessionAgent::OnInjectKeyEvent(
468     const std::string& serialized_event) {
469   DCHECK(caller_task_runner_->BelongsToCurrentThread());
470
471   protocol::KeyEvent event;
472   if (!event.ParseFromString(serialized_event)) {
473     LOG(ERROR) << "Failed to parse protocol::KeyEvent.";
474     return;
475   }
476
477   // InputStub implementations must verify events themselves, so we need only
478   // basic verification here. This matches HostEventDispatcher.
479   if (!event.has_usb_keycode() || !event.has_pressed()) {
480     LOG(ERROR) << "Received invalid key event.";
481     return;
482   }
483
484   remote_input_filter_->InjectKeyEvent(event);
485 }
486
487 void DesktopSessionAgent::OnInjectTextEvent(
488     const std::string& serialized_event) {
489   DCHECK(caller_task_runner_->BelongsToCurrentThread());
490
491   protocol::TextEvent event;
492   if (!event.ParseFromString(serialized_event)) {
493     LOG(ERROR) << "Failed to parse protocol::TextEvent.";
494     return;
495   }
496
497   // InputStub implementations must verify events themselves, so we need only
498   // basic verification here. This matches HostEventDispatcher.
499   if (!event.has_text()) {
500     LOG(ERROR) << "Received invalid TextEvent.";
501     return;
502   }
503
504   remote_input_filter_->InjectTextEvent(event);
505 }
506
507 void DesktopSessionAgent::OnInjectMouseEvent(
508     const std::string& serialized_event) {
509   DCHECK(caller_task_runner_->BelongsToCurrentThread());
510
511   protocol::MouseEvent event;
512   if (!event.ParseFromString(serialized_event)) {
513     LOG(ERROR) << "Failed to parse protocol::MouseEvent.";
514     return;
515   }
516
517   // InputStub implementations must verify events themselves, so we don't need
518   // verification here. This matches HostEventDispatcher.
519   remote_input_filter_->InjectMouseEvent(event);
520 }
521
522 void DesktopSessionAgent::SetScreenResolution(
523     const ScreenResolution& resolution) {
524   DCHECK(caller_task_runner_->BelongsToCurrentThread());
525
526   if (screen_controls_ && resolution.IsEmpty())
527     screen_controls_->SetScreenResolution(resolution);
528 }
529
530 void DesktopSessionAgent::SendToNetwork(IPC::Message* message) {
531   if (!caller_task_runner_->BelongsToCurrentThread()) {
532     caller_task_runner_->PostTask(
533         FROM_HERE,
534         base::Bind(&DesktopSessionAgent::SendToNetwork, this, message));
535     return;
536   }
537
538   if (network_channel_) {
539     network_channel_->Send(message);
540   } else {
541     delete message;
542   }
543 }
544
545 void DesktopSessionAgent::StartAudioCapturer() {
546   DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
547
548   if (audio_capturer_) {
549     audio_capturer_->Start(base::Bind(&DesktopSessionAgent::ProcessAudioPacket,
550                                       this));
551   }
552 }
553
554 void DesktopSessionAgent::StopAudioCapturer() {
555   DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
556
557   audio_capturer_.reset();
558 }
559
560 void DesktopSessionAgent::StartVideoCapturerAndMouseMonitor() {
561   DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
562
563   if (video_capturer_) {
564     video_capturer_->Start(this);
565   }
566
567   if (mouse_cursor_monitor_) {
568     mouse_cursor_monitor_->Init(this, webrtc::MouseCursorMonitor::SHAPE_ONLY);
569   }
570 }
571
572 void DesktopSessionAgent::StopVideoCapturerAndMouseMonitor() {
573   DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
574
575   video_capturer_.reset();
576   last_frame_.reset();
577   mouse_cursor_monitor_.reset();
578
579   // Video capturer must delete all buffers.
580   DCHECK_EQ(shared_buffers_, 0);
581 }
582
583 void DesktopSessionAgent::OnSharedBufferDeleted(int id) {
584   DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
585   DCHECK(id != 0);
586
587   shared_buffers_--;
588   DCHECK_GE(shared_buffers_, 0);
589   SendToNetwork(new ChromotingDesktopNetworkMsg_ReleaseSharedBuffer(id));
590 }
591
592 }  // namespace remoting