Revert 264226 "Reduce dependency of TiclInvalidationService on P..."
[chromium-blink-merge.git] / remoting / host / desktop_session_proxy.cc
blobd077d5a62dd62fe2e2f06e0753f9cc725ddceee0
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/platform_file.h"
10 #include "base/process/process_handle.h"
11 #include "base/memory/shared_memory.h"
12 #include "base/single_thread_task_runner.h"
13 #include "ipc/ipc_channel_proxy.h"
14 #include "ipc/ipc_message_macros.h"
15 #include "remoting/base/capabilities.h"
16 #include "remoting/host/chromoting_messages.h"
17 #include "remoting/host/client_session.h"
18 #include "remoting/host/client_session_control.h"
19 #include "remoting/host/desktop_session_connector.h"
20 #include "remoting/host/ipc_audio_capturer.h"
21 #include "remoting/host/ipc_input_injector.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/shared_memory.h"
31 #if defined(OS_WIN)
32 #include "base/win/scoped_handle.h"
33 #endif // defined(OS_WIN)
35 const bool kReadOnly = true;
36 const char kSendInitialResolution[] = "sendInitialResolution";
37 const char kRateLimitResizeRequests[] = "rateLimitResizeRequests";
39 namespace remoting {
41 class DesktopSessionProxy::IpcSharedBufferCore
42 : public base::RefCountedThreadSafe<IpcSharedBufferCore> {
43 public:
44 IpcSharedBufferCore(int id,
45 base::SharedMemoryHandle handle,
46 base::ProcessHandle process,
47 size_t size)
48 : id_(id),
49 #if defined(OS_WIN)
50 shared_memory_(handle, kReadOnly, process),
51 #else // !defined(OS_WIN)
52 shared_memory_(handle, kReadOnly),
53 #endif // !defined(OS_WIN)
54 size_(size) {
55 if (!shared_memory_.Map(size)) {
56 LOG(ERROR) << "Failed to map a shared buffer: id=" << id
57 #if defined(OS_WIN)
58 << ", handle=" << handle
59 #else
60 << ", handle.fd=" << handle.fd
61 #endif
62 << ", size=" << size;
66 int id() { return id_; }
67 size_t size() { return size_; }
68 void* memory() { return shared_memory_.memory(); }
69 webrtc::SharedMemory::Handle handle() {
70 #if defined(OS_WIN)
71 return shared_memory_.handle();
72 #else
73 return shared_memory_.handle().fd;
74 #endif
77 private:
78 virtual ~IpcSharedBufferCore() {}
79 friend class base::RefCountedThreadSafe<IpcSharedBufferCore>;
81 int id_;
82 base::SharedMemory shared_memory_;
83 size_t size_;
85 DISALLOW_COPY_AND_ASSIGN(IpcSharedBufferCore);
88 class DesktopSessionProxy::IpcSharedBuffer : public webrtc::SharedMemory {
89 public:
90 IpcSharedBuffer(scoped_refptr<IpcSharedBufferCore> core)
91 : SharedMemory(core->memory(), core->size(),
92 core->handle(), core->id()),
93 core_(core) {
96 private:
97 scoped_refptr<IpcSharedBufferCore> core_;
99 DISALLOW_COPY_AND_ASSIGN(IpcSharedBuffer);
102 DesktopSessionProxy::DesktopSessionProxy(
103 scoped_refptr<base::SingleThreadTaskRunner> audio_capture_task_runner,
104 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
105 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
106 scoped_refptr<base::SingleThreadTaskRunner> video_capture_task_runner,
107 base::WeakPtr<ClientSessionControl> client_session_control,
108 base::WeakPtr<DesktopSessionConnector> desktop_session_connector,
109 bool virtual_terminal)
110 : audio_capture_task_runner_(audio_capture_task_runner),
111 caller_task_runner_(caller_task_runner),
112 io_task_runner_(io_task_runner),
113 video_capture_task_runner_(video_capture_task_runner),
114 client_session_control_(client_session_control),
115 desktop_session_connector_(desktop_session_connector),
116 desktop_process_(base::kNullProcessHandle),
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 scoped_ptr<AudioCapturer>(new IpcAudioCapturer(this));
129 scoped_ptr<InputInjector> DesktopSessionProxy::CreateInputInjector() {
130 DCHECK(caller_task_runner_->BelongsToCurrentThread());
132 return scoped_ptr<InputInjector>(new IpcInputInjector(this));
135 scoped_ptr<ScreenControls> DesktopSessionProxy::CreateScreenControls() {
136 DCHECK(caller_task_runner_->BelongsToCurrentThread());
138 return scoped_ptr<ScreenControls>(new IpcScreenControls(this));
141 scoped_ptr<webrtc::ScreenCapturer> DesktopSessionProxy::CreateVideoCapturer() {
142 DCHECK(caller_task_runner_->BelongsToCurrentThread());
144 return scoped_ptr<webrtc::ScreenCapturer>(new IpcVideoFrameCapturer(this));
147 std::string DesktopSessionProxy::GetCapabilities() const {
148 std::string result = kRateLimitResizeRequests;
149 // Ask the client to send its resolution unconditionally.
150 if (virtual_terminal_)
151 result = result + " " + kSendInitialResolution;
152 return result;
155 void DesktopSessionProxy::SetCapabilities(const std::string& capabilities) {
156 // Delay creation of the desktop session until the client screen resolution is
157 // received if the desktop session requires the initial screen resolution
158 // (when |virtual_terminal_| is true) and the client is expected to
159 // sent its screen resolution (the 'sendInitialResolution' capability is
160 // supported).
161 if (virtual_terminal_ &&
162 HasCapability(capabilities, kSendInitialResolution)) {
163 VLOG(1) << "Waiting for the client screen resolution.";
164 return;
167 // Connect to the desktop session.
168 if (!is_desktop_session_connected_) {
169 is_desktop_session_connected_ = true;
170 if (desktop_session_connector_.get()) {
171 desktop_session_connector_->ConnectTerminal(
172 this, screen_resolution_, virtual_terminal_);
177 bool DesktopSessionProxy::OnMessageReceived(const IPC::Message& message) {
178 DCHECK(caller_task_runner_->BelongsToCurrentThread());
180 bool handled = true;
181 IPC_BEGIN_MESSAGE_MAP(DesktopSessionProxy, message)
182 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_AudioPacket,
183 OnAudioPacket)
184 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_CaptureCompleted,
185 OnCaptureCompleted)
186 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_CursorShapeChanged,
187 OnCursorShapeChanged)
188 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_CreateSharedBuffer,
189 OnCreateSharedBuffer)
190 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_ReleaseSharedBuffer,
191 OnReleaseSharedBuffer)
192 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_InjectClipboardEvent,
193 OnInjectClipboardEvent)
194 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_DisconnectSession,
195 DisconnectSession);
196 IPC_END_MESSAGE_MAP()
198 CHECK(handled) << "Received unexpected IPC type: " << message.type();
199 return handled;
202 void DesktopSessionProxy::OnChannelConnected(int32 peer_pid) {
203 DCHECK(caller_task_runner_->BelongsToCurrentThread());
205 VLOG(1) << "IPC: network <- desktop (" << peer_pid << ")";
208 void DesktopSessionProxy::OnChannelError() {
209 DCHECK(caller_task_runner_->BelongsToCurrentThread());
211 DetachFromDesktop();
214 bool DesktopSessionProxy::AttachToDesktop(
215 base::ProcessHandle desktop_process,
216 IPC::PlatformFileForTransit desktop_pipe) {
217 DCHECK(caller_task_runner_->BelongsToCurrentThread());
218 DCHECK(!desktop_channel_);
219 DCHECK_EQ(desktop_process_, base::kNullProcessHandle);
221 // Ignore the attach notification if the client session has been disconnected
222 // already.
223 if (!client_session_control_.get()) {
224 base::CloseProcessHandle(desktop_process);
225 return false;
228 desktop_process_ = desktop_process;
230 #if defined(OS_WIN)
231 // On Windows: |desktop_process| is a valid handle, but |desktop_pipe| needs
232 // to be duplicated from the desktop process.
233 HANDLE temp_handle;
234 if (!DuplicateHandle(desktop_process_, desktop_pipe, GetCurrentProcess(),
235 &temp_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
236 LOG_GETLASTERROR(ERROR) << "Failed to duplicate the desktop-to-network"
237 " pipe handle";
239 desktop_process_ = base::kNullProcessHandle;
240 base::CloseProcessHandle(desktop_process);
241 return false;
243 base::win::ScopedHandle pipe(temp_handle);
245 IPC::ChannelHandle desktop_channel_handle(pipe);
247 #elif defined(OS_POSIX)
248 // On posix: |desktop_pipe| is a valid file descriptor.
249 DCHECK(desktop_pipe.auto_close);
251 IPC::ChannelHandle desktop_channel_handle(std::string(), desktop_pipe);
253 #else
254 #error Unsupported platform.
255 #endif
257 // Connect to the desktop process.
258 desktop_channel_.reset(new IPC::ChannelProxy(desktop_channel_handle,
259 IPC::Channel::MODE_CLIENT,
260 this,
261 io_task_runner_.get()));
263 // Pass ID of the client (which is authenticated at this point) to the desktop
264 // session agent and start the agent.
265 SendToDesktop(new ChromotingNetworkDesktopMsg_StartSessionAgent(
266 client_session_control_->client_jid(),
267 screen_resolution_,
268 virtual_terminal_));
270 return true;
273 void DesktopSessionProxy::DetachFromDesktop() {
274 DCHECK(caller_task_runner_->BelongsToCurrentThread());
276 desktop_channel_.reset();
278 if (desktop_process_ != base::kNullProcessHandle) {
279 base::CloseProcessHandle(desktop_process_);
280 desktop_process_ = base::kNullProcessHandle;
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(scoped_ptr<webrtc::DesktopFrame>());
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(scoped_ptr<webrtc::DesktopFrame>());
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::DisconnectSession() {
322 DCHECK(caller_task_runner_->BelongsToCurrentThread());
324 // Disconnect the client session if it hasn't been disconnected yet.
325 if (client_session_control_.get())
326 client_session_control_->DisconnectSession();
329 void DesktopSessionProxy::InjectClipboardEvent(
330 const protocol::ClipboardEvent& event) {
331 DCHECK(caller_task_runner_->BelongsToCurrentThread());
333 std::string serialized_event;
334 if (!event.SerializeToString(&serialized_event)) {
335 LOG(ERROR) << "Failed to serialize protocol::ClipboardEvent.";
336 return;
339 SendToDesktop(
340 new ChromotingNetworkDesktopMsg_InjectClipboardEvent(serialized_event));
343 void DesktopSessionProxy::InjectKeyEvent(const protocol::KeyEvent& 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::KeyEvent.";
349 return;
352 SendToDesktop(
353 new ChromotingNetworkDesktopMsg_InjectKeyEvent(serialized_event));
356 void DesktopSessionProxy::InjectTextEvent(const protocol::TextEvent& 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::TextEvent.";
362 return;
365 SendToDesktop(
366 new ChromotingNetworkDesktopMsg_InjectTextEvent(serialized_event));
369 void DesktopSessionProxy::InjectMouseEvent(const protocol::MouseEvent& 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::MouseEvent.";
375 return;
378 SendToDesktop(
379 new ChromotingNetworkDesktopMsg_InjectMouseEvent(serialized_event));
382 void DesktopSessionProxy::StartInputInjector(
383 scoped_ptr<protocol::ClipboardStub> client_clipboard) {
384 DCHECK(caller_task_runner_->BelongsToCurrentThread());
386 client_clipboard_ = client_clipboard.Pass();
389 void DesktopSessionProxy::SetScreenResolution(
390 const ScreenResolution& resolution) {
391 DCHECK(caller_task_runner_->BelongsToCurrentThread());
393 if (resolution.IsEmpty())
394 return;
396 screen_resolution_ = resolution;
398 // Connect to the desktop session if it is not done yet.
399 if (!is_desktop_session_connected_) {
400 is_desktop_session_connected_ = true;
401 if (desktop_session_connector_.get()) {
402 desktop_session_connector_->ConnectTerminal(
403 this, screen_resolution_, virtual_terminal_);
405 return;
408 // Pass the client's resolution to both daemon and desktop session agent.
409 // Depending on the session kind the screen resolution can be set by either
410 // the daemon (for example RDP sessions on Windows) or by the desktop session
411 // agent (when sharing the physical console).
412 if (desktop_session_connector_.get())
413 desktop_session_connector_->SetScreenResolution(this, screen_resolution_);
414 SendToDesktop(
415 new ChromotingNetworkDesktopMsg_SetScreenResolution(screen_resolution_));
418 DesktopSessionProxy::~DesktopSessionProxy() {
419 DCHECK(caller_task_runner_->BelongsToCurrentThread());
421 if (desktop_session_connector_.get() && is_desktop_session_connected_)
422 desktop_session_connector_->DisconnectTerminal(this);
424 if (desktop_process_ != base::kNullProcessHandle) {
425 base::CloseProcessHandle(desktop_process_);
426 desktop_process_ = base::kNullProcessHandle;
430 scoped_refptr<DesktopSessionProxy::IpcSharedBufferCore>
431 DesktopSessionProxy::GetSharedBufferCore(int id) {
432 DCHECK(caller_task_runner_->BelongsToCurrentThread());
434 SharedBuffers::const_iterator i = shared_buffers_.find(id);
435 if (i != shared_buffers_.end()) {
436 return i->second;
437 } else {
438 LOG(ERROR) << "Failed to find the shared buffer " << id;
439 return NULL;
443 void DesktopSessionProxy::OnAudioPacket(const std::string& serialized_packet) {
444 DCHECK(caller_task_runner_->BelongsToCurrentThread());
446 // Parse a serialized audio packet. No further validation is done since
447 // the message was sent by more privileged process.
448 scoped_ptr<AudioPacket> packet(new AudioPacket());
449 if (!packet->ParseFromString(serialized_packet)) {
450 LOG(ERROR) << "Failed to parse AudioPacket.";
451 return;
454 // Pass a captured audio packet to |audio_capturer_|.
455 audio_capture_task_runner_->PostTask(
456 FROM_HERE, base::Bind(&IpcAudioCapturer::OnAudioPacket, audio_capturer_,
457 base::Passed(&packet)));
460 void DesktopSessionProxy::OnCreateSharedBuffer(
461 int id,
462 IPC::PlatformFileForTransit handle,
463 uint32 size) {
464 DCHECK(caller_task_runner_->BelongsToCurrentThread());
466 scoped_refptr<IpcSharedBufferCore> shared_buffer =
467 new IpcSharedBufferCore(id, handle, desktop_process_, size);
469 if (shared_buffer->memory() != NULL &&
470 !shared_buffers_.insert(std::make_pair(id, shared_buffer)).second) {
471 LOG(ERROR) << "Duplicate shared buffer id " << id << " encountered";
475 void DesktopSessionProxy::OnReleaseSharedBuffer(int id) {
476 DCHECK(caller_task_runner_->BelongsToCurrentThread());
478 // Drop the cached reference to the buffer.
479 shared_buffers_.erase(id);
482 void DesktopSessionProxy::OnCaptureCompleted(
483 const SerializedDesktopFrame& serialized_frame) {
484 DCHECK(caller_task_runner_->BelongsToCurrentThread());
486 // Assume that |serialized_frame| is well-formed because it was received from
487 // a more privileged process.
488 scoped_refptr<IpcSharedBufferCore> shared_buffer_core =
489 GetSharedBufferCore(serialized_frame.shared_buffer_id);
490 CHECK(shared_buffer_core.get());
492 scoped_ptr<webrtc::DesktopFrame> frame(
493 new webrtc::SharedMemoryDesktopFrame(
494 serialized_frame.dimensions, serialized_frame.bytes_per_row,
495 new IpcSharedBuffer(shared_buffer_core)));
496 frame->set_capture_time_ms(serialized_frame.capture_time_ms);
497 frame->set_dpi(serialized_frame.dpi);
499 for (size_t i = 0; i < serialized_frame.dirty_region.size(); ++i) {
500 frame->mutable_updated_region()->AddRect(serialized_frame.dirty_region[i]);
503 --pending_capture_frame_requests_;
504 PostCaptureCompleted(frame.Pass());
507 void DesktopSessionProxy::OnCursorShapeChanged(
508 const webrtc::MouseCursorShape& cursor_shape) {
509 DCHECK(caller_task_runner_->BelongsToCurrentThread());
510 PostCursorShape(scoped_ptr<webrtc::MouseCursorShape>(
511 new webrtc::MouseCursorShape(cursor_shape)));
514 void DesktopSessionProxy::OnInjectClipboardEvent(
515 const std::string& serialized_event) {
516 DCHECK(caller_task_runner_->BelongsToCurrentThread());
518 if (client_clipboard_) {
519 protocol::ClipboardEvent event;
520 if (!event.ParseFromString(serialized_event)) {
521 LOG(ERROR) << "Failed to parse protocol::ClipboardEvent.";
522 return;
525 client_clipboard_->InjectClipboardEvent(event);
529 void DesktopSessionProxy::PostCaptureCompleted(
530 scoped_ptr<webrtc::DesktopFrame> frame) {
531 DCHECK(caller_task_runner_->BelongsToCurrentThread());
533 video_capture_task_runner_->PostTask(
534 FROM_HERE,
535 base::Bind(&IpcVideoFrameCapturer::OnCaptureCompleted, video_capturer_,
536 base::Passed(&frame)));
539 void DesktopSessionProxy::PostCursorShape(
540 scoped_ptr<webrtc::MouseCursorShape> cursor_shape) {
541 DCHECK(caller_task_runner_->BelongsToCurrentThread());
543 video_capture_task_runner_->PostTask(
544 FROM_HERE,
545 base::Bind(&IpcVideoFrameCapturer::OnCursorShapeChanged, video_capturer_,
546 base::Passed(&cursor_shape)));
549 void DesktopSessionProxy::SendToDesktop(IPC::Message* message) {
550 DCHECK(caller_task_runner_->BelongsToCurrentThread());
552 if (desktop_channel_) {
553 desktop_channel_->Send(message);
554 } else {
555 delete message;
559 // static
560 void DesktopSessionProxyTraits::Destruct(
561 const DesktopSessionProxy* desktop_session_proxy) {
562 desktop_session_proxy->caller_task_runner_->DeleteSoon(FROM_HERE,
563 desktop_session_proxy);
566 } // namespace remoting