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