blob: 9d3e541e503b7d0e4808120f21e8ed446f684518 [file] [log] [blame]
// Copyright 2020 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 "chrome/browser/sharing/webrtc/sharing_service_host.h"
#include "base/callback.h"
#include "base/guid.h"
#include "chrome/browser/sharing/sharing_constants.h"
#include "chrome/browser/sharing/sharing_device_source.h"
#include "chrome/browser/sharing/sharing_metrics.h"
#include "chrome/browser/sharing/sharing_sync_preference.h"
#include "chrome/browser/sharing/sharing_utils.h"
#include "chrome/browser/sharing/webrtc/sharing_mojo_service.h"
#include "chrome/browser/sharing/webrtc/sharing_webrtc_connection_host.h"
#include "chrome/browser/sharing/webrtc/webrtc_signalling_host_fcm.h"
#include "components/sync_device_info/device_info_util.h"
#include "content/public/browser/network_context_client_base.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/service_process_host.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/mojom/network_service.mojom.h"
#include "services/network/public/mojom/p2p.mojom.h"
#include "services/network/public/mojom/p2p_trusted.mojom.h"
#include "url/gurl.h"
namespace {
bool HasVapidInfo(
const chrome_browser_sharing::FCMChannelConfiguration& fcm_configuration) {
return !fcm_configuration.vapid_fcm_token().empty() &&
!fcm_configuration.vapid_p256dh().empty() &&
!fcm_configuration.vapid_auth_secret().empty();
}
bool HasSenderIdInfo(
const chrome_browser_sharing::FCMChannelConfiguration& fcm_configuration) {
return !fcm_configuration.sender_id_fcm_token().empty() &&
!fcm_configuration.sender_id_p256dh().empty() &&
!fcm_configuration.sender_id_auth_secret().empty();
}
syncer::DeviceInfo::SharingTargetInfo GetVapidTargetInfo(
const chrome_browser_sharing::FCMChannelConfiguration& fcm_configuration) {
if (!HasVapidInfo(fcm_configuration))
return {};
return {fcm_configuration.vapid_fcm_token(), fcm_configuration.vapid_p256dh(),
fcm_configuration.vapid_auth_secret()};
}
syncer::DeviceInfo::SharingTargetInfo GetSenderIdTargetInfo(
const chrome_browser_sharing::FCMChannelConfiguration& fcm_configuration) {
if (!HasSenderIdInfo(fcm_configuration))
return {};
return {fcm_configuration.sender_id_fcm_token(),
fcm_configuration.sender_id_p256dh(),
fcm_configuration.sender_id_auth_secret()};
}
SharingWebRtcConnectionHost::EncryptionInfo GetEncryptionInfo(
const chrome_browser_sharing::FCMChannelConfiguration& fcm_configuration,
SharingSyncPreference* sync_prefs) {
// Prefer sender id info if available.
if (HasSenderIdInfo(fcm_configuration)) {
return {kSharingSenderID, fcm_configuration.sender_id_p256dh(),
fcm_configuration.sender_id_auth_secret()};
}
base::Optional<SharingSyncPreference::FCMRegistration> fcm_registration =
sync_prefs->GetFCMRegistration();
if (!fcm_registration || !fcm_registration->authorized_entity) {
LOG(ERROR) << "Unable to retrieve FCM registration";
// TODO(knollr): add UMA logging for this
return {};
}
return {*fcm_registration->authorized_entity,
fcm_configuration.vapid_p256dh(),
fcm_configuration.vapid_auth_secret()};
}
// TODO(crbug.com/1047246): Remove this after SharingMessageSender supports
// messages via FCMChannelConfiguration directly.
std::unique_ptr<syncer::DeviceInfo> CreateDeviceInfo(
const std::string& device_guid,
const chrome_browser_sharing::FCMChannelConfiguration& fcm_configuration,
SharingDeviceSource* device_source) {
syncer::DeviceInfo::SharingInfo sharing_info(
GetVapidTargetInfo(fcm_configuration),
GetSenderIdTargetInfo(fcm_configuration),
/*enabled_features=*/{});
// Try to look up the device to get all data for the sending metrics.
auto device_info = device_source->GetDeviceByGuid(device_guid);
LogSharingDeviceInfoAvailable(device_info != nullptr);
if (!device_info) {
// Device is not available in the local DeviceInfoTracker but we know how to
// send a message to it via |fcm_configuration| so create an otherwise empty
// DeviceInfo object.
return std::make_unique<syncer::DeviceInfo>(
device_guid, /*client_name=*/std::string(),
/*chrome_version=*/std::string(), /*sync_user_agent=*/std::string(),
/*device_type=*/sync_pb::SyncEnums::TYPE_UNSET,
/*signin_scoped_device_id=*/std::string(),
/*hardware_info=*/base::SysInfo::HardwareInfo(),
/*last_updated_timestamp=*/base::Time(),
/*pulse_interval=*/base::TimeDelta(),
/*send_tab_to_self_receiving_enabled=*/true, std::move(sharing_info));
}
// We want to send to the passed |fcm_configuration| as it is the most up to
// date information we have for that device.
device_info->set_sharing_info(std::move(sharing_info));
return device_info;
}
template <typename T>
struct MojoPipe {
mojo::PendingRemote<T> remote;
mojo::PendingReceiver<T> receiver{remote.InitWithNewPipeAndPassReceiver()};
};
// This is called from the sandboxed process after sending a message.
void OnMessageSent(
SharingMessageSender::SendMessageDelegate::SendMessageCallback callback,
std::string message_guid,
sharing::mojom::SendMessageResult result) {
switch (result) {
case sharing::mojom::SendMessageResult::kSuccess:
std::move(callback).Run(SharingSendMessageResult::kSuccessful,
message_guid, SharingChannelType::kWebRtc);
break;
case sharing::mojom::SendMessageResult::kError:
std::move(callback).Run(SharingSendMessageResult::kInternalError,
/*message_id=*/base::nullopt,
SharingChannelType::kWebRtc);
break;
}
}
} // namespace
struct SharingWebRtcMojoPipes {
MojoPipe<sharing::mojom::SignallingSender> signalling_sender;
MojoPipe<sharing::mojom::SignallingReceiver> signalling_receiver;
MojoPipe<sharing::mojom::SharingWebRtcConnectionDelegate> delegate;
MojoPipe<sharing::mojom::SharingWebRtcConnection> connection;
MojoPipe<network::mojom::P2PTrustedSocketManagerClient> socket_manager_client;
MojoPipe<network::mojom::P2PTrustedSocketManager> trusted_socket_manager;
MojoPipe<network::mojom::P2PSocketManager> socket_manager;
MojoPipe<network::mojom::MdnsResponder> mdns_responder;
};
SharingServiceHost::SharingServiceHost(
SharingMessageSender* message_sender,
gcm::GCMDriver* gcm_driver,
SharingSyncPreference* sync_prefs,
SharingDeviceSource* device_source,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: message_sender_(message_sender),
gcm_driver_(gcm_driver),
sync_prefs_(sync_prefs),
device_source_(device_source),
ice_config_fetcher_(url_loader_factory) {}
SharingServiceHost::~SharingServiceHost() = default;
void SharingServiceHost::DoSendMessageToDevice(
const syncer::DeviceInfo& device,
base::TimeDelta time_to_live,
chrome_browser_sharing::SharingMessage message,
SendMessageCallback callback) {
chrome_browser_sharing::WebRtcMessage webrtc_message;
std::string message_guid = base::GenerateGUID();
webrtc_message.mutable_message()->Swap(&message);
webrtc_message.set_message_guid(message_guid);
// TODO(crbug.com/1044539): support multiple messages over the same connection
// or queue messages instead of rejecting them here.
if (connections_.find(device.guid()) != connections_.end()) {
std::move(callback).Run(SharingSendMessageResult::kInternalError,
/*message_id=*/base::nullopt,
SharingChannelType::kWebRtc);
return;
}
auto fcm_configuration = GetFCMChannel(device);
// Remote device must have a valid fcm config.
if (!fcm_configuration || (!HasVapidInfo(*fcm_configuration) &&
!HasSenderIdInfo(*fcm_configuration))) {
std::move(callback).Run(SharingSendMessageResult::kInternalError,
/*message_id=*/base::nullopt,
SharingChannelType::kWebRtc);
return;
}
CreateConnection(device.guid(), *fcm_configuration)
->SendMessage(std::move(webrtc_message),
base::BindOnce(&OnMessageSent, std::move(callback),
std::move(message_guid)));
}
void SharingServiceHost::OnPeerConnectionClosed(
const std::string& device_guid) {
connections_.erase(device_guid);
if (connections_.empty())
sharing_utility_service_.reset();
}
// |callback| will be called from the sandboxed process with the remote answer.
void SharingServiceHost::OnOfferReceived(
const std::string& device_guid,
const chrome_browser_sharing::FCMChannelConfiguration& fcm_configuration,
const std::string& offer,
base::OnceCallback<void(const std::string&)> callback) {
// TODO(crbug.com/1044539): support multiple messages over the same connection
// or queue messages instead of rejecting them here.
if (connections_.find(device_guid) != connections_.end()) {
std::move(callback).Run(/*answer=*/std::string());
return;
}
CreateConnection(device_guid, fcm_configuration)
->OnOfferReceived(offer, std::move(callback));
}
void SharingServiceHost::OnIceCandidatesReceived(
const std::string& device_guid,
const chrome_browser_sharing::FCMChannelConfiguration& fcm_configuration,
std::vector<sharing::mojom::IceCandidatePtr> ice_candidates) {
// IceCandidates should only be received to already open connections. Ignore
// all other ones as we don't want to start a new process here.
auto connection_iter = connections_.find(device_guid);
if (connection_iter == connections_.end())
return;
connection_iter->second->OnIceCandidatesReceived(std::move(ice_candidates));
}
void SharingServiceHost::SetSharingHandlerRegistry(
SharingHandlerRegistry* handler_registry) {
handler_registry_ = handler_registry;
}
SharingServiceHost::Connections&
SharingServiceHost::GetConnectionsForTesting() {
return connections_;
}
void SharingServiceHost::BindSharingServiceForTesting(
mojo::PendingRemote<sharing::mojom::Sharing> service) {
sharing_utility_service_.Bind(std::move(service));
sharing_utility_service_.reset_on_disconnect();
}
SharingWebRtcConnectionHost* SharingServiceHost::CreateConnection(
const std::string& device_guid,
const chrome_browser_sharing::FCMChannelConfiguration& fcm_configuration) {
auto pipes = std::make_unique<SharingWebRtcMojoPipes>();
auto signalling_host = std::make_unique<WebRtcSignallingHostFCM>(
std::move(pipes->signalling_sender.receiver),
std::move(pipes->signalling_receiver.remote), message_sender_,
CreateDeviceInfo(device_guid, fcm_configuration, device_source_));
// base::Unretained is safe as the connection is owned by |this|.
auto result = connections_.emplace(
device_guid,
std::make_unique<SharingWebRtcConnectionHost>(
std::move(signalling_host), handler_registry_, gcm_driver_,
GetEncryptionInfo(fcm_configuration, sync_prefs_),
base::BindOnce(&SharingServiceHost::OnPeerConnectionClosed,
base::Unretained(this), device_guid),
std::move(pipes->delegate.receiver),
std::move(pipes->connection.remote),
std::move(pipes->socket_manager_client.receiver),
std::move(pipes->trusted_socket_manager.remote)));
DCHECK(result.second);
GetNetworkContext()->CreateP2PSocketManager(
std::move(pipes->socket_manager_client.remote),
std::move(pipes->trusted_socket_manager.receiver),
std::move(pipes->socket_manager.receiver));
GetNetworkContext()->CreateMdnsResponder(
std::move(pipes->mdns_responder.receiver));
ice_config_fetcher_.GetIceServers(
base::BindOnce(&SharingServiceHost::OnIceServersReceived,
weak_ptr_factory_.GetWeakPtr(), std::move(pipes)));
return result.first->second.get();
}
void SharingServiceHost::OnIceServersReceived(
std::unique_ptr<SharingWebRtcMojoPipes> pipes,
std::vector<sharing::mojom::IceServerPtr> ice_servers) {
if (!sharing_utility_service_) {
sharing_utility_service_.Bind(sharing::LaunchSharing());
sharing_utility_service_.reset_on_disconnect();
}
sharing_utility_service_->CreateSharingWebRtcConnection(
std::move(pipes->signalling_sender.remote),
std::move(pipes->signalling_receiver.receiver),
std::move(pipes->delegate.remote), std::move(pipes->connection.receiver),
std::move(pipes->socket_manager.remote),
std::move(pipes->mdns_responder.remote), std::move(ice_servers));
}
network::mojom::NetworkContext* SharingServiceHost::GetNetworkContext() {
if (network_context_ && network_context_.is_connected())
return network_context_.get();
network_context_.reset();
network::mojom::NetworkContextParamsPtr context_params =
network::mojom::NetworkContextParams::New();
context_params->user_agent = "";
context_params->accept_language = "en-us,en";
content::GetNetworkService()->CreateNetworkContext(
network_context_.BindNewPipeAndPassReceiver(), std::move(context_params));
mojo::PendingRemote<network::mojom::NetworkContextClient> client_remote;
mojo::MakeSelfOwnedReceiver(
std::make_unique<content::NetworkContextClientBase>(),
client_remote.InitWithNewPipeAndPassReceiver());
network_context_->SetClient(std::move(client_remote));
return network_context_.get();
}