Chromium Code Reviews| 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> |
| (...skipping 371 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 382 std::string())), | 382 std::string())), |
| 383 downstream_throughput_kbps_observations_(weight_multiplier_per_second_), | 383 downstream_throughput_kbps_observations_(weight_multiplier_per_second_), |
| 384 rtt_observations_(weight_multiplier_per_second_), | 384 rtt_observations_(weight_multiplier_per_second_), |
| 385 effective_connection_type_at_last_main_frame_( | 385 effective_connection_type_at_last_main_frame_( |
| 386 EFFECTIVE_CONNECTION_TYPE_UNKNOWN), | 386 EFFECTIVE_CONNECTION_TYPE_UNKNOWN), |
| 387 external_estimate_provider_(std::move(external_estimates_provider)), | 387 external_estimate_provider_(std::move(external_estimates_provider)), |
| 388 effective_connection_type_recomputation_interval_( | 388 effective_connection_type_recomputation_interval_( |
| 389 base::TimeDelta::FromSeconds(10)), | 389 base::TimeDelta::FromSeconds(10)), |
| 390 rtt_observations_size_at_last_ect_computation_(0), | 390 rtt_observations_size_at_last_ect_computation_(0), |
| 391 throughput_observations_size_at_last_ect_computation_(0), | 391 throughput_observations_size_at_last_ect_computation_(0), |
| 392 http_rtt_(nqe::internal::InvalidRTT()), | |
| 393 transport_rtt_(nqe::internal::InvalidRTT()), | |
| 394 downstream_throughput_kbps_(nqe::internal::kInvalidThroughput), | |
| 395 effective_connection_type_(EFFECTIVE_CONNECTION_TYPE_UNKNOWN), | 392 effective_connection_type_(EFFECTIVE_CONNECTION_TYPE_UNKNOWN), |
| 396 min_signal_strength_since_connection_change_(INT32_MAX), | 393 min_signal_strength_since_connection_change_(INT32_MAX), |
| 397 max_signal_strength_since_connection_change_(INT32_MIN), | 394 max_signal_strength_since_connection_change_(INT32_MIN), |
| 398 correlation_uma_logging_probability_( | 395 correlation_uma_logging_probability_( |
| 399 GetDoubleValueForVariationParamWithDefaultValue( | 396 GetDoubleValueForVariationParamWithDefaultValue( |
| 400 variation_params, | 397 variation_params, |
| 401 "correlation_logging_probability", | 398 "correlation_logging_probability", |
| 402 0.0)), | 399 0.0)), |
| 403 forced_effective_connection_type_set_( | 400 forced_effective_connection_type_set_( |
| 404 !GetStringValueForVariationParamWithDefaultValue( | 401 !GetStringValueForVariationParamWithDefaultValue( |
| (...skipping 349 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 754 // 2. This can happen if the task is executed much later than its scheduled | 751 // 2. This can happen if the task is executed much later than its scheduled |
| 755 // time. Returning here ensures that we do not take inaccurate readings. | 752 // time. Returning here ensures that we do not take inaccurate readings. |
| 756 if (now - last_main_frame_request_ > 2 * measuring_duration) | 753 if (now - last_main_frame_request_ > 2 * measuring_duration) |
| 757 return; | 754 return; |
| 758 | 755 |
| 759 // Do not record accuracy if there was a connection change since the last main | 756 // Do not record accuracy if there was a connection change since the last main |
| 760 // frame request. | 757 // frame request. |
| 761 if (last_main_frame_request_ <= last_connection_change_) | 758 if (last_main_frame_request_ <= last_connection_change_) |
| 762 return; | 759 return; |
| 763 | 760 |
| 764 base::TimeDelta recent_http_rtt; | 761 base::TimeDelta recent_http_rtt = nqe::internal::InvalidRTT(); |
|
RyanSturm
2016/10/31 19:22:10
nit: Is this part necessary? If GetRecentHttpRTT r
tbansal1
2016/10/31 21:34:51
Done.
| |
| 762 if (!GetRecentHttpRTT(last_main_frame_request_, &recent_http_rtt)) | |
| 763 recent_http_rtt = nqe::internal::InvalidRTT(); | |
| 764 | |
| 765 if (estimated_quality_at_last_main_frame_.http_rtt() != | 765 if (estimated_quality_at_last_main_frame_.http_rtt() != |
| 766 nqe::internal::InvalidRTT() && | 766 nqe::internal::InvalidRTT() && |
| 767 GetRecentHttpRTT(last_main_frame_request_, &recent_http_rtt)) { | 767 recent_http_rtt != nqe::internal::InvalidRTT()) { |
| 768 const int estimated_observed_diff_milliseconds = | 768 const int estimated_observed_diff_milliseconds = |
| 769 estimated_quality_at_last_main_frame_.http_rtt().InMilliseconds() - | 769 estimated_quality_at_last_main_frame_.http_rtt().InMilliseconds() - |
| 770 recent_http_rtt.InMilliseconds(); | 770 recent_http_rtt.InMilliseconds(); |
| 771 | 771 |
| 772 RecordRTTAccuracy("NQE.Accuracy.HttpRTT", | 772 RecordRTTAccuracy("NQE.Accuracy.HttpRTT", |
| 773 estimated_observed_diff_milliseconds, measuring_duration, | 773 estimated_observed_diff_milliseconds, measuring_duration, |
| 774 recent_http_rtt); | 774 recent_http_rtt); |
| 775 } | 775 } |
| 776 | 776 |
| 777 base::TimeDelta recent_transport_rtt; | 777 base::TimeDelta recent_transport_rtt; |
| (...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1020 } | 1020 } |
| 1021 | 1021 |
| 1022 void NetworkQualityEstimator::OnConnectionTypeChanged( | 1022 void NetworkQualityEstimator::OnConnectionTypeChanged( |
| 1023 NetworkChangeNotifier::ConnectionType type) { | 1023 NetworkChangeNotifier::ConnectionType type) { |
| 1024 DCHECK(thread_checker_.CalledOnValidThread()); | 1024 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1025 | 1025 |
| 1026 RecordMetricsOnConnectionTypeChanged(); | 1026 RecordMetricsOnConnectionTypeChanged(); |
| 1027 | 1027 |
| 1028 // Write the estimates of the previous network to the cache. | 1028 // Write the estimates of the previous network to the cache. |
| 1029 network_quality_store_->Add( | 1029 network_quality_store_->Add( |
| 1030 current_network_id_, | 1030 current_network_id_, nqe::internal::CachedNetworkQuality( |
| 1031 nqe::internal::CachedNetworkQuality( | 1031 last_effective_connection_type_computation_, |
| 1032 last_effective_connection_type_computation_, | 1032 network_quality_, effective_connection_type_)); |
| 1033 estimated_quality_at_last_main_frame_, effective_connection_type_)); | |
| 1034 | 1033 |
| 1035 // Clear the local state. | 1034 // Clear the local state. |
| 1036 last_connection_change_ = tick_clock_->NowTicks(); | 1035 last_connection_change_ = tick_clock_->NowTicks(); |
| 1037 peak_network_quality_ = nqe::internal::NetworkQuality(); | 1036 peak_network_quality_ = nqe::internal::NetworkQuality(); |
| 1038 downstream_throughput_kbps_observations_.Clear(); | 1037 downstream_throughput_kbps_observations_.Clear(); |
| 1039 rtt_observations_.Clear(); | 1038 rtt_observations_.Clear(); |
| 1040 | 1039 |
| 1041 #if defined(OS_ANDROID) | 1040 #if defined(OS_ANDROID) |
| 1042 if (NetworkChangeNotifier::IsConnectionCellular(current_network_id_.type)) { | 1041 if (NetworkChangeNotifier::IsConnectionCellular(current_network_id_.type)) { |
| 1043 UMA_HISTOGRAM_BOOLEAN( | 1042 UMA_HISTOGRAM_BOOLEAN( |
| 1044 "NQE.CellularSignalStrengthAvailable", | 1043 "NQE.CellularSignalStrengthAvailable", |
| 1045 min_signal_strength_since_connection_change_ != INT32_MAX && | 1044 min_signal_strength_since_connection_change_ != INT32_MAX && |
| 1046 max_signal_strength_since_connection_change_ != INT32_MIN); | 1045 max_signal_strength_since_connection_change_ != INT32_MIN); |
| 1047 } | 1046 } |
| 1048 #endif // OS_ANDROID | 1047 #endif // OS_ANDROID |
| 1049 min_signal_strength_since_connection_change_ = INT32_MAX; | 1048 min_signal_strength_since_connection_change_ = INT32_MAX; |
| 1050 max_signal_strength_since_connection_change_ = INT32_MIN; | 1049 max_signal_strength_since_connection_change_ = INT32_MIN; |
| 1051 estimated_quality_at_last_main_frame_ = nqe::internal::NetworkQuality(); | 1050 network_quality_ = nqe::internal::NetworkQuality(); |
| 1052 http_rtt_ = nqe::internal::InvalidRTT(); | |
| 1053 transport_rtt_ = nqe::internal::InvalidRTT(); | |
| 1054 downstream_throughput_kbps_ = nqe::internal::kInvalidThroughput; | |
| 1055 effective_connection_type_ = EFFECTIVE_CONNECTION_TYPE_UNKNOWN; | 1051 effective_connection_type_ = EFFECTIVE_CONNECTION_TYPE_UNKNOWN; |
| 1056 effective_connection_type_at_last_main_frame_ = | 1052 effective_connection_type_at_last_main_frame_ = |
| 1057 EFFECTIVE_CONNECTION_TYPE_UNKNOWN; | 1053 EFFECTIVE_CONNECTION_TYPE_UNKNOWN; |
| 1058 | 1054 |
| 1059 // Update the local state as part of preparation for the new connection. | 1055 // Update the local state as part of preparation for the new connection. |
| 1060 current_network_id_ = GetCurrentNetworkID(); | 1056 current_network_id_ = GetCurrentNetworkID(); |
| 1061 RecordNetworkIDAvailability(); | 1057 RecordNetworkIDAvailability(); |
| 1062 | 1058 |
| 1063 MaybeQueryExternalEstimateProvider(); | 1059 MaybeQueryExternalEstimateProvider(); |
| 1064 | 1060 |
| (...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1231 } | 1227 } |
| 1232 | 1228 |
| 1233 void NetworkQualityEstimator::ComputeEffectiveConnectionType() { | 1229 void NetworkQualityEstimator::ComputeEffectiveConnectionType() { |
| 1234 DCHECK(thread_checker_.CalledOnValidThread()); | 1230 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1235 | 1231 |
| 1236 const base::TimeTicks now = tick_clock_->NowTicks(); | 1232 const base::TimeTicks now = tick_clock_->NowTicks(); |
| 1237 | 1233 |
| 1238 const EffectiveConnectionType past_type = effective_connection_type_; | 1234 const EffectiveConnectionType past_type = effective_connection_type_; |
| 1239 last_effective_connection_type_computation_ = now; | 1235 last_effective_connection_type_computation_ = now; |
| 1240 | 1236 |
| 1237 base::TimeDelta http_rtt = nqe::internal::InvalidRTT(); | |
| 1238 base::TimeDelta transport_rtt = nqe::internal::InvalidRTT(); | |
| 1239 int32_t downstream_throughput_kbps = nqe::internal::kInvalidThroughput; | |
| 1240 | |
| 1241 effective_connection_type_ = | 1241 effective_connection_type_ = |
| 1242 GetRecentEffectiveConnectionTypeAndNetworkQuality( | 1242 GetRecentEffectiveConnectionTypeAndNetworkQuality( |
| 1243 base::TimeTicks(), &http_rtt_, &transport_rtt_, | 1243 base::TimeTicks(), &http_rtt, &transport_rtt, |
| 1244 &downstream_throughput_kbps_); | 1244 &downstream_throughput_kbps); |
| 1245 | |
| 1246 network_quality_ = nqe::internal::NetworkQuality(http_rtt, transport_rtt, | |
| 1247 downstream_throughput_kbps); | |
| 1248 | |
| 1245 NotifyObserversOfRTTOrThroughputComputed(); | 1249 NotifyObserversOfRTTOrThroughputComputed(); |
| 1246 | 1250 |
| 1247 if (past_type != effective_connection_type_) | 1251 if (past_type != effective_connection_type_) |
| 1248 NotifyObserversOfEffectiveConnectionTypeChanged(); | 1252 NotifyObserversOfEffectiveConnectionTypeChanged(); |
| 1249 | 1253 |
| 1250 rtt_observations_size_at_last_ect_computation_ = rtt_observations_.Size(); | 1254 rtt_observations_size_at_last_ect_computation_ = rtt_observations_.Size(); |
| 1251 throughput_observations_size_at_last_ect_computation_ = | 1255 throughput_observations_size_at_last_ect_computation_ = |
| 1252 downstream_throughput_kbps_observations_.Size(); | 1256 downstream_throughput_kbps_observations_.Size(); |
| 1253 } | 1257 } |
| 1254 | 1258 |
| 1255 EffectiveConnectionType NetworkQualityEstimator::GetEffectiveConnectionType() | 1259 EffectiveConnectionType NetworkQualityEstimator::GetEffectiveConnectionType() |
| 1256 const { | 1260 const { |
| 1257 DCHECK(thread_checker_.CalledOnValidThread()); | 1261 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1258 return effective_connection_type_; | 1262 return effective_connection_type_; |
| 1259 } | 1263 } |
| 1260 | 1264 |
| 1261 EffectiveConnectionType | 1265 EffectiveConnectionType |
| 1262 NetworkQualityEstimator::GetRecentEffectiveConnectionType( | 1266 NetworkQualityEstimator::GetRecentEffectiveConnectionType( |
| 1263 const base::TimeTicks& start_time) const { | 1267 const base::TimeTicks& start_time) const { |
| 1264 DCHECK(thread_checker_.CalledOnValidThread()); | 1268 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1265 | 1269 |
| 1266 base::TimeDelta http_rtt; | 1270 base::TimeDelta http_rtt = nqe::internal::InvalidRTT(); |
| 1267 base::TimeDelta transport_rtt; | 1271 base::TimeDelta transport_rtt = nqe::internal::InvalidRTT(); |
| 1268 int32_t downstream_throughput_kbps; | 1272 int32_t downstream_throughput_kbps = nqe::internal::kInvalidThroughput; |
| 1269 | 1273 |
| 1270 return GetRecentEffectiveConnectionTypeAndNetworkQuality( | 1274 return GetRecentEffectiveConnectionTypeAndNetworkQuality( |
| 1271 start_time, &http_rtt, &transport_rtt, &downstream_throughput_kbps); | 1275 start_time, &http_rtt, &transport_rtt, &downstream_throughput_kbps); |
| 1272 } | 1276 } |
| 1273 | 1277 |
| 1274 EffectiveConnectionType | 1278 EffectiveConnectionType |
| 1275 NetworkQualityEstimator::GetRecentEffectiveConnectionTypeAndNetworkQuality( | 1279 NetworkQualityEstimator::GetRecentEffectiveConnectionTypeAndNetworkQuality( |
| 1276 const base::TimeTicks& start_time, | 1280 const base::TimeTicks& start_time, |
| 1277 base::TimeDelta* http_rtt, | 1281 base::TimeDelta* http_rtt, |
| 1278 base::TimeDelta* transport_rtt, | 1282 base::TimeDelta* transport_rtt, |
| (...skipping 312 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1591 cached_estimate_available); | 1595 cached_estimate_available); |
| 1592 | 1596 |
| 1593 if (!cached_estimate_available) | 1597 if (!cached_estimate_available) |
| 1594 return false; | 1598 return false; |
| 1595 | 1599 |
| 1596 const base::TimeTicks now = tick_clock_->NowTicks(); | 1600 const base::TimeTicks now = tick_clock_->NowTicks(); |
| 1597 | 1601 |
| 1598 if (effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_UNKNOWN) { | 1602 if (effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_UNKNOWN) { |
| 1599 // Read the effective connection type from the cached estimate. | 1603 // Read the effective connection type from the cached estimate. |
| 1600 last_effective_connection_type_computation_ = now; | 1604 last_effective_connection_type_computation_ = now; |
| 1601 http_rtt_ = cached_network_quality.network_quality().http_rtt(); | 1605 network_quality_ = cached_network_quality.network_quality(); |
| 1602 transport_rtt_ = cached_network_quality.network_quality().transport_rtt(); | |
| 1603 downstream_throughput_kbps_ = | |
| 1604 cached_network_quality.network_quality().downstream_throughput_kbps(); | |
| 1605 effective_connection_type_ = | 1606 effective_connection_type_ = |
| 1606 cached_network_quality.effective_connection_type(); | 1607 cached_network_quality.effective_connection_type(); |
| 1607 | 1608 |
| 1608 if (effective_connection_type_ != EFFECTIVE_CONNECTION_TYPE_UNKNOWN) | 1609 if (effective_connection_type_ != EFFECTIVE_CONNECTION_TYPE_UNKNOWN) |
| 1609 NotifyObserversOfEffectiveConnectionTypeChanged(); | 1610 NotifyObserversOfEffectiveConnectionTypeChanged(); |
| 1610 } | 1611 } |
| 1611 | 1612 |
| 1612 if (cached_network_quality.network_quality().downstream_throughput_kbps() != | 1613 if (cached_network_quality.network_quality().downstream_throughput_kbps() != |
| 1613 nqe::internal::kInvalidThroughput) { | 1614 nqe::internal::kInvalidThroughput) { |
| 1614 ThroughputObservation througphput_observation( | 1615 ThroughputObservation througphput_observation( |
| (...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1772 NotifyObserversOfEffectiveConnectionTypeChanged() { | 1773 NotifyObserversOfEffectiveConnectionTypeChanged() { |
| 1773 DCHECK(thread_checker_.CalledOnValidThread()); | 1774 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1774 DCHECK_NE(EFFECTIVE_CONNECTION_TYPE_LAST, effective_connection_type_); | 1775 DCHECK_NE(EFFECTIVE_CONNECTION_TYPE_LAST, effective_connection_type_); |
| 1775 | 1776 |
| 1776 // TODO(tbansal): Add hysteresis in the notification. | 1777 // TODO(tbansal): Add hysteresis in the notification. |
| 1777 for (auto& observer : effective_connection_type_observer_list_) | 1778 for (auto& observer : effective_connection_type_observer_list_) |
| 1778 observer.OnEffectiveConnectionTypeChanged(effective_connection_type_); | 1779 observer.OnEffectiveConnectionTypeChanged(effective_connection_type_); |
| 1779 | 1780 |
| 1780 // Add the estimates of the current network to the cache store. | 1781 // Add the estimates of the current network to the cache store. |
| 1781 if (effective_connection_type_ != EFFECTIVE_CONNECTION_TYPE_UNKNOWN) { | 1782 if (effective_connection_type_ != EFFECTIVE_CONNECTION_TYPE_UNKNOWN) { |
| 1782 network_quality_store_->Add( | 1783 network_quality_store_->Add(current_network_id_, |
| 1783 current_network_id_, | 1784 nqe::internal::CachedNetworkQuality( |
| 1784 nqe::internal::CachedNetworkQuality( | 1785 tick_clock_->NowTicks(), network_quality_, |
| 1785 tick_clock_->NowTicks(), estimated_quality_at_last_main_frame_, | 1786 effective_connection_type_)); |
| 1786 effective_connection_type_)); | |
| 1787 } | 1787 } |
| 1788 } | 1788 } |
| 1789 | 1789 |
| 1790 void NetworkQualityEstimator::NotifyObserversOfRTTOrThroughputComputed() const { | 1790 void NetworkQualityEstimator::NotifyObserversOfRTTOrThroughputComputed() const { |
| 1791 DCHECK(thread_checker_.CalledOnValidThread()); | 1791 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1792 | 1792 |
| 1793 // TODO(tbansal): Add hysteresis in the notification. | 1793 // TODO(tbansal): Add hysteresis in the notification. |
| 1794 for (auto& observer : rtt_and_throughput_estimates_observer_list_) { | 1794 for (auto& observer : rtt_and_throughput_estimates_observer_list_) { |
| 1795 observer.OnRTTOrThroughputEstimatesComputed(http_rtt_, transport_rtt_, | 1795 observer.OnRTTOrThroughputEstimatesComputed( |
| 1796 downstream_throughput_kbps_); | 1796 network_quality_.http_rtt(), network_quality_.transport_rtt(), |
| 1797 network_quality_.downstream_throughput_kbps()); | |
| 1797 } | 1798 } |
| 1798 } | 1799 } |
| 1799 | 1800 |
| 1800 void NetworkQualityEstimator::AddNetworkQualitiesCacheObserver( | 1801 void NetworkQualityEstimator::AddNetworkQualitiesCacheObserver( |
| 1801 nqe::internal::NetworkQualityStore::NetworkQualitiesCacheObserver* | 1802 nqe::internal::NetworkQualityStore::NetworkQualitiesCacheObserver* |
| 1802 observer) { | 1803 observer) { |
| 1803 DCHECK(thread_checker_.CalledOnValidThread()); | 1804 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1804 network_quality_store_->AddNetworkQualitiesCacheObserver(observer); | 1805 network_quality_store_->AddNetworkQualitiesCacheObserver(observer); |
| 1805 } | 1806 } |
| 1806 | 1807 |
| 1807 void NetworkQualityEstimator::RemoveNetworkQualitiesCacheObserver( | 1808 void NetworkQualityEstimator::RemoveNetworkQualitiesCacheObserver( |
| 1808 nqe::internal::NetworkQualityStore::NetworkQualitiesCacheObserver* | 1809 nqe::internal::NetworkQualityStore::NetworkQualitiesCacheObserver* |
| 1809 observer) { | 1810 observer) { |
| 1810 DCHECK(thread_checker_.CalledOnValidThread()); | 1811 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1811 network_quality_store_->RemoveNetworkQualitiesCacheObserver(observer); | 1812 network_quality_store_->RemoveNetworkQualitiesCacheObserver(observer); |
| 1812 } | 1813 } |
| 1813 | 1814 |
| 1814 void NetworkQualityEstimator::OnPrefsRead( | 1815 void NetworkQualityEstimator::OnPrefsRead( |
| 1815 const std::map<nqe::internal::NetworkID, | 1816 const std::map<nqe::internal::NetworkID, |
| 1816 nqe::internal::CachedNetworkQuality> read_prefs) { | 1817 nqe::internal::CachedNetworkQuality> read_prefs) { |
| 1817 DCHECK(thread_checker_.CalledOnValidThread()); | 1818 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1818 UMA_HISTOGRAM_COUNTS("NQE.Prefs.ReadSize", read_prefs.size()); | 1819 UMA_HISTOGRAM_COUNTS("NQE.Prefs.ReadSize", read_prefs.size()); |
| 1819 // TODO(tbansal): crbug.com/490870. Incorporate the network quality into the | 1820 // TODO(tbansal): crbug.com/490870. Incorporate the network quality into the |
| 1820 // current estimates. | 1821 // current estimates. |
| 1821 } | 1822 } |
| 1822 | 1823 |
| 1823 } // namespace net | 1824 } // namespace net |
| OLD | NEW |