blob: a2be0a4577243253919f68df42d5fb9d259a59c5 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// 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_agent.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/platform_shared_memory_region.h"
#include "base/memory/ptr_util.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "base/memory/weak_ptr.h"
#include "base/notreached.h"
#include "base/process/process_handle.h"
#include "base/task/bind_post_task.h"
#include "build/build_config.h"
#include "ipc/ipc_channel_proxy.h"
#include "ipc/ipc_message.h"
#include "ipc/ipc_message_macros.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "remoting/base/auto_thread_task_runner.h"
#include "remoting/base/constants.h"
#include "remoting/host/action_executor.h"
#include "remoting/host/audio_capturer.h"
#include "remoting/host/base/screen_controls.h"
#include "remoting/host/base/screen_resolution.h"
#include "remoting/host/chromoting_messages.h"
#include "remoting/host/crash_process.h"
#include "remoting/host/desktop_display_info_monitor.h"
#include "remoting/host/desktop_environment.h"
#include "remoting/host/input_injector.h"
#include "remoting/host/keyboard_layout_monitor.h"
#include "remoting/host/mojom/desktop_session.mojom-shared.h"
#include "remoting/host/remote_input_filter.h"
#include "remoting/host/remote_open_url/url_forwarder_configurator.h"
#include "remoting/proto/action.pb.h"
#include "remoting/proto/audio.pb.h"
#include "remoting/proto/control.pb.h"
#include "remoting/proto/event.pb.h"
#include "remoting/protocol/clipboard_stub.h"
#include "remoting/protocol/errors.h"
#include "remoting/protocol/input_event_tracker.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
#include "third_party/webrtc/modules/desktop_capture/mouse_cursor.h"
#include "third_party/webrtc/modules/desktop_capture/shared_memory.h"
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#include "base/memory/writable_shared_memory_region.h"
#endif
namespace remoting {
// webrtc::SharedMemory implementation that creates a
// base::ReadOnlySharedMemoryRegion along with a writable mapping.
//
// This is declared outside the anonymous namespace so that it can be friended
// with base::WritableSharedMemoryRegion. It is not exported in the header.
class SharedMemoryImpl : public webrtc::SharedMemory {
public:
static std::unique_ptr<SharedMemoryImpl>
Create(size_t size, int id, base::OnceClosure on_deleted_callback) {
webrtc::SharedMemory::Handle handle = webrtc::SharedMemory::kInvalidHandle;
#if BUILDFLAG(IS_WIN)
// webrtc::ScreenCapturer uses webrtc::SharedMemory::handle() only on
// windows. This handle must be writable. A WritableSharedMemoryRegion is
// created, and then it is converted to read-only. On the windows platform,
// it happens to be the case that converting a region to read-only does not
// change the status of existing handles. This is not true on all other
// platforms, so please don't emulate this behavior!
base::WritableSharedMemoryRegion region =
base::WritableSharedMemoryRegion::Create(size);
base::WritableSharedMemoryMapping mapping = region.Map();
// Converting |region| to read-only will close its associated handle, so we
// must duplicate it into the handle used for |webrtc::ScreenCapturer|.
HANDLE process = ::GetCurrentProcess();
BOOL success =
::DuplicateHandle(process, region.UnsafeGetPlatformHandle(), process,
&handle, 0, FALSE, DUPLICATE_SAME_ACCESS);
if (!success)
return nullptr;
base::ReadOnlySharedMemoryRegion read_only_region =
base::WritableSharedMemoryRegion::ConvertToReadOnly(std::move(region));
#else
base::MappedReadOnlyRegion region_mapping =
base::ReadOnlySharedMemoryRegion::Create(size);
base::ReadOnlySharedMemoryRegion read_only_region =
std::move(region_mapping.region);
base::WritableSharedMemoryMapping mapping =
std::move(region_mapping.mapping);
#endif
if (!mapping.IsValid())
return nullptr;
// The SharedMemoryImpl ctor is private, so std::make_unique can't be
// used.
return base::WrapUnique(
new SharedMemoryImpl(std::move(read_only_region), std::move(mapping),
handle, id, std::move(on_deleted_callback)));
}
SharedMemoryImpl(const SharedMemoryImpl&) = delete;
SharedMemoryImpl& operator=(const SharedMemoryImpl&) = delete;
~SharedMemoryImpl() override { std::move(on_deleted_callback_).Run(); }
const base::ReadOnlySharedMemoryRegion& region() const { return region_; }
private:
SharedMemoryImpl(base::ReadOnlySharedMemoryRegion region,
base::WritableSharedMemoryMapping mapping,
webrtc::SharedMemory::Handle handle,
int id,
base::OnceClosure on_deleted_callback)
: SharedMemory(mapping.memory(), mapping.size(), handle, id),
on_deleted_callback_(std::move(on_deleted_callback))
#if BUILDFLAG(IS_WIN)
,
writable_handle_(handle)
#endif
{
region_ = std::move(region);
mapping_ = std::move(mapping);
}
base::OnceClosure on_deleted_callback_;
base::ReadOnlySharedMemoryRegion region_;
base::WritableSharedMemoryMapping mapping_;
#if BUILDFLAG(IS_WIN)
// Owns the handle passed to the base class which is used by
// webrtc::ScreenCapturer.
base::win::ScopedHandle writable_handle_;
#endif
};
namespace {
using SetUpUrlForwarderResponse =
protocol::UrlForwarderControl::SetUpUrlForwarderResponse;
// Routes local clipboard events though the IPC channel to the network process.
class DesktopSessionClipboardStub : public protocol::ClipboardStub {
public:
explicit DesktopSessionClipboardStub(
scoped_refptr<DesktopSessionAgent> desktop_session_agent);
DesktopSessionClipboardStub(const DesktopSessionClipboardStub&) = delete;
DesktopSessionClipboardStub& operator=(const DesktopSessionClipboardStub&) =
delete;
~DesktopSessionClipboardStub() override;
// protocol::ClipboardStub implementation.
void InjectClipboardEvent(const protocol::ClipboardEvent& event) override;
private:
scoped_refptr<DesktopSessionAgent> desktop_session_agent_;
};
DesktopSessionClipboardStub::DesktopSessionClipboardStub(
scoped_refptr<DesktopSessionAgent> desktop_session_agent)
: desktop_session_agent_(desktop_session_agent) {}
DesktopSessionClipboardStub::~DesktopSessionClipboardStub() = default;
void DesktopSessionClipboardStub::InjectClipboardEvent(
const protocol::ClipboardEvent& event) {
desktop_session_agent_->OnClipboardEvent(event);
}
class SharedMemoryFactoryImpl : public webrtc::SharedMemoryFactory {
public:
using SharedMemoryCreatedCallback = base::RepeatingCallback<
void(int id, base::ReadOnlySharedMemoryRegion, uint32_t size)>;
using SharedMemoryReleasedCallback = base::RepeatingCallback<void(int id)>;
SharedMemoryFactoryImpl(
SharedMemoryCreatedCallback shared_memory_created_callback,
SharedMemoryReleasedCallback shared_memory_released_callback)
: shared_memory_created_callback_(
std::move(shared_memory_created_callback)),
shared_memory_released_callback_(
std::move(shared_memory_released_callback)) {}
SharedMemoryFactoryImpl(const SharedMemoryFactoryImpl&) = delete;
SharedMemoryFactoryImpl& operator=(const SharedMemoryFactoryImpl&) = delete;
std::unique_ptr<webrtc::SharedMemory> CreateSharedMemory(
size_t size) override {
base::OnceClosure release_buffer_callback = base::BindOnce(
shared_memory_released_callback_, next_shared_buffer_id_);
std::unique_ptr<SharedMemoryImpl> buffer = SharedMemoryImpl::Create(
size, next_shared_buffer_id_, std::move(release_buffer_callback));
if (buffer) {
// |next_shared_buffer_id_| starts from 1 and incrementing it by 2 makes
// sure it is always odd and therefore zero is never used as a valid
// buffer ID.
//
// It is very unlikely (though theoretically possible) to allocate the
// same ID for two different buffers due to integer overflow. It should
// take about a year of allocating 100 new buffers every second.
// Practically speaking it never happens.
next_shared_buffer_id_ += 2;
shared_memory_created_callback_.Run(
buffer->id(), buffer->region().Duplicate(), buffer->size());
}
return std::move(buffer);
}
private:
int next_shared_buffer_id_ = 1;
SharedMemoryCreatedCallback shared_memory_created_callback_;
SharedMemoryReleasedCallback shared_memory_released_callback_;
};
} // namespace
DesktopSessionAgent::Delegate::~Delegate() = default;
DesktopSessionAgent::DesktopSessionAgent(
scoped_refptr<AutoThreadTaskRunner> audio_capture_task_runner,
scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
scoped_refptr<AutoThreadTaskRunner> input_task_runner,
scoped_refptr<AutoThreadTaskRunner> io_task_runner)
: audio_capture_task_runner_(audio_capture_task_runner),
caller_task_runner_(caller_task_runner),
input_task_runner_(input_task_runner),
io_task_runner_(io_task_runner) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
}
bool DesktopSessionAgent::OnMessageReceived(const IPC::Message& message) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
CHECK(started_);
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(DesktopSessionAgent, message)
IPC_MESSAGE_FORWARD(ChromotingNetworkDesktopMsg_ReadFile,
&*session_file_operations_handler_,
SessionFileOperationsHandler::ReadFile)
IPC_MESSAGE_FORWARD(ChromotingNetworkDesktopMsg_ReadFileChunk,
&*session_file_operations_handler_,
SessionFileOperationsHandler::ReadChunk)
IPC_MESSAGE_FORWARD(ChromotingNetworkDesktopMsg_WriteFile,
&*session_file_operations_handler_,
SessionFileOperationsHandler::WriteFile)
IPC_MESSAGE_FORWARD(ChromotingNetworkDesktopMsg_WriteFileChunk,
&*session_file_operations_handler_,
SessionFileOperationsHandler::WriteChunk)
IPC_MESSAGE_FORWARD(ChromotingNetworkDesktopMsg_CloseFile,
&*session_file_operations_handler_,
SessionFileOperationsHandler::Close)
IPC_MESSAGE_FORWARD(ChromotingNetworkDesktopMsg_CancelFile,
&*session_file_operations_handler_,
SessionFileOperationsHandler::Cancel)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
CHECK(handled) << "Received unexpected IPC type: " << message.type();
return handled;
}
void DesktopSessionAgent::OnChannelConnected(int32_t peer_pid) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
VLOG(1) << "IPC: desktop <- network (" << peer_pid << ")";
}
void DesktopSessionAgent::OnChannelError() {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
// Make sure the channel is closed.
network_channel_.reset();
// Notify the caller that the channel has been disconnected.
if (delegate_.get())
delegate_->OnNetworkProcessDisconnected();
}
void DesktopSessionAgent::OnAssociatedInterfaceRequest(
const std::string& interface_name,
mojo::ScopedInterfaceEndpointHandle handle) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
if (interface_name == mojom::DesktopSessionAgent::Name_) {
if (desktop_session_agent_.is_bound()) {
LOG(ERROR) << "Receiver already bound for associated interface: "
<< mojom::DesktopSessionAgent::Name_;
delegate_->CrashNetworkProcess(base::Location::Current());
}
mojo::PendingAssociatedReceiver<mojom::DesktopSessionAgent>
pending_receiver(std::move(handle));
desktop_session_agent_.Bind(std::move(pending_receiver));
} else {
LOG(ERROR) << "Unknown associated interface requested: " << interface_name
<< ", crashing the network process";
delegate_->CrashNetworkProcess(base::Location::Current());
}
}
DesktopSessionAgent::~DesktopSessionAgent() {
DCHECK(!audio_capturer_);
DCHECK(!desktop_environment_);
DCHECK(!network_channel_);
DCHECK(!screen_controls_);
DCHECK(!video_capturer_);
DCHECK(!session_file_operations_handler_);
}
const std::string& DesktopSessionAgent::client_jid() const {
return client_jid_;
}
void DesktopSessionAgent::DisconnectSession(protocol::ErrorCode error) {
if (desktop_session_state_handler_) {
desktop_session_state_handler_->DisconnectSession(error);
}
}
void DesktopSessionAgent::OnLocalKeyPressed(uint32_t usb_keycode) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
remote_input_filter_->LocalKeyPressed(usb_keycode);
}
void DesktopSessionAgent::OnLocalPointerMoved(
const webrtc::DesktopVector& new_pos,
ui::EventType type) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
remote_input_filter_->LocalPointerMoved(new_pos, type);
}
void DesktopSessionAgent::SetDisableInputs(bool disable_inputs) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
// Do not expect this method to be called because it is only used by It2Me.
NOTREACHED();
}
void DesktopSessionAgent::OnDesktopDisplayChanged(
std::unique_ptr<protocol::VideoLayout> layout) {
LOG(INFO) << "DSA::OnDesktopDisplayChanged";
for (int display_id = 0; display_id < layout->video_track_size();
display_id++) {
protocol::VideoTrackLayout track = layout->video_track(display_id);
LOG(INFO) << " #" << display_id << " : "
<< " [" << track.x_dpi() << "," << track.y_dpi() << "]";
}
SendToNetwork(std::make_unique<ChromotingDesktopNetworkMsg_DisplayChanged>(
*layout.get()));
}
void DesktopSessionAgent::Start(
const std::string& authenticated_jid,
const ScreenResolution& resolution,
const remoting::DesktopEnvironmentOptions& options,
StartCallback callback) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
DCHECK(!audio_capturer_);
DCHECK(!desktop_environment_);
DCHECK(!input_injector_);
DCHECK(!screen_controls_);
DCHECK(!video_capturer_);
DCHECK(!session_file_operations_handler_);
if (started_) {
LOG(ERROR) << __func__ << " called more than once for the current process.";
delegate_->CrashNetworkProcess(base::Location::Current());
// No need to run the callback since it just calls into the process we are
// asking the daemon process to crash.
callback.Reset();
return;
}
started_ = true;
client_jid_ = authenticated_jid;
// Hook up the associated interfaces.
network_channel_->GetRemoteAssociatedInterface(
&desktop_session_event_handler_);
network_channel_->GetRemoteAssociatedInterface(
&desktop_session_state_handler_);
// Create a desktop environment for the new session.
desktop_environment_ = delegate_->desktop_environment_factory().Create(
weak_factory_.GetWeakPtr(), /* client_session_events= */ nullptr,
options);
// Create the session controller and set the initial screen resolution.
screen_controls_ = desktop_environment_->CreateScreenControls();
SetScreenResolution(resolution);
// Create the input injector.
input_injector_ = desktop_environment_->CreateInputInjector();
action_executor_ = desktop_environment_->CreateActionExecutor();
// Hook up the input filter.
input_tracker_ =
std::make_unique<protocol::InputEventTracker>(input_injector_.get());
remote_input_filter_ =
std::make_unique<RemoteInputFilter>(input_tracker_.get());
#if BUILDFLAG(IS_WIN)
// LocalInputMonitorWin filters out an echo of the injected input before it
// reaches |remote_input_filter_|.
remote_input_filter_->SetExpectLocalEcho(false);
#endif // BUILDFLAG(IS_WIN)
// Start the input injector.
std::unique_ptr<protocol::ClipboardStub> clipboard_stub(
new DesktopSessionClipboardStub(this));
input_injector_->Start(std::move(clipboard_stub));
// Start the audio capturer.
if (delegate_->desktop_environment_factory().SupportsAudioCapture()) {
audio_capturer_ = desktop_environment_->CreateAudioCapturer();
audio_capture_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&DesktopSessionAgent::StartAudioCapturer, this));
}
// Start the video capturer and mouse cursor monitor.
// TODO(lambroslambrou): When supporting multiple streams, this class should
// create and own a single DesktopDisplayInfoMonitor and call Start() on it,
// instead of passing it to CreateVideoCapturer() here.
video_capturer_ = std::make_unique<DesktopAndCursorConditionalComposer>(
desktop_environment_->CreateVideoCapturer(
desktop_environment_->CreateDisplayInfoMonitor()));
video_capturer_->Start(this);
video_capturer_->SetSharedMemoryFactory(
std::make_unique<SharedMemoryFactoryImpl>(
base::BindPostTask(
caller_task_runner_,
base::BindRepeating(
&DesktopSessionAgent::OnSharedMemoryRegionCreated, this)),
base::BindPostTask(
caller_task_runner_,
base::BindRepeating(
&DesktopSessionAgent::OnSharedMemoryRegionReleased, this))));
mouse_cursor_monitor_ = desktop_environment_->CreateMouseCursorMonitor();
mouse_cursor_monitor_->Init(this,
webrtc::MouseCursorMonitor::SHAPE_AND_POSITION);
// Unretained is sound because callback will never be invoked after
// |keyboard_layout_monitor_| is destroyed.
keyboard_layout_monitor_ = desktop_environment_->CreateKeyboardLayoutMonitor(
base::BindRepeating(&DesktopSessionAgent::OnKeyboardLayoutChange,
base::Unretained(this)));
keyboard_layout_monitor_->Start();
// Set up the message handler for file transfers.
session_file_operations_handler_.emplace(
this, desktop_environment_->CreateFileOperations());
url_forwarder_configurator_ =
desktop_environment_->CreateUrlForwarderConfigurator();
// Check and report the initial URL forwarder setup state.
url_forwarder_configurator_->IsUrlForwarderSetUp(base::BindOnce(
&DesktopSessionAgent::OnCheckUrlForwarderSetUpResult, this));
std::move(callback).Run(
desktop_session_control_.BindNewEndpointAndPassRemote());
}
void DesktopSessionAgent::OnCaptureResult(
webrtc::DesktopCapturer::Result result,
std::unique_ptr<webrtc::DesktopFrame> frame) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
mojom::CaptureResultPtr capture_result;
if (frame) {
DCHECK_EQ(result, webrtc::DesktopCapturer::Result::SUCCESS);
std::vector<webrtc::DesktopRect> dirty_region;
for (webrtc::DesktopRegion::Iterator i(frame->updated_region());
!i.IsAtEnd(); i.Advance()) {
dirty_region.push_back(i.rect());
}
capture_result =
mojom::CaptureResult::NewDesktopFrame(mojom::DesktopFrame::New(
frame->shared_memory()->id(), frame->stride(), frame->size(),
std::move(dirty_region), frame->capture_time_ms(), frame->dpi(),
frame->capturer_id()));
} else {
DCHECK_NE(result, webrtc::DesktopCapturer::Result::SUCCESS);
capture_result = mojom::CaptureResult::NewCaptureError(result);
}
last_frame_ = std::move(frame);
desktop_session_event_handler_->OnCaptureResult(std::move(capture_result));
}
void DesktopSessionAgent::OnMouseCursor(webrtc::MouseCursor* cursor) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
std::unique_ptr<webrtc::MouseCursor> owned_cursor(cursor);
SendToNetwork(
std::make_unique<ChromotingDesktopNetworkMsg_MouseCursor>(*owned_cursor));
if (video_capturer_)
video_capturer_->SetMouseCursor(std::move(owned_cursor));
}
void DesktopSessionAgent::OnMouseCursorPosition(
const webrtc::DesktopVector& position) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
if (video_capturer_)
video_capturer_->SetMouseCursorPosition(position);
}
void DesktopSessionAgent::OnClipboardEvent(
const protocol::ClipboardEvent& event) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
CHECK(started_) << "OnClipboardEvent called before agent was started.";
if (desktop_session_event_handler_) {
desktop_session_event_handler_->OnClipboardEvent(event);
}
}
void DesktopSessionAgent::ProcessAudioPacket(
std::unique_ptr<AudioPacket> packet) {
// AudioPackets are received on the audio_capture task runner but must be sent
// over IPC on the same task_runner the mojo remote was bound on.
if (!caller_task_runner_->BelongsToCurrentThread()) {
DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
caller_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&DesktopSessionAgent::ProcessAudioPacket,
this, std::move(packet)));
return;
}
if (desktop_session_event_handler_) {
desktop_session_event_handler_->OnAudioPacket(std::move(packet));
}
}
void DesktopSessionAgent::OnResult(uint64_t file_id,
ResultHandler::Result result) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
SendToNetwork(std::make_unique<ChromotingDesktopNetworkMsg_FileResult>(
file_id, std::move(result)));
}
void DesktopSessionAgent::OnInfoResult(std::uint64_t file_id,
ResultHandler::InfoResult result) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
SendToNetwork(std::make_unique<ChromotingDesktopNetworkMsg_FileInfoResult>(
file_id, std::move(result)));
}
void DesktopSessionAgent::OnDataResult(std::uint64_t file_id,
ResultHandler::DataResult result) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
SendToNetwork(std::make_unique<ChromotingDesktopNetworkMsg_FileDataResult>(
file_id, std::move(result)));
}
mojo::ScopedMessagePipeHandle DesktopSessionAgent::Initialize(
const base::WeakPtr<Delegate>& delegate) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
DCHECK(delegate);
DCHECK(!delegate_);
delegate_ = delegate;
mojo::MessagePipe pipe;
network_channel_ = IPC::ChannelProxy::Create(
pipe.handle0.release(), IPC::Channel::MODE_SERVER, this, io_task_runner_,
base::ThreadTaskRunnerHandle::Get());
return std::move(pipe.handle1);
}
void DesktopSessionAgent::Stop() {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
delegate_.reset();
// Make sure the channel is closed.
network_channel_.reset();
if (started_) {
started_ = false;
// Ignore any further callbacks.
weak_factory_.InvalidateWeakPtrs();
client_jid_.clear();
desktop_session_event_handler_.reset();
desktop_session_state_handler_.reset();
desktop_session_control_.reset();
desktop_session_agent_.reset();
url_forwarder_configurator_.reset();
remote_input_filter_.reset();
session_file_operations_handler_.reset();
// Ensure that any pressed keys or buttons are released.
input_tracker_->ReleaseAll();
input_tracker_.reset();
desktop_environment_.reset();
action_executor_.reset();
input_injector_.reset();
screen_controls_.reset();
keyboard_layout_monitor_.reset();
// Stop the audio capturer.
audio_capture_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&DesktopSessionAgent::StopAudioCapturer, this));
// Stop the video capturer.
video_capturer_.reset();
last_frame_.reset();
mouse_cursor_monitor_.reset();
}
}
void DesktopSessionAgent::CaptureFrame() {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
CHECK(started_);
mouse_cursor_monitor_->Capture();
// webrtc::DesktopCapturer supports a very few (currently 2) outstanding
// capture requests. The requests are serialized on
// |video_capture_task_runner()| task runner. If the client issues more
// requests, pixel data in captured frames will likely be corrupted but
// stability of webrtc::DesktopCapturer will not be affected.
video_capturer_->CaptureFrame();
}
void DesktopSessionAgent::SelectSource(int id) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
CHECK(started_);
video_capturer_->SelectSource(id);
}
void DesktopSessionAgent::InjectClipboardEvent(
const protocol::ClipboardEvent& event) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
CHECK(started_);
// InputStub implementations must verify events themselves, so we don't need
// verification here. This matches HostEventDispatcher.
input_injector_->InjectClipboardEvent(event);
}
void DesktopSessionAgent::InjectKeyEvent(const protocol::KeyEvent& event) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
CHECK(started_);
// InputStub implementations must verify events themselves, so we need only
// basic verification here. This matches HostEventDispatcher.
if (!event.has_usb_keycode() || !event.has_pressed()) {
LOG(ERROR) << "Received invalid key event.";
return;
}
remote_input_filter_->InjectKeyEvent(event);
}
void DesktopSessionAgent::InjectTextEvent(const protocol::TextEvent& event) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
CHECK(started_);
// InputStub implementations must verify events themselves, so we need only
// basic verification here. This matches HostEventDispatcher.
if (!event.has_text()) {
LOG(ERROR) << "Received invalid TextEvent.";
return;
}
remote_input_filter_->InjectTextEvent(event);
}
void DesktopSessionAgent::InjectMouseEvent(const protocol::MouseEvent& event) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
CHECK(started_);
if (video_capturer_)
video_capturer_->SetComposeEnabled(event.has_delta_x() ||
event.has_delta_y());
// InputStub implementations must verify events themselves, so we don't need
// verification here. This matches HostEventDispatcher.
remote_input_filter_->InjectMouseEvent(event);
}
void DesktopSessionAgent::InjectTouchEvent(const protocol::TouchEvent& event) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
CHECK(started_);
remote_input_filter_->InjectTouchEvent(event);
}
void DesktopSessionAgent::InjectSendAttentionSequence() {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
CHECK(started_);
protocol::ActionRequest request;
request.set_action(protocol::ActionRequest::SEND_ATTENTION_SEQUENCE);
action_executor_->ExecuteAction(request);
}
void DesktopSessionAgent::LockWorkstation() {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
CHECK(started_);
protocol::ActionRequest request;
request.set_action(protocol::ActionRequest::LOCK_WORKSTATION);
action_executor_->ExecuteAction(request);
}
void DesktopSessionAgent::OnKeyboardLayoutChange(
const protocol::KeyboardLayout& layout) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
SendToNetwork(
std::make_unique<ChromotingDesktopNetworkMsg_KeyboardChanged>(layout));
}
void DesktopSessionAgent::OnSharedMemoryRegionCreated(
int id,
base::ReadOnlySharedMemoryRegion region,
uint32_t size) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
if (desktop_session_event_handler_) {
desktop_session_event_handler_->OnSharedMemoryRegionCreated(
id, std::move(region), size);
}
}
void DesktopSessionAgent::OnSharedMemoryRegionReleased(int id) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
if (desktop_session_event_handler_) {
desktop_session_event_handler_->OnSharedMemoryRegionReleased(id);
}
}
void DesktopSessionAgent::SetScreenResolution(
const ScreenResolution& resolution) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
CHECK(started_);
if (screen_controls_)
screen_controls_->SetScreenResolution(resolution);
}
void DesktopSessionAgent::SendToNetwork(std::unique_ptr<IPC::Message> message) {
if (!caller_task_runner_->BelongsToCurrentThread()) {
caller_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&DesktopSessionAgent::SendToNetwork, this,
std::move(message)));
return;
}
if (network_channel_) {
network_channel_->Send(message.release());
}
}
void DesktopSessionAgent::StartAudioCapturer() {
DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
if (audio_capturer_) {
audio_capturer_->Start(
base::BindRepeating(&DesktopSessionAgent::ProcessAudioPacket, this));
}
}
void DesktopSessionAgent::StopAudioCapturer() {
DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
audio_capturer_.reset();
}
void DesktopSessionAgent::SetUpUrlForwarder() {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
CHECK(started_);
url_forwarder_configurator_->SetUpUrlForwarder(base::BindRepeating(
&DesktopSessionAgent::OnUrlForwarderSetUpStateChanged, this));
}
void DesktopSessionAgent::OnCheckUrlForwarderSetUpResult(bool is_set_up) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
if (!desktop_session_event_handler_) {
// Callback passed to UrlForwarderConfiguratorWin::IsUrlForwarderSetUp()
// may be called after the configurator is deleted and the agent is
// stopped, so we need to null-check |desktop_session_event_handler_|.
//
// TODO(yuweih): Scope callback of IsUrlForwarderSetUp() to the lifetime of
// the UrlForwarderConfiguratorWin instance.
return;
}
desktop_session_event_handler_->OnUrlForwarderStateChange(
is_set_up ? mojom::UrlForwarderState::kSetUp
: mojom::UrlForwarderState::kNotSetUp);
}
void DesktopSessionAgent::OnUrlForwarderSetUpStateChanged(
SetUpUrlForwarderResponse::State state) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
mojom::UrlForwarderState mojo_state;
switch (state) {
case SetUpUrlForwarderResponse::COMPLETE:
mojo_state = mojom::UrlForwarderState::kSetUp;
break;
case SetUpUrlForwarderResponse::FAILED:
mojo_state = mojom::UrlForwarderState::kFailed;
break;
case SetUpUrlForwarderResponse::USER_INTERVENTION_REQUIRED:
mojo_state = mojom::UrlForwarderState::kSetupPendingUserIntervention;
break;
default:
NOTREACHED() << "Unknown state: " << state;
mojo_state = mojom::UrlForwarderState::kUnknown;
}
desktop_session_event_handler_->OnUrlForwarderStateChange(mojo_state);
}
} // namespace remoting