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