Remove dependency on content from remoting_host.
[chromium-blink-merge.git] / remoting / host / desktop_session_proxy.cc
blobb321655904dcaaa03372d3e6e52544a6f0d33687
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_proxy.h"
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"
32 #if defined(OS_WIN)
33 #include "base/win/scoped_handle.h"
34 #endif // defined(OS_WIN)
36 const bool kReadOnly = true;
37 const char kSendInitialResolution[] = "sendInitialResolution";
38 const char kRateLimitResizeRequests[] = "rateLimitResizeRequests";
40 namespace remoting {
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;
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
78 private:
79 virtual ~IpcSharedBufferCore() {}
80 friend class base::RefCountedThreadSafe<IpcSharedBufferCore>;
82 int id_;
83 base::SharedMemory shared_memory_;
84 size_t size_;
86 DISALLOW_COPY_AND_ASSIGN(IpcSharedBufferCore);
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) {
97 private:
98 scoped_refptr<IpcSharedBufferCore> core_;
100 DISALLOW_COPY_AND_ASSIGN(IpcSharedBuffer);
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 pending_capture_frame_requests_(0),
118 is_desktop_session_connected_(false),
119 virtual_terminal_(virtual_terminal) {
120 DCHECK(caller_task_runner_->BelongsToCurrentThread());
123 scoped_ptr<AudioCapturer> DesktopSessionProxy::CreateAudioCapturer() {
124 DCHECK(caller_task_runner_->BelongsToCurrentThread());
126 return make_scoped_ptr(new IpcAudioCapturer(this));
129 scoped_ptr<InputInjector> DesktopSessionProxy::CreateInputInjector() {
130 DCHECK(caller_task_runner_->BelongsToCurrentThread());
132 return make_scoped_ptr(new IpcInputInjector(this));
135 scoped_ptr<ScreenControls> DesktopSessionProxy::CreateScreenControls() {
136 DCHECK(caller_task_runner_->BelongsToCurrentThread());
138 return make_scoped_ptr(new IpcScreenControls(this));
141 scoped_ptr<webrtc::DesktopCapturer> DesktopSessionProxy::CreateVideoCapturer() {
142 DCHECK(caller_task_runner_->BelongsToCurrentThread());
144 return make_scoped_ptr(new IpcVideoFrameCapturer(this));
147 scoped_ptr<webrtc::MouseCursorMonitor>
148 DesktopSessionProxy::CreateMouseCursorMonitor() {
149 return make_scoped_ptr(new IpcMouseCursorMonitor(this));
152 std::string DesktopSessionProxy::GetCapabilities() const {
153 std::string result = kRateLimitResizeRequests;
154 // Ask the client to send its resolution unconditionally.
155 if (virtual_terminal_)
156 result = result + " " + kSendInitialResolution;
157 return result;
160 void DesktopSessionProxy::SetCapabilities(const std::string& capabilities) {
161 // Delay creation of the desktop session until the client screen resolution is
162 // received if the desktop session requires the initial screen resolution
163 // (when |virtual_terminal_| is true) and the client is expected to
164 // sent its screen resolution (the 'sendInitialResolution' capability is
165 // supported).
166 if (virtual_terminal_ &&
167 HasCapability(capabilities, kSendInitialResolution)) {
168 VLOG(1) << "Waiting for the client screen resolution.";
169 return;
172 // Connect to the desktop session.
173 if (!is_desktop_session_connected_) {
174 is_desktop_session_connected_ = true;
175 if (desktop_session_connector_.get()) {
176 desktop_session_connector_->ConnectTerminal(
177 this, screen_resolution_, virtual_terminal_);
182 bool DesktopSessionProxy::OnMessageReceived(const IPC::Message& message) {
183 DCHECK(caller_task_runner_->BelongsToCurrentThread());
185 bool handled = true;
186 IPC_BEGIN_MESSAGE_MAP(DesktopSessionProxy, message)
187 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_AudioPacket,
188 OnAudioPacket)
189 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_CaptureCompleted,
190 OnCaptureCompleted)
191 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_MouseCursor,
192 OnMouseCursor)
193 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_CreateSharedBuffer,
194 OnCreateSharedBuffer)
195 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_ReleaseSharedBuffer,
196 OnReleaseSharedBuffer)
197 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_InjectClipboardEvent,
198 OnInjectClipboardEvent)
199 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_DisconnectSession,
200 DisconnectSession);
201 IPC_END_MESSAGE_MAP()
203 CHECK(handled) << "Received unexpected IPC type: " << message.type();
204 return handled;
207 void DesktopSessionProxy::OnChannelConnected(int32 peer_pid) {
208 DCHECK(caller_task_runner_->BelongsToCurrentThread());
210 VLOG(1) << "IPC: network <- desktop (" << peer_pid << ")";
213 void DesktopSessionProxy::OnChannelError() {
214 DCHECK(caller_task_runner_->BelongsToCurrentThread());
216 DetachFromDesktop();
219 bool DesktopSessionProxy::AttachToDesktop(
220 base::Process desktop_process,
221 IPC::PlatformFileForTransit desktop_pipe) {
222 DCHECK(caller_task_runner_->BelongsToCurrentThread());
223 DCHECK(!desktop_channel_);
224 DCHECK(!desktop_process_.IsValid());
226 // Ignore the attach notification if the client session has been disconnected
227 // already.
228 if (!client_session_control_.get())
229 return false;
231 desktop_process_ = desktop_process.Pass();
233 #if defined(OS_WIN)
234 // On Windows: |desktop_process| is a valid handle, but |desktop_pipe| needs
235 // to be duplicated from the desktop process.
236 HANDLE temp_handle;
237 if (!DuplicateHandle(desktop_process_.Handle(), desktop_pipe,
238 GetCurrentProcess(), &temp_handle, 0,
239 FALSE, DUPLICATE_SAME_ACCESS)) {
240 PLOG(ERROR) << "Failed to duplicate the desktop-to-network pipe handle";
242 desktop_process_.Close();
243 return false;
245 base::win::ScopedHandle pipe(temp_handle);
247 IPC::ChannelHandle desktop_channel_handle(pipe.Get());
249 #elif defined(OS_POSIX)
250 // On posix: |desktop_pipe| is a valid file descriptor.
251 DCHECK(desktop_pipe.auto_close);
253 IPC::ChannelHandle desktop_channel_handle(std::string(), desktop_pipe);
255 #else
256 #error Unsupported platform.
257 #endif
259 // Connect to the desktop process.
260 desktop_channel_ = IPC::ChannelProxy::Create(desktop_channel_handle,
261 IPC::Channel::MODE_CLIENT,
262 this,
263 io_task_runner_.get());
265 // Pass ID of the client (which is authenticated at this point) to the desktop
266 // session agent and start the agent.
267 SendToDesktop(new ChromotingNetworkDesktopMsg_StartSessionAgent(
268 client_session_control_->client_jid(),
269 screen_resolution_,
270 virtual_terminal_));
272 return true;
275 void DesktopSessionProxy::DetachFromDesktop() {
276 DCHECK(caller_task_runner_->BelongsToCurrentThread());
278 desktop_channel_.reset();
280 if (desktop_process_.IsValid())
281 desktop_process_.Close();
283 shared_buffers_.clear();
285 // Generate fake responses to keep the video capturer in sync.
286 while (pending_capture_frame_requests_) {
287 --pending_capture_frame_requests_;
288 PostCaptureCompleted(nullptr);
292 void DesktopSessionProxy::SetAudioCapturer(
293 const base::WeakPtr<IpcAudioCapturer>& audio_capturer) {
294 DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
296 audio_capturer_ = audio_capturer;
299 void DesktopSessionProxy::CaptureFrame() {
300 if (!caller_task_runner_->BelongsToCurrentThread()) {
301 caller_task_runner_->PostTask(
302 FROM_HERE, base::Bind(&DesktopSessionProxy::CaptureFrame, this));
303 return;
306 if (desktop_channel_) {
307 ++pending_capture_frame_requests_;
308 SendToDesktop(new ChromotingNetworkDesktopMsg_CaptureFrame());
309 } else {
310 PostCaptureCompleted(nullptr);
314 void DesktopSessionProxy::SetVideoCapturer(
315 const base::WeakPtr<IpcVideoFrameCapturer> video_capturer) {
316 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
318 video_capturer_ = video_capturer;
321 void DesktopSessionProxy::SetMouseCursorMonitor(
322 const base::WeakPtr<IpcMouseCursorMonitor>& mouse_cursor_monitor) {
323 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
325 mouse_cursor_monitor_ = mouse_cursor_monitor;
328 void DesktopSessionProxy::DisconnectSession() {
329 DCHECK(caller_task_runner_->BelongsToCurrentThread());
331 // Disconnect the client session if it hasn't been disconnected yet.
332 if (client_session_control_.get())
333 client_session_control_->DisconnectSession();
336 void DesktopSessionProxy::InjectClipboardEvent(
337 const protocol::ClipboardEvent& event) {
338 DCHECK(caller_task_runner_->BelongsToCurrentThread());
340 std::string serialized_event;
341 if (!event.SerializeToString(&serialized_event)) {
342 LOG(ERROR) << "Failed to serialize protocol::ClipboardEvent.";
343 return;
346 SendToDesktop(
347 new ChromotingNetworkDesktopMsg_InjectClipboardEvent(serialized_event));
350 void DesktopSessionProxy::InjectKeyEvent(const protocol::KeyEvent& event) {
351 DCHECK(caller_task_runner_->BelongsToCurrentThread());
353 std::string serialized_event;
354 if (!event.SerializeToString(&serialized_event)) {
355 LOG(ERROR) << "Failed to serialize protocol::KeyEvent.";
356 return;
359 SendToDesktop(
360 new ChromotingNetworkDesktopMsg_InjectKeyEvent(serialized_event));
363 void DesktopSessionProxy::InjectTextEvent(const protocol::TextEvent& event) {
364 DCHECK(caller_task_runner_->BelongsToCurrentThread());
366 std::string serialized_event;
367 if (!event.SerializeToString(&serialized_event)) {
368 LOG(ERROR) << "Failed to serialize protocol::TextEvent.";
369 return;
372 SendToDesktop(
373 new ChromotingNetworkDesktopMsg_InjectTextEvent(serialized_event));
376 void DesktopSessionProxy::InjectMouseEvent(const protocol::MouseEvent& event) {
377 DCHECK(caller_task_runner_->BelongsToCurrentThread());
379 std::string serialized_event;
380 if (!event.SerializeToString(&serialized_event)) {
381 LOG(ERROR) << "Failed to serialize protocol::MouseEvent.";
382 return;
385 SendToDesktop(
386 new ChromotingNetworkDesktopMsg_InjectMouseEvent(serialized_event));
389 void DesktopSessionProxy::StartInputInjector(
390 scoped_ptr<protocol::ClipboardStub> client_clipboard) {
391 DCHECK(caller_task_runner_->BelongsToCurrentThread());
393 client_clipboard_ = client_clipboard.Pass();
396 void DesktopSessionProxy::SetScreenResolution(
397 const ScreenResolution& resolution) {
398 DCHECK(caller_task_runner_->BelongsToCurrentThread());
400 if (resolution.IsEmpty())
401 return;
403 screen_resolution_ = resolution;
405 // Connect to the desktop session if it is not done yet.
406 if (!is_desktop_session_connected_) {
407 is_desktop_session_connected_ = true;
408 if (desktop_session_connector_.get()) {
409 desktop_session_connector_->ConnectTerminal(
410 this, screen_resolution_, virtual_terminal_);
412 return;
415 // Pass the client's resolution to both daemon and desktop session agent.
416 // Depending on the session kind the screen resolution can be set by either
417 // the daemon (for example RDP sessions on Windows) or by the desktop session
418 // agent (when sharing the physical console).
419 if (desktop_session_connector_.get())
420 desktop_session_connector_->SetScreenResolution(this, screen_resolution_);
421 SendToDesktop(
422 new ChromotingNetworkDesktopMsg_SetScreenResolution(screen_resolution_));
425 DesktopSessionProxy::~DesktopSessionProxy() {
426 DCHECK(caller_task_runner_->BelongsToCurrentThread());
428 if (desktop_session_connector_.get() && is_desktop_session_connected_)
429 desktop_session_connector_->DisconnectTerminal(this);
432 scoped_refptr<DesktopSessionProxy::IpcSharedBufferCore>
433 DesktopSessionProxy::GetSharedBufferCore(int id) {
434 DCHECK(caller_task_runner_->BelongsToCurrentThread());
436 SharedBuffers::const_iterator i = shared_buffers_.find(id);
437 if (i != shared_buffers_.end()) {
438 return i->second;
439 } else {
440 LOG(ERROR) << "Failed to find the shared buffer " << id;
441 return nullptr;
445 void DesktopSessionProxy::OnAudioPacket(const std::string& serialized_packet) {
446 DCHECK(caller_task_runner_->BelongsToCurrentThread());
448 // Parse a serialized audio packet. No further validation is done since
449 // the message was sent by more privileged process.
450 scoped_ptr<AudioPacket> packet(new AudioPacket());
451 if (!packet->ParseFromString(serialized_packet)) {
452 LOG(ERROR) << "Failed to parse AudioPacket.";
453 return;
456 // Pass a captured audio packet to |audio_capturer_|.
457 audio_capture_task_runner_->PostTask(
458 FROM_HERE, base::Bind(&IpcAudioCapturer::OnAudioPacket, audio_capturer_,
459 base::Passed(&packet)));
462 void DesktopSessionProxy::OnCreateSharedBuffer(
463 int id,
464 IPC::PlatformFileForTransit handle,
465 uint32 size) {
466 DCHECK(caller_task_runner_->BelongsToCurrentThread());
468 scoped_refptr<IpcSharedBufferCore> shared_buffer =
469 new IpcSharedBufferCore(id, handle, desktop_process_.Handle(), size);
471 if (shared_buffer->memory() != nullptr &&
472 !shared_buffers_.insert(std::make_pair(id, shared_buffer)).second) {
473 LOG(ERROR) << "Duplicate shared buffer id " << id << " encountered";
477 void DesktopSessionProxy::OnReleaseSharedBuffer(int id) {
478 DCHECK(caller_task_runner_->BelongsToCurrentThread());
480 // Drop the cached reference to the buffer.
481 shared_buffers_.erase(id);
484 void DesktopSessionProxy::OnCaptureCompleted(
485 const SerializedDesktopFrame& serialized_frame) {
486 DCHECK(caller_task_runner_->BelongsToCurrentThread());
488 // Assume that |serialized_frame| is well-formed because it was received from
489 // a more privileged process.
490 scoped_refptr<IpcSharedBufferCore> shared_buffer_core =
491 GetSharedBufferCore(serialized_frame.shared_buffer_id);
492 CHECK(shared_buffer_core.get());
494 scoped_ptr<webrtc::DesktopFrame> frame(
495 new webrtc::SharedMemoryDesktopFrame(
496 serialized_frame.dimensions, serialized_frame.bytes_per_row,
497 new IpcSharedBuffer(shared_buffer_core)));
498 frame->set_capture_time_ms(serialized_frame.capture_time_ms);
499 frame->set_dpi(serialized_frame.dpi);
501 for (size_t i = 0; i < serialized_frame.dirty_region.size(); ++i) {
502 frame->mutable_updated_region()->AddRect(serialized_frame.dirty_region[i]);
505 --pending_capture_frame_requests_;
506 PostCaptureCompleted(frame.Pass());
509 void DesktopSessionProxy::OnMouseCursor(
510 const webrtc::MouseCursor& mouse_cursor) {
511 DCHECK(caller_task_runner_->BelongsToCurrentThread());
512 PostMouseCursor(make_scoped_ptr(webrtc::MouseCursor::CopyOf(mouse_cursor)));
515 void DesktopSessionProxy::OnInjectClipboardEvent(
516 const std::string& serialized_event) {
517 DCHECK(caller_task_runner_->BelongsToCurrentThread());
519 if (client_clipboard_) {
520 protocol::ClipboardEvent event;
521 if (!event.ParseFromString(serialized_event)) {
522 LOG(ERROR) << "Failed to parse protocol::ClipboardEvent.";
523 return;
526 client_clipboard_->InjectClipboardEvent(event);
530 void DesktopSessionProxy::PostCaptureCompleted(
531 scoped_ptr<webrtc::DesktopFrame> frame) {
532 DCHECK(caller_task_runner_->BelongsToCurrentThread());
534 video_capture_task_runner_->PostTask(
535 FROM_HERE,
536 base::Bind(&IpcVideoFrameCapturer::OnCaptureCompleted, video_capturer_,
537 base::Passed(&frame)));
540 void DesktopSessionProxy::PostMouseCursor(
541 scoped_ptr<webrtc::MouseCursor> mouse_cursor) {
542 DCHECK(caller_task_runner_->BelongsToCurrentThread());
544 video_capture_task_runner_->PostTask(
545 FROM_HERE,
546 base::Bind(&IpcMouseCursorMonitor::OnMouseCursor, mouse_cursor_monitor_,
547 base::Passed(&mouse_cursor)));
550 void DesktopSessionProxy::SendToDesktop(IPC::Message* message) {
551 DCHECK(caller_task_runner_->BelongsToCurrentThread());
553 if (desktop_channel_) {
554 desktop_channel_->Send(message);
555 } else {
556 delete message;
560 // static
561 void DesktopSessionProxyTraits::Destruct(
562 const DesktopSessionProxy* desktop_session_proxy) {
563 desktop_session_proxy->caller_task_runner_->DeleteSoon(FROM_HERE,
564 desktop_session_proxy);
567 } // namespace remoting