// Copyright 2014 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/metrics/net/network_metrics_provider.h" #include #include #include #include #include "base/compiler_specific.h" #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" #include "base/metrics/histogram_macros.h" #include "base/metrics/sparse_histogram.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/task/thread_pool.h" #include "build/build_config.h" #include "net/base/net_errors.h" #include "net/nqe/effective_connection_type_observer.h" #include "net/nqe/network_quality_estimator.h" #if BUILDFLAG(IS_ANDROID) #include "services/network/public/cpp/network_connection_tracker.h" #endif namespace metrics { SystemProfileProto::Network::EffectiveConnectionType ConvertEffectiveConnectionType( net::EffectiveConnectionType effective_connection_type) { switch (effective_connection_type) { case net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN: return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_UNKNOWN; case net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G: return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_SLOW_2G; case net::EFFECTIVE_CONNECTION_TYPE_2G: return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_2G; case net::EFFECTIVE_CONNECTION_TYPE_3G: return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_3G; case net::EFFECTIVE_CONNECTION_TYPE_4G: return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_4G; case net::EFFECTIVE_CONNECTION_TYPE_OFFLINE: return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_OFFLINE; case net::EFFECTIVE_CONNECTION_TYPE_LAST: NOTREACHED(); } NOTREACHED(); } NetworkMetricsProvider::NetworkMetricsProvider( network::NetworkConnectionTrackerAsyncGetter network_connection_tracker_async_getter, std::unique_ptr network_quality_estimator_provider) : network_connection_tracker_(nullptr), connection_type_is_ambiguous_(false), connection_type_(network::mojom::ConnectionType::CONNECTION_UNKNOWN), network_connection_tracker_initialized_(false), network_quality_estimator_provider_( std::move(network_quality_estimator_provider)), effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN), min_effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN), max_effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN) { network_connection_tracker_async_getter.Run( base::BindOnce(&NetworkMetricsProvider::SetNetworkConnectionTracker, weak_ptr_factory_.GetWeakPtr())); if (network_quality_estimator_provider_) { // Use |network_quality_estimator_provider_| to get network quality // tracker. network_quality_estimator_provider_->PostReplyOnNetworkQualityChanged( base::BindRepeating( &NetworkMetricsProvider::OnEffectiveConnectionTypeChanged, weak_ptr_factory_.GetWeakPtr())); } } NetworkMetricsProvider::~NetworkMetricsProvider() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (network_connection_tracker_) network_connection_tracker_->RemoveNetworkConnectionObserver(this); } void NetworkMetricsProvider::SetNetworkConnectionTracker( network::NetworkConnectionTracker* network_connection_tracker) { DCHECK(network_connection_tracker); network_connection_tracker_ = network_connection_tracker; network_connection_tracker_->AddNetworkConnectionObserver(this); network_connection_tracker_->GetConnectionType( &connection_type_, base::BindOnce(&NetworkMetricsProvider::OnConnectionChanged, weak_ptr_factory_.GetWeakPtr())); if (connection_type_ != network::mojom::ConnectionType::CONNECTION_UNKNOWN) network_connection_tracker_initialized_ = true; } void NetworkMetricsProvider::ProvideSystemProfileMetrics( SystemProfileProto* system_profile) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!connection_type_is_ambiguous_ || network_connection_tracker_initialized_); SystemProfileProto::Network* network = system_profile->mutable_network(); network->set_connection_type_is_ambiguous(connection_type_is_ambiguous_); network->set_connection_type(GetConnectionType()); network->set_min_effective_connection_type( ConvertEffectiveConnectionType(min_effective_connection_type_)); network->set_max_effective_connection_type( ConvertEffectiveConnectionType(max_effective_connection_type_)); // Note: We get the initial connection type when it becomes available and it // is handled at SetNetworkConnectionTracker() when GetConnectionType() is // called. // // Update the connection type. Note that this is necessary to set the network // type to "none" if there is no network connection for an entire UMA logging // window, since OnConnectionTypeChanged() ignores transitions to the "none" // state, and that is ok since it just deals with the current known state. if (network_connection_tracker_) { network_connection_tracker_->GetConnectionType(&connection_type_, base::DoNothing()); } if (connection_type_ != network::mojom::ConnectionType::CONNECTION_UNKNOWN) network_connection_tracker_initialized_ = true; // Reset the "ambiguous" flags, since a new metrics log session has started. connection_type_is_ambiguous_ = false; min_effective_connection_type_ = effective_connection_type_; max_effective_connection_type_ = effective_connection_type_; } void NetworkMetricsProvider::OnConnectionChanged( network::mojom::ConnectionType type) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // To avoid reporting an ambiguous connection type for users on flaky // connections, ignore transitions to the "none" state. Note that the // connection type is refreshed in ProvideSystemProfileMetrics() each time a // new UMA logging window begins, so users who genuinely transition to offline // mode for an extended duration will still be at least partially represented // in the metrics logs. if (type == network::mojom::ConnectionType::CONNECTION_NONE) { network_connection_tracker_initialized_ = true; return; } DCHECK(network_connection_tracker_initialized_ || connection_type_ == network::mojom::ConnectionType::CONNECTION_UNKNOWN); if (type != connection_type_ && connection_type_ != network::mojom::ConnectionType::CONNECTION_NONE && network_connection_tracker_initialized_) { // If |network_connection_tracker_initialized_| is false, it implies that // this is the first connection change callback received from network // connection tracker, and the previous connection type was // CONNECTION_UNKNOWN. In that case, connection type should not be marked as // ambiguous since there was no actual change in the connection type. connection_type_is_ambiguous_ = true; } network_connection_tracker_initialized_ = true; connection_type_ = type; } SystemProfileProto::Network::ConnectionType NetworkMetricsProvider::GetConnectionType() const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); switch (connection_type_) { case network::mojom::ConnectionType::CONNECTION_NONE: return SystemProfileProto::Network::CONNECTION_NONE; case network::mojom::ConnectionType::CONNECTION_UNKNOWN: return SystemProfileProto::Network::CONNECTION_UNKNOWN; case network::mojom::ConnectionType::CONNECTION_ETHERNET: return SystemProfileProto::Network::CONNECTION_ETHERNET; case network::mojom::ConnectionType::CONNECTION_WIFI: return SystemProfileProto::Network::CONNECTION_WIFI; case network::mojom::ConnectionType::CONNECTION_2G: return SystemProfileProto::Network::CONNECTION_2G; case network::mojom::ConnectionType::CONNECTION_3G: return SystemProfileProto::Network::CONNECTION_3G; case network::mojom::ConnectionType::CONNECTION_4G: return SystemProfileProto::Network::CONNECTION_4G; case network::mojom::ConnectionType::CONNECTION_5G: return SystemProfileProto::Network::CONNECTION_5G; case network::mojom::ConnectionType::CONNECTION_BLUETOOTH: return SystemProfileProto::Network::CONNECTION_BLUETOOTH; } NOTREACHED(); } void NetworkMetricsProvider::OnEffectiveConnectionTypeChanged( net::EffectiveConnectionType type) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); effective_connection_type_ = type; if (effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN || effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_OFFLINE) { // The effective connection type may be reported as Unknown if there is a // change in the connection type. Disregard it since network requests can't // be send during the changes in connection type. Similarly, disregard // offline as the type since it may be reported as the effective connection // type for a short period when there is a change in the connection type. return; } if (min_effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN && max_effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN) { min_effective_connection_type_ = type; max_effective_connection_type_ = type; return; } if (min_effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_OFFLINE && max_effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_OFFLINE) { min_effective_connection_type_ = type; max_effective_connection_type_ = type; return; } min_effective_connection_type_ = std::min(min_effective_connection_type_, effective_connection_type_); max_effective_connection_type_ = std::max(max_effective_connection_type_, effective_connection_type_); DCHECK_EQ( min_effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN, max_effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN); DCHECK_EQ( min_effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_OFFLINE, max_effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_OFFLINE); } } // namespace metrics