Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(126)

Side by Side Diff: net/base/network_quality_estimator.cc

Issue 1898603002: Move Network Quality Estimator files to //net/nqe (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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/base/network_quality_estimator.h"
6
7 #include <float.h>
8 #include <algorithm>
9 #include <cmath>
10 #include <limits>
11 #include <utility>
12 #include <vector>
13
14 #include "base/logging.h"
15 #include "base/metrics/histogram.h"
16 #include "base/metrics/histogram_base.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/thread_task_runner_handle.h"
19 #include "base/trace_event/trace_event.h"
20 #include "build/build_config.h"
21 #include "net/base/load_flags.h"
22 #include "net/base/load_timing_info.h"
23 #include "net/base/network_interfaces.h"
24 #include "net/base/socket_performance_watcher.h"
25 #include "net/base/url_util.h"
26 #include "net/url_request/url_request.h"
27 #include "url/gurl.h"
28
29 #if defined(OS_ANDROID)
30 #include "net/android/network_library.h"
31 #endif // OS_ANDROID
32
33 namespace {
34
35 // Default value of the half life (in seconds) for computing time weighted
36 // percentiles. Every half life, the weight of all observations reduces by
37 // half. Lowering the half life would reduce the weight of older values faster.
38 const int kDefaultHalfLifeSeconds = 60;
39
40 // Name of the variation parameter that holds the value of the half life (in
41 // seconds) of the observations.
42 const char kHalfLifeSecondsParamName[] = "HalfLifeSeconds";
43
44 // Returns a descriptive name corresponding to |connection_type|.
45 const char* GetNameForConnectionType(
46 net::NetworkChangeNotifier::ConnectionType connection_type) {
47 switch (connection_type) {
48 case net::NetworkChangeNotifier::CONNECTION_UNKNOWN:
49 return "Unknown";
50 case net::NetworkChangeNotifier::CONNECTION_ETHERNET:
51 return "Ethernet";
52 case net::NetworkChangeNotifier::CONNECTION_WIFI:
53 return "WiFi";
54 case net::NetworkChangeNotifier::CONNECTION_2G:
55 return "2G";
56 case net::NetworkChangeNotifier::CONNECTION_3G:
57 return "3G";
58 case net::NetworkChangeNotifier::CONNECTION_4G:
59 return "4G";
60 case net::NetworkChangeNotifier::CONNECTION_NONE:
61 return "None";
62 case net::NetworkChangeNotifier::CONNECTION_BLUETOOTH:
63 return "Bluetooth";
64 default:
65 NOTREACHED();
66 break;
67 }
68 return "";
69 }
70
71 // Suffix of the name of the variation parameter that contains the default RTT
72 // observation (in milliseconds). Complete name of the variation parameter
73 // would be |ConnectionType|.|kDefaultRTTMsecObservationSuffix| where
74 // |ConnectionType| is from |kConnectionTypeNames|. For example, variation
75 // parameter for Wi-Fi would be "WiFi.DefaultMedianRTTMsec".
76 const char kDefaultRTTMsecObservationSuffix[] = ".DefaultMedianRTTMsec";
77
78 // Suffix of the name of the variation parameter that contains the default
79 // downstream throughput observation (in Kbps). Complete name of the variation
80 // parameter would be |ConnectionType|.|kDefaultKbpsObservationSuffix| where
81 // |ConnectionType| is from |kConnectionTypeNames|. For example, variation
82 // parameter for Wi-Fi would be "WiFi.DefaultMedianKbps".
83 const char kDefaultKbpsObservationSuffix[] = ".DefaultMedianKbps";
84
85 // Computes and returns the weight multiplier per second.
86 // |variation_params| is the map containing all field trial parameters
87 // related to NetworkQualityEstimator field trial.
88 double GetWeightMultiplierPerSecond(
89 const std::map<std::string, std::string>& variation_params) {
90 int half_life_seconds = kDefaultHalfLifeSeconds;
91 int32_t variations_value = 0;
92 auto it = variation_params.find(kHalfLifeSecondsParamName);
93 if (it != variation_params.end() &&
94 base::StringToInt(it->second, &variations_value) &&
95 variations_value >= 1) {
96 half_life_seconds = variations_value;
97 }
98 DCHECK_GT(half_life_seconds, 0);
99 return exp(log(0.5) / half_life_seconds);
100 }
101
102 // Returns the histogram that should be used to record the given statistic.
103 // |max_limit| is the maximum value that can be stored in the histogram.
104 base::HistogramBase* GetHistogram(
105 const std::string& statistic_name,
106 net::NetworkChangeNotifier::ConnectionType type,
107 int32_t max_limit) {
108 const base::LinearHistogram::Sample kLowerLimit = 1;
109 DCHECK_GT(max_limit, kLowerLimit);
110 const size_t kBucketCount = 50;
111
112 // Prefix of network quality estimator histograms.
113 const char prefix[] = "NQE.";
114 return base::Histogram::FactoryGet(
115 prefix + statistic_name + GetNameForConnectionType(type), kLowerLimit,
116 max_limit, kBucketCount, base::HistogramBase::kUmaTargetedHistogramFlag);
117 }
118
119 } // namespace
120
121 namespace net {
122
123 // SocketWatcher implements SocketPerformanceWatcher, and notifies
124 // NetworkQualityEstimator of various socket performance events. SocketWatcher
125 // is not thread-safe.
126 class NetworkQualityEstimator::SocketWatcher : public SocketPerformanceWatcher {
127 public:
128 SocketWatcher(
129 SocketPerformanceWatcherFactory::Protocol protocol,
130 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
131 const base::WeakPtr<NetworkQualityEstimator>& network_quality_estimator)
132 : protocol_(protocol),
133 task_runner_(std::move(task_runner)),
134 network_quality_estimator_(network_quality_estimator) {}
135
136 ~SocketWatcher() override {}
137
138 // SocketPerformanceWatcher implementation:
139 bool ShouldNotifyUpdatedRTT() const override {
140 DCHECK(thread_checker_.CalledOnValidThread());
141
142 return true;
143 }
144
145 void OnUpdatedRTTAvailable(const base::TimeDelta& rtt) override {
146 DCHECK(thread_checker_.CalledOnValidThread());
147
148 task_runner_->PostTask(
149 FROM_HERE, base::Bind(&NetworkQualityEstimator::OnUpdatedRTTAvailable,
150 network_quality_estimator_, protocol_, rtt));
151 }
152
153 void OnConnectionChanged() override {
154 DCHECK(thread_checker_.CalledOnValidThread());
155 }
156
157 private:
158 // Transport layer protocol used by the socket that |this| is watching.
159 const SocketPerformanceWatcherFactory::Protocol protocol_;
160
161 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
162
163 base::WeakPtr<NetworkQualityEstimator> network_quality_estimator_;
164
165 base::ThreadChecker thread_checker_;
166
167 DISALLOW_COPY_AND_ASSIGN(SocketWatcher);
168 };
169
170 // SocketWatcherFactory implements SocketPerformanceWatcherFactory, and is
171 // owned by NetworkQualityEstimator. SocketWatcherFactory is thread safe.
172 class NetworkQualityEstimator::SocketWatcherFactory
173 : public SocketPerformanceWatcherFactory {
174 public:
175 SocketWatcherFactory(
176 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
177 const base::WeakPtr<NetworkQualityEstimator>& network_quality_estimator)
178 : task_runner_(std::move(task_runner)),
179 network_quality_estimator_(network_quality_estimator) {}
180
181 ~SocketWatcherFactory() override {}
182
183 // SocketPerformanceWatcherFactory implementation:
184 scoped_ptr<SocketPerformanceWatcher> CreateSocketPerformanceWatcher(
185 const Protocol protocol) override {
186 return scoped_ptr<SocketPerformanceWatcher>(
187 new SocketWatcher(protocol, task_runner_, network_quality_estimator_));
188 }
189
190 private:
191 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
192
193 base::WeakPtr<NetworkQualityEstimator> network_quality_estimator_;
194
195 DISALLOW_COPY_AND_ASSIGN(SocketWatcherFactory);
196 };
197
198 const int32_t NetworkQualityEstimator::kInvalidThroughput = 0;
199
200 NetworkQualityEstimator::NetworkQualityEstimator(
201 scoped_ptr<ExternalEstimateProvider> external_estimates_provider,
202 const std::map<std::string, std::string>& variation_params)
203 : NetworkQualityEstimator(std::move(external_estimates_provider),
204 variation_params,
205 false,
206 false) {}
207
208 NetworkQualityEstimator::NetworkQualityEstimator(
209 scoped_ptr<ExternalEstimateProvider> external_estimates_provider,
210 const std::map<std::string, std::string>& variation_params,
211 bool allow_local_host_requests_for_tests,
212 bool allow_smaller_responses_for_tests)
213 : allow_localhost_requests_(allow_local_host_requests_for_tests),
214 allow_small_responses_(allow_smaller_responses_for_tests),
215 last_connection_change_(base::TimeTicks::Now()),
216 current_network_id_(
217 NetworkID(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN,
218 std::string())),
219 downstream_throughput_kbps_observations_(
220 GetWeightMultiplierPerSecond(variation_params)),
221 rtt_observations_(GetWeightMultiplierPerSecond(variation_params)),
222 external_estimate_provider_(std::move(external_estimates_provider)),
223 weak_ptr_factory_(this) {
224 static_assert(kMinRequestDurationMicroseconds > 0,
225 "Minimum request duration must be > 0");
226 static_assert(kDefaultHalfLifeSeconds > 0,
227 "Default half life duration must be > 0");
228 static_assert(kMaximumNetworkQualityCacheSize > 0,
229 "Size of the network quality cache must be > 0");
230 // This limit should not be increased unless the logic for removing the
231 // oldest cache entry is rewritten to use a doubly-linked-list LRU queue.
232 static_assert(kMaximumNetworkQualityCacheSize <= 10,
233 "Size of the network quality cache must <= 10");
234
235 ObtainOperatingParams(variation_params);
236 NetworkChangeNotifier::AddConnectionTypeObserver(this);
237 if (external_estimate_provider_) {
238 RecordExternalEstimateProviderMetrics(
239 EXTERNAL_ESTIMATE_PROVIDER_STATUS_AVAILABLE);
240 external_estimate_provider_->SetUpdatedEstimateDelegate(this);
241 QueryExternalEstimateProvider();
242 } else {
243 RecordExternalEstimateProviderMetrics(
244 EXTERNAL_ESTIMATE_PROVIDER_STATUS_NOT_AVAILABLE);
245 }
246 current_network_id_ = GetCurrentNetworkID();
247 AddDefaultEstimates();
248
249 watcher_factory_.reset(new SocketWatcherFactory(
250 base::ThreadTaskRunnerHandle::Get(), weak_ptr_factory_.GetWeakPtr()));
251 }
252
253 // static
254 const base::TimeDelta NetworkQualityEstimator::InvalidRTT() {
255 return base::TimeDelta::Max();
256 }
257
258 void NetworkQualityEstimator::ObtainOperatingParams(
259 const std::map<std::string, std::string>& variation_params) {
260 DCHECK(thread_checker_.CalledOnValidThread());
261
262 for (size_t i = 0; i <= NetworkChangeNotifier::CONNECTION_LAST; ++i) {
263 NetworkChangeNotifier::ConnectionType type =
264 static_cast<NetworkChangeNotifier::ConnectionType>(i);
265 DCHECK_EQ(InvalidRTT(), default_observations_[i].rtt());
266 DCHECK_EQ(kInvalidThroughput,
267 default_observations_[i].downstream_throughput_kbps());
268 int32_t variations_value = kMinimumRTTVariationParameterMsec - 1;
269 // Name of the parameter that holds the RTT value for this connection type.
270 std::string rtt_parameter_name =
271 std::string(GetNameForConnectionType(type))
272 .append(kDefaultRTTMsecObservationSuffix);
273 auto it = variation_params.find(rtt_parameter_name);
274 if (it != variation_params.end() &&
275 base::StringToInt(it->second, &variations_value) &&
276 variations_value >= kMinimumRTTVariationParameterMsec) {
277 default_observations_[i] =
278 NetworkQuality(base::TimeDelta::FromMilliseconds(variations_value),
279 default_observations_[i].downstream_throughput_kbps());
280 }
281
282 variations_value = kMinimumThroughputVariationParameterKbps - 1;
283 // Name of the parameter that holds the Kbps value for this connection
284 // type.
285 std::string kbps_parameter_name =
286 std::string(GetNameForConnectionType(type))
287 .append(kDefaultKbpsObservationSuffix);
288 it = variation_params.find(kbps_parameter_name);
289 if (it != variation_params.end() &&
290 base::StringToInt(it->second, &variations_value) &&
291 variations_value >= kMinimumThroughputVariationParameterKbps) {
292 default_observations_[i] =
293 NetworkQuality(default_observations_[i].rtt(), variations_value);
294 }
295 }
296 }
297
298 void NetworkQualityEstimator::AddDefaultEstimates() {
299 DCHECK(thread_checker_.CalledOnValidThread());
300 if (default_observations_[current_network_id_.type].rtt() != InvalidRTT()) {
301 RttObservation rtt_observation(
302 default_observations_[current_network_id_.type].rtt(),
303 base::TimeTicks::Now(), DEFAULT_FROM_PLATFORM);
304 rtt_observations_.AddObservation(rtt_observation);
305 NotifyObserversOfRTT(rtt_observation);
306 }
307 if (default_observations_[current_network_id_.type]
308 .downstream_throughput_kbps() != kInvalidThroughput) {
309 ThroughputObservation throughput_observation(
310 default_observations_[current_network_id_.type]
311 .downstream_throughput_kbps(),
312 base::TimeTicks::Now(), DEFAULT_FROM_PLATFORM);
313 downstream_throughput_kbps_observations_.AddObservation(
314 throughput_observation);
315 NotifyObserversOfThroughput(throughput_observation);
316 }
317 }
318
319 NetworkQualityEstimator::~NetworkQualityEstimator() {
320 DCHECK(thread_checker_.CalledOnValidThread());
321 NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
322 }
323
324 void NetworkQualityEstimator::NotifyHeadersReceived(const URLRequest& request) {
325 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"),
326 "NetworkQualityEstimator::NotifyHeadersReceived");
327 DCHECK(thread_checker_.CalledOnValidThread());
328
329 if (!RequestProvidesUsefulObservations(request))
330 return;
331
332 // Update |estimated_median_network_quality_| if this is a main frame request.
333 if (request.load_flags() & LOAD_MAIN_FRAME) {
334 estimated_median_network_quality_ = NetworkQuality(
335 GetURLRequestRTTEstimateInternal(base::TimeTicks(), 50),
336 GetDownlinkThroughputKbpsEstimateInternal(base::TimeTicks(), 50));
337 }
338
339 base::TimeTicks now = base::TimeTicks::Now();
340 LoadTimingInfo load_timing_info;
341 request.GetLoadTimingInfo(&load_timing_info);
342
343 // If the load timing info is unavailable, it probably means that the request
344 // did not go over the network.
345 if (load_timing_info.send_start.is_null() ||
346 load_timing_info.receive_headers_end.is_null()) {
347 return;
348 }
349
350 // Time when the resource was requested.
351 base::TimeTicks request_start_time = load_timing_info.send_start;
352
353 // Time when the headers were received.
354 base::TimeTicks headers_received_time = load_timing_info.receive_headers_end;
355
356 // Duration between when the resource was requested and when response
357 // headers were received.
358 base::TimeDelta observed_rtt = headers_received_time - request_start_time;
359 DCHECK_GE(observed_rtt, base::TimeDelta());
360 if (observed_rtt < peak_network_quality_.rtt()) {
361 peak_network_quality_ = NetworkQuality(
362 observed_rtt, peak_network_quality_.downstream_throughput_kbps());
363 }
364
365 RttObservation rtt_observation(observed_rtt, now, URL_REQUEST);
366 rtt_observations_.AddObservation(rtt_observation);
367 NotifyObserversOfRTT(rtt_observation);
368
369 // Compare the RTT observation with the estimated value and record it.
370 if (estimated_median_network_quality_.rtt() != InvalidRTT()) {
371 RecordRTTUMA(estimated_median_network_quality_.rtt().InMilliseconds(),
372 observed_rtt.InMilliseconds());
373 }
374 }
375
376 void NetworkQualityEstimator::NotifyRequestCompleted(
377 const URLRequest& request) {
378 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"),
379 "NetworkQualityEstimator::NotifyRequestCompleted");
380 DCHECK(thread_checker_.CalledOnValidThread());
381
382 if (!RequestProvidesUsefulObservations(request))
383 return;
384
385 base::TimeTicks now = base::TimeTicks::Now();
386 LoadTimingInfo load_timing_info;
387 request.GetLoadTimingInfo(&load_timing_info);
388
389 // If the load timing info is unavailable, it probably means that the request
390 // did not go over the network.
391 if (load_timing_info.send_start.is_null() ||
392 load_timing_info.receive_headers_end.is_null()) {
393 return;
394 }
395
396 // Time since the resource was requested.
397 // TODO(tbansal): Change the start time to receive_headers_end, once we use
398 // NetworkActivityMonitor.
399 base::TimeDelta request_start_to_completed =
400 now - load_timing_info.send_start;
401 DCHECK_GE(request_start_to_completed, base::TimeDelta());
402
403 // Ignore tiny transfers which will not produce accurate rates.
404 // Ignore short duration transfers.
405 // Skip the checks if |allow_small_responses_| is true.
406 if (!allow_small_responses_ &&
407 (request.GetTotalReceivedBytes() < kMinTransferSizeInBytes ||
408 request_start_to_completed < base::TimeDelta::FromMicroseconds(
409 kMinRequestDurationMicroseconds))) {
410 return;
411 }
412
413 double downstream_kbps = request.GetTotalReceivedBytes() * 8.0 / 1000.0 /
414 request_start_to_completed.InSecondsF();
415 DCHECK_GE(downstream_kbps, 0.0);
416
417 // Check overflow errors. This may happen if the downstream_kbps is more than
418 // 2 * 10^9 (= 2000 Gbps).
419 if (downstream_kbps >= std::numeric_limits<int32_t>::max())
420 downstream_kbps = std::numeric_limits<int32_t>::max();
421
422 int32_t downstream_kbps_as_integer = static_cast<int32_t>(downstream_kbps);
423
424 // Round up |downstream_kbps_as_integer|. If the |downstream_kbps_as_integer|
425 // is less than 1, it is set to 1 to differentiate from case when there is no
426 // connection.
427 if (downstream_kbps - downstream_kbps_as_integer > 0)
428 downstream_kbps_as_integer++;
429
430 DCHECK_GT(downstream_kbps_as_integer, 0.0);
431 if (downstream_kbps_as_integer >
432 peak_network_quality_.downstream_throughput_kbps())
433 peak_network_quality_ =
434 NetworkQuality(peak_network_quality_.rtt(), downstream_kbps_as_integer);
435
436 ThroughputObservation throughput_observation(downstream_kbps_as_integer, now,
437 URL_REQUEST);
438 downstream_throughput_kbps_observations_.AddObservation(
439 throughput_observation);
440 NotifyObserversOfThroughput(throughput_observation);
441 }
442
443 void NetworkQualityEstimator::AddRTTObserver(RTTObserver* rtt_observer) {
444 DCHECK(thread_checker_.CalledOnValidThread());
445 rtt_observer_list_.AddObserver(rtt_observer);
446 }
447
448 void NetworkQualityEstimator::RemoveRTTObserver(RTTObserver* rtt_observer) {
449 DCHECK(thread_checker_.CalledOnValidThread());
450 rtt_observer_list_.RemoveObserver(rtt_observer);
451 }
452
453 void NetworkQualityEstimator::AddThroughputObserver(
454 ThroughputObserver* throughput_observer) {
455 DCHECK(thread_checker_.CalledOnValidThread());
456 throughput_observer_list_.AddObserver(throughput_observer);
457 }
458
459 void NetworkQualityEstimator::RemoveThroughputObserver(
460 ThroughputObserver* throughput_observer) {
461 DCHECK(thread_checker_.CalledOnValidThread());
462 throughput_observer_list_.RemoveObserver(throughput_observer);
463 }
464
465 SocketPerformanceWatcherFactory*
466 NetworkQualityEstimator::GetSocketPerformanceWatcherFactory() {
467 DCHECK(thread_checker_.CalledOnValidThread());
468
469 return watcher_factory_.get();
470 }
471
472 void NetworkQualityEstimator::RecordRTTUMA(int32_t estimated_value_msec,
473 int32_t actual_value_msec) const {
474 DCHECK(thread_checker_.CalledOnValidThread());
475
476 // Record the difference between the actual and the estimated value.
477 if (estimated_value_msec >= actual_value_msec) {
478 base::HistogramBase* difference_rtt =
479 GetHistogram("DifferenceRTTEstimatedAndActual.",
480 current_network_id_.type, 10 * 1000); // 10 seconds
481 difference_rtt->Add(estimated_value_msec - actual_value_msec);
482 } else {
483 base::HistogramBase* difference_rtt =
484 GetHistogram("DifferenceRTTActualAndEstimated.",
485 current_network_id_.type, 10 * 1000); // 10 seconds
486 difference_rtt->Add(actual_value_msec - estimated_value_msec);
487 }
488
489 // Record all the RTT observations.
490 base::HistogramBase* rtt_observations =
491 GetHistogram("RTTObservations.", current_network_id_.type,
492 10 * 1000); // 10 seconds upper bound
493 rtt_observations->Add(actual_value_msec);
494
495 if (actual_value_msec == 0)
496 return;
497
498 int32_t ratio = (estimated_value_msec * 100) / actual_value_msec;
499
500 // Record the accuracy of estimation by recording the ratio of estimated
501 // value to the actual value.
502 base::HistogramBase* ratio_median_rtt = GetHistogram(
503 "RatioEstimatedToActualRTT.", current_network_id_.type, 1000);
504 ratio_median_rtt->Add(ratio);
505 }
506
507 bool NetworkQualityEstimator::RequestProvidesUsefulObservations(
508 const URLRequest& request) const {
509 return request.url().is_valid() &&
510 (allow_localhost_requests_ || !IsLocalhost(request.url().host())) &&
511 request.url().SchemeIsHTTPOrHTTPS() &&
512 // Verify that response headers are received, so it can be ensured that
513 // response is not cached.
514 !request.response_info().response_time.is_null() &&
515 !request.was_cached() &&
516 request.creation_time() >= last_connection_change_;
517 }
518
519 void NetworkQualityEstimator::RecordExternalEstimateProviderMetrics(
520 NQEExternalEstimateProviderStatus status) const {
521 UMA_HISTOGRAM_ENUMERATION("NQE.ExternalEstimateProviderStatus", status,
522 EXTERNAL_ESTIMATE_PROVIDER_STATUS_BOUNDARY);
523 }
524
525 void NetworkQualityEstimator::OnConnectionTypeChanged(
526 NetworkChangeNotifier::ConnectionType type) {
527 DCHECK(thread_checker_.CalledOnValidThread());
528 if (peak_network_quality_.rtt() != InvalidRTT()) {
529 switch (current_network_id_.type) {
530 case NetworkChangeNotifier::CONNECTION_UNKNOWN:
531 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Unknown",
532 peak_network_quality_.rtt());
533 break;
534 case NetworkChangeNotifier::CONNECTION_ETHERNET:
535 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Ethernet",
536 peak_network_quality_.rtt());
537 break;
538 case NetworkChangeNotifier::CONNECTION_WIFI:
539 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Wifi", peak_network_quality_.rtt());
540 break;
541 case NetworkChangeNotifier::CONNECTION_2G:
542 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.2G", peak_network_quality_.rtt());
543 break;
544 case NetworkChangeNotifier::CONNECTION_3G:
545 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.3G", peak_network_quality_.rtt());
546 break;
547 case NetworkChangeNotifier::CONNECTION_4G:
548 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.4G", peak_network_quality_.rtt());
549 break;
550 case NetworkChangeNotifier::CONNECTION_NONE:
551 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.None", peak_network_quality_.rtt());
552 break;
553 case NetworkChangeNotifier::CONNECTION_BLUETOOTH:
554 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Bluetooth",
555 peak_network_quality_.rtt());
556 break;
557 default:
558 NOTREACHED() << "Unexpected connection type = "
559 << current_network_id_.type;
560 break;
561 }
562 }
563
564 if (peak_network_quality_.downstream_throughput_kbps() !=
565 kInvalidThroughput) {
566 switch (current_network_id_.type) {
567 case NetworkChangeNotifier::CONNECTION_UNKNOWN:
568 UMA_HISTOGRAM_COUNTS(
569 "NQE.PeakKbps.Unknown",
570 peak_network_quality_.downstream_throughput_kbps());
571 break;
572 case NetworkChangeNotifier::CONNECTION_ETHERNET:
573 UMA_HISTOGRAM_COUNTS(
574 "NQE.PeakKbps.Ethernet",
575 peak_network_quality_.downstream_throughput_kbps());
576 break;
577 case NetworkChangeNotifier::CONNECTION_WIFI:
578 UMA_HISTOGRAM_COUNTS(
579 "NQE.PeakKbps.Wifi",
580 peak_network_quality_.downstream_throughput_kbps());
581 break;
582 case NetworkChangeNotifier::CONNECTION_2G:
583 UMA_HISTOGRAM_COUNTS(
584 "NQE.PeakKbps.2G",
585 peak_network_quality_.downstream_throughput_kbps());
586 break;
587 case NetworkChangeNotifier::CONNECTION_3G:
588 UMA_HISTOGRAM_COUNTS(
589 "NQE.PeakKbps.3G",
590 peak_network_quality_.downstream_throughput_kbps());
591 break;
592 case NetworkChangeNotifier::CONNECTION_4G:
593 UMA_HISTOGRAM_COUNTS(
594 "NQE.PeakKbps.4G",
595 peak_network_quality_.downstream_throughput_kbps());
596 break;
597 case NetworkChangeNotifier::CONNECTION_NONE:
598 UMA_HISTOGRAM_COUNTS(
599 "NQE.PeakKbps.None",
600 peak_network_quality_.downstream_throughput_kbps());
601 break;
602 case NetworkChangeNotifier::CONNECTION_BLUETOOTH:
603 UMA_HISTOGRAM_COUNTS(
604 "NQE.PeakKbps.Bluetooth",
605 peak_network_quality_.downstream_throughput_kbps());
606 break;
607 default:
608 NOTREACHED() << "Unexpected connection type = "
609 << current_network_id_.type;
610 break;
611 }
612 }
613
614 base::TimeDelta rtt = GetURLRequestRTTEstimateInternal(base::TimeTicks(), 50);
615 if (rtt != InvalidRTT()) {
616 // Add the 50th percentile value.
617 base::HistogramBase* rtt_percentile =
618 GetHistogram("RTT.Percentile50.", current_network_id_.type,
619 10 * 1000); // 10 seconds
620 rtt_percentile->Add(rtt.InMilliseconds());
621
622 // Add the remaining percentile values.
623 static const int kPercentiles[] = {0, 10, 90, 100};
624 for (size_t i = 0; i < arraysize(kPercentiles); ++i) {
625 rtt =
626 GetURLRequestRTTEstimateInternal(base::TimeTicks(), kPercentiles[i]);
627
628 rtt_percentile = GetHistogram(
629 "RTT.Percentile" + base::IntToString(kPercentiles[i]) + ".",
630 current_network_id_.type, 10 * 1000); // 10 seconds
631 rtt_percentile->Add(rtt.InMilliseconds());
632 }
633 }
634
635 // Write the estimates of the previous network to the cache.
636 CacheNetworkQualityEstimate();
637
638 // Clear the local state.
639 last_connection_change_ = base::TimeTicks::Now();
640 peak_network_quality_ = NetworkQuality();
641 downstream_throughput_kbps_observations_.Clear();
642 rtt_observations_.Clear();
643 current_network_id_ = GetCurrentNetworkID();
644
645 QueryExternalEstimateProvider();
646
647 // Read any cached estimates for the new network. If cached estimates are
648 // unavailable, add the default estimates.
649 if (!ReadCachedNetworkQualityEstimate())
650 AddDefaultEstimates();
651 estimated_median_network_quality_ = NetworkQuality();
652 }
653
654 bool NetworkQualityEstimator::GetURLRequestRTTEstimate(
655 base::TimeDelta* rtt) const {
656 DCHECK(thread_checker_.CalledOnValidThread());
657 DCHECK(rtt);
658 if (rtt_observations_.Size() == 0) {
659 *rtt = InvalidRTT();
660 return false;
661 }
662 *rtt = GetURLRequestRTTEstimateInternal(base::TimeTicks(), 50);
663 return (*rtt != InvalidRTT());
664 }
665
666 bool NetworkQualityEstimator::GetDownlinkThroughputKbpsEstimate(
667 int32_t* kbps) const {
668 DCHECK(thread_checker_.CalledOnValidThread());
669 DCHECK(kbps);
670 if (downstream_throughput_kbps_observations_.Size() == 0) {
671 *kbps = kInvalidThroughput;
672 return false;
673 }
674 *kbps = GetDownlinkThroughputKbpsEstimateInternal(base::TimeTicks(), 50);
675 return (*kbps != kInvalidThroughput);
676 }
677
678 bool NetworkQualityEstimator::GetRecentURLRequestRTTMedian(
679 const base::TimeTicks& begin_timestamp,
680 base::TimeDelta* rtt) const {
681 DCHECK(thread_checker_.CalledOnValidThread());
682 DCHECK(rtt);
683 *rtt = GetURLRequestRTTEstimateInternal(begin_timestamp, 50);
684 return (*rtt != InvalidRTT());
685 }
686
687 bool NetworkQualityEstimator::GetRecentMedianDownlinkThroughputKbps(
688 const base::TimeTicks& begin_timestamp,
689 int32_t* kbps) const {
690 DCHECK(thread_checker_.CalledOnValidThread());
691 DCHECK(kbps);
692 *kbps = GetDownlinkThroughputKbpsEstimateInternal(begin_timestamp, 50);
693 return (*kbps != kInvalidThroughput);
694 }
695
696 template <typename ValueType>
697 NetworkQualityEstimator::ObservationBuffer<ValueType>::ObservationBuffer(
698 double weight_multiplier_per_second)
699 : weight_multiplier_per_second_(weight_multiplier_per_second) {
700 static_assert(kMaximumObservationsBufferSize > 0U,
701 "Minimum size of observation buffer must be > 0");
702 DCHECK_GE(weight_multiplier_per_second_, 0.0);
703 DCHECK_LE(weight_multiplier_per_second_, 1.0);
704 }
705
706 template <typename ValueType>
707 NetworkQualityEstimator::ObservationBuffer<ValueType>::~ObservationBuffer() {}
708
709 base::TimeDelta NetworkQualityEstimator::GetURLRequestRTTEstimateInternal(
710 const base::TimeTicks& begin_timestamp,
711 int percentile) const {
712 DCHECK(thread_checker_.CalledOnValidThread());
713 DCHECK_GE(percentile, 0);
714 DCHECK_LE(percentile, 100);
715 if (rtt_observations_.Size() == 0)
716 return InvalidRTT();
717
718 // RTT observations are sorted by duration from shortest to longest, thus
719 // a higher percentile RTT will have a longer RTT than a lower percentile.
720 base::TimeDelta rtt = InvalidRTT();
721 std::vector<ObservationSource> disallowed_observation_sources;
722 disallowed_observation_sources.push_back(TCP);
723 disallowed_observation_sources.push_back(QUIC);
724 rtt_observations_.GetPercentile(begin_timestamp, &rtt, percentile,
725 disallowed_observation_sources);
726 return rtt;
727 }
728
729 int32_t NetworkQualityEstimator::GetDownlinkThroughputKbpsEstimateInternal(
730 const base::TimeTicks& begin_timestamp,
731 int percentile) const {
732 DCHECK(thread_checker_.CalledOnValidThread());
733 DCHECK_GE(percentile, 0);
734 DCHECK_LE(percentile, 100);
735 if (downstream_throughput_kbps_observations_.Size() == 0)
736 return kInvalidThroughput;
737
738 // Throughput observations are sorted by kbps from slowest to fastest,
739 // thus a higher percentile throughput will be faster than a lower one.
740 int32_t kbps = kInvalidThroughput;
741 downstream_throughput_kbps_observations_.GetPercentile(
742 begin_timestamp, &kbps, 100 - percentile,
743 std::vector<ObservationSource>());
744 return kbps;
745 }
746
747 template <typename ValueType>
748 void NetworkQualityEstimator::ObservationBuffer<ValueType>::
749 ComputeWeightedObservations(
750 const base::TimeTicks& begin_timestamp,
751 std::vector<WeightedObservation<ValueType>>& weighted_observations,
752 double* total_weight,
753 const std::vector<ObservationSource>& disallowed_observation_sources)
754 const {
755 weighted_observations.clear();
756 double total_weight_observations = 0.0;
757 base::TimeTicks now = base::TimeTicks::Now();
758
759 for (const auto& observation : observations_) {
760 if (observation.timestamp < begin_timestamp)
761 continue;
762 bool disallowed = false;
763 for (const auto& disallowed_source : disallowed_observation_sources) {
764 if (disallowed_source == observation.source)
765 disallowed = true;
766 }
767 if (disallowed)
768 continue;
769 base::TimeDelta time_since_sample_taken = now - observation.timestamp;
770 double weight =
771 pow(weight_multiplier_per_second_, time_since_sample_taken.InSeconds());
772 weight = std::max(DBL_MIN, std::min(1.0, weight));
773
774 weighted_observations.push_back(
775 WeightedObservation<ValueType>(observation.value, weight));
776 total_weight_observations += weight;
777 }
778
779 // Sort the samples by value in ascending order.
780 std::sort(weighted_observations.begin(), weighted_observations.end());
781 *total_weight = total_weight_observations;
782 }
783
784 template <typename ValueType>
785 bool NetworkQualityEstimator::ObservationBuffer<ValueType>::GetPercentile(
786 const base::TimeTicks& begin_timestamp,
787 ValueType* result,
788 int percentile,
789 const std::vector<ObservationSource>& disallowed_observation_sources)
790 const {
791 DCHECK(result);
792 // Stores WeightedObservation in increasing order of value.
793 std::vector<WeightedObservation<ValueType>> weighted_observations;
794
795 // Total weight of all observations in |weighted_observations|.
796 double total_weight = 0.0;
797
798 ComputeWeightedObservations(begin_timestamp, weighted_observations,
799 &total_weight, disallowed_observation_sources);
800 if (weighted_observations.empty())
801 return false;
802
803 DCHECK(!weighted_observations.empty());
804 DCHECK_GT(total_weight, 0.0);
805
806 // weighted_observations may have a smaller size than observations_ since the
807 // former contains only the observations later than begin_timestamp.
808 DCHECK_GE(observations_.size(), weighted_observations.size());
809
810 double desired_weight = percentile / 100.0 * total_weight;
811
812 double cumulative_weight_seen_so_far = 0.0;
813 for (const auto& weighted_observation : weighted_observations) {
814 cumulative_weight_seen_so_far += weighted_observation.weight;
815
816 if (cumulative_weight_seen_so_far >= desired_weight) {
817 *result = weighted_observation.value;
818 return true;
819 }
820 }
821
822 // Computation may reach here due to floating point errors. This may happen
823 // if |percentile| was 100 (or close to 100), and |desired_weight| was
824 // slightly larger than |total_weight| (due to floating point errors).
825 // In this case, we return the highest |value| among all observations.
826 // This is same as value of the last observation in the sorted vector.
827 *result = weighted_observations.at(weighted_observations.size() - 1).value;
828 return true;
829 }
830
831 NetworkQualityEstimator::NetworkID
832 NetworkQualityEstimator::GetCurrentNetworkID() const {
833 DCHECK(thread_checker_.CalledOnValidThread());
834
835 // TODO(tbansal): crbug.com/498068 Add NetworkQualityEstimatorAndroid class
836 // that overrides this method on the Android platform.
837
838 // It is possible that the connection type changed between when
839 // GetConnectionType() was called and when the API to determine the
840 // network name was called. Check if that happened and retry until the
841 // connection type stabilizes. This is an imperfect solution but should
842 // capture majority of cases, and should not significantly affect estimates
843 // (that are approximate to begin with).
844 while (true) {
845 NetworkQualityEstimator::NetworkID network_id(
846 NetworkChangeNotifier::GetConnectionType(), std::string());
847
848 switch (network_id.type) {
849 case NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN:
850 case NetworkChangeNotifier::ConnectionType::CONNECTION_NONE:
851 case NetworkChangeNotifier::ConnectionType::CONNECTION_BLUETOOTH:
852 case NetworkChangeNotifier::ConnectionType::CONNECTION_ETHERNET:
853 break;
854 case NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI:
855 #if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS) || \
856 defined(OS_WIN)
857 network_id.id = GetWifiSSID();
858 #endif
859 break;
860 case NetworkChangeNotifier::ConnectionType::CONNECTION_2G:
861 case NetworkChangeNotifier::ConnectionType::CONNECTION_3G:
862 case NetworkChangeNotifier::ConnectionType::CONNECTION_4G:
863 #if defined(OS_ANDROID)
864 network_id.id = android::GetTelephonyNetworkOperator();
865 #endif
866 break;
867 default:
868 NOTREACHED() << "Unexpected connection type = " << network_id.type;
869 break;
870 }
871
872 if (network_id.type == NetworkChangeNotifier::GetConnectionType())
873 return network_id;
874 }
875 NOTREACHED();
876 }
877
878 bool NetworkQualityEstimator::ReadCachedNetworkQualityEstimate() {
879 DCHECK(thread_checker_.CalledOnValidThread());
880
881 // If the network name is unavailable, caching should not be performed.
882 if (current_network_id_.id.empty())
883 return false;
884
885 CachedNetworkQualities::const_iterator it =
886 cached_network_qualities_.find(current_network_id_);
887
888 if (it == cached_network_qualities_.end())
889 return false;
890
891 NetworkQuality network_quality(it->second.network_quality());
892
893 DCHECK_NE(InvalidRTT(), network_quality.rtt());
894 DCHECK_NE(kInvalidThroughput, network_quality.downstream_throughput_kbps());
895
896 ThroughputObservation througphput_observation(
897 network_quality.downstream_throughput_kbps(), base::TimeTicks::Now(),
898 CACHED_ESTIMATE);
899 downstream_throughput_kbps_observations_.AddObservation(
900 througphput_observation);
901 NotifyObserversOfThroughput(througphput_observation);
902
903 RttObservation rtt_observation(network_quality.rtt(), base::TimeTicks::Now(),
904 CACHED_ESTIMATE);
905 rtt_observations_.AddObservation(rtt_observation);
906 NotifyObserversOfRTT(rtt_observation);
907
908 return true;
909 }
910
911 void NetworkQualityEstimator::OnUpdatedEstimateAvailable() {
912 DCHECK(thread_checker_.CalledOnValidThread());
913 DCHECK(external_estimate_provider_);
914
915 RecordExternalEstimateProviderMetrics(
916 EXTERNAL_ESTIMATE_PROVIDER_STATUS_CALLBACK);
917 QueryExternalEstimateProvider();
918 }
919
920 void NetworkQualityEstimator::QueryExternalEstimateProvider() {
921 DCHECK(thread_checker_.CalledOnValidThread());
922
923 if (!external_estimate_provider_)
924 return;
925 RecordExternalEstimateProviderMetrics(
926 EXTERNAL_ESTIMATE_PROVIDER_STATUS_QUERIED);
927
928 base::TimeDelta time_since_last_update;
929
930 // Request a new estimate if estimate is not available, or if the available
931 // estimate is not fresh.
932 if (!external_estimate_provider_->GetTimeSinceLastUpdate(
933 &time_since_last_update) ||
934 time_since_last_update >
935 base::TimeDelta::FromMilliseconds(
936 kExternalEstimateProviderFreshnessDurationMsec)) {
937 // Request the external estimate provider for updated estimates. When the
938 // updates estimates are available, OnUpdatedEstimateAvailable() will be
939 // called.
940 external_estimate_provider_->Update();
941 return;
942 }
943
944 RecordExternalEstimateProviderMetrics(
945 EXTERNAL_ESTIMATE_PROVIDER_STATUS_QUERY_SUCCESSFUL);
946 base::TimeDelta rtt;
947 if (external_estimate_provider_->GetRTT(&rtt)) {
948 rtt_observations_.AddObservation(
949 RttObservation(rtt, base::TimeTicks::Now(), EXTERNAL_ESTIMATE));
950 }
951
952 int32_t downstream_throughput_kbps;
953 if (external_estimate_provider_->GetDownstreamThroughputKbps(
954 &downstream_throughput_kbps)) {
955 downstream_throughput_kbps_observations_.AddObservation(
956 ThroughputObservation(downstream_throughput_kbps,
957 base::TimeTicks::Now(), EXTERNAL_ESTIMATE));
958 }
959 }
960
961 void NetworkQualityEstimator::CacheNetworkQualityEstimate() {
962 DCHECK(thread_checker_.CalledOnValidThread());
963 DCHECK_LE(cached_network_qualities_.size(),
964 static_cast<size_t>(kMaximumNetworkQualityCacheSize));
965
966 // If the network name is unavailable, caching should not be performed.
967 if (current_network_id_.id.empty())
968 return;
969
970 NetworkQuality network_quality = NetworkQuality(
971 GetURLRequestRTTEstimateInternal(base::TimeTicks(), 50),
972 GetDownlinkThroughputKbpsEstimateInternal(base::TimeTicks(), 50));
973 if (network_quality.rtt() == InvalidRTT() ||
974 network_quality.downstream_throughput_kbps() == kInvalidThroughput) {
975 return;
976 }
977
978 if (cached_network_qualities_.size() == kMaximumNetworkQualityCacheSize) {
979 // Remove the oldest entry.
980 CachedNetworkQualities::iterator oldest_entry_iterator =
981 cached_network_qualities_.begin();
982
983 for (CachedNetworkQualities::iterator it =
984 cached_network_qualities_.begin();
985 it != cached_network_qualities_.end(); ++it) {
986 if ((it->second).OlderThan(oldest_entry_iterator->second))
987 oldest_entry_iterator = it;
988 }
989 cached_network_qualities_.erase(oldest_entry_iterator);
990 }
991 DCHECK_LT(cached_network_qualities_.size(),
992 static_cast<size_t>(kMaximumNetworkQualityCacheSize));
993
994 cached_network_qualities_.insert(std::make_pair(
995 current_network_id_, CachedNetworkQuality(network_quality)));
996 DCHECK_LE(cached_network_qualities_.size(),
997 static_cast<size_t>(kMaximumNetworkQualityCacheSize));
998 }
999
1000 void NetworkQualityEstimator::OnUpdatedRTTAvailable(
1001 SocketPerformanceWatcherFactory::Protocol protocol,
1002 const base::TimeDelta& rtt) {
1003 DCHECK(thread_checker_.CalledOnValidThread());
1004
1005 switch (protocol) {
1006 case SocketPerformanceWatcherFactory::PROTOCOL_TCP:
1007 NotifyObserversOfRTT(RttObservation(rtt, base::TimeTicks::Now(), TCP));
1008 return;
1009 case SocketPerformanceWatcherFactory::PROTOCOL_QUIC:
1010 NotifyObserversOfRTT(RttObservation(rtt, base::TimeTicks::Now(), QUIC));
1011 return;
1012 default:
1013 NOTREACHED();
1014 }
1015 }
1016
1017 void NetworkQualityEstimator::NotifyObserversOfRTT(
1018 const RttObservation& observation) {
1019 FOR_EACH_OBSERVER(
1020 RTTObserver, rtt_observer_list_,
1021 OnRTTObservation(observation.value.InMilliseconds(),
1022 observation.timestamp, observation.source));
1023 }
1024
1025 void NetworkQualityEstimator::NotifyObserversOfThroughput(
1026 const ThroughputObservation& observation) {
1027 FOR_EACH_OBSERVER(
1028 ThroughputObserver, throughput_observer_list_,
1029 OnThroughputObservation(observation.value, observation.timestamp,
1030 observation.source));
1031 }
1032
1033 NetworkQualityEstimator::CachedNetworkQuality::CachedNetworkQuality(
1034 const NetworkQuality& network_quality)
1035 : last_update_time_(base::TimeTicks::Now()),
1036 network_quality_(network_quality) {
1037 }
1038
1039 NetworkQualityEstimator::CachedNetworkQuality::CachedNetworkQuality(
1040 const CachedNetworkQuality& other)
1041 : last_update_time_(other.last_update_time_),
1042 network_quality_(other.network_quality_) {
1043 }
1044
1045 NetworkQualityEstimator::CachedNetworkQuality::~CachedNetworkQuality() {
1046 }
1047
1048 bool NetworkQualityEstimator::CachedNetworkQuality::OlderThan(
1049 const CachedNetworkQuality& cached_network_quality) const {
1050 return last_update_time_ < cached_network_quality.last_update_time_;
1051 }
1052
1053 NetworkQualityEstimator::NetworkQuality::NetworkQuality()
1054 : NetworkQuality(NetworkQualityEstimator::InvalidRTT(),
1055 NetworkQualityEstimator::kInvalidThroughput) {}
1056
1057 NetworkQualityEstimator::NetworkQuality::NetworkQuality(
1058 const base::TimeDelta& rtt,
1059 int32_t downstream_throughput_kbps)
1060 : rtt_(rtt), downstream_throughput_kbps_(downstream_throughput_kbps) {
1061 DCHECK_GE(rtt_, base::TimeDelta());
1062 DCHECK_GE(downstream_throughput_kbps_, 0);
1063 }
1064
1065 NetworkQualityEstimator::NetworkQuality::NetworkQuality(
1066 const NetworkQuality& other)
1067 : NetworkQuality(other.rtt_, other.downstream_throughput_kbps_) {}
1068
1069 NetworkQualityEstimator::NetworkQuality::~NetworkQuality() {}
1070
1071 NetworkQualityEstimator::NetworkQuality&
1072 NetworkQualityEstimator::NetworkQuality::
1073 operator=(const NetworkQuality& other) {
1074 rtt_ = other.rtt_;
1075 downstream_throughput_kbps_ = other.downstream_throughput_kbps_;
1076 return *this;
1077 }
1078
1079 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698