Roll src/third_party/WebKit b41a10f:afd8afd (svn 202201:202202)
[chromium-blink-merge.git] / remoting / host / desktop_session_proxy.cc
blob54399d98285b70656dc1fa0cd87973acd4d24c87
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 "remoting/protocol/capability_names.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 #if defined(OS_WIN)
34 #include "base/win/scoped_handle.h"
35 #endif // defined(OS_WIN)
37 const bool kReadOnly = true;
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="
61 << base::SharedMemory::GetFdFromSharedMemoryHandle(handle)
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 base::SharedMemory::GetFdFromSharedMemoryHandle(
75 shared_memory_.handle());
76 #endif
79 private:
80 virtual ~IpcSharedBufferCore() {}
81 friend class base::RefCountedThreadSafe<IpcSharedBufferCore>;
83 int id_;
84 base::SharedMemory shared_memory_;
85 size_t size_;
87 DISALLOW_COPY_AND_ASSIGN(IpcSharedBufferCore);
90 class DesktopSessionProxy::IpcSharedBuffer : public webrtc::SharedMemory {
91 public:
92 IpcSharedBuffer(scoped_refptr<IpcSharedBufferCore> core)
93 : SharedMemory(core->memory(), core->size(),
94 core->handle(), core->id()),
95 core_(core) {
98 private:
99 scoped_refptr<IpcSharedBufferCore> core_;
101 DISALLOW_COPY_AND_ASSIGN(IpcSharedBuffer);
104 DesktopSessionProxy::DesktopSessionProxy(
105 scoped_refptr<base::SingleThreadTaskRunner> audio_capture_task_runner,
106 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
107 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
108 scoped_refptr<base::SingleThreadTaskRunner> video_capture_task_runner,
109 base::WeakPtr<ClientSessionControl> client_session_control,
110 base::WeakPtr<DesktopSessionConnector> desktop_session_connector,
111 bool virtual_terminal,
112 bool supports_touch_events)
113 : audio_capture_task_runner_(audio_capture_task_runner),
114 caller_task_runner_(caller_task_runner),
115 io_task_runner_(io_task_runner),
116 video_capture_task_runner_(video_capture_task_runner),
117 client_session_control_(client_session_control),
118 desktop_session_connector_(desktop_session_connector),
119 pending_capture_frame_requests_(0),
120 is_desktop_session_connected_(false),
121 virtual_terminal_(virtual_terminal),
122 supports_touch_events_(supports_touch_events) {
123 DCHECK(caller_task_runner_->BelongsToCurrentThread());
126 scoped_ptr<AudioCapturer> DesktopSessionProxy::CreateAudioCapturer() {
127 DCHECK(caller_task_runner_->BelongsToCurrentThread());
129 return make_scoped_ptr(new IpcAudioCapturer(this));
132 scoped_ptr<InputInjector> DesktopSessionProxy::CreateInputInjector() {
133 DCHECK(caller_task_runner_->BelongsToCurrentThread());
135 return make_scoped_ptr(new IpcInputInjector(this));
138 scoped_ptr<ScreenControls> DesktopSessionProxy::CreateScreenControls() {
139 DCHECK(caller_task_runner_->BelongsToCurrentThread());
141 return make_scoped_ptr(new IpcScreenControls(this));
144 scoped_ptr<webrtc::DesktopCapturer> DesktopSessionProxy::CreateVideoCapturer() {
145 DCHECK(caller_task_runner_->BelongsToCurrentThread());
147 return make_scoped_ptr(new IpcVideoFrameCapturer(this));
150 scoped_ptr<webrtc::MouseCursorMonitor>
151 DesktopSessionProxy::CreateMouseCursorMonitor() {
152 return make_scoped_ptr(new IpcMouseCursorMonitor(this));
155 std::string DesktopSessionProxy::GetCapabilities() const {
156 std::string result = protocol::kRateLimitResizeRequests;
157 // Ask the client to send its resolution unconditionally.
158 if (virtual_terminal_)
159 result = result + " " + protocol::kSendInitialResolution;
161 if (supports_touch_events_)
162 result = result + " " + protocol::kTouchEventsCapability;
164 return result;
167 void DesktopSessionProxy::SetCapabilities(const std::string& capabilities) {
168 // Delay creation of the desktop session until the client screen resolution is
169 // received if the desktop session requires the initial screen resolution
170 // (when |virtual_terminal_| is true) and the client is expected to
171 // sent its screen resolution (the 'sendInitialResolution' capability is
172 // supported).
173 if (virtual_terminal_ &&
174 HasCapability(capabilities, protocol::kSendInitialResolution)) {
175 VLOG(1) << "Waiting for the client screen resolution.";
176 return;
179 // Connect to the desktop session.
180 if (!is_desktop_session_connected_) {
181 is_desktop_session_connected_ = true;
182 if (desktop_session_connector_.get()) {
183 desktop_session_connector_->ConnectTerminal(
184 this, screen_resolution_, virtual_terminal_);
189 bool DesktopSessionProxy::OnMessageReceived(const IPC::Message& message) {
190 DCHECK(caller_task_runner_->BelongsToCurrentThread());
192 bool handled = true;
193 IPC_BEGIN_MESSAGE_MAP(DesktopSessionProxy, message)
194 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_AudioPacket,
195 OnAudioPacket)
196 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_CaptureCompleted,
197 OnCaptureCompleted)
198 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_MouseCursor,
199 OnMouseCursor)
200 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_CreateSharedBuffer,
201 OnCreateSharedBuffer)
202 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_ReleaseSharedBuffer,
203 OnReleaseSharedBuffer)
204 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_InjectClipboardEvent,
205 OnInjectClipboardEvent)
206 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_DisconnectSession,
207 DisconnectSession);
208 IPC_END_MESSAGE_MAP()
210 CHECK(handled) << "Received unexpected IPC type: " << message.type();
211 return handled;
214 void DesktopSessionProxy::OnChannelConnected(int32 peer_pid) {
215 DCHECK(caller_task_runner_->BelongsToCurrentThread());
217 VLOG(1) << "IPC: network <- desktop (" << peer_pid << ")";
220 void DesktopSessionProxy::OnChannelError() {
221 DCHECK(caller_task_runner_->BelongsToCurrentThread());
223 DetachFromDesktop();
226 bool DesktopSessionProxy::AttachToDesktop(
227 base::Process desktop_process,
228 IPC::PlatformFileForTransit desktop_pipe) {
229 DCHECK(caller_task_runner_->BelongsToCurrentThread());
230 DCHECK(!desktop_channel_);
231 DCHECK(!desktop_process_.IsValid());
233 // Ignore the attach notification if the client session has been disconnected
234 // already.
235 if (!client_session_control_.get())
236 return false;
238 desktop_process_ = desktop_process.Pass();
240 #if defined(OS_WIN)
241 // On Windows: |desktop_process| is a valid handle, but |desktop_pipe| needs
242 // to be duplicated from the desktop process.
243 HANDLE temp_handle;
244 if (!DuplicateHandle(desktop_process_.Handle(), desktop_pipe,
245 GetCurrentProcess(), &temp_handle, 0,
246 FALSE, DUPLICATE_SAME_ACCESS)) {
247 PLOG(ERROR) << "Failed to duplicate the desktop-to-network pipe handle";
249 desktop_process_.Close();
250 return false;
252 base::win::ScopedHandle pipe(temp_handle);
254 IPC::ChannelHandle desktop_channel_handle(pipe.Get());
256 #elif defined(OS_POSIX)
257 // On posix: |desktop_pipe| is a valid file descriptor.
258 DCHECK(desktop_pipe.auto_close);
260 IPC::ChannelHandle desktop_channel_handle(std::string(), desktop_pipe);
262 #else
263 #error Unsupported platform.
264 #endif
266 // Connect to the desktop process.
267 desktop_channel_ = IPC::ChannelProxy::Create(desktop_channel_handle,
268 IPC::Channel::MODE_CLIENT,
269 this,
270 io_task_runner_.get(),
271 nullptr);
273 // Pass ID of the client (which is authenticated at this point) to the desktop
274 // session agent and start the agent.
275 SendToDesktop(new ChromotingNetworkDesktopMsg_StartSessionAgent(
276 client_session_control_->client_jid(),
277 screen_resolution_,
278 virtual_terminal_));
280 return true;
283 void DesktopSessionProxy::DetachFromDesktop() {
284 DCHECK(caller_task_runner_->BelongsToCurrentThread());
286 desktop_channel_.reset();
288 if (desktop_process_.IsValid())
289 desktop_process_.Close();
291 shared_buffers_.clear();
293 // Generate fake responses to keep the video capturer in sync.
294 while (pending_capture_frame_requests_) {
295 --pending_capture_frame_requests_;
296 PostCaptureCompleted(nullptr);
300 void DesktopSessionProxy::SetAudioCapturer(
301 const base::WeakPtr<IpcAudioCapturer>& audio_capturer) {
302 DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
304 audio_capturer_ = audio_capturer;
307 void DesktopSessionProxy::CaptureFrame() {
308 if (!caller_task_runner_->BelongsToCurrentThread()) {
309 caller_task_runner_->PostTask(
310 FROM_HERE, base::Bind(&DesktopSessionProxy::CaptureFrame, this));
311 return;
314 if (desktop_channel_) {
315 ++pending_capture_frame_requests_;
316 SendToDesktop(new ChromotingNetworkDesktopMsg_CaptureFrame());
317 } else {
318 PostCaptureCompleted(nullptr);
322 void DesktopSessionProxy::SetVideoCapturer(
323 const base::WeakPtr<IpcVideoFrameCapturer> video_capturer) {
324 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
326 video_capturer_ = video_capturer;
329 void DesktopSessionProxy::SetMouseCursorMonitor(
330 const base::WeakPtr<IpcMouseCursorMonitor>& mouse_cursor_monitor) {
331 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
333 mouse_cursor_monitor_ = mouse_cursor_monitor;
336 void DesktopSessionProxy::DisconnectSession() {
337 DCHECK(caller_task_runner_->BelongsToCurrentThread());
339 // Disconnect the client session if it hasn't been disconnected yet.
340 if (client_session_control_.get())
341 client_session_control_->DisconnectSession();
344 void DesktopSessionProxy::InjectClipboardEvent(
345 const protocol::ClipboardEvent& event) {
346 DCHECK(caller_task_runner_->BelongsToCurrentThread());
348 std::string serialized_event;
349 if (!event.SerializeToString(&serialized_event)) {
350 LOG(ERROR) << "Failed to serialize protocol::ClipboardEvent.";
351 return;
354 SendToDesktop(
355 new ChromotingNetworkDesktopMsg_InjectClipboardEvent(serialized_event));
358 void DesktopSessionProxy::InjectKeyEvent(const protocol::KeyEvent& event) {
359 DCHECK(caller_task_runner_->BelongsToCurrentThread());
361 std::string serialized_event;
362 if (!event.SerializeToString(&serialized_event)) {
363 LOG(ERROR) << "Failed to serialize protocol::KeyEvent.";
364 return;
367 SendToDesktop(
368 new ChromotingNetworkDesktopMsg_InjectKeyEvent(serialized_event));
371 void DesktopSessionProxy::InjectTextEvent(const protocol::TextEvent& event) {
372 DCHECK(caller_task_runner_->BelongsToCurrentThread());
374 std::string serialized_event;
375 if (!event.SerializeToString(&serialized_event)) {
376 LOG(ERROR) << "Failed to serialize protocol::TextEvent.";
377 return;
380 SendToDesktop(
381 new ChromotingNetworkDesktopMsg_InjectTextEvent(serialized_event));
384 void DesktopSessionProxy::InjectMouseEvent(const protocol::MouseEvent& event) {
385 DCHECK(caller_task_runner_->BelongsToCurrentThread());
387 std::string serialized_event;
388 if (!event.SerializeToString(&serialized_event)) {
389 LOG(ERROR) << "Failed to serialize protocol::MouseEvent.";
390 return;
393 SendToDesktop(
394 new ChromotingNetworkDesktopMsg_InjectMouseEvent(serialized_event));
397 void DesktopSessionProxy::InjectTouchEvent(const protocol::TouchEvent& event) {
398 DCHECK(caller_task_runner_->BelongsToCurrentThread());
400 std::string serialized_event;
401 if (!event.SerializeToString(&serialized_event)) {
402 LOG(ERROR) << "Failed to serialize protocol::TouchEvent.";
403 return;
406 SendToDesktop(
407 new ChromotingNetworkDesktopMsg_InjectTouchEvent(serialized_event));
410 void DesktopSessionProxy::StartInputInjector(
411 scoped_ptr<protocol::ClipboardStub> client_clipboard) {
412 DCHECK(caller_task_runner_->BelongsToCurrentThread());
414 client_clipboard_ = client_clipboard.Pass();
417 void DesktopSessionProxy::SetScreenResolution(
418 const ScreenResolution& resolution) {
419 DCHECK(caller_task_runner_->BelongsToCurrentThread());
421 if (resolution.IsEmpty())
422 return;
424 screen_resolution_ = resolution;
426 // Connect to the desktop session if it is not done yet.
427 if (!is_desktop_session_connected_) {
428 is_desktop_session_connected_ = true;
429 if (desktop_session_connector_.get()) {
430 desktop_session_connector_->ConnectTerminal(
431 this, screen_resolution_, virtual_terminal_);
433 return;
436 // Pass the client's resolution to both daemon and desktop session agent.
437 // Depending on the session kind the screen resolution can be set by either
438 // the daemon (for example RDP sessions on Windows) or by the desktop session
439 // agent (when sharing the physical console).
440 if (desktop_session_connector_.get())
441 desktop_session_connector_->SetScreenResolution(this, screen_resolution_);
442 SendToDesktop(
443 new ChromotingNetworkDesktopMsg_SetScreenResolution(screen_resolution_));
446 DesktopSessionProxy::~DesktopSessionProxy() {
447 DCHECK(caller_task_runner_->BelongsToCurrentThread());
449 if (desktop_session_connector_.get() && is_desktop_session_connected_)
450 desktop_session_connector_->DisconnectTerminal(this);
453 scoped_refptr<DesktopSessionProxy::IpcSharedBufferCore>
454 DesktopSessionProxy::GetSharedBufferCore(int id) {
455 DCHECK(caller_task_runner_->BelongsToCurrentThread());
457 SharedBuffers::const_iterator i = shared_buffers_.find(id);
458 if (i != shared_buffers_.end()) {
459 return i->second;
460 } else {
461 LOG(ERROR) << "Failed to find the shared buffer " << id;
462 return nullptr;
466 void DesktopSessionProxy::OnAudioPacket(const std::string& serialized_packet) {
467 DCHECK(caller_task_runner_->BelongsToCurrentThread());
469 // Parse a serialized audio packet. No further validation is done since
470 // the message was sent by more privileged process.
471 scoped_ptr<AudioPacket> packet(new AudioPacket());
472 if (!packet->ParseFromString(serialized_packet)) {
473 LOG(ERROR) << "Failed to parse AudioPacket.";
474 return;
477 // Pass a captured audio packet to |audio_capturer_|.
478 audio_capture_task_runner_->PostTask(
479 FROM_HERE, base::Bind(&IpcAudioCapturer::OnAudioPacket, audio_capturer_,
480 base::Passed(&packet)));
483 void DesktopSessionProxy::OnCreateSharedBuffer(
484 int id,
485 IPC::PlatformFileForTransit handle,
486 uint32 size) {
487 DCHECK(caller_task_runner_->BelongsToCurrentThread());
489 base::SharedMemoryHandle shm_handle = base::SharedMemoryHandle(handle);
490 scoped_refptr<IpcSharedBufferCore> shared_buffer =
491 new IpcSharedBufferCore(id, shm_handle, desktop_process_.Handle(), size);
493 if (shared_buffer->memory() != nullptr &&
494 !shared_buffers_.insert(std::make_pair(id, shared_buffer)).second) {
495 LOG(ERROR) << "Duplicate shared buffer id " << id << " encountered";
499 void DesktopSessionProxy::OnReleaseSharedBuffer(int id) {
500 DCHECK(caller_task_runner_->BelongsToCurrentThread());
502 // Drop the cached reference to the buffer.
503 shared_buffers_.erase(id);
506 void DesktopSessionProxy::OnCaptureCompleted(
507 const SerializedDesktopFrame& serialized_frame) {
508 DCHECK(caller_task_runner_->BelongsToCurrentThread());
510 // Assume that |serialized_frame| is well-formed because it was received from
511 // a more privileged process.
512 scoped_refptr<IpcSharedBufferCore> shared_buffer_core =
513 GetSharedBufferCore(serialized_frame.shared_buffer_id);
514 CHECK(shared_buffer_core.get());
516 scoped_ptr<webrtc::DesktopFrame> frame(
517 new webrtc::SharedMemoryDesktopFrame(
518 serialized_frame.dimensions, serialized_frame.bytes_per_row,
519 new IpcSharedBuffer(shared_buffer_core)));
520 frame->set_capture_time_ms(serialized_frame.capture_time_ms);
521 frame->set_dpi(serialized_frame.dpi);
523 for (size_t i = 0; i < serialized_frame.dirty_region.size(); ++i) {
524 frame->mutable_updated_region()->AddRect(serialized_frame.dirty_region[i]);
527 --pending_capture_frame_requests_;
528 PostCaptureCompleted(frame.Pass());
531 void DesktopSessionProxy::OnMouseCursor(
532 const webrtc::MouseCursor& mouse_cursor) {
533 DCHECK(caller_task_runner_->BelongsToCurrentThread());
534 PostMouseCursor(make_scoped_ptr(webrtc::MouseCursor::CopyOf(mouse_cursor)));
537 void DesktopSessionProxy::OnInjectClipboardEvent(
538 const std::string& serialized_event) {
539 DCHECK(caller_task_runner_->BelongsToCurrentThread());
541 if (client_clipboard_) {
542 protocol::ClipboardEvent event;
543 if (!event.ParseFromString(serialized_event)) {
544 LOG(ERROR) << "Failed to parse protocol::ClipboardEvent.";
545 return;
548 client_clipboard_->InjectClipboardEvent(event);
552 void DesktopSessionProxy::PostCaptureCompleted(
553 scoped_ptr<webrtc::DesktopFrame> frame) {
554 DCHECK(caller_task_runner_->BelongsToCurrentThread());
556 video_capture_task_runner_->PostTask(
557 FROM_HERE,
558 base::Bind(&IpcVideoFrameCapturer::OnCaptureCompleted, video_capturer_,
559 base::Passed(&frame)));
562 void DesktopSessionProxy::PostMouseCursor(
563 scoped_ptr<webrtc::MouseCursor> mouse_cursor) {
564 DCHECK(caller_task_runner_->BelongsToCurrentThread());
566 video_capture_task_runner_->PostTask(
567 FROM_HERE,
568 base::Bind(&IpcMouseCursorMonitor::OnMouseCursor, mouse_cursor_monitor_,
569 base::Passed(&mouse_cursor)));
572 void DesktopSessionProxy::SendToDesktop(IPC::Message* message) {
573 DCHECK(caller_task_runner_->BelongsToCurrentThread());
575 if (desktop_channel_) {
576 desktop_channel_->Send(message);
577 } else {
578 delete message;
582 // static
583 void DesktopSessionProxyTraits::Destruct(
584 const DesktopSessionProxy* desktop_session_proxy) {
585 desktop_session_proxy->caller_task_runner_->DeleteSoon(FROM_HERE,
586 desktop_session_proxy);
589 } // namespace remoting