Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / remoting / host / desktop_session_proxy.cc
blob4cda02a53e9789d31ddd105996e70915e89982ce
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 desktop_process_(base::kNullProcessHandle),
118 pending_capture_frame_requests_(0),
119 is_desktop_session_connected_(false),
120 virtual_terminal_(virtual_terminal) {
121 DCHECK(caller_task_runner_->BelongsToCurrentThread());
124 scoped_ptr<AudioCapturer> DesktopSessionProxy::CreateAudioCapturer() {
125 DCHECK(caller_task_runner_->BelongsToCurrentThread());
127 return scoped_ptr<AudioCapturer>(new IpcAudioCapturer(this));
130 scoped_ptr<InputInjector> DesktopSessionProxy::CreateInputInjector() {
131 DCHECK(caller_task_runner_->BelongsToCurrentThread());
133 return scoped_ptr<InputInjector>(new IpcInputInjector(this));
136 scoped_ptr<ScreenControls> DesktopSessionProxy::CreateScreenControls() {
137 DCHECK(caller_task_runner_->BelongsToCurrentThread());
139 return scoped_ptr<ScreenControls>(new IpcScreenControls(this));
142 scoped_ptr<webrtc::DesktopCapturer> DesktopSessionProxy::CreateVideoCapturer() {
143 DCHECK(caller_task_runner_->BelongsToCurrentThread());
145 return scoped_ptr<webrtc::DesktopCapturer>(new IpcVideoFrameCapturer(this));
148 scoped_ptr<webrtc::MouseCursorMonitor>
149 DesktopSessionProxy::CreateMouseCursorMonitor() {
150 return scoped_ptr<webrtc::MouseCursorMonitor>(
151 new IpcMouseCursorMonitor(this));
154 std::string DesktopSessionProxy::GetCapabilities() const {
155 std::string result = kRateLimitResizeRequests;
156 // Ask the client to send its resolution unconditionally.
157 if (virtual_terminal_)
158 result = result + " " + kSendInitialResolution;
159 return result;
162 void DesktopSessionProxy::SetCapabilities(const std::string& capabilities) {
163 // Delay creation of the desktop session until the client screen resolution is
164 // received if the desktop session requires the initial screen resolution
165 // (when |virtual_terminal_| is true) and the client is expected to
166 // sent its screen resolution (the 'sendInitialResolution' capability is
167 // supported).
168 if (virtual_terminal_ &&
169 HasCapability(capabilities, kSendInitialResolution)) {
170 VLOG(1) << "Waiting for the client screen resolution.";
171 return;
174 // Connect to the desktop session.
175 if (!is_desktop_session_connected_) {
176 is_desktop_session_connected_ = true;
177 if (desktop_session_connector_.get()) {
178 desktop_session_connector_->ConnectTerminal(
179 this, screen_resolution_, virtual_terminal_);
184 bool DesktopSessionProxy::OnMessageReceived(const IPC::Message& message) {
185 DCHECK(caller_task_runner_->BelongsToCurrentThread());
187 bool handled = true;
188 IPC_BEGIN_MESSAGE_MAP(DesktopSessionProxy, message)
189 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_AudioPacket,
190 OnAudioPacket)
191 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_CaptureCompleted,
192 OnCaptureCompleted)
193 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_MouseCursor,
194 OnMouseCursor)
195 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_CreateSharedBuffer,
196 OnCreateSharedBuffer)
197 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_ReleaseSharedBuffer,
198 OnReleaseSharedBuffer)
199 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_InjectClipboardEvent,
200 OnInjectClipboardEvent)
201 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_DisconnectSession,
202 DisconnectSession);
203 IPC_END_MESSAGE_MAP()
205 CHECK(handled) << "Received unexpected IPC type: " << message.type();
206 return handled;
209 void DesktopSessionProxy::OnChannelConnected(int32 peer_pid) {
210 DCHECK(caller_task_runner_->BelongsToCurrentThread());
212 VLOG(1) << "IPC: network <- desktop (" << peer_pid << ")";
215 void DesktopSessionProxy::OnChannelError() {
216 DCHECK(caller_task_runner_->BelongsToCurrentThread());
218 DetachFromDesktop();
221 bool DesktopSessionProxy::AttachToDesktop(
222 base::ProcessHandle desktop_process,
223 IPC::PlatformFileForTransit desktop_pipe) {
224 DCHECK(caller_task_runner_->BelongsToCurrentThread());
225 DCHECK(!desktop_channel_);
226 DCHECK_EQ(desktop_process_, base::kNullProcessHandle);
228 // Ignore the attach notification if the client session has been disconnected
229 // already.
230 if (!client_session_control_.get()) {
231 base::CloseProcessHandle(desktop_process);
232 return false;
235 desktop_process_ = desktop_process;
237 #if defined(OS_WIN)
238 // On Windows: |desktop_process| is a valid handle, but |desktop_pipe| needs
239 // to be duplicated from the desktop process.
240 HANDLE temp_handle;
241 if (!DuplicateHandle(desktop_process_, desktop_pipe, GetCurrentProcess(),
242 &temp_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
243 PLOG(ERROR) << "Failed to duplicate the desktop-to-network pipe handle";
245 desktop_process_ = base::kNullProcessHandle;
246 base::CloseProcessHandle(desktop_process);
247 return false;
249 base::win::ScopedHandle pipe(temp_handle);
251 IPC::ChannelHandle desktop_channel_handle(pipe);
253 #elif defined(OS_POSIX)
254 // On posix: |desktop_pipe| is a valid file descriptor.
255 DCHECK(desktop_pipe.auto_close);
257 IPC::ChannelHandle desktop_channel_handle(std::string(), desktop_pipe);
259 #else
260 #error Unsupported platform.
261 #endif
263 // Connect to the desktop process.
264 desktop_channel_ = IPC::ChannelProxy::Create(desktop_channel_handle,
265 IPC::Channel::MODE_CLIENT,
266 this,
267 io_task_runner_.get());
269 // Pass ID of the client (which is authenticated at this point) to the desktop
270 // session agent and start the agent.
271 SendToDesktop(new ChromotingNetworkDesktopMsg_StartSessionAgent(
272 client_session_control_->client_jid(),
273 screen_resolution_,
274 virtual_terminal_));
276 return true;
279 void DesktopSessionProxy::DetachFromDesktop() {
280 DCHECK(caller_task_runner_->BelongsToCurrentThread());
282 desktop_channel_.reset();
284 if (desktop_process_ != base::kNullProcessHandle) {
285 base::CloseProcessHandle(desktop_process_);
286 desktop_process_ = base::kNullProcessHandle;
289 shared_buffers_.clear();
291 // Generate fake responses to keep the video capturer in sync.
292 while (pending_capture_frame_requests_) {
293 --pending_capture_frame_requests_;
294 PostCaptureCompleted(scoped_ptr<webrtc::DesktopFrame>());
298 void DesktopSessionProxy::SetAudioCapturer(
299 const base::WeakPtr<IpcAudioCapturer>& audio_capturer) {
300 DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
302 audio_capturer_ = audio_capturer;
305 void DesktopSessionProxy::CaptureFrame() {
306 if (!caller_task_runner_->BelongsToCurrentThread()) {
307 caller_task_runner_->PostTask(
308 FROM_HERE, base::Bind(&DesktopSessionProxy::CaptureFrame, this));
309 return;
312 if (desktop_channel_) {
313 ++pending_capture_frame_requests_;
314 SendToDesktop(new ChromotingNetworkDesktopMsg_CaptureFrame());
315 } else {
316 PostCaptureCompleted(scoped_ptr<webrtc::DesktopFrame>());
320 void DesktopSessionProxy::SetVideoCapturer(
321 const base::WeakPtr<IpcVideoFrameCapturer> video_capturer) {
322 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
324 video_capturer_ = video_capturer;
327 void DesktopSessionProxy::SetMouseCursorMonitor(
328 const base::WeakPtr<IpcMouseCursorMonitor>& mouse_cursor_monitor) {
329 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
331 mouse_cursor_monitor_ = mouse_cursor_monitor;
334 void DesktopSessionProxy::DisconnectSession() {
335 DCHECK(caller_task_runner_->BelongsToCurrentThread());
337 // Disconnect the client session if it hasn't been disconnected yet.
338 if (client_session_control_.get())
339 client_session_control_->DisconnectSession();
342 void DesktopSessionProxy::InjectClipboardEvent(
343 const protocol::ClipboardEvent& event) {
344 DCHECK(caller_task_runner_->BelongsToCurrentThread());
346 std::string serialized_event;
347 if (!event.SerializeToString(&serialized_event)) {
348 LOG(ERROR) << "Failed to serialize protocol::ClipboardEvent.";
349 return;
352 SendToDesktop(
353 new ChromotingNetworkDesktopMsg_InjectClipboardEvent(serialized_event));
356 void DesktopSessionProxy::InjectKeyEvent(const protocol::KeyEvent& event) {
357 DCHECK(caller_task_runner_->BelongsToCurrentThread());
359 std::string serialized_event;
360 if (!event.SerializeToString(&serialized_event)) {
361 LOG(ERROR) << "Failed to serialize protocol::KeyEvent.";
362 return;
365 SendToDesktop(
366 new ChromotingNetworkDesktopMsg_InjectKeyEvent(serialized_event));
369 void DesktopSessionProxy::InjectTextEvent(const protocol::TextEvent& event) {
370 DCHECK(caller_task_runner_->BelongsToCurrentThread());
372 std::string serialized_event;
373 if (!event.SerializeToString(&serialized_event)) {
374 LOG(ERROR) << "Failed to serialize protocol::TextEvent.";
375 return;
378 SendToDesktop(
379 new ChromotingNetworkDesktopMsg_InjectTextEvent(serialized_event));
382 void DesktopSessionProxy::InjectMouseEvent(const protocol::MouseEvent& event) {
383 DCHECK(caller_task_runner_->BelongsToCurrentThread());
385 std::string serialized_event;
386 if (!event.SerializeToString(&serialized_event)) {
387 LOG(ERROR) << "Failed to serialize protocol::MouseEvent.";
388 return;
391 SendToDesktop(
392 new ChromotingNetworkDesktopMsg_InjectMouseEvent(serialized_event));
395 void DesktopSessionProxy::StartInputInjector(
396 scoped_ptr<protocol::ClipboardStub> client_clipboard) {
397 DCHECK(caller_task_runner_->BelongsToCurrentThread());
399 client_clipboard_ = client_clipboard.Pass();
402 void DesktopSessionProxy::SetScreenResolution(
403 const ScreenResolution& resolution) {
404 DCHECK(caller_task_runner_->BelongsToCurrentThread());
406 if (resolution.IsEmpty())
407 return;
409 screen_resolution_ = resolution;
411 // Connect to the desktop session if it is not done yet.
412 if (!is_desktop_session_connected_) {
413 is_desktop_session_connected_ = true;
414 if (desktop_session_connector_.get()) {
415 desktop_session_connector_->ConnectTerminal(
416 this, screen_resolution_, virtual_terminal_);
418 return;
421 // Pass the client's resolution to both daemon and desktop session agent.
422 // Depending on the session kind the screen resolution can be set by either
423 // the daemon (for example RDP sessions on Windows) or by the desktop session
424 // agent (when sharing the physical console).
425 if (desktop_session_connector_.get())
426 desktop_session_connector_->SetScreenResolution(this, screen_resolution_);
427 SendToDesktop(
428 new ChromotingNetworkDesktopMsg_SetScreenResolution(screen_resolution_));
431 DesktopSessionProxy::~DesktopSessionProxy() {
432 DCHECK(caller_task_runner_->BelongsToCurrentThread());
434 if (desktop_session_connector_.get() && is_desktop_session_connected_)
435 desktop_session_connector_->DisconnectTerminal(this);
437 if (desktop_process_ != base::kNullProcessHandle) {
438 base::CloseProcessHandle(desktop_process_);
439 desktop_process_ = base::kNullProcessHandle;
443 scoped_refptr<DesktopSessionProxy::IpcSharedBufferCore>
444 DesktopSessionProxy::GetSharedBufferCore(int id) {
445 DCHECK(caller_task_runner_->BelongsToCurrentThread());
447 SharedBuffers::const_iterator i = shared_buffers_.find(id);
448 if (i != shared_buffers_.end()) {
449 return i->second;
450 } else {
451 LOG(ERROR) << "Failed to find the shared buffer " << id;
452 return NULL;
456 void DesktopSessionProxy::OnAudioPacket(const std::string& serialized_packet) {
457 DCHECK(caller_task_runner_->BelongsToCurrentThread());
459 // Parse a serialized audio packet. No further validation is done since
460 // the message was sent by more privileged process.
461 scoped_ptr<AudioPacket> packet(new AudioPacket());
462 if (!packet->ParseFromString(serialized_packet)) {
463 LOG(ERROR) << "Failed to parse AudioPacket.";
464 return;
467 // Pass a captured audio packet to |audio_capturer_|.
468 audio_capture_task_runner_->PostTask(
469 FROM_HERE, base::Bind(&IpcAudioCapturer::OnAudioPacket, audio_capturer_,
470 base::Passed(&packet)));
473 void DesktopSessionProxy::OnCreateSharedBuffer(
474 int id,
475 IPC::PlatformFileForTransit handle,
476 uint32 size) {
477 DCHECK(caller_task_runner_->BelongsToCurrentThread());
479 scoped_refptr<IpcSharedBufferCore> shared_buffer =
480 new IpcSharedBufferCore(id, handle, desktop_process_, size);
482 if (shared_buffer->memory() != NULL &&
483 !shared_buffers_.insert(std::make_pair(id, shared_buffer)).second) {
484 LOG(ERROR) << "Duplicate shared buffer id " << id << " encountered";
488 void DesktopSessionProxy::OnReleaseSharedBuffer(int id) {
489 DCHECK(caller_task_runner_->BelongsToCurrentThread());
491 // Drop the cached reference to the buffer.
492 shared_buffers_.erase(id);
495 void DesktopSessionProxy::OnCaptureCompleted(
496 const SerializedDesktopFrame& serialized_frame) {
497 DCHECK(caller_task_runner_->BelongsToCurrentThread());
499 // Assume that |serialized_frame| is well-formed because it was received from
500 // a more privileged process.
501 scoped_refptr<IpcSharedBufferCore> shared_buffer_core =
502 GetSharedBufferCore(serialized_frame.shared_buffer_id);
503 CHECK(shared_buffer_core.get());
505 scoped_ptr<webrtc::DesktopFrame> frame(
506 new webrtc::SharedMemoryDesktopFrame(
507 serialized_frame.dimensions, serialized_frame.bytes_per_row,
508 new IpcSharedBuffer(shared_buffer_core)));
509 frame->set_capture_time_ms(serialized_frame.capture_time_ms);
510 frame->set_dpi(serialized_frame.dpi);
512 for (size_t i = 0; i < serialized_frame.dirty_region.size(); ++i) {
513 frame->mutable_updated_region()->AddRect(serialized_frame.dirty_region[i]);
516 --pending_capture_frame_requests_;
517 PostCaptureCompleted(frame.Pass());
520 void DesktopSessionProxy::OnMouseCursor(
521 const webrtc::MouseCursor& mouse_cursor) {
522 DCHECK(caller_task_runner_->BelongsToCurrentThread());
523 scoped_ptr<webrtc::MouseCursor> cursor(
524 webrtc::MouseCursor::CopyOf(mouse_cursor));
525 PostMouseCursor(cursor.Pass());
528 void DesktopSessionProxy::OnInjectClipboardEvent(
529 const std::string& serialized_event) {
530 DCHECK(caller_task_runner_->BelongsToCurrentThread());
532 if (client_clipboard_) {
533 protocol::ClipboardEvent event;
534 if (!event.ParseFromString(serialized_event)) {
535 LOG(ERROR) << "Failed to parse protocol::ClipboardEvent.";
536 return;
539 client_clipboard_->InjectClipboardEvent(event);
543 void DesktopSessionProxy::PostCaptureCompleted(
544 scoped_ptr<webrtc::DesktopFrame> frame) {
545 DCHECK(caller_task_runner_->BelongsToCurrentThread());
547 video_capture_task_runner_->PostTask(
548 FROM_HERE,
549 base::Bind(&IpcVideoFrameCapturer::OnCaptureCompleted, video_capturer_,
550 base::Passed(&frame)));
553 void DesktopSessionProxy::PostMouseCursor(
554 scoped_ptr<webrtc::MouseCursor> mouse_cursor) {
555 DCHECK(caller_task_runner_->BelongsToCurrentThread());
557 video_capture_task_runner_->PostTask(
558 FROM_HERE,
559 base::Bind(&IpcMouseCursorMonitor::OnMouseCursor, mouse_cursor_monitor_,
560 base::Passed(&mouse_cursor)));
563 void DesktopSessionProxy::SendToDesktop(IPC::Message* message) {
564 DCHECK(caller_task_runner_->BelongsToCurrentThread());
566 if (desktop_channel_) {
567 desktop_channel_->Send(message);
568 } else {
569 delete message;
573 // static
574 void DesktopSessionProxyTraits::Destruct(
575 const DesktopSessionProxy* desktop_session_proxy) {
576 desktop_session_proxy->caller_task_runner_->DeleteSoon(FROM_HERE,
577 desktop_session_proxy);
580 } // namespace remoting