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 289 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
300 algorithm_name_to_enum_.find(GetEffectiveConnectionTypeAlgorithm( | 300 algorithm_name_to_enum_.find(GetEffectiveConnectionTypeAlgorithm( |
301 variation_params)) == algorithm_name_to_enum_.end() | 301 variation_params)) == algorithm_name_to_enum_.end() |
302 ? kDefaultEffectiveConnectionTypeAlgorithm | 302 ? kDefaultEffectiveConnectionTypeAlgorithm |
303 : algorithm_name_to_enum_ | 303 : algorithm_name_to_enum_ |
304 .find(GetEffectiveConnectionTypeAlgorithm(variation_params)) | 304 .find(GetEffectiveConnectionTypeAlgorithm(variation_params)) |
305 ->second), | 305 ->second), |
306 tick_clock_(new base::DefaultTickClock()), | 306 tick_clock_(new base::DefaultTickClock()), |
307 effective_connection_type_recomputation_interval_( | 307 effective_connection_type_recomputation_interval_( |
308 base::TimeDelta::FromSeconds(15)), | 308 base::TimeDelta::FromSeconds(15)), |
309 last_connection_change_(tick_clock_->NowTicks()), | 309 last_connection_change_(tick_clock_->NowTicks()), |
310 current_network_id_( | 310 current_network_id_(nqe::internal::NetworkID( |
311 NetworkID(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, | 311 NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, |
312 std::string())), | 312 std::string())), |
313 downstream_throughput_kbps_observations_(weight_multiplier_per_second_), | 313 downstream_throughput_kbps_observations_(weight_multiplier_per_second_), |
314 rtt_observations_(weight_multiplier_per_second_), | 314 rtt_observations_(weight_multiplier_per_second_), |
315 effective_connection_type_at_last_main_frame_( | 315 effective_connection_type_at_last_main_frame_( |
316 EFFECTIVE_CONNECTION_TYPE_UNKNOWN), | 316 EFFECTIVE_CONNECTION_TYPE_UNKNOWN), |
317 external_estimate_provider_(std::move(external_estimates_provider)), | 317 external_estimate_provider_(std::move(external_estimates_provider)), |
318 effective_connection_type_(EFFECTIVE_CONNECTION_TYPE_UNKNOWN), | 318 effective_connection_type_(EFFECTIVE_CONNECTION_TYPE_UNKNOWN), |
319 min_signal_strength_since_connection_change_(INT32_MAX), | 319 min_signal_strength_since_connection_change_(INT32_MAX), |
320 max_signal_strength_since_connection_change_(INT32_MIN), | 320 max_signal_strength_since_connection_change_(INT32_MIN), |
321 correlation_uma_logging_probability_( | 321 correlation_uma_logging_probability_( |
322 GetDoubleValueForVariationParamWithDefaultValue( | 322 GetDoubleValueForVariationParamWithDefaultValue( |
323 variation_params, | 323 variation_params, |
324 "correlation_logging_probability", | 324 "correlation_logging_probability", |
325 0.0)), | 325 0.0)), |
326 weak_ptr_factory_(this) { | 326 weak_ptr_factory_(this) { |
327 static_assert(kDefaultHalfLifeSeconds > 0, | 327 static_assert(kDefaultHalfLifeSeconds > 0, |
328 "Default half life duration must be > 0"); | 328 "Default half life duration must be > 0"); |
329 static_assert(kMaximumNetworkQualityCacheSize > 0, | |
330 "Size of the network quality cache must be > 0"); | |
331 // This limit should not be increased unless the logic for removing the | |
332 // oldest cache entry is rewritten to use a doubly-linked-list LRU queue. | |
333 static_assert(kMaximumNetworkQualityCacheSize <= 10, | |
334 "Size of the network quality cache must <= 10"); | |
335 // None of the algorithms can have an empty name. | 329 // None of the algorithms can have an empty name. |
336 DCHECK(algorithm_name_to_enum_.end() == | 330 DCHECK(algorithm_name_to_enum_.end() == |
337 algorithm_name_to_enum_.find(std::string())); | 331 algorithm_name_to_enum_.find(std::string())); |
338 | 332 |
339 DCHECK_EQ(algorithm_name_to_enum_.size(), | 333 DCHECK_EQ(algorithm_name_to_enum_.size(), |
340 static_cast<size_t>(EffectiveConnectionTypeAlgorithm:: | 334 static_cast<size_t>(EffectiveConnectionTypeAlgorithm:: |
341 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST)); | 335 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST)); |
342 DCHECK_NE(EffectiveConnectionTypeAlgorithm:: | 336 DCHECK_NE(EffectiveConnectionTypeAlgorithm:: |
343 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST, | 337 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST, |
344 effective_connection_type_algorithm_); | 338 effective_connection_type_algorithm_); |
(...skipping 571 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
916 EXTERNAL_ESTIMATE_PROVIDER_STATUS_BOUNDARY); | 910 EXTERNAL_ESTIMATE_PROVIDER_STATUS_BOUNDARY); |
917 } | 911 } |
918 | 912 |
919 void NetworkQualityEstimator::OnConnectionTypeChanged( | 913 void NetworkQualityEstimator::OnConnectionTypeChanged( |
920 NetworkChangeNotifier::ConnectionType type) { | 914 NetworkChangeNotifier::ConnectionType type) { |
921 DCHECK(thread_checker_.CalledOnValidThread()); | 915 DCHECK(thread_checker_.CalledOnValidThread()); |
922 | 916 |
923 RecordMetricsOnConnectionTypeChanged(); | 917 RecordMetricsOnConnectionTypeChanged(); |
924 | 918 |
925 // Write the estimates of the previous network to the cache. | 919 // Write the estimates of the previous network to the cache. |
926 CacheNetworkQualityEstimate(); | 920 network_quality_store_.Add(current_network_id_, |
| 921 nqe::internal::CachedNetworkQuality( |
| 922 last_effective_connection_type_computation_, |
| 923 estimated_quality_at_last_main_frame_)); |
927 | 924 |
928 // Clear the local state. | 925 // Clear the local state. |
929 last_connection_change_ = tick_clock_->NowTicks(); | 926 last_connection_change_ = tick_clock_->NowTicks(); |
930 peak_network_quality_ = nqe::internal::NetworkQuality(); | 927 peak_network_quality_ = nqe::internal::NetworkQuality(); |
931 downstream_throughput_kbps_observations_.Clear(); | 928 downstream_throughput_kbps_observations_.Clear(); |
932 rtt_observations_.Clear(); | 929 rtt_observations_.Clear(); |
933 | 930 |
934 #if defined(OS_ANDROID) | 931 #if defined(OS_ANDROID) |
935 if (NetworkChangeNotifier::IsConnectionCellular(current_network_id_.type)) { | 932 if (NetworkChangeNotifier::IsConnectionCellular(current_network_id_.type)) { |
936 UMA_HISTOGRAM_BOOLEAN( | 933 UMA_HISTOGRAM_BOOLEAN( |
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1163 NetworkQualityEstimator::GetRecentEffectiveConnectionTypeUsingMetrics( | 1160 NetworkQualityEstimator::GetRecentEffectiveConnectionTypeUsingMetrics( |
1164 const base::TimeTicks& start_time, | 1161 const base::TimeTicks& start_time, |
1165 NetworkQualityEstimator::MetricUsage http_rtt_metric, | 1162 NetworkQualityEstimator::MetricUsage http_rtt_metric, |
1166 NetworkQualityEstimator::MetricUsage transport_rtt_metric, | 1163 NetworkQualityEstimator::MetricUsage transport_rtt_metric, |
1167 NetworkQualityEstimator::MetricUsage downstream_throughput_kbps_metric) | 1164 NetworkQualityEstimator::MetricUsage downstream_throughput_kbps_metric) |
1168 const { | 1165 const { |
1169 DCHECK(thread_checker_.CalledOnValidThread()); | 1166 DCHECK(thread_checker_.CalledOnValidThread()); |
1170 | 1167 |
1171 // If the device is currently offline, then return | 1168 // If the device is currently offline, then return |
1172 // EFFECTIVE_CONNECTION_TYPE_OFFLINE. | 1169 // EFFECTIVE_CONNECTION_TYPE_OFFLINE. |
1173 if (GetCurrentNetworkID().type == NetworkChangeNotifier::CONNECTION_NONE) | 1170 if (current_network_id_.type == NetworkChangeNotifier::CONNECTION_NONE) |
1174 return EFFECTIVE_CONNECTION_TYPE_OFFLINE; | 1171 return EFFECTIVE_CONNECTION_TYPE_OFFLINE; |
1175 | 1172 |
1176 base::TimeDelta http_rtt = nqe::internal::InvalidRTT(); | 1173 base::TimeDelta http_rtt = nqe::internal::InvalidRTT(); |
1177 if (http_rtt_metric != NetworkQualityEstimator::MetricUsage::DO_NOT_USE && | 1174 if (http_rtt_metric != NetworkQualityEstimator::MetricUsage::DO_NOT_USE && |
1178 !GetRecentHttpRTTMedian(start_time, &http_rtt)) { | 1175 !GetRecentHttpRTTMedian(start_time, &http_rtt)) { |
1179 http_rtt = nqe::internal::InvalidRTT(); | 1176 http_rtt = nqe::internal::InvalidRTT(); |
1180 } | 1177 } |
1181 | 1178 |
1182 base::TimeDelta transport_rtt = nqe::internal::InvalidRTT(); | 1179 base::TimeDelta transport_rtt = nqe::internal::InvalidRTT(); |
1183 if (transport_rtt_metric != | 1180 if (transport_rtt_metric != |
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1348 // thus a higher percentile throughput will be faster than a lower one. | 1345 // thus a higher percentile throughput will be faster than a lower one. |
1349 int32_t kbps = nqe::internal::kInvalidThroughput; | 1346 int32_t kbps = nqe::internal::kInvalidThroughput; |
1350 if (!downstream_throughput_kbps_observations_.GetPercentile( | 1347 if (!downstream_throughput_kbps_observations_.GetPercentile( |
1351 start_time, &kbps, 100 - percentile, | 1348 start_time, &kbps, 100 - percentile, |
1352 std::vector<NetworkQualityObservationSource>())) { | 1349 std::vector<NetworkQualityObservationSource>())) { |
1353 return nqe::internal::kInvalidThroughput; | 1350 return nqe::internal::kInvalidThroughput; |
1354 } | 1351 } |
1355 return kbps; | 1352 return kbps; |
1356 } | 1353 } |
1357 | 1354 |
1358 NetworkQualityEstimator::NetworkID | 1355 nqe::internal::NetworkID NetworkQualityEstimator::GetCurrentNetworkID() const { |
1359 NetworkQualityEstimator::GetCurrentNetworkID() const { | |
1360 DCHECK(thread_checker_.CalledOnValidThread()); | 1356 DCHECK(thread_checker_.CalledOnValidThread()); |
1361 | 1357 |
1362 // TODO(tbansal): crbug.com/498068 Add NetworkQualityEstimatorAndroid class | 1358 // TODO(tbansal): crbug.com/498068 Add NetworkQualityEstimatorAndroid class |
1363 // that overrides this method on the Android platform. | 1359 // that overrides this method on the Android platform. |
1364 | 1360 |
1365 // It is possible that the connection type changed between when | 1361 // It is possible that the connection type changed between when |
1366 // GetConnectionType() was called and when the API to determine the | 1362 // GetConnectionType() was called and when the API to determine the |
1367 // network name was called. Check if that happened and retry until the | 1363 // network name was called. Check if that happened and retry until the |
1368 // connection type stabilizes. This is an imperfect solution but should | 1364 // connection type stabilizes. This is an imperfect solution but should |
1369 // capture majority of cases, and should not significantly affect estimates | 1365 // capture majority of cases, and should not significantly affect estimates |
1370 // (that are approximate to begin with). | 1366 // (that are approximate to begin with). |
1371 while (true) { | 1367 while (true) { |
1372 NetworkQualityEstimator::NetworkID network_id( | 1368 nqe::internal::NetworkID network_id( |
1373 NetworkChangeNotifier::GetConnectionType(), std::string()); | 1369 NetworkChangeNotifier::GetConnectionType(), std::string()); |
1374 | 1370 |
1375 switch (network_id.type) { | 1371 switch (network_id.type) { |
1376 case NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN: | 1372 case NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN: |
1377 case NetworkChangeNotifier::ConnectionType::CONNECTION_NONE: | 1373 case NetworkChangeNotifier::ConnectionType::CONNECTION_NONE: |
1378 case NetworkChangeNotifier::ConnectionType::CONNECTION_BLUETOOTH: | 1374 case NetworkChangeNotifier::ConnectionType::CONNECTION_BLUETOOTH: |
1379 case NetworkChangeNotifier::ConnectionType::CONNECTION_ETHERNET: | 1375 case NetworkChangeNotifier::ConnectionType::CONNECTION_ETHERNET: |
1380 break; | 1376 break; |
1381 case NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI: | 1377 case NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI: |
1382 #if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS) || \ | 1378 #if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS) || \ |
(...skipping 15 matching lines...) Expand all Loading... |
1398 | 1394 |
1399 if (network_id.type == NetworkChangeNotifier::GetConnectionType()) | 1395 if (network_id.type == NetworkChangeNotifier::GetConnectionType()) |
1400 return network_id; | 1396 return network_id; |
1401 } | 1397 } |
1402 NOTREACHED(); | 1398 NOTREACHED(); |
1403 } | 1399 } |
1404 | 1400 |
1405 bool NetworkQualityEstimator::ReadCachedNetworkQualityEstimate() { | 1401 bool NetworkQualityEstimator::ReadCachedNetworkQualityEstimate() { |
1406 DCHECK(thread_checker_.CalledOnValidThread()); | 1402 DCHECK(thread_checker_.CalledOnValidThread()); |
1407 | 1403 |
1408 // If the network name is unavailable, caching should not be performed. | 1404 nqe::internal::CachedNetworkQuality cached_network_quality; |
1409 if (current_network_id_.id.empty()) | 1405 |
| 1406 const bool cached_estimate_available = network_quality_store_.GetById( |
| 1407 current_network_id_, &cached_network_quality); |
| 1408 UMA_HISTOGRAM_BOOLEAN("NQE.CachedNetworkQualityAvailable", |
| 1409 cached_estimate_available); |
| 1410 |
| 1411 if (!cached_estimate_available) |
1410 return false; | 1412 return false; |
1411 | 1413 |
1412 CachedNetworkQualities::const_iterator it = | 1414 const base::TimeTicks now = tick_clock_->NowTicks(); |
1413 cached_network_qualities_.find(current_network_id_); | |
1414 | 1415 |
1415 if (it == cached_network_qualities_.end()) | 1416 if (cached_network_quality.network_quality().downstream_throughput_kbps() != |
1416 return false; | |
1417 | |
1418 nqe::internal::NetworkQuality network_quality(it->second.network_quality()); | |
1419 | |
1420 const base::TimeTicks now = tick_clock_->NowTicks(); | |
1421 bool read_cached_estimate = false; | |
1422 | |
1423 if (network_quality.downstream_throughput_kbps() != | |
1424 nqe::internal::kInvalidThroughput) { | 1417 nqe::internal::kInvalidThroughput) { |
1425 read_cached_estimate = true; | |
1426 ThroughputObservation througphput_observation( | 1418 ThroughputObservation througphput_observation( |
1427 network_quality.downstream_throughput_kbps(), now, | 1419 cached_network_quality.network_quality().downstream_throughput_kbps(), |
1428 NETWORK_QUALITY_OBSERVATION_SOURCE_CACHED_ESTIMATE); | 1420 now, NETWORK_QUALITY_OBSERVATION_SOURCE_CACHED_ESTIMATE); |
1429 downstream_throughput_kbps_observations_.AddObservation( | 1421 downstream_throughput_kbps_observations_.AddObservation( |
1430 througphput_observation); | 1422 througphput_observation); |
1431 NotifyObserversOfThroughput(througphput_observation); | 1423 NotifyObserversOfThroughput(througphput_observation); |
1432 } | 1424 } |
1433 | 1425 |
1434 if (network_quality.http_rtt() != nqe::internal::InvalidRTT()) { | 1426 if (cached_network_quality.network_quality().http_rtt() != |
1435 read_cached_estimate = true; | 1427 nqe::internal::InvalidRTT()) { |
1436 RttObservation rtt_observation( | 1428 RttObservation rtt_observation( |
1437 network_quality.http_rtt(), now, | 1429 cached_network_quality.network_quality().http_rtt(), now, |
1438 NETWORK_QUALITY_OBSERVATION_SOURCE_CACHED_ESTIMATE); | 1430 NETWORK_QUALITY_OBSERVATION_SOURCE_CACHED_ESTIMATE); |
1439 rtt_observations_.AddObservation(rtt_observation); | 1431 rtt_observations_.AddObservation(rtt_observation); |
1440 NotifyObserversOfRTT(rtt_observation); | 1432 NotifyObserversOfRTT(rtt_observation); |
1441 } | 1433 } |
1442 | 1434 return true; |
1443 return read_cached_estimate; | |
1444 } | 1435 } |
1445 | 1436 |
1446 void NetworkQualityEstimator::OnUpdatedEstimateAvailable( | 1437 void NetworkQualityEstimator::OnUpdatedEstimateAvailable( |
1447 const base::TimeDelta& rtt, | 1438 const base::TimeDelta& rtt, |
1448 int32_t downstream_throughput_kbps, | 1439 int32_t downstream_throughput_kbps, |
1449 int32_t upstream_throughput_kbps) { | 1440 int32_t upstream_throughput_kbps) { |
1450 DCHECK(thread_checker_.CalledOnValidThread()); | 1441 DCHECK(thread_checker_.CalledOnValidThread()); |
1451 DCHECK(external_estimate_provider_); | 1442 DCHECK(external_estimate_provider_); |
1452 | 1443 |
1453 RecordExternalEstimateProviderMetrics( | 1444 RecordExternalEstimateProviderMetrics( |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1524 void NetworkQualityEstimator::SetTickClockForTesting( | 1515 void NetworkQualityEstimator::SetTickClockForTesting( |
1525 std::unique_ptr<base::TickClock> tick_clock) { | 1516 std::unique_ptr<base::TickClock> tick_clock) { |
1526 DCHECK(thread_checker_.CalledOnValidThread()); | 1517 DCHECK(thread_checker_.CalledOnValidThread()); |
1527 tick_clock_ = std::move(tick_clock); | 1518 tick_clock_ = std::move(tick_clock); |
1528 } | 1519 } |
1529 | 1520 |
1530 double NetworkQualityEstimator::RandDouble() const { | 1521 double NetworkQualityEstimator::RandDouble() const { |
1531 return base::RandDouble(); | 1522 return base::RandDouble(); |
1532 } | 1523 } |
1533 | 1524 |
1534 void NetworkQualityEstimator::CacheNetworkQualityEstimate() { | |
1535 DCHECK(thread_checker_.CalledOnValidThread()); | |
1536 DCHECK_LE(cached_network_qualities_.size(), | |
1537 static_cast<size_t>(kMaximumNetworkQualityCacheSize)); | |
1538 | |
1539 // If the network name is unavailable, caching should not be performed. | |
1540 if (current_network_id_.id.empty()) | |
1541 return; | |
1542 | |
1543 base::TimeDelta http_rtt = nqe::internal::InvalidRTT(); | |
1544 int32_t downlink_throughput_kbps = nqe::internal::kInvalidThroughput; | |
1545 | |
1546 if (!GetHttpRTTEstimate(&http_rtt) || | |
1547 !GetDownlinkThroughputKbpsEstimate(&downlink_throughput_kbps)) { | |
1548 return; | |
1549 } | |
1550 | |
1551 // |transport_rtt| is currently not cached. | |
1552 nqe::internal::NetworkQuality network_quality = nqe::internal::NetworkQuality( | |
1553 http_rtt, nqe::internal::InvalidRTT() /* transport_rtt */, | |
1554 downlink_throughput_kbps); | |
1555 | |
1556 if (cached_network_qualities_.size() == kMaximumNetworkQualityCacheSize) { | |
1557 // Remove the oldest entry. | |
1558 CachedNetworkQualities::iterator oldest_entry_iterator = | |
1559 cached_network_qualities_.begin(); | |
1560 | |
1561 for (CachedNetworkQualities::iterator it = | |
1562 cached_network_qualities_.begin(); | |
1563 it != cached_network_qualities_.end(); ++it) { | |
1564 if ((it->second).OlderThan(oldest_entry_iterator->second)) | |
1565 oldest_entry_iterator = it; | |
1566 } | |
1567 cached_network_qualities_.erase(oldest_entry_iterator); | |
1568 } | |
1569 DCHECK_LT(cached_network_qualities_.size(), | |
1570 static_cast<size_t>(kMaximumNetworkQualityCacheSize)); | |
1571 | |
1572 cached_network_qualities_.insert( | |
1573 std::make_pair(current_network_id_, | |
1574 nqe::internal::CachedNetworkQuality(network_quality))); | |
1575 DCHECK_LE(cached_network_qualities_.size(), | |
1576 static_cast<size_t>(kMaximumNetworkQualityCacheSize)); | |
1577 } | |
1578 | |
1579 void NetworkQualityEstimator::OnUpdatedRTTAvailable( | 1525 void NetworkQualityEstimator::OnUpdatedRTTAvailable( |
1580 SocketPerformanceWatcherFactory::Protocol protocol, | 1526 SocketPerformanceWatcherFactory::Protocol protocol, |
1581 const base::TimeDelta& rtt) { | 1527 const base::TimeDelta& rtt) { |
1582 DCHECK(thread_checker_.CalledOnValidThread()); | 1528 DCHECK(thread_checker_.CalledOnValidThread()); |
1583 DCHECK_NE(nqe::internal::InvalidRTT(), rtt); | 1529 DCHECK_NE(nqe::internal::InvalidRTT(), rtt); |
1584 | 1530 |
1585 RttObservation observation(rtt, tick_clock_->NowTicks(), | 1531 RttObservation observation(rtt, tick_clock_->NowTicks(), |
1586 ProtocolSourceToObservationSource(protocol)); | 1532 ProtocolSourceToObservationSource(protocol)); |
1587 NotifyObserversOfRTT(observation); | 1533 NotifyObserversOfRTT(observation); |
1588 rtt_observations_.AddObservation(observation); | 1534 rtt_observations_.AddObservation(observation); |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1666 NotifyObserversOfEffectiveConnectionTypeChanged() { | 1612 NotifyObserversOfEffectiveConnectionTypeChanged() { |
1667 DCHECK(thread_checker_.CalledOnValidThread()); | 1613 DCHECK(thread_checker_.CalledOnValidThread()); |
1668 | 1614 |
1669 // TODO(tbansal): Add hysteresis in the notification. | 1615 // TODO(tbansal): Add hysteresis in the notification. |
1670 FOR_EACH_OBSERVER( | 1616 FOR_EACH_OBSERVER( |
1671 EffectiveConnectionTypeObserver, effective_connection_type_observer_list_, | 1617 EffectiveConnectionTypeObserver, effective_connection_type_observer_list_, |
1672 OnEffectiveConnectionTypeChanged(effective_connection_type_)); | 1618 OnEffectiveConnectionTypeChanged(effective_connection_type_)); |
1673 } | 1619 } |
1674 | 1620 |
1675 } // namespace net | 1621 } // namespace net |
OLD | NEW |