blob: 5bbf6ad0714cf6d9042097c2b537b9263fad5483 [file]
// Copyright 2026 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/ipc_peer_session.h"
#include <utility>
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "base/task/single_thread_task_runner.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "remoting/signaling/signaling_id_util.h"
namespace remoting {
IpcPeerSession::IpcPeerSession(
mojo::PendingRemote<mojom::PeerSession> peer_session_remote,
GetDesktopSessionCallback get_desktop_session_callback,
std::unique_ptr<protocol::IceConfigFetcher> ice_config_fetcher,
PeerSessionFactory::RequestPairingOnceCallback request_pairing_cb)
: get_desktop_session_callback_(std::move(get_desktop_session_callback)),
ice_config_fetcher_(std::move(ice_config_fetcher)),
request_pairing_cb_(std::move(request_pairing_cb)) {
CHECK(get_desktop_session_callback_);
CHECK(ice_config_fetcher_);
if (peer_session_remote) {
remote_.Bind(std::move(peer_session_remote));
remote_.set_disconnect_handler(base::BindOnce(
&IpcPeerSession::OnPeerSessionDisconnected, base::Unretained(this)));
}
}
IpcPeerSession::~IpcPeerSession() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void IpcPeerSession::Start(
EventHandler* event_handler,
std::string_view client_jid,
const DesktopEnvironmentOptions& desktop_environment_options,
const SessionPolicies& session_policies,
const SessionOptions& session_options) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(event_handler);
event_handler_ = event_handler;
if (!remote_.is_bound()) {
LOG(WARNING) << "Start called when PeerSession remote is not bound.";
NotifySessionClosed(protocol::ErrorCode::CHANNEL_CONNECTION_ERROR,
"PeerSession remote is not bound.", FROM_HERE);
return;
}
DCHECK(!event_handler_receiver_);
desktop_session_options_ = mojom::DesktopSessionOptions::New();
SplitSignalingIdResource(client_jid, &desktop_session_options_->client_id,
/*resource=*/nullptr);
desktop_session_options_->is_curtained =
desktop_environment_options.enable_curtaining();
DCHECK(!desktop_session_requester_receiver_.is_bound());
mojo::PendingRemote<mojom::DesktopSessionRequester>
desktop_session_requester_remote =
desktop_session_requester_receiver_.BindNewPipeAndPassRemote();
mojo::PendingRemote<mojom::PeerSessionEventHandler> event_handler_remote;
if (event_handler) {
event_handler_ = event_handler;
event_handler_receiver_ =
std::make_unique<mojo::Receiver<mojom::PeerSessionEventHandler>>(
event_handler);
event_handler_remote = event_handler_receiver_->BindNewPipeAndPassRemote();
event_handler_receiver_->set_disconnect_handler(base::BindOnce(
&IpcPeerSession::OnEventHandlerDisconnected, base::Unretained(this)));
}
DCHECK(!ice_config_fetcher_receiver_.is_bound());
mojo::PendingRemote<mojom::IceConfigFetcher> ice_config_fetcher_remote =
ice_config_fetcher_receiver_.BindNewPipeAndPassRemote();
DCHECK(!pairing_requester_receiver_.is_bound());
mojo::PendingRemote<mojom::PairingRequester> pairing_requester_remote =
pairing_requester_receiver_.BindNewPipeAndPassRemote();
remote_->Start(
std::string(client_jid), std::move(event_handler_remote),
std::move(desktop_session_requester_remote),
std::move(ice_config_fetcher_remote), std::move(pairing_requester_remote),
desktop_environment_options, session_policies, session_options);
}
void IpcPeerSession::RequestDesktopSession(
mojo::PendingReceiver<mojom::DesktopSession> control_receiver,
mojo::PendingRemote<mojom::DesktopSessionEvents> events_remote,
const ScreenResolution& screen_resolution) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!desktop_session_options_) {
LOG(ERROR)
<< "Desktop session already requested or session is not started.";
desktop_session_requester_receiver_.ReportBadMessage(
"Desktop session already requested or session is not started.");
return;
}
if (!get_desktop_session_callback_) {
LOG(ERROR) << "RequestDesktopSession called without a valid callback.";
return;
}
desktop_session_options_->screen_resolution = screen_resolution;
get_desktop_session_callback_.Run(std::move(control_receiver),
std::move(events_remote),
std::move(desktop_session_options_));
}
void IpcPeerSession::GetIceConfig(GetIceConfigCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ice_config_fetcher_->GetIceConfig(std::move(callback));
}
void IpcPeerSession::RequestPairing(const std::string& client_name,
RequestPairingCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!request_pairing_cb_) {
std::move(callback).Run(std::nullopt);
return;
}
// Pairing can only be requested once per connection, so we reset the callback
// to prevent possible DOS attacks.
std::move(request_pairing_cb_).Run(client_name, std::move(callback));
}
void IpcPeerSession::DisconnectSession(protocol::ErrorCode error,
std::string_view error_details,
const SourceLocation& error_location) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!remote_.is_bound()) {
LOG(WARNING)
<< "DisconnectSession called when PeerSession remote is not bound.";
NotifySessionClosed(error, std::string(error_details), error_location);
return;
}
remote_->DisconnectSession(error, std::string(error_details), error_location);
}
void IpcPeerSession::OnSessionServicesClientConnected(
mojo::PendingReceiver<mojom::ChromotingSessionServices> receiver) {
// In multi-process host, ChromotingSessionServices clients are connected
// directly to the PC process via DesktopSessionEvents by the Daemon process,
// so this method is never invoked on IpcPeerSession.
NOTREACHED();
}
protocol::Transport* IpcPeerSession::transport() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return this;
}
void IpcPeerSession::Start(
const std::string& auth_key,
SendTransportInfoCallback send_transport_info_callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
send_transport_info_callback_ = std::move(send_transport_info_callback);
if (remote_.is_bound()) {
transport_event_handler_receiver_.reset();
remote_->StartTransport(
auth_key, transport_event_handler_receiver_.BindNewPipeAndPassRemote());
}
}
bool IpcPeerSession::ProcessTransportInfo(
const JingleTransportInfo& transport_info) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (remote_.is_bound()) {
remote_->ProcessTransportInfo(transport_info);
}
return true;
}
void IpcPeerSession::SendTransportInfo(
const JingleTransportInfo& transport_info) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (send_transport_info_callback_) {
send_transport_info_callback_.Run(
std::make_unique<JingleTransportInfo>(transport_info));
}
}
void IpcPeerSession::OnPeerSessionDisconnected() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
LOG(ERROR) << "PeerSession remote dropped unexpectedly.";
remote_.reset();
NotifySessionClosed(protocol::ErrorCode::CHANNEL_CONNECTION_ERROR,
"PeerSession remote dropped unexpectedly.", FROM_HERE);
}
void IpcPeerSession::OnEventHandlerDisconnected() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
LOG(ERROR) << "PeerSessionEventHandler remote dropped unexpectedly.";
event_handler_receiver_.reset();
NotifySessionClosed(protocol::ErrorCode::CHANNEL_CONNECTION_ERROR,
"PeerSessionEventHandler dropped unexpectedly.",
FROM_HERE);
}
void IpcPeerSession::NotifySessionClosed(protocol::ErrorCode error,
const std::string& error_details,
const SourceLocation& error_location) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&IpcPeerSession::DoNotifySessionClosed,
weak_factory_.GetWeakPtr(), error,
error_details, error_location));
}
void IpcPeerSession::DoNotifySessionClosed(
protocol::ErrorCode error,
const std::string& error_details,
const SourceLocation& error_location) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
event_handler_receiver_.reset();
transport_event_handler_receiver_.reset();
ice_config_fetcher_receiver_.reset();
pairing_requester_receiver_.reset();
desktop_session_requester_receiver_.reset();
desktop_session_options_.reset();
send_transport_info_callback_.Reset();
if (event_handler_) {
auto* handler = event_handler_.get();
event_handler_ = nullptr;
handler->OnSessionClosed(error, error_details, error_location);
}
}
IpcPeerSessionFactory::IpcPeerSessionFactory(
mojo::PendingAssociatedRemote<mojom::PeerSessionManager>
peer_session_manager,
mojo::PendingAssociatedRemote<mojom::DesktopSessionManager>
desktop_session_manager,
GetIceConfigFetcherCallback get_ice_config_fetcher_cb)
: pending_peer_session_manager_(std::move(peer_session_manager)),
pending_desktop_session_manager_(std::move(desktop_session_manager)),
get_ice_config_fetcher_cb_(std::move(get_ice_config_fetcher_cb)) {
CHECK(get_ice_config_fetcher_cb_);
DETACH_FROM_SEQUENCE(sequence_checker_);
}
IpcPeerSessionFactory::~IpcPeerSessionFactory() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void IpcPeerSessionFactory::EnsureBound() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (pending_peer_session_manager_) {
peer_session_manager_.Bind(std::move(pending_peer_session_manager_));
peer_session_manager_.set_disconnect_handler(
base::BindOnce(&IpcPeerSessionFactory::OnPeerSessionManagerDisconnected,
base::Unretained(this)));
}
if (pending_desktop_session_manager_) {
desktop_session_manager_.Bind(std::move(pending_desktop_session_manager_));
desktop_session_manager_.set_disconnect_handler(base::BindOnce(
&IpcPeerSessionFactory::OnDesktopSessionManagerDisconnected,
base::Unretained(this)));
}
}
void IpcPeerSessionFactory::OnPeerSessionManagerDisconnected() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
LOG(ERROR) << "PeerSessionManager associated remote disconnected.";
peer_session_manager_.reset();
}
void IpcPeerSessionFactory::OnDesktopSessionManagerDisconnected() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
LOG(ERROR) << "DesktopSessionManager associated remote disconnected.";
desktop_session_manager_.reset();
}
void IpcPeerSessionFactory::set_request_pairing_callback(
const RequestPairingCallback& request_pairing_cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
request_pairing_cb_ = request_pairing_cb;
}
void IpcPeerSessionFactory::SetRequiredUsername(std::string_view username) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
required_username_ = std::string(username);
}
void IpcPeerSessionFactory::GetDesktopSession(
mojo::PendingReceiver<mojom::DesktopSession> control_receiver,
mojo::PendingRemote<mojom::DesktopSessionEvents> events_remote,
mojom::DesktopSessionOptionsPtr options) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
EnsureBound();
if (!desktop_session_manager_.is_bound()) {
LOG(ERROR) << "DesktopSessionManager is not bound.";
return;
}
options->required_username = required_username_;
desktop_session_manager_->GetDesktopSession(std::move(control_receiver),
std::move(events_remote),
std::move(options));
}
std::unique_ptr<PeerSession> IpcPeerSessionFactory::Create() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
EnsureBound();
if (!peer_session_manager_.is_bound()) {
LOG(ERROR) << "PeerSessionManager is not bound.";
return nullptr;
}
if (!desktop_session_manager_.is_bound()) {
LOG(ERROR) << "DesktopSessionManager is not bound.";
return nullptr;
}
CHECK(get_ice_config_fetcher_cb_) << "Missing Ice Config Fetcher callback.";
std::unique_ptr<protocol::IceConfigFetcher> ice_config_fetcher =
get_ice_config_fetcher_cb_.Run();
mojo::PendingRemote<mojom::PeerSession> pending_remote;
peer_session_manager_->LaunchPeerSession(
pending_remote.InitWithNewPipeAndPassReceiver());
return std::make_unique<IpcPeerSession>(
std::move(pending_remote),
base::BindRepeating(&IpcPeerSessionFactory::GetDesktopSession,
weak_factory_.GetWeakPtr()),
std::move(ice_config_fetcher), request_pairing_cb_);
}
} // namespace remoting