123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- // Copyright 2014 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 "components/metrics/net/network_metrics_provider.h"
- #include <stdint.h>
- #include <algorithm>
- #include <string>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/compiler_specific.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 "base/threading/thread_task_runner_handle.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();
- return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
- }
- NOTREACHED();
- return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
- }
- 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),
- wifi_phy_layer_protocol_is_ambiguous_(false),
- wifi_phy_layer_protocol_(net::WIFI_PHY_LAYER_PROTOCOL_UNKNOWN),
- total_aborts_(0),
- total_codes_(0),
- 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()));
- ProbeWifiPHYLayerProtocol();
- 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::FinalizingMetricsLogRecord() {
- #if BUILDFLAG(IS_ANDROID)
- // Metrics logged here will be included in every metrics log record. It's not
- // yet clear if these metrics are generally useful enough to warrant being
- // added to the SystemProfile proto, so they are logged here as histograms for
- // now.
- if (network::NetworkConnectionTracker::IsConnectionCellular(
- connection_type_)) {
- UMA_HISTOGRAM_ENUMERATION(
- "NCN.CellularConnectionSubtype",
- net::NetworkChangeNotifier::GetConnectionSubtype(),
- net::NetworkChangeNotifier::ConnectionSubtype::SUBTYPE_LAST + 1);
- }
- #endif
- }
- void NetworkMetricsProvider::ProvideCurrentSessionData(
- ChromeUserMetricsExtension*) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // ProvideCurrentSessionData is called on the main thread, at the time a
- // metrics record is being finalized.
- FinalizingMetricsLogRecord();
- LogAggregatedMetrics();
- }
- 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_wifi_phy_layer_protocol_is_ambiguous(
- wifi_phy_layer_protocol_is_ambiguous_);
- network->set_wifi_phy_layer_protocol(GetWifiPHYLayerProtocol());
- 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;
- wifi_phy_layer_protocol_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;
- ProbeWifiPHYLayerProtocol();
- }
- 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();
- return SystemProfileProto::Network::CONNECTION_UNKNOWN;
- }
- SystemProfileProto::Network::WifiPHYLayerProtocol
- NetworkMetricsProvider::GetWifiPHYLayerProtocol() const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- switch (wifi_phy_layer_protocol_) {
- case net::WIFI_PHY_LAYER_PROTOCOL_NONE:
- return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_NONE;
- case net::WIFI_PHY_LAYER_PROTOCOL_ANCIENT:
- return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_ANCIENT;
- case net::WIFI_PHY_LAYER_PROTOCOL_A:
- return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_A;
- case net::WIFI_PHY_LAYER_PROTOCOL_B:
- return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_B;
- case net::WIFI_PHY_LAYER_PROTOCOL_G:
- return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_G;
- case net::WIFI_PHY_LAYER_PROTOCOL_N:
- return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_N;
- case net::WIFI_PHY_LAYER_PROTOCOL_AC:
- return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_AC;
- case net::WIFI_PHY_LAYER_PROTOCOL_AD:
- return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_AD;
- case net::WIFI_PHY_LAYER_PROTOCOL_AX:
- return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_AX;
- case net::WIFI_PHY_LAYER_PROTOCOL_UNKNOWN:
- return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_UNKNOWN;
- }
- NOTREACHED();
- return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_UNKNOWN;
- }
- void NetworkMetricsProvider::ProbeWifiPHYLayerProtocol() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- base::ThreadPool::PostTaskAndReplyWithResult(
- FROM_HERE,
- {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
- base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
- base::BindOnce(&net::GetWifiPHYLayerProtocol),
- base::BindOnce(&NetworkMetricsProvider::OnWifiPHYLayerProtocolResult,
- weak_ptr_factory_.GetWeakPtr()));
- }
- void NetworkMetricsProvider::OnWifiPHYLayerProtocolResult(
- net::WifiPHYLayerProtocol mode) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (wifi_phy_layer_protocol_ != net::WIFI_PHY_LAYER_PROTOCOL_UNKNOWN &&
- mode != wifi_phy_layer_protocol_) {
- wifi_phy_layer_protocol_is_ambiguous_ = true;
- }
- wifi_phy_layer_protocol_ = mode;
- }
- void NetworkMetricsProvider::LogAggregatedMetrics() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- base::HistogramBase* error_codes = base::SparseHistogram::FactoryGet(
- "Net.ErrorCodesForMainFrame4",
- base::HistogramBase::kUmaTargetedHistogramFlag);
- std::unique_ptr<base::HistogramSamples> samples =
- error_codes->SnapshotSamples();
- base::HistogramBase::Count new_aborts =
- samples->GetCount(-net::ERR_ABORTED) - total_aborts_;
- base::HistogramBase::Count new_codes = samples->TotalCount() - total_codes_;
- if (new_codes > 0) {
- UMA_HISTOGRAM_CUSTOM_COUNTS("Net.ErrAborted.CountPerUpload2", new_aborts, 1,
- 100000000, 50);
- UMA_HISTOGRAM_PERCENTAGE("Net.ErrAborted.ProportionPerUpload",
- (100 * new_aborts) / new_codes);
- total_codes_ += new_codes;
- total_aborts_ += new_aborts;
- }
- }
- 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
|