xref: /aosp_15_r20/external/cronet/net/quic/quic_connectivity_monitor.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2020 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 "net/quic/quic_connectivity_monitor.h"
6 
7 #include "base/metrics/histogram_functions.h"
8 #include "base/metrics/histogram_macros.h"
9 
10 namespace net {
11 
12 namespace {
13 
IsErrorRelatedToConnectivity(int error_code)14 bool IsErrorRelatedToConnectivity(int error_code) {
15   return (error_code == ERR_ADDRESS_UNREACHABLE ||
16           error_code == ERR_ACCESS_DENIED ||
17           error_code == ERR_INTERNET_DISCONNECTED);
18 }
19 
20 }  // namespace
21 
QuicConnectivityMonitor(handles::NetworkHandle default_network)22 QuicConnectivityMonitor::QuicConnectivityMonitor(
23     handles::NetworkHandle default_network)
24     : default_network_(default_network) {}
25 
26 QuicConnectivityMonitor::~QuicConnectivityMonitor() = default;
27 
RecordConnectivityStatsToHistograms(const std::string & notification,handles::NetworkHandle affected_network) const28 void QuicConnectivityMonitor::RecordConnectivityStatsToHistograms(
29     const std::string& notification,
30     handles::NetworkHandle affected_network) const {
31   if (notification == "OnNetworkSoonToDisconnect" ||
32       notification == "OnNetworkDisconnected") {
33     // If the disconnected network is not the default network, ignore
34     // stats collections.
35     if (affected_network != default_network_)
36       return;
37   }
38 
39   base::ClampedNumeric<int> num_degrading_sessions = GetNumDegradingSessions();
40 
41   if (num_sessions_active_during_current_speculative_connectivity_failure_) {
42     UMA_HISTOGRAM_COUNTS_100(
43         "Net.QuicConnectivityMonitor.NumSessionsTrackedSinceSpeculativeError",
44         num_sessions_active_during_current_speculative_connectivity_failure_
45             .value());
46   }
47 
48   UMA_HISTOGRAM_COUNTS_100(
49       "Net.QuicConnectivityMonitor.NumActiveQuicSessionsAtNetworkChange",
50       active_sessions_.size());
51 
52   int percentage = 0;
53   if (num_sessions_active_during_current_speculative_connectivity_failure_ &&
54       num_sessions_active_during_current_speculative_connectivity_failure_
55               .value() > 0) {
56     percentage = base::saturated_cast<int>(
57         num_all_degraded_sessions_ * 100.0 /
58         num_sessions_active_during_current_speculative_connectivity_failure_
59             .value());
60   }
61 
62   UMA_HISTOGRAM_COUNTS_100(
63       "Net.QuicConnectivityMonitor.NumAllSessionsDegradedAtNetworkChange",
64       num_all_degraded_sessions_);
65 
66   const std::string raw_histogram_name1 =
67       "Net.QuicConnectivityMonitor.NumAllDegradedSessions." + notification;
68   base::UmaHistogramCustomCounts(raw_histogram_name1,
69                                  num_all_degraded_sessions_, 1, 100, 50);
70 
71   const std::string percentage_histogram_name1 =
72       "Net.QuicConnectivityMonitor.PercentageAllDegradedSessions." +
73       notification;
74 
75   base::UmaHistogramPercentageObsoleteDoNotUse(percentage_histogram_name1,
76                                                percentage);
77 
78   // Skip degrading session collection if there are less than two sessions.
79   if (active_sessions_.size() < 2u)
80     return;
81 
82   const std::string raw_histogram_name =
83       "Net.QuicConnectivityMonitor.NumActiveDegradingSessions." + notification;
84 
85   base::UmaHistogramCustomCounts(raw_histogram_name, num_degrading_sessions, 1,
86                                  100, 50);
87 
88   percentage = base::saturated_cast<double>(num_degrading_sessions * 100.0 /
89                                             active_sessions_.size());
90 
91   const std::string percentage_histogram_name =
92       "Net.QuicConnectivityMonitor.PercentageActiveDegradingSessions." +
93       notification;
94   base::UmaHistogramPercentageObsoleteDoNotUse(percentage_histogram_name,
95                                                percentage);
96 }
97 
GetNumDegradingSessions() const98 size_t QuicConnectivityMonitor::GetNumDegradingSessions() const {
99   return degrading_sessions_.size();
100 }
101 
GetCountForWriteErrorCode(int write_error_code) const102 size_t QuicConnectivityMonitor::GetCountForWriteErrorCode(
103     int write_error_code) const {
104   auto it = write_error_map_.find(write_error_code);
105   return it == write_error_map_.end() ? 0u : it->second;
106 }
107 
SetInitialDefaultNetwork(handles::NetworkHandle default_network)108 void QuicConnectivityMonitor::SetInitialDefaultNetwork(
109     handles::NetworkHandle default_network) {
110   default_network_ = default_network;
111 }
112 
OnSessionPathDegrading(QuicChromiumClientSession * session,handles::NetworkHandle network)113 void QuicConnectivityMonitor::OnSessionPathDegrading(
114     QuicChromiumClientSession* session,
115     handles::NetworkHandle network) {
116   if (network != default_network_)
117     return;
118 
119   degrading_sessions_.insert(session);
120   num_all_degraded_sessions_++;
121   // If the degrading session used to be on the previous default network, it is
122   // possible that the session is no longer tracked in |active_sessions_| due
123   // to the recent default network change.
124   active_sessions_.insert(session);
125 
126   if (!num_sessions_active_during_current_speculative_connectivity_failure_) {
127     num_sessions_active_during_current_speculative_connectivity_failure_ =
128         active_sessions_.size();
129   } else {
130     // Before seeing session degrading, PACKET_WRITE_ERROR has been observed.
131     UMA_HISTOGRAM_COUNTS_100(
132         "Net.QuicConnectivityMonitor.NumWriteErrorsSeenBeforeDegradation",
133         quic_error_map_[quic::QUIC_PACKET_WRITE_ERROR]);
134   }
135 }
136 
OnSessionResumedPostPathDegrading(QuicChromiumClientSession * session,handles::NetworkHandle network)137 void QuicConnectivityMonitor::OnSessionResumedPostPathDegrading(
138     QuicChromiumClientSession* session,
139     handles::NetworkHandle network) {
140   if (network != default_network_)
141     return;
142 
143   degrading_sessions_.erase(session);
144 
145   // If the resumed session used to be on the previous default network, it is
146   // possible that the session is no longer tracked in |active_sessions_| due
147   // to the recent default network change.
148   active_sessions_.insert(session);
149 
150   num_all_degraded_sessions_ = 0u;
151   num_sessions_active_during_current_speculative_connectivity_failure_ =
152       std::nullopt;
153 }
154 
OnSessionEncounteringWriteError(QuicChromiumClientSession * session,handles::NetworkHandle network,int error_code)155 void QuicConnectivityMonitor::OnSessionEncounteringWriteError(
156     QuicChromiumClientSession* session,
157     handles::NetworkHandle network,
158     int error_code) {
159   if (network != default_network_)
160     return;
161 
162   // If the session used to be on the previous default network, it is
163   // possible that the session is no longer tracked in |active_sessions_| due
164   // to the recent default network change.
165   active_sessions_.insert(session);
166 
167   ++write_error_map_[error_code];
168 
169   bool is_session_degraded =
170       degrading_sessions_.find(session) != degrading_sessions_.end();
171 
172   UMA_HISTOGRAM_BOOLEAN(
173       "Net.QuicConnectivityMonitor.SessionDegradedBeforeWriteError",
174       is_session_degraded);
175 
176   if (!num_sessions_active_during_current_speculative_connectivity_failure_ &&
177       IsErrorRelatedToConnectivity(error_code)) {
178     num_sessions_active_during_current_speculative_connectivity_failure_ =
179         active_sessions_.size();
180   }
181 }
182 
OnSessionClosedAfterHandshake(QuicChromiumClientSession * session,handles::NetworkHandle network,quic::ConnectionCloseSource source,quic::QuicErrorCode error_code)183 void QuicConnectivityMonitor::OnSessionClosedAfterHandshake(
184     QuicChromiumClientSession* session,
185     handles::NetworkHandle network,
186     quic::ConnectionCloseSource source,
187     quic::QuicErrorCode error_code) {
188   if (network != default_network_)
189     return;
190 
191   if (source == quic::ConnectionCloseSource::FROM_PEER) {
192     // Connection closed by the peer post handshake with PUBLIC RESET
193     // is most likely a NAT rebinding issue.
194     if (error_code == quic::QUIC_PUBLIC_RESET)
195       quic_error_map_[error_code]++;
196     return;
197   }
198 
199   if (error_code == quic::QUIC_PACKET_WRITE_ERROR ||
200       error_code == quic::QUIC_TOO_MANY_RTOS) {
201     // Connection close by self with PACKET_WRITE_ERROR or TOO_MANY_RTOS
202     // is likely a connectivity issue.
203     quic_error_map_[error_code]++;
204   }
205 }
206 
OnSessionRegistered(QuicChromiumClientSession * session,handles::NetworkHandle network)207 void QuicConnectivityMonitor::OnSessionRegistered(
208     QuicChromiumClientSession* session,
209     handles::NetworkHandle network) {
210   if (network != default_network_)
211     return;
212 
213   active_sessions_.insert(session);
214   if (num_sessions_active_during_current_speculative_connectivity_failure_) {
215     num_sessions_active_during_current_speculative_connectivity_failure_
216         .value()++;
217   }
218 }
219 
OnSessionRemoved(QuicChromiumClientSession * session)220 void QuicConnectivityMonitor::OnSessionRemoved(
221     QuicChromiumClientSession* session) {
222   degrading_sessions_.erase(session);
223   active_sessions_.erase(session);
224 }
225 
OnDefaultNetworkUpdated(handles::NetworkHandle default_network)226 void QuicConnectivityMonitor::OnDefaultNetworkUpdated(
227     handles::NetworkHandle default_network) {
228   default_network_ = default_network;
229   active_sessions_.clear();
230   degrading_sessions_.clear();
231   num_sessions_active_during_current_speculative_connectivity_failure_ =
232       std::nullopt;
233   write_error_map_.clear();
234   quic_error_map_.clear();
235 }
236 
OnIPAddressChanged()237 void QuicConnectivityMonitor::OnIPAddressChanged() {
238   // If handles::NetworkHandle is supported, connectivity monitor will receive
239   // notifications via OnDefaultNetworkUpdated.
240   if (NetworkChangeNotifier::AreNetworkHandlesSupported())
241     return;
242 
243   DCHECK_EQ(default_network_, handles::kInvalidNetworkHandle);
244   degrading_sessions_.clear();
245   write_error_map_.clear();
246 }
247 
OnSessionGoingAwayOnIPAddressChange(QuicChromiumClientSession * session)248 void QuicConnectivityMonitor::OnSessionGoingAwayOnIPAddressChange(
249     QuicChromiumClientSession* session) {
250   // This should only be called after ConnectivityMonitor gets notified via
251   // OnIPAddressChanged().
252   DCHECK(degrading_sessions_.empty());
253   // |session| that encounters IP address change will lose track which network
254   // it is bound to. Future connectivity monitoring may be misleading.
255   session->RemoveConnectivityObserver(this);
256 }
257 
258 }  // namespace net
259