Stop using legacy GrContext member function aliases.
[chromium-blink-merge.git] / remoting / host / desktop_session_agent.cc
blobd4b15a0cd4e1c4648f7705d10b662257f5f153ca
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 virtual ~DesktopSesssionClipboardStub();
44 // protocol::ClipboardStub implementation.
45 virtual void InjectClipboardEvent(
46 const protocol::ClipboardEvent& event) override;
48 private:
49 scoped_refptr<DesktopSessionAgent> desktop_session_agent_;
51 DISALLOW_COPY_AND_ASSIGN(DesktopSesssionClipboardStub);
54 DesktopSesssionClipboardStub::DesktopSesssionClipboardStub(
55 scoped_refptr<DesktopSessionAgent> desktop_session_agent)
56 : desktop_session_agent_(desktop_session_agent) {
59 DesktopSesssionClipboardStub::~DesktopSesssionClipboardStub() {
62 void DesktopSesssionClipboardStub::InjectClipboardEvent(
63 const protocol::ClipboardEvent& event) {
64 desktop_session_agent_->InjectClipboardEvent(event);
67 } // namespace
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 nullptr;
79 return make_scoped_ptr(new SharedBuffer(agent, memory.Pass(), size, id));
82 virtual ~SharedBuffer() {
83 agent_->OnSharedBufferDeleted(id());
86 private:
87 SharedBuffer(DesktopSessionAgent* agent,
88 scoped_ptr<base::SharedMemory> memory,
89 size_t size,
90 int id)
91 : SharedMemory(memory->memory(), size,
92 #if defined(OS_WIN)
93 memory->handle(),
94 #else
95 memory->handle().fd,
96 #endif
97 id),
98 agent_(agent),
99 shared_memory_(memory.Pass()) {
102 DesktopSessionAgent* agent_;
103 scoped_ptr<base::SharedMemory> shared_memory_;
105 DISALLOW_COPY_AND_ASSIGN(SharedBuffer);
108 DesktopSessionAgent::Delegate::~Delegate() {
111 DesktopSessionAgent::DesktopSessionAgent(
112 scoped_refptr<AutoThreadTaskRunner> audio_capture_task_runner,
113 scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
114 scoped_refptr<AutoThreadTaskRunner> input_task_runner,
115 scoped_refptr<AutoThreadTaskRunner> io_task_runner,
116 scoped_refptr<AutoThreadTaskRunner> video_capture_task_runner)
117 : audio_capture_task_runner_(audio_capture_task_runner),
118 caller_task_runner_(caller_task_runner),
119 input_task_runner_(input_task_runner),
120 io_task_runner_(io_task_runner),
121 video_capture_task_runner_(video_capture_task_runner),
122 next_shared_buffer_id_(1),
123 shared_buffers_(0),
124 started_(false),
125 weak_factory_(this) {
126 DCHECK(caller_task_runner_->BelongsToCurrentThread());
129 bool DesktopSessionAgent::OnMessageReceived(const IPC::Message& message) {
130 DCHECK(caller_task_runner_->BelongsToCurrentThread());
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_InjectTextEvent,
142 OnInjectTextEvent)
143 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectMouseEvent,
144 OnInjectMouseEvent)
145 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_SetScreenResolution,
146 SetScreenResolution)
147 IPC_MESSAGE_UNHANDLED(handled = false)
148 IPC_END_MESSAGE_MAP()
149 } else {
150 IPC_BEGIN_MESSAGE_MAP(DesktopSessionAgent, message)
151 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_StartSessionAgent,
152 OnStartSessionAgent)
153 IPC_MESSAGE_UNHANDLED(handled = false)
154 IPC_END_MESSAGE_MAP()
157 CHECK(handled) << "Received unexpected IPC type: " << message.type();
158 return handled;
161 void DesktopSessionAgent::OnChannelConnected(int32 peer_pid) {
162 DCHECK(caller_task_runner_->BelongsToCurrentThread());
164 VLOG(1) << "IPC: desktop <- network (" << peer_pid << ")";
166 desktop_pipe_.Close();
169 void DesktopSessionAgent::OnChannelError() {
170 DCHECK(caller_task_runner_->BelongsToCurrentThread());
172 // Make sure the channel is closed.
173 network_channel_.reset();
174 desktop_pipe_.Close();
176 // Notify the caller that the channel has been disconnected.
177 if (delegate_.get())
178 delegate_->OnNetworkProcessDisconnected();
181 webrtc::SharedMemory* DesktopSessionAgent::CreateSharedMemory(size_t size) {
182 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
184 scoped_ptr<SharedBuffer> buffer =
185 SharedBuffer::Create(this, size, next_shared_buffer_id_);
186 if (buffer) {
187 shared_buffers_++;
189 // |next_shared_buffer_id_| starts from 1 and incrementing it by 2 makes
190 // sure it is always odd and therefore zero is never used as a valid buffer
191 // ID.
193 // It is very unlikely (though theoretically possible) to allocate the same
194 // ID for two different buffers due to integer overflow. It should take
195 // about a year of allocating 100 new buffers every second. Practically
196 // speaking it never happens.
197 next_shared_buffer_id_ += 2;
199 IPC::PlatformFileForTransit handle;
200 #if defined(OS_WIN)
201 handle = buffer->handle();
202 #else
203 handle = base::FileDescriptor(buffer->handle(), false);
204 #endif
205 SendToNetwork(new ChromotingDesktopNetworkMsg_CreateSharedBuffer(
206 buffer->id(), handle, buffer->size()));
209 return buffer.release();
212 DesktopSessionAgent::~DesktopSessionAgent() {
213 DCHECK(!audio_capturer_);
214 DCHECK(!desktop_environment_);
215 DCHECK(!network_channel_);
216 DCHECK(!screen_controls_);
217 DCHECK(!video_capturer_);
220 const std::string& DesktopSessionAgent::client_jid() const {
221 return client_jid_;
224 void DesktopSessionAgent::DisconnectSession() {
225 SendToNetwork(new ChromotingDesktopNetworkMsg_DisconnectSession());
228 void DesktopSessionAgent::OnLocalMouseMoved(
229 const webrtc::DesktopVector& new_pos) {
230 DCHECK(caller_task_runner_->BelongsToCurrentThread());
232 remote_input_filter_->LocalMouseMoved(new_pos);
235 void DesktopSessionAgent::SetDisableInputs(bool disable_inputs) {
236 DCHECK(caller_task_runner_->BelongsToCurrentThread());
238 // Do not expect this method to be called because it is only used by It2Me.
239 NOTREACHED();
242 void DesktopSessionAgent::ResetVideoPipeline() {
243 DCHECK(caller_task_runner_->BelongsToCurrentThread());
245 // This method is only used by HostExtensionSessions in the network process.
246 NOTREACHED();
249 void DesktopSessionAgent::OnStartSessionAgent(
250 const std::string& authenticated_jid,
251 const ScreenResolution& resolution,
252 bool virtual_terminal) {
253 DCHECK(caller_task_runner_->BelongsToCurrentThread());
254 DCHECK(!started_);
255 DCHECK(!audio_capturer_);
256 DCHECK(!desktop_environment_);
257 DCHECK(!input_injector_);
258 DCHECK(!screen_controls_);
259 DCHECK(!video_capturer_);
261 started_ = true;
262 client_jid_ = authenticated_jid;
264 // Enable the curtain mode.
265 delegate_->desktop_environment_factory().SetEnableCurtaining(
266 virtual_terminal);
268 // Create a desktop environment for the new session.
269 desktop_environment_ = delegate_->desktop_environment_factory().Create(
270 weak_factory_.GetWeakPtr());
272 // Create the session controller and set the initial screen resolution.
273 screen_controls_ = desktop_environment_->CreateScreenControls();
274 SetScreenResolution(resolution);
276 // Create the input injector.
277 input_injector_ = desktop_environment_->CreateInputInjector();
279 // Hook up the input filter.
280 input_tracker_.reset(new protocol::InputEventTracker(input_injector_.get()));
281 remote_input_filter_.reset(new RemoteInputFilter(input_tracker_.get()));
283 #if defined(OS_WIN)
284 // LocalInputMonitorWin filters out an echo of the injected input before it
285 // reaches |remote_input_filter_|.
286 remote_input_filter_->SetExpectLocalEcho(false);
287 #endif // defined(OS_WIN)
289 // Start the input injector.
290 scoped_ptr<protocol::ClipboardStub> clipboard_stub(
291 new DesktopSesssionClipboardStub(this));
292 input_injector_->Start(clipboard_stub.Pass());
294 // Start the audio capturer.
295 if (delegate_->desktop_environment_factory().SupportsAudioCapture()) {
296 audio_capturer_ = desktop_environment_->CreateAudioCapturer();
297 audio_capture_task_runner_->PostTask(
298 FROM_HERE, base::Bind(&DesktopSessionAgent::StartAudioCapturer, this));
301 // Start the video capturer and mouse cursor monitor.
302 video_capturer_ = desktop_environment_->CreateVideoCapturer();
303 mouse_cursor_monitor_ = desktop_environment_->CreateMouseCursorMonitor();
304 video_capture_task_runner_->PostTask(
305 FROM_HERE, base::Bind(
306 &DesktopSessionAgent::StartVideoCapturerAndMouseMonitor, this));
309 void DesktopSessionAgent::OnCaptureCompleted(webrtc::DesktopFrame* frame) {
310 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
312 last_frame_.reset(frame);
314 current_size_ = frame->size();
316 // Serialize webrtc::DesktopFrame.
317 SerializedDesktopFrame serialized_frame;
318 serialized_frame.shared_buffer_id = frame->shared_memory()->id();
319 serialized_frame.bytes_per_row = frame->stride();
320 serialized_frame.dimensions = frame->size();
321 serialized_frame.capture_time_ms = frame->capture_time_ms();
322 serialized_frame.dpi = frame->dpi();
323 for (webrtc::DesktopRegion::Iterator i(frame->updated_region());
324 !i.IsAtEnd(); i.Advance()) {
325 serialized_frame.dirty_region.push_back(i.rect());
328 SendToNetwork(
329 new ChromotingDesktopNetworkMsg_CaptureCompleted(serialized_frame));
332 void DesktopSessionAgent::OnMouseCursor(webrtc::MouseCursor* cursor) {
333 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
335 scoped_ptr<webrtc::MouseCursor> owned_cursor(cursor);
337 SendToNetwork(
338 new ChromotingDesktopNetworkMsg_MouseCursor(*owned_cursor));
341 void DesktopSessionAgent::OnMouseCursorPosition(
342 webrtc::MouseCursorMonitor::CursorState state,
343 const webrtc::DesktopVector& position) {
344 // We're not subscribing to mouse position changes.
345 NOTREACHED();
348 void DesktopSessionAgent::InjectClipboardEvent(
349 const protocol::ClipboardEvent& event) {
350 DCHECK(caller_task_runner_->BelongsToCurrentThread());
352 std::string serialized_event;
353 if (!event.SerializeToString(&serialized_event)) {
354 LOG(ERROR) << "Failed to serialize protocol::ClipboardEvent.";
355 return;
358 SendToNetwork(
359 new ChromotingDesktopNetworkMsg_InjectClipboardEvent(serialized_event));
362 void DesktopSessionAgent::ProcessAudioPacket(scoped_ptr<AudioPacket> packet) {
363 DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
365 std::string serialized_packet;
366 if (!packet->SerializeToString(&serialized_packet)) {
367 LOG(ERROR) << "Failed to serialize AudioPacket.";
368 return;
371 SendToNetwork(new ChromotingDesktopNetworkMsg_AudioPacket(serialized_packet));
374 bool DesktopSessionAgent::Start(const base::WeakPtr<Delegate>& delegate,
375 IPC::PlatformFileForTransit* desktop_pipe_out) {
376 DCHECK(caller_task_runner_->BelongsToCurrentThread());
377 DCHECK(delegate_.get() == NULL);
379 delegate_ = delegate;
381 // Create an IPC channel to communicate with the network process.
382 bool result = CreateConnectedIpcChannel(io_task_runner_,
383 this,
384 &desktop_pipe_,
385 &network_channel_);
386 base::PlatformFile raw_desktop_pipe = desktop_pipe_.GetPlatformFile();
387 #if defined(OS_WIN)
388 *desktop_pipe_out = IPC::PlatformFileForTransit(raw_desktop_pipe);
389 #elif defined(OS_POSIX)
390 *desktop_pipe_out = IPC::PlatformFileForTransit(raw_desktop_pipe, false);
391 #else
392 #error Unsupported platform.
393 #endif
394 return result;
397 void DesktopSessionAgent::Stop() {
398 DCHECK(caller_task_runner_->BelongsToCurrentThread());
400 delegate_.reset();
402 // Make sure the channel is closed.
403 network_channel_.reset();
405 if (started_) {
406 started_ = false;
408 // Ignore any further callbacks.
409 weak_factory_.InvalidateWeakPtrs();
410 client_jid_.clear();
412 remote_input_filter_.reset();
414 // Ensure that any pressed keys or buttons are released.
415 input_tracker_->ReleaseAll();
416 input_tracker_.reset();
418 desktop_environment_.reset();
419 input_injector_.reset();
420 screen_controls_.reset();
422 // Stop the audio capturer.
423 audio_capture_task_runner_->PostTask(
424 FROM_HERE, base::Bind(&DesktopSessionAgent::StopAudioCapturer, this));
426 // Stop the video capturer.
427 video_capture_task_runner_->PostTask(
428 FROM_HERE, base::Bind(
429 &DesktopSessionAgent::StopVideoCapturerAndMouseMonitor, this));
433 void DesktopSessionAgent::OnCaptureFrame() {
434 if (!video_capture_task_runner_->BelongsToCurrentThread()) {
435 video_capture_task_runner_->PostTask(
436 FROM_HERE,
437 base::Bind(&DesktopSessionAgent::OnCaptureFrame, this));
438 return;
441 mouse_cursor_monitor_->Capture();
443 // webrtc::DesktopCapturer supports a very few (currently 2) outstanding
444 // capture requests. The requests are serialized on
445 // |video_capture_task_runner()| task runner. If the client issues more
446 // requests, pixel data in captured frames will likely be corrupted but
447 // stability of webrtc::DesktopCapturer will not be affected.
448 video_capturer_->Capture(webrtc::DesktopRegion());
451 void DesktopSessionAgent::OnInjectClipboardEvent(
452 const std::string& serialized_event) {
453 DCHECK(caller_task_runner_->BelongsToCurrentThread());
455 protocol::ClipboardEvent event;
456 if (!event.ParseFromString(serialized_event)) {
457 LOG(ERROR) << "Failed to parse protocol::ClipboardEvent.";
458 return;
461 // InputStub implementations must verify events themselves, so we don't need
462 // verification here. This matches HostEventDispatcher.
463 input_injector_->InjectClipboardEvent(event);
466 void DesktopSessionAgent::OnInjectKeyEvent(
467 const std::string& serialized_event) {
468 DCHECK(caller_task_runner_->BelongsToCurrentThread());
470 protocol::KeyEvent event;
471 if (!event.ParseFromString(serialized_event)) {
472 LOG(ERROR) << "Failed to parse protocol::KeyEvent.";
473 return;
476 // InputStub implementations must verify events themselves, so we need only
477 // basic verification here. This matches HostEventDispatcher.
478 if (!event.has_usb_keycode() || !event.has_pressed()) {
479 LOG(ERROR) << "Received invalid key event.";
480 return;
483 remote_input_filter_->InjectKeyEvent(event);
486 void DesktopSessionAgent::OnInjectTextEvent(
487 const std::string& serialized_event) {
488 DCHECK(caller_task_runner_->BelongsToCurrentThread());
490 protocol::TextEvent event;
491 if (!event.ParseFromString(serialized_event)) {
492 LOG(ERROR) << "Failed to parse protocol::TextEvent.";
493 return;
496 // InputStub implementations must verify events themselves, so we need only
497 // basic verification here. This matches HostEventDispatcher.
498 if (!event.has_text()) {
499 LOG(ERROR) << "Received invalid TextEvent.";
500 return;
503 remote_input_filter_->InjectTextEvent(event);
506 void DesktopSessionAgent::OnInjectMouseEvent(
507 const std::string& serialized_event) {
508 DCHECK(caller_task_runner_->BelongsToCurrentThread());
510 protocol::MouseEvent event;
511 if (!event.ParseFromString(serialized_event)) {
512 LOG(ERROR) << "Failed to parse protocol::MouseEvent.";
513 return;
516 // InputStub implementations must verify events themselves, so we don't need
517 // verification here. This matches HostEventDispatcher.
518 remote_input_filter_->InjectMouseEvent(event);
521 void DesktopSessionAgent::SetScreenResolution(
522 const ScreenResolution& resolution) {
523 DCHECK(caller_task_runner_->BelongsToCurrentThread());
525 if (screen_controls_ && resolution.IsEmpty())
526 screen_controls_->SetScreenResolution(resolution);
529 void DesktopSessionAgent::SendToNetwork(IPC::Message* message) {
530 if (!caller_task_runner_->BelongsToCurrentThread()) {
531 caller_task_runner_->PostTask(
532 FROM_HERE,
533 base::Bind(&DesktopSessionAgent::SendToNetwork, this, message));
534 return;
537 if (network_channel_) {
538 network_channel_->Send(message);
539 } else {
540 delete message;
544 void DesktopSessionAgent::StartAudioCapturer() {
545 DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
547 if (audio_capturer_) {
548 audio_capturer_->Start(base::Bind(&DesktopSessionAgent::ProcessAudioPacket,
549 this));
553 void DesktopSessionAgent::StopAudioCapturer() {
554 DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
556 audio_capturer_.reset();
559 void DesktopSessionAgent::StartVideoCapturerAndMouseMonitor() {
560 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
562 if (video_capturer_) {
563 video_capturer_->Start(this);
566 if (mouse_cursor_monitor_) {
567 mouse_cursor_monitor_->Init(this, webrtc::MouseCursorMonitor::SHAPE_ONLY);
571 void DesktopSessionAgent::StopVideoCapturerAndMouseMonitor() {
572 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
574 video_capturer_.reset();
575 last_frame_.reset();
576 mouse_cursor_monitor_.reset();
578 // Video capturer must delete all buffers.
579 DCHECK_EQ(shared_buffers_, 0);
582 void DesktopSessionAgent::OnSharedBufferDeleted(int id) {
583 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
584 DCHECK(id != 0);
586 shared_buffers_--;
587 DCHECK_GE(shared_buffers_, 0);
588 SendToNetwork(new ChromotingDesktopNetworkMsg_ReleaseSharedBuffer(id));
591 } // namespace remoting