Roll src/third_party/WebKit f36d5e0:68b67cd (svn 193299:193303)
[chromium-blink-merge.git] / remoting / host / desktop_session_agent.cc
blob98e053bea0ebc832e046c6a23da5ea757b81f43c
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.
5 #include "remoting/host/desktop_session_agent.h"
7 #include "base/files/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/desktop_geometry.h"
30 #include "third_party/webrtc/modules/desktop_capture/mouse_cursor.h"
31 #include "third_party/webrtc/modules/desktop_capture/shared_memory.h"
33 namespace remoting {
35 namespace {
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 ~DesktopSesssionClipboardStub() override;
44 // protocol::ClipboardStub implementation.
45 void InjectClipboardEvent(const protocol::ClipboardEvent& event) override;
47 private:
48 scoped_refptr<DesktopSessionAgent> desktop_session_agent_;
50 DISALLOW_COPY_AND_ASSIGN(DesktopSesssionClipboardStub);
53 DesktopSesssionClipboardStub::DesktopSesssionClipboardStub(
54 scoped_refptr<DesktopSessionAgent> desktop_session_agent)
55 : desktop_session_agent_(desktop_session_agent) {
58 DesktopSesssionClipboardStub::~DesktopSesssionClipboardStub() {
61 void DesktopSesssionClipboardStub::InjectClipboardEvent(
62 const protocol::ClipboardEvent& event) {
63 desktop_session_agent_->InjectClipboardEvent(event);
66 } // namespace
68 // webrtc::SharedMemory implementation that notifies creating
69 // DesktopSessionAgent when it's deleted.
70 class DesktopSessionAgent::SharedBuffer : public webrtc::SharedMemory {
71 public:
72 static scoped_ptr<SharedBuffer> Create(DesktopSessionAgent* agent,
73 size_t size,
74 int id) {
75 scoped_ptr<base::SharedMemory> memory(new base::SharedMemory());
76 if (!memory->CreateAndMapAnonymous(size))
77 return nullptr;
78 return make_scoped_ptr(new SharedBuffer(agent, memory.Pass(), size, id));
81 ~SharedBuffer() override { agent_->OnSharedBufferDeleted(id()); }
83 private:
84 SharedBuffer(DesktopSessionAgent* agent,
85 scoped_ptr<base::SharedMemory> memory,
86 size_t size,
87 int id)
88 : SharedMemory(memory->memory(), size,
89 #if defined(OS_WIN)
90 memory->handle(),
91 #else
92 memory->handle().fd,
93 #endif
94 id),
95 agent_(agent),
96 shared_memory_(memory.Pass()) {
99 DesktopSessionAgent* agent_;
100 scoped_ptr<base::SharedMemory> shared_memory_;
102 DISALLOW_COPY_AND_ASSIGN(SharedBuffer);
105 DesktopSessionAgent::Delegate::~Delegate() {
108 DesktopSessionAgent::DesktopSessionAgent(
109 scoped_refptr<AutoThreadTaskRunner> audio_capture_task_runner,
110 scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
111 scoped_refptr<AutoThreadTaskRunner> input_task_runner,
112 scoped_refptr<AutoThreadTaskRunner> io_task_runner,
113 scoped_refptr<AutoThreadTaskRunner> video_capture_task_runner)
114 : audio_capture_task_runner_(audio_capture_task_runner),
115 caller_task_runner_(caller_task_runner),
116 input_task_runner_(input_task_runner),
117 io_task_runner_(io_task_runner),
118 video_capture_task_runner_(video_capture_task_runner),
119 next_shared_buffer_id_(1),
120 shared_buffers_(0),
121 started_(false),
122 weak_factory_(this) {
123 DCHECK(caller_task_runner_->BelongsToCurrentThread());
126 bool DesktopSessionAgent::OnMessageReceived(const IPC::Message& message) {
127 DCHECK(caller_task_runner_->BelongsToCurrentThread());
129 bool handled = true;
130 if (started_) {
131 IPC_BEGIN_MESSAGE_MAP(DesktopSessionAgent, message)
132 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_CaptureFrame,
133 OnCaptureFrame)
134 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectClipboardEvent,
135 OnInjectClipboardEvent)
136 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectKeyEvent,
137 OnInjectKeyEvent)
138 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectTextEvent,
139 OnInjectTextEvent)
140 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectMouseEvent,
141 OnInjectMouseEvent)
142 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectTouchEvent,
143 OnInjectTouchEvent)
144 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_SetScreenResolution,
145 SetScreenResolution)
146 IPC_MESSAGE_UNHANDLED(handled = false)
147 IPC_END_MESSAGE_MAP()
148 } else {
149 IPC_BEGIN_MESSAGE_MAP(DesktopSessionAgent, message)
150 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_StartSessionAgent,
151 OnStartSessionAgent)
152 IPC_MESSAGE_UNHANDLED(handled = false)
153 IPC_END_MESSAGE_MAP()
156 CHECK(handled) << "Received unexpected IPC type: " << message.type();
157 return handled;
160 void DesktopSessionAgent::OnChannelConnected(int32 peer_pid) {
161 DCHECK(caller_task_runner_->BelongsToCurrentThread());
163 VLOG(1) << "IPC: desktop <- network (" << peer_pid << ")";
165 desktop_pipe_.Close();
168 void DesktopSessionAgent::OnChannelError() {
169 DCHECK(caller_task_runner_->BelongsToCurrentThread());
171 // Make sure the channel is closed.
172 network_channel_.reset();
173 desktop_pipe_.Close();
175 // Notify the caller that the channel has been disconnected.
176 if (delegate_.get())
177 delegate_->OnNetworkProcessDisconnected();
180 webrtc::SharedMemory* DesktopSessionAgent::CreateSharedMemory(size_t size) {
181 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
183 scoped_ptr<SharedBuffer> buffer =
184 SharedBuffer::Create(this, size, next_shared_buffer_id_);
185 if (buffer) {
186 shared_buffers_++;
188 // |next_shared_buffer_id_| starts from 1 and incrementing it by 2 makes
189 // sure it is always odd and therefore zero is never used as a valid buffer
190 // ID.
192 // It is very unlikely (though theoretically possible) to allocate the same
193 // ID for two different buffers due to integer overflow. It should take
194 // about a year of allocating 100 new buffers every second. Practically
195 // speaking it never happens.
196 next_shared_buffer_id_ += 2;
198 IPC::PlatformFileForTransit handle;
199 #if defined(OS_WIN)
200 handle = buffer->handle();
201 #else
202 handle = base::FileDescriptor(buffer->handle(), false);
203 #endif
204 SendToNetwork(new ChromotingDesktopNetworkMsg_CreateSharedBuffer(
205 buffer->id(), handle, buffer->size()));
208 return buffer.release();
211 DesktopSessionAgent::~DesktopSessionAgent() {
212 DCHECK(!audio_capturer_);
213 DCHECK(!desktop_environment_);
214 DCHECK(!network_channel_);
215 DCHECK(!screen_controls_);
216 DCHECK(!video_capturer_);
219 const std::string& DesktopSessionAgent::client_jid() const {
220 return client_jid_;
223 void DesktopSessionAgent::DisconnectSession() {
224 SendToNetwork(new ChromotingDesktopNetworkMsg_DisconnectSession());
227 void DesktopSessionAgent::OnLocalMouseMoved(
228 const webrtc::DesktopVector& new_pos) {
229 DCHECK(caller_task_runner_->BelongsToCurrentThread());
231 remote_input_filter_->LocalMouseMoved(new_pos);
234 void DesktopSessionAgent::SetDisableInputs(bool disable_inputs) {
235 DCHECK(caller_task_runner_->BelongsToCurrentThread());
237 // Do not expect this method to be called because it is only used by It2Me.
238 NOTREACHED();
241 void DesktopSessionAgent::ResetVideoPipeline() {
242 DCHECK(caller_task_runner_->BelongsToCurrentThread());
244 // This method is only used by HostExtensionSessions in the network process.
245 NOTREACHED();
248 void DesktopSessionAgent::OnStartSessionAgent(
249 const std::string& authenticated_jid,
250 const ScreenResolution& resolution,
251 bool virtual_terminal) {
252 DCHECK(caller_task_runner_->BelongsToCurrentThread());
253 DCHECK(!started_);
254 DCHECK(!audio_capturer_);
255 DCHECK(!desktop_environment_);
256 DCHECK(!input_injector_);
257 DCHECK(!screen_controls_);
258 DCHECK(!video_capturer_);
260 started_ = true;
261 client_jid_ = authenticated_jid;
263 // Enable the curtain mode.
264 delegate_->desktop_environment_factory().SetEnableCurtaining(
265 virtual_terminal);
267 // Create a desktop environment for the new session.
268 desktop_environment_ = delegate_->desktop_environment_factory().Create(
269 weak_factory_.GetWeakPtr());
271 // Create the session controller and set the initial screen resolution.
272 screen_controls_ = desktop_environment_->CreateScreenControls();
273 SetScreenResolution(resolution);
275 // Create the input injector.
276 input_injector_ = desktop_environment_->CreateInputInjector();
278 // Hook up the input filter.
279 input_tracker_.reset(new protocol::InputEventTracker(input_injector_.get()));
280 remote_input_filter_.reset(new RemoteInputFilter(input_tracker_.get()));
282 #if defined(OS_WIN)
283 // LocalInputMonitorWin filters out an echo of the injected input before it
284 // reaches |remote_input_filter_|.
285 remote_input_filter_->SetExpectLocalEcho(false);
286 #endif // defined(OS_WIN)
288 // Start the input injector.
289 scoped_ptr<protocol::ClipboardStub> clipboard_stub(
290 new DesktopSesssionClipboardStub(this));
291 input_injector_->Start(clipboard_stub.Pass());
293 // Start the audio capturer.
294 if (delegate_->desktop_environment_factory().SupportsAudioCapture()) {
295 audio_capturer_ = desktop_environment_->CreateAudioCapturer();
296 audio_capture_task_runner_->PostTask(
297 FROM_HERE, base::Bind(&DesktopSessionAgent::StartAudioCapturer, this));
300 // Start the video capturer and mouse cursor monitor.
301 video_capturer_ = desktop_environment_->CreateVideoCapturer();
302 mouse_cursor_monitor_ = desktop_environment_->CreateMouseCursorMonitor();
303 video_capture_task_runner_->PostTask(
304 FROM_HERE, base::Bind(
305 &DesktopSessionAgent::StartVideoCapturerAndMouseMonitor, this));
308 void DesktopSessionAgent::OnCaptureCompleted(webrtc::DesktopFrame* frame) {
309 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
311 last_frame_.reset(frame);
313 current_size_ = frame->size();
315 // Serialize webrtc::DesktopFrame.
316 SerializedDesktopFrame serialized_frame;
317 serialized_frame.shared_buffer_id = frame->shared_memory()->id();
318 serialized_frame.bytes_per_row = frame->stride();
319 serialized_frame.dimensions = frame->size();
320 serialized_frame.capture_time_ms = frame->capture_time_ms();
321 serialized_frame.dpi = frame->dpi();
322 for (webrtc::DesktopRegion::Iterator i(frame->updated_region());
323 !i.IsAtEnd(); i.Advance()) {
324 serialized_frame.dirty_region.push_back(i.rect());
327 SendToNetwork(
328 new ChromotingDesktopNetworkMsg_CaptureCompleted(serialized_frame));
331 void DesktopSessionAgent::OnMouseCursor(webrtc::MouseCursor* cursor) {
332 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
334 scoped_ptr<webrtc::MouseCursor> owned_cursor(cursor);
336 SendToNetwork(
337 new ChromotingDesktopNetworkMsg_MouseCursor(*owned_cursor));
340 void DesktopSessionAgent::OnMouseCursorPosition(
341 webrtc::MouseCursorMonitor::CursorState state,
342 const webrtc::DesktopVector& position) {
343 // We're not subscribing to mouse position changes.
344 NOTREACHED();
347 void DesktopSessionAgent::InjectClipboardEvent(
348 const protocol::ClipboardEvent& event) {
349 DCHECK(caller_task_runner_->BelongsToCurrentThread());
351 std::string serialized_event;
352 if (!event.SerializeToString(&serialized_event)) {
353 LOG(ERROR) << "Failed to serialize protocol::ClipboardEvent.";
354 return;
357 SendToNetwork(
358 new ChromotingDesktopNetworkMsg_InjectClipboardEvent(serialized_event));
361 void DesktopSessionAgent::ProcessAudioPacket(scoped_ptr<AudioPacket> packet) {
362 DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
364 std::string serialized_packet;
365 if (!packet->SerializeToString(&serialized_packet)) {
366 LOG(ERROR) << "Failed to serialize AudioPacket.";
367 return;
370 SendToNetwork(new ChromotingDesktopNetworkMsg_AudioPacket(serialized_packet));
373 bool DesktopSessionAgent::Start(const base::WeakPtr<Delegate>& delegate,
374 IPC::PlatformFileForTransit* desktop_pipe_out) {
375 DCHECK(caller_task_runner_->BelongsToCurrentThread());
376 DCHECK(delegate_.get() == nullptr);
378 delegate_ = delegate;
380 // Create an IPC channel to communicate with the network process.
381 bool result = CreateConnectedIpcChannel(io_task_runner_,
382 this,
383 &desktop_pipe_,
384 &network_channel_);
385 base::PlatformFile raw_desktop_pipe = desktop_pipe_.GetPlatformFile();
386 #if defined(OS_WIN)
387 *desktop_pipe_out = IPC::PlatformFileForTransit(raw_desktop_pipe);
388 #elif defined(OS_POSIX)
389 *desktop_pipe_out = IPC::PlatformFileForTransit(raw_desktop_pipe, false);
390 #else
391 #error Unsupported platform.
392 #endif
393 return result;
396 void DesktopSessionAgent::Stop() {
397 DCHECK(caller_task_runner_->BelongsToCurrentThread());
399 delegate_.reset();
401 // Make sure the channel is closed.
402 network_channel_.reset();
404 if (started_) {
405 started_ = false;
407 // Ignore any further callbacks.
408 weak_factory_.InvalidateWeakPtrs();
409 client_jid_.clear();
411 remote_input_filter_.reset();
413 // Ensure that any pressed keys or buttons are released.
414 input_tracker_->ReleaseAll();
415 input_tracker_.reset();
417 desktop_environment_.reset();
418 input_injector_.reset();
419 screen_controls_.reset();
421 // Stop the audio capturer.
422 audio_capture_task_runner_->PostTask(
423 FROM_HERE, base::Bind(&DesktopSessionAgent::StopAudioCapturer, this));
425 // Stop the video capturer.
426 video_capture_task_runner_->PostTask(
427 FROM_HERE, base::Bind(
428 &DesktopSessionAgent::StopVideoCapturerAndMouseMonitor, this));
432 void DesktopSessionAgent::OnCaptureFrame() {
433 if (!video_capture_task_runner_->BelongsToCurrentThread()) {
434 video_capture_task_runner_->PostTask(
435 FROM_HERE,
436 base::Bind(&DesktopSessionAgent::OnCaptureFrame, this));
437 return;
440 mouse_cursor_monitor_->Capture();
442 // webrtc::DesktopCapturer supports a very few (currently 2) outstanding
443 // capture requests. The requests are serialized on
444 // |video_capture_task_runner()| task runner. If the client issues more
445 // requests, pixel data in captured frames will likely be corrupted but
446 // stability of webrtc::DesktopCapturer will not be affected.
447 video_capturer_->Capture(webrtc::DesktopRegion());
450 void DesktopSessionAgent::OnInjectClipboardEvent(
451 const std::string& serialized_event) {
452 DCHECK(caller_task_runner_->BelongsToCurrentThread());
454 protocol::ClipboardEvent event;
455 if (!event.ParseFromString(serialized_event)) {
456 LOG(ERROR) << "Failed to parse protocol::ClipboardEvent.";
457 return;
460 // InputStub implementations must verify events themselves, so we don't need
461 // verification here. This matches HostEventDispatcher.
462 input_injector_->InjectClipboardEvent(event);
465 void DesktopSessionAgent::OnInjectKeyEvent(
466 const std::string& serialized_event) {
467 DCHECK(caller_task_runner_->BelongsToCurrentThread());
469 protocol::KeyEvent event;
470 if (!event.ParseFromString(serialized_event)) {
471 LOG(ERROR) << "Failed to parse protocol::KeyEvent.";
472 return;
475 // InputStub implementations must verify events themselves, so we need only
476 // basic verification here. This matches HostEventDispatcher.
477 if (!event.has_usb_keycode() || !event.has_pressed()) {
478 LOG(ERROR) << "Received invalid key event.";
479 return;
482 remote_input_filter_->InjectKeyEvent(event);
485 void DesktopSessionAgent::OnInjectTextEvent(
486 const std::string& serialized_event) {
487 DCHECK(caller_task_runner_->BelongsToCurrentThread());
489 protocol::TextEvent event;
490 if (!event.ParseFromString(serialized_event)) {
491 LOG(ERROR) << "Failed to parse protocol::TextEvent.";
492 return;
495 // InputStub implementations must verify events themselves, so we need only
496 // basic verification here. This matches HostEventDispatcher.
497 if (!event.has_text()) {
498 LOG(ERROR) << "Received invalid TextEvent.";
499 return;
502 remote_input_filter_->InjectTextEvent(event);
505 void DesktopSessionAgent::OnInjectMouseEvent(
506 const std::string& serialized_event) {
507 DCHECK(caller_task_runner_->BelongsToCurrentThread());
509 protocol::MouseEvent event;
510 if (!event.ParseFromString(serialized_event)) {
511 LOG(ERROR) << "Failed to parse protocol::MouseEvent.";
512 return;
515 // InputStub implementations must verify events themselves, so we don't need
516 // verification here. This matches HostEventDispatcher.
517 remote_input_filter_->InjectMouseEvent(event);
520 void DesktopSessionAgent::OnInjectTouchEvent(
521 const std::string& serialized_event) {
522 DCHECK(caller_task_runner_->BelongsToCurrentThread());
524 protocol::TouchEvent event;
525 if (!event.ParseFromString(serialized_event)) {
526 LOG(ERROR) << "Failed to parse protocol::TouchEvent.";
527 return;
530 remote_input_filter_->InjectTouchEvent(event);
533 void DesktopSessionAgent::SetScreenResolution(
534 const ScreenResolution& resolution) {
535 DCHECK(caller_task_runner_->BelongsToCurrentThread());
537 if (screen_controls_ && resolution.IsEmpty())
538 screen_controls_->SetScreenResolution(resolution);
541 void DesktopSessionAgent::SendToNetwork(IPC::Message* message) {
542 if (!caller_task_runner_->BelongsToCurrentThread()) {
543 caller_task_runner_->PostTask(
544 FROM_HERE,
545 base::Bind(&DesktopSessionAgent::SendToNetwork, this, message));
546 return;
549 if (network_channel_) {
550 network_channel_->Send(message);
551 } else {
552 delete message;
556 void DesktopSessionAgent::StartAudioCapturer() {
557 DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
559 if (audio_capturer_) {
560 audio_capturer_->Start(base::Bind(&DesktopSessionAgent::ProcessAudioPacket,
561 this));
565 void DesktopSessionAgent::StopAudioCapturer() {
566 DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
568 audio_capturer_.reset();
571 void DesktopSessionAgent::StartVideoCapturerAndMouseMonitor() {
572 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
574 if (video_capturer_) {
575 video_capturer_->Start(this);
578 if (mouse_cursor_monitor_) {
579 mouse_cursor_monitor_->Init(this, webrtc::MouseCursorMonitor::SHAPE_ONLY);
583 void DesktopSessionAgent::StopVideoCapturerAndMouseMonitor() {
584 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
586 video_capturer_.reset();
587 last_frame_.reset();
588 mouse_cursor_monitor_.reset();
590 // Video capturer must delete all buffers.
591 DCHECK_EQ(shared_buffers_, 0);
594 void DesktopSessionAgent::OnSharedBufferDeleted(int id) {
595 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
596 DCHECK(id != 0);
598 shared_buffers_--;
599 DCHECK_GE(shared_buffers_, 0);
600 SendToNetwork(new ChromotingDesktopNetworkMsg_ReleaseSharedBuffer(id));
603 } // namespace remoting