| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "remoting/host/me2me_desktop_environment.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "base/check.h" |
| #include "base/functional/bind.h" |
| #include "base/logging.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "build/build_config.h" |
| #include "remoting/host/action_executor.h" |
| #include "remoting/host/base/desktop_environment_options.h" |
| #include "remoting/host/base/screen_controls.h" |
| #include "remoting/host/basic_desktop_environment.h" |
| #include "remoting/host/client_session_control.h" |
| #include "remoting/host/curtain_mode.h" |
| #include "remoting/host/desktop_environment.h" |
| #include "remoting/host/desktop_interaction_strategy.h" |
| #include "remoting/host/host_window.h" |
| #include "remoting/host/host_window_proxy.h" |
| #include "remoting/host/input_monitor/local_input_monitor.h" |
| #include "remoting/host/resizing_host_observer.h" |
| #include "remoting/protocol/capability_names.h" |
| #include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h" |
| #include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h" |
| |
| #if BUILDFLAG(IS_POSIX) |
| #include <sys/types.h> |
| #include <unistd.h> |
| #endif // BUILDFLAG(IS_POSIX) |
| |
| #if BUILDFLAG(IS_WIN) |
| #include "base/win/windows_version.h" |
| #endif // BUILDFLAG(IS_WIN) |
| |
| #if defined(REMOTING_USE_X11) |
| #include "remoting/host/linux/desktop_resizer_x11.h" |
| #include "remoting/host/linux/x11_util.h" |
| #include "ui/gfx/x/connection.h" |
| #endif // defined(REMOTING_USE_X11) |
| |
| namespace remoting { |
| |
| namespace { |
| |
| #if defined(REMOTING_USE_X11) |
| |
| // Helper function that caches the result of IsUsingVideoDummyDriver(). |
| bool UsingVideoDummyDriver() { |
| static bool is_using_dummy_driver = |
| IsUsingVideoDummyDriver(x11::Connection::Get()); |
| return is_using_dummy_driver; |
| } |
| |
| bool RunningUnderWayland() { |
| static bool is_running_under_wayland = |
| webrtc::DesktopCapturer::IsRunningUnderWayland(); |
| return is_running_under_wayland; |
| } |
| |
| #endif // defined(REMOTING_USE_X11) |
| |
| } // namespace |
| |
| Me2MeDesktopEnvironment::~Me2MeDesktopEnvironment() { |
| DCHECK(caller_task_runner()->BelongsToCurrentThread()); |
| } |
| |
| std::unique_ptr<ActionExecutor> |
| Me2MeDesktopEnvironment::CreateActionExecutor() { |
| DCHECK(caller_task_runner()->BelongsToCurrentThread()); |
| |
| return interaction_strategy().CreateActionExecutor(); |
| } |
| |
| std::unique_ptr<ScreenControls> |
| Me2MeDesktopEnvironment::CreateScreenControls() { |
| DCHECK(caller_task_runner()->BelongsToCurrentThread()); |
| |
| // We only want to restore the host resolution on disconnect if we are not |
| // curtained so we don't mess up the user's window layout unnecessarily if |
| // they disconnect and reconnect. Both OS X and Windows will restore the |
| // resolution automatically when the user logs back in on the console, and on |
| // Linux the curtain-mode uses a separate session. |
| auto resizer = std::make_unique<ResizingHostObserver>( |
| interaction_strategy().CreateDesktopResizer(), curtain_ == nullptr); |
| resizer->RegisterForDisplayChanges(*GetDisplayInfoMonitor()); |
| return resizer; |
| } |
| |
| std::string Me2MeDesktopEnvironment::GetCapabilities() const { |
| std::string capabilities = BasicDesktopEnvironment::GetCapabilities(); |
| if (!capabilities.empty()) { |
| capabilities += " "; |
| } |
| capabilities += protocol::kRateLimitResizeRequests; |
| |
| #if BUILDFLAG(IS_WIN) |
| capabilities += " "; |
| capabilities += protocol::kSendAttentionSequenceAction; |
| |
| if (base::win::OSInfo::GetInstance()->version_type() != |
| base::win::VersionType::SUITE_HOME) { |
| capabilities += " "; |
| capabilities += protocol::kLockWorkstationAction; |
| } |
| #endif // BUILDFLAG(IS_WIN) |
| |
| if (desktop_environment_options().enable_remote_webauthn()) { |
| capabilities += " "; |
| capabilities += protocol::kRemoteWebAuthnCapability; |
| } |
| |
| #if BUILDFLAG(IS_LINUX) && defined(REMOTING_USE_X11) |
| capabilities += " "; |
| capabilities += protocol::kMultiStreamCapability; |
| capabilities += " "; |
| capabilities += protocol::kDefaultResizeCapability; |
| |
| if (RunningUnderWayland()) { |
| capabilities += " "; |
| capabilities += protocol::kClientControlledLayoutCapability; |
| capabilities += " "; |
| capabilities += protocol::kHighDpiCapability; |
| } else if (UsingVideoDummyDriver()) { |
| capabilities += " "; |
| capabilities += protocol::kClientControlledLayoutCapability; |
| |
| if (DesktopResizerX11::supportsHighDpiResize()) { |
| capabilities += " "; |
| capabilities += protocol::kHighDpiCapability; |
| } |
| } |
| #elif BUILDFLAG(IS_MAC) |
| capabilities += " "; |
| capabilities += protocol::kMultiStreamCapability; |
| #endif // BUILDFLAG(IS_LINUX) && defined(REMOTING_USE_X11) |
| |
| return capabilities; |
| } |
| |
| Me2MeDesktopEnvironment::Me2MeDesktopEnvironment( |
| scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner, |
| scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, |
| std::unique_ptr<DesktopInteractionStrategy> interaction_strategy, |
| base::WeakPtr<ClientSessionControl> client_session_control, |
| const DesktopEnvironmentOptions& options) |
| : BasicDesktopEnvironment(caller_task_runner, |
| ui_task_runner, |
| std::move(interaction_strategy), |
| client_session_control, |
| options) { |
| DCHECK(caller_task_runner->BelongsToCurrentThread()); |
| |
| // TODO(zijiehe): This logic should belong to RemotingMe2MeHost, instead of |
| // Me2MeDesktopEnvironment, which does not take response to create a new |
| // session. |
| // X DAMAGE is not enabled by default, since it is broken on many systems - |
| // see http://crbug.com/73423. It's safe to enable it here because it works |
| // properly under Xvfb. |
| mutable_desktop_capture_options()->set_use_update_notifications(true); |
| |
| #if BUILDFLAG(IS_LINUX) |
| // Setting this option to false means that the capture differ wrapper will not |
| // be used when the X11 capturer is selected. This reduces the X11 capture |
| // time by a few milliseconds per frame and is safe because we can rely on |
| // XDAMAGE to identify the changed regions rather than checking each pixel |
| // ourselves. |
| mutable_desktop_capture_options()->set_detect_updated_region(false); |
| #endif |
| } |
| |
| bool Me2MeDesktopEnvironment::InitializeSecurity( |
| base::WeakPtr<ClientSessionControl> client_session_control) { |
| DCHECK(caller_task_runner()->BelongsToCurrentThread()); |
| |
| // Detach the session from the local console if the caller requested. |
| if (desktop_environment_options().enable_curtaining()) { |
| curtain_ = interaction_strategy().CreateCurtainMode(client_session_control); |
| if (!curtain_->Activate()) { |
| LOG(ERROR) << "Failed to activate the curtain mode."; |
| curtain_ = nullptr; |
| return false; |
| } |
| return true; |
| } |
| |
| // Otherwise, if the session is shared with the local user start monitoring |
| // the local input and create the in-session UI. |
| #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) |
| bool want_user_interface = false; |
| #elif BUILDFLAG(IS_APPLE) |
| // Don't try to display any UI on top of the system's login screen as this |
| // is rejected by the Window Server on OS X 10.7.4, and prevents the |
| // capturer from working (http://crbug.com/140984). |
| |
| // TODO(lambroslambrou): Use a better technique of detecting whether we're |
| // running in the LoginWindow context, and refactor this into a separate |
| // function to be used here and in CurtainMode::ActivateCurtain(). |
| bool want_user_interface = getuid() != 0; |
| #else |
| bool want_user_interface = |
| desktop_environment_options().enable_user_interface(); |
| #endif |
| |
| if (want_user_interface) { |
| // Create the local input monitor. |
| local_input_monitor_ = interaction_strategy().CreateLocalInputMonitor(); |
| local_input_monitor_->StartMonitoringForClientSession( |
| client_session_control); |
| |
| // Create the disconnect window. |
| #if BUILDFLAG(IS_WIN) |
| disconnect_window_ = HostWindow::CreateAutoHidingDisconnectWindow( |
| interaction_strategy().CreateLocalInputMonitor()); |
| #else |
| disconnect_window_ = HostWindow::CreateDisconnectWindow(); |
| #endif |
| disconnect_window_ = std::make_unique<HostWindowProxy>( |
| caller_task_runner(), ui_task_runner(), std::move(disconnect_window_)); |
| disconnect_window_->Start(client_session_control); |
| } |
| |
| return true; |
| } |
| |
| Me2MeDesktopEnvironmentFactory::Me2MeDesktopEnvironmentFactory( |
| scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner, |
| scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, |
| std::unique_ptr<DesktopInteractionStrategyFactory> |
| interaction_strategy_factory) |
| : BasicDesktopEnvironmentFactory(std::move(caller_task_runner), |
| std::move(ui_task_runner), |
| std::move(interaction_strategy_factory)) {} |
| |
| Me2MeDesktopEnvironmentFactory::~Me2MeDesktopEnvironmentFactory() = default; |
| |
| void Me2MeDesktopEnvironmentFactory::Create( |
| base::WeakPtr<ClientSessionControl> client_session_control, |
| base::WeakPtr<ClientSessionEvents> client_session_events, |
| const DesktopEnvironmentOptions& options, |
| CreateCallback callback) { |
| DCHECK(caller_task_runner()->BelongsToCurrentThread()); |
| |
| auto create_with_interaction_strategy = |
| [](scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner, |
| scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, |
| base::WeakPtr<ClientSessionControl> client_session_control, |
| const DesktopEnvironmentOptions& options, |
| std::unique_ptr<DesktopInteractionStrategy> interaction_strategy) |
| -> std::unique_ptr<DesktopEnvironment> { |
| if (!interaction_strategy) { |
| return nullptr; |
| } |
| auto desktop_environment = base::WrapUnique(new Me2MeDesktopEnvironment( |
| std::move(caller_task_runner), std::move(ui_task_runner), |
| std::move(interaction_strategy), client_session_control, options)); |
| if (!desktop_environment->InitializeSecurity(client_session_control)) { |
| return nullptr; |
| } |
| |
| return desktop_environment; |
| }; |
| |
| CreateInteractionStrategy( |
| options, base::BindOnce(create_with_interaction_strategy, |
| caller_task_runner(), ui_task_runner(), |
| std::move(client_session_control), options) |
| .Then(std::move(callback))); |
| } |
| |
| } // namespace remoting |