blob: 9a099618d5977c4e68729b3ae568384228763276 [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/peer_connection_process.h"
#include <utility>
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/process/process.h"
#include "base/sequence_checker.h"
#include "base/task/single_thread_task_runner.h"
#include "ipc/ipc_channel_proxy.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/shared_remote.h"
#include "remoting/base/session_policies.h"
#include "remoting/host/crash_process.h"
#include "remoting/host/ipc_desktop_environment.h"
#include "remoting/host/peer_session_impl.h"
#include "remoting/protocol/ice_config_fetcher.h"
#include "remoting/protocol/transport.h"
#include "remoting/signaling/jingle_data_structures.h"
namespace remoting {
namespace {
class MojoIceConfigFetcher final : public protocol::IceConfigFetcher {
public:
explicit MojoIceConfigFetcher(
mojo::SharedRemote<mojom::IceConfigFetcher> remote)
: remote_(std::move(remote)) {}
~MojoIceConfigFetcher() override = default;
void GetIceConfig(OnIceConfigCallback callback) override {
if (!remote_) {
std::move(callback).Run(std::nullopt);
return;
}
remote_->GetIceConfig(base::BindOnce(
[](OnIceConfigCallback callback,
std::optional<protocol::IceConfig> ice_config) {
std::move(callback).Run(std::move(ice_config));
},
std::move(callback)));
}
private:
mojo::SharedRemote<mojom::IceConfigFetcher> remote_;
};
} // namespace
PeerConnectionProcess::PeerConnectionProcess(
scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
: caller_task_runner_(caller_task_runner),
io_task_runner_(io_task_runner) {}
PeerConnectionProcess::~PeerConnectionProcess() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
bool PeerConnectionProcess::Start(
mojo::ScopedMessagePipeHandle channel_handle) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!daemon_channel_);
daemon_channel_ = IPC::ChannelProxy::Create(
std::move(channel_handle), IPC::Channel::MODE_CLIENT, this,
io_task_runner_, caller_task_runner_);
return true;
}
void PeerConnectionProcess::CrashProcess(const std::string& function_name,
const std::string& file_name,
int line_number) {
remoting::CrashProcess(function_name, file_name, line_number);
}
void PeerConnectionProcess::OnAssociatedInterfaceRequest(
const std::string& interface_name,
mojo::ScopedInterfaceEndpointHandle handle) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (interface_name == mojom::WorkerProcessControl::Name_) {
if (worker_process_control_.is_bound()) {
LOG(FATAL) << "Receiver already bound for associated interface: "
<< mojom::WorkerProcessControl::Name_;
}
mojo::PendingAssociatedReceiver<mojom::WorkerProcessControl>
pending_receiver(std::move(handle));
worker_process_control_.Bind(std::move(pending_receiver));
} else if (interface_name == mojom::PeerConnectionProcessControl::Name_) {
if (control_receiver_.is_bound()) {
LOG(FATAL) << "Receiver already bound for associated interface: "
<< mojom::PeerConnectionProcessControl::Name_;
}
mojo::PendingAssociatedReceiver<mojom::PeerConnectionProcessControl>
pending_receiver(std::move(handle));
control_receiver_.Bind(std::move(pending_receiver));
control_receiver_.set_disconnect_handler(base::BindOnce(
&PeerConnectionProcess::OnChannelError, base::Unretained(this)));
} else {
LOG(FATAL) << "Received unexpected associated interface request: "
<< interface_name;
}
}
void PeerConnectionProcess::BindPeerSession(
mojo::PendingReceiver<mojom::PeerSession> session_receiver) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
session_receiver_.Bind(std::move(session_receiver));
session_receiver_.set_disconnect_handler(base::BindOnce(
&PeerConnectionProcess::OnSessionDisconnected, base::Unretained(this)));
}
void PeerConnectionProcess::Start(
const std::string& client_jid,
mojo::PendingRemote<mojom::PeerSessionEventHandler> event_handler,
mojo::PendingRemote<mojom::DesktopSessionRequester>
desktop_session_requester,
mojo::PendingRemote<mojom::IceConfigFetcher> ice_config_fetcher,
mojo::PendingRemote<mojom::PairingRequester> pairing_requester,
const DesktopEnvironmentOptions& desktop_environment_options,
const SessionPolicies& session_policies,
const SessionOptions& session_options) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!peer_session_);
desktop_session_requester_remote_.Bind(std::move(desktop_session_requester));
desktop_session_requester_remote_.set_disconnect_handler(base::BindOnce(
&PeerConnectionProcess::OnSessionDisconnected, base::Unretained(this)));
desktop_environment_factory_ = std::make_unique<IpcDesktopEnvironmentFactory>(
caller_task_runner_, io_task_runner_,
base::BindRepeating(&PeerConnectionProcess::GetDesktopSession,
base::Unretained(this)));
PeerSessionImplFactory::GetIceConfigFetcherCallback get_ice_config_fetcher_cb;
if (ice_config_fetcher) {
// Uses SharedRemote because `get_ice_config_fetcher_cb` is a repeating
// callback that creates an IceConfigFetcher for each connection, but only a
// single IceConfigFetcher pending remote is received from the Network
// process over IPC. SharedRemote allows multiple IceConfigFetcher instances
// to share the same underlying Mojo channel safely.
mojo::SharedRemote<mojom::IceConfigFetcher> shared_remote(
std::move(ice_config_fetcher), caller_task_runner_);
get_ice_config_fetcher_cb = base::BindRepeating(
[](mojo::SharedRemote<mojom::IceConfigFetcher> shared_remote)
-> std::unique_ptr<protocol::IceConfigFetcher> {
return std::make_unique<MojoIceConfigFetcher>(
std::move(shared_remote));
},
std::move(shared_remote));
} else {
get_ice_config_fetcher_cb = base::BindRepeating(
[]() -> std::unique_ptr<protocol::IceConfigFetcher> {
return nullptr;
});
}
PeerSessionImplFactory::RequestPairingCallback request_pairing_cb;
if (pairing_requester) {
pairing_requester_remote_.Bind(std::move(pairing_requester));
request_pairing_cb = base::BindRepeating(
&PeerConnectionProcess::RequestPairing, base::Unretained(this));
}
PeerSessionImplFactory peer_session_factory(
desktop_environment_factory_.get(), get_ice_config_fetcher_cb,
std::move(request_pairing_cb));
peer_session_ = peer_session_factory.Create();
if (!peer_session_) {
LOG(ERROR) << "Failed to create PeerSession.";
Shutdown(1);
return;
}
event_handler_.Bind(std::move(event_handler));
peer_session_->Start(event_handler_.get(), client_jid,
desktop_environment_options, session_policies,
session_options);
if (pending_start_transport_) {
auto pending = std::move(*pending_start_transport_);
pending_start_transport_.reset();
StartTransport(pending.auth_key,
std::move(pending.transport_event_handler));
}
auto pending_transport_infos = std::move(pending_transport_infos_);
for (const auto& transport_info : pending_transport_infos) {
ProcessTransportInfo(transport_info);
}
}
void PeerConnectionProcess::StartTransport(
const std::string& auth_key,
mojo::PendingRemote<mojom::TransportEventHandler> transport_event_handler) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!peer_session_ || !peer_session_->transport()) {
pending_start_transport_ =
PendingStartTransport{auth_key, std::move(transport_event_handler)};
return;
}
transport_event_handler_.reset();
if (transport_event_handler) {
transport_event_handler_.Bind(std::move(transport_event_handler));
}
peer_session_->transport()->Start(
auth_key, base::BindRepeating(&PeerConnectionProcess::OnSendTransportInfo,
weak_factory_.GetWeakPtr()));
}
void PeerConnectionProcess::ProcessTransportInfo(
const JingleTransportInfo& transport_info) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!peer_session_ || !peer_session_->transport()) {
pending_transport_infos_.push_back(transport_info);
return;
}
peer_session_->transport()->ProcessTransportInfo(transport_info);
}
void PeerConnectionProcess::OnSendTransportInfo(
std::unique_ptr<JingleTransportInfo> transport_info) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!transport_info) {
return;
}
if (transport_event_handler_.is_bound()) {
transport_event_handler_->SendTransportInfo(std::move(*transport_info));
}
}
void PeerConnectionProcess::DisconnectSession(
protocol::ErrorCode error,
const std::string& error_details,
const SourceLocation& error_location) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (peer_session_) {
peer_session_->DisconnectSession(error, error_details, error_location);
}
}
void PeerConnectionProcess::OnChannelError() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
LOG(ERROR) << "Daemon channel error in PC process. Terminating.";
Shutdown(1);
}
void PeerConnectionProcess::OnSessionDisconnected() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
LOG(INFO)
<< "PeerSession dropped by Network process; shutting down PC process.";
desktop_session_requester_remote_.reset();
pairing_requester_remote_.reset();
transport_event_handler_.reset();
pending_start_transport_.reset();
pending_transport_infos_.clear();
Shutdown(0);
}
void PeerConnectionProcess::RequestPairing(
const std::string& client_name,
PeerSessionFactory::RequestPairingResponseCallback response_cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!pairing_requester_remote_.is_bound()) {
std::move(response_cb).Run(std::nullopt);
return;
}
pairing_requester_remote_->RequestPairing(client_name,
std::move(response_cb));
}
void PeerConnectionProcess::Shutdown(int exit_code) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (on_shutdown_for_testing_) {
std::move(on_shutdown_for_testing_).Run();
return;
}
base::Process::TerminateCurrentProcessImmediately(exit_code);
}
void PeerConnectionProcess::GetDesktopSession(
mojo::PendingReceiver<mojom::DesktopSession> control_receiver,
mojo::PendingRemote<mojom::DesktopSessionEvents> events_remote,
mojom::DesktopSessionOptionsPtr options) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!desktop_session_requester_remote_.is_bound()) {
LOG(ERROR)
<< "GetDesktopSession called when DesktopSessionRequester is not "
<< "bound.";
return;
}
// The rest of `options` (such as `client_id` and `is_curtained`) is already
// known by the network process and will be populated there.
// TODO(crbug.com/552604892): Clean up the arguments of the GetDesktopSession
// callback so it only takes the required parameters instead of full options.
desktop_session_requester_remote_->RequestDesktopSession(
std::move(control_receiver), std::move(events_remote),
options ? options->screen_resolution : ScreenResolution());
}
} // namespace remoting