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 27 matching lines...) Expand all Loading... | |
181 static const char* const kSuffixes[] = { | 201 static const char* const kSuffixes[] = { |
182 "0_20", "20_60", "60_140", "140_300", "300_620", | 202 "0_20", "20_60", "60_140", "140_300", "300_620", |
183 "620_1260", "1260_2540", "2540_5100", "5100_Infinity"}; | 203 "620_1260", "1260_2540", "2540_5100", "5100_Infinity"}; |
184 for (size_t i = 0; i < arraysize(kSuffixes) - 1; ++i) { | 204 for (size_t i = 0; i < arraysize(kSuffixes) - 1; ++i) { |
185 if (rtt_milliseconds <= static_cast<float>((20 * (2 << i) - 20))) | 205 if (rtt_milliseconds <= static_cast<float>((20 * (2 << i) - 20))) |
186 return kSuffixes[i]; | 206 return kSuffixes[i]; |
187 } | 207 } |
188 return kSuffixes[arraysize(kSuffixes) - 1]; | 208 return kSuffixes[arraysize(kSuffixes) - 1]; |
189 } | 209 } |
190 | 210 |
211 // The least significant kTrimBits of the metric will be discarded. Next, it | |
212 // will be rounded down so it fits within kBitsPerMetric bits. This implies | |
213 // that if the metric's value is more than 2^(kTrimBits + kBitsPerMetric), | |
214 // then it will be rounded down. | |
Not at Google. Contact bengr
2016/06/30 17:49:44
This is confusing. Can you please rephrase it. How
tbansal1
2016/07/01 00:16:00
Done.
| |
215 const int32_t kTrimBits = 5; | |
216 const int32_t kBitsPerMetric = 7; | |
Not at Google. Contact bengr
2016/06/30 17:49:44
Add:
static_assert(31 > kBitsPerMetric * 4, "4 met
tbansal1
2016/07/01 00:16:00
Done.
| |
217 | |
218 // Trims the |metric| by removing the last kTrimBits, and then rounding down | |
219 // the |metric| such that the |metric| fits in kBitsPerMetric. | |
220 int32_t TrimAndRoundDown(int32_t metric) { | |
Not at Google. Contact bengr
2016/06/30 17:49:44
Rename method to FitInKBitsPerMetricBits?
tbansal1
2016/07/01 00:16:00
Done.
| |
221 // Remove the last kTrimBits. This will allow the metric to fit within | |
222 // kBitsPerMetric while losing only the least significant bits. | |
223 metric = metric >> kTrimBits; | |
224 if (metric >= (1 << kBitsPerMetric)) { | |
225 // Fit |metric| in kBitsPerMetric by rounding it down. | |
226 metric = (1 << kBitsPerMetric) - 1; | |
Not at Google. Contact bengr
2016/06/30 17:49:44
Possible Refactor:
int32_t largest_value_using_kBi
tbansal1
2016/07/01 00:16:01
Done.
| |
227 } | |
228 DCHECK_EQ(0, metric >> kBitsPerMetric); | |
229 return metric; | |
230 } | |
231 | |
191 } // namespace | 232 } // namespace |
192 | 233 |
193 namespace net { | 234 namespace net { |
194 | 235 |
195 NetworkQualityEstimator::NetworkQualityEstimator( | 236 NetworkQualityEstimator::NetworkQualityEstimator( |
196 std::unique_ptr<ExternalEstimateProvider> external_estimates_provider, | 237 std::unique_ptr<ExternalEstimateProvider> external_estimates_provider, |
197 const std::map<std::string, std::string>& variation_params) | 238 const std::map<std::string, std::string>& variation_params) |
198 : NetworkQualityEstimator(std::move(external_estimates_provider), | 239 : NetworkQualityEstimator(std::move(external_estimates_provider), |
199 variation_params, | 240 variation_params, |
200 false, | 241 false, |
(...skipping 22 matching lines...) Expand all Loading... | |
223 effective_connection_type_recomputation_interval_( | 264 effective_connection_type_recomputation_interval_( |
224 base::TimeDelta::FromSeconds(15)), | 265 base::TimeDelta::FromSeconds(15)), |
225 last_connection_change_(tick_clock_->NowTicks()), | 266 last_connection_change_(tick_clock_->NowTicks()), |
226 current_network_id_( | 267 current_network_id_( |
227 NetworkID(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, | 268 NetworkID(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, |
228 std::string())), | 269 std::string())), |
229 downstream_throughput_kbps_observations_(weight_multiplier_per_second_), | 270 downstream_throughput_kbps_observations_(weight_multiplier_per_second_), |
230 rtt_observations_(weight_multiplier_per_second_), | 271 rtt_observations_(weight_multiplier_per_second_), |
231 external_estimate_provider_(std::move(external_estimates_provider)), | 272 external_estimate_provider_(std::move(external_estimates_provider)), |
232 effective_connection_type_(EFFECTIVE_CONNECTION_TYPE_UNKNOWN), | 273 effective_connection_type_(EFFECTIVE_CONNECTION_TYPE_UNKNOWN), |
274 correlation_logging_probability_( | |
275 GetDoubleValueForVariationParamWithDefaultValue( | |
276 variation_params, | |
277 "correlation_logging_probability", | |
278 0.0)), | |
233 weak_ptr_factory_(this) { | 279 weak_ptr_factory_(this) { |
234 static_assert(kDefaultHalfLifeSeconds > 0, | 280 static_assert(kDefaultHalfLifeSeconds > 0, |
235 "Default half life duration must be > 0"); | 281 "Default half life duration must be > 0"); |
236 static_assert(kMaximumNetworkQualityCacheSize > 0, | 282 static_assert(kMaximumNetworkQualityCacheSize > 0, |
237 "Size of the network quality cache must be > 0"); | 283 "Size of the network quality cache must be > 0"); |
238 // This limit should not be increased unless the logic for removing the | 284 // This limit should not be increased unless the logic for removing the |
239 // oldest cache entry is rewritten to use a doubly-linked-list LRU queue. | 285 // oldest cache entry is rewritten to use a doubly-linked-list LRU queue. |
240 static_assert(kMaximumNetworkQualityCacheSize <= 10, | 286 static_assert(kMaximumNetworkQualityCacheSize <= 10, |
241 "Size of the network quality cache must <= 10"); | 287 "Size of the network quality cache must <= 10"); |
242 // None of the algorithms can have an empty name. | 288 // None of the algorithms can have an empty name. |
243 DCHECK(algorithm_name_to_enum_.end() == | 289 DCHECK(algorithm_name_to_enum_.end() == |
244 algorithm_name_to_enum_.find(std::string())); | 290 algorithm_name_to_enum_.find(std::string())); |
245 | 291 |
246 DCHECK_EQ(algorithm_name_to_enum_.size(), | 292 DCHECK_EQ(algorithm_name_to_enum_.size(), |
247 static_cast<size_t>(EffectiveConnectionTypeAlgorithm:: | 293 static_cast<size_t>(EffectiveConnectionTypeAlgorithm:: |
248 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST)); | 294 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST)); |
249 DCHECK_NE(EffectiveConnectionTypeAlgorithm:: | 295 DCHECK_NE(EffectiveConnectionTypeAlgorithm:: |
250 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST, | 296 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST, |
251 effective_connection_type_algorithm_); | 297 effective_connection_type_algorithm_); |
298 DCHECK_LE(0.0, correlation_logging_probability_); | |
299 DCHECK_GE(1.0, correlation_logging_probability_); | |
252 | 300 |
253 ObtainOperatingParams(variation_params); | 301 ObtainOperatingParams(variation_params); |
254 ObtainEffectiveConnectionTypeModelParams(variation_params); | 302 ObtainEffectiveConnectionTypeModelParams(variation_params); |
255 NetworkChangeNotifier::AddConnectionTypeObserver(this); | 303 NetworkChangeNotifier::AddConnectionTypeObserver(this); |
256 if (external_estimate_provider_) { | 304 if (external_estimate_provider_) { |
257 RecordExternalEstimateProviderMetrics( | 305 RecordExternalEstimateProviderMetrics( |
258 EXTERNAL_ESTIMATE_PROVIDER_STATUS_AVAILABLE); | 306 EXTERNAL_ESTIMATE_PROVIDER_STATUS_AVAILABLE); |
259 external_estimate_provider_->SetUpdatedEstimateDelegate(this); | 307 external_estimate_provider_->SetUpdatedEstimateDelegate(this); |
260 } else { | 308 } else { |
261 RecordExternalEstimateProviderMetrics( | 309 RecordExternalEstimateProviderMetrics( |
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
483 | 531 |
484 LoadTimingInfo load_timing_info; | 532 LoadTimingInfo load_timing_info; |
485 request.GetLoadTimingInfo(&load_timing_info); | 533 request.GetLoadTimingInfo(&load_timing_info); |
486 | 534 |
487 // If the load timing info is unavailable, it probably means that the request | 535 // If the load timing info is unavailable, it probably means that the request |
488 // did not go over the network. | 536 // did not go over the network. |
489 if (load_timing_info.send_start.is_null() || | 537 if (load_timing_info.send_start.is_null() || |
490 load_timing_info.receive_headers_end.is_null()) { | 538 load_timing_info.receive_headers_end.is_null()) { |
491 return; | 539 return; |
492 } | 540 } |
541 DCHECK(!request.response_info().was_cached); | |
493 | 542 |
494 // Duration between when the resource was requested and when the response | 543 // Duration between when the resource was requested and when the response |
495 // headers were received. | 544 // headers were received. |
496 base::TimeDelta observed_http_rtt = | 545 base::TimeDelta observed_http_rtt = |
497 load_timing_info.receive_headers_end - load_timing_info.send_start; | 546 load_timing_info.receive_headers_end - load_timing_info.send_start; |
498 DCHECK_GE(observed_http_rtt, base::TimeDelta()); | 547 DCHECK_GE(observed_http_rtt, base::TimeDelta()); |
499 if (observed_http_rtt < peak_network_quality_.http_rtt()) { | 548 if (observed_http_rtt < peak_network_quality_.http_rtt()) { |
500 peak_network_quality_ = nqe::internal::NetworkQuality( | 549 peak_network_quality_ = nqe::internal::NetworkQuality( |
501 observed_http_rtt, peak_network_quality_.transport_rtt(), | 550 observed_http_rtt, peak_network_quality_.transport_rtt(), |
502 peak_network_quality_.downstream_throughput_kbps()); | 551 peak_network_quality_.downstream_throughput_kbps()); |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
577 void NetworkQualityEstimator::NotifyRequestCompleted( | 626 void NetworkQualityEstimator::NotifyRequestCompleted( |
578 const URLRequest& request) { | 627 const URLRequest& request) { |
579 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"), | 628 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"), |
580 "NetworkQualityEstimator::NotifyRequestCompleted"); | 629 "NetworkQualityEstimator::NotifyRequestCompleted"); |
581 DCHECK(thread_checker_.CalledOnValidThread()); | 630 DCHECK(thread_checker_.CalledOnValidThread()); |
582 | 631 |
583 if (!RequestSchemeIsHTTPOrHTTPS(request)) | 632 if (!RequestSchemeIsHTTPOrHTTPS(request)) |
584 return; | 633 return; |
585 | 634 |
586 throughput_analyzer_->NotifyRequestCompleted(request); | 635 throughput_analyzer_->NotifyRequestCompleted(request); |
636 RecordCorrelationMetric(request); | |
637 } | |
638 | |
639 void NetworkQualityEstimator::RecordCorrelationMetric( | |
640 const URLRequest& request) const { | |
641 DCHECK(thread_checker_.CalledOnValidThread()); | |
642 | |
643 // The histogram is recorded with probability | |
644 // |correlation_logging_probability_| to reduce overhead. | |
645 if (base::RandDouble() >= correlation_logging_probability_) | |
Not at Google. Contact bengr
2016/06/30 17:49:44
Shouldn't this be base::RandDouble() <= correlatio
tbansal1
2016/07/01 00:16:00
No. Say correlation_logging_probability_ is 1.0. T
Not at Google. Contact bengr
2016/07/01 18:26:26
Misread as condition to execute below. You are rig
| |
646 return; | |
647 | |
648 if (request.response_info().was_cached) | |
649 return; | |
650 LoadTimingInfo load_timing_info; | |
651 request.GetLoadTimingInfo(&load_timing_info); | |
652 | |
653 // If the load timing info is unavailable, it probably means that the request | |
654 // did not go over the network. | |
Not at Google. Contact bengr
2016/06/30 17:49:44
Could this be replaced with request->response_info
tbansal1
2016/07/01 00:16:01
Done.
| |
655 if (load_timing_info.send_start.is_null() || | |
656 load_timing_info.receive_headers_end.is_null()) { | |
657 return; | |
658 } | |
659 | |
660 // Record UMA only for successful requests that have completed. | |
661 if (!request.status().is_success() || request.status().is_io_pending()) | |
662 return; | |
663 if (request.GetResponseCode() != HTTP_OK) | |
664 return; | |
665 if (load_timing_info.receive_headers_end < last_main_frame_request_) | |
666 return; | |
Not at Google. Contact bengr
2016/06/30 17:49:44
Nit: Add new line.
tbansal1
2016/07/01 00:16:00
Done.
| |
667 const base::TimeTicks now = tick_clock_->NowTicks(); | |
668 // Record UMA only for requests that started recently. | |
669 if (now - last_main_frame_request_ > base::TimeDelta::FromSeconds(15)) | |
Not at Google. Contact bengr
2016/06/30 17:49:44
Why ignore older requests?
tbansal1
2016/07/01 00:16:00
It is possible that network quality was recorded a
Not at Google. Contact bengr
2016/07/01 18:26:26
Acknowledged.
| |
670 return; | |
671 | |
672 DCHECK_GE(now, load_timing_info.send_start); | |
Not at Google. Contact bengr
2016/06/30 17:49:44
Not sure this is necessary.
tbansal1
2016/07/01 00:16:01
It checks that we are not adding negative values t
Not at Google. Contact bengr
2016/07/01 18:26:26
Acknowledged.
| |
673 | |
674 // Histogram samples are 32-bit in size with first bit reserved for sign. | |
675 // Four metrics will be recorded per sample. | |
676 DCHECK_GE(31, kBitsPerMetric * 4); | |
Not at Google. Contact bengr
2016/06/30 17:49:44
Prefer replacing with static assert above where kB
tbansal1
2016/07/01 00:16:01
Done.
| |
677 | |
678 const int32_t transport_rtt_milliseconds = | |
Not at Google. Contact bengr
2016/06/30 17:49:44
Does it help to extract estimated_quality_at_last_
tbansal1
2016/07/01 00:16:00
I am not sure. I wanted to declare it as a const v
| |
679 estimated_quality_at_last_main_frame_.transport_rtt() != | |
680 nqe::internal::InvalidRTT() | |
681 ? TrimAndRoundDown( | |
682 estimated_quality_at_last_main_frame_.transport_rtt() | |
683 .InMilliseconds()) | |
684 : 0; | |
685 | |
686 const int32_t http_rtt_milliseconds = | |
687 estimated_quality_at_last_main_frame_.http_rtt() != | |
688 nqe::internal::InvalidRTT() | |
689 ? TrimAndRoundDown(estimated_quality_at_last_main_frame_.http_rtt() | |
690 .InMilliseconds()) | |
691 : 0; | |
692 | |
693 const int32_t downstream_throughput_kbps = | |
694 estimated_quality_at_last_main_frame_.downstream_throughput_kbps() != | |
695 nqe::internal::kInvalidThroughput | |
696 ? TrimAndRoundDown(estimated_quality_at_last_main_frame_ | |
697 .downstream_throughput_kbps()) | |
698 : 0; | |
699 | |
700 const int32_t resource_load_time_milliseconds = | |
701 TrimAndRoundDown((now - load_timing_info.send_start).InMilliseconds()); | |
702 | |
703 // First 32 - (4* kBitsPerMetric) of the sample are unset. Next | |
704 // kBitsPerMetric of the sample contain |transport_rtt_milliseconds|. | |
705 // Next kBitsPerMetric contain |http_rtt_milliseconds|. Next kBitsPerMetric | |
706 // contain |downstream_throughput_kbps|. And, the last kBitsPerMetric | |
707 // contain |resource_load_time_milliseconds|. | |
708 int32_t sample = transport_rtt_milliseconds; | |
Not at Google. Contact bengr
2016/06/30 17:49:44
Suggest adding DCHECK_EQ(0, transport_rtt_millisec
tbansal1
2016/07/01 00:16:00
Done.
| |
709 sample = (sample << kBitsPerMetric) + http_rtt_milliseconds; | |
Not at Google. Contact bengr
2016/06/30 17:49:44
Minor: | seems prefereable to +.
tbansal1
2016/07/01 00:16:00
Done.
| |
710 sample = (sample << kBitsPerMetric) + downstream_throughput_kbps; | |
711 sample = (sample << kBitsPerMetric) + resource_load_time_milliseconds; | |
712 | |
713 UMA_HISTOGRAM_SPARSE_SLOWLY("NQE.Correlation.ResourceLoadTime", sample); | |
587 } | 714 } |
588 | 715 |
589 void NetworkQualityEstimator::NotifyURLRequestDestroyed( | 716 void NetworkQualityEstimator::NotifyURLRequestDestroyed( |
590 const URLRequest& request) { | 717 const URLRequest& request) { |
591 DCHECK(thread_checker_.CalledOnValidThread()); | 718 DCHECK(thread_checker_.CalledOnValidThread()); |
592 | 719 |
593 NotifyRequestCompleted(request); | 720 NotifyRequestCompleted(request); |
594 } | 721 } |
595 | 722 |
596 void NetworkQualityEstimator::AddRTTObserver(RTTObserver* rtt_observer) { | 723 void NetworkQualityEstimator::AddRTTObserver(RTTObserver* rtt_observer) { |
(...skipping 692 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1289 NotifyObserversOfEffectiveConnectionTypeChanged() { | 1416 NotifyObserversOfEffectiveConnectionTypeChanged() { |
1290 DCHECK(thread_checker_.CalledOnValidThread()); | 1417 DCHECK(thread_checker_.CalledOnValidThread()); |
1291 | 1418 |
1292 // TODO(tbansal): Add hysteresis in the notification. | 1419 // TODO(tbansal): Add hysteresis in the notification. |
1293 FOR_EACH_OBSERVER( | 1420 FOR_EACH_OBSERVER( |
1294 EffectiveConnectionTypeObserver, effective_connection_type_observer_list_, | 1421 EffectiveConnectionTypeObserver, effective_connection_type_observer_list_, |
1295 OnEffectiveConnectionTypeChanged(effective_connection_type_)); | 1422 OnEffectiveConnectionTypeChanged(effective_connection_type_)); |
1296 } | 1423 } |
1297 | 1424 |
1298 } // namespace net | 1425 } // namespace net |
OLD | NEW |