blob: 41dc47931a947bfb05c541e74bdadbff779ed5c6 [file] [log] [blame]
// 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 <stdint.h>
#include <algorithm>
#include <string>
#include <utility>
#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<NetworkQualityEstimatorProvider>
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