1 // Copyright (c) 2013 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.
6 #include "base/bind_helpers.h"
7 #include "base/callback.h"
8 #include "base/memory/ref_counted.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/process/process.h"
12 #include "base/process/process_handle.h"
13 #include "base/run_loop.h"
14 #include "ipc/ipc_channel.h"
15 #include "ipc/ipc_channel_proxy.h"
16 #include "ipc/ipc_listener.h"
17 #include "ipc/ipc_message.h"
18 #include "ipc/ipc_platform_file.h"
19 #include "remoting/base/auto_thread.h"
20 #include "remoting/base/auto_thread_task_runner.h"
21 #include "remoting/base/constants.h"
22 #include "remoting/host/chromoting_messages.h"
23 #include "remoting/host/desktop_process.h"
24 #include "remoting/host/desktop_session.h"
25 #include "remoting/host/desktop_session_connector.h"
26 #include "remoting/host/desktop_session_proxy.h"
27 #include "remoting/host/fake_desktop_capturer.h"
28 #include "remoting/host/fake_mouse_cursor_monitor.h"
29 #include "remoting/host/host_mock_objects.h"
30 #include "remoting/host/ipc_desktop_environment.h"
31 #include "remoting/protocol/protocol_mock_objects.h"
32 #include "testing/gmock/include/gmock/gmock.h"
33 #include "testing/gtest/include/gtest/gtest.h"
34 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
35 #include "third_party/webrtc/modules/desktop_capture/desktop_region.h"
36 #include "third_party/webrtc/modules/desktop_capture/screen_capturer_mock_objects.h"
39 using testing::AnyNumber
;
40 using testing::AtLeast
;
41 using testing::AtMost
;
42 using testing::DeleteArg
;
44 using testing::Return
;
45 using testing::ReturnRef
;
51 // Receives messages sent from the network process to the daemon.
52 class FakeDaemonSender
: public IPC::Sender
{
55 virtual ~FakeDaemonSender() {}
57 // IPC::Sender implementation.
58 virtual bool Send(IPC::Message
* message
) override
;
60 MOCK_METHOD3(ConnectTerminal
, void(int, const ScreenResolution
&, bool));
61 MOCK_METHOD1(DisconnectTerminal
, void(int));
62 MOCK_METHOD2(SetScreenResolution
, void(int, const ScreenResolution
&));
65 void OnMessageReceived(const IPC::Message
& message
);
67 DISALLOW_COPY_AND_ASSIGN(FakeDaemonSender
);
70 // Receives messages sent from the desktop process to the daemon.
71 class MockDaemonListener
: public IPC::Listener
{
73 MockDaemonListener() {}
74 virtual ~MockDaemonListener() {}
76 virtual bool OnMessageReceived(const IPC::Message
& message
) override
;
78 MOCK_METHOD1(OnDesktopAttached
, void(IPC::PlatformFileForTransit
));
79 MOCK_METHOD1(OnChannelConnected
, void(int32
));
80 MOCK_METHOD0(OnChannelError
, void());
83 DISALLOW_COPY_AND_ASSIGN(MockDaemonListener
);
86 bool FakeDaemonSender::Send(IPC::Message
* message
) {
87 OnMessageReceived(*message
);
92 void FakeDaemonSender::OnMessageReceived(const IPC::Message
& message
) {
94 IPC_BEGIN_MESSAGE_MAP(FakeDaemonSender
, message
)
95 IPC_MESSAGE_HANDLER(ChromotingNetworkHostMsg_ConnectTerminal
,
97 IPC_MESSAGE_HANDLER(ChromotingNetworkHostMsg_DisconnectTerminal
,
99 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_SetScreenResolution
,
101 IPC_MESSAGE_UNHANDLED(handled
= false)
102 IPC_END_MESSAGE_MAP()
104 EXPECT_TRUE(handled
);
107 bool MockDaemonListener::OnMessageReceived(const IPC::Message
& message
) {
109 IPC_BEGIN_MESSAGE_MAP(MockDaemonListener
, message
)
110 IPC_MESSAGE_HANDLER(ChromotingDesktopDaemonMsg_DesktopAttached
,
112 IPC_MESSAGE_UNHANDLED(handled
= false)
113 IPC_END_MESSAGE_MAP()
115 EXPECT_TRUE(handled
);
121 class IpcDesktopEnvironmentTest
: public testing::Test
{
123 IpcDesktopEnvironmentTest();
124 ~IpcDesktopEnvironmentTest() override
;
126 void SetUp() override
;
128 void ConnectTerminal(int terminal_id
,
129 const ScreenResolution
& resolution
,
130 bool virtual_terminal
);
131 void DisconnectTerminal(int terminal_id
);
133 // Creates a DesktopEnvironment with a fake webrtc::DesktopCapturer, to mock
134 // DesktopEnvironmentFactory::Create().
135 DesktopEnvironment
* CreateDesktopEnvironment();
137 // Creates a dummy InputInjector, to mock
138 // DesktopEnvironment::CreateInputInjector().
139 InputInjector
* CreateInputInjector();
141 // Creates a fake webrtc::DesktopCapturer, to mock
142 // DesktopEnvironment::CreateVideoCapturer().
143 webrtc::DesktopCapturer
* CreateVideoCapturer();
145 // Creates a MockMouseCursorMonitor, to mock
146 // DesktopEnvironment::CreateMouseCursorMonitor
147 webrtc::MouseCursorMonitor
* CreateMouseCursorMonitor();
149 void DeleteDesktopEnvironment();
151 // Forwards |event| to |clipboard_stub_|.
152 void ReflectClipboardEvent(const protocol::ClipboardEvent
& event
);
155 // Creates and starts an instance of desktop process object.
156 void CreateDesktopProcess();
158 // Destroys the desktop process object created by CreateDesktopProcess().
159 void DestoyDesktopProcess();
161 void OnDisconnectCallback();
163 // Invoked when ChromotingDesktopDaemonMsg_DesktopAttached message is
165 void OnDesktopAttached(IPC::PlatformFileForTransit desktop_pipe
);
167 // The main message loop.
168 base::MessageLoopForUI message_loop_
;
170 // Runs until |desktop_session_proxy_| is connected to the desktop.
171 scoped_ptr
<base::RunLoop
> setup_run_loop_
;
173 // Runs until there are references to |task_runner_|.
174 base::RunLoop main_run_loop_
;
176 scoped_refptr
<AutoThreadTaskRunner
> task_runner_
;
177 scoped_refptr
<AutoThreadTaskRunner
> io_task_runner_
;
179 std::string client_jid_
;
181 // Clipboard stub that receives clipboard events from the desktop process.
182 protocol::ClipboardStub
* clipboard_stub_
;
184 // The daemons's end of the daemon-to-desktop channel.
185 scoped_ptr
<IPC::ChannelProxy
> desktop_channel_
;
187 // Name of the daemon-to-desktop channel.
188 std::string desktop_channel_name_
;
190 // Delegate that is passed to |desktop_channel_|.
191 MockDaemonListener desktop_listener_
;
193 FakeDaemonSender daemon_channel_
;
195 scoped_ptr
<IpcDesktopEnvironmentFactory
> desktop_environment_factory_
;
196 scoped_ptr
<DesktopEnvironment
> desktop_environment_
;
198 // The IPC input injector.
199 scoped_ptr
<InputInjector
> input_injector_
;
201 // The IPC screen controls.
202 scoped_ptr
<ScreenControls
> screen_controls_
;
204 // The IPC screen capturer.
205 scoped_ptr
<webrtc::DesktopCapturer
> video_capturer_
;
207 // Represents the desktop process running in a user session.
208 scoped_ptr
<DesktopProcess
> desktop_process_
;
210 // Input injector owned by |desktop_process_|.
211 MockInputInjector
* remote_input_injector_
;
213 // The last |terminal_id| passed to ConnectTermina();
216 webrtc::MockScreenCapturerCallback desktop_capturer_callback_
;
218 MockClientSessionControl client_session_control_
;
219 base::WeakPtrFactory
<ClientSessionControl
> client_session_control_factory_
;
222 IpcDesktopEnvironmentTest::IpcDesktopEnvironmentTest()
223 : client_jid_("user@domain/rest-of-jid"),
224 clipboard_stub_(nullptr),
225 remote_input_injector_(nullptr),
227 client_session_control_factory_(&client_session_control_
) {
230 IpcDesktopEnvironmentTest::~IpcDesktopEnvironmentTest() {
233 void IpcDesktopEnvironmentTest::SetUp() {
234 // Arrange to run |message_loop_| until no components depend on it.
235 task_runner_
= new AutoThreadTaskRunner(
236 message_loop_
.message_loop_proxy(), main_run_loop_
.QuitClosure());
238 io_task_runner_
= AutoThread::CreateWithType(
239 "IPC thread", task_runner_
, base::MessageLoop::TYPE_IO
);
241 setup_run_loop_
.reset(new base::RunLoop());
243 // Set expectation that the DaemonProcess will send DesktopAttached message
245 EXPECT_CALL(desktop_listener_
, OnChannelConnected(_
))
247 EXPECT_CALL(desktop_listener_
, OnDesktopAttached(_
))
249 .WillRepeatedly(Invoke(this,
250 &IpcDesktopEnvironmentTest::OnDesktopAttached
));
251 EXPECT_CALL(desktop_listener_
, OnChannelError())
253 .WillOnce(Invoke(this,
254 &IpcDesktopEnvironmentTest::DestoyDesktopProcess
));
256 // Intercept requests to connect and disconnect a terminal.
257 EXPECT_CALL(daemon_channel_
, ConnectTerminal(_
, _
, _
))
259 .WillRepeatedly(Invoke(this,
260 &IpcDesktopEnvironmentTest::ConnectTerminal
));
261 EXPECT_CALL(daemon_channel_
, DisconnectTerminal(_
))
263 .WillRepeatedly(Invoke(this,
264 &IpcDesktopEnvironmentTest::DisconnectTerminal
));
266 EXPECT_CALL(client_session_control_
, client_jid())
268 .WillRepeatedly(ReturnRef(client_jid_
));
269 EXPECT_CALL(client_session_control_
, DisconnectSession())
271 .WillRepeatedly(Invoke(
272 this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment
));
273 EXPECT_CALL(client_session_control_
, OnLocalMouseMoved(_
))
275 EXPECT_CALL(client_session_control_
, SetDisableInputs(_
))
278 // Create a desktop environment instance.
279 desktop_environment_factory_
.reset(new IpcDesktopEnvironmentFactory(
285 desktop_environment_
= desktop_environment_factory_
->Create(
286 client_session_control_factory_
.GetWeakPtr());
288 screen_controls_
= desktop_environment_
->CreateScreenControls();
290 // Create the input injector.
291 input_injector_
= desktop_environment_
->CreateInputInjector();
293 // Create the screen capturer.
295 desktop_environment_
->CreateVideoCapturer();
297 desktop_environment_
->SetCapabilities(std::string());
300 void IpcDesktopEnvironmentTest::ConnectTerminal(
302 const ScreenResolution
& resolution
,
303 bool virtual_terminal
) {
304 EXPECT_NE(terminal_id_
, terminal_id
);
306 terminal_id_
= terminal_id
;
307 CreateDesktopProcess();
310 void IpcDesktopEnvironmentTest::DisconnectTerminal(int terminal_id
) {
311 EXPECT_EQ(terminal_id_
, terminal_id
);
313 // The IPC desktop environment is fully destroyed now. Release the remaining
315 desktop_environment_factory_
.reset();
318 DesktopEnvironment
* IpcDesktopEnvironmentTest::CreateDesktopEnvironment() {
319 MockDesktopEnvironment
* desktop_environment
= new MockDesktopEnvironment();
320 EXPECT_CALL(*desktop_environment
, CreateAudioCapturerPtr())
322 EXPECT_CALL(*desktop_environment
, CreateInputInjectorPtr())
325 this, &IpcDesktopEnvironmentTest::CreateInputInjector
));
326 EXPECT_CALL(*desktop_environment
, CreateScreenControlsPtr())
328 EXPECT_CALL(*desktop_environment
, CreateVideoCapturerPtr())
331 this, &IpcDesktopEnvironmentTest::CreateVideoCapturer
));
332 EXPECT_CALL(*desktop_environment
, CreateMouseCursorMonitorPtr())
335 this, &IpcDesktopEnvironmentTest::CreateMouseCursorMonitor
));
336 EXPECT_CALL(*desktop_environment
, GetCapabilities())
338 EXPECT_CALL(*desktop_environment
, SetCapabilities(_
))
341 // Let tests know that the remote desktop environment is created.
342 message_loop_
.PostTask(FROM_HERE
, setup_run_loop_
->QuitClosure());
344 return desktop_environment
;
347 InputInjector
* IpcDesktopEnvironmentTest::CreateInputInjector() {
348 EXPECT_TRUE(remote_input_injector_
== nullptr);
349 remote_input_injector_
= new testing::StrictMock
<MockInputInjector
>();
351 EXPECT_CALL(*remote_input_injector_
, StartPtr(_
));
352 return remote_input_injector_
;
355 webrtc::DesktopCapturer
* IpcDesktopEnvironmentTest::CreateVideoCapturer() {
356 return new FakeDesktopCapturer();
359 webrtc::MouseCursorMonitor
*
360 IpcDesktopEnvironmentTest::CreateMouseCursorMonitor() {
361 return new FakeMouseCursorMonitor();
364 void IpcDesktopEnvironmentTest::DeleteDesktopEnvironment() {
365 input_injector_
.reset();
366 screen_controls_
.reset();
367 video_capturer_
.reset();
369 // Trigger DisconnectTerminal().
370 desktop_environment_
.reset();
373 void IpcDesktopEnvironmentTest::ReflectClipboardEvent(
374 const protocol::ClipboardEvent
& event
) {
375 clipboard_stub_
->InjectClipboardEvent(event
);
378 void IpcDesktopEnvironmentTest::CreateDesktopProcess() {
379 EXPECT_TRUE(task_runner_
.get());
380 EXPECT_TRUE(io_task_runner_
.get());
382 // Create the daemon end of the daemon-to-desktop channel.
383 desktop_channel_name_
= IPC::Channel::GenerateUniqueRandomChannelID();
385 IPC::ChannelProxy::Create(IPC::ChannelHandle(desktop_channel_name_
),
386 IPC::Channel::MODE_SERVER
,
388 io_task_runner_
.get());
390 // Create and start the desktop process.
391 desktop_process_
.reset(new DesktopProcess(task_runner_
,
393 desktop_channel_name_
));
395 scoped_ptr
<MockDesktopEnvironmentFactory
> desktop_environment_factory(
396 new MockDesktopEnvironmentFactory());
397 EXPECT_CALL(*desktop_environment_factory
, CreatePtr())
399 .WillRepeatedly(Invoke(
400 this, &IpcDesktopEnvironmentTest::CreateDesktopEnvironment
));
401 EXPECT_CALL(*desktop_environment_factory
, SupportsAudioCapture())
403 .WillRepeatedly(Return(false));
405 EXPECT_TRUE(desktop_process_
->Start(desktop_environment_factory
.Pass()));
408 void IpcDesktopEnvironmentTest::DestoyDesktopProcess() {
409 desktop_channel_
.reset();
410 if (desktop_process_
) {
411 desktop_process_
->OnChannelError();
412 desktop_process_
.reset();
414 remote_input_injector_
= nullptr;
417 void IpcDesktopEnvironmentTest::OnDisconnectCallback() {
418 DeleteDesktopEnvironment();
421 void IpcDesktopEnvironmentTest::OnDesktopAttached(
422 IPC::PlatformFileForTransit desktop_pipe
) {
424 base::ProcessHandle process_handle
= base::GetCurrentProcessHandle();
426 ASSERT_NE(FALSE
, ::DuplicateHandle(GetCurrentProcess(), process_handle
,
427 GetCurrentProcess(), &process_handle
,
428 0, FALSE
, DUPLICATE_SAME_ACCESS
));
431 // Instruct DesktopSessionProxy to connect to the network-to-desktop pipe.
432 desktop_environment_factory_
->OnDesktopSessionAgentAttached(
433 terminal_id_
, process_handle
, desktop_pipe
);
436 // Runs until the desktop is attached and exits immediately after that.
437 TEST_F(IpcDesktopEnvironmentTest
, Basic
) {
438 scoped_ptr
<protocol::MockClipboardStub
> clipboard_stub(
439 new protocol::MockClipboardStub());
440 EXPECT_CALL(*clipboard_stub
, InjectClipboardEvent(_
))
443 // Start the input injector and screen capturer.
444 input_injector_
->Start(clipboard_stub
.Pass());
446 // Run the message loop until the desktop is attached.
447 setup_run_loop_
->Run();
450 DeleteDesktopEnvironment();
452 task_runner_
= nullptr;
453 io_task_runner_
= nullptr;
454 main_run_loop_
.Run();
457 // Tests that the video capturer receives a frame over IPC.
458 TEST_F(IpcDesktopEnvironmentTest
, CaptureFrame
) {
459 scoped_ptr
<protocol::MockClipboardStub
> clipboard_stub(
460 new protocol::MockClipboardStub());
461 EXPECT_CALL(*clipboard_stub
, InjectClipboardEvent(_
))
464 // Start the input injector and screen capturer.
465 input_injector_
->Start(clipboard_stub
.Pass());
466 video_capturer_
->Start(&desktop_capturer_callback_
);
468 // Run the message loop until the desktop is attached.
469 setup_run_loop_
->Run();
471 // Stop the test when the first frame is captured.
472 EXPECT_CALL(desktop_capturer_callback_
, OnCaptureCompleted(_
))
476 this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment
)));
478 // Capture a single frame.
479 video_capturer_
->Capture(webrtc::DesktopRegion());
481 task_runner_
= nullptr;
482 io_task_runner_
= nullptr;
483 main_run_loop_
.Run();
486 // Tests that attaching to a new desktop works.
487 TEST_F(IpcDesktopEnvironmentTest
, Reattach
) {
488 scoped_ptr
<protocol::MockClipboardStub
> clipboard_stub(
489 new protocol::MockClipboardStub());
490 EXPECT_CALL(*clipboard_stub
, InjectClipboardEvent(_
))
493 // Start the input injector and screen capturer.
494 input_injector_
->Start(clipboard_stub
.Pass());
495 video_capturer_
->Start(&desktop_capturer_callback_
);
497 // Run the message loop until the desktop is attached.
498 setup_run_loop_
->Run();
500 // Create and start a new desktop process object.
501 setup_run_loop_
.reset(new base::RunLoop());
502 DestoyDesktopProcess();
503 CreateDesktopProcess();
504 setup_run_loop_
->Run();
507 DeleteDesktopEnvironment();
509 task_runner_
= nullptr;
510 io_task_runner_
= nullptr;
511 main_run_loop_
.Run();
514 // Tests injection of clipboard events.
515 TEST_F(IpcDesktopEnvironmentTest
, InjectClipboardEvent
) {
516 scoped_ptr
<protocol::MockClipboardStub
> clipboard_stub(
517 new protocol::MockClipboardStub());
518 clipboard_stub_
= clipboard_stub
.get();
520 // Stop the test when a clipboard event is received from the desktop process.
521 EXPECT_CALL(*clipboard_stub
, InjectClipboardEvent(_
))
523 .WillOnce(InvokeWithoutArgs(
524 this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment
));
526 // Start the input injector and screen capturer.
527 input_injector_
->Start(clipboard_stub
.Pass());
528 video_capturer_
->Start(&desktop_capturer_callback_
);
530 // Run the message loop until the desktop is attached.
531 setup_run_loop_
->Run();
533 // Expect a single clipboard event.
534 EXPECT_CALL(*remote_input_injector_
, InjectClipboardEvent(_
))
536 .WillOnce(Invoke(this,
537 &IpcDesktopEnvironmentTest::ReflectClipboardEvent
));
539 // Send a clipboard event.
540 protocol::ClipboardEvent event
;
541 event
.set_mime_type(kMimeTypeTextUtf8
);
543 input_injector_
->InjectClipboardEvent(event
);
545 task_runner_
= nullptr;
546 io_task_runner_
= nullptr;
547 main_run_loop_
.Run();
550 // Tests injection of key events.
551 TEST_F(IpcDesktopEnvironmentTest
, InjectKeyEvent
) {
552 scoped_ptr
<protocol::MockClipboardStub
> clipboard_stub(
553 new protocol::MockClipboardStub());
554 EXPECT_CALL(*clipboard_stub
, InjectClipboardEvent(_
))
557 // Start the input injector and screen capturer.
558 input_injector_
->Start(clipboard_stub
.Pass());
559 video_capturer_
->Start(&desktop_capturer_callback_
);
561 // Run the message loop until the desktop is attached.
562 setup_run_loop_
->Run();
564 // Expect a single key event.
565 EXPECT_CALL(*remote_input_injector_
, InjectKeyEvent(_
))
567 .WillRepeatedly(InvokeWithoutArgs(
568 this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment
));
571 protocol::KeyEvent event
;
572 event
.set_usb_keycode(0x070004);
573 event
.set_pressed(true);
574 input_injector_
->InjectKeyEvent(event
);
576 task_runner_
= nullptr;
577 io_task_runner_
= nullptr;
578 main_run_loop_
.Run();
581 // Tests injection of text events.
582 TEST_F(IpcDesktopEnvironmentTest
, InjectTextEvent
) {
583 scoped_ptr
<protocol::MockClipboardStub
> clipboard_stub(
584 new protocol::MockClipboardStub());
585 EXPECT_CALL(*clipboard_stub
, InjectClipboardEvent(_
))
588 // Start the input injector and screen capturer.
589 input_injector_
->Start(clipboard_stub
.Pass());
590 video_capturer_
->Start(&desktop_capturer_callback_
);
592 // Run the message loop until the desktop is attached.
593 setup_run_loop_
->Run();
595 // Expect a single text event.
596 EXPECT_CALL(*remote_input_injector_
, InjectTextEvent(_
))
598 .WillRepeatedly(InvokeWithoutArgs(
599 this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment
));
601 // Send a text event.
602 protocol::TextEvent event
;
603 event
.set_text("hello");
604 input_injector_
->InjectTextEvent(event
);
606 task_runner_
= nullptr;
607 io_task_runner_
= nullptr;
608 main_run_loop_
.Run();
611 // Tests injection of mouse events.
612 TEST_F(IpcDesktopEnvironmentTest
, InjectMouseEvent
) {
613 scoped_ptr
<protocol::MockClipboardStub
> clipboard_stub(
614 new protocol::MockClipboardStub());
615 EXPECT_CALL(*clipboard_stub
, InjectClipboardEvent(_
))
618 // Start the input injector and screen capturer.
619 input_injector_
->Start(clipboard_stub
.Pass());
620 video_capturer_
->Start(&desktop_capturer_callback_
);
622 // Run the message loop until the desktop is attached.
623 setup_run_loop_
->Run();
625 // Expect a single mouse event.
626 EXPECT_CALL(*remote_input_injector_
, InjectMouseEvent(_
))
628 .WillOnce(InvokeWithoutArgs(
629 this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment
));
631 // Send a mouse event.
632 protocol::MouseEvent event
;
635 input_injector_
->InjectMouseEvent(event
);
637 task_runner_
= nullptr;
638 io_task_runner_
= nullptr;
639 main_run_loop_
.Run();
642 // Tests that setting the desktop resolution works.
643 TEST_F(IpcDesktopEnvironmentTest
, SetScreenResolution
) {
644 scoped_ptr
<protocol::MockClipboardStub
> clipboard_stub(
645 new protocol::MockClipboardStub());
646 EXPECT_CALL(*clipboard_stub
, InjectClipboardEvent(_
))
649 // Start the input injector and screen capturer.
650 input_injector_
->Start(clipboard_stub
.Pass());
651 video_capturer_
->Start(&desktop_capturer_callback_
);
653 // Run the message loop until the desktop is attached.
654 setup_run_loop_
->Run();
656 EXPECT_CALL(daemon_channel_
, SetScreenResolution(_
, _
))
658 .WillOnce(InvokeWithoutArgs(
659 this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment
));
661 // Change the desktop resolution.
662 screen_controls_
->SetScreenResolution(ScreenResolution(
663 webrtc::DesktopSize(100, 100),
664 webrtc::DesktopVector(96, 96)));
666 task_runner_
= nullptr;
667 io_task_runner_
= nullptr;
668 main_run_loop_
.Run();
671 } // namespace remoting