1 // Copyright 2015 The Chromium Authors. All rights reserved.
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 <utility>
11
12 #include "base/bind.h"
13 #include "base/callback_helpers.h"
14 #include "base/check_op.h"
15 #include "base/location.h"
16 #include "base/metrics/field_trial_params.h"
17 #include "base/metrics/histogram.h"
18 #include "base/metrics/histogram_base.h"
19 #include "base/metrics/histogram_functions.h"
20 #include "base/metrics/histogram_macros.h"
21 #include "base/metrics/histogram_macros_local.h"
22 #include "base/notreached.h"
23 #include "base/single_thread_task_runner.h"
24 #include "base/stl_util.h"
25 #include "base/strings/string_number_conversions.h"
26 #include "base/strings/string_piece.h"
27 #include "base/strings/stringprintf.h"
28 #include "base/task/lazy_thread_pool_task_runner.h"
29 #include "base/threading/thread_task_runner_handle.h"
30 #include "base/time/default_tick_clock.h"
31 #include "base/trace_event/trace_event.h"
32 #include "build/build_config.h"
33 #include "net/base/features.h"
34 #include "net/base/host_port_pair.h"
35 #include "net/base/load_flags.h"
36 #include "net/base/load_timing_info.h"
37 #include "net/base/network_interfaces.h"
38 #include "net/base/trace_constants.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/connectivity_monitor.h"
43 #include "net/nqe/network_quality_estimator_util.h"
44 #include "net/nqe/throughput_analyzer.h"
45 #include "net/nqe/weighted_observation.h"
46 #include "net/url_request/url_request.h"
47 #include "net/url_request/url_request_context.h"
48 #include "url/gurl.h"
49
50 #if defined(OS_ANDROID)
51 #include "net/android/cellular_signal_strength.h"
52 #include "net/android/network_library.h"
53 #endif // OS_ANDROID
54
55 namespace net {
56
57 class HostResolver;
58
59 namespace {
60
61 #if defined(OS_CHROMEOS)
62 // SequencedTaskRunner to get the network id. A SequencedTaskRunner is used
63 // rather than parallel tasks to avoid having many threads getting the network
64 // id concurrently.
65 base::LazyThreadPoolSequencedTaskRunner g_get_network_id_task_runner =
66 LAZY_THREAD_POOL_SEQUENCED_TASK_RUNNER_INITIALIZER(
67 base::TaskTraits(base::MayBlock(),
68 base::TaskPriority::BEST_EFFORT,
69 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN));
70 #endif
71
ProtocolSourceToObservationSource(SocketPerformanceWatcherFactory::Protocol protocol)72 NetworkQualityObservationSource ProtocolSourceToObservationSource(
73 SocketPerformanceWatcherFactory::Protocol protocol) {
74 switch (protocol) {
75 case SocketPerformanceWatcherFactory::PROTOCOL_TCP:
76 return NETWORK_QUALITY_OBSERVATION_SOURCE_TCP;
77 case SocketPerformanceWatcherFactory::PROTOCOL_QUIC:
78 return NETWORK_QUALITY_OBSERVATION_SOURCE_QUIC;
79 }
80 NOTREACHED();
81 return NETWORK_QUALITY_OBSERVATION_SOURCE_TCP;
82 }
83
84 // Returns true if the scheme of the |request| is either HTTP or HTTPS.
RequestSchemeIsHTTPOrHTTPS(const URLRequest & request)85 bool RequestSchemeIsHTTPOrHTTPS(const URLRequest& request) {
86 return request.url().is_valid() && request.url().SchemeIsHTTPOrHTTPS();
87 }
88
DoGetCurrentNetworkID(NetworkQualityEstimatorParams * params)89 nqe::internal::NetworkID DoGetCurrentNetworkID(
90 NetworkQualityEstimatorParams* params) {
91 // It is possible that the connection type changed between when
92 // GetConnectionType() was called and when the API to determine the
93 // network name was called. Check if that happened and retry until the
94 // connection type stabilizes. This is an imperfect solution but should
95 // capture majority of cases, and should not significantly affect estimates
96 // (that are approximate to begin with).
97 while (true) {
98 nqe::internal::NetworkID network_id(
99 NetworkChangeNotifier::GetConnectionType(), std::string(), INT32_MIN);
100
101 if (!params || !params->get_signal_strength_and_detailed_network_id())
102 return network_id;
103
104 switch (network_id.type) {
105 case NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN:
106 case NetworkChangeNotifier::ConnectionType::CONNECTION_NONE:
107 case NetworkChangeNotifier::ConnectionType::CONNECTION_BLUETOOTH:
108 case NetworkChangeNotifier::ConnectionType::CONNECTION_ETHERNET:
109 break;
110 case NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI:
111 #if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD) || \
112 defined(OS_WIN)
113 network_id.id = GetWifiSSID();
114 #endif
115 break;
116 case NetworkChangeNotifier::ConnectionType::CONNECTION_2G:
117 case NetworkChangeNotifier::ConnectionType::CONNECTION_3G:
118 case NetworkChangeNotifier::ConnectionType::CONNECTION_4G:
119 case NetworkChangeNotifier::ConnectionType::CONNECTION_5G:
120 #if defined(OS_ANDROID)
121 network_id.id = android::GetTelephonyNetworkOperator();
122 #endif
123 break;
124 default:
125 NOTREACHED() << "Unexpected connection type = " << network_id.type;
126 break;
127 }
128
129 if (network_id.type == NetworkChangeNotifier::GetConnectionType())
130 return network_id;
131 }
132 NOTREACHED();
133 }
134
135 } // namespace
136
NetworkQualityEstimator(std::unique_ptr<NetworkQualityEstimatorParams> params,NetLog * net_log)137 NetworkQualityEstimator::NetworkQualityEstimator(
138 std::unique_ptr<NetworkQualityEstimatorParams> params,
139 NetLog* net_log)
140 : params_(std::move(params)),
141 end_to_end_rtt_observation_count_at_last_ect_computation_(0),
142 use_localhost_requests_(false),
143 disable_offline_check_(false),
144 tick_clock_(base::DefaultTickClock::GetInstance()),
145 last_connection_change_(tick_clock_->NowTicks()),
146 current_network_id_(nqe::internal::NetworkID(
147 NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN,
148 std::string(),
149 INT32_MIN)),
150 http_downstream_throughput_kbps_observations_(
151 params_.get(),
152 tick_clock_,
153 params_->weight_multiplier_per_second(),
154 params_->weight_multiplier_per_signal_strength_level()),
155 rtt_ms_observations_{
156 ObservationBuffer(
157 params_.get(),
158 tick_clock_,
159 params_->weight_multiplier_per_second(),
160 params_->weight_multiplier_per_signal_strength_level()),
161 ObservationBuffer(
162 params_.get(),
163 tick_clock_,
164 params_->weight_multiplier_per_second(),
165 params_->weight_multiplier_per_signal_strength_level()),
166 ObservationBuffer(
167 params_.get(),
168 tick_clock_,
169 params_->weight_multiplier_per_second(),
170 params_->weight_multiplier_per_signal_strength_level())},
171 effective_connection_type_at_last_main_frame_(
172 EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
173 queueing_delay_update_interval_(base::TimeDelta::FromMilliseconds(2000)),
174 effective_connection_type_recomputation_interval_(
175 base::TimeDelta::FromSeconds(10)),
176 rtt_observations_size_at_last_ect_computation_(0),
177 throughput_observations_size_at_last_ect_computation_(0),
178 transport_rtt_observation_count_last_ect_computation_(0),
179 new_rtt_observations_since_last_ect_computation_(0),
180 new_throughput_observations_since_last_ect_computation_(0),
181 network_congestion_analyzer_(this, tick_clock_),
182 effective_connection_type_(EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
183 cached_estimate_applied_(false),
184 net_log_(NetLogWithSource::Make(
185 net_log,
186 net::NetLogSourceType::NETWORK_QUALITY_ESTIMATOR)),
187 event_creator_(net_log_),
188 connectivity_monitor_(std::make_unique<ConnectivityMonitor>()) {
189 DCHECK_EQ(nqe::internal::OBSERVATION_CATEGORY_COUNT,
190 base::size(rtt_ms_observations_));
191
192 AddEffectiveConnectionTypeObserver(&network_congestion_analyzer_);
193 network_quality_store_.reset(new nqe::internal::NetworkQualityStore());
194 NetworkChangeNotifier::AddConnectionTypeObserver(this);
195 throughput_analyzer_.reset(new nqe::internal::ThroughputAnalyzer(
196 this, params_.get(), base::ThreadTaskRunnerHandle::Get(),
197 base::BindRepeating(
198 &NetworkQualityEstimator::OnNewThroughputObservationAvailable,
199 weak_ptr_factory_.GetWeakPtr()),
200 tick_clock_, net_log_));
201
202 watcher_factory_.reset(new nqe::internal::SocketWatcherFactory(
203 base::ThreadTaskRunnerHandle::Get(),
204 params_->min_socket_watcher_notification_interval(),
205 // OnUpdatedTransportRTTAvailable() may be called via PostTask() by
206 // socket watchers that live on a different thread than the current thread
207 // (i.e., base::ThreadTaskRunnerHandle::Get()).
208 // Use WeakPtr() to avoid crashes where the socket watcher is destroyed
209 // after |this| is destroyed.
210 base::BindRepeating(
211 &NetworkQualityEstimator::OnUpdatedTransportRTTAvailable,
212 weak_ptr_factory_.GetWeakPtr()),
213 // ShouldSocketWatcherNotifyRTT() below is called by only the socket
214 // watchers that live on the same thread as the current thread
215 // (i.e., base::ThreadTaskRunnerHandle::Get()). Also, network quality
216 // estimator is destroyed after network contexts and URLRequestContexts.
217 // It's safe to use base::Unretained() below since the socket watcher
218 // (owned by sockets) would be destroyed before |this|.
219 base::BindRepeating(
220 &NetworkQualityEstimator::ShouldSocketWatcherNotifyRTT,
221 base::Unretained(this)),
222 tick_clock_));
223
224 GatherEstimatesForNextConnectionType();
225 }
226
AddDefaultEstimates()227 void NetworkQualityEstimator::AddDefaultEstimates() {
228 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
229
230 if (!params_->add_default_platform_observations())
231 return;
232
233 if (params_->DefaultObservation(current_network_id_.type).http_rtt() !=
234 nqe::internal::InvalidRTT()) {
235 Observation rtt_observation(
236 params_->DefaultObservation(current_network_id_.type)
237 .http_rtt()
238 .InMilliseconds(),
239 tick_clock_->NowTicks(), INT32_MIN,
240 NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_HTTP_FROM_PLATFORM);
241 AddAndNotifyObserversOfRTT(rtt_observation);
242 }
243
244 if (params_->DefaultObservation(current_network_id_.type).transport_rtt() !=
245 nqe::internal::InvalidRTT()) {
246 Observation rtt_observation(
247 params_->DefaultObservation(current_network_id_.type)
248 .transport_rtt()
249 .InMilliseconds(),
250 tick_clock_->NowTicks(), INT32_MIN,
251 NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_TRANSPORT_FROM_PLATFORM);
252 AddAndNotifyObserversOfRTT(rtt_observation);
253 }
254
255 if (params_->DefaultObservation(current_network_id_.type)
256 .downstream_throughput_kbps() !=
257 nqe::internal::INVALID_RTT_THROUGHPUT) {
258 Observation throughput_observation(
259 params_->DefaultObservation(current_network_id_.type)
260 .downstream_throughput_kbps(),
261 tick_clock_->NowTicks(), INT32_MIN,
262 NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_HTTP_FROM_PLATFORM);
263 AddAndNotifyObserversOfThroughput(throughput_observation);
264 }
265 }
266
~NetworkQualityEstimator()267 NetworkQualityEstimator::~NetworkQualityEstimator() {
268 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
269 NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
270 RemoveEffectiveConnectionTypeObserver(&network_congestion_analyzer_);
271 }
272
NotifyStartTransaction(const URLRequest & request)273 void NetworkQualityEstimator::NotifyStartTransaction(
274 const URLRequest& request) {
275 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
276
277 if (!RequestSchemeIsHTTPOrHTTPS(request))
278 return;
279
280 // Update |estimated_quality_at_last_main_frame_| if this is a main frame
281 // request.
282 // TODO(tbansal): Refactor this to a separate method.
283 if (request.load_flags() & LOAD_MAIN_FRAME_DEPRECATED) {
284 base::TimeTicks now = tick_clock_->NowTicks();
285 last_main_frame_request_ = now;
286
287 ComputeEffectiveConnectionType();
288 effective_connection_type_at_last_main_frame_ = effective_connection_type_;
289 estimated_quality_at_last_main_frame_ = network_quality_;
290 } else {
291 MaybeComputeEffectiveConnectionType();
292 }
293 throughput_analyzer_->NotifyStartTransaction(request);
294 network_congestion_analyzer_.NotifyStartTransaction(request);
295 connectivity_monitor_->TrackNewRequest(request);
296 }
297
IsHangingRequest(base::TimeDelta observed_http_rtt) const298 bool NetworkQualityEstimator::IsHangingRequest(
299 base::TimeDelta observed_http_rtt) const {
300 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
301
302 // If there are sufficient number of end to end RTT samples available, use
303 // the end to end RTT estimate to determine if the request is hanging.
304 // If |observed_http_rtt| is within a fixed multiplier of |end_to_end_rtt_|,
305 // then |observed_http_rtt| is determined to be not a hanging-request RTT.
306 if (params_->use_end_to_end_rtt() && end_to_end_rtt_.has_value() &&
307 end_to_end_rtt_observation_count_at_last_ect_computation_ >=
308 params_->http_rtt_transport_rtt_min_count() &&
309 params_->hanging_request_http_rtt_upper_bound_transport_rtt_multiplier() >
310 0 &&
311 observed_http_rtt <
312 params_->hanging_request_http_rtt_upper_bound_transport_rtt_multiplier() *
313 end_to_end_rtt_.value()) {
314 return false;
315 }
316
317 DCHECK_LT(
318 0,
319 params_->hanging_request_http_rtt_upper_bound_transport_rtt_multiplier());
320
321 if (transport_rtt_observation_count_last_ect_computation_ >=
322 params_->http_rtt_transport_rtt_min_count() &&
323 (observed_http_rtt <
324 params_->hanging_request_http_rtt_upper_bound_transport_rtt_multiplier() *
325 GetTransportRTT().value_or(base::TimeDelta::FromSeconds(10)))) {
326 // If there are sufficient number of transport RTT samples available, use
327 // the transport RTT estimate to determine if the request is hanging.
328 return false;
329 }
330
331 DCHECK_LT(
332 0, params_->hanging_request_http_rtt_upper_bound_http_rtt_multiplier());
333
334 if (observed_http_rtt <
335 params_->hanging_request_http_rtt_upper_bound_http_rtt_multiplier() *
336 GetHttpRTT().value_or(base::TimeDelta::FromSeconds(10))) {
337 // Use the HTTP RTT estimate to determine if the request is hanging.
338 return false;
339 }
340
341 if (observed_http_rtt <=
342 params_->hanging_request_upper_bound_min_http_rtt()) {
343 return false;
344 }
345 return true;
346 }
347
NotifyHeadersReceived(const URLRequest & request,int64_t prefilter_total_bytes_read)348 void NetworkQualityEstimator::NotifyHeadersReceived(
349 const URLRequest& request,
350 int64_t prefilter_total_bytes_read) {
351 TRACE_EVENT0(NetTracingCategory(),
352 "NetworkQualityEstimator::NotifyHeadersReceived");
353 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
354
355 if (!RequestSchemeIsHTTPOrHTTPS(request) ||
356 !RequestProvidesRTTObservation(request)) {
357 return;
358 }
359
360 if (request.load_flags() & LOAD_MAIN_FRAME_DEPRECATED) {
361 ComputeEffectiveConnectionType();
362 RecordMetricsOnMainFrameRequest();
363 }
364
365 LoadTimingInfo load_timing_info;
366 request.GetLoadTimingInfo(&load_timing_info);
367
368 // If the load timing info is unavailable, it probably means that the request
369 // did not go over the network.
370 if (load_timing_info.send_start.is_null() ||
371 load_timing_info.receive_headers_end.is_null()) {
372 return;
373 }
374 DCHECK(!request.response_info().was_cached);
375
376 // Duration between when the resource was requested and when the response
377 // headers were received.
378 const base::TimeDelta observed_http_rtt =
379 load_timing_info.receive_headers_end - load_timing_info.send_start;
380 if (observed_http_rtt <= base::TimeDelta())
381 return;
382 DCHECK_GE(observed_http_rtt, base::TimeDelta());
383
384 if (IsHangingRequest(observed_http_rtt))
385 return;
386
387 Observation http_rtt_observation(observed_http_rtt.InMilliseconds(),
388 tick_clock_->NowTicks(),
389 current_network_id_.signal_strength,
390 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP);
391 AddAndNotifyObserversOfRTT(http_rtt_observation);
392 throughput_analyzer_->NotifyBytesRead(request);
393 throughput_analyzer_->NotifyExpectedResponseContentSize(
394 request, request.GetExpectedContentSize());
395 connectivity_monitor_->NotifyRequestProgress(request);
396 }
397
NotifyBytesRead(const URLRequest & request,int64_t prefilter_total_bytes_read)398 void NetworkQualityEstimator::NotifyBytesRead(
399 const URLRequest& request,
400 int64_t prefilter_total_bytes_read) {
401 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
402 throughput_analyzer_->NotifyBytesRead(request);
403 connectivity_monitor_->NotifyRequestProgress(request);
404 }
405
NotifyRequestCompleted(const URLRequest & request)406 void NetworkQualityEstimator::NotifyRequestCompleted(
407 const URLRequest& request) {
408 TRACE_EVENT0(NetTracingCategory(),
409 "NetworkQualityEstimator::NotifyRequestCompleted");
410 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
411
412 if (!RequestSchemeIsHTTPOrHTTPS(request))
413 return;
414
415 throughput_analyzer_->NotifyRequestCompleted(request);
416 network_congestion_analyzer_.NotifyRequestCompleted(request);
417 connectivity_monitor_->NotifyRequestCompleted(request);
418 }
419
NotifyURLRequestDestroyed(const URLRequest & request)420 void NetworkQualityEstimator::NotifyURLRequestDestroyed(
421 const URLRequest& request) {
422 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
423
424 if (!RequestSchemeIsHTTPOrHTTPS(request))
425 return;
426
427 throughput_analyzer_->NotifyRequestCompleted(request);
428 connectivity_monitor_->NotifyRequestCompleted(request);
429 }
430
AddRTTObserver(RTTObserver * rtt_observer)431 void NetworkQualityEstimator::AddRTTObserver(RTTObserver* rtt_observer) {
432 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
433 rtt_observer_list_.AddObserver(rtt_observer);
434 }
435
RemoveRTTObserver(RTTObserver * rtt_observer)436 void NetworkQualityEstimator::RemoveRTTObserver(RTTObserver* rtt_observer) {
437 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
438 rtt_observer_list_.RemoveObserver(rtt_observer);
439 }
440
AddThroughputObserver(ThroughputObserver * throughput_observer)441 void NetworkQualityEstimator::AddThroughputObserver(
442 ThroughputObserver* throughput_observer) {
443 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
444 throughput_observer_list_.AddObserver(throughput_observer);
445 }
446
RemoveThroughputObserver(ThroughputObserver * throughput_observer)447 void NetworkQualityEstimator::RemoveThroughputObserver(
448 ThroughputObserver* throughput_observer) {
449 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
450 throughput_observer_list_.RemoveObserver(throughput_observer);
451 }
452
453 SocketPerformanceWatcherFactory*
GetSocketPerformanceWatcherFactory()454 NetworkQualityEstimator::GetSocketPerformanceWatcherFactory() {
455 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
456
457 return watcher_factory_.get();
458 }
459
SetUseLocalHostRequestsForTesting(bool use_localhost_requests)460 void NetworkQualityEstimator::SetUseLocalHostRequestsForTesting(
461 bool use_localhost_requests) {
462 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
463 use_localhost_requests_ = use_localhost_requests;
464 watcher_factory_->SetUseLocalHostRequestsForTesting(use_localhost_requests_);
465 throughput_analyzer_->SetUseLocalHostRequestsForTesting(
466 use_localhost_requests_);
467 }
468
SetUseSmallResponsesForTesting(bool use_small_responses)469 void NetworkQualityEstimator::SetUseSmallResponsesForTesting(
470 bool use_small_responses) {
471 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
472 params_->SetUseSmallResponsesForTesting(use_small_responses);
473 }
474
DisableOfflineCheckForTesting(bool disable_offline_check)475 void NetworkQualityEstimator::DisableOfflineCheckForTesting(
476 bool disable_offline_check) {
477 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
478 disable_offline_check_ = disable_offline_check;
479 }
480
ReportEffectiveConnectionTypeForTesting(EffectiveConnectionType effective_connection_type)481 void NetworkQualityEstimator::ReportEffectiveConnectionTypeForTesting(
482 EffectiveConnectionType effective_connection_type) {
483 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
484
485 event_creator_.MaybeAddNetworkQualityChangedEventToNetLog(
486 effective_connection_type_,
487 params_->TypicalNetworkQuality(effective_connection_type));
488
489 for (auto& observer : effective_connection_type_observer_list_)
490 observer.OnEffectiveConnectionTypeChanged(effective_connection_type);
491
492 network_quality_store_->Add(current_network_id_,
493 nqe::internal::CachedNetworkQuality(
494 tick_clock_->NowTicks(), network_quality_,
495 effective_connection_type));
496 }
497
ReportRTTsAndThroughputForTesting(base::TimeDelta http_rtt,base::TimeDelta transport_rtt,int32_t downstream_throughput_kbps)498 void NetworkQualityEstimator::ReportRTTsAndThroughputForTesting(
499 base::TimeDelta http_rtt,
500 base::TimeDelta transport_rtt,
501 int32_t downstream_throughput_kbps) {
502 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
503
504 for (auto& observer : rtt_and_throughput_estimates_observer_list_)
505 observer.OnRTTOrThroughputEstimatesComputed(http_rtt, transport_rtt,
506 downstream_throughput_kbps);
507 }
508
RequestProvidesRTTObservation(const URLRequest & request) const509 bool NetworkQualityEstimator::RequestProvidesRTTObservation(
510 const URLRequest& request) const {
511 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
512
513 bool private_network_request =
514 nqe::internal::IsRequestForPrivateHost(request);
515
516 return (use_localhost_requests_ || !private_network_request) &&
517 // Verify that response headers are received, so it can be ensured that
518 // response is not cached.
519 !request.response_info().response_time.is_null() &&
520 !request.was_cached() &&
521 request.creation_time() >= last_connection_change_ &&
522 request.method() == "GET";
523 }
524
OnConnectionTypeChanged(NetworkChangeNotifier::ConnectionType type)525 void NetworkQualityEstimator::OnConnectionTypeChanged(
526 NetworkChangeNotifier::ConnectionType type) {
527 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
528
529 // It's possible that |type| has the same value as |current_network_id_.type|.
530 // This can happen if the device switches from one WiFi SSID to another.
531
532 DCHECK_EQ(nqe::internal::OBSERVATION_CATEGORY_COUNT,
533 base::size(rtt_ms_observations_));
534
535 // Write the estimates of the previous network to the cache.
536 network_quality_store_->Add(
537 current_network_id_, nqe::internal::CachedNetworkQuality(
538 last_effective_connection_type_computation_,
539 network_quality_, effective_connection_type_));
540
541 // Clear the local state.
542 last_connection_change_ = tick_clock_->NowTicks();
543 http_downstream_throughput_kbps_observations_.Clear();
544 for (int i = 0; i < nqe::internal::OBSERVATION_CATEGORY_COUNT; ++i)
545 rtt_ms_observations_[i].Clear();
546
547 #if defined(OS_ANDROID)
548
549 bool is_cell_connection =
550 NetworkChangeNotifier::IsConnectionCellular(current_network_id_.type);
551 bool is_wifi_connection =
552 (current_network_id_.type == NetworkChangeNotifier::CONNECTION_WIFI);
553
554 if (params_->weight_multiplier_per_signal_strength_level() < 1.0 &&
555 (is_cell_connection || is_wifi_connection)) {
556 bool signal_strength_available =
557 min_signal_strength_since_connection_change_ &&
558 max_signal_strength_since_connection_change_;
559
560 std::string histogram_name =
561 is_cell_connection ? "NQE.CellularSignalStrength.LevelAvailable"
562 : "NQE.WifiSignalStrength.LevelAvailable";
563
564 base::UmaHistogramBoolean(histogram_name, signal_strength_available);
565 }
566 #endif // OS_ANDROID
567 current_network_id_.signal_strength = INT32_MIN;
568 min_signal_strength_since_connection_change_.reset();
569 max_signal_strength_since_connection_change_.reset();
570 network_quality_ = nqe::internal::NetworkQuality();
571 end_to_end_rtt_ = base::nullopt;
572 effective_connection_type_ = EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
573 effective_connection_type_at_last_main_frame_ =
574 EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
575 rtt_observations_size_at_last_ect_computation_ = 0;
576 throughput_observations_size_at_last_ect_computation_ = 0;
577 new_rtt_observations_since_last_ect_computation_ = 0;
578 new_throughput_observations_since_last_ect_computation_ = 0;
579 transport_rtt_observation_count_last_ect_computation_ = 0;
580 end_to_end_rtt_observation_count_at_last_ect_computation_ = 0;
581 last_socket_watcher_rtt_notification_ = base::TimeTicks();
582 estimated_quality_at_last_main_frame_ = nqe::internal::NetworkQuality();
583 cached_estimate_applied_ = false;
584
585 GatherEstimatesForNextConnectionType();
586 throughput_analyzer_->OnConnectionTypeChanged();
587 connectivity_monitor_->NotifyConnectionTypeChanged(type);
588 }
589
GatherEstimatesForNextConnectionType()590 void NetworkQualityEstimator::GatherEstimatesForNextConnectionType() {
591 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
592
593 #if defined(OS_CHROMEOS)
594 if (get_network_id_asynchronously_) {
595 // Doing PostTaskAndReplyWithResult by handle because it requires the result
596 // type have a default constructor and nqe::internal::NetworkID does not
597 // have that.
598 g_get_network_id_task_runner.Get()->PostTask(
599 FROM_HERE,
600 base::BindOnce(
601 [](scoped_refptr<base::TaskRunner> reply_task_runner,
602 base::OnceCallback<void(const nqe::internal::NetworkID&)>
603 reply_callback) {
604 reply_task_runner->PostTask(
605 FROM_HERE, base::BindOnce(std::move(reply_callback),
606 DoGetCurrentNetworkID(nullptr)));
607 },
608 base::ThreadTaskRunnerHandle::Get(),
609 base::BindOnce(&NetworkQualityEstimator::
610 ContinueGatherEstimatesForNextConnectionType,
611 weak_ptr_factory_.GetWeakPtr())));
612 return;
613 }
614 #endif // defined(OS_CHROMEOS)
615
616 ContinueGatherEstimatesForNextConnectionType(GetCurrentNetworkID());
617 }
618
ContinueGatherEstimatesForNextConnectionType(const nqe::internal::NetworkID & network_id)619 void NetworkQualityEstimator::ContinueGatherEstimatesForNextConnectionType(
620 const nqe::internal::NetworkID& network_id) {
621 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
622 // Update the local state as part of preparation for the new connection.
623 current_network_id_ = network_id;
624 RecordNetworkIDAvailability();
625
626 // Read any cached estimates for the new network. If cached estimates are
627 // unavailable, add the default estimates.
628 if (!ReadCachedNetworkQualityEstimate())
629 AddDefaultEstimates();
630
631 ComputeEffectiveConnectionType();
632 }
633
634 base::Optional<int32_t>
GetCurrentSignalStrengthWithThrottling()635 NetworkQualityEstimator::GetCurrentSignalStrengthWithThrottling() {
636 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
637
638 if (!params_->get_signal_strength_and_detailed_network_id())
639 return base::nullopt;
640
641 if (params_->weight_multiplier_per_signal_strength_level() >= 1.0)
642 return base::nullopt;
643
644 if ((current_network_id_.type != NetworkChangeNotifier::CONNECTION_WIFI) &&
645 !NetworkChangeNotifier::IsConnectionCellular(current_network_id_.type)) {
646 return base::nullopt;
647 }
648
649 // Do not call more than once per |wifi_signal_strength_query_interval|
650 // duration.
651 if (last_signal_strength_check_timestamp_.has_value() &&
652 (tick_clock_->NowTicks() - last_signal_strength_check_timestamp_.value() <
653 params_->wifi_signal_strength_query_interval()) &&
654 (last_signal_strength_check_timestamp_.value() >
655 last_connection_change_)) {
656 return base::nullopt;
657 }
658
659 last_signal_strength_check_timestamp_ = tick_clock_->NowTicks();
660
661 if (current_network_id_.type == NetworkChangeNotifier::CONNECTION_WIFI) {
662 UMA_HISTOGRAM_BOOLEAN("NQE.SignalStrengthQueried.WiFi", true);
663
664 #if defined(OS_ANDROID)
665 return android::GetWifiSignalLevel();
666 #endif // OS_ANDROID
667 }
668
669 if (NetworkChangeNotifier::IsConnectionCellular(current_network_id_.type)) {
670 UMA_HISTOGRAM_BOOLEAN("NQE.SignalStrengthQueried.Cellular", true);
671 #if defined(OS_ANDROID)
672 return android::cellular_signal_strength::GetSignalStrengthLevel();
673 #endif // OS_ANDROID
674 }
675
676 return base::nullopt;
677 }
678
UpdateSignalStrength()679 void NetworkQualityEstimator::UpdateSignalStrength() {
680 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
681
682 int32_t past_signal_strength = current_network_id_.signal_strength;
683 base::Optional<int32_t> new_signal_strength =
684 GetCurrentSignalStrengthWithThrottling();
685
686 // A fresh value is unavailable. So, return early.
687 if (!new_signal_strength)
688 return;
689
690 // Check if there is no change in the signal strength.
691 if (past_signal_strength == new_signal_strength.value())
692 return;
693
694 // Check if the signal strength is unavailable.
695 if (new_signal_strength.value() == INT32_MIN)
696 return;
697
698 DCHECK(new_signal_strength.value() >= 0 && new_signal_strength.value() <= 4);
699
700 // Record the network quality we experienced for the previous signal strength
701 // (for when we return to that signal strength).
702 network_quality_store_->Add(current_network_id_,
703 nqe::internal::CachedNetworkQuality(
704 tick_clock_->NowTicks(), network_quality_,
705 effective_connection_type_));
706
707 current_network_id_.signal_strength = new_signal_strength.value();
708 // Update network quality from cached value for new signal strength.
709 ReadCachedNetworkQualityEstimate();
710
711 min_signal_strength_since_connection_change_ =
712 std::min(min_signal_strength_since_connection_change_.value_or(INT32_MAX),
713 current_network_id_.signal_strength);
714 max_signal_strength_since_connection_change_ =
715 std::max(max_signal_strength_since_connection_change_.value_or(INT32_MIN),
716 current_network_id_.signal_strength);
717 }
718
RecordNetworkIDAvailability() const719 void NetworkQualityEstimator::RecordNetworkIDAvailability() const {
720 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
721 if (current_network_id_.type ==
722 NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI ||
723 NetworkChangeNotifier::IsConnectionCellular(current_network_id_.type)) {
724 UMA_HISTOGRAM_BOOLEAN("NQE.NetworkIdAvailable",
725 !current_network_id_.id.empty());
726 }
727 }
728
RecordMetricsOnMainFrameRequest() const729 void NetworkQualityEstimator::RecordMetricsOnMainFrameRequest() const {
730 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
731
732 if (estimated_quality_at_last_main_frame_.http_rtt() !=
733 nqe::internal::InvalidRTT()) {
734 // Add the 50th percentile value.
735 LOCAL_HISTOGRAM_TIMES("NQE.MainFrame.RTT.Percentile50",
736 estimated_quality_at_last_main_frame_.http_rtt());
737 }
738
739 if (estimated_quality_at_last_main_frame_.transport_rtt() !=
740 nqe::internal::InvalidRTT()) {
741 // Add the 50th percentile value.
742 LOCAL_HISTOGRAM_TIMES(
743 "NQE.MainFrame.TransportRTT.Percentile50",
744 estimated_quality_at_last_main_frame_.transport_rtt());
745 }
746
747 if (estimated_quality_at_last_main_frame_.downstream_throughput_kbps() !=
748 nqe::internal::INVALID_RTT_THROUGHPUT) {
749 // Add the 50th percentile value.
750 LOCAL_HISTOGRAM_COUNTS_1000000(
751 "NQE.MainFrame.Kbps.Percentile50",
752 estimated_quality_at_last_main_frame_.downstream_throughput_kbps());
753 }
754
755 LOCAL_HISTOGRAM_ENUMERATION("NQE.MainFrame.EffectiveConnectionType",
756 effective_connection_type_at_last_main_frame_,
757 EFFECTIVE_CONNECTION_TYPE_LAST);
758 }
759
ShouldComputeNetworkQueueingDelay() const760 bool NetworkQualityEstimator::ShouldComputeNetworkQueueingDelay() const {
761 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
762
763 const base::TimeTicks now = tick_clock_->NowTicks();
764 // Recomputes the queueing delay estimate if |queueing_delay_update_interval_|
765 // has passed.
766 return (now - last_queueing_delay_computation_ >=
767 queueing_delay_update_interval_);
768 }
769
ComputeNetworkQueueingDelay()770 void NetworkQualityEstimator::ComputeNetworkQueueingDelay() {
771 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
772
773 if (!ShouldComputeNetworkQueueingDelay())
774 return;
775
776 const base::TimeTicks now = tick_clock_->NowTicks();
777 last_queueing_delay_computation_ = now;
778 // The time after which observations are considered as recent data.
779 const base::TimeTicks recent_start_time =
780 now - base::TimeDelta::FromMilliseconds(1000);
781 // The time after which observations are considered as historical data.
782 const base::TimeTicks historical_start_time =
783 now - base::TimeDelta::FromMilliseconds(30000);
784
785 // Checks if a valid downlink throughput estimation is available.
786 int32_t downlink_kbps = 0;
787 if (!GetRecentDownlinkThroughputKbps(recent_start_time, &downlink_kbps))
788 downlink_kbps = nqe::internal::INVALID_RTT_THROUGHPUT;
789
790 // Gets recent RTT statistic values.
791 std::map<nqe::internal::IPHash, nqe::internal::CanonicalStats>
792 recent_rtt_stats =
793 rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT]
794 .GetCanonicalStatsKeyedByHosts(recent_start_time,
795 std::set<nqe::internal::IPHash>());
796
797 if (recent_rtt_stats.empty())
798 return;
799
800 // Gets the set of active hosts. Only computes the historical stats for recent
801 // active hosts.
802 std::set<nqe::internal::IPHash> active_hosts;
803 for (const auto& host_stat : recent_rtt_stats)
804 active_hosts.insert(host_stat.first);
805
806 std::map<nqe::internal::IPHash, nqe::internal::CanonicalStats>
807 historical_rtt_stats =
808 rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT]
809 .GetCanonicalStatsKeyedByHosts(historical_start_time,
810 active_hosts);
811
812 network_congestion_analyzer_.ComputeRecentQueueingDelay(
813 recent_rtt_stats, historical_rtt_stats, downlink_kbps);
814
815 // Gets the total number of inflight requests including hanging GETs. The app
816 // cannot determine whether a request is hanging or is still in the wire.
817 size_t count_inflight_requests =
818 throughput_analyzer_->CountTotalInFlightRequests();
819
820 // Tracks the mapping between the peak observed queueing delay to the peak
821 // count of in-flight requests.
822 network_congestion_analyzer_.UpdatePeakDelayMapping(
823 network_congestion_analyzer_.recent_queueing_delay(),
824 count_inflight_requests);
825 }
826
ComputeEffectiveConnectionType()827 void NetworkQualityEstimator::ComputeEffectiveConnectionType() {
828 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
829
830 UpdateSignalStrength();
831
832 const base::TimeTicks now = tick_clock_->NowTicks();
833
834 const EffectiveConnectionType past_type = effective_connection_type_;
835 last_effective_connection_type_computation_ = now;
836
837 base::TimeDelta http_rtt = nqe::internal::InvalidRTT();
838 base::TimeDelta transport_rtt = nqe::internal::InvalidRTT();
839 base::TimeDelta end_to_end_rtt = nqe::internal::InvalidRTT();
840 int32_t downstream_throughput_kbps = nqe::internal::INVALID_RTT_THROUGHPUT;
841
842 effective_connection_type_ = GetRecentEffectiveConnectionTypeUsingMetrics(
843 &http_rtt, &transport_rtt, &end_to_end_rtt, &downstream_throughput_kbps,
844 &transport_rtt_observation_count_last_ect_computation_,
845 &end_to_end_rtt_observation_count_at_last_ect_computation_);
846
847 network_quality_ = nqe::internal::NetworkQuality(http_rtt, transport_rtt,
848 downstream_throughput_kbps);
849 net::EffectiveConnectionType signal_strength_capped_ect =
850 GetCappedECTBasedOnSignalStrength();
851
852 if (signal_strength_capped_ect != effective_connection_type_) {
853 DCHECK_LE(signal_strength_capped_ect, effective_connection_type_);
854 UMA_HISTOGRAM_EXACT_LINEAR(
855 "NQE.CellularSignalStrength.ECTReduction",
856 effective_connection_type_ - signal_strength_capped_ect,
857 static_cast<int>(EFFECTIVE_CONNECTION_TYPE_LAST));
858
859 effective_connection_type_ = signal_strength_capped_ect;
860
861 // Reset |network_quality_| based on the updated effective connection type.
862 network_quality_ = nqe::internal::NetworkQuality(
863 params_->TypicalNetworkQuality(effective_connection_type_).http_rtt(),
864 params_->TypicalNetworkQuality(effective_connection_type_)
865 .transport_rtt(),
866 params_->TypicalNetworkQuality(effective_connection_type_)
867 .downstream_throughput_kbps());
868 }
869
870 ClampKbpsBasedOnEct();
871
872 UMA_HISTOGRAM_ENUMERATION("NQE.EffectiveConnectionType.OnECTComputation",
873 effective_connection_type_,
874 EFFECTIVE_CONNECTION_TYPE_LAST);
875 if (network_quality_.http_rtt() != nqe::internal::InvalidRTT()) {
876 UMA_HISTOGRAM_TIMES("NQE.RTT.OnECTComputation",
877 network_quality_.http_rtt());
878 }
879
880 if (network_quality_.transport_rtt() != nqe::internal::InvalidRTT()) {
881 UMA_HISTOGRAM_TIMES("NQE.TransportRTT.OnECTComputation",
882 network_quality_.transport_rtt());
883 }
884
885 if (end_to_end_rtt != nqe::internal::InvalidRTT()) {
886 UMA_HISTOGRAM_TIMES("NQE.EndToEndRTT.OnECTComputation", end_to_end_rtt);
887 }
888 end_to_end_rtt_ = base::nullopt;
889 if (end_to_end_rtt != nqe::internal::InvalidRTT())
890 end_to_end_rtt_ = end_to_end_rtt;
891
892 if (network_quality_.downstream_throughput_kbps() !=
893 nqe::internal::INVALID_RTT_THROUGHPUT) {
894 UMA_HISTOGRAM_COUNTS_1M("NQE.Kbps.OnECTComputation",
895 network_quality_.downstream_throughput_kbps());
896 }
897
898 NotifyObserversOfRTTOrThroughputComputed();
899
900 if (past_type != effective_connection_type_)
901 NotifyObserversOfEffectiveConnectionTypeChanged();
902
903 event_creator_.MaybeAddNetworkQualityChangedEventToNetLog(
904 effective_connection_type_, network_quality_);
905
906 rtt_observations_size_at_last_ect_computation_ =
907 rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_HTTP].Size() +
908 rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT]
909 .Size();
910 throughput_observations_size_at_last_ect_computation_ =
911 http_downstream_throughput_kbps_observations_.Size();
912 new_rtt_observations_since_last_ect_computation_ = 0;
913 new_throughput_observations_since_last_ect_computation_ = 0;
914 }
915
916 base::Optional<net::EffectiveConnectionType>
GetOverrideECT() const917 NetworkQualityEstimator::GetOverrideECT() const {
918 return base::nullopt;
919 }
920
ClampKbpsBasedOnEct()921 void NetworkQualityEstimator::ClampKbpsBasedOnEct() {
922 // No need to clamp when ECT is unknown or if the connection speed is fast.
923 if (effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_UNKNOWN ||
924 effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_OFFLINE ||
925 effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_4G) {
926 return;
927 }
928
929 if (params_->upper_bound_typical_kbps_multiplier() <= 0.0)
930 return;
931
932 DCHECK_LT(0, params_->TypicalNetworkQuality(effective_connection_type_)
933 .downstream_throughput_kbps());
934 // For a given ECT, upper bound on Kbps can't be less than the typical Kbps
935 // for that ECT.
936 DCHECK_LE(1.0, params_->upper_bound_typical_kbps_multiplier());
937
938 DCHECK(effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_SLOW_2G ||
939 effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_2G ||
940 effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_3G);
941
942 // Put an upper bound on Kbps.
943 network_quality_.set_downstream_throughput_kbps(
944 std::min(network_quality_.downstream_throughput_kbps(),
945 static_cast<int>(
946 params_->TypicalNetworkQuality(effective_connection_type_)
947 .downstream_throughput_kbps() *
948 params_->upper_bound_typical_kbps_multiplier())));
949 }
950
951 EffectiveConnectionType
GetCappedECTBasedOnSignalStrength() const952 NetworkQualityEstimator::GetCappedECTBasedOnSignalStrength() const {
953 if (!params_->cap_ect_based_on_signal_strength())
954 return effective_connection_type_;
955
956 // Check if signal strength is available.
957 if (current_network_id_.signal_strength == INT32_MIN)
958 return effective_connection_type_;
959
960 if (effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_UNKNOWN ||
961 effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_OFFLINE) {
962 return effective_connection_type_;
963 }
964
965 // Do not cap ECT if the signal strength is high.
966 if (current_network_id_.signal_strength > 2)
967 return effective_connection_type_;
968
969 DCHECK_LE(0, current_network_id_.signal_strength);
970
971 // When signal strength is 0, the device is almost offline.
972 if (current_network_id_.signal_strength == 0) {
973 switch (current_network_id_.type) {
974 case NetworkChangeNotifier::CONNECTION_2G:
975 case NetworkChangeNotifier::CONNECTION_3G:
976 return std::min(effective_connection_type_,
977 EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
978 case NetworkChangeNotifier::CONNECTION_4G:
979 case NetworkChangeNotifier::CONNECTION_5G:
980 case NetworkChangeNotifier::CONNECTION_WIFI:
981 return std::min(effective_connection_type_,
982 EFFECTIVE_CONNECTION_TYPE_2G);
983 default:
984 NOTREACHED();
985 return effective_connection_type_;
986 }
987 }
988
989 if (current_network_id_.signal_strength == 1) {
990 switch (current_network_id_.type) {
991 case NetworkChangeNotifier::CONNECTION_2G:
992 return std::min(effective_connection_type_,
993 EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
994 case NetworkChangeNotifier::CONNECTION_3G:
995 return std::min(effective_connection_type_,
996 EFFECTIVE_CONNECTION_TYPE_2G);
997 case NetworkChangeNotifier::CONNECTION_4G:
998 case NetworkChangeNotifier::CONNECTION_5G:
999 case NetworkChangeNotifier::CONNECTION_WIFI:
1000 return std::min(effective_connection_type_,
1001 EFFECTIVE_CONNECTION_TYPE_3G);
1002 default:
1003 NOTREACHED();
1004 return effective_connection_type_;
1005 }
1006 }
1007
1008 if (current_network_id_.signal_strength == 2) {
1009 switch (current_network_id_.type) {
1010 case NetworkChangeNotifier::CONNECTION_2G:
1011 return std::min(effective_connection_type_,
1012 EFFECTIVE_CONNECTION_TYPE_2G);
1013 case NetworkChangeNotifier::CONNECTION_3G:
1014 return std::min(effective_connection_type_,
1015 EFFECTIVE_CONNECTION_TYPE_3G);
1016 case NetworkChangeNotifier::CONNECTION_4G:
1017 case NetworkChangeNotifier::CONNECTION_5G:
1018 case NetworkChangeNotifier::CONNECTION_WIFI:
1019 return std::min(effective_connection_type_,
1020 EFFECTIVE_CONNECTION_TYPE_4G);
1021 default:
1022 NOTREACHED();
1023 return effective_connection_type_;
1024 }
1025 }
1026 NOTREACHED();
1027 return effective_connection_type_;
1028 }
1029
GetEffectiveConnectionType() const1030 EffectiveConnectionType NetworkQualityEstimator::GetEffectiveConnectionType()
1031 const {
1032 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1033
1034 base::Optional<net::EffectiveConnectionType> override_ect = GetOverrideECT();
1035 if (override_ect) {
1036 return override_ect.value();
1037 }
1038 return effective_connection_type_;
1039 }
1040
UpdateHttpRttUsingAllRttValues(base::TimeDelta * http_rtt,const base::TimeDelta transport_rtt,const base::TimeDelta end_to_end_rtt) const1041 void NetworkQualityEstimator::UpdateHttpRttUsingAllRttValues(
1042 base::TimeDelta* http_rtt,
1043 const base::TimeDelta transport_rtt,
1044 const base::TimeDelta end_to_end_rtt) const {
1045 DCHECK(http_rtt);
1046
1047 // Use transport RTT to clamp the lower bound on HTTP RTT.
1048 // To improve accuracy, the transport RTT estimate is used only when the
1049 // transport RTT estimate was computed using at least
1050 // |params_->http_rtt_transport_rtt_min_count()| observations.
1051 if (*http_rtt != nqe::internal::InvalidRTT() &&
1052 transport_rtt != nqe::internal::InvalidRTT() &&
1053 transport_rtt_observation_count_last_ect_computation_ >=
1054 params_->http_rtt_transport_rtt_min_count() &&
1055 params_->lower_bound_http_rtt_transport_rtt_multiplier() > 0) {
1056 *http_rtt =
1057 std::max(*http_rtt,
1058 transport_rtt *
1059 params_->lower_bound_http_rtt_transport_rtt_multiplier());
1060 }
1061
1062 // Put lower bound on |http_rtt| using |end_to_end_rtt|.
1063 if (*http_rtt != nqe::internal::InvalidRTT() &&
1064 params_->use_end_to_end_rtt() &&
1065 end_to_end_rtt != nqe::internal::InvalidRTT() &&
1066 end_to_end_rtt_observation_count_at_last_ect_computation_ >=
1067 params_->http_rtt_transport_rtt_min_count() &&
1068 params_->lower_bound_http_rtt_transport_rtt_multiplier() > 0) {
1069 *http_rtt =
1070 std::max(*http_rtt,
1071 end_to_end_rtt *
1072 params_->lower_bound_http_rtt_transport_rtt_multiplier());
1073 }
1074
1075 // Put upper bound on |http_rtt| using |end_to_end_rtt|.
1076 if (*http_rtt != nqe::internal::InvalidRTT() &&
1077 params_->use_end_to_end_rtt() &&
1078 end_to_end_rtt != nqe::internal::InvalidRTT() &&
1079 end_to_end_rtt_observation_count_at_last_ect_computation_ >=
1080 params_->http_rtt_transport_rtt_min_count() &&
1081 params_->upper_bound_http_rtt_endtoend_rtt_multiplier() > 0) {
1082 *http_rtt = std::min(
1083 *http_rtt, end_to_end_rtt *
1084 params_->upper_bound_http_rtt_endtoend_rtt_multiplier());
1085 }
1086 }
1087
1088 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) const1089 NetworkQualityEstimator::GetRecentEffectiveConnectionTypeUsingMetrics(
1090 base::TimeDelta* http_rtt,
1091 base::TimeDelta* transport_rtt,
1092 base::TimeDelta* end_to_end_rtt,
1093 int32_t* downstream_throughput_kbps,
1094 size_t* transport_rtt_observation_count,
1095 size_t* end_to_end_rtt_observation_count) const {
1096 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1097
1098 *http_rtt = nqe::internal::InvalidRTT();
1099 *transport_rtt = nqe::internal::InvalidRTT();
1100 *end_to_end_rtt = nqe::internal::InvalidRTT();
1101 *downstream_throughput_kbps = nqe::internal::INVALID_RTT_THROUGHPUT;
1102
1103 auto forced_ect =
1104 params_->GetForcedEffectiveConnectionType(current_network_id_.type);
1105 if (forced_ect) {
1106 *http_rtt = params_->TypicalNetworkQuality(forced_ect.value()).http_rtt();
1107 *transport_rtt =
1108 params_->TypicalNetworkQuality(forced_ect.value()).transport_rtt();
1109 *downstream_throughput_kbps =
1110 params_->TypicalNetworkQuality(forced_ect.value())
1111 .downstream_throughput_kbps();
1112 return forced_ect.value();
1113 }
1114
1115 // If the device is currently offline, then return
1116 // EFFECTIVE_CONNECTION_TYPE_OFFLINE.
1117 if (current_network_id_.type == NetworkChangeNotifier::CONNECTION_NONE &&
1118 !disable_offline_check_) {
1119 return EFFECTIVE_CONNECTION_TYPE_OFFLINE;
1120 }
1121
1122 if (!GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, base::TimeTicks(),
1123 http_rtt, nullptr)) {
1124 *http_rtt = nqe::internal::InvalidRTT();
1125 }
1126
1127 if (!GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_TRANSPORT,
1128 base::TimeTicks(), transport_rtt,
1129 transport_rtt_observation_count)) {
1130 *transport_rtt = nqe::internal::InvalidRTT();
1131 }
1132
1133 if (!GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_END_TO_END,
1134 base::TimeTicks(), end_to_end_rtt,
1135 end_to_end_rtt_observation_count)) {
1136 *end_to_end_rtt = nqe::internal::InvalidRTT();
1137 }
1138
1139 UpdateHttpRttUsingAllRttValues(http_rtt, *transport_rtt, *end_to_end_rtt);
1140
1141 if (!GetRecentDownlinkThroughputKbps(base::TimeTicks(),
1142 downstream_throughput_kbps)) {
1143 *downstream_throughput_kbps = nqe::internal::INVALID_RTT_THROUGHPUT;
1144 }
1145
1146 if (*http_rtt == nqe::internal::InvalidRTT()) {
1147 return EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
1148 }
1149
1150 if (*http_rtt == nqe::internal::InvalidRTT() &&
1151 *transport_rtt == nqe::internal::InvalidRTT() &&
1152 *downstream_throughput_kbps == nqe::internal::INVALID_RTT_THROUGHPUT) {
1153 // None of the metrics are available.
1154 return EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
1155 }
1156
1157 // Search from the slowest connection type to the fastest to find the
1158 // EffectiveConnectionType that best matches the current connection's
1159 // performance. The match is done by comparing RTT and throughput.
1160 for (size_t i = 0; i < EFFECTIVE_CONNECTION_TYPE_LAST; ++i) {
1161 EffectiveConnectionType type = static_cast<EffectiveConnectionType>(i);
1162 if (i == EFFECTIVE_CONNECTION_TYPE_UNKNOWN)
1163 continue;
1164
1165 const bool estimated_http_rtt_is_higher_than_threshold =
1166 *http_rtt != nqe::internal::InvalidRTT() &&
1167 params_->ConnectionThreshold(type).http_rtt() !=
1168 nqe::internal::InvalidRTT() &&
1169 *http_rtt >= params_->ConnectionThreshold(type).http_rtt();
1170
1171 if (estimated_http_rtt_is_higher_than_threshold)
1172 return type;
1173 }
1174 // Return the fastest connection type.
1175 return static_cast<EffectiveConnectionType>(EFFECTIVE_CONNECTION_TYPE_LAST -
1176 1);
1177 }
1178
AddEffectiveConnectionTypeObserver(EffectiveConnectionTypeObserver * observer)1179 void NetworkQualityEstimator::AddEffectiveConnectionTypeObserver(
1180 EffectiveConnectionTypeObserver* observer) {
1181 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1182 DCHECK(observer);
1183 effective_connection_type_observer_list_.AddObserver(observer);
1184
1185 // Notify the |observer| on the next message pump since |observer| may not
1186 // be completely set up for receiving the callbacks.
1187 base::ThreadTaskRunnerHandle::Get()->PostTask(
1188 FROM_HERE,
1189 base::BindOnce(&NetworkQualityEstimator::
1190 NotifyEffectiveConnectionTypeObserverIfPresent,
1191 weak_ptr_factory_.GetWeakPtr(), observer));
1192 }
1193
RemoveEffectiveConnectionTypeObserver(EffectiveConnectionTypeObserver * observer)1194 void NetworkQualityEstimator::RemoveEffectiveConnectionTypeObserver(
1195 EffectiveConnectionTypeObserver* observer) {
1196 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1197 effective_connection_type_observer_list_.RemoveObserver(observer);
1198 }
1199
AddPeerToPeerConnectionsCountObserver(PeerToPeerConnectionsCountObserver * observer)1200 void NetworkQualityEstimator::AddPeerToPeerConnectionsCountObserver(
1201 PeerToPeerConnectionsCountObserver* observer) {
1202 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1203 DCHECK(observer);
1204 peer_to_peer_type_observer_list_.AddObserver(observer);
1205
1206 // Notify the |observer| on the next message pump since |observer| may not
1207 // be completely set up for receiving the callbacks.
1208 base::ThreadTaskRunnerHandle::Get()->PostTask(
1209 FROM_HERE,
1210 base::BindOnce(&NetworkQualityEstimator::
1211 NotifyPeerToPeerConnectionsCountObserverIfPresent,
1212 weak_ptr_factory_.GetWeakPtr(), observer));
1213 }
1214
RemovePeerToPeerConnectionsCountObserver(PeerToPeerConnectionsCountObserver * observer)1215 void NetworkQualityEstimator::RemovePeerToPeerConnectionsCountObserver(
1216 PeerToPeerConnectionsCountObserver* observer) {
1217 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1218 peer_to_peer_type_observer_list_.RemoveObserver(observer);
1219 }
1220
AddRTTAndThroughputEstimatesObserver(RTTAndThroughputEstimatesObserver * observer)1221 void NetworkQualityEstimator::AddRTTAndThroughputEstimatesObserver(
1222 RTTAndThroughputEstimatesObserver* observer) {
1223 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1224 DCHECK(observer);
1225 rtt_and_throughput_estimates_observer_list_.AddObserver(observer);
1226
1227 // Notify the |observer| on the next message pump since |observer| may not
1228 // be completely set up for receiving the callbacks.
1229 base::ThreadTaskRunnerHandle::Get()->PostTask(
1230 FROM_HERE,
1231 base::BindOnce(&NetworkQualityEstimator::
1232 NotifyRTTAndThroughputEstimatesObserverIfPresent,
1233 weak_ptr_factory_.GetWeakPtr(), observer));
1234 }
1235
RemoveRTTAndThroughputEstimatesObserver(RTTAndThroughputEstimatesObserver * observer)1236 void NetworkQualityEstimator::RemoveRTTAndThroughputEstimatesObserver(
1237 RTTAndThroughputEstimatesObserver* observer) {
1238 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1239 rtt_and_throughput_estimates_observer_list_.RemoveObserver(observer);
1240 }
1241
GetRecentRTT(nqe::internal::ObservationCategory observation_category,const base::TimeTicks & start_time,base::TimeDelta * rtt,size_t * observations_count) const1242 bool NetworkQualityEstimator::GetRecentRTT(
1243 nqe::internal::ObservationCategory observation_category,
1244 const base::TimeTicks& start_time,
1245 base::TimeDelta* rtt,
1246 size_t* observations_count) const {
1247 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1248 *rtt = GetRTTEstimateInternal(start_time, observation_category, 50,
1249 observations_count);
1250 return (*rtt != nqe::internal::InvalidRTT());
1251 }
1252
GetRecentDownlinkThroughputKbps(const base::TimeTicks & start_time,int32_t * kbps) const1253 bool NetworkQualityEstimator::GetRecentDownlinkThroughputKbps(
1254 const base::TimeTicks& start_time,
1255 int32_t* kbps) const {
1256 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1257 *kbps = GetDownlinkThroughputKbpsEstimateInternal(start_time, 50);
1258 return (*kbps != nqe::internal::INVALID_RTT_THROUGHPUT);
1259 }
1260
GetRTTEstimateInternal(base::TimeTicks start_time,nqe::internal::ObservationCategory observation_category,int percentile,size_t * observations_count) const1261 base::TimeDelta NetworkQualityEstimator::GetRTTEstimateInternal(
1262 base::TimeTicks start_time,
1263 nqe::internal::ObservationCategory observation_category,
1264 int percentile,
1265 size_t* observations_count) const {
1266 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1267 DCHECK_EQ(nqe::internal::OBSERVATION_CATEGORY_COUNT,
1268 base::size(rtt_ms_observations_));
1269
1270 // RTT observations are sorted by duration from shortest to longest, thus
1271 // a higher percentile RTT will have a longer RTT than a lower percentile.
1272 switch (observation_category) {
1273 case nqe::internal::OBSERVATION_CATEGORY_HTTP:
1274 case nqe::internal::OBSERVATION_CATEGORY_TRANSPORT:
1275 case nqe::internal::OBSERVATION_CATEGORY_END_TO_END:
1276 return base::TimeDelta::FromMilliseconds(
1277 rtt_ms_observations_[observation_category]
1278 .GetPercentile(start_time, current_network_id_.signal_strength,
1279 percentile, observations_count)
1280 .value_or(nqe::internal::INVALID_RTT_THROUGHPUT));
1281 case nqe::internal::OBSERVATION_CATEGORY_COUNT:
1282 NOTREACHED();
1283 return base::TimeDelta();
1284 }
1285 }
1286
GetDownlinkThroughputKbpsEstimateInternal(const base::TimeTicks & start_time,int percentile) const1287 int32_t NetworkQualityEstimator::GetDownlinkThroughputKbpsEstimateInternal(
1288 const base::TimeTicks& start_time,
1289 int percentile) const {
1290 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1291
1292 // Throughput observations are sorted by kbps from slowest to fastest,
1293 // thus a higher percentile throughput will be faster than a lower one.
1294 return http_downstream_throughput_kbps_observations_
1295 .GetPercentile(start_time, current_network_id_.signal_strength,
1296 100 - percentile, nullptr)
1297 .value_or(nqe::internal::INVALID_RTT_THROUGHPUT);
1298 }
1299
GetCurrentNetworkID() const1300 nqe::internal::NetworkID NetworkQualityEstimator::GetCurrentNetworkID() const {
1301 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1302
1303 // TODO(tbansal): crbug.com/498068 Add NetworkQualityEstimatorAndroid class
1304 // that overrides this method on the Android platform.
1305
1306 return DoGetCurrentNetworkID(params_.get());
1307 }
1308
ReadCachedNetworkQualityEstimate()1309 bool NetworkQualityEstimator::ReadCachedNetworkQualityEstimate() {
1310 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1311
1312 if (!params_->persistent_cache_reading_enabled())
1313 return false;
1314
1315 nqe::internal::CachedNetworkQuality cached_network_quality;
1316
1317 const bool cached_estimate_available = network_quality_store_->GetById(
1318 current_network_id_, &cached_network_quality);
1319 UMA_HISTOGRAM_BOOLEAN("NQE.CachedNetworkQualityAvailable",
1320 cached_estimate_available);
1321
1322 if (!cached_estimate_available)
1323 return false;
1324
1325 EffectiveConnectionType effective_connection_type =
1326 cached_network_quality.effective_connection_type();
1327
1328 if (effective_connection_type == EFFECTIVE_CONNECTION_TYPE_UNKNOWN ||
1329 effective_connection_type == EFFECTIVE_CONNECTION_TYPE_OFFLINE ||
1330 effective_connection_type == EFFECTIVE_CONNECTION_TYPE_LAST) {
1331 return false;
1332 }
1333
1334 nqe::internal::NetworkQuality network_quality =
1335 cached_network_quality.network_quality();
1336
1337 bool update_network_quality_store = false;
1338
1339 // Populate |network_quality| with synthetic RTT and throughput observations
1340 // if they are missing.
1341 if (network_quality.http_rtt().InMilliseconds() ==
1342 nqe::internal::INVALID_RTT_THROUGHPUT) {
1343 network_quality.set_http_rtt(
1344 params_->TypicalNetworkQuality(effective_connection_type).http_rtt());
1345 update_network_quality_store = true;
1346 }
1347
1348 if (network_quality.transport_rtt().InMilliseconds() ==
1349 nqe::internal::INVALID_RTT_THROUGHPUT) {
1350 network_quality.set_transport_rtt(
1351 params_->TypicalNetworkQuality(effective_connection_type)
1352 .transport_rtt());
1353 update_network_quality_store = true;
1354 }
1355
1356 if (network_quality.downstream_throughput_kbps() ==
1357 nqe::internal::INVALID_RTT_THROUGHPUT) {
1358 network_quality.set_downstream_throughput_kbps(
1359 params_->TypicalNetworkQuality(effective_connection_type)
1360 .downstream_throughput_kbps());
1361 update_network_quality_store = true;
1362 }
1363
1364 if (update_network_quality_store) {
1365 network_quality_store_->Add(current_network_id_,
1366 nqe::internal::CachedNetworkQuality(
1367 tick_clock_->NowTicks(), network_quality,
1368 effective_connection_type));
1369 }
1370
1371 Observation http_rtt_observation(
1372 network_quality.http_rtt().InMilliseconds(), tick_clock_->NowTicks(),
1373 INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_CACHED_ESTIMATE);
1374 AddAndNotifyObserversOfRTT(http_rtt_observation);
1375
1376 Observation transport_rtt_observation(
1377 network_quality.transport_rtt().InMilliseconds(), tick_clock_->NowTicks(),
1378 INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_TRANSPORT_CACHED_ESTIMATE);
1379 AddAndNotifyObserversOfRTT(transport_rtt_observation);
1380
1381 Observation througphput_observation(
1382 network_quality.downstream_throughput_kbps(), tick_clock_->NowTicks(),
1383 INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_CACHED_ESTIMATE);
1384 AddAndNotifyObserversOfThroughput(througphput_observation);
1385
1386 ComputeEffectiveConnectionType();
1387 return true;
1388 }
1389
SetTickClockForTesting(const base::TickClock * tick_clock)1390 void NetworkQualityEstimator::SetTickClockForTesting(
1391 const base::TickClock* tick_clock) {
1392 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1393 tick_clock_ = tick_clock;
1394 for (int i = 0; i < nqe::internal::OBSERVATION_CATEGORY_COUNT; ++i)
1395 rtt_ms_observations_[i].SetTickClockForTesting(tick_clock_);
1396 http_downstream_throughput_kbps_observations_.SetTickClockForTesting(
1397 tick_clock_);
1398 throughput_analyzer_->SetTickClockForTesting(tick_clock_);
1399 watcher_factory_->SetTickClockForTesting(tick_clock_);
1400 }
1401
OnUpdatedTransportRTTAvailable(SocketPerformanceWatcherFactory::Protocol protocol,const base::TimeDelta & rtt,const base::Optional<nqe::internal::IPHash> & host)1402 void NetworkQualityEstimator::OnUpdatedTransportRTTAvailable(
1403 SocketPerformanceWatcherFactory::Protocol protocol,
1404 const base::TimeDelta& rtt,
1405 const base::Optional<nqe::internal::IPHash>& host) {
1406 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1407 DCHECK_LT(nqe::internal::INVALID_RTT_THROUGHPUT, rtt.InMilliseconds());
1408 Observation observation(rtt.InMilliseconds(), tick_clock_->NowTicks(),
1409 current_network_id_.signal_strength,
1410 ProtocolSourceToObservationSource(protocol), host);
1411 AddAndNotifyObserversOfRTT(observation);
1412
1413 ComputeNetworkQueueingDelay();
1414 }
1415
AddAndNotifyObserversOfRTT(const Observation & observation)1416 void NetworkQualityEstimator::AddAndNotifyObserversOfRTT(
1417 const Observation& observation) {
1418 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1419 DCHECK_NE(nqe::internal::InvalidRTT(),
1420 base::TimeDelta::FromMilliseconds(observation.value()));
1421 DCHECK_GT(NETWORK_QUALITY_OBSERVATION_SOURCE_MAX, observation.source());
1422
1423 if (!ShouldAddObservation(observation))
1424 return;
1425
1426 MaybeUpdateCachedEstimateApplied(
1427 observation,
1428 &rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_HTTP]);
1429 MaybeUpdateCachedEstimateApplied(
1430 observation,
1431 &rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT]);
1432 ++new_rtt_observations_since_last_ect_computation_;
1433
1434 std::vector<nqe::internal::ObservationCategory> observation_categories =
1435 observation.GetObservationCategories();
1436 for (nqe::internal::ObservationCategory observation_category :
1437 observation_categories) {
1438 rtt_ms_observations_[observation_category].AddObservation(observation);
1439 }
1440
1441 if (observation.source() == NETWORK_QUALITY_OBSERVATION_SOURCE_TCP ||
1442 observation.source() == NETWORK_QUALITY_OBSERVATION_SOURCE_QUIC) {
1443 last_socket_watcher_rtt_notification_ = tick_clock_->NowTicks();
1444 }
1445
1446 UMA_HISTOGRAM_ENUMERATION("NQE.RTT.ObservationSource", observation.source(),
1447 NETWORK_QUALITY_OBSERVATION_SOURCE_MAX);
1448
1449 // Maybe recompute the effective connection type since a new RTT observation
1450 // is available.
1451 if (observation.source() !=
1452 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_CACHED_ESTIMATE &&
1453 observation.source() !=
1454 NETWORK_QUALITY_OBSERVATION_SOURCE_TRANSPORT_CACHED_ESTIMATE) {
1455 MaybeComputeEffectiveConnectionType();
1456 }
1457 for (auto& observer : rtt_observer_list_) {
1458 observer.OnRTTObservation(observation.value(), observation.timestamp(),
1459 observation.source());
1460 }
1461 }
1462
AddAndNotifyObserversOfThroughput(const Observation & observation)1463 void NetworkQualityEstimator::AddAndNotifyObserversOfThroughput(
1464 const Observation& observation) {
1465 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1466 DCHECK_NE(nqe::internal::INVALID_RTT_THROUGHPUT, observation.value());
1467 DCHECK_GT(NETWORK_QUALITY_OBSERVATION_SOURCE_MAX, observation.source());
1468 DCHECK_EQ(1u, observation.GetObservationCategories().size());
1469 DCHECK_EQ(nqe::internal::OBSERVATION_CATEGORY_HTTP,
1470 observation.GetObservationCategories().front());
1471
1472 if (!ShouldAddObservation(observation))
1473 return;
1474
1475 MaybeUpdateCachedEstimateApplied(
1476 observation, &http_downstream_throughput_kbps_observations_);
1477 ++new_throughput_observations_since_last_ect_computation_;
1478 http_downstream_throughput_kbps_observations_.AddObservation(observation);
1479
1480 LOCAL_HISTOGRAM_ENUMERATION("NQE.Kbps.ObservationSource",
1481 observation.source(),
1482 NETWORK_QUALITY_OBSERVATION_SOURCE_MAX);
1483
1484 // Maybe recompute the effective connection type since a new throughput
1485 // observation is available.
1486 if (observation.source() !=
1487 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_CACHED_ESTIMATE &&
1488 observation.source() !=
1489 NETWORK_QUALITY_OBSERVATION_SOURCE_TRANSPORT_CACHED_ESTIMATE) {
1490 MaybeComputeEffectiveConnectionType();
1491 }
1492 for (auto& observer : throughput_observer_list_) {
1493 observer.OnThroughputObservation(
1494 observation.value(), observation.timestamp(), observation.source());
1495 }
1496 }
1497
OnNewThroughputObservationAvailable(int32_t downstream_kbps)1498 void NetworkQualityEstimator::OnNewThroughputObservationAvailable(
1499 int32_t downstream_kbps) {
1500 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1501
1502 if (downstream_kbps <= 0)
1503 return;
1504
1505 DCHECK_NE(nqe::internal::INVALID_RTT_THROUGHPUT, downstream_kbps);
1506
1507 Observation throughput_observation(downstream_kbps, tick_clock_->NowTicks(),
1508 current_network_id_.signal_strength,
1509 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP);
1510 AddAndNotifyObserversOfThroughput(throughput_observation);
1511 }
1512
ShouldComputeEffectiveConnectionType() const1513 bool NetworkQualityEstimator::ShouldComputeEffectiveConnectionType() const {
1514 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1515 DCHECK_EQ(nqe::internal::OBSERVATION_CATEGORY_COUNT,
1516 base::size(rtt_ms_observations_));
1517
1518 const base::TimeTicks now = tick_clock_->NowTicks();
1519 // Recompute effective connection type only if
1520 // |effective_connection_type_recomputation_interval_| has passed since it was
1521 // last computed or a connection change event was observed since the last
1522 // computation. Strict inequalities are used to ensure that effective
1523 // connection type is recomputed on connection change events even if the clock
1524 // has not updated.
1525 if (now - last_effective_connection_type_computation_ >=
1526 effective_connection_type_recomputation_interval_) {
1527 return true;
1528 }
1529
1530 if (last_connection_change_ >= last_effective_connection_type_computation_) {
1531 return true;
1532 }
1533
1534 // Recompute the effective connection type if the previously computed
1535 // effective connection type was unknown.
1536 if (effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {
1537 return true;
1538 }
1539
1540 // Recompute the effective connection type if the number of samples
1541 // available now are 50% more than the number of samples that were
1542 // available when the effective connection type was last computed.
1543 if (rtt_observations_size_at_last_ect_computation_ * 1.5 <
1544 (rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_HTTP].Size() +
1545 rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT]
1546 .Size())) {
1547 return true;
1548 }
1549
1550 if (throughput_observations_size_at_last_ect_computation_ * 1.5 <
1551 http_downstream_throughput_kbps_observations_.Size()) {
1552 return true;
1553 }
1554
1555 if ((new_rtt_observations_since_last_ect_computation_ +
1556 new_throughput_observations_since_last_ect_computation_) >=
1557 params_->count_new_observations_received_compute_ect()) {
1558 return true;
1559 }
1560 return false;
1561 }
1562
MaybeComputeEffectiveConnectionType()1563 void NetworkQualityEstimator::MaybeComputeEffectiveConnectionType() {
1564 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1565
1566 if (!ShouldComputeEffectiveConnectionType())
1567 return;
1568 ComputeEffectiveConnectionType();
1569 }
1570
1571 void NetworkQualityEstimator::
NotifyObserversOfEffectiveConnectionTypeChanged()1572 NotifyObserversOfEffectiveConnectionTypeChanged() {
1573 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1574 DCHECK_NE(EFFECTIVE_CONNECTION_TYPE_LAST, effective_connection_type_);
1575
1576 base::Optional<net::EffectiveConnectionType> override_ect = GetOverrideECT();
1577
1578 // TODO(tbansal): Add hysteresis in the notification.
1579 for (auto& observer : effective_connection_type_observer_list_)
1580 observer.OnEffectiveConnectionTypeChanged(
1581 override_ect ? override_ect.value() : effective_connection_type_);
1582 // Add the estimates of the current network to the cache store.
1583 network_quality_store_->Add(current_network_id_,
1584 nqe::internal::CachedNetworkQuality(
1585 tick_clock_->NowTicks(), network_quality_,
1586 effective_connection_type_));
1587 }
1588
NotifyObserversOfRTTOrThroughputComputed() const1589 void NetworkQualityEstimator::NotifyObserversOfRTTOrThroughputComputed() const {
1590 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1591
1592 // TODO(tbansal): Add hysteresis in the notification.
1593 for (auto& observer : rtt_and_throughput_estimates_observer_list_) {
1594 observer.OnRTTOrThroughputEstimatesComputed(
1595 network_quality_.http_rtt(), network_quality_.transport_rtt(),
1596 network_quality_.downstream_throughput_kbps());
1597 }
1598 }
1599
NotifyEffectiveConnectionTypeObserverIfPresent(EffectiveConnectionTypeObserver * observer) const1600 void NetworkQualityEstimator::NotifyEffectiveConnectionTypeObserverIfPresent(
1601 EffectiveConnectionTypeObserver* observer) const {
1602 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1603
1604 if (!effective_connection_type_observer_list_.HasObserver(observer))
1605 return;
1606
1607 base::Optional<net::EffectiveConnectionType> override_ect = GetOverrideECT();
1608 if (override_ect) {
1609 observer->OnEffectiveConnectionTypeChanged(override_ect.value());
1610 return;
1611 }
1612 if (effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_UNKNOWN)
1613 return;
1614 observer->OnEffectiveConnectionTypeChanged(effective_connection_type_);
1615 }
1616
NotifyPeerToPeerConnectionsCountObserverIfPresent(PeerToPeerConnectionsCountObserver * observer) const1617 void NetworkQualityEstimator::NotifyPeerToPeerConnectionsCountObserverIfPresent(
1618 PeerToPeerConnectionsCountObserver* observer) const {
1619 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1620
1621 if (!peer_to_peer_type_observer_list_.HasObserver(observer))
1622 return;
1623 observer->OnPeerToPeerConnectionsCountChange(p2p_connections_count_);
1624 }
1625
NotifyRTTAndThroughputEstimatesObserverIfPresent(RTTAndThroughputEstimatesObserver * observer) const1626 void NetworkQualityEstimator::NotifyRTTAndThroughputEstimatesObserverIfPresent(
1627 RTTAndThroughputEstimatesObserver* observer) const {
1628 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1629
1630 if (!rtt_and_throughput_estimates_observer_list_.HasObserver(observer))
1631 return;
1632 observer->OnRTTOrThroughputEstimatesComputed(
1633 network_quality_.http_rtt(), network_quality_.transport_rtt(),
1634 network_quality_.downstream_throughput_kbps());
1635 }
1636
AddNetworkQualitiesCacheObserver(nqe::internal::NetworkQualityStore::NetworkQualitiesCacheObserver * observer)1637 void NetworkQualityEstimator::AddNetworkQualitiesCacheObserver(
1638 nqe::internal::NetworkQualityStore::NetworkQualitiesCacheObserver*
1639 observer) {
1640 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1641 network_quality_store_->AddNetworkQualitiesCacheObserver(observer);
1642 }
1643
RemoveNetworkQualitiesCacheObserver(nqe::internal::NetworkQualityStore::NetworkQualitiesCacheObserver * observer)1644 void NetworkQualityEstimator::RemoveNetworkQualitiesCacheObserver(
1645 nqe::internal::NetworkQualityStore::NetworkQualitiesCacheObserver*
1646 observer) {
1647 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1648 network_quality_store_->RemoveNetworkQualitiesCacheObserver(observer);
1649 }
1650
OnPrefsRead(const std::map<nqe::internal::NetworkID,nqe::internal::CachedNetworkQuality> read_prefs)1651 void NetworkQualityEstimator::OnPrefsRead(
1652 const std::map<nqe::internal::NetworkID,
1653 nqe::internal::CachedNetworkQuality> read_prefs) {
1654 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1655
1656 UMA_HISTOGRAM_COUNTS_1M("NQE.Prefs.ReadSize", read_prefs.size());
1657 for (auto& it : read_prefs) {
1658 EffectiveConnectionType effective_connection_type =
1659 it.second.effective_connection_type();
1660 if (effective_connection_type == EFFECTIVE_CONNECTION_TYPE_UNKNOWN ||
1661 effective_connection_type == EFFECTIVE_CONNECTION_TYPE_OFFLINE) {
1662 continue;
1663 }
1664
1665 // RTT and throughput values are not set in the prefs.
1666 DCHECK_EQ(nqe::internal::InvalidRTT(),
1667 it.second.network_quality().http_rtt());
1668 DCHECK_EQ(nqe::internal::InvalidRTT(),
1669 it.second.network_quality().transport_rtt());
1670 DCHECK_EQ(nqe::internal::INVALID_RTT_THROUGHPUT,
1671 it.second.network_quality().downstream_throughput_kbps());
1672
1673 nqe::internal::CachedNetworkQuality cached_network_quality(
1674 tick_clock_->NowTicks(),
1675 params_->TypicalNetworkQuality(effective_connection_type),
1676 effective_connection_type);
1677
1678 network_quality_store_->Add(it.first, cached_network_quality);
1679 }
1680 ReadCachedNetworkQualityEstimate();
1681 }
1682
1683 #if defined(OS_CHROMEOS)
EnableGetNetworkIdAsynchronously()1684 void NetworkQualityEstimator::EnableGetNetworkIdAsynchronously() {
1685 get_network_id_asynchronously_ = true;
1686 }
1687 #endif // defined(OS_CHROMEOS)
1688
GetHttpRTT() const1689 base::Optional<base::TimeDelta> NetworkQualityEstimator::GetHttpRTT() const {
1690 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1691
1692 if (network_quality_.http_rtt() == nqe::internal::InvalidRTT())
1693 return base::Optional<base::TimeDelta>();
1694 return network_quality_.http_rtt();
1695 }
1696
GetTransportRTT() const1697 base::Optional<base::TimeDelta> NetworkQualityEstimator::GetTransportRTT()
1698 const {
1699 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1700
1701 if (network_quality_.transport_rtt() == nqe::internal::InvalidRTT())
1702 return base::Optional<base::TimeDelta>();
1703 return network_quality_.transport_rtt();
1704 }
1705
GetDownstreamThroughputKbps() const1706 base::Optional<int32_t> NetworkQualityEstimator::GetDownstreamThroughputKbps()
1707 const {
1708 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1709
1710 if (network_quality_.downstream_throughput_kbps() ==
1711 nqe::internal::INVALID_RTT_THROUGHPUT) {
1712 return base::Optional<int32_t>();
1713 }
1714 return network_quality_.downstream_throughput_kbps();
1715 }
1716
MaybeUpdateCachedEstimateApplied(const Observation & observation,ObservationBuffer * buffer)1717 void NetworkQualityEstimator::MaybeUpdateCachedEstimateApplied(
1718 const Observation& observation,
1719 ObservationBuffer* buffer) {
1720 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1721 if (observation.source() !=
1722 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_CACHED_ESTIMATE &&
1723 observation.source() !=
1724 NETWORK_QUALITY_OBSERVATION_SOURCE_TRANSPORT_CACHED_ESTIMATE) {
1725 return;
1726 }
1727
1728 cached_estimate_applied_ = true;
1729 bool deleted_observation_sources[NETWORK_QUALITY_OBSERVATION_SOURCE_MAX] = {
1730 false};
1731 deleted_observation_sources
1732 [NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_HTTP_FROM_PLATFORM] = true;
1733 deleted_observation_sources
1734 [NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_TRANSPORT_FROM_PLATFORM] =
1735 true;
1736
1737 buffer->RemoveObservationsWithSource(deleted_observation_sources);
1738 }
1739
ShouldAddObservation(const Observation & observation) const1740 bool NetworkQualityEstimator::ShouldAddObservation(
1741 const Observation& observation) const {
1742 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1743
1744 if (cached_estimate_applied_ &&
1745 (observation.source() ==
1746 NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_HTTP_FROM_PLATFORM ||
1747 observation.source() ==
1748 NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_TRANSPORT_FROM_PLATFORM)) {
1749 return false;
1750 }
1751 return true;
1752 }
1753
ShouldSocketWatcherNotifyRTT(base::TimeTicks now)1754 bool NetworkQualityEstimator::ShouldSocketWatcherNotifyRTT(
1755 base::TimeTicks now) {
1756 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1757 return (now - last_socket_watcher_rtt_notification_ >=
1758 params_->socket_watchers_min_notification_interval());
1759 }
1760
SimulateNetworkQualityChangeForTesting(net::EffectiveConnectionType type)1761 void NetworkQualityEstimator::SimulateNetworkQualityChangeForTesting(
1762 net::EffectiveConnectionType type) {
1763 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1764 params_->SetForcedEffectiveConnectionTypeForTesting(type);
1765 ComputeEffectiveConnectionType();
1766 }
1767
RecordSpdyPingLatency(const HostPortPair & host_port_pair,base::TimeDelta rtt)1768 void NetworkQualityEstimator::RecordSpdyPingLatency(
1769 const HostPortPair& host_port_pair,
1770 base::TimeDelta rtt) {
1771 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1772 DCHECK_LT(nqe::internal::INVALID_RTT_THROUGHPUT, rtt.InMilliseconds());
1773
1774 Observation observation(rtt.InMilliseconds(), tick_clock_->NowTicks(),
1775 current_network_id_.signal_strength,
1776 NETWORK_QUALITY_OBSERVATION_SOURCE_H2_PINGS);
1777 AddAndNotifyObserversOfRTT(observation);
1778 }
1779
OnPeerToPeerConnectionsCountChange(uint32_t count)1780 void NetworkQualityEstimator::OnPeerToPeerConnectionsCountChange(
1781 uint32_t count) {
1782 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1783
1784 if (p2p_connections_count_ == count)
1785 return;
1786
1787 if (p2p_connections_count_ == 0 && count > 0) {
1788 DCHECK(!p2p_connections_count_active_timestamp_);
1789 p2p_connections_count_active_timestamp_ = tick_clock_->NowTicks();
1790 }
1791
1792 if (p2p_connections_count_ > 0 && count == 0) {
1793 DCHECK(p2p_connections_count_active_timestamp_);
1794 base::TimeDelta duration = tick_clock_->NowTicks() -
1795 p2p_connections_count_active_timestamp_.value();
1796 LOCAL_HISTOGRAM_CUSTOM_TIMES("NQE.PeerToPeerConnectionsDuration", duration,
1797 base::TimeDelta::FromMilliseconds(1),
1798 base::TimeDelta::FromHours(1), 50);
1799 p2p_connections_count_active_timestamp_ = base::nullopt;
1800 }
1801
1802 p2p_connections_count_ = count;
1803
1804 for (auto& observer : peer_to_peer_type_observer_list_) {
1805 observer.OnPeerToPeerConnectionsCountChange(p2p_connections_count_);
1806 }
1807 }
1808
GetPeerToPeerConnectionsCountChange() const1809 uint32_t NetworkQualityEstimator::GetPeerToPeerConnectionsCountChange() const {
1810 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1811 return p2p_connections_count_;
1812 }
1813
1814 } // namespace net
1815