1 // Copyright 2015 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/nqe/network_quality_estimator.h"
6
7 #include <algorithm>
8 #include <cmath>
9 #include <limits>
10 #include <memory>
11 #include <utility>
12
13 #include "base/check_op.h"
14 #include "base/functional/bind.h"
15 #include "base/functional/callback_helpers.h"
16 #include "base/location.h"
17 #include "base/metrics/field_trial_params.h"
18 #include "base/metrics/histogram.h"
19 #include "base/metrics/histogram_base.h"
20 #include "base/metrics/histogram_functions.h"
21 #include "base/metrics/histogram_macros.h"
22 #include "base/metrics/histogram_macros_local.h"
23 #include "base/notreached.h"
24 #include "base/observer_list.h"
25 #include "base/strings/string_number_conversions.h"
26 #include "base/strings/string_piece.h"
27 #include "base/task/lazy_thread_pool_task_runner.h"
28 #include "base/task/single_thread_task_runner.h"
29 #include "base/time/default_tick_clock.h"
30 #include "build/build_config.h"
31 #include "build/chromeos_buildflags.h"
32 #include "net/base/features.h"
33 #include "net/base/host_port_pair.h"
34 #include "net/base/load_flags.h"
35 #include "net/base/load_timing_info.h"
36 #include "net/base/network_interfaces.h"
37 #include "net/base/trace_constants.h"
38 #include "net/base/tracing.h"
39 #include "net/http/http_response_headers.h"
40 #include "net/http/http_response_info.h"
41 #include "net/http/http_status_code.h"
42 #include "net/nqe/network_quality_estimator_util.h"
43 #include "net/nqe/throughput_analyzer.h"
44 #include "net/nqe/weighted_observation.h"
45 #include "net/url_request/url_request.h"
46 #include "net/url_request/url_request_context.h"
47 #include "url/gurl.h"
48
49 namespace net {
50
51 namespace {
52
53 #if BUILDFLAG(IS_CHROMEOS_ASH)
54 // SequencedTaskRunner to get the network id. A SequencedTaskRunner is used
55 // rather than parallel tasks to avoid having many threads getting the network
56 // id concurrently.
57 base::LazyThreadPoolSequencedTaskRunner g_get_network_id_task_runner =
58 LAZY_THREAD_POOL_SEQUENCED_TASK_RUNNER_INITIALIZER(
59 base::TaskTraits(base::MayBlock(),
60 base::TaskPriority::BEST_EFFORT,
61 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN));
62 #endif
63
ProtocolSourceToObservationSource(SocketPerformanceWatcherFactory::Protocol protocol)64 NetworkQualityObservationSource ProtocolSourceToObservationSource(
65 SocketPerformanceWatcherFactory::Protocol protocol) {
66 switch (protocol) {
67 case SocketPerformanceWatcherFactory::PROTOCOL_TCP:
68 return NETWORK_QUALITY_OBSERVATION_SOURCE_TCP;
69 case SocketPerformanceWatcherFactory::PROTOCOL_QUIC:
70 return NETWORK_QUALITY_OBSERVATION_SOURCE_QUIC;
71 }
72 NOTREACHED();
73 return NETWORK_QUALITY_OBSERVATION_SOURCE_TCP;
74 }
75
76 // Returns true if the scheme of the |request| is either HTTP or HTTPS.
RequestSchemeIsHTTPOrHTTPS(const URLRequest & request)77 bool RequestSchemeIsHTTPOrHTTPS(const URLRequest& request) {
78 return request.url().is_valid() && request.url().SchemeIsHTTPOrHTTPS();
79 }
80
DoGetCurrentNetworkID(NetworkQualityEstimatorParams * params)81 nqe::internal::NetworkID DoGetCurrentNetworkID(
82 NetworkQualityEstimatorParams* params) {
83 nqe::internal::NetworkID network_id(
84 NetworkChangeNotifier::GetConnectionType(), std::string(), INT32_MIN);
85
86 return network_id;
87 }
88
89 } // namespace
90
NetworkQualityEstimator(std::unique_ptr<NetworkQualityEstimatorParams> params,NetLog * net_log)91 NetworkQualityEstimator::NetworkQualityEstimator(
92 std::unique_ptr<NetworkQualityEstimatorParams> params,
93 NetLog* net_log)
94 : params_(std::move(params)),
95 tick_clock_(base::DefaultTickClock::GetInstance()),
96 last_connection_change_(tick_clock_->NowTicks()),
97 current_network_id_(nqe::internal::NetworkID(
98 NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN,
99 std::string(),
100 INT32_MIN)),
101 http_downstream_throughput_kbps_observations_(
102 params_.get(),
103 tick_clock_,
104 params_->weight_multiplier_per_second(),
105 1.0 /*params_->weight_multiplier_per_signal_strength_level()*/),
106 rtt_ms_observations_{
107 ObservationBuffer(
108 params_.get(),
109 tick_clock_,
110 params_->weight_multiplier_per_second(),
111 1.0 /*params_->weight_multiplier_per_signal_strength_level()*/),
112 ObservationBuffer(
113 params_.get(),
114 tick_clock_,
115 params_->weight_multiplier_per_second(),
116 1.0 /*params_->weight_multiplier_per_signal_strength_level()*/),
117 ObservationBuffer(
118 params_.get(),
119 tick_clock_,
120 params_->weight_multiplier_per_second(),
121 1.0 /*params_->weight_multiplier_per_signal_strength_level()*/)},
122 net_log_(NetLogWithSource::Make(
123 net_log,
124 net::NetLogSourceType::NETWORK_QUALITY_ESTIMATOR)),
125 event_creator_(net_log_) {
126 DCHECK_EQ(nqe::internal::OBSERVATION_CATEGORY_COUNT,
127 std::size(rtt_ms_observations_));
128
129 network_quality_store_ =
130 std::make_unique<nqe::internal::NetworkQualityStore>();
131 NetworkChangeNotifier::AddConnectionTypeObserver(this);
132 throughput_analyzer_ = std::make_unique<nqe::internal::ThroughputAnalyzer>(
133 this, params_.get(), base::SingleThreadTaskRunner::GetCurrentDefault(),
134 base::BindRepeating(
135 &NetworkQualityEstimator::OnNewThroughputObservationAvailable,
136 weak_ptr_factory_.GetWeakPtr()),
137 tick_clock_, net_log_);
138
139 watcher_factory_ = std::make_unique<nqe::internal::SocketWatcherFactory>(
140 base::SingleThreadTaskRunner::GetCurrentDefault(),
141 params_->min_socket_watcher_notification_interval(),
142 // OnUpdatedTransportRTTAvailable() may be called via PostTask() by
143 // socket watchers that live on a different thread than the current thread
144 // (i.e., base::SingleThreadTaskRunner::GetCurrentDefault()).
145 // Use WeakPtr() to avoid crashes where the socket watcher is destroyed
146 // after |this| is destroyed.
147 base::BindRepeating(
148 &NetworkQualityEstimator::OnUpdatedTransportRTTAvailable,
149 weak_ptr_factory_.GetWeakPtr()),
150 // ShouldSocketWatcherNotifyRTT() below is called by only the socket
151 // watchers that live on the same thread as the current thread
152 // (i.e., base::SingleThreadTaskRunner::GetCurrentDefault()). Also,
153 // network quality estimator is destroyed after network contexts and
154 // URLRequestContexts. It's safe to use base::Unretained() below since the
155 // socket watcher (owned by sockets) would be destroyed before |this|.
156 base::BindRepeating(
157 &NetworkQualityEstimator::ShouldSocketWatcherNotifyRTT,
158 base::Unretained(this)),
159 tick_clock_);
160
161 GatherEstimatesForNextConnectionType();
162 }
163
AddDefaultEstimates()164 void NetworkQualityEstimator::AddDefaultEstimates() {
165 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
166
167 if (!params_->add_default_platform_observations())
168 return;
169
170 if (params_->DefaultObservation(current_network_id_.type).http_rtt() !=
171 nqe::internal::InvalidRTT()) {
172 Observation rtt_observation(
173 params_->DefaultObservation(current_network_id_.type)
174 .http_rtt()
175 .InMilliseconds(),
176 tick_clock_->NowTicks(), INT32_MIN,
177 NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_HTTP_FROM_PLATFORM);
178 AddAndNotifyObserversOfRTT(rtt_observation);
179 }
180
181 if (params_->DefaultObservation(current_network_id_.type).transport_rtt() !=
182 nqe::internal::InvalidRTT()) {
183 Observation rtt_observation(
184 params_->DefaultObservation(current_network_id_.type)
185 .transport_rtt()
186 .InMilliseconds(),
187 tick_clock_->NowTicks(), INT32_MIN,
188 NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_TRANSPORT_FROM_PLATFORM);
189 AddAndNotifyObserversOfRTT(rtt_observation);
190 }
191
192 if (params_->DefaultObservation(current_network_id_.type)
193 .downstream_throughput_kbps() !=
194 nqe::internal::INVALID_RTT_THROUGHPUT) {
195 Observation throughput_observation(
196 params_->DefaultObservation(current_network_id_.type)
197 .downstream_throughput_kbps(),
198 tick_clock_->NowTicks(), INT32_MIN,
199 NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_HTTP_FROM_PLATFORM);
200 AddAndNotifyObserversOfThroughput(throughput_observation);
201 }
202 }
203
~NetworkQualityEstimator()204 NetworkQualityEstimator::~NetworkQualityEstimator() {
205 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
206 NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
207 }
208
NotifyStartTransaction(const URLRequest & request)209 void NetworkQualityEstimator::NotifyStartTransaction(
210 const URLRequest& request) {
211 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
212
213 if (!RequestSchemeIsHTTPOrHTTPS(request))
214 return;
215
216 // TODO(tbansal): Refactor this to a separate method.
217 if (request.load_flags() & LOAD_MAIN_FRAME_DEPRECATED) {
218 ComputeEffectiveConnectionType();
219 } else {
220 MaybeComputeEffectiveConnectionType();
221 }
222 throughput_analyzer_->NotifyStartTransaction(request);
223 }
224
IsHangingRequest(base::TimeDelta observed_http_rtt) const225 bool NetworkQualityEstimator::IsHangingRequest(
226 base::TimeDelta observed_http_rtt) const {
227 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
228
229 // If there are sufficient number of end to end RTT samples available, use
230 // the end to end RTT estimate to determine if the request is hanging.
231 // If |observed_http_rtt| is within a fixed multiplier of |end_to_end_rtt_|,
232 // then |observed_http_rtt| is determined to be not a hanging-request RTT.
233 if (params_->use_end_to_end_rtt() && end_to_end_rtt_.has_value() &&
234 end_to_end_rtt_observation_count_at_last_ect_computation_ >=
235 params_->http_rtt_transport_rtt_min_count() &&
236 params_->hanging_request_http_rtt_upper_bound_transport_rtt_multiplier() >
237 0 &&
238 observed_http_rtt <
239 params_->hanging_request_http_rtt_upper_bound_transport_rtt_multiplier() *
240 end_to_end_rtt_.value()) {
241 return false;
242 }
243
244 DCHECK_LT(
245 0,
246 params_->hanging_request_http_rtt_upper_bound_transport_rtt_multiplier());
247
248 if (transport_rtt_observation_count_last_ect_computation_ >=
249 params_->http_rtt_transport_rtt_min_count() &&
250 (observed_http_rtt <
251 params_->hanging_request_http_rtt_upper_bound_transport_rtt_multiplier() *
252 GetTransportRTT().value_or(base::Seconds(10)))) {
253 // If there are sufficient number of transport RTT samples available, use
254 // the transport RTT estimate to determine if the request is hanging.
255 return false;
256 }
257
258 DCHECK_LT(
259 0, params_->hanging_request_http_rtt_upper_bound_http_rtt_multiplier());
260
261 if (observed_http_rtt <
262 params_->hanging_request_http_rtt_upper_bound_http_rtt_multiplier() *
263 GetHttpRTT().value_or(base::Seconds(10))) {
264 // Use the HTTP RTT estimate to determine if the request is hanging.
265 return false;
266 }
267
268 if (observed_http_rtt <=
269 params_->hanging_request_upper_bound_min_http_rtt()) {
270 return false;
271 }
272 return true;
273 }
274
NotifyHeadersReceived(const URLRequest & request,int64_t prefilter_total_bytes_read)275 void NetworkQualityEstimator::NotifyHeadersReceived(
276 const URLRequest& request,
277 int64_t prefilter_total_bytes_read) {
278 TRACE_EVENT0(NetTracingCategory(),
279 "NetworkQualityEstimator::NotifyHeadersReceived");
280 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
281
282 if (!RequestSchemeIsHTTPOrHTTPS(request) ||
283 !RequestProvidesRTTObservation(request)) {
284 return;
285 }
286
287 if (request.load_flags() & LOAD_MAIN_FRAME_DEPRECATED) {
288 ComputeEffectiveConnectionType();
289 }
290
291 LoadTimingInfo load_timing_info;
292 request.GetLoadTimingInfo(&load_timing_info);
293
294 // If the load timing info is unavailable, it probably means that the request
295 // did not go over the network.
296 if (load_timing_info.send_start.is_null() ||
297 load_timing_info.receive_headers_end.is_null()) {
298 return;
299 }
300 DCHECK(!request.response_info().was_cached);
301
302 // Duration between when the resource was requested and when the response
303 // headers were received.
304 const base::TimeDelta observed_http_rtt =
305 load_timing_info.receive_headers_end - load_timing_info.send_start;
306 if (observed_http_rtt <= base::TimeDelta())
307 return;
308 DCHECK_GE(observed_http_rtt, base::TimeDelta());
309
310 if (IsHangingRequest(observed_http_rtt))
311 return;
312
313 Observation http_rtt_observation(observed_http_rtt.InMilliseconds(),
314 tick_clock_->NowTicks(),
315 current_network_id_.signal_strength,
316 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP);
317 AddAndNotifyObserversOfRTT(http_rtt_observation);
318 throughput_analyzer_->NotifyBytesRead(request);
319 throughput_analyzer_->NotifyExpectedResponseContentSize(
320 request, request.GetExpectedContentSize());
321 }
322
NotifyBytesRead(const URLRequest & request,int64_t prefilter_total_bytes_read)323 void NetworkQualityEstimator::NotifyBytesRead(
324 const URLRequest& request,
325 int64_t prefilter_total_bytes_read) {
326 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
327 throughput_analyzer_->NotifyBytesRead(request);
328 }
329
NotifyRequestCompleted(const URLRequest & request)330 void NetworkQualityEstimator::NotifyRequestCompleted(
331 const URLRequest& request) {
332 TRACE_EVENT0(NetTracingCategory(),
333 "NetworkQualityEstimator::NotifyRequestCompleted");
334 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
335
336 if (!RequestSchemeIsHTTPOrHTTPS(request))
337 return;
338
339 throughput_analyzer_->NotifyRequestCompleted(request);
340 }
341
NotifyURLRequestDestroyed(const URLRequest & request)342 void NetworkQualityEstimator::NotifyURLRequestDestroyed(
343 const URLRequest& request) {
344 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
345
346 if (!RequestSchemeIsHTTPOrHTTPS(request))
347 return;
348
349 throughput_analyzer_->NotifyRequestCompleted(request);
350 }
351
AddRTTObserver(RTTObserver * rtt_observer)352 void NetworkQualityEstimator::AddRTTObserver(RTTObserver* rtt_observer) {
353 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
354 rtt_observer_list_.AddObserver(rtt_observer);
355 }
356
RemoveRTTObserver(RTTObserver * rtt_observer)357 void NetworkQualityEstimator::RemoveRTTObserver(RTTObserver* rtt_observer) {
358 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
359 rtt_observer_list_.RemoveObserver(rtt_observer);
360 }
361
AddThroughputObserver(ThroughputObserver * throughput_observer)362 void NetworkQualityEstimator::AddThroughputObserver(
363 ThroughputObserver* throughput_observer) {
364 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
365 throughput_observer_list_.AddObserver(throughput_observer);
366 }
367
RemoveThroughputObserver(ThroughputObserver * throughput_observer)368 void NetworkQualityEstimator::RemoveThroughputObserver(
369 ThroughputObserver* throughput_observer) {
370 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
371 throughput_observer_list_.RemoveObserver(throughput_observer);
372 }
373
374 SocketPerformanceWatcherFactory*
GetSocketPerformanceWatcherFactory()375 NetworkQualityEstimator::GetSocketPerformanceWatcherFactory() {
376 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
377
378 return watcher_factory_.get();
379 }
380
SetUseLocalHostRequestsForTesting(bool use_localhost_requests)381 void NetworkQualityEstimator::SetUseLocalHostRequestsForTesting(
382 bool use_localhost_requests) {
383 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
384 use_localhost_requests_ = use_localhost_requests;
385 watcher_factory_->SetUseLocalHostRequestsForTesting(use_localhost_requests_);
386 throughput_analyzer_->SetUseLocalHostRequestsForTesting(
387 use_localhost_requests_);
388 }
389
SetUseSmallResponsesForTesting(bool use_small_responses)390 void NetworkQualityEstimator::SetUseSmallResponsesForTesting(
391 bool use_small_responses) {
392 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
393 params_->SetUseSmallResponsesForTesting(use_small_responses);
394 }
395
DisableOfflineCheckForTesting(bool disable_offline_check)396 void NetworkQualityEstimator::DisableOfflineCheckForTesting(
397 bool disable_offline_check) {
398 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
399 disable_offline_check_ = disable_offline_check;
400 }
401
ReportEffectiveConnectionTypeForTesting(EffectiveConnectionType effective_connection_type)402 void NetworkQualityEstimator::ReportEffectiveConnectionTypeForTesting(
403 EffectiveConnectionType effective_connection_type) {
404 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
405
406 event_creator_.MaybeAddNetworkQualityChangedEventToNetLog(
407 effective_connection_type_,
408 params_->TypicalNetworkQuality(effective_connection_type));
409
410 for (auto& observer : effective_connection_type_observer_list_)
411 observer.OnEffectiveConnectionTypeChanged(effective_connection_type);
412
413 network_quality_store_->Add(current_network_id_,
414 nqe::internal::CachedNetworkQuality(
415 tick_clock_->NowTicks(), network_quality_,
416 effective_connection_type));
417 }
418
ReportRTTsAndThroughputForTesting(base::TimeDelta http_rtt,base::TimeDelta transport_rtt,int32_t downstream_throughput_kbps)419 void NetworkQualityEstimator::ReportRTTsAndThroughputForTesting(
420 base::TimeDelta http_rtt,
421 base::TimeDelta transport_rtt,
422 int32_t downstream_throughput_kbps) {
423 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
424
425 for (auto& observer : rtt_and_throughput_estimates_observer_list_)
426 observer.OnRTTOrThroughputEstimatesComputed(http_rtt, transport_rtt,
427 downstream_throughput_kbps);
428 }
429
RequestProvidesRTTObservation(const URLRequest & request) const430 bool NetworkQualityEstimator::RequestProvidesRTTObservation(
431 const URLRequest& request) const {
432 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
433
434 bool private_network_request =
435 nqe::internal::IsRequestForPrivateHost(request, net_log_);
436
437 return (use_localhost_requests_ || !private_network_request) &&
438 // Verify that response headers are received, so it can be ensured that
439 // response is not cached.
440 !request.response_info().response_time.is_null() &&
441 !request.was_cached() &&
442 request.creation_time() >= last_connection_change_ &&
443 request.method() == "GET";
444 }
445
OnConnectionTypeChanged(NetworkChangeNotifier::ConnectionType type)446 void NetworkQualityEstimator::OnConnectionTypeChanged(
447 NetworkChangeNotifier::ConnectionType type) {
448 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
449
450 // It's possible that |type| has the same value as |current_network_id_.type|.
451 // This can happen if the device switches from one WiFi SSID to another.
452
453 DCHECK_EQ(nqe::internal::OBSERVATION_CATEGORY_COUNT,
454 std::size(rtt_ms_observations_));
455
456 // Write the estimates of the previous network to the cache.
457 network_quality_store_->Add(
458 current_network_id_, nqe::internal::CachedNetworkQuality(
459 last_effective_connection_type_computation_,
460 network_quality_, effective_connection_type_));
461
462 // Clear the local state.
463 last_connection_change_ = tick_clock_->NowTicks();
464 http_downstream_throughput_kbps_observations_.Clear();
465 for (auto& rtt_ms_observation : rtt_ms_observations_)
466 rtt_ms_observation.Clear();
467
468 current_network_id_.signal_strength = INT32_MIN;
469 network_quality_ = nqe::internal::NetworkQuality();
470 end_to_end_rtt_ = absl::nullopt;
471 effective_connection_type_ = EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
472 rtt_observations_size_at_last_ect_computation_ = 0;
473 throughput_observations_size_at_last_ect_computation_ = 0;
474 new_rtt_observations_since_last_ect_computation_ = 0;
475 new_throughput_observations_since_last_ect_computation_ = 0;
476 transport_rtt_observation_count_last_ect_computation_ = 0;
477 end_to_end_rtt_observation_count_at_last_ect_computation_ = 0;
478 last_socket_watcher_rtt_notification_ = base::TimeTicks();
479 cached_estimate_applied_ = false;
480
481 GatherEstimatesForNextConnectionType();
482 throughput_analyzer_->OnConnectionTypeChanged();
483 }
484
GatherEstimatesForNextConnectionType()485 void NetworkQualityEstimator::GatherEstimatesForNextConnectionType() {
486 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
487
488 #if BUILDFLAG(IS_CHROMEOS_ASH)
489 if (get_network_id_asynchronously_) {
490 // Doing PostTaskAndReplyWithResult by handle because it requires the result
491 // type have a default constructor and nqe::internal::NetworkID does not
492 // have that.
493 g_get_network_id_task_runner.Get()->PostTask(
494 FROM_HERE,
495 base::BindOnce(
496 [](scoped_refptr<base::TaskRunner> reply_task_runner,
497 base::OnceCallback<void(const nqe::internal::NetworkID&)>
498 reply_callback) {
499 reply_task_runner->PostTask(
500 FROM_HERE, base::BindOnce(std::move(reply_callback),
501 DoGetCurrentNetworkID(nullptr)));
502 },
503 base::SingleThreadTaskRunner::GetCurrentDefault(),
504 base::BindOnce(&NetworkQualityEstimator::
505 ContinueGatherEstimatesForNextConnectionType,
506 weak_ptr_factory_.GetWeakPtr())));
507 return;
508 }
509 #endif // BUILDFLAG(IS_CHROMEOS_ASH)
510
511 ContinueGatherEstimatesForNextConnectionType(GetCurrentNetworkID());
512 }
513
ContinueGatherEstimatesForNextConnectionType(const nqe::internal::NetworkID & network_id)514 void NetworkQualityEstimator::ContinueGatherEstimatesForNextConnectionType(
515 const nqe::internal::NetworkID& network_id) {
516 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
517 // Update the local state as part of preparation for the new connection.
518 current_network_id_ = network_id;
519
520 // Read any cached estimates for the new network. If cached estimates are
521 // unavailable, add the default estimates.
522 if (!ReadCachedNetworkQualityEstimate())
523 AddDefaultEstimates();
524
525 ComputeEffectiveConnectionType();
526 }
527
ComputeEffectiveConnectionType()528 void NetworkQualityEstimator::ComputeEffectiveConnectionType() {
529 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
530
531 const base::TimeTicks now = tick_clock_->NowTicks();
532
533 const EffectiveConnectionType past_type = effective_connection_type_;
534 last_effective_connection_type_computation_ = now;
535
536 base::TimeDelta http_rtt = nqe::internal::InvalidRTT();
537 base::TimeDelta transport_rtt = nqe::internal::InvalidRTT();
538 base::TimeDelta end_to_end_rtt = nqe::internal::InvalidRTT();
539 int32_t downstream_throughput_kbps = nqe::internal::INVALID_RTT_THROUGHPUT;
540
541 effective_connection_type_ = GetRecentEffectiveConnectionTypeUsingMetrics(
542 &http_rtt, &transport_rtt, &end_to_end_rtt, &downstream_throughput_kbps,
543 &transport_rtt_observation_count_last_ect_computation_,
544 &end_to_end_rtt_observation_count_at_last_ect_computation_);
545
546 network_quality_ = nqe::internal::NetworkQuality(http_rtt, transport_rtt,
547 downstream_throughput_kbps);
548 ClampKbpsBasedOnEct();
549 if (network_quality_.http_rtt() != nqe::internal::InvalidRTT()) {
550 UMA_HISTOGRAM_TIMES("NQE.RTT.OnECTComputation",
551 network_quality_.http_rtt());
552 }
553
554 end_to_end_rtt_ = absl::nullopt;
555 if (end_to_end_rtt != nqe::internal::InvalidRTT()) {
556 end_to_end_rtt_ = end_to_end_rtt;
557 }
558
559 NotifyObserversOfRTTOrThroughputComputed();
560
561 if (past_type != effective_connection_type_)
562 NotifyObserversOfEffectiveConnectionTypeChanged();
563
564 event_creator_.MaybeAddNetworkQualityChangedEventToNetLog(
565 effective_connection_type_, network_quality_);
566
567 rtt_observations_size_at_last_ect_computation_ =
568 rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_HTTP].Size() +
569 rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT]
570 .Size();
571 throughput_observations_size_at_last_ect_computation_ =
572 http_downstream_throughput_kbps_observations_.Size();
573 new_rtt_observations_since_last_ect_computation_ = 0;
574 new_throughput_observations_since_last_ect_computation_ = 0;
575 }
576
577 absl::optional<net::EffectiveConnectionType>
GetOverrideECT() const578 NetworkQualityEstimator::GetOverrideECT() const {
579 return absl::nullopt;
580 }
581
ClampKbpsBasedOnEct()582 void NetworkQualityEstimator::ClampKbpsBasedOnEct() {
583 // No need to clamp when ECT is unknown or if the connection speed is fast.
584 if (effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_UNKNOWN ||
585 effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_OFFLINE ||
586 effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_4G) {
587 return;
588 }
589
590 if (params_->upper_bound_typical_kbps_multiplier() <= 0.0)
591 return;
592
593 DCHECK_LT(0, params_->TypicalNetworkQuality(effective_connection_type_)
594 .downstream_throughput_kbps());
595 // For a given ECT, upper bound on Kbps can't be less than the typical Kbps
596 // for that ECT.
597 DCHECK_LE(1.0, params_->upper_bound_typical_kbps_multiplier());
598
599 DCHECK(effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_SLOW_2G ||
600 effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_2G ||
601 effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_3G);
602
603 // Put an upper bound on Kbps.
604 network_quality_.set_downstream_throughput_kbps(
605 std::min(network_quality_.downstream_throughput_kbps(),
606 static_cast<int>(
607 params_->TypicalNetworkQuality(effective_connection_type_)
608 .downstream_throughput_kbps() *
609 params_->upper_bound_typical_kbps_multiplier())));
610 }
611
AdjustHttpRttBasedOnRTTCounts(base::TimeDelta * http_rtt) const612 void NetworkQualityEstimator::AdjustHttpRttBasedOnRTTCounts(
613 base::TimeDelta* http_rtt) const {
614 if (!params_->adjust_rtt_based_on_rtt_counts())
615 return;
616
617 // This is needed only when RTT from TCP sockets or
618 // QUIC/H2 connections is unavailable.
619 if (transport_rtt_observation_count_last_ect_computation_ >=
620 params_->http_rtt_transport_rtt_min_count() ||
621 end_to_end_rtt_observation_count_at_last_ect_computation_ >=
622 params_->http_rtt_transport_rtt_min_count()) {
623 return;
624 }
625
626 // We prefer to use the cached value if it's available and the network change
627 // happened recently.
628 base::TimeDelta time_since_connection_change =
629 tick_clock_->NowTicks() - last_connection_change_;
630 if (cached_estimate_applied_ &&
631 time_since_connection_change <= base::Minutes(1)) {
632 return;
633 }
634
635 // If there are not enough transport RTT samples, end-to-end RTT samples and
636 // the cached estimates are unavailble/too stale, then the computed value of
637 // HTTP RTT can't be trusted due to hanging GETs. In that case, return the
638 // typical HTTP RTT for a fast connection.
639 if (current_network_id_.type == net::NetworkChangeNotifier::CONNECTION_NONE) {
640 return;
641 }
642
643 base::TimeDelta upper_bound_http_rtt =
644 params_->TypicalNetworkQuality(net::EFFECTIVE_CONNECTION_TYPE_4G)
645 .http_rtt();
646 if (upper_bound_http_rtt > *http_rtt) {
647 return;
648 }
649
650 DCHECK_LE(upper_bound_http_rtt, *http_rtt);
651 *http_rtt = upper_bound_http_rtt;
652 }
653
GetEffectiveConnectionType() const654 EffectiveConnectionType NetworkQualityEstimator::GetEffectiveConnectionType()
655 const {
656 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
657
658 absl::optional<net::EffectiveConnectionType> override_ect = GetOverrideECT();
659 if (override_ect) {
660 return override_ect.value();
661 }
662 return effective_connection_type_;
663 }
664
UpdateHttpRttUsingAllRttValues(base::TimeDelta * http_rtt,const base::TimeDelta transport_rtt,const base::TimeDelta end_to_end_rtt) const665 void NetworkQualityEstimator::UpdateHttpRttUsingAllRttValues(
666 base::TimeDelta* http_rtt,
667 const base::TimeDelta transport_rtt,
668 const base::TimeDelta end_to_end_rtt) const {
669 DCHECK(http_rtt);
670
671 // Use transport RTT to clamp the lower bound on HTTP RTT.
672 // To improve accuracy, the transport RTT estimate is used only when the
673 // transport RTT estimate was computed using at least
674 // |params_->http_rtt_transport_rtt_min_count()| observations.
675 if (*http_rtt != nqe::internal::InvalidRTT() &&
676 transport_rtt != nqe::internal::InvalidRTT() &&
677 transport_rtt_observation_count_last_ect_computation_ >=
678 params_->http_rtt_transport_rtt_min_count() &&
679 params_->lower_bound_http_rtt_transport_rtt_multiplier() > 0) {
680 *http_rtt =
681 std::max(*http_rtt,
682 transport_rtt *
683 params_->lower_bound_http_rtt_transport_rtt_multiplier());
684 }
685
686 // Put lower bound on |http_rtt| using |end_to_end_rtt|.
687 if (*http_rtt != nqe::internal::InvalidRTT() &&
688 params_->use_end_to_end_rtt() &&
689 end_to_end_rtt != nqe::internal::InvalidRTT() &&
690 end_to_end_rtt_observation_count_at_last_ect_computation_ >=
691 params_->http_rtt_transport_rtt_min_count() &&
692 params_->lower_bound_http_rtt_transport_rtt_multiplier() > 0) {
693 *http_rtt =
694 std::max(*http_rtt,
695 end_to_end_rtt *
696 params_->lower_bound_http_rtt_transport_rtt_multiplier());
697 }
698
699 // Put upper bound on |http_rtt| using |end_to_end_rtt|.
700 if (*http_rtt != nqe::internal::InvalidRTT() &&
701 params_->use_end_to_end_rtt() &&
702 end_to_end_rtt != nqe::internal::InvalidRTT() &&
703 end_to_end_rtt_observation_count_at_last_ect_computation_ >=
704 params_->http_rtt_transport_rtt_min_count() &&
705 params_->upper_bound_http_rtt_endtoend_rtt_multiplier() > 0) {
706 *http_rtt = std::min(
707 *http_rtt, end_to_end_rtt *
708 params_->upper_bound_http_rtt_endtoend_rtt_multiplier());
709 }
710
711 // Put upper bound on |http_rtt| if there is not enough HTTP RTT samples
712 // available.
713 AdjustHttpRttBasedOnRTTCounts(http_rtt);
714 }
715
716 EffectiveConnectionType
GetRecentEffectiveConnectionTypeUsingMetrics(base::TimeDelta * http_rtt,base::TimeDelta * transport_rtt,base::TimeDelta * end_to_end_rtt,int32_t * downstream_throughput_kbps,size_t * transport_rtt_observation_count,size_t * end_to_end_rtt_observation_count) const717 NetworkQualityEstimator::GetRecentEffectiveConnectionTypeUsingMetrics(
718 base::TimeDelta* http_rtt,
719 base::TimeDelta* transport_rtt,
720 base::TimeDelta* end_to_end_rtt,
721 int32_t* downstream_throughput_kbps,
722 size_t* transport_rtt_observation_count,
723 size_t* end_to_end_rtt_observation_count) const {
724 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
725
726 *http_rtt = nqe::internal::InvalidRTT();
727 *transport_rtt = nqe::internal::InvalidRTT();
728 *end_to_end_rtt = nqe::internal::InvalidRTT();
729 *downstream_throughput_kbps = nqe::internal::INVALID_RTT_THROUGHPUT;
730
731 auto forced_ect =
732 params_->GetForcedEffectiveConnectionType(current_network_id_.type);
733 if (forced_ect) {
734 *http_rtt = params_->TypicalNetworkQuality(forced_ect.value()).http_rtt();
735 *transport_rtt =
736 params_->TypicalNetworkQuality(forced_ect.value()).transport_rtt();
737 *downstream_throughput_kbps =
738 params_->TypicalNetworkQuality(forced_ect.value())
739 .downstream_throughput_kbps();
740 return forced_ect.value();
741 }
742
743 // If the device is currently offline, then return
744 // EFFECTIVE_CONNECTION_TYPE_OFFLINE.
745 if (current_network_id_.type == NetworkChangeNotifier::CONNECTION_NONE &&
746 !disable_offline_check_) {
747 return EFFECTIVE_CONNECTION_TYPE_OFFLINE;
748 }
749
750 if (force_report_wifi_as_slow_2g_for_testing_ &&
751 current_network_id_.type == NetworkChangeNotifier::CONNECTION_WIFI) {
752 return EFFECTIVE_CONNECTION_TYPE_SLOW_2G;
753 }
754
755 if (!GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, base::TimeTicks(),
756 http_rtt, nullptr)) {
757 *http_rtt = nqe::internal::InvalidRTT();
758 }
759
760 if (!GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_TRANSPORT,
761 base::TimeTicks(), transport_rtt,
762 transport_rtt_observation_count)) {
763 *transport_rtt = nqe::internal::InvalidRTT();
764 }
765
766 if (!GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_END_TO_END,
767 base::TimeTicks(), end_to_end_rtt,
768 end_to_end_rtt_observation_count)) {
769 *end_to_end_rtt = nqe::internal::InvalidRTT();
770 }
771
772 UpdateHttpRttUsingAllRttValues(http_rtt, *transport_rtt, *end_to_end_rtt);
773
774 if (!GetRecentDownlinkThroughputKbps(base::TimeTicks(),
775 downstream_throughput_kbps)) {
776 *downstream_throughput_kbps = nqe::internal::INVALID_RTT_THROUGHPUT;
777 }
778
779 if (*http_rtt == nqe::internal::InvalidRTT()) {
780 return EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
781 }
782
783 if (*http_rtt == nqe::internal::InvalidRTT() &&
784 *transport_rtt == nqe::internal::InvalidRTT() &&
785 *downstream_throughput_kbps == nqe::internal::INVALID_RTT_THROUGHPUT) {
786 // None of the metrics are available.
787 return EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
788 }
789
790 // Search from the slowest connection type to the fastest to find the
791 // EffectiveConnectionType that best matches the current connection's
792 // performance. The match is done by comparing RTT and throughput.
793 for (size_t i = 0; i < EFFECTIVE_CONNECTION_TYPE_LAST; ++i) {
794 EffectiveConnectionType type = static_cast<EffectiveConnectionType>(i);
795 if (i == EFFECTIVE_CONNECTION_TYPE_UNKNOWN)
796 continue;
797
798 const bool estimated_http_rtt_is_higher_than_threshold =
799 *http_rtt != nqe::internal::InvalidRTT() &&
800 params_->ConnectionThreshold(type).http_rtt() !=
801 nqe::internal::InvalidRTT() &&
802 *http_rtt >= params_->ConnectionThreshold(type).http_rtt();
803
804 if (estimated_http_rtt_is_higher_than_threshold)
805 return type;
806 }
807 // Return the fastest connection type.
808 return static_cast<EffectiveConnectionType>(EFFECTIVE_CONNECTION_TYPE_LAST -
809 1);
810 }
811
AddEffectiveConnectionTypeObserver(EffectiveConnectionTypeObserver * observer)812 void NetworkQualityEstimator::AddEffectiveConnectionTypeObserver(
813 EffectiveConnectionTypeObserver* observer) {
814 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
815 DCHECK(observer);
816 effective_connection_type_observer_list_.AddObserver(observer);
817
818 // Notify the |observer| on the next message pump since |observer| may not
819 // be completely set up for receiving the callbacks.
820 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
821 FROM_HERE,
822 base::BindOnce(&NetworkQualityEstimator::
823 NotifyEffectiveConnectionTypeObserverIfPresent,
824 weak_ptr_factory_.GetWeakPtr(),
825 // This is safe as `handle` is checked against a map to
826 // verify it hasn't been removed before dereferencing.
827 base::UnsafeDangling(observer)));
828 }
829
RemoveEffectiveConnectionTypeObserver(EffectiveConnectionTypeObserver * observer)830 void NetworkQualityEstimator::RemoveEffectiveConnectionTypeObserver(
831 EffectiveConnectionTypeObserver* observer) {
832 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
833 effective_connection_type_observer_list_.RemoveObserver(observer);
834 }
835
AddPeerToPeerConnectionsCountObserver(PeerToPeerConnectionsCountObserver * observer)836 void NetworkQualityEstimator::AddPeerToPeerConnectionsCountObserver(
837 PeerToPeerConnectionsCountObserver* observer) {
838 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
839 DCHECK(observer);
840 peer_to_peer_type_observer_list_.AddObserver(observer);
841
842 // Notify the |observer| on the next message pump since |observer| may not
843 // be completely set up for receiving the callbacks.
844 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
845 FROM_HERE,
846 base::BindOnce(&NetworkQualityEstimator::
847 NotifyPeerToPeerConnectionsCountObserverIfPresent,
848 weak_ptr_factory_.GetWeakPtr(),
849 // This is safe as `handle` is checked against a map to
850 // verify it hasn't been removed before dereferencing.
851 base::UnsafeDangling(observer)));
852 }
853
RemovePeerToPeerConnectionsCountObserver(PeerToPeerConnectionsCountObserver * observer)854 void NetworkQualityEstimator::RemovePeerToPeerConnectionsCountObserver(
855 PeerToPeerConnectionsCountObserver* observer) {
856 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
857 peer_to_peer_type_observer_list_.RemoveObserver(observer);
858 }
859
AddRTTAndThroughputEstimatesObserver(RTTAndThroughputEstimatesObserver * observer)860 void NetworkQualityEstimator::AddRTTAndThroughputEstimatesObserver(
861 RTTAndThroughputEstimatesObserver* observer) {
862 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
863 DCHECK(observer);
864 rtt_and_throughput_estimates_observer_list_.AddObserver(observer);
865
866 // Notify the |observer| on the next message pump since |observer| may not
867 // be completely set up for receiving the callbacks.
868 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
869 FROM_HERE,
870 base::BindOnce(&NetworkQualityEstimator::
871 NotifyRTTAndThroughputEstimatesObserverIfPresent,
872 weak_ptr_factory_.GetWeakPtr(), observer));
873 }
874
RemoveRTTAndThroughputEstimatesObserver(RTTAndThroughputEstimatesObserver * observer)875 void NetworkQualityEstimator::RemoveRTTAndThroughputEstimatesObserver(
876 RTTAndThroughputEstimatesObserver* observer) {
877 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
878 rtt_and_throughput_estimates_observer_list_.RemoveObserver(observer);
879 }
880
GetRecentRTT(nqe::internal::ObservationCategory observation_category,const base::TimeTicks & start_time,base::TimeDelta * rtt,size_t * observations_count) const881 bool NetworkQualityEstimator::GetRecentRTT(
882 nqe::internal::ObservationCategory observation_category,
883 const base::TimeTicks& start_time,
884 base::TimeDelta* rtt,
885 size_t* observations_count) const {
886 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
887 *rtt = GetRTTEstimateInternal(start_time, observation_category, 50,
888 observations_count);
889 return (*rtt != nqe::internal::InvalidRTT());
890 }
891
GetRecentDownlinkThroughputKbps(const base::TimeTicks & start_time,int32_t * kbps) const892 bool NetworkQualityEstimator::GetRecentDownlinkThroughputKbps(
893 const base::TimeTicks& start_time,
894 int32_t* kbps) const {
895 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
896 *kbps = GetDownlinkThroughputKbpsEstimateInternal(start_time, 50);
897 return (*kbps != nqe::internal::INVALID_RTT_THROUGHPUT);
898 }
899
GetRTTEstimateInternal(base::TimeTicks start_time,nqe::internal::ObservationCategory observation_category,int percentile,size_t * observations_count) const900 base::TimeDelta NetworkQualityEstimator::GetRTTEstimateInternal(
901 base::TimeTicks start_time,
902 nqe::internal::ObservationCategory observation_category,
903 int percentile,
904 size_t* observations_count) const {
905 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
906 DCHECK_EQ(nqe::internal::OBSERVATION_CATEGORY_COUNT,
907 std::size(rtt_ms_observations_));
908
909 // RTT observations are sorted by duration from shortest to longest, thus
910 // a higher percentile RTT will have a longer RTT than a lower percentile.
911 switch (observation_category) {
912 case nqe::internal::OBSERVATION_CATEGORY_HTTP:
913 case nqe::internal::OBSERVATION_CATEGORY_TRANSPORT:
914 case nqe::internal::OBSERVATION_CATEGORY_END_TO_END:
915 return base::Milliseconds(
916 rtt_ms_observations_[observation_category]
917 .GetPercentile(start_time, current_network_id_.signal_strength,
918 percentile, observations_count)
919 .value_or(nqe::internal::INVALID_RTT_THROUGHPUT));
920 case nqe::internal::OBSERVATION_CATEGORY_COUNT:
921 NOTREACHED();
922 return base::TimeDelta();
923 }
924 }
925
GetDownlinkThroughputKbpsEstimateInternal(const base::TimeTicks & start_time,int percentile) const926 int32_t NetworkQualityEstimator::GetDownlinkThroughputKbpsEstimateInternal(
927 const base::TimeTicks& start_time,
928 int percentile) const {
929 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
930
931 // Throughput observations are sorted by kbps from slowest to fastest,
932 // thus a higher percentile throughput will be faster than a lower one.
933 return http_downstream_throughput_kbps_observations_
934 .GetPercentile(start_time, current_network_id_.signal_strength,
935 100 - percentile, nullptr)
936 .value_or(nqe::internal::INVALID_RTT_THROUGHPUT);
937 }
938
GetCurrentNetworkID() const939 nqe::internal::NetworkID NetworkQualityEstimator::GetCurrentNetworkID() const {
940 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
941
942 // TODO(tbansal): crbug.com/498068 Add NetworkQualityEstimatorAndroid class
943 // that overrides this method on the Android platform.
944
945 return DoGetCurrentNetworkID(params_.get());
946 }
947
ReadCachedNetworkQualityEstimate()948 bool NetworkQualityEstimator::ReadCachedNetworkQualityEstimate() {
949 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
950
951 if (!params_->persistent_cache_reading_enabled())
952 return false;
953
954 nqe::internal::CachedNetworkQuality cached_network_quality;
955
956 const bool cached_estimate_available = network_quality_store_->GetById(
957 current_network_id_, &cached_network_quality);
958
959 if (!cached_estimate_available) {
960 return false;
961 }
962
963 EffectiveConnectionType effective_connection_type =
964 cached_network_quality.effective_connection_type();
965
966 if (effective_connection_type == EFFECTIVE_CONNECTION_TYPE_UNKNOWN ||
967 effective_connection_type == EFFECTIVE_CONNECTION_TYPE_OFFLINE ||
968 effective_connection_type == EFFECTIVE_CONNECTION_TYPE_LAST) {
969 return false;
970 }
971
972 nqe::internal::NetworkQuality network_quality =
973 cached_network_quality.network_quality();
974
975 bool update_network_quality_store = false;
976
977 // Populate |network_quality| with synthetic RTT and throughput observations
978 // if they are missing.
979 if (network_quality.http_rtt().InMilliseconds() ==
980 nqe::internal::INVALID_RTT_THROUGHPUT) {
981 network_quality.set_http_rtt(
982 params_->TypicalNetworkQuality(effective_connection_type).http_rtt());
983 update_network_quality_store = true;
984 }
985
986 if (network_quality.transport_rtt().InMilliseconds() ==
987 nqe::internal::INVALID_RTT_THROUGHPUT) {
988 network_quality.set_transport_rtt(
989 params_->TypicalNetworkQuality(effective_connection_type)
990 .transport_rtt());
991 update_network_quality_store = true;
992 }
993
994 if (network_quality.downstream_throughput_kbps() ==
995 nqe::internal::INVALID_RTT_THROUGHPUT) {
996 network_quality.set_downstream_throughput_kbps(
997 params_->TypicalNetworkQuality(effective_connection_type)
998 .downstream_throughput_kbps());
999 update_network_quality_store = true;
1000 }
1001
1002 if (update_network_quality_store) {
1003 network_quality_store_->Add(current_network_id_,
1004 nqe::internal::CachedNetworkQuality(
1005 tick_clock_->NowTicks(), network_quality,
1006 effective_connection_type));
1007 }
1008
1009 Observation http_rtt_observation(
1010 network_quality.http_rtt().InMilliseconds(), tick_clock_->NowTicks(),
1011 INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_CACHED_ESTIMATE);
1012 AddAndNotifyObserversOfRTT(http_rtt_observation);
1013
1014 Observation transport_rtt_observation(
1015 network_quality.transport_rtt().InMilliseconds(), tick_clock_->NowTicks(),
1016 INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_TRANSPORT_CACHED_ESTIMATE);
1017 AddAndNotifyObserversOfRTT(transport_rtt_observation);
1018
1019 Observation througphput_observation(
1020 network_quality.downstream_throughput_kbps(), tick_clock_->NowTicks(),
1021 INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_CACHED_ESTIMATE);
1022 AddAndNotifyObserversOfThroughput(througphput_observation);
1023
1024 ComputeEffectiveConnectionType();
1025 return true;
1026 }
1027
SetTickClockForTesting(const base::TickClock * tick_clock)1028 void NetworkQualityEstimator::SetTickClockForTesting(
1029 const base::TickClock* tick_clock) {
1030 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1031 tick_clock_ = tick_clock;
1032 for (auto& rtt_ms_observation : rtt_ms_observations_)
1033 rtt_ms_observation.SetTickClockForTesting(tick_clock_); // IN-TEST
1034 http_downstream_throughput_kbps_observations_.SetTickClockForTesting(
1035 tick_clock_);
1036 throughput_analyzer_->SetTickClockForTesting(tick_clock_);
1037 watcher_factory_->SetTickClockForTesting(tick_clock_);
1038 }
1039
OnUpdatedTransportRTTAvailable(SocketPerformanceWatcherFactory::Protocol protocol,const base::TimeDelta & rtt,const absl::optional<nqe::internal::IPHash> & host)1040 void NetworkQualityEstimator::OnUpdatedTransportRTTAvailable(
1041 SocketPerformanceWatcherFactory::Protocol protocol,
1042 const base::TimeDelta& rtt,
1043 const absl::optional<nqe::internal::IPHash>& host) {
1044 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1045 DCHECK_LT(nqe::internal::INVALID_RTT_THROUGHPUT, rtt.InMilliseconds());
1046 Observation observation(rtt.InMilliseconds(), tick_clock_->NowTicks(),
1047 current_network_id_.signal_strength,
1048 ProtocolSourceToObservationSource(protocol), host);
1049 AddAndNotifyObserversOfRTT(observation);
1050 }
1051
AddAndNotifyObserversOfRTT(const Observation & observation)1052 void NetworkQualityEstimator::AddAndNotifyObserversOfRTT(
1053 const Observation& observation) {
1054 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1055 DCHECK_NE(nqe::internal::InvalidRTT(),
1056 base::Milliseconds(observation.value()));
1057 DCHECK_GT(NETWORK_QUALITY_OBSERVATION_SOURCE_MAX, observation.source());
1058
1059 if (!ShouldAddObservation(observation))
1060 return;
1061
1062 MaybeUpdateCachedEstimateApplied(
1063 observation,
1064 &rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_HTTP]);
1065 MaybeUpdateCachedEstimateApplied(
1066 observation,
1067 &rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT]);
1068 ++new_rtt_observations_since_last_ect_computation_;
1069
1070 std::vector<nqe::internal::ObservationCategory> observation_categories =
1071 observation.GetObservationCategories();
1072 for (nqe::internal::ObservationCategory observation_category :
1073 observation_categories) {
1074 rtt_ms_observations_[observation_category].AddObservation(observation);
1075 }
1076
1077 if (observation.source() == NETWORK_QUALITY_OBSERVATION_SOURCE_TCP ||
1078 observation.source() == NETWORK_QUALITY_OBSERVATION_SOURCE_QUIC) {
1079 last_socket_watcher_rtt_notification_ = tick_clock_->NowTicks();
1080 }
1081
1082 UMA_HISTOGRAM_ENUMERATION("NQE.RTT.ObservationSource", observation.source(),
1083 NETWORK_QUALITY_OBSERVATION_SOURCE_MAX);
1084
1085 // Maybe recompute the effective connection type since a new RTT observation
1086 // is available.
1087 if (observation.source() !=
1088 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_CACHED_ESTIMATE &&
1089 observation.source() !=
1090 NETWORK_QUALITY_OBSERVATION_SOURCE_TRANSPORT_CACHED_ESTIMATE) {
1091 MaybeComputeEffectiveConnectionType();
1092 }
1093 for (auto& observer : rtt_observer_list_) {
1094 observer.OnRTTObservation(observation.value(), observation.timestamp(),
1095 observation.source());
1096 }
1097 }
1098
AddAndNotifyObserversOfThroughput(const Observation & observation)1099 void NetworkQualityEstimator::AddAndNotifyObserversOfThroughput(
1100 const Observation& observation) {
1101 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1102 DCHECK_NE(nqe::internal::INVALID_RTT_THROUGHPUT, observation.value());
1103 DCHECK_GT(NETWORK_QUALITY_OBSERVATION_SOURCE_MAX, observation.source());
1104 DCHECK_EQ(1u, observation.GetObservationCategories().size());
1105 DCHECK_EQ(nqe::internal::OBSERVATION_CATEGORY_HTTP,
1106 observation.GetObservationCategories().front());
1107
1108 if (!ShouldAddObservation(observation))
1109 return;
1110
1111 MaybeUpdateCachedEstimateApplied(
1112 observation, &http_downstream_throughput_kbps_observations_);
1113 ++new_throughput_observations_since_last_ect_computation_;
1114 http_downstream_throughput_kbps_observations_.AddObservation(observation);
1115
1116 // Maybe recompute the effective connection type since a new throughput
1117 // observation is available.
1118 if (observation.source() !=
1119 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_CACHED_ESTIMATE &&
1120 observation.source() !=
1121 NETWORK_QUALITY_OBSERVATION_SOURCE_TRANSPORT_CACHED_ESTIMATE) {
1122 MaybeComputeEffectiveConnectionType();
1123 }
1124 for (auto& observer : throughput_observer_list_) {
1125 observer.OnThroughputObservation(
1126 observation.value(), observation.timestamp(), observation.source());
1127 }
1128 }
1129
OnNewThroughputObservationAvailable(int32_t downstream_kbps)1130 void NetworkQualityEstimator::OnNewThroughputObservationAvailable(
1131 int32_t downstream_kbps) {
1132 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1133
1134 if (downstream_kbps <= 0)
1135 return;
1136
1137 DCHECK_NE(nqe::internal::INVALID_RTT_THROUGHPUT, downstream_kbps);
1138
1139 Observation throughput_observation(downstream_kbps, tick_clock_->NowTicks(),
1140 current_network_id_.signal_strength,
1141 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP);
1142 AddAndNotifyObserversOfThroughput(throughput_observation);
1143 }
1144
ShouldComputeEffectiveConnectionType() const1145 bool NetworkQualityEstimator::ShouldComputeEffectiveConnectionType() const {
1146 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1147 DCHECK_EQ(nqe::internal::OBSERVATION_CATEGORY_COUNT,
1148 std::size(rtt_ms_observations_));
1149
1150 const base::TimeTicks now = tick_clock_->NowTicks();
1151 // Recompute effective connection type only if
1152 // |effective_connection_type_recomputation_interval_| has passed since it was
1153 // last computed or a connection change event was observed since the last
1154 // computation. Strict inequalities are used to ensure that effective
1155 // connection type is recomputed on connection change events even if the clock
1156 // has not updated.
1157 if (now - last_effective_connection_type_computation_ >=
1158 effective_connection_type_recomputation_interval_) {
1159 return true;
1160 }
1161
1162 if (last_connection_change_ >= last_effective_connection_type_computation_) {
1163 return true;
1164 }
1165
1166 // Recompute the effective connection type if the previously computed
1167 // effective connection type was unknown.
1168 if (effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {
1169 return true;
1170 }
1171
1172 // Recompute the effective connection type if the number of samples
1173 // available now are 50% more than the number of samples that were
1174 // available when the effective connection type was last computed.
1175 if (rtt_observations_size_at_last_ect_computation_ * 1.5 <
1176 (rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_HTTP].Size() +
1177 rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT]
1178 .Size())) {
1179 return true;
1180 }
1181
1182 if (throughput_observations_size_at_last_ect_computation_ * 1.5 <
1183 http_downstream_throughput_kbps_observations_.Size()) {
1184 return true;
1185 }
1186
1187 if ((new_rtt_observations_since_last_ect_computation_ +
1188 new_throughput_observations_since_last_ect_computation_) >=
1189 params_->count_new_observations_received_compute_ect()) {
1190 return true;
1191 }
1192 return false;
1193 }
1194
MaybeComputeEffectiveConnectionType()1195 void NetworkQualityEstimator::MaybeComputeEffectiveConnectionType() {
1196 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1197
1198 if (!ShouldComputeEffectiveConnectionType())
1199 return;
1200 ComputeEffectiveConnectionType();
1201 }
1202
1203 void NetworkQualityEstimator::
NotifyObserversOfEffectiveConnectionTypeChanged()1204 NotifyObserversOfEffectiveConnectionTypeChanged() {
1205 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1206 DCHECK_NE(EFFECTIVE_CONNECTION_TYPE_LAST, effective_connection_type_);
1207
1208 absl::optional<net::EffectiveConnectionType> override_ect = GetOverrideECT();
1209
1210 // TODO(tbansal): Add hysteresis in the notification.
1211 for (auto& observer : effective_connection_type_observer_list_)
1212 observer.OnEffectiveConnectionTypeChanged(
1213 override_ect ? override_ect.value() : effective_connection_type_);
1214 // Add the estimates of the current network to the cache store.
1215 network_quality_store_->Add(current_network_id_,
1216 nqe::internal::CachedNetworkQuality(
1217 tick_clock_->NowTicks(), network_quality_,
1218 effective_connection_type_));
1219 }
1220
NotifyObserversOfRTTOrThroughputComputed() const1221 void NetworkQualityEstimator::NotifyObserversOfRTTOrThroughputComputed() const {
1222 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1223
1224 // TODO(tbansal): Add hysteresis in the notification.
1225 for (auto& observer : rtt_and_throughput_estimates_observer_list_) {
1226 observer.OnRTTOrThroughputEstimatesComputed(
1227 network_quality_.http_rtt(), network_quality_.transport_rtt(),
1228 network_quality_.downstream_throughput_kbps());
1229 }
1230 }
1231
NotifyEffectiveConnectionTypeObserverIfPresent(MayBeDangling<EffectiveConnectionTypeObserver> observer) const1232 void NetworkQualityEstimator::NotifyEffectiveConnectionTypeObserverIfPresent(
1233 MayBeDangling<EffectiveConnectionTypeObserver> observer) const {
1234 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1235
1236 if (!effective_connection_type_observer_list_.HasObserver(observer))
1237 return;
1238
1239 absl::optional<net::EffectiveConnectionType> override_ect = GetOverrideECT();
1240 if (override_ect) {
1241 observer->OnEffectiveConnectionTypeChanged(override_ect.value());
1242 return;
1243 }
1244 if (effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_UNKNOWN)
1245 return;
1246 observer->OnEffectiveConnectionTypeChanged(effective_connection_type_);
1247 }
1248
NotifyPeerToPeerConnectionsCountObserverIfPresent(MayBeDangling<PeerToPeerConnectionsCountObserver> observer) const1249 void NetworkQualityEstimator::NotifyPeerToPeerConnectionsCountObserverIfPresent(
1250 MayBeDangling<PeerToPeerConnectionsCountObserver> observer) const {
1251 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1252
1253 if (!peer_to_peer_type_observer_list_.HasObserver(observer))
1254 return;
1255 observer->OnPeerToPeerConnectionsCountChange(p2p_connections_count_);
1256 }
1257
NotifyRTTAndThroughputEstimatesObserverIfPresent(RTTAndThroughputEstimatesObserver * observer) const1258 void NetworkQualityEstimator::NotifyRTTAndThroughputEstimatesObserverIfPresent(
1259 RTTAndThroughputEstimatesObserver* observer) const {
1260 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1261
1262 if (!rtt_and_throughput_estimates_observer_list_.HasObserver(observer))
1263 return;
1264 observer->OnRTTOrThroughputEstimatesComputed(
1265 network_quality_.http_rtt(), network_quality_.transport_rtt(),
1266 network_quality_.downstream_throughput_kbps());
1267 }
1268
AddNetworkQualitiesCacheObserver(nqe::internal::NetworkQualityStore::NetworkQualitiesCacheObserver * observer)1269 void NetworkQualityEstimator::AddNetworkQualitiesCacheObserver(
1270 nqe::internal::NetworkQualityStore::NetworkQualitiesCacheObserver*
1271 observer) {
1272 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1273 network_quality_store_->AddNetworkQualitiesCacheObserver(observer);
1274 }
1275
RemoveNetworkQualitiesCacheObserver(nqe::internal::NetworkQualityStore::NetworkQualitiesCacheObserver * observer)1276 void NetworkQualityEstimator::RemoveNetworkQualitiesCacheObserver(
1277 nqe::internal::NetworkQualityStore::NetworkQualitiesCacheObserver*
1278 observer) {
1279 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1280 network_quality_store_->RemoveNetworkQualitiesCacheObserver(observer);
1281 }
1282
OnPrefsRead(const std::map<nqe::internal::NetworkID,nqe::internal::CachedNetworkQuality> read_prefs)1283 void NetworkQualityEstimator::OnPrefsRead(
1284 const std::map<nqe::internal::NetworkID,
1285 nqe::internal::CachedNetworkQuality> read_prefs) {
1286 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1287
1288 UMA_HISTOGRAM_COUNTS_1M("NQE.Prefs.ReadSize", read_prefs.size());
1289 for (auto& it : read_prefs) {
1290 EffectiveConnectionType effective_connection_type =
1291 it.second.effective_connection_type();
1292 if (effective_connection_type == EFFECTIVE_CONNECTION_TYPE_UNKNOWN ||
1293 effective_connection_type == EFFECTIVE_CONNECTION_TYPE_OFFLINE) {
1294 continue;
1295 }
1296
1297 // RTT and throughput values are not set in the prefs.
1298 DCHECK_EQ(nqe::internal::InvalidRTT(),
1299 it.second.network_quality().http_rtt());
1300 DCHECK_EQ(nqe::internal::InvalidRTT(),
1301 it.second.network_quality().transport_rtt());
1302 DCHECK_EQ(nqe::internal::INVALID_RTT_THROUGHPUT,
1303 it.second.network_quality().downstream_throughput_kbps());
1304
1305 nqe::internal::CachedNetworkQuality cached_network_quality(
1306 tick_clock_->NowTicks(),
1307 params_->TypicalNetworkQuality(effective_connection_type),
1308 effective_connection_type);
1309
1310 network_quality_store_->Add(it.first, cached_network_quality);
1311 }
1312 ReadCachedNetworkQualityEstimate();
1313 }
1314
1315 #if BUILDFLAG(IS_CHROMEOS_ASH)
EnableGetNetworkIdAsynchronously()1316 void NetworkQualityEstimator::EnableGetNetworkIdAsynchronously() {
1317 get_network_id_asynchronously_ = true;
1318 }
1319 #endif // BUILDFLAG(IS_CHROMEOS_ASH)
1320
GetHttpRTT() const1321 absl::optional<base::TimeDelta> NetworkQualityEstimator::GetHttpRTT() const {
1322 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1323
1324 if (network_quality_.http_rtt() == nqe::internal::InvalidRTT())
1325 return absl::optional<base::TimeDelta>();
1326 return network_quality_.http_rtt();
1327 }
1328
GetTransportRTT() const1329 absl::optional<base::TimeDelta> NetworkQualityEstimator::GetTransportRTT()
1330 const {
1331 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1332
1333 if (network_quality_.transport_rtt() == nqe::internal::InvalidRTT())
1334 return absl::optional<base::TimeDelta>();
1335 return network_quality_.transport_rtt();
1336 }
1337
GetDownstreamThroughputKbps() const1338 absl::optional<int32_t> NetworkQualityEstimator::GetDownstreamThroughputKbps()
1339 const {
1340 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1341
1342 if (network_quality_.downstream_throughput_kbps() ==
1343 nqe::internal::INVALID_RTT_THROUGHPUT) {
1344 return absl::optional<int32_t>();
1345 }
1346 return network_quality_.downstream_throughput_kbps();
1347 }
1348
MaybeUpdateCachedEstimateApplied(const Observation & observation,ObservationBuffer * buffer)1349 void NetworkQualityEstimator::MaybeUpdateCachedEstimateApplied(
1350 const Observation& observation,
1351 ObservationBuffer* buffer) {
1352 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1353 if (observation.source() !=
1354 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_CACHED_ESTIMATE &&
1355 observation.source() !=
1356 NETWORK_QUALITY_OBSERVATION_SOURCE_TRANSPORT_CACHED_ESTIMATE) {
1357 return;
1358 }
1359
1360 cached_estimate_applied_ = true;
1361 bool deleted_observation_sources[NETWORK_QUALITY_OBSERVATION_SOURCE_MAX] = {
1362 false};
1363 deleted_observation_sources
1364 [NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_HTTP_FROM_PLATFORM] = true;
1365 deleted_observation_sources
1366 [NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_TRANSPORT_FROM_PLATFORM] =
1367 true;
1368
1369 buffer->RemoveObservationsWithSource(deleted_observation_sources);
1370 }
1371
ShouldAddObservation(const Observation & observation) const1372 bool NetworkQualityEstimator::ShouldAddObservation(
1373 const Observation& observation) const {
1374 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1375
1376 if (cached_estimate_applied_ &&
1377 (observation.source() ==
1378 NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_HTTP_FROM_PLATFORM ||
1379 observation.source() ==
1380 NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_TRANSPORT_FROM_PLATFORM)) {
1381 return false;
1382 }
1383 return true;
1384 }
1385
ShouldSocketWatcherNotifyRTT(base::TimeTicks now)1386 bool NetworkQualityEstimator::ShouldSocketWatcherNotifyRTT(
1387 base::TimeTicks now) {
1388 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1389 return (now - last_socket_watcher_rtt_notification_ >=
1390 params_->socket_watchers_min_notification_interval());
1391 }
1392
SimulateNetworkQualityChangeForTesting(net::EffectiveConnectionType type)1393 void NetworkQualityEstimator::SimulateNetworkQualityChangeForTesting(
1394 net::EffectiveConnectionType type) {
1395 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1396 params_->SetForcedEffectiveConnectionTypeForTesting(type);
1397 ComputeEffectiveConnectionType();
1398 }
1399
ForceReportWifiAsSlow2GForTesting()1400 void NetworkQualityEstimator::ForceReportWifiAsSlow2GForTesting() {
1401 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1402 force_report_wifi_as_slow_2g_for_testing_ = true;
1403 }
1404
RecordSpdyPingLatency(const HostPortPair & host_port_pair,base::TimeDelta rtt)1405 void NetworkQualityEstimator::RecordSpdyPingLatency(
1406 const HostPortPair& host_port_pair,
1407 base::TimeDelta rtt) {
1408 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1409 DCHECK_LT(nqe::internal::INVALID_RTT_THROUGHPUT, rtt.InMilliseconds());
1410
1411 Observation observation(rtt.InMilliseconds(), tick_clock_->NowTicks(),
1412 current_network_id_.signal_strength,
1413 NETWORK_QUALITY_OBSERVATION_SOURCE_H2_PINGS);
1414 AddAndNotifyObserversOfRTT(observation);
1415 }
1416
OnPeerToPeerConnectionsCountChange(uint32_t count)1417 void NetworkQualityEstimator::OnPeerToPeerConnectionsCountChange(
1418 uint32_t count) {
1419 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1420
1421 if (p2p_connections_count_ == count)
1422 return;
1423
1424 p2p_connections_count_ = count;
1425
1426 for (auto& observer : peer_to_peer_type_observer_list_) {
1427 observer.OnPeerToPeerConnectionsCountChange(p2p_connections_count_);
1428 }
1429 }
1430
GetPeerToPeerConnectionsCountChange() const1431 uint32_t NetworkQualityEstimator::GetPeerToPeerConnectionsCountChange() const {
1432 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1433 return p2p_connections_count_;
1434 }
1435
1436 } // namespace net
1437