[go: nahoru, domu]

blob: 05a5607fb15ccb006bc3e60ff7b4e5f9f332ed9a [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/nearby_sharing/nearby_connections_manager_impl.h"
#include "base/containers/contains.h"
#include "base/files/file_util.h"
#include "base/functional/callback_helpers.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_number_conversions.h"
#include "base/unguessable_token.h"
#include "chrome/browser/nearby_sharing/common/nearby_share_features.h"
#include "chrome/browser/nearby_sharing/constants.h"
#include "chrome/browser/nearby_sharing/public/cpp/nearby_connections_manager.h"
#include "chromeos/ash/services/nearby/public/mojom/nearby_connections_types.mojom.h"
#include "components/cross_device/logging/logging.h"
#include "crypto/random.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "net/base/network_change_notifier.h"
namespace {
const char kFastAdvertisementServiceUuid[] =
"0000fef3-0000-1000-8000-00805f9b34fb";
const nearby::connections::mojom::Strategy kStrategy =
nearby::connections::mojom::Strategy::kP2pPointToPoint;
bool ShouldUseInternet(DataUsage data_usage, PowerLevel power_level) {
// We won't use internet if the user requested we don't.
if (data_usage == DataUsage::kOffline) {
return false;
}
// We won't use internet in a low power mode.
if (power_level == PowerLevel::kLowPower) {
return false;
}
net::NetworkChangeNotifier::ConnectionType connection_type =
net::NetworkChangeNotifier::GetConnectionType();
// Verify that this network has an internet connection.
if (connection_type == net::NetworkChangeNotifier::CONNECTION_NONE) {
CD_LOG(VERBOSE, Feature::NC) << __func__ << ": No internet connection.";
return false;
}
// If the user wants to limit Wi-Fi, then don't use it on metered networks.
if (data_usage == DataUsage::kWifiOnly &&
net::NetworkChangeNotifier::GetConnectionCost() ==
net::NetworkChangeNotifier::CONNECTION_COST_METERED) {
CD_LOG(VERBOSE, Feature::NC) << __func__ << ": Do not use internet with "
<< data_usage << " and a metered connection.";
return false;
}
// We're online, the user hasn't disabled Wi-Fi, let's use it!
return true;
}
bool ShouldEnableWebRtc(DataUsage data_usage, PowerLevel power_level) {
return base::FeatureList::IsEnabled(features::kNearbySharingWebRtc) &&
ShouldUseInternet(data_usage, power_level);
}
bool ShouldEnableWifiLan(DataUsage data_usage, PowerLevel power_level) {
if (!base::FeatureList::IsEnabled(features::kNearbySharingWifiLan)) {
return false;
}
// WifiLan only works if both devices are using the same router. We can't
// guarantee this, but at least check that we are using Wi-Fi or ethernet.
// TODO(https://crbug.com/1261238): Test if WifiLan can work if both devices
// are connected to the router without an internet connection. If so, return
// true if connection_type == net::NetworkChangeNotifier::CONNECTION_NONE.
net::NetworkChangeNotifier::ConnectionType connection_type =
net::NetworkChangeNotifier::GetConnectionType();
bool is_connection_wifi_or_ethernet =
connection_type == net::NetworkChangeNotifier::CONNECTION_WIFI ||
connection_type == net::NetworkChangeNotifier::CONNECTION_ETHERNET;
return ShouldUseInternet(data_usage, power_level) &&
is_connection_wifi_or_ethernet;
}
std::string MediumSelectionToString(
const nearby::connections::mojom::MediumSelection& mediums) {
std::stringstream ss;
ss << "{";
if (mediums.bluetooth) {
ss << "bluetooth ";
}
if (mediums.ble) {
ss << "ble ";
}
if (mediums.web_rtc) {
ss << "webrtc ";
}
if (mediums.wifi_lan) {
ss << "wifilan ";
}
ss << "}";
return ss.str();
}
} // namespace
NearbyConnectionsManagerImpl::NearbyConnectionsManagerImpl(
ash::nearby::NearbyProcessManager* process_manager,
const std::string& service_id)
: process_manager_(process_manager), service_id_(service_id) {
DCHECK(process_manager_);
}
NearbyConnectionsManagerImpl::~NearbyConnectionsManagerImpl() {
ClearIncomingPayloads();
}
void NearbyConnectionsManagerImpl::Shutdown() {
Reset();
}
void NearbyConnectionsManagerImpl::StartAdvertising(
std::vector<uint8_t> endpoint_info,
IncomingConnectionListener* listener,
PowerLevel power_level,
DataUsage data_usage,
ConnectionsCallback callback) {
DCHECK(listener);
DCHECK(!incoming_connection_listener_);
nearby::connections::mojom::NearbyConnections* nearby_connections =
GetNearbyConnections();
if (!nearby_connections) {
std::move(callback).Run(ConnectionsStatus::kError);
return;
}
bool is_high_power = power_level == PowerLevel::kHighPower;
bool use_ble = !is_high_power;
auto allowed_mediums = MediumSelection::New(
/*bluetooth=*/is_high_power, /*ble=*/use_ble,
// Using kHighPower here rather than power_level to signal that power
// level isn't a factor when deciding whether or not to allow WebRTC
// upgrades from this advertisement.
ShouldEnableWebRtc(data_usage, PowerLevel::kHighPower),
/*wifi_lan=*/
ShouldEnableWifiLan(data_usage, PowerLevel::kHighPower) &&
kIsWifiLanAdvertisingSupported);
CD_LOG(VERBOSE, Feature::NC)
<< __func__ << ": "
<< "is_high_power=" << (is_high_power ? "yes" : "no")
<< ", data_usage=" << data_usage
<< ", allowed_mediums=" << MediumSelectionToString(*allowed_mediums);
mojo::PendingRemote<ConnectionLifecycleListener> lifecycle_listener;
connection_lifecycle_listeners_.Add(
this, lifecycle_listener.InitWithNewPipeAndPassReceiver());
// Only auto-upgrade bandwidth if advertising at high-visibility.
// This acts as a privacy safeguard when advertising in the background.
// Bandwidth upgrades may expose stable identifiers, and so they're
// only safe to expose after we've verified the sender's identity.
// Once we have verified their identity, we will manually trigger
// a bandwidth upgrade. This isn't a concern in the foreground
// because high-visibility already leaks the device name.
bool auto_upgrade_bandwidth = is_high_power;
incoming_connection_listener_ = listener;
nearby_connections->StartAdvertising(
service_id_, endpoint_info,
AdvertisingOptions::New(
kStrategy, std::move(allowed_mediums), auto_upgrade_bandwidth,
/*enforce_topology_constraints=*/true,
/*enable_bluetooth_listening=*/use_ble,
/*enable_webrtc_listening=*/
ShouldEnableWebRtc(data_usage, power_level),
/*fast_advertisement_service_uuid=*/
device::BluetoothUUID(kFastAdvertisementServiceUuid)),
std::move(lifecycle_listener), std::move(callback));
}
void NearbyConnectionsManagerImpl::StopAdvertising(
ConnectionsCallback callback) {
incoming_connection_listener_ = nullptr;
// TODO(https://crbug.com/1177088): Determine if we should attempt to bind to
// process.
if (!process_reference_) {
return;
}
process_reference_->GetNearbyConnections()->StopAdvertising(
service_id_, std::move(callback));
}
void NearbyConnectionsManagerImpl::StartDiscovery(
DiscoveryListener* listener,
DataUsage data_usage,
ConnectionsCallback callback) {
DCHECK(listener);
DCHECK(!discovery_listener_);
nearby::connections::mojom::NearbyConnections* nearby_connections =
GetNearbyConnections();
if (!nearby_connections) {
std::move(callback).Run(ConnectionsStatus::kError);
return;
}
auto allowed_mediums = MediumSelection::New(
/*bluetooth=*/true,
/*ble=*/true,
/*webrtc=*/ShouldEnableWebRtc(data_usage, PowerLevel::kHighPower),
/*wifi_lan=*/
ShouldEnableWifiLan(data_usage, PowerLevel::kHighPower) &&
kIsWifiLanDiscoverySupported);
CD_LOG(VERBOSE, Feature::NC)
<< __func__ << ": "
<< "data_usage=" << data_usage
<< ", allowed_mediums=" << MediumSelectionToString(*allowed_mediums);
discovery_listener_ = listener;
nearby_connections->StartDiscovery(
service_id_,
DiscoveryOptions::New(
kStrategy, std::move(allowed_mediums),
device::BluetoothUUID(kFastAdvertisementServiceUuid),
/*is_out_of_band_connection=*/false),
endpoint_discovery_listener_.BindNewPipeAndPassRemote(),
std::move(callback));
}
void NearbyConnectionsManagerImpl::StopDiscovery() {
discovered_endpoints_.clear();
discovery_listener_ = nullptr;
endpoint_discovery_listener_.reset();
// TODO(https://crbug.com/1177088): Determine if we should attempt to bind to
// process.
if (!process_reference_) {
return;
}
process_reference_->GetNearbyConnections()->StopDiscovery(
service_id_, base::BindOnce([](ConnectionsStatus status) {
CD_LOG(VERBOSE, Feature::NC)
<< __func__
<< ": Stop discovery attempted over Nearby "
"Connections with result: "
<< ConnectionsStatusToString(status);
}));
}
void NearbyConnectionsManagerImpl::Connect(
std::vector<uint8_t> endpoint_info,
const std::string& endpoint_id,
absl::optional<std::vector<uint8_t>> bluetooth_mac_address,
DataUsage data_usage,
NearbyConnectionCallback callback) {
// TODO(https://crbug.com/1177088): Determine if we should attempt to bind to
// process.
if (!process_reference_) {
std::move(callback).Run(nullptr);
return;
}
if (bluetooth_mac_address && bluetooth_mac_address->size() != 6) {
bluetooth_mac_address.reset();
}
auto allowed_mediums = MediumSelection::New(
/*bluetooth=*/true,
/*ble=*/false, ShouldEnableWebRtc(data_usage, PowerLevel::kHighPower),
/*wifi_lan=*/ShouldEnableWifiLan(data_usage, PowerLevel::kHighPower));
CD_LOG(VERBOSE, Feature::NC)
<< __func__ << ": "
<< "data_usage=" << data_usage
<< ", allowed_mediums=" << MediumSelectionToString(*allowed_mediums);
mojo::PendingRemote<ConnectionLifecycleListener> lifecycle_listener;
connection_lifecycle_listeners_.Add(
this, lifecycle_listener.InitWithNewPipeAndPassReceiver());
auto result =
pending_outgoing_connections_.emplace(endpoint_id, std::move(callback));
DCHECK(result.second);
auto timeout_timer = std::make_unique<base::OneShotTimer>();
timeout_timer->Start(
FROM_HERE, kInitiateNearbyConnectionTimeout,
base::BindOnce(&NearbyConnectionsManagerImpl::OnConnectionTimedOut,
weak_ptr_factory_.GetWeakPtr(), endpoint_id));
connect_timeout_timers_.emplace(endpoint_id, std::move(timeout_timer));
process_reference_->GetNearbyConnections()->RequestConnection(
service_id_, endpoint_info, endpoint_id,
ConnectionOptions::New(std::move(allowed_mediums),
std::move(bluetooth_mac_address),
/*keep_alive_interval_millis=*/absl::nullopt,
/*keep_alive_timeout_millis=*/absl::nullopt),
std::move(lifecycle_listener),
base::BindOnce(&NearbyConnectionsManagerImpl::OnConnectionRequested,
weak_ptr_factory_.GetWeakPtr(), endpoint_id));
}
void NearbyConnectionsManagerImpl::OnConnectionTimedOut(
const std::string& endpoint_id) {
CD_LOG(ERROR, Feature::NC)
<< "Failed to connect to the remote shareTarget: Timed out.";
Disconnect(endpoint_id);
}
void NearbyConnectionsManagerImpl::OnConnectionRequested(
const std::string& endpoint_id,
ConnectionsStatus status) {
auto it = pending_outgoing_connections_.find(endpoint_id);
if (it == pending_outgoing_connections_.end()) {
return;
}
if (status != ConnectionsStatus::kSuccess) {
CD_LOG(ERROR, Feature::NC)
<< "Failed to connect to the remote shareTarget: "
<< ConnectionsStatusToString(status);
Disconnect(endpoint_id);
return;
}
// TODO(crbug/1111458): Support TransferManager.
}
void NearbyConnectionsManagerImpl::Disconnect(const std::string& endpoint_id) {
// TODO(https://crbug.com/1177088): Determine if we should attempt to bind to
// process.
if (!process_reference_) {
return;
}
process_reference_->GetNearbyConnections()->DisconnectFromEndpoint(
service_id_, endpoint_id,
base::BindOnce(
[](const std::string& endpoint_id, ConnectionsStatus status) {
CD_LOG(VERBOSE, Feature::NC)
<< __func__ << ": Disconnecting from endpoint " << endpoint_id
<< " attempted over Nearby Connections with result: "
<< ConnectionsStatusToString(status);
},
endpoint_id));
OnDisconnected(endpoint_id);
CD_LOG(INFO, Feature::NC) << "Disconnected from " << endpoint_id;
}
void NearbyConnectionsManagerImpl::Send(
const std::string& endpoint_id,
PayloadPtr payload,
base::WeakPtr<PayloadStatusListener> listener) {
// TODO(https://crbug.com/1177088): Determine if we should attempt to bind to
// process.
if (!process_reference_) {
return;
}
if (listener) {
RegisterPayloadStatusListener(payload->id, listener);
}
process_reference_->GetNearbyConnections()->SendPayload(
service_id_, {endpoint_id}, std::move(payload),
base::BindOnce(
[](const std::string& endpoint_id, ConnectionsStatus status) {
CD_LOG(VERBOSE, Feature::NC)
<< __func__ << ": Sending payload to endpoint " << endpoint_id
<< " attempted over Nearby Connections with result: "
<< ConnectionsStatusToString(status);
},
endpoint_id));
}
void NearbyConnectionsManagerImpl::RegisterPayloadStatusListener(
int64_t payload_id,
base::WeakPtr<PayloadStatusListener> listener) {
payload_status_listeners_.insert_or_assign(payload_id, listener);
}
void NearbyConnectionsManagerImpl::RegisterPayloadPath(
int64_t payload_id,
const base::FilePath& file_path,
ConnectionsCallback callback) {
if (!process_reference_) {
return;
}
DCHECK(!file_path.empty());
file_handler_.CreateFile(
file_path, base::BindOnce(&NearbyConnectionsManagerImpl::OnFileCreated,
weak_ptr_factory_.GetWeakPtr(), payload_id,
std::move(callback)));
}
void NearbyConnectionsManagerImpl::OnFileCreated(
int64_t payload_id,
ConnectionsCallback callback,
NearbyFileHandler::CreateFileResult result) {
// TODO(https://crbug.com/1177088): Determine if we should attempt to bind to
// process.
if (!process_reference_) {
return;
}
process_reference_->GetNearbyConnections()->RegisterPayloadFile(
service_id_, payload_id, std::move(result.input_file),
std::move(result.output_file), std::move(callback));
}
NearbyConnectionsManagerImpl::Payload*
NearbyConnectionsManagerImpl::GetIncomingPayload(int64_t payload_id) {
auto it = incoming_payloads_.find(payload_id);
if (it == incoming_payloads_.end()) {
return nullptr;
}
return it->second.get();
}
void NearbyConnectionsManagerImpl::Cancel(int64_t payload_id) {
// TODO(https://crbug.com/1177088): Determine if we should attempt to bind to
// process.
if (!process_reference_) {
return;
}
auto it = payload_status_listeners_.find(payload_id);
if (it != payload_status_listeners_.end()) {
base::WeakPtr<PayloadStatusListener> listener = it->second;
payload_status_listeners_.erase(payload_id);
// Note: The listener might be invalidated, for example, if it is shared
// with another payload in the same transfer.
if (listener) {
listener->OnStatusUpdate(
PayloadTransferUpdate::New(payload_id, PayloadStatus::kCanceled,
/*total_bytes=*/0,
/*bytes_transferred=*/0),
/*upgraded_medium=*/absl::nullopt);
}
}
process_reference_->GetNearbyConnections()->CancelPayload(
service_id_, payload_id,
base::BindOnce(
[](int64_t payload_id, ConnectionsStatus status) {
CD_LOG(VERBOSE, Feature::NC)
<< __func__ << ": Cancelling payload to id " << payload_id
<< " attempted over Nearby Connections with result: "
<< ConnectionsStatusToString(status);
},
payload_id));
CD_LOG(INFO, Feature::NC) << "Cancelling payload: " << payload_id;
}
void NearbyConnectionsManagerImpl::ClearIncomingPayloads() {
std::vector<PayloadPtr> payloads;
for (auto& it : incoming_payloads_) {
payloads.push_back(std::move(it.second));
payload_status_listeners_.erase(it.first);
}
file_handler_.ReleaseFilePayloads(std::move(payloads));
incoming_payloads_.clear();
}
absl::optional<std::string>
NearbyConnectionsManagerImpl::GetAuthenticationToken(
const std::string& endpoint_id) {
auto it = connection_info_map_.find(endpoint_id);
if (it == connection_info_map_.end()) {
return absl::nullopt;
}
return it->second->authentication_token;
}
absl::optional<std::vector<uint8_t>>
NearbyConnectionsManagerImpl::GetRawAuthenticationToken(
const std::string& endpoint_id) {
auto it = connection_info_map_.find(endpoint_id);
if (it == connection_info_map_.end()) {
return absl::nullopt;
}
return it->second->raw_authentication_token;
}
void NearbyConnectionsManagerImpl::RegisterBandwidthUpgradeListener(
base::WeakPtr<BandwidthUpgradeListener> listener) {
CHECK(!bandwidth_upgrade_listener_);
bandwidth_upgrade_listener_ = listener;
}
void NearbyConnectionsManagerImpl::UpgradeBandwidth(
const std::string& endpoint_id) {
// TODO(https://crbug.com/1177088): Determine if we should attempt to bind to
// process.
if (!process_reference_) {
return;
}
// The only bandwidth upgrade mediums at this point are WebRTC and WifiLan.
if (!base::FeatureList::IsEnabled(features::kNearbySharingWebRtc) &&
!base::FeatureList::IsEnabled(features::kNearbySharingWifiLan)) {
return;
}
requested_bwu_endpoint_ids_.emplace(endpoint_id);
process_reference_->GetNearbyConnections()->InitiateBandwidthUpgrade(
service_id_, endpoint_id,
base::BindOnce(
[](const std::string& endpoint_id, ConnectionsStatus status) {
CD_LOG(VERBOSE, Feature::NC)
<< __func__ << ": Bandwidth upgrade attempted to endpoint "
<< endpoint_id << "over Nearby Connections with result: "
<< ConnectionsStatusToString(status);
base::UmaHistogramBoolean(
"Nearby.Share.Medium.InitiateBandwidthUpgradeResult",
status == ConnectionsStatus::kSuccess);
},
endpoint_id));
}
base::WeakPtr<NearbyConnectionsManager>
NearbyConnectionsManagerImpl::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
void NearbyConnectionsManagerImpl::OnNearbyProcessStopped(
ash::nearby::NearbyProcessManager::NearbyProcessShutdownReason) {
CD_LOG(VERBOSE, Feature::NC) << __func__;
Reset();
}
void NearbyConnectionsManagerImpl::OnEndpointFound(
const std::string& endpoint_id,
DiscoveredEndpointInfoPtr info) {
if (!discovery_listener_) {
CD_LOG(INFO, Feature::NC) << "Ignoring discovered endpoint "
<< base::HexEncode(info->endpoint_info.data(),
info->endpoint_info.size())
<< " because we're no longer "
"in discovery mode";
return;
}
auto result = discovered_endpoints_.insert(endpoint_id);
if (!result.second) {
CD_LOG(INFO, Feature::NC) << "Ignoring discovered endpoint "
<< base::HexEncode(info->endpoint_info.data(),
info->endpoint_info.size())
<< " because we've already "
"reported this endpoint";
return;
}
discovery_listener_->OnEndpointDiscovered(endpoint_id, info->endpoint_info);
CD_LOG(INFO, Feature::NC)
<< "Discovered "
<< base::HexEncode(info->endpoint_info.data(), info->endpoint_info.size())
<< " over Nearby Connections";
}
void NearbyConnectionsManagerImpl::OnEndpointLost(
const std::string& endpoint_id) {
if (!discovered_endpoints_.erase(endpoint_id)) {
CD_LOG(INFO, Feature::NC) << "Ignoring lost endpoint " << endpoint_id
<< " because we haven't reported this endpoint";
return;
}
if (!discovery_listener_) {
CD_LOG(INFO, Feature::NC) << "Ignoring lost endpoint " << endpoint_id
<< " because we're no longer in discovery mode";
return;
}
discovery_listener_->OnEndpointLost(endpoint_id);
CD_LOG(INFO, Feature::NC)
<< "Endpoint " << endpoint_id << " lost over Nearby Connections";
}
void NearbyConnectionsManagerImpl::OnConnectionInitiated(
const std::string& endpoint_id,
ConnectionInfoPtr info) {
// TODO(https://crbug.com/1177088): Determine if we should attempt to bind to
// process.
if (!process_reference_) {
return;
}
bool is_incoming_connection = info->is_incoming_connection;
const std::vector<uint8_t>& endpoint_info = info->endpoint_info;
auto result = connection_info_map_.emplace(endpoint_id, std::move(info));
DCHECK(result.second);
mojo::PendingRemote<PayloadListener> payload_listener;
payload_listeners_.Add(this,
payload_listener.InitWithNewPipeAndPassReceiver());
if (is_incoming_connection && incoming_connection_listener_) {
incoming_connection_listener_->OnIncomingConnectionInitiated(endpoint_id,
endpoint_info);
}
process_reference_->GetNearbyConnections()->AcceptConnection(
service_id_, endpoint_id, std::move(payload_listener),
base::BindOnce(
[](const std::string& endpoint_id, ConnectionsStatus status) {
CD_LOG(VERBOSE, Feature::NC)
<< __func__ << ": Accept connection attempted to endpoint "
<< endpoint_id << " over Nearby Connections with result: "
<< ConnectionsStatusToString(status);
},
endpoint_id));
}
void NearbyConnectionsManagerImpl::OnConnectionAccepted(
const std::string& endpoint_id) {
auto it = connection_info_map_.find(endpoint_id);
if (it == connection_info_map_.end()) {
return;
}
if (it->second->is_incoming_connection) {
if (!incoming_connection_listener_) {
// Not in advertising mode.
Disconnect(endpoint_id);
return;
}
auto result = connections_.emplace(
endpoint_id, std::make_unique<NearbyConnectionImpl>(this, endpoint_id));
DCHECK(result.second);
incoming_connection_listener_->OnIncomingConnectionAccepted(
endpoint_id, it->second->endpoint_info, result.first->second.get());
} else {
auto pending_it = pending_outgoing_connections_.find(endpoint_id);
if (pending_it == pending_outgoing_connections_.end()) {
Disconnect(endpoint_id);
return;
}
auto result = connections_.emplace(
endpoint_id, std::make_unique<NearbyConnectionImpl>(this, endpoint_id));
DCHECK(result.second);
std::move(pending_it->second).Run(result.first->second.get());
pending_outgoing_connections_.erase(pending_it);
connect_timeout_timers_.erase(endpoint_id);
}
}
void NearbyConnectionsManagerImpl::OnConnectionRejected(
const std::string& endpoint_id,
Status status) {
connection_info_map_.erase(endpoint_id);
auto it = pending_outgoing_connections_.find(endpoint_id);
if (it != pending_outgoing_connections_.end()) {
std::move(it->second).Run(nullptr);
pending_outgoing_connections_.erase(it);
connect_timeout_timers_.erase(endpoint_id);
}
// TODO(crbug/1111458): Support TransferManager.
}
void NearbyConnectionsManagerImpl::OnDisconnected(
const std::string& endpoint_id) {
connection_info_map_.erase(endpoint_id);
auto it = pending_outgoing_connections_.find(endpoint_id);
if (it != pending_outgoing_connections_.end()) {
std::move(it->second).Run(nullptr);
pending_outgoing_connections_.erase(it);
connect_timeout_timers_.erase(endpoint_id);
}
connections_.erase(endpoint_id);
if (base::Contains(requested_bwu_endpoint_ids_, endpoint_id)) {
base::UmaHistogramBoolean(
"Nearby.Share.Medium.RequestedBandwidthUpgradeResult",
base::Contains(current_upgraded_mediums_, endpoint_id));
}
requested_bwu_endpoint_ids_.erase(endpoint_id);
on_bandwidth_changed_endpoint_ids_.erase(endpoint_id);
current_upgraded_mediums_.erase(endpoint_id);
// TODO(crbug/1111458): Support TransferManager.
}
void NearbyConnectionsManagerImpl::OnBandwidthChanged(
const std::string& endpoint_id,
Medium medium) {
// `OnBandwidthChanged` is always called for the first Medium we connected to.
// This is not guaranteed to be a specific Medium, but is usually Bluetooth.
// This may or may not be preceded by a call to `UpgradeBandwidth`. It's not
// useful to record this first Medium since no Bandwidth Upgrade occurred, so
// we ignore it.
if (!base::Contains(on_bandwidth_changed_endpoint_ids_, endpoint_id)) {
CD_LOG(VERBOSE, Feature::NC)
<< __func__ << ": Initial call with medium=" << medium
<< "; endpoint_id=" << endpoint_id;
on_bandwidth_changed_endpoint_ids_.emplace(endpoint_id);
} else {
CD_LOG(VERBOSE, Feature::NC) << __func__ << ": Changed to medium=" << medium
<< "; endpoint_id=" << endpoint_id;
base::UmaHistogramEnumeration("Nearby.Share.Medium.ChangedToMedium",
medium);
current_upgraded_mediums_.insert_or_assign(endpoint_id, medium);
// Only propagate this event on actual bandwidth upgrades.
if (bandwidth_upgrade_listener_) {
bandwidth_upgrade_listener_->OnBandwidthUpgrade(endpoint_id, medium);
}
}
// TODO(crbug/1111458): Support TransferManager.
}
void NearbyConnectionsManagerImpl::OnPayloadReceived(
const std::string& endpoint_id,
PayloadPtr payload) {
auto result = incoming_payloads_.emplace(payload->id, std::move(payload));
DCHECK(result.second);
}
void NearbyConnectionsManagerImpl::OnPayloadTransferUpdate(
const std::string& endpoint_id,
PayloadTransferUpdatePtr update) {
// TODO(https://crbug.com/1177088): Determine if we should attempt to bind to
// process.
if (!process_reference_) {
return;
}
// If this is a payload we've registered for, then forward its status to the
// PayloadStatusListener if it still exists. We don't need to do anything more
// with the payload.
auto listener_it = payload_status_listeners_.find(update->payload_id);
if (listener_it != payload_status_listeners_.end()) {
base::WeakPtr<PayloadStatusListener> listener = listener_it->second;
switch (update->status) {
case PayloadStatus::kInProgress:
break;
case PayloadStatus::kSuccess:
case PayloadStatus::kCanceled:
case PayloadStatus::kFailure:
payload_status_listeners_.erase(update->payload_id);
break;
}
// Note: The listener might be invalidated, for example, if it is shared
// with another payload in the same transfer.
if (listener) {
listener->OnStatusUpdate(std::move(update),
GetUpgradedMedium(endpoint_id));
}
return;
}
// If this is an incoming payload that we have not registered for, then we'll
// treat it as a control frame (eg. IntroductionFrame) and forward it to the
// associated NearbyConnection.
auto payload_it = incoming_payloads_.find(update->payload_id);
if (payload_it == incoming_payloads_.end()) {
return;
}
if (!payload_it->second->content->is_bytes()) {
CD_LOG(WARNING, Feature::NC)
<< "Received unknown payload of file type. Cancelling.";
process_reference_->GetNearbyConnections()->CancelPayload(
service_id_, payload_it->first, base::DoNothing());
return;
}
if (update->status != PayloadStatus::kSuccess) {
return;
}
auto connections_it = connections_.find(endpoint_id);
if (connections_it == connections_.end()) {
return;
}
CD_LOG(INFO, Feature::NC)
<< "Writing incoming byte message to NearbyConnection.";
connections_it->second->WriteMessage(
payload_it->second->content->get_bytes()->bytes);
}
nearby::connections::mojom::NearbyConnections*
NearbyConnectionsManagerImpl::GetNearbyConnections() {
if (!process_reference_) {
process_reference_ = process_manager_->GetNearbyProcessReference(
base::BindOnce(&NearbyConnectionsManagerImpl::OnNearbyProcessStopped,
base::Unretained(this)));
if (!process_reference_) {
CD_LOG(WARNING, Feature::NC)
<< __func__ << "Failed to get a reference to the nearby process.";
return nullptr;
}
}
nearby::connections::mojom::NearbyConnections* nearby_connections =
process_reference_->GetNearbyConnections().get();
if (!nearby_connections) {
CD_LOG(WARNING, Feature::NC)
<< __func__
<< "Failed to get a nearby connections from process reference.";
}
return nearby_connections;
}
void NearbyConnectionsManagerImpl::Reset() {
if (process_reference_) {
process_reference_->GetNearbyConnections()->StopAllEndpoints(
service_id_, base::BindOnce([](ConnectionsStatus status) {
CD_LOG(VERBOSE, Feature::NC)
<< __func__
<< ": Stop all endpoints attempted over Nearby "
"Connections with result: "
<< ConnectionsStatusToString(status);
}));
}
process_reference_.reset();
discovered_endpoints_.clear();
payload_status_listeners_.clear();
ClearIncomingPayloads();
connections_.clear();
connection_info_map_.clear();
discovery_listener_ = nullptr;
incoming_connection_listener_ = nullptr;
endpoint_discovery_listener_.reset();
connect_timeout_timers_.clear();
requested_bwu_endpoint_ids_.clear();
on_bandwidth_changed_endpoint_ids_.clear();
current_upgraded_mediums_.clear();
for (auto& entry : pending_outgoing_connections_) {
std::move(entry.second).Run(/*connection=*/nullptr);
}
pending_outgoing_connections_.clear();
}
absl::optional<nearby::connections::mojom::Medium>
NearbyConnectionsManagerImpl::GetUpgradedMedium(
const std::string& endpoint_id) const {
const auto it = current_upgraded_mediums_.find(endpoint_id);
if (it == current_upgraded_mediums_.end()) {
return absl::nullopt;
}
return it->second;
}