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 return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
52 }
53 NOTREACHED();
54 return SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
55 }
56
NetworkMetricsProvider(network::NetworkConnectionTrackerAsyncGetter network_connection_tracker_async_getter,std::unique_ptr<NetworkQualityEstimatorProvider> network_quality_estimator_provider)57 NetworkMetricsProvider::NetworkMetricsProvider(
58 network::NetworkConnectionTrackerAsyncGetter
59 network_connection_tracker_async_getter,
60 std::unique_ptr<NetworkQualityEstimatorProvider>
61 network_quality_estimator_provider)
62 : network_connection_tracker_(nullptr),
63 connection_type_is_ambiguous_(false),
64 connection_type_(network::mojom::ConnectionType::CONNECTION_UNKNOWN),
65 network_connection_tracker_initialized_(false),
66 wifi_phy_layer_protocol_is_ambiguous_(false),
67 wifi_phy_layer_protocol_(net::WIFI_PHY_LAYER_PROTOCOL_UNKNOWN),
68 network_quality_estimator_provider_(
69 std::move(network_quality_estimator_provider)),
70 effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
71 min_effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
72 max_effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {
73 network_connection_tracker_async_getter.Run(
74 base::BindOnce(&NetworkMetricsProvider::SetNetworkConnectionTracker,
75 weak_ptr_factory_.GetWeakPtr()));
76 ProbeWifiPHYLayerProtocol();
77
78 if (network_quality_estimator_provider_) {
79 // Use |network_quality_estimator_provider_| to get network quality
80 // tracker.
81 network_quality_estimator_provider_->PostReplyOnNetworkQualityChanged(
82 base::BindRepeating(
83 &NetworkMetricsProvider::OnEffectiveConnectionTypeChanged,
84 weak_ptr_factory_.GetWeakPtr()));
85 }
86 }
87
~NetworkMetricsProvider()88 NetworkMetricsProvider::~NetworkMetricsProvider() {
89 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
90 if (network_connection_tracker_)
91 network_connection_tracker_->RemoveNetworkConnectionObserver(this);
92 }
93
SetNetworkConnectionTracker(network::NetworkConnectionTracker * network_connection_tracker)94 void NetworkMetricsProvider::SetNetworkConnectionTracker(
95 network::NetworkConnectionTracker* network_connection_tracker) {
96 DCHECK(network_connection_tracker);
97 network_connection_tracker_ = network_connection_tracker;
98 network_connection_tracker_->AddNetworkConnectionObserver(this);
99 network_connection_tracker_->GetConnectionType(
100 &connection_type_,
101 base::BindOnce(&NetworkMetricsProvider::OnConnectionChanged,
102 weak_ptr_factory_.GetWeakPtr()));
103 if (connection_type_ != network::mojom::ConnectionType::CONNECTION_UNKNOWN)
104 network_connection_tracker_initialized_ = true;
105 }
106
ProvideSystemProfileMetrics(SystemProfileProto * system_profile)107 void NetworkMetricsProvider::ProvideSystemProfileMetrics(
108 SystemProfileProto* system_profile) {
109 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
110 DCHECK(!connection_type_is_ambiguous_ ||
111 network_connection_tracker_initialized_);
112 SystemProfileProto::Network* network = system_profile->mutable_network();
113 network->set_connection_type_is_ambiguous(connection_type_is_ambiguous_);
114 network->set_connection_type(GetConnectionType());
115 network->set_wifi_phy_layer_protocol_is_ambiguous(
116 wifi_phy_layer_protocol_is_ambiguous_);
117 network->set_wifi_phy_layer_protocol(GetWifiPHYLayerProtocol());
118
119 network->set_min_effective_connection_type(
120 ConvertEffectiveConnectionType(min_effective_connection_type_));
121 network->set_max_effective_connection_type(
122 ConvertEffectiveConnectionType(max_effective_connection_type_));
123
124 // Note: We get the initial connection type when it becomes available and it
125 // is handled at SetNetworkConnectionTracker() when GetConnectionType() is
126 // called.
127 //
128 // Update the connection type. Note that this is necessary to set the network
129 // type to "none" if there is no network connection for an entire UMA logging
130 // window, since OnConnectionTypeChanged() ignores transitions to the "none"
131 // state, and that is ok since it just deals with the current known state.
132 if (network_connection_tracker_) {
133 network_connection_tracker_->GetConnectionType(&connection_type_,
134 base::DoNothing());
135 }
136
137 if (connection_type_ != network::mojom::ConnectionType::CONNECTION_UNKNOWN)
138 network_connection_tracker_initialized_ = true;
139 // Reset the "ambiguous" flags, since a new metrics log session has started.
140 connection_type_is_ambiguous_ = false;
141 wifi_phy_layer_protocol_is_ambiguous_ = false;
142 min_effective_connection_type_ = effective_connection_type_;
143 max_effective_connection_type_ = effective_connection_type_;
144 }
145
OnConnectionChanged(network::mojom::ConnectionType type)146 void NetworkMetricsProvider::OnConnectionChanged(
147 network::mojom::ConnectionType type) {
148 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
149 // To avoid reporting an ambiguous connection type for users on flaky
150 // connections, ignore transitions to the "none" state. Note that the
151 // connection type is refreshed in ProvideSystemProfileMetrics() each time a
152 // new UMA logging window begins, so users who genuinely transition to offline
153 // mode for an extended duration will still be at least partially represented
154 // in the metrics logs.
155 if (type == network::mojom::ConnectionType::CONNECTION_NONE) {
156 network_connection_tracker_initialized_ = true;
157 return;
158 }
159
160 DCHECK(network_connection_tracker_initialized_ ||
161 connection_type_ ==
162 network::mojom::ConnectionType::CONNECTION_UNKNOWN);
163
164 if (type != connection_type_ &&
165 connection_type_ != network::mojom::ConnectionType::CONNECTION_NONE &&
166 network_connection_tracker_initialized_) {
167 // If |network_connection_tracker_initialized_| is false, it implies that
168 // this is the first connection change callback received from network
169 // connection tracker, and the previous connection type was
170 // CONNECTION_UNKNOWN. In that case, connection type should not be marked as
171 // ambiguous since there was no actual change in the connection type.
172 connection_type_is_ambiguous_ = true;
173 }
174
175 network_connection_tracker_initialized_ = true;
176 connection_type_ = type;
177
178 ProbeWifiPHYLayerProtocol();
179 }
180
181 SystemProfileProto::Network::ConnectionType
GetConnectionType() const182 NetworkMetricsProvider::GetConnectionType() const {
183 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
184 switch (connection_type_) {
185 case network::mojom::ConnectionType::CONNECTION_NONE:
186 return SystemProfileProto::Network::CONNECTION_NONE;
187 case network::mojom::ConnectionType::CONNECTION_UNKNOWN:
188 return SystemProfileProto::Network::CONNECTION_UNKNOWN;
189 case network::mojom::ConnectionType::CONNECTION_ETHERNET:
190 return SystemProfileProto::Network::CONNECTION_ETHERNET;
191 case network::mojom::ConnectionType::CONNECTION_WIFI:
192 return SystemProfileProto::Network::CONNECTION_WIFI;
193 case network::mojom::ConnectionType::CONNECTION_2G:
194 return SystemProfileProto::Network::CONNECTION_2G;
195 case network::mojom::ConnectionType::CONNECTION_3G:
196 return SystemProfileProto::Network::CONNECTION_3G;
197 case network::mojom::ConnectionType::CONNECTION_4G:
198 return SystemProfileProto::Network::CONNECTION_4G;
199 case network::mojom::ConnectionType::CONNECTION_5G:
200 return SystemProfileProto::Network::CONNECTION_5G;
201 case network::mojom::ConnectionType::CONNECTION_BLUETOOTH:
202 return SystemProfileProto::Network::CONNECTION_BLUETOOTH;
203 }
204 NOTREACHED();
205 return SystemProfileProto::Network::CONNECTION_UNKNOWN;
206 }
207
208 SystemProfileProto::Network::WifiPHYLayerProtocol
GetWifiPHYLayerProtocol() const209 NetworkMetricsProvider::GetWifiPHYLayerProtocol() const {
210 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
211 switch (wifi_phy_layer_protocol_) {
212 case net::WIFI_PHY_LAYER_PROTOCOL_NONE:
213 return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_NONE;
214 case net::WIFI_PHY_LAYER_PROTOCOL_ANCIENT:
215 return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_ANCIENT;
216 case net::WIFI_PHY_LAYER_PROTOCOL_A:
217 return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_A;
218 case net::WIFI_PHY_LAYER_PROTOCOL_B:
219 return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_B;
220 case net::WIFI_PHY_LAYER_PROTOCOL_G:
221 return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_G;
222 case net::WIFI_PHY_LAYER_PROTOCOL_N:
223 return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_N;
224 case net::WIFI_PHY_LAYER_PROTOCOL_AC:
225 return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_AC;
226 case net::WIFI_PHY_LAYER_PROTOCOL_AD:
227 return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_AD;
228 case net::WIFI_PHY_LAYER_PROTOCOL_AX:
229 return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_AX;
230 case net::WIFI_PHY_LAYER_PROTOCOL_UNKNOWN:
231 return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_UNKNOWN;
232 }
233 NOTREACHED();
234 return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_UNKNOWN;
235 }
236
ProbeWifiPHYLayerProtocol()237 void NetworkMetricsProvider::ProbeWifiPHYLayerProtocol() {
238 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
239 base::ThreadPool::PostTaskAndReplyWithResult(
240 FROM_HERE,
241 {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
242 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
243 base::BindOnce(&net::GetWifiPHYLayerProtocol),
244 base::BindOnce(&NetworkMetricsProvider::OnWifiPHYLayerProtocolResult,
245 weak_ptr_factory_.GetWeakPtr()));
246 }
247
OnWifiPHYLayerProtocolResult(net::WifiPHYLayerProtocol mode)248 void NetworkMetricsProvider::OnWifiPHYLayerProtocolResult(
249 net::WifiPHYLayerProtocol mode) {
250 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
251 if (wifi_phy_layer_protocol_ != net::WIFI_PHY_LAYER_PROTOCOL_UNKNOWN &&
252 mode != wifi_phy_layer_protocol_) {
253 wifi_phy_layer_protocol_is_ambiguous_ = true;
254 }
255 wifi_phy_layer_protocol_ = mode;
256 }
257
OnEffectiveConnectionTypeChanged(net::EffectiveConnectionType type)258 void NetworkMetricsProvider::OnEffectiveConnectionTypeChanged(
259 net::EffectiveConnectionType type) {
260 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
261 effective_connection_type_ = type;
262
263 if (effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN ||
264 effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_OFFLINE) {
265 // The effective connection type may be reported as Unknown if there is a
266 // change in the connection type. Disregard it since network requests can't
267 // be send during the changes in connection type. Similarly, disregard
268 // offline as the type since it may be reported as the effective connection
269 // type for a short period when there is a change in the connection type.
270 return;
271 }
272
273 if (min_effective_connection_type_ ==
274 net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN &&
275 max_effective_connection_type_ ==
276 net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {
277 min_effective_connection_type_ = type;
278 max_effective_connection_type_ = type;
279 return;
280 }
281
282 if (min_effective_connection_type_ ==
283 net::EFFECTIVE_CONNECTION_TYPE_OFFLINE &&
284 max_effective_connection_type_ ==
285 net::EFFECTIVE_CONNECTION_TYPE_OFFLINE) {
286 min_effective_connection_type_ = type;
287 max_effective_connection_type_ = type;
288 return;
289 }
290
291 min_effective_connection_type_ =
292 std::min(min_effective_connection_type_, effective_connection_type_);
293 max_effective_connection_type_ =
294 std::max(max_effective_connection_type_, effective_connection_type_);
295
296 DCHECK_EQ(
297 min_effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
298 max_effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN);
299 DCHECK_EQ(
300 min_effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_OFFLINE,
301 max_effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_OFFLINE);
302 }
303
304 } // namespace metrics
305