OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/nqe/network_quality_estimator.h" | 5 #include "net/nqe/network_quality_estimator.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <cmath> | 8 #include <cmath> |
9 #include <limits> | 9 #include <limits> |
10 #include <utility> | 10 #include <utility> |
11 #include <vector> | 11 #include <vector> |
12 | 12 |
13 #include "base/bind_helpers.h" | 13 #include "base/bind_helpers.h" |
14 #include "base/location.h" | 14 #include "base/location.h" |
15 #include "base/logging.h" | 15 #include "base/logging.h" |
16 #include "base/metrics/histogram.h" | 16 #include "base/metrics/histogram.h" |
17 #include "base/metrics/histogram_base.h" | 17 #include "base/metrics/histogram_base.h" |
| 18 #include "base/metrics/sparse_histogram.h" |
| 19 #include "base/rand_util.h" |
18 #include "base/single_thread_task_runner.h" | 20 #include "base/single_thread_task_runner.h" |
19 #include "base/strings/string_number_conversions.h" | 21 #include "base/strings/string_number_conversions.h" |
20 #include "base/threading/thread_task_runner_handle.h" | 22 #include "base/threading/thread_task_runner_handle.h" |
21 #include "base/time/default_tick_clock.h" | 23 #include "base/time/default_tick_clock.h" |
22 #include "base/trace_event/trace_event.h" | 24 #include "base/trace_event/trace_event.h" |
23 #include "build/build_config.h" | 25 #include "build/build_config.h" |
24 #include "net/base/load_flags.h" | 26 #include "net/base/load_flags.h" |
25 #include "net/base/load_timing_info.h" | 27 #include "net/base/load_timing_info.h" |
26 #include "net/base/network_interfaces.h" | 28 #include "net/base/network_interfaces.h" |
27 #include "net/base/url_util.h" | 29 #include "net/base/url_util.h" |
| 30 #include "net/http/http_status_code.h" |
28 #include "net/nqe/socket_watcher_factory.h" | 31 #include "net/nqe/socket_watcher_factory.h" |
29 #include "net/nqe/throughput_analyzer.h" | 32 #include "net/nqe/throughput_analyzer.h" |
30 #include "net/url_request/url_request.h" | 33 #include "net/url_request/url_request.h" |
| 34 #include "net/url_request/url_request_status.h" |
31 #include "url/gurl.h" | 35 #include "url/gurl.h" |
32 | 36 |
33 #if defined(OS_ANDROID) | 37 #if defined(OS_ANDROID) |
34 #include "net/android/network_library.h" | 38 #include "net/android/network_library.h" |
35 #endif // OS_ANDROID | 39 #endif // OS_ANDROID |
36 | 40 |
37 namespace { | 41 namespace { |
38 | 42 |
39 // Default value of the half life (in seconds) for computing time weighted | 43 // Default value of the half life (in seconds) for computing time weighted |
40 // percentiles. Every half life, the weight of all observations reduces by | 44 // percentiles. Every half life, the weight of all observations reduces by |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
134 | 138 |
135 bool GetValueForVariationParam( | 139 bool GetValueForVariationParam( |
136 const std::map<std::string, std::string>& variation_params, | 140 const std::map<std::string, std::string>& variation_params, |
137 const std::string& parameter_name, | 141 const std::string& parameter_name, |
138 int32_t* variations_value) { | 142 int32_t* variations_value) { |
139 const auto it = variation_params.find(parameter_name); | 143 const auto it = variation_params.find(parameter_name); |
140 return it != variation_params.end() && | 144 return it != variation_params.end() && |
141 base::StringToInt(it->second, variations_value); | 145 base::StringToInt(it->second, variations_value); |
142 } | 146 } |
143 | 147 |
| 148 // Returns the variation value for |parameter_name|. If the value is |
| 149 // unavailable, |default_value| is returned. |
| 150 double GetDoubleValueForVariationParamWithDefaultValue( |
| 151 const std::map<std::string, std::string>& variation_params, |
| 152 const std::string& parameter_name, |
| 153 double default_value) { |
| 154 const auto it = variation_params.find(parameter_name); |
| 155 if (it == variation_params.end()) |
| 156 return default_value; |
| 157 |
| 158 double variations_value = default_value; |
| 159 if (!base::StringToDouble(it->second, &variations_value)) |
| 160 return default_value; |
| 161 return variations_value; |
| 162 } |
| 163 |
144 // Returns the algorithm that should be used for computing effective connection | 164 // Returns the algorithm that should be used for computing effective connection |
145 // type based on field trial params. Returns an empty string if a valid | 165 // type based on field trial params. Returns an empty string if a valid |
146 // algorithm paramter is not present in the field trial params. | 166 // algorithm paramter is not present in the field trial params. |
147 std::string GetEffectiveConnectionTypeAlgorithm( | 167 std::string GetEffectiveConnectionTypeAlgorithm( |
148 const std::map<std::string, std::string>& variation_params) { | 168 const std::map<std::string, std::string>& variation_params) { |
149 const auto it = variation_params.find("effective_connection_type_algorithm"); | 169 const auto it = variation_params.find("effective_connection_type_algorithm"); |
150 if (it == variation_params.end()) | 170 if (it == variation_params.end()) |
151 return std::string(); | 171 return std::string(); |
152 return it->second; | 172 return it->second; |
153 } | 173 } |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
201 static const char* const kSuffixes[] = { | 221 static const char* const kSuffixes[] = { |
202 "0_20", "20_60", "60_140", "140_300", "300_620", | 222 "0_20", "20_60", "60_140", "140_300", "300_620", |
203 "620_1260", "1260_2540", "2540_5100", "5100_Infinity"}; | 223 "620_1260", "1260_2540", "2540_5100", "5100_Infinity"}; |
204 for (size_t i = 0; i < arraysize(kSuffixes) - 1; ++i) { | 224 for (size_t i = 0; i < arraysize(kSuffixes) - 1; ++i) { |
205 if (observed_throughput_kbps <= static_cast<float>((20 * (2 << i) - 20))) | 225 if (observed_throughput_kbps <= static_cast<float>((20 * (2 << i) - 20))) |
206 return kSuffixes[i]; | 226 return kSuffixes[i]; |
207 } | 227 } |
208 return kSuffixes[arraysize(kSuffixes) - 1]; | 228 return kSuffixes[arraysize(kSuffixes) - 1]; |
209 } | 229 } |
210 | 230 |
| 231 // The least significant kTrimBits of the metric will be discarded. If the |
| 232 // trimmed metric value is greater than what can be fit in kBitsPerMetric bits, |
| 233 // then the largest value that can be represented in kBitsPerMetric bits is |
| 234 // returned. |
| 235 const int32_t kTrimBits = 5; |
| 236 const int32_t kBitsPerMetric = 7; |
| 237 |
| 238 static_assert(31 >= kBitsPerMetric * 4, |
| 239 "Four metrics would not fit in a 32-bit int with first bit " |
| 240 "reserved for sign"); |
| 241 |
| 242 // Trims the |metric| by removing the last kTrimBits, and then rounding down |
| 243 // the |metric| such that the |metric| fits in kBitsPerMetric. |
| 244 int32_t FitInKBitsPerMetricBits(int32_t metric) { |
| 245 // Remove the last kTrimBits. This will allow the metric to fit within |
| 246 // kBitsPerMetric while losing only the least significant bits. |
| 247 metric = metric >> kTrimBits; |
| 248 |
| 249 // kLargestValuePossible is the largest value that can be recorded using |
| 250 // kBitsPerMetric. |
| 251 static const int32_t kLargestValuePossible = (1 << kBitsPerMetric) - 1; |
| 252 if (metric > kLargestValuePossible) { |
| 253 // Fit |metric| in kBitsPerMetric by clamping it down. |
| 254 metric = kLargestValuePossible; |
| 255 } |
| 256 DCHECK_EQ(0, metric >> kBitsPerMetric); |
| 257 return metric; |
| 258 } |
| 259 |
211 } // namespace | 260 } // namespace |
212 | 261 |
213 namespace net { | 262 namespace net { |
214 | 263 |
215 NetworkQualityEstimator::NetworkQualityEstimator( | 264 NetworkQualityEstimator::NetworkQualityEstimator( |
216 std::unique_ptr<ExternalEstimateProvider> external_estimates_provider, | 265 std::unique_ptr<ExternalEstimateProvider> external_estimates_provider, |
217 const std::map<std::string, std::string>& variation_params) | 266 const std::map<std::string, std::string>& variation_params) |
218 : NetworkQualityEstimator(std::move(external_estimates_provider), | 267 : NetworkQualityEstimator(std::move(external_estimates_provider), |
219 variation_params, | 268 variation_params, |
220 false, | 269 false, |
(...skipping 24 matching lines...) Expand all Loading... |
245 last_connection_change_(tick_clock_->NowTicks()), | 294 last_connection_change_(tick_clock_->NowTicks()), |
246 current_network_id_( | 295 current_network_id_( |
247 NetworkID(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, | 296 NetworkID(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, |
248 std::string())), | 297 std::string())), |
249 downstream_throughput_kbps_observations_(weight_multiplier_per_second_), | 298 downstream_throughput_kbps_observations_(weight_multiplier_per_second_), |
250 rtt_observations_(weight_multiplier_per_second_), | 299 rtt_observations_(weight_multiplier_per_second_), |
251 effective_connection_type_at_last_main_frame_( | 300 effective_connection_type_at_last_main_frame_( |
252 EFFECTIVE_CONNECTION_TYPE_UNKNOWN), | 301 EFFECTIVE_CONNECTION_TYPE_UNKNOWN), |
253 external_estimate_provider_(std::move(external_estimates_provider)), | 302 external_estimate_provider_(std::move(external_estimates_provider)), |
254 effective_connection_type_(EFFECTIVE_CONNECTION_TYPE_UNKNOWN), | 303 effective_connection_type_(EFFECTIVE_CONNECTION_TYPE_UNKNOWN), |
| 304 correlation_uma_logging_probability_( |
| 305 GetDoubleValueForVariationParamWithDefaultValue( |
| 306 variation_params, |
| 307 "correlation_logging_probability", |
| 308 0.0)), |
255 weak_ptr_factory_(this) { | 309 weak_ptr_factory_(this) { |
256 static_assert(kDefaultHalfLifeSeconds > 0, | 310 static_assert(kDefaultHalfLifeSeconds > 0, |
257 "Default half life duration must be > 0"); | 311 "Default half life duration must be > 0"); |
258 static_assert(kMaximumNetworkQualityCacheSize > 0, | 312 static_assert(kMaximumNetworkQualityCacheSize > 0, |
259 "Size of the network quality cache must be > 0"); | 313 "Size of the network quality cache must be > 0"); |
260 // This limit should not be increased unless the logic for removing the | 314 // This limit should not be increased unless the logic for removing the |
261 // oldest cache entry is rewritten to use a doubly-linked-list LRU queue. | 315 // oldest cache entry is rewritten to use a doubly-linked-list LRU queue. |
262 static_assert(kMaximumNetworkQualityCacheSize <= 10, | 316 static_assert(kMaximumNetworkQualityCacheSize <= 10, |
263 "Size of the network quality cache must <= 10"); | 317 "Size of the network quality cache must <= 10"); |
264 // None of the algorithms can have an empty name. | 318 // None of the algorithms can have an empty name. |
265 DCHECK(algorithm_name_to_enum_.end() == | 319 DCHECK(algorithm_name_to_enum_.end() == |
266 algorithm_name_to_enum_.find(std::string())); | 320 algorithm_name_to_enum_.find(std::string())); |
267 | 321 |
268 DCHECK_EQ(algorithm_name_to_enum_.size(), | 322 DCHECK_EQ(algorithm_name_to_enum_.size(), |
269 static_cast<size_t>(EffectiveConnectionTypeAlgorithm:: | 323 static_cast<size_t>(EffectiveConnectionTypeAlgorithm:: |
270 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST)); | 324 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST)); |
271 DCHECK_NE(EffectiveConnectionTypeAlgorithm:: | 325 DCHECK_NE(EffectiveConnectionTypeAlgorithm:: |
272 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST, | 326 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST, |
273 effective_connection_type_algorithm_); | 327 effective_connection_type_algorithm_); |
| 328 DCHECK_LE(0.0, correlation_uma_logging_probability_); |
| 329 DCHECK_GE(1.0, correlation_uma_logging_probability_); |
274 | 330 |
275 ObtainOperatingParams(variation_params); | 331 ObtainOperatingParams(variation_params); |
276 ObtainEffectiveConnectionTypeModelParams(variation_params); | 332 ObtainEffectiveConnectionTypeModelParams(variation_params); |
277 NetworkChangeNotifier::AddConnectionTypeObserver(this); | 333 NetworkChangeNotifier::AddConnectionTypeObserver(this); |
278 if (external_estimate_provider_) { | 334 if (external_estimate_provider_) { |
279 RecordExternalEstimateProviderMetrics( | 335 RecordExternalEstimateProviderMetrics( |
280 EXTERNAL_ESTIMATE_PROVIDER_STATUS_AVAILABLE); | 336 EXTERNAL_ESTIMATE_PROVIDER_STATUS_AVAILABLE); |
281 external_estimate_provider_->SetUpdatedEstimateDelegate(this); | 337 external_estimate_provider_->SetUpdatedEstimateDelegate(this); |
282 } else { | 338 } else { |
283 RecordExternalEstimateProviderMetrics( | 339 RecordExternalEstimateProviderMetrics( |
(...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
507 | 563 |
508 LoadTimingInfo load_timing_info; | 564 LoadTimingInfo load_timing_info; |
509 request.GetLoadTimingInfo(&load_timing_info); | 565 request.GetLoadTimingInfo(&load_timing_info); |
510 | 566 |
511 // If the load timing info is unavailable, it probably means that the request | 567 // If the load timing info is unavailable, it probably means that the request |
512 // did not go over the network. | 568 // did not go over the network. |
513 if (load_timing_info.send_start.is_null() || | 569 if (load_timing_info.send_start.is_null() || |
514 load_timing_info.receive_headers_end.is_null()) { | 570 load_timing_info.receive_headers_end.is_null()) { |
515 return; | 571 return; |
516 } | 572 } |
| 573 DCHECK(!request.response_info().was_cached); |
517 | 574 |
518 // Duration between when the resource was requested and when the response | 575 // Duration between when the resource was requested and when the response |
519 // headers were received. | 576 // headers were received. |
520 base::TimeDelta observed_http_rtt = | 577 base::TimeDelta observed_http_rtt = |
521 load_timing_info.receive_headers_end - load_timing_info.send_start; | 578 load_timing_info.receive_headers_end - load_timing_info.send_start; |
522 DCHECK_GE(observed_http_rtt, base::TimeDelta()); | 579 DCHECK_GE(observed_http_rtt, base::TimeDelta()); |
523 if (observed_http_rtt < peak_network_quality_.http_rtt()) { | 580 if (observed_http_rtt < peak_network_quality_.http_rtt()) { |
524 peak_network_quality_ = nqe::internal::NetworkQuality( | 581 peak_network_quality_ = nqe::internal::NetworkQuality( |
525 observed_http_rtt, peak_network_quality_.transport_rtt(), | 582 observed_http_rtt, peak_network_quality_.transport_rtt(), |
526 peak_network_quality_.downstream_throughput_kbps()); | 583 peak_network_quality_.downstream_throughput_kbps()); |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
648 void NetworkQualityEstimator::NotifyRequestCompleted( | 705 void NetworkQualityEstimator::NotifyRequestCompleted( |
649 const URLRequest& request) { | 706 const URLRequest& request) { |
650 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"), | 707 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"), |
651 "NetworkQualityEstimator::NotifyRequestCompleted"); | 708 "NetworkQualityEstimator::NotifyRequestCompleted"); |
652 DCHECK(thread_checker_.CalledOnValidThread()); | 709 DCHECK(thread_checker_.CalledOnValidThread()); |
653 | 710 |
654 if (!RequestSchemeIsHTTPOrHTTPS(request)) | 711 if (!RequestSchemeIsHTTPOrHTTPS(request)) |
655 return; | 712 return; |
656 | 713 |
657 throughput_analyzer_->NotifyRequestCompleted(request); | 714 throughput_analyzer_->NotifyRequestCompleted(request); |
| 715 RecordCorrelationMetric(request); |
| 716 } |
| 717 |
| 718 void NetworkQualityEstimator::RecordCorrelationMetric( |
| 719 const URLRequest& request) const { |
| 720 DCHECK(thread_checker_.CalledOnValidThread()); |
| 721 |
| 722 // The histogram is recorded with probability |
| 723 // |correlation_uma_logging_probability_| to reduce overhead involved with |
| 724 // sparse histograms. Also, recording the correlation on each request is |
| 725 // unnecessary. |
| 726 if (RandDouble() >= correlation_uma_logging_probability_) |
| 727 return; |
| 728 |
| 729 if (request.response_info().was_cached || |
| 730 !request.response_info().network_accessed) { |
| 731 return; |
| 732 } |
| 733 |
| 734 LoadTimingInfo load_timing_info; |
| 735 request.GetLoadTimingInfo(&load_timing_info); |
| 736 DCHECK(!load_timing_info.send_start.is_null() && |
| 737 !load_timing_info.receive_headers_end.is_null()); |
| 738 |
| 739 // Record UMA only for successful requests that have completed. |
| 740 if (!request.status().is_success() || request.status().is_io_pending()) |
| 741 return; |
| 742 if (request.GetResponseCode() != HTTP_OK) |
| 743 return; |
| 744 if (load_timing_info.receive_headers_end < last_main_frame_request_) |
| 745 return; |
| 746 |
| 747 const base::TimeTicks now = tick_clock_->NowTicks(); |
| 748 // Record UMA only for requests that started recently. |
| 749 if (now - last_main_frame_request_ > base::TimeDelta::FromSeconds(15)) |
| 750 return; |
| 751 |
| 752 DCHECK_GE(now, load_timing_info.send_start); |
| 753 |
| 754 const int32_t transport_rtt_milliseconds = |
| 755 estimated_quality_at_last_main_frame_.transport_rtt() != |
| 756 nqe::internal::InvalidRTT() |
| 757 ? FitInKBitsPerMetricBits( |
| 758 estimated_quality_at_last_main_frame_.transport_rtt() |
| 759 .InMilliseconds()) |
| 760 : 0; |
| 761 |
| 762 const int32_t http_rtt_milliseconds = |
| 763 estimated_quality_at_last_main_frame_.http_rtt() != |
| 764 nqe::internal::InvalidRTT() |
| 765 ? FitInKBitsPerMetricBits( |
| 766 estimated_quality_at_last_main_frame_.http_rtt() |
| 767 .InMilliseconds()) |
| 768 : 0; |
| 769 |
| 770 const int32_t downstream_throughput_kbps = |
| 771 estimated_quality_at_last_main_frame_.downstream_throughput_kbps() != |
| 772 nqe::internal::kInvalidThroughput |
| 773 ? FitInKBitsPerMetricBits(estimated_quality_at_last_main_frame_ |
| 774 .downstream_throughput_kbps()) |
| 775 : 0; |
| 776 |
| 777 const int32_t resource_load_time_milliseconds = FitInKBitsPerMetricBits( |
| 778 (now - load_timing_info.send_start).InMilliseconds()); |
| 779 |
| 780 DCHECK_EQ(0, (transport_rtt_milliseconds | http_rtt_milliseconds | |
| 781 downstream_throughput_kbps | resource_load_time_milliseconds) >> |
| 782 kBitsPerMetric); |
| 783 |
| 784 // First 32 - (4* kBitsPerMetric) of the sample are unset. Next |
| 785 // kBitsPerMetric of the sample contain |transport_rtt_milliseconds|. |
| 786 // Next kBitsPerMetric contain |http_rtt_milliseconds|. Next kBitsPerMetric |
| 787 // contain |downstream_throughput_kbps|. And, the last kBitsPerMetric |
| 788 // contain |resource_load_time_milliseconds|. |
| 789 int32_t sample = transport_rtt_milliseconds; |
| 790 sample = (sample << kBitsPerMetric) | http_rtt_milliseconds; |
| 791 sample = (sample << kBitsPerMetric) | downstream_throughput_kbps; |
| 792 sample = (sample << kBitsPerMetric) | resource_load_time_milliseconds; |
| 793 |
| 794 UMA_HISTOGRAM_SPARSE_SLOWLY("NQE.Correlation.ResourceLoadTime", sample); |
658 } | 795 } |
659 | 796 |
660 void NetworkQualityEstimator::NotifyURLRequestDestroyed( | 797 void NetworkQualityEstimator::NotifyURLRequestDestroyed( |
661 const URLRequest& request) { | 798 const URLRequest& request) { |
662 DCHECK(thread_checker_.CalledOnValidThread()); | 799 DCHECK(thread_checker_.CalledOnValidThread()); |
663 | 800 |
664 NotifyRequestCompleted(request); | 801 NotifyRequestCompleted(request); |
665 } | 802 } |
666 | 803 |
667 void NetworkQualityEstimator::AddRTTObserver(RTTObserver* rtt_observer) { | 804 void NetworkQualityEstimator::AddRTTObserver(RTTObserver* rtt_observer) { |
(...skipping 550 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1218 NOTREACHED(); | 1355 NOTREACHED(); |
1219 return EFFECTIVE_CONNECTION_TYPE_UNKNOWN; | 1356 return EFFECTIVE_CONNECTION_TYPE_UNKNOWN; |
1220 } | 1357 } |
1221 | 1358 |
1222 void NetworkQualityEstimator::SetTickClockForTesting( | 1359 void NetworkQualityEstimator::SetTickClockForTesting( |
1223 std::unique_ptr<base::TickClock> tick_clock) { | 1360 std::unique_ptr<base::TickClock> tick_clock) { |
1224 DCHECK(thread_checker_.CalledOnValidThread()); | 1361 DCHECK(thread_checker_.CalledOnValidThread()); |
1225 tick_clock_ = std::move(tick_clock); | 1362 tick_clock_ = std::move(tick_clock); |
1226 } | 1363 } |
1227 | 1364 |
| 1365 double NetworkQualityEstimator::RandDouble() const { |
| 1366 return base::RandDouble(); |
| 1367 } |
| 1368 |
1228 void NetworkQualityEstimator::CacheNetworkQualityEstimate() { | 1369 void NetworkQualityEstimator::CacheNetworkQualityEstimate() { |
1229 DCHECK(thread_checker_.CalledOnValidThread()); | 1370 DCHECK(thread_checker_.CalledOnValidThread()); |
1230 DCHECK_LE(cached_network_qualities_.size(), | 1371 DCHECK_LE(cached_network_qualities_.size(), |
1231 static_cast<size_t>(kMaximumNetworkQualityCacheSize)); | 1372 static_cast<size_t>(kMaximumNetworkQualityCacheSize)); |
1232 | 1373 |
1233 // If the network name is unavailable, caching should not be performed. | 1374 // If the network name is unavailable, caching should not be performed. |
1234 if (current_network_id_.id.empty()) | 1375 if (current_network_id_.id.empty()) |
1235 return; | 1376 return; |
1236 | 1377 |
1237 base::TimeDelta http_rtt = nqe::internal::InvalidRTT(); | 1378 base::TimeDelta http_rtt = nqe::internal::InvalidRTT(); |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1360 NotifyObserversOfEffectiveConnectionTypeChanged() { | 1501 NotifyObserversOfEffectiveConnectionTypeChanged() { |
1361 DCHECK(thread_checker_.CalledOnValidThread()); | 1502 DCHECK(thread_checker_.CalledOnValidThread()); |
1362 | 1503 |
1363 // TODO(tbansal): Add hysteresis in the notification. | 1504 // TODO(tbansal): Add hysteresis in the notification. |
1364 FOR_EACH_OBSERVER( | 1505 FOR_EACH_OBSERVER( |
1365 EffectiveConnectionTypeObserver, effective_connection_type_observer_list_, | 1506 EffectiveConnectionTypeObserver, effective_connection_type_observer_list_, |
1366 OnEffectiveConnectionTypeChanged(effective_connection_type_)); | 1507 OnEffectiveConnectionTypeChanged(effective_connection_type_)); |
1367 } | 1508 } |
1368 | 1509 |
1369 } // namespace net | 1510 } // namespace net |
OLD | NEW |