blob: 03761a150f486826901d683fa65b558a74afdaf0 [file] [log] [blame]
// 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/desktop_session_proxy.h"
#include <stddef.h>
#include <memory>
#include <utility>
#include "base/compiler_specific.h"
#include "base/containers/map_util.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/notreached.h"
#include "base/process/process_handle.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "build/build_config.h"
#include "ipc/ipc_channel_proxy.h"
#include "remoting/base/capabilities.h"
#include "remoting/host/client_session.h"
#include "remoting/host/client_session_control.h"
#include "remoting/host/crash_process.h"
#include "remoting/host/desktop_session_connector.h"
#include "remoting/host/ipc_action_executor.h"
#include "remoting/host/ipc_audio_capturer.h"
#include "remoting/host/ipc_input_injector.h"
#include "remoting/host/ipc_keyboard_layout_monitor.h"
#include "remoting/host/ipc_mouse_cursor_monitor.h"
#include "remoting/host/ipc_screen_controls.h"
#include "remoting/host/ipc_url_forwarder_configurator.h"
#include "remoting/host/ipc_video_frame_capturer.h"
#include "remoting/host/mojom/desktop_session.mojom.h"
#include "remoting/host/remote_open_url/remote_open_url_util.h"
#include "remoting/host/webauthn/remote_webauthn_delegated_state_change_notifier.h"
#include "remoting/proto/audio.pb.h"
#include "remoting/proto/control.pb.h"
#include "remoting/proto/event.pb.h"
#include "remoting/protocol/capability_names.h"
#include "third_party/webrtc/modules/desktop_capture/mouse_cursor.h"
namespace remoting {
using SetUpUrlForwarderResponse =
protocol::UrlForwarderControl::SetUpUrlForwarderResponse;
DesktopSessionProxy::DesktopSessionProxy(
scoped_refptr<base::SingleThreadTaskRunner> audio_capture_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
base::WeakPtr<ClientSessionControl> client_session_control,
base::WeakPtr<ClientSessionEvents> client_session_events,
base::WeakPtr<DesktopSessionConnector> desktop_session_connector,
const DesktopEnvironmentOptions& options)
: base::RefCountedDeleteOnSequence<DesktopSessionProxy>(
base::SequencedTaskRunner::GetCurrentDefault()),
audio_capture_task_runner_(audio_capture_task_runner),
io_task_runner_(io_task_runner),
client_session_control_(client_session_control),
client_session_events_(client_session_events),
desktop_session_connector_(desktop_session_connector),
ipc_file_operations_factory_(this),
is_desktop_session_connected_(false),
options_(options) {}
std::unique_ptr<ActionExecutor> DesktopSessionProxy::CreateActionExecutor() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return std::make_unique<IpcActionExecutor>(this);
}
std::unique_ptr<AudioCapturer> DesktopSessionProxy::CreateAudioCapturer() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return std::make_unique<IpcAudioCapturer>(this);
}
std::unique_ptr<InputInjector> DesktopSessionProxy::CreateInputInjector() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return std::make_unique<IpcInputInjector>(this);
}
std::unique_ptr<ScreenControls> DesktopSessionProxy::CreateScreenControls() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return std::make_unique<IpcScreenControls>(this);
}
std::unique_ptr<DesktopCapturer> DesktopSessionProxy::CreateVideoCapturer(
webrtc::ScreenId id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Cursor compositing is done by the desktop process if necessary so just
// return a non-composing frame capturer.
auto video_capturer = std::make_unique<IpcVideoFrameCapturer>(this);
DCHECK(!base::FindPtrOrNull(video_capturers_, id))
<< "Multiple capturers created for screen-id " << id;
auto capturer_weakptr = video_capturer->GetWeakPtr();
video_capturers_[id] = capturer_weakptr;
// If the session-control endpoint is not bound, the Mojo endpoints will
// be requested for each IpcVideFrameCapturer when the Desktop process
// becomes attached.
if (desktop_session_control_) {
RequestMojoVideoCapturer(id, capturer_weakptr);
}
return video_capturer;
}
std::unique_ptr<protocol::MouseCursorMonitor>
DesktopSessionProxy::CreateMouseCursorMonitor() {
return std::make_unique<IpcMouseCursorMonitor>(this);
}
std::unique_ptr<KeyboardLayoutMonitor>
DesktopSessionProxy::CreateKeyboardLayoutMonitor(
base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return std::make_unique<IpcKeyboardLayoutMonitor>(std::move(callback), this);
}
std::unique_ptr<FileOperations> DesktopSessionProxy::CreateFileOperations() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return ipc_file_operations_factory_.CreateFileOperations();
}
std::unique_ptr<UrlForwarderConfigurator>
DesktopSessionProxy::CreateUrlForwarderConfigurator() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return std::make_unique<IpcUrlForwarderConfigurator>(this);
}
std::unique_ptr<RemoteWebAuthnStateChangeNotifier>
DesktopSessionProxy::CreateRemoteWebAuthnStateChangeNotifier() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return std::make_unique<RemoteWebAuthnDelegatedStateChangeNotifier>(
base::BindRepeating(&DesktopSessionProxy::SignalWebAuthnExtension, this));
}
std::string DesktopSessionProxy::GetCapabilities() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::string result = protocol::kRateLimitResizeRequests;
result += " ";
result += protocol::kMultiStreamCapability;
if (options_.enable_curtaining()) {
// Ask the client to send its resolution unconditionally.
result += " ";
result += protocol::kSendInitialResolution;
// Advertise support for high-DPI resizing.
result += " ";
result += protocol::kHighDpiCapability;
// Advertise good support for resize.
result += " ";
result += protocol::kDefaultResizeCapability;
}
if (InputInjector::SupportsTouchEvents()) {
result += " ";
result += protocol::kTouchEventsCapability;
}
if (options_.enable_remote_webauthn()) {
result += " ";
result += protocol::kRemoteWebAuthnCapability;
}
return result;
}
void DesktopSessionProxy::SetCapabilities(const std::string& capabilities) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Delay creation of the desktop session until the client screen resolution is
// received if the desktop session requires the initial screen resolution
// (when enable_curtaining() is true) and the client is expected to
// sent its screen resolution (the 'sendInitialResolution' capability is
// supported).
if (options_.enable_curtaining() &&
HasCapability(capabilities, protocol::kSendInitialResolution)) {
VLOG(1) << "Waiting for the client screen resolution.";
return;
}
// Connect to the desktop session.
if (!is_desktop_session_connected_) {
is_desktop_session_connected_ = true;
if (desktop_session_connector_.get()) {
desktop_session_connector_->ConnectTerminal(this, screen_resolution_,
options_.enable_curtaining());
}
}
}
void DesktopSessionProxy::OnChannelConnected(int32_t peer_pid) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VLOG(1) << "IPC: network <- desktop (" << peer_pid << ")";
desktop_session_agent_->Start(
client_session_control_->client_jid(), screen_resolution_, options_,
base::BindOnce(&DesktopSessionProxy::OnDesktopSessionAgentStarted,
base::Unretained(this)));
}
void DesktopSessionProxy::OnChannelError() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DetachFromDesktop();
}
void DesktopSessionProxy::OnAssociatedInterfaceRequest(
const std::string& interface_name,
mojo::ScopedInterfaceEndpointHandle handle) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (interface_name == mojom::DesktopSessionEventHandler::Name_) {
if (desktop_session_event_handler_.is_bound()) {
LOG(ERROR) << "Receiver already bound for associated interface: "
<< mojom::DesktopSessionEventHandler::Name_;
CrashProcess(base::Location::Current());
}
mojo::PendingAssociatedReceiver<mojom::DesktopSessionEventHandler>
pending_receiver(std::move(handle));
desktop_session_event_handler_.Bind(std::move(pending_receiver));
} else if (interface_name == mojom::DesktopSessionStateHandler::Name_) {
if (desktop_session_state_handler_.is_bound()) {
LOG(ERROR) << "Receiver already bound for associated interface: "
<< mojom::DesktopSessionStateHandler::Name_;
CrashProcess(base::Location::Current());
}
mojo::PendingAssociatedReceiver<mojom::DesktopSessionStateHandler>
pending_receiver(std::move(handle));
desktop_session_state_handler_.Bind(std::move(pending_receiver));
} else {
LOG(ERROR) << "Unknown associated interface requested: " << interface_name
<< ", crashing this process";
CrashProcess(base::Location::Current());
}
}
bool DesktopSessionProxy::AttachToDesktop(
mojo::ScopedMessagePipeHandle desktop_pipe,
int session_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!desktop_channel_);
// Ignore the attach event if the client session has already disconnected.
if (!client_session_control_.get()) {
return false;
}
// Connect to the desktop process.
desktop_channel_ = IPC::ChannelProxy::Create(
desktop_pipe.release(), IPC::Channel::MODE_CLIENT, this,
io_task_runner_.get(), base::SingleThreadTaskRunner::GetCurrentDefault());
// Reset the associated remote to allow us to connect to the new desktop
// process. This is needed as the desktop may crash and the daemon process
// will restart it however the remote will still be bound to the previous
// process since DetachFromDesktop() will not be called.
desktop_session_agent_.reset();
desktop_channel_->GetRemoteAssociatedInterface(&desktop_session_agent_);
desktop_session_id_ = session_id;
return true;
}
void DesktopSessionProxy::DetachFromDesktop() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
desktop_channel_.reset();
desktop_session_agent_.reset();
desktop_session_control_.reset();
desktop_session_event_handler_.reset();
desktop_session_state_handler_.reset();
desktop_session_id_ = UINT32_MAX;
current_url_forwarder_state_ = mojom::UrlForwarderState::kUnknown;
// We don't reset |is_url_forwarder_set_up_callback_| here since the request
// can come in before the DetachFromDesktop-AttachToDesktop sequence.
// Notify interested folks that the IPC has been disconnected.
disconnect_handlers_.Notify();
if (client_session_events_) {
client_session_events_->OnDesktopDetached();
}
}
void DesktopSessionProxy::OnDesktopSessionAgentStarted(
mojo::PendingAssociatedRemote<mojom::DesktopSessionControl>
pending_remote) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Reset the associated remote to allow us to connect to the new desktop
// process. This is needed as the desktop may crash and the daemon process
// will restart it however the remote will still be bound to the previous
// process since DetachFromDesktop() will not be called.
desktop_session_control_.reset();
desktop_session_control_.Bind(std::move(pending_remote));
// Create new capturers in the Desktop process and bind the Mojo endpoints
// to each video-capturer.
for (auto& [id, capturer] : video_capturers_) {
if (capturer) {
RequestMojoVideoCapturer(id, capturer);
}
}
if (client_session_events_) {
client_session_events_->OnDesktopAttached(desktop_session_id_);
}
}
void DesktopSessionProxy::SetAudioCapturer(
const base::WeakPtr<IpcAudioCapturer>& audio_capturer) {
DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
audio_capturer_ = audio_capturer;
}
void DesktopSessionProxy::SetMouseCursorMonitor(
const base::WeakPtr<IpcMouseCursorMonitor>& mouse_cursor_monitor) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
mouse_cursor_monitor_ = mouse_cursor_monitor;
}
void DesktopSessionProxy::SetKeyboardLayoutMonitor(
const base::WeakPtr<IpcKeyboardLayoutMonitor>& keyboard_layout_monitor) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
keyboard_layout_monitor_ = std::move(keyboard_layout_monitor);
}
void DesktopSessionProxy::RebindSingleVideoCapturer(
webrtc::ScreenId new_id,
base::WeakPtr<IpcVideoFrameCapturer> capturer_weakptr) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// SelectSource() is not used in multi-stream mode.
DCHECK_LE(video_capturers_.size(), 1U);
if (base::FindPtrOrNull(video_capturers_, new_id) == capturer_weakptr.get()) {
// The capturer is already bound to `new_id`, so there's no value in
// recreating it.
LOG(WARNING) << "Ignoring SelectSource() for the same ID: " << new_id;
return;
}
video_capturers_.clear();
video_capturers_[new_id] = capturer_weakptr;
// If the session-control endpoint is not bound, the Mojo endpoints will
// be requested for each IpcVideFrameCapturer when the Desktop process
// becomes attached.
if (desktop_session_control_) {
RequestMojoVideoCapturer(new_id, capturer_weakptr);
}
}
const std::optional<protocol::KeyboardLayout>&
DesktopSessionProxy::GetKeyboardCurrentLayout() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return keyboard_layout_;
}
void DesktopSessionProxy::DisconnectSession(
protocol::ErrorCode error,
const std::string& error_details,
const SourceLocation& error_location) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Disconnect the client session if it hasn't been disconnected yet.
if (client_session_control_.get()) {
client_session_control_->DisconnectSession(error, error_details,
error_location);
}
}
void DesktopSessionProxy::InjectClipboardEvent(
const protocol::ClipboardEvent& event) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (desktop_session_control_) {
desktop_session_control_->InjectClipboardEvent(event);
}
}
void DesktopSessionProxy::InjectKeyEvent(const protocol::KeyEvent& event) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (desktop_session_control_) {
desktop_session_control_->InjectKeyEvent(event);
}
}
void DesktopSessionProxy::InjectTextEvent(const protocol::TextEvent& event) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (desktop_session_control_) {
desktop_session_control_->InjectTextEvent(event);
}
}
void DesktopSessionProxy::InjectMouseEvent(const protocol::MouseEvent& event) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (desktop_session_control_) {
desktop_session_control_->InjectMouseEvent(event);
}
}
void DesktopSessionProxy::InjectTouchEvent(const protocol::TouchEvent& event) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (desktop_session_control_) {
desktop_session_control_->InjectTouchEvent(event);
}
}
void DesktopSessionProxy::StartInputInjector(
std::unique_ptr<protocol::ClipboardStub> client_clipboard) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
client_clipboard_ = std::move(client_clipboard);
}
void DesktopSessionProxy::SetScreenResolution(
const ScreenResolution& resolution) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
screen_resolution_ = resolution;
// Connect to the desktop session if it is not done yet.
if (!is_desktop_session_connected_) {
is_desktop_session_connected_ = true;
if (desktop_session_connector_.get()) {
desktop_session_connector_->ConnectTerminal(this, screen_resolution_,
options_.enable_curtaining());
}
return;
}
// Pass the client's resolution to both daemon and desktop session agent.
// Depending on the session kind the screen resolution can be set by either
// the daemon (for example RDP sessions on Windows) or by the desktop session
// agent (when sharing the physical console).
// Desktop-size-restore functionality (via an empty resolution param) does not
// exist for the Daemon process. Passing an empty resolution object is
// treated as a critical error so we want to prevent that here.
if (desktop_session_connector_.get() && !screen_resolution_.IsEmpty()) {
desktop_session_connector_->SetScreenResolution(this, screen_resolution_);
}
// Passing an empty |screen_resolution_| value to the desktop process
// indicates that the original resolution, if one exists, should be restored.
if (desktop_session_control_) {
desktop_session_control_->SetScreenResolution(screen_resolution_);
}
}
void DesktopSessionProxy::ExecuteAction(
const protocol::ActionRequest& request) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!desktop_session_control_) {
return;
}
switch (request.action()) {
case protocol::ActionRequest::LOCK_WORKSTATION:
desktop_session_control_->LockWorkstation();
break;
case protocol::ActionRequest::SEND_ATTENTION_SEQUENCE:
desktop_session_control_->InjectSendAttentionSequence();
break;
default:
LOG(WARNING) << "Unknown action requested: " << request.action();
}
}
void DesktopSessionProxy::BeginFileRead(
IpcFileOperations::BeginFileReadCallback callback,
base::OnceClosure on_disconnect) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!desktop_session_control_) {
std::move(callback).Run(
mojom::BeginFileReadResult::NewError(protocol::MakeFileTransferError(
FROM_HERE, protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR)));
return;
}
auto disconnect_handler_subscription =
disconnect_handlers_.Add(std::move(on_disconnect));
// Unretained is sound as DesktopSessionProxy owns |desktop_session_control_|
// and the callback won't be invoked after the remote is destroyed.
desktop_session_control_->BeginFileRead(base::BindOnce(
&DesktopSessionProxy::OnBeginFileReadResult, base::Unretained(this),
std::move(callback), std::move(disconnect_handler_subscription)));
}
void DesktopSessionProxy::BeginFileWrite(
const base::FilePath& file_path,
IpcFileOperations::BeginFileWriteCallback callback,
base::OnceClosure on_disconnect) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!desktop_session_control_) {
std::move(callback).Run(
mojom::BeginFileWriteResult::NewError(protocol::MakeFileTransferError(
FROM_HERE, protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR)));
return;
}
auto disconnect_handler_subscription =
disconnect_handlers_.Add(std::move(on_disconnect));
// Unretained is sound as DesktopSessionProxy owns |desktop_session_control_|
// and the callback won't be invoked after the remote is destroyed.
desktop_session_control_->BeginFileWrite(
file_path, base::BindOnce(&DesktopSessionProxy::OnBeginFileWriteResult,
base::Unretained(this), std::move(callback),
std::move(disconnect_handler_subscription)));
}
void DesktopSessionProxy::IsUrlForwarderSetUp(
UrlForwarderConfigurator::IsUrlForwarderSetUpCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
switch (current_url_forwarder_state_) {
case mojom::UrlForwarderState::kUnknown:
// State is not known yet. Wait for OnUrlForwarderStateChange() to be
// called.
DCHECK(!is_url_forwarder_set_up_callback_);
is_url_forwarder_set_up_callback_ = std::move(callback);
break;
case mojom::UrlForwarderState::kSetUp:
std::move(callback).Run(true);
break;
default:
std::move(callback).Run(false);
}
}
void DesktopSessionProxy::SetUpUrlForwarder(
const UrlForwarderConfigurator::SetUpUrlForwarderCallback& callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!set_up_url_forwarder_callback_);
if (!desktop_session_control_) {
LOG(ERROR) << "The UrlForwarderConfigurator remote is not connected. Setup "
<< "request ignored.";
callback.Run(SetUpUrlForwarderResponse::FAILED);
return;
}
set_up_url_forwarder_callback_ = callback;
desktop_session_control_->SetUpUrlForwarder();
}
void DesktopSessionProxy::OnUrlForwarderStateChange(
mojom::UrlForwarderState state) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
current_url_forwarder_state_ = state;
if (is_url_forwarder_set_up_callback_) {
std::move(is_url_forwarder_set_up_callback_)
.Run(state == mojom::UrlForwarderState::kSetUp);
}
if (set_up_url_forwarder_callback_) {
switch (state) {
case mojom::UrlForwarderState::kSetUp:
set_up_url_forwarder_callback_.Run(SetUpUrlForwarderResponse::COMPLETE);
// Cleanup callback due to terminating state.
set_up_url_forwarder_callback_.Reset();
break;
case mojom::UrlForwarderState::kNotSetUp:
// The desktop session agent during the setup process will only report
// SET_UP or FAILED. NOT_SET_UP must come from a freshly started agent.
LOG(WARNING) << "Setup process failed because the previous desktop "
<< "session agent has exited";
[[fallthrough]];
case mojom::UrlForwarderState::kFailed:
set_up_url_forwarder_callback_.Run(SetUpUrlForwarderResponse::FAILED);
// Cleanup callback due to terminating state.
set_up_url_forwarder_callback_.Reset();
break;
case mojom::UrlForwarderState::kSetupPendingUserIntervention:
set_up_url_forwarder_callback_.Run(
SetUpUrlForwarderResponse::USER_INTERVENTION_REQUIRED);
break;
default:
LOG(ERROR) << "Received unexpected state: " << state;
}
}
}
DesktopSessionProxy::~DesktopSessionProxy() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (desktop_session_connector_.get() && is_desktop_session_connected_) {
desktop_session_connector_->DisconnectTerminal(this);
}
}
void DesktopSessionProxy::OnAudioPacket(
std::unique_ptr<AudioPacket> audio_packet) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Pass the captured audio packet to |audio_capturer_|.
audio_capture_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&IpcAudioCapturer::OnAudioPacket,
audio_capturer_, std::move(audio_packet)));
}
void DesktopSessionProxy::OnDesktopDisplayChanged(
const protocol::VideoLayout& displays) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
LOG(INFO) << "DSP::OnDesktopDisplayChanged";
for (int display_id = 0; display_id < displays.video_track_size();
display_id++) {
protocol::VideoTrackLayout track = displays.video_track(display_id);
LOG(INFO) << " #" << display_id << " : "
<< " [" << track.x_dpi() << "," << track.y_dpi() << "]";
}
if (client_session_control_) {
auto layout = std::make_unique<protocol::VideoLayout>();
layout->CopyFrom(displays);
client_session_control_->OnDesktopDisplayChanged(std::move(layout));
}
}
void DesktopSessionProxy::OnBeginFileReadResult(
IpcFileOperations::BeginFileReadCallback callback,
base::CallbackListSubscription disconnect_handler_subscription,
mojom::BeginFileReadResultPtr result) {
// This handler is only needed until the pending_remote is returned from the
// DesktopSessionAgent as the IpcFileReader will then use the new channel and
// hook into its disconnect handler.
disconnect_handler_subscription = {};
std::move(callback).Run(std::move(result));
}
void DesktopSessionProxy::OnBeginFileWriteResult(
IpcFileOperations::BeginFileWriteCallback callback,
base::CallbackListSubscription disconnect_handler_subscription,
mojom::BeginFileWriteResultPtr result) {
// This handler is only needed until the pending_remote is returned from the
// DesktopSessionAgent as the IpcFileWriter will then use the new channel and
// hook into its disconnect handler.
disconnect_handler_subscription = {};
std::move(callback).Run(std::move(result));
}
void DesktopSessionProxy::OnMouseCursorChanged(
const webrtc::MouseCursor& mouse_cursor) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (mouse_cursor_monitor_) {
mouse_cursor_monitor_->OnMouseCursor(
base::WrapUnique(webrtc::MouseCursor::CopyOf(mouse_cursor)));
}
}
void DesktopSessionProxy::OnKeyboardLayoutChanged(
const protocol::KeyboardLayout& layout) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
keyboard_layout_ = layout;
if (keyboard_layout_monitor_) {
keyboard_layout_monitor_->OnKeyboardChanged(layout);
}
}
void DesktopSessionProxy::OnLocalMouseMoveDetected(
const webrtc::DesktopVector& new_position) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (client_session_control_) {
client_session_control_->OnLocalPointerMoved(new_position,
ui::EventType::kMouseMoved);
}
}
void DesktopSessionProxy::OnLocalKeyboardInputDetected(int32_t usb_keycode) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (client_session_control_) {
client_session_control_->OnLocalKeyPressed(usb_keycode);
}
}
void DesktopSessionProxy::OnClipboardEvent(
const protocol::ClipboardEvent& event) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (client_clipboard_) {
client_clipboard_->InjectClipboardEvent(event);
}
}
void DesktopSessionProxy::SignalWebAuthnExtension() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (desktop_session_control_) {
desktop_session_control_->SignalWebAuthnExtension();
}
}
void DesktopSessionProxy::RequestMojoVideoCapturer(
webrtc::ScreenId id,
base::WeakPtr<IpcVideoFrameCapturer> capturer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(desktop_session_control_);
DCHECK(capturer);
desktop_session_control_->CreateVideoCapturer(
id, base::BindOnce(&IpcVideoFrameCapturer::OnCreateVideoCapturerResult,
capturer));
}
} // namespace remoting