• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/metrics/net/network_metrics_provider.h"
6 
7 #include <stdint.h>
8 
9 #include <algorithm>
10 #include <string>
11 #include <utility>
12 
13 #include "base/compiler_specific.h"
14 #include "base/functional/bind.h"
15 #include "base/functional/callback_helpers.h"
16 #include "base/metrics/histogram_macros.h"
17 #include "base/metrics/sparse_histogram.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_split.h"
20 #include "base/strings/string_util.h"
21 #include "base/task/thread_pool.h"
22 #include "build/build_config.h"
23 #include "net/base/net_errors.h"
24 #include "net/nqe/effective_connection_type_observer.h"
25 #include "net/nqe/network_quality_estimator.h"
26 
27 #if BUILDFLAG(IS_ANDROID)
28 #include "services/network/public/cpp/network_connection_tracker.h"
29 #endif
30 
31 namespace metrics {
32 
33 SystemProfileProto::Network::EffectiveConnectionType
ConvertEffectiveConnectionType(net::EffectiveConnectionType effective_connection_type)34 ConvertEffectiveConnectionType(
35     net::EffectiveConnectionType effective_connection_type) {
36   switch (effective_connection_type) {
37     case net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN:
38       return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
39     case net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G:
40       return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_SLOW_2G;
41     case net::EFFECTIVE_CONNECTION_TYPE_2G:
42       return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_2G;
43     case net::EFFECTIVE_CONNECTION_TYPE_3G:
44       return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_3G;
45     case net::EFFECTIVE_CONNECTION_TYPE_4G:
46       return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_4G;
47     case net::EFFECTIVE_CONNECTION_TYPE_OFFLINE:
48       return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_OFFLINE;
49     case net::EFFECTIVE_CONNECTION_TYPE_LAST:
50       NOTREACHED();
51   }
52   NOTREACHED();
53 }
54 
NetworkMetricsProvider(network::NetworkConnectionTrackerAsyncGetter network_connection_tracker_async_getter,std::unique_ptr<NetworkQualityEstimatorProvider> network_quality_estimator_provider)55 NetworkMetricsProvider::NetworkMetricsProvider(
56     network::NetworkConnectionTrackerAsyncGetter
57         network_connection_tracker_async_getter,
58     std::unique_ptr<NetworkQualityEstimatorProvider>
59         network_quality_estimator_provider)
60     : network_connection_tracker_(nullptr),
61       connection_type_is_ambiguous_(false),
62       connection_type_(network::mojom::ConnectionType::CONNECTION_UNKNOWN),
63       network_connection_tracker_initialized_(false),
64       network_quality_estimator_provider_(
65           std::move(network_quality_estimator_provider)),
66       effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
67       min_effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
68       max_effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {
69   network_connection_tracker_async_getter.Run(
70       base::BindOnce(&NetworkMetricsProvider::SetNetworkConnectionTracker,
71                      weak_ptr_factory_.GetWeakPtr()));
72 
73   if (network_quality_estimator_provider_) {
74     // Use |network_quality_estimator_provider_| to get network quality
75     // tracker.
76     network_quality_estimator_provider_->PostReplyOnNetworkQualityChanged(
77         base::BindRepeating(
78             &NetworkMetricsProvider::OnEffectiveConnectionTypeChanged,
79             weak_ptr_factory_.GetWeakPtr()));
80   }
81 }
82 
~NetworkMetricsProvider()83 NetworkMetricsProvider::~NetworkMetricsProvider() {
84   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
85   if (network_connection_tracker_)
86     network_connection_tracker_->RemoveNetworkConnectionObserver(this);
87 }
88 
SetNetworkConnectionTracker(network::NetworkConnectionTracker * network_connection_tracker)89 void NetworkMetricsProvider::SetNetworkConnectionTracker(
90     network::NetworkConnectionTracker* network_connection_tracker) {
91   DCHECK(network_connection_tracker);
92   network_connection_tracker_ = network_connection_tracker;
93   network_connection_tracker_->AddNetworkConnectionObserver(this);
94   network_connection_tracker_->GetConnectionType(
95       &connection_type_,
96       base::BindOnce(&NetworkMetricsProvider::OnConnectionChanged,
97                      weak_ptr_factory_.GetWeakPtr()));
98   if (connection_type_ != network::mojom::ConnectionType::CONNECTION_UNKNOWN)
99     network_connection_tracker_initialized_ = true;
100 }
101 
ProvideSystemProfileMetrics(SystemProfileProto * system_profile)102 void NetworkMetricsProvider::ProvideSystemProfileMetrics(
103     SystemProfileProto* system_profile) {
104   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
105   DCHECK(!connection_type_is_ambiguous_ ||
106          network_connection_tracker_initialized_);
107   SystemProfileProto::Network* network = system_profile->mutable_network();
108   network->set_connection_type_is_ambiguous(connection_type_is_ambiguous_);
109   network->set_connection_type(GetConnectionType());
110 
111   network->set_min_effective_connection_type(
112       ConvertEffectiveConnectionType(min_effective_connection_type_));
113   network->set_max_effective_connection_type(
114       ConvertEffectiveConnectionType(max_effective_connection_type_));
115 
116   // Note: We get the initial connection type when it becomes available and it
117   // is handled at SetNetworkConnectionTracker() when GetConnectionType() is
118   // called.
119   //
120   // Update the connection type. Note that this is necessary to set the network
121   // type to "none" if there is no network connection for an entire UMA logging
122   // window, since OnConnectionTypeChanged() ignores transitions to the "none"
123   // state, and that is ok since it just deals with the current known state.
124   if (network_connection_tracker_) {
125     network_connection_tracker_->GetConnectionType(&connection_type_,
126                                                    base::DoNothing());
127   }
128 
129   if (connection_type_ != network::mojom::ConnectionType::CONNECTION_UNKNOWN)
130     network_connection_tracker_initialized_ = true;
131   // Reset the "ambiguous" flags, since a new metrics log session has started.
132   connection_type_is_ambiguous_ = false;
133   min_effective_connection_type_ = effective_connection_type_;
134   max_effective_connection_type_ = effective_connection_type_;
135 }
136 
OnConnectionChanged(network::mojom::ConnectionType type)137 void NetworkMetricsProvider::OnConnectionChanged(
138     network::mojom::ConnectionType type) {
139   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
140   // To avoid reporting an ambiguous connection type for users on flaky
141   // connections, ignore transitions to the "none" state. Note that the
142   // connection type is refreshed in ProvideSystemProfileMetrics() each time a
143   // new UMA logging window begins, so users who genuinely transition to offline
144   // mode for an extended duration will still be at least partially represented
145   // in the metrics logs.
146   if (type == network::mojom::ConnectionType::CONNECTION_NONE) {
147     network_connection_tracker_initialized_ = true;
148     return;
149   }
150 
151   DCHECK(network_connection_tracker_initialized_ ||
152          connection_type_ ==
153              network::mojom::ConnectionType::CONNECTION_UNKNOWN);
154 
155   if (type != connection_type_ &&
156       connection_type_ != network::mojom::ConnectionType::CONNECTION_NONE &&
157       network_connection_tracker_initialized_) {
158     // If |network_connection_tracker_initialized_| is false, it implies that
159     // this is the first connection change callback received from network
160     // connection tracker, and the previous connection type was
161     // CONNECTION_UNKNOWN. In that case, connection type should not be marked as
162     // ambiguous since there was no actual change in the connection type.
163     connection_type_is_ambiguous_ = true;
164   }
165 
166   network_connection_tracker_initialized_ = true;
167   connection_type_ = type;
168 }
169 
170 SystemProfileProto::Network::ConnectionType
GetConnectionType() const171 NetworkMetricsProvider::GetConnectionType() const {
172   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
173   switch (connection_type_) {
174     case network::mojom::ConnectionType::CONNECTION_NONE:
175       return SystemProfileProto::Network::CONNECTION_NONE;
176     case network::mojom::ConnectionType::CONNECTION_UNKNOWN:
177       return SystemProfileProto::Network::CONNECTION_UNKNOWN;
178     case network::mojom::ConnectionType::CONNECTION_ETHERNET:
179       return SystemProfileProto::Network::CONNECTION_ETHERNET;
180     case network::mojom::ConnectionType::CONNECTION_WIFI:
181       return SystemProfileProto::Network::CONNECTION_WIFI;
182     case network::mojom::ConnectionType::CONNECTION_2G:
183       return SystemProfileProto::Network::CONNECTION_2G;
184     case network::mojom::ConnectionType::CONNECTION_3G:
185       return SystemProfileProto::Network::CONNECTION_3G;
186     case network::mojom::ConnectionType::CONNECTION_4G:
187       return SystemProfileProto::Network::CONNECTION_4G;
188     case network::mojom::ConnectionType::CONNECTION_5G:
189       return SystemProfileProto::Network::CONNECTION_5G;
190     case network::mojom::ConnectionType::CONNECTION_BLUETOOTH:
191       return SystemProfileProto::Network::CONNECTION_BLUETOOTH;
192   }
193   NOTREACHED();
194 }
195 
OnEffectiveConnectionTypeChanged(net::EffectiveConnectionType type)196 void NetworkMetricsProvider::OnEffectiveConnectionTypeChanged(
197     net::EffectiveConnectionType type) {
198   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
199   effective_connection_type_ = type;
200 
201   if (effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN ||
202       effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_OFFLINE) {
203     // The effective connection type may be reported as Unknown if there is a
204     // change in the connection type. Disregard it since network requests can't
205     // be send during the changes in connection type. Similarly, disregard
206     // offline as the type since it may be reported as the effective connection
207     // type for a short period when there is a change in the connection type.
208     return;
209   }
210 
211   if (min_effective_connection_type_ ==
212           net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN &&
213       max_effective_connection_type_ ==
214           net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {
215     min_effective_connection_type_ = type;
216     max_effective_connection_type_ = type;
217     return;
218   }
219 
220   if (min_effective_connection_type_ ==
221           net::EFFECTIVE_CONNECTION_TYPE_OFFLINE &&
222       max_effective_connection_type_ ==
223           net::EFFECTIVE_CONNECTION_TYPE_OFFLINE) {
224     min_effective_connection_type_ = type;
225     max_effective_connection_type_ = type;
226     return;
227   }
228 
229   min_effective_connection_type_ =
230       std::min(min_effective_connection_type_, effective_connection_type_);
231   max_effective_connection_type_ =
232       std::max(max_effective_connection_type_, effective_connection_type_);
233 
234   DCHECK_EQ(
235       min_effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
236       max_effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN);
237   DCHECK_EQ(
238       min_effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_OFFLINE,
239       max_effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_OFFLINE);
240 }
241 
242 }  // namespace metrics
243