Disable overview on alt-tab by default and update test expectation to match.
[chromium-blink-merge.git] / remoting / host / desktop_session_agent.cc
blob581f2c10a7201b1ebc7519e59751bbdd57f9eb5d
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_agent.h"
7 #include "base/file_util.h"
8 #include "base/logging.h"
9 #include "base/memory/shared_memory.h"
10 #include "ipc/ipc_channel_proxy.h"
11 #include "ipc/ipc_message.h"
12 #include "ipc/ipc_message_macros.h"
13 #include "remoting/base/auto_thread_task_runner.h"
14 #include "remoting/base/constants.h"
15 #include "remoting/host/audio_capturer.h"
16 #include "remoting/host/chromoting_messages.h"
17 #include "remoting/host/desktop_environment.h"
18 #include "remoting/host/input_injector.h"
19 #include "remoting/host/ipc_util.h"
20 #include "remoting/host/remote_input_filter.h"
21 #include "remoting/host/screen_controls.h"
22 #include "remoting/host/screen_resolution.h"
23 #include "remoting/proto/audio.pb.h"
24 #include "remoting/proto/control.pb.h"
25 #include "remoting/proto/event.pb.h"
26 #include "remoting/protocol/clipboard_stub.h"
27 #include "remoting/protocol/input_event_tracker.h"
28 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
29 #include "third_party/webrtc/modules/desktop_capture/shared_memory.h"
31 namespace remoting {
33 namespace {
35 // Routes local clipboard events though the IPC channel to the network process.
36 class DesktopSesssionClipboardStub : public protocol::ClipboardStub {
37 public:
38 explicit DesktopSesssionClipboardStub(
39 scoped_refptr<DesktopSessionAgent> desktop_session_agent);
40 virtual ~DesktopSesssionClipboardStub();
42 // protocol::ClipboardStub implementation.
43 virtual void InjectClipboardEvent(
44 const protocol::ClipboardEvent& event) OVERRIDE;
46 private:
47 scoped_refptr<DesktopSessionAgent> desktop_session_agent_;
49 DISALLOW_COPY_AND_ASSIGN(DesktopSesssionClipboardStub);
52 DesktopSesssionClipboardStub::DesktopSesssionClipboardStub(
53 scoped_refptr<DesktopSessionAgent> desktop_session_agent)
54 : desktop_session_agent_(desktop_session_agent) {
57 DesktopSesssionClipboardStub::~DesktopSesssionClipboardStub() {
60 void DesktopSesssionClipboardStub::InjectClipboardEvent(
61 const protocol::ClipboardEvent& event) {
62 desktop_session_agent_->InjectClipboardEvent(event);
65 } // namespace
67 // webrtc::SharedMemory implementation that notifies creating
68 // DesktopSessionAgent when it's deleted.
69 class DesktopSessionAgent::SharedBuffer : public webrtc::SharedMemory {
70 public:
71 static scoped_ptr<SharedBuffer> Create(DesktopSessionAgent* agent,
72 size_t size,
73 int id) {
74 scoped_ptr<base::SharedMemory> memory(new base::SharedMemory());
75 if (!memory->CreateAndMapAnonymous(size))
76 return scoped_ptr<SharedBuffer>();
77 return scoped_ptr<SharedBuffer>(
78 new SharedBuffer(agent, memory.Pass(), size, id));
81 virtual ~SharedBuffer() {
82 agent_->OnSharedBufferDeleted(id());
85 private:
86 SharedBuffer(DesktopSessionAgent* agent,
87 scoped_ptr<base::SharedMemory> memory,
88 size_t size,
89 int id)
90 : SharedMemory(memory->memory(), size,
91 #if defined(OS_WIN)
92 memory->handle(),
93 #else
94 memory->handle().fd,
95 #endif
96 id),
97 agent_(agent),
98 shared_memory_(memory.Pass()) {
101 DesktopSessionAgent* agent_;
102 scoped_ptr<base::SharedMemory> shared_memory_;
104 DISALLOW_COPY_AND_ASSIGN(SharedBuffer);
107 DesktopSessionAgent::Delegate::~Delegate() {
110 DesktopSessionAgent::DesktopSessionAgent(
111 scoped_refptr<AutoThreadTaskRunner> audio_capture_task_runner,
112 scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
113 scoped_refptr<AutoThreadTaskRunner> input_task_runner,
114 scoped_refptr<AutoThreadTaskRunner> io_task_runner,
115 scoped_refptr<AutoThreadTaskRunner> video_capture_task_runner)
116 : audio_capture_task_runner_(audio_capture_task_runner),
117 caller_task_runner_(caller_task_runner),
118 input_task_runner_(input_task_runner),
119 io_task_runner_(io_task_runner),
120 video_capture_task_runner_(video_capture_task_runner),
121 control_factory_(this),
122 desktop_pipe_(IPC::InvalidPlatformFileForTransit()),
123 next_shared_buffer_id_(1),
124 shared_buffers_(0),
125 started_(false) {
126 DCHECK(caller_task_runner_->BelongsToCurrentThread());
129 bool DesktopSessionAgent::OnMessageReceived(const IPC::Message& message) {
130 DCHECK(caller_task_runner_->BelongsToCurrentThread());
132 bool handled = true;
133 if (started_) {
134 IPC_BEGIN_MESSAGE_MAP(DesktopSessionAgent, message)
135 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_CaptureFrame,
136 OnCaptureFrame)
137 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectClipboardEvent,
138 OnInjectClipboardEvent)
139 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectKeyEvent,
140 OnInjectKeyEvent)
141 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectMouseEvent,
142 OnInjectMouseEvent)
143 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_SetScreenResolution,
144 SetScreenResolution)
145 IPC_MESSAGE_UNHANDLED(handled = false)
146 IPC_END_MESSAGE_MAP()
147 } else {
148 IPC_BEGIN_MESSAGE_MAP(DesktopSessionAgent, message)
149 IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_StartSessionAgent,
150 OnStartSessionAgent)
151 IPC_MESSAGE_UNHANDLED(handled = false)
152 IPC_END_MESSAGE_MAP()
155 CHECK(handled) << "Received unexpected IPC type: " << message.type();
156 return handled;
159 void DesktopSessionAgent::OnChannelConnected(int32 peer_pid) {
160 DCHECK(caller_task_runner_->BelongsToCurrentThread());
162 VLOG(1) << "IPC: desktop <- network (" << peer_pid << ")";
164 CloseDesktopPipeHandle();
167 void DesktopSessionAgent::OnChannelError() {
168 DCHECK(caller_task_runner_->BelongsToCurrentThread());
170 // Make sure the channel is closed.
171 network_channel_.reset();
172 CloseDesktopPipeHandle();
174 // Notify the caller that the channel has been disconnected.
175 if (delegate_.get())
176 delegate_->OnNetworkProcessDisconnected();
179 webrtc::SharedMemory* DesktopSessionAgent::CreateSharedMemory(size_t size) {
180 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
182 scoped_ptr<SharedBuffer> buffer =
183 SharedBuffer::Create(this, size, next_shared_buffer_id_);
184 if (buffer) {
185 shared_buffers_++;
187 // |next_shared_buffer_id_| starts from 1 and incrementing it by 2 makes
188 // sure it is always odd and therefore zero is never used as a valid buffer
189 // ID.
191 // It is very unlikely (though theoretically possible) to allocate the same
192 // ID for two different buffers due to integer overflow. It should take
193 // about a year of allocating 100 new buffers every second. Practically
194 // speaking it never happens.
195 next_shared_buffer_id_ += 2;
197 IPC::PlatformFileForTransit handle;
198 #if defined(OS_WIN)
199 handle = buffer->handle();
200 #else
201 handle = base::FileDescriptor(buffer->handle(), false);
202 #endif
203 SendToNetwork(new ChromotingDesktopNetworkMsg_CreateSharedBuffer(
204 buffer->id(), handle, buffer->size()));
207 return buffer.release();
210 DesktopSessionAgent::~DesktopSessionAgent() {
211 DCHECK(!audio_capturer_);
212 DCHECK(!desktop_environment_);
213 DCHECK(!network_channel_);
214 DCHECK(!screen_controls_);
215 DCHECK(!video_capturer_);
217 CloseDesktopPipeHandle();
220 const std::string& DesktopSessionAgent::client_jid() const {
221 return client_jid_;
224 void DesktopSessionAgent::DisconnectSession() {
225 SendToNetwork(new ChromotingDesktopNetworkMsg_DisconnectSession());
228 void DesktopSessionAgent::OnLocalMouseMoved(const SkIPoint& new_pos) {
229 DCHECK(caller_task_runner_->BelongsToCurrentThread());
231 remote_input_filter_->LocalMouseMoved(new_pos);
234 void DesktopSessionAgent::SetDisableInputs(bool disable_inputs) {
235 DCHECK(caller_task_runner_->BelongsToCurrentThread());
237 // Do not expect this method to be called because it is only used by It2Me.
238 NOTREACHED();
241 void DesktopSessionAgent::OnStartSessionAgent(
242 const std::string& authenticated_jid,
243 const ScreenResolution& resolution,
244 bool virtual_terminal) {
245 DCHECK(caller_task_runner_->BelongsToCurrentThread());
246 DCHECK(!started_);
247 DCHECK(!audio_capturer_);
248 DCHECK(!desktop_environment_);
249 DCHECK(!input_injector_);
250 DCHECK(!screen_controls_);
251 DCHECK(!video_capturer_);
253 started_ = true;
254 client_jid_ = authenticated_jid;
256 // Enable the curtain mode.
257 delegate_->desktop_environment_factory().SetEnableCurtaining(
258 virtual_terminal);
260 // Create a desktop environment for the new session.
261 desktop_environment_ = delegate_->desktop_environment_factory().Create(
262 control_factory_.GetWeakPtr());
264 // Create the session controller and set the initial screen resolution.
265 screen_controls_ = desktop_environment_->CreateScreenControls();
266 SetScreenResolution(resolution);
268 // Create the input injector.
269 input_injector_ = desktop_environment_->CreateInputInjector();
271 // Hook up the input filter.
272 input_tracker_.reset(new protocol::InputEventTracker(input_injector_.get()));
273 remote_input_filter_.reset(new RemoteInputFilter(input_tracker_.get()));
275 #if defined(OS_WIN)
276 // LocalInputMonitorWin filters out an echo of the injected input before it
277 // reaches |remote_input_filter_|.
278 remote_input_filter_->SetExpectLocalEcho(false);
279 #endif // defined(OS_WIN)
281 // Start the input injector.
282 scoped_ptr<protocol::ClipboardStub> clipboard_stub(
283 new DesktopSesssionClipboardStub(this));
284 input_injector_->Start(clipboard_stub.Pass());
286 // Start the audio capturer.
287 if (delegate_->desktop_environment_factory().SupportsAudioCapture()) {
288 audio_capturer_ = desktop_environment_->CreateAudioCapturer();
289 audio_capture_task_runner_->PostTask(
290 FROM_HERE, base::Bind(&DesktopSessionAgent::StartAudioCapturer, this));
293 // Start the video capturer.
294 video_capturer_ = desktop_environment_->CreateVideoCapturer();
295 video_capture_task_runner_->PostTask(
296 FROM_HERE, base::Bind(&DesktopSessionAgent::StartVideoCapturer, this));
299 void DesktopSessionAgent::OnCaptureCompleted(webrtc::DesktopFrame* frame) {
300 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
302 last_frame_.reset(frame);
304 current_size_ = frame->size();
306 // Serialize webrtc::DesktopFrame.
307 SerializedDesktopFrame serialized_frame;
308 serialized_frame.shared_buffer_id = frame->shared_memory()->id();
309 serialized_frame.bytes_per_row = frame->stride();
310 serialized_frame.dimensions = frame->size();
311 serialized_frame.capture_time_ms = frame->capture_time_ms();
312 serialized_frame.dpi = frame->dpi();
313 for (webrtc::DesktopRegion::Iterator i(frame->updated_region());
314 !i.IsAtEnd(); i.Advance()) {
315 serialized_frame.dirty_region.push_back(i.rect());
318 SendToNetwork(
319 new ChromotingDesktopNetworkMsg_CaptureCompleted(serialized_frame));
322 void DesktopSessionAgent::OnCursorShapeChanged(
323 webrtc::MouseCursorShape* cursor_shape) {
324 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
326 scoped_ptr<webrtc::MouseCursorShape> owned_cursor(cursor_shape);
328 SendToNetwork(new ChromotingDesktopNetworkMsg_CursorShapeChanged(
329 *cursor_shape));
332 void DesktopSessionAgent::InjectClipboardEvent(
333 const protocol::ClipboardEvent& event) {
334 DCHECK(caller_task_runner_->BelongsToCurrentThread());
336 std::string serialized_event;
337 if (!event.SerializeToString(&serialized_event)) {
338 LOG(ERROR) << "Failed to serialize protocol::ClipboardEvent.";
339 return;
342 SendToNetwork(
343 new ChromotingDesktopNetworkMsg_InjectClipboardEvent(serialized_event));
346 void DesktopSessionAgent::ProcessAudioPacket(scoped_ptr<AudioPacket> packet) {
347 DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
349 std::string serialized_packet;
350 if (!packet->SerializeToString(&serialized_packet)) {
351 LOG(ERROR) << "Failed to serialize AudioPacket.";
352 return;
355 SendToNetwork(new ChromotingDesktopNetworkMsg_AudioPacket(serialized_packet));
358 bool DesktopSessionAgent::Start(const base::WeakPtr<Delegate>& delegate,
359 IPC::PlatformFileForTransit* desktop_pipe_out) {
360 DCHECK(caller_task_runner_->BelongsToCurrentThread());
361 DCHECK(delegate_.get() == NULL);
363 delegate_ = delegate;
365 // Create an IPC channel to communicate with the network process.
366 bool result = CreateConnectedIpcChannel(io_task_runner_,
367 this,
368 &desktop_pipe_,
369 &network_channel_);
370 *desktop_pipe_out = desktop_pipe_;
371 return result;
374 void DesktopSessionAgent::Stop() {
375 DCHECK(caller_task_runner_->BelongsToCurrentThread());
377 delegate_.reset();
379 // Make sure the channel is closed.
380 network_channel_.reset();
382 if (started_) {
383 started_ = false;
385 // Ignore any further callbacks.
386 control_factory_.InvalidateWeakPtrs();
387 client_jid_.clear();
389 remote_input_filter_.reset();
391 // Ensure that any pressed keys or buttons are released.
392 input_tracker_->ReleaseAll();
393 input_tracker_.reset();
395 desktop_environment_.reset();
396 input_injector_.reset();
397 screen_controls_.reset();
399 // Stop the audio capturer.
400 audio_capture_task_runner_->PostTask(
401 FROM_HERE, base::Bind(&DesktopSessionAgent::StopAudioCapturer, this));
403 // Stop the video capturer.
404 video_capture_task_runner_->PostTask(
405 FROM_HERE, base::Bind(&DesktopSessionAgent::StopVideoCapturer, this));
409 void DesktopSessionAgent::OnCaptureFrame() {
410 if (!video_capture_task_runner_->BelongsToCurrentThread()) {
411 video_capture_task_runner_->PostTask(
412 FROM_HERE,
413 base::Bind(&DesktopSessionAgent::OnCaptureFrame, this));
414 return;
417 // webrtc::ScreenCapturer supports a very few (currently 2) outstanding
418 // capture requests. The requests are serialized on
419 // |video_capture_task_runner()| task runner. If the client issues more
420 // requests, pixel data in captured frames will likely be corrupted but
421 // stability of webrtc::ScreenCapturer will not be affected.
422 video_capturer_->Capture(webrtc::DesktopRegion());
425 void DesktopSessionAgent::OnInjectClipboardEvent(
426 const std::string& serialized_event) {
427 DCHECK(caller_task_runner_->BelongsToCurrentThread());
429 protocol::ClipboardEvent event;
430 if (!event.ParseFromString(serialized_event)) {
431 LOG(ERROR) << "Failed to parse protocol::ClipboardEvent.";
432 return;
435 // InputStub implementations must verify events themselves, so we don't need
436 // verification here. This matches HostEventDispatcher.
437 input_injector_->InjectClipboardEvent(event);
440 void DesktopSessionAgent::OnInjectKeyEvent(
441 const std::string& serialized_event) {
442 DCHECK(caller_task_runner_->BelongsToCurrentThread());
444 protocol::KeyEvent event;
445 if (!event.ParseFromString(serialized_event)) {
446 LOG(ERROR) << "Failed to parse protocol::KeyEvent.";
447 return;
450 // InputStub implementations must verify events themselves, so we need only
451 // basic verification here. This matches HostEventDispatcher.
452 if (!event.has_usb_keycode() || !event.has_pressed()) {
453 LOG(ERROR) << "Received invalid key event.";
454 return;
457 remote_input_filter_->InjectKeyEvent(event);
460 void DesktopSessionAgent::OnInjectMouseEvent(
461 const std::string& serialized_event) {
462 DCHECK(caller_task_runner_->BelongsToCurrentThread());
464 protocol::MouseEvent event;
465 if (!event.ParseFromString(serialized_event)) {
466 LOG(ERROR) << "Failed to parse protocol::MouseEvent.";
467 return;
470 // InputStub implementations must verify events themselves, so we don't need
471 // verification here. This matches HostEventDispatcher.
472 remote_input_filter_->InjectMouseEvent(event);
475 void DesktopSessionAgent::SetScreenResolution(
476 const ScreenResolution& resolution) {
477 DCHECK(caller_task_runner_->BelongsToCurrentThread());
479 if (screen_controls_ && resolution.IsEmpty())
480 screen_controls_->SetScreenResolution(resolution);
483 void DesktopSessionAgent::SendToNetwork(IPC::Message* message) {
484 if (!caller_task_runner_->BelongsToCurrentThread()) {
485 caller_task_runner_->PostTask(
486 FROM_HERE,
487 base::Bind(&DesktopSessionAgent::SendToNetwork, this, message));
488 return;
491 if (network_channel_) {
492 network_channel_->Send(message);
493 } else {
494 delete message;
498 void DesktopSessionAgent::StartAudioCapturer() {
499 DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
501 if (audio_capturer_) {
502 audio_capturer_->Start(base::Bind(&DesktopSessionAgent::ProcessAudioPacket,
503 this));
507 void DesktopSessionAgent::StopAudioCapturer() {
508 DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
510 audio_capturer_.reset();
513 void DesktopSessionAgent::StartVideoCapturer() {
514 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
516 if (video_capturer_) {
517 video_capturer_->SetMouseShapeObserver(this);
518 video_capturer_->Start(this);
522 void DesktopSessionAgent::StopVideoCapturer() {
523 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
525 video_capturer_.reset();
526 last_frame_.reset();
528 // Video capturer must delete all buffers.
529 DCHECK_EQ(shared_buffers_, 0);
532 void DesktopSessionAgent::OnSharedBufferDeleted(int id) {
533 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
534 DCHECK(id != 0);
536 shared_buffers_--;
537 DCHECK_GE(shared_buffers_, 0);
538 SendToNetwork(new ChromotingDesktopNetworkMsg_ReleaseSharedBuffer(id));
541 void DesktopSessionAgent::CloseDesktopPipeHandle() {
542 if (!(desktop_pipe_ == IPC::InvalidPlatformFileForTransit())) {
543 #if defined(OS_WIN)
544 base::ClosePlatformFile(desktop_pipe_);
545 #elif defined(OS_POSIX)
546 base::ClosePlatformFile(desktop_pipe_.fd);
547 #else // !defined(OS_POSIX)
548 #error Unsupported platform.
549 #endif // !defined(OS_POSIX)
551 desktop_pipe_ = IPC::InvalidPlatformFileForTransit();
555 } // namespace remoting