xref: /aosp_15_r20/external/cronet/components/metrics/net/network_metrics_provider.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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