Revert of Report viewport memory and timing correctly for Slimming Paint. (patchset...
[chromium-blink-merge.git] / remoting / host / desktop_session_proxy.cc
blobcb928dd4fd1f910363f56fb7ecf37d880963c831
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 make_scoped_ptr(new IpcAudioCapturer(this));
130 scoped_ptr<InputInjector> DesktopSessionProxy::CreateInputInjector() {
131 DCHECK(caller_task_runner_->BelongsToCurrentThread());
133 return make_scoped_ptr(new IpcInputInjector(this));
136 scoped_ptr<ScreenControls> DesktopSessionProxy::CreateScreenControls() {
137 DCHECK(caller_task_runner_->BelongsToCurrentThread());
139 return make_scoped_ptr(new IpcScreenControls(this));
142 scoped_ptr<webrtc::DesktopCapturer> DesktopSessionProxy::CreateVideoCapturer() {
143 DCHECK(caller_task_runner_->BelongsToCurrentThread());
145 return make_scoped_ptr(new IpcVideoFrameCapturer(this));
148 scoped_ptr<webrtc::MouseCursorMonitor>
149 DesktopSessionProxy::CreateMouseCursorMonitor() {
150 return make_scoped_ptr(new IpcMouseCursorMonitor(this));
153 std::string DesktopSessionProxy::GetCapabilities() const {
154 std::string result = kRateLimitResizeRequests;
155 // Ask the client to send its resolution unconditionally.
156 if (virtual_terminal_)
157 result = result + " " + kSendInitialResolution;
158 return result;
161 void DesktopSessionProxy::SetCapabilities(const std::string& capabilities) {
162 // Delay creation of the desktop session until the client screen resolution is
163 // received if the desktop session requires the initial screen resolution
164 // (when |virtual_terminal_| is true) and the client is expected to
165 // sent its screen resolution (the 'sendInitialResolution' capability is
166 // supported).
167 if (virtual_terminal_ &&
168 HasCapability(capabilities, kSendInitialResolution)) {
169 VLOG(1) << "Waiting for the client screen resolution.";
170 return;
173 // Connect to the desktop session.
174 if (!is_desktop_session_connected_) {
175 is_desktop_session_connected_ = true;
176 if (desktop_session_connector_.get()) {
177 desktop_session_connector_->ConnectTerminal(
178 this, screen_resolution_, virtual_terminal_);
183 bool DesktopSessionProxy::OnMessageReceived(const IPC::Message& message) {
184 DCHECK(caller_task_runner_->BelongsToCurrentThread());
186 bool handled = true;
187 IPC_BEGIN_MESSAGE_MAP(DesktopSessionProxy, message)
188 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_AudioPacket,
189 OnAudioPacket)
190 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_CaptureCompleted,
191 OnCaptureCompleted)
192 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_MouseCursor,
193 OnMouseCursor)
194 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_CreateSharedBuffer,
195 OnCreateSharedBuffer)
196 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_ReleaseSharedBuffer,
197 OnReleaseSharedBuffer)
198 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_InjectClipboardEvent,
199 OnInjectClipboardEvent)
200 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_DisconnectSession,
201 DisconnectSession);
202 IPC_END_MESSAGE_MAP()
204 CHECK(handled) << "Received unexpected IPC type: " << message.type();
205 return handled;
208 void DesktopSessionProxy::OnChannelConnected(int32 peer_pid) {
209 DCHECK(caller_task_runner_->BelongsToCurrentThread());
211 VLOG(1) << "IPC: network <- desktop (" << peer_pid << ")";
214 void DesktopSessionProxy::OnChannelError() {
215 DCHECK(caller_task_runner_->BelongsToCurrentThread());
217 DetachFromDesktop();
220 bool DesktopSessionProxy::AttachToDesktop(
221 base::ProcessHandle desktop_process,
222 IPC::PlatformFileForTransit desktop_pipe) {
223 DCHECK(caller_task_runner_->BelongsToCurrentThread());
224 DCHECK(!desktop_channel_);
225 DCHECK_EQ(desktop_process_, base::kNullProcessHandle);
227 // Ignore the attach notification if the client session has been disconnected
228 // already.
229 if (!client_session_control_.get()) {
230 base::CloseProcessHandle(desktop_process);
231 return false;
234 desktop_process_ = desktop_process;
236 #if defined(OS_WIN)
237 // On Windows: |desktop_process| is a valid handle, but |desktop_pipe| needs
238 // to be duplicated from the desktop process.
239 HANDLE temp_handle;
240 if (!DuplicateHandle(desktop_process_, desktop_pipe, GetCurrentProcess(),
241 &temp_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
242 PLOG(ERROR) << "Failed to duplicate the desktop-to-network pipe handle";
244 desktop_process_ = base::kNullProcessHandle;
245 base::CloseProcessHandle(desktop_process);
246 return false;
248 base::win::ScopedHandle pipe(temp_handle);
250 IPC::ChannelHandle desktop_channel_handle(pipe.Get());
252 #elif defined(OS_POSIX)
253 // On posix: |desktop_pipe| is a valid file descriptor.
254 DCHECK(desktop_pipe.auto_close);
256 IPC::ChannelHandle desktop_channel_handle(std::string(), desktop_pipe);
258 #else
259 #error Unsupported platform.
260 #endif
262 // Connect to the desktop process.
263 desktop_channel_ = IPC::ChannelProxy::Create(desktop_channel_handle,
264 IPC::Channel::MODE_CLIENT,
265 this,
266 io_task_runner_.get());
268 // Pass ID of the client (which is authenticated at this point) to the desktop
269 // session agent and start the agent.
270 SendToDesktop(new ChromotingNetworkDesktopMsg_StartSessionAgent(
271 client_session_control_->client_jid(),
272 screen_resolution_,
273 virtual_terminal_));
275 return true;
278 void DesktopSessionProxy::DetachFromDesktop() {
279 DCHECK(caller_task_runner_->BelongsToCurrentThread());
281 desktop_channel_.reset();
283 if (desktop_process_ != base::kNullProcessHandle) {
284 base::CloseProcessHandle(desktop_process_);
285 desktop_process_ = base::kNullProcessHandle;
288 shared_buffers_.clear();
290 // Generate fake responses to keep the video capturer in sync.
291 while (pending_capture_frame_requests_) {
292 --pending_capture_frame_requests_;
293 PostCaptureCompleted(nullptr);
297 void DesktopSessionProxy::SetAudioCapturer(
298 const base::WeakPtr<IpcAudioCapturer>& audio_capturer) {
299 DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
301 audio_capturer_ = audio_capturer;
304 void DesktopSessionProxy::CaptureFrame() {
305 if (!caller_task_runner_->BelongsToCurrentThread()) {
306 caller_task_runner_->PostTask(
307 FROM_HERE, base::Bind(&DesktopSessionProxy::CaptureFrame, this));
308 return;
311 if (desktop_channel_) {
312 ++pending_capture_frame_requests_;
313 SendToDesktop(new ChromotingNetworkDesktopMsg_CaptureFrame());
314 } else {
315 PostCaptureCompleted(nullptr);
319 void DesktopSessionProxy::SetVideoCapturer(
320 const base::WeakPtr<IpcVideoFrameCapturer> video_capturer) {
321 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
323 video_capturer_ = video_capturer;
326 void DesktopSessionProxy::SetMouseCursorMonitor(
327 const base::WeakPtr<IpcMouseCursorMonitor>& mouse_cursor_monitor) {
328 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
330 mouse_cursor_monitor_ = mouse_cursor_monitor;
333 void DesktopSessionProxy::DisconnectSession() {
334 DCHECK(caller_task_runner_->BelongsToCurrentThread());
336 // Disconnect the client session if it hasn't been disconnected yet.
337 if (client_session_control_.get())
338 client_session_control_->DisconnectSession();
341 void DesktopSessionProxy::InjectClipboardEvent(
342 const protocol::ClipboardEvent& event) {
343 DCHECK(caller_task_runner_->BelongsToCurrentThread());
345 std::string serialized_event;
346 if (!event.SerializeToString(&serialized_event)) {
347 LOG(ERROR) << "Failed to serialize protocol::ClipboardEvent.";
348 return;
351 SendToDesktop(
352 new ChromotingNetworkDesktopMsg_InjectClipboardEvent(serialized_event));
355 void DesktopSessionProxy::InjectKeyEvent(const protocol::KeyEvent& event) {
356 DCHECK(caller_task_runner_->BelongsToCurrentThread());
358 std::string serialized_event;
359 if (!event.SerializeToString(&serialized_event)) {
360 LOG(ERROR) << "Failed to serialize protocol::KeyEvent.";
361 return;
364 SendToDesktop(
365 new ChromotingNetworkDesktopMsg_InjectKeyEvent(serialized_event));
368 void DesktopSessionProxy::InjectTextEvent(const protocol::TextEvent& event) {
369 DCHECK(caller_task_runner_->BelongsToCurrentThread());
371 std::string serialized_event;
372 if (!event.SerializeToString(&serialized_event)) {
373 LOG(ERROR) << "Failed to serialize protocol::TextEvent.";
374 return;
377 SendToDesktop(
378 new ChromotingNetworkDesktopMsg_InjectTextEvent(serialized_event));
381 void DesktopSessionProxy::InjectMouseEvent(const protocol::MouseEvent& event) {
382 DCHECK(caller_task_runner_->BelongsToCurrentThread());
384 std::string serialized_event;
385 if (!event.SerializeToString(&serialized_event)) {
386 LOG(ERROR) << "Failed to serialize protocol::MouseEvent.";
387 return;
390 SendToDesktop(
391 new ChromotingNetworkDesktopMsg_InjectMouseEvent(serialized_event));
394 void DesktopSessionProxy::StartInputInjector(
395 scoped_ptr<protocol::ClipboardStub> client_clipboard) {
396 DCHECK(caller_task_runner_->BelongsToCurrentThread());
398 client_clipboard_ = client_clipboard.Pass();
401 void DesktopSessionProxy::SetScreenResolution(
402 const ScreenResolution& resolution) {
403 DCHECK(caller_task_runner_->BelongsToCurrentThread());
405 if (resolution.IsEmpty())
406 return;
408 screen_resolution_ = resolution;
410 // Connect to the desktop session if it is not done yet.
411 if (!is_desktop_session_connected_) {
412 is_desktop_session_connected_ = true;
413 if (desktop_session_connector_.get()) {
414 desktop_session_connector_->ConnectTerminal(
415 this, screen_resolution_, virtual_terminal_);
417 return;
420 // Pass the client's resolution to both daemon and desktop session agent.
421 // Depending on the session kind the screen resolution can be set by either
422 // the daemon (for example RDP sessions on Windows) or by the desktop session
423 // agent (when sharing the physical console).
424 if (desktop_session_connector_.get())
425 desktop_session_connector_->SetScreenResolution(this, screen_resolution_);
426 SendToDesktop(
427 new ChromotingNetworkDesktopMsg_SetScreenResolution(screen_resolution_));
430 DesktopSessionProxy::~DesktopSessionProxy() {
431 DCHECK(caller_task_runner_->BelongsToCurrentThread());
433 if (desktop_session_connector_.get() && is_desktop_session_connected_)
434 desktop_session_connector_->DisconnectTerminal(this);
436 if (desktop_process_ != base::kNullProcessHandle) {
437 base::CloseProcessHandle(desktop_process_);
438 desktop_process_ = base::kNullProcessHandle;
442 scoped_refptr<DesktopSessionProxy::IpcSharedBufferCore>
443 DesktopSessionProxy::GetSharedBufferCore(int id) {
444 DCHECK(caller_task_runner_->BelongsToCurrentThread());
446 SharedBuffers::const_iterator i = shared_buffers_.find(id);
447 if (i != shared_buffers_.end()) {
448 return i->second;
449 } else {
450 LOG(ERROR) << "Failed to find the shared buffer " << id;
451 return nullptr;
455 void DesktopSessionProxy::OnAudioPacket(const std::string& serialized_packet) {
456 DCHECK(caller_task_runner_->BelongsToCurrentThread());
458 // Parse a serialized audio packet. No further validation is done since
459 // the message was sent by more privileged process.
460 scoped_ptr<AudioPacket> packet(new AudioPacket());
461 if (!packet->ParseFromString(serialized_packet)) {
462 LOG(ERROR) << "Failed to parse AudioPacket.";
463 return;
466 // Pass a captured audio packet to |audio_capturer_|.
467 audio_capture_task_runner_->PostTask(
468 FROM_HERE, base::Bind(&IpcAudioCapturer::OnAudioPacket, audio_capturer_,
469 base::Passed(&packet)));
472 void DesktopSessionProxy::OnCreateSharedBuffer(
473 int id,
474 IPC::PlatformFileForTransit handle,
475 uint32 size) {
476 DCHECK(caller_task_runner_->BelongsToCurrentThread());
478 scoped_refptr<IpcSharedBufferCore> shared_buffer =
479 new IpcSharedBufferCore(id, handle, desktop_process_, size);
481 if (shared_buffer->memory() != nullptr &&
482 !shared_buffers_.insert(std::make_pair(id, shared_buffer)).second) {
483 LOG(ERROR) << "Duplicate shared buffer id " << id << " encountered";
487 void DesktopSessionProxy::OnReleaseSharedBuffer(int id) {
488 DCHECK(caller_task_runner_->BelongsToCurrentThread());
490 // Drop the cached reference to the buffer.
491 shared_buffers_.erase(id);
494 void DesktopSessionProxy::OnCaptureCompleted(
495 const SerializedDesktopFrame& serialized_frame) {
496 DCHECK(caller_task_runner_->BelongsToCurrentThread());
498 // Assume that |serialized_frame| is well-formed because it was received from
499 // a more privileged process.
500 scoped_refptr<IpcSharedBufferCore> shared_buffer_core =
501 GetSharedBufferCore(serialized_frame.shared_buffer_id);
502 CHECK(shared_buffer_core.get());
504 scoped_ptr<webrtc::DesktopFrame> frame(
505 new webrtc::SharedMemoryDesktopFrame(
506 serialized_frame.dimensions, serialized_frame.bytes_per_row,
507 new IpcSharedBuffer(shared_buffer_core)));
508 frame->set_capture_time_ms(serialized_frame.capture_time_ms);
509 frame->set_dpi(serialized_frame.dpi);
511 for (size_t i = 0; i < serialized_frame.dirty_region.size(); ++i) {
512 frame->mutable_updated_region()->AddRect(serialized_frame.dirty_region[i]);
515 --pending_capture_frame_requests_;
516 PostCaptureCompleted(frame.Pass());
519 void DesktopSessionProxy::OnMouseCursor(
520 const webrtc::MouseCursor& mouse_cursor) {
521 DCHECK(caller_task_runner_->BelongsToCurrentThread());
522 PostMouseCursor(make_scoped_ptr(webrtc::MouseCursor::CopyOf(mouse_cursor)));
525 void DesktopSessionProxy::OnInjectClipboardEvent(
526 const std::string& serialized_event) {
527 DCHECK(caller_task_runner_->BelongsToCurrentThread());
529 if (client_clipboard_) {
530 protocol::ClipboardEvent event;
531 if (!event.ParseFromString(serialized_event)) {
532 LOG(ERROR) << "Failed to parse protocol::ClipboardEvent.";
533 return;
536 client_clipboard_->InjectClipboardEvent(event);
540 void DesktopSessionProxy::PostCaptureCompleted(
541 scoped_ptr<webrtc::DesktopFrame> frame) {
542 DCHECK(caller_task_runner_->BelongsToCurrentThread());
544 video_capture_task_runner_->PostTask(
545 FROM_HERE,
546 base::Bind(&IpcVideoFrameCapturer::OnCaptureCompleted, video_capturer_,
547 base::Passed(&frame)));
550 void DesktopSessionProxy::PostMouseCursor(
551 scoped_ptr<webrtc::MouseCursor> mouse_cursor) {
552 DCHECK(caller_task_runner_->BelongsToCurrentThread());
554 video_capture_task_runner_->PostTask(
555 FROM_HERE,
556 base::Bind(&IpcMouseCursorMonitor::OnMouseCursor, mouse_cursor_monitor_,
557 base::Passed(&mouse_cursor)));
560 void DesktopSessionProxy::SendToDesktop(IPC::Message* message) {
561 DCHECK(caller_task_runner_->BelongsToCurrentThread());
563 if (desktop_channel_) {
564 desktop_channel_->Send(message);
565 } else {
566 delete message;
570 // static
571 void DesktopSessionProxyTraits::Destruct(
572 const DesktopSessionProxy* desktop_session_proxy) {
573 desktop_session_proxy->caller_task_runner_->DeleteSoon(FROM_HERE,
574 desktop_session_proxy);
577 } // namespace remoting