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/base/network_quality_estimator.h" | 5 #include "net/base/network_quality_estimator.h" |
| 6 | 6 |
| 7 #include <string> | |
| 8 | |
| 9 #include "base/logging.h" | 7 #include "base/logging.h" |
| 10 #include "base/metrics/histogram.h" | 8 #include "base/metrics/histogram.h" |
| 9 #include "build/build_config.h" | |
| 11 #include "net/base/net_util.h" | 10 #include "net/base/net_util.h" |
| 11 #include "net/base/network_interfaces.h" | |
| 12 #include "net/base/network_quality.h" | 12 #include "net/base/network_quality.h" |
| 13 #include "net/url_request/url_request.h" | 13 #include "net/url_request/url_request.h" |
| 14 #include "url/gurl.h" | 14 #include "url/gurl.h" |
| 15 | 15 |
| 16 #if defined(OS_ANDROID) | |
| 17 #include "net/android/network_library.h" | |
| 18 #endif // OS_ANDROID | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 // Maximum size of the cache that holds network quality estimates. | |
| 23 // Smaller size may reduce the cache hit rate due to frequent evictions. | |
| 24 // Larger size may affect performance. | |
| 25 const uint32_t kMaximumNetworkQualityCacheSize = 10; | |
| 26 | |
| 27 } // namespace | |
| 28 | |
| 16 namespace net { | 29 namespace net { |
| 17 | 30 |
| 18 NetworkQualityEstimator::NetworkQualityEstimator() | 31 NetworkQualityEstimator::NetworkQualityEstimator() |
| 19 : NetworkQualityEstimator(false) { | 32 : NetworkQualityEstimator(false) { |
| 20 } | 33 } |
| 21 | 34 |
| 22 NetworkQualityEstimator::NetworkQualityEstimator( | 35 NetworkQualityEstimator::NetworkQualityEstimator( |
| 23 bool allow_local_host_requests_for_tests) | 36 bool allow_local_host_requests_for_tests) |
| 24 : allow_localhost_requests_(allow_local_host_requests_for_tests), | 37 : allow_localhost_requests_(allow_local_host_requests_for_tests), |
| 25 last_connection_change_(base::TimeTicks::Now()), | 38 last_connection_change_(base::TimeTicks::Now()), |
| 26 current_connection_type_(NetworkChangeNotifier::GetConnectionType()), | 39 current_connection_type_(NetworkChangeNotifier::GetConnectionType()), |
| 27 bytes_read_since_last_connection_change_(false), | 40 bytes_read_since_last_connection_change_(false), |
| 28 peak_kbps_since_last_connection_change_(0) { | 41 peak_kbps_since_last_connection_change_(0) { |
| 29 static_assert(kMinRequestDurationMicroseconds > 0, | 42 static_assert(kMinRequestDurationMicroseconds > 0, |
| 30 "Minimum request duration must be > 0"); | 43 "Minimum request duration must be > 0"); |
| 44 static_assert(kMaximumNetworkQualityCacheSize > 0, | |
| 45 "Size of the network quality cache must be > 0"); | |
| 31 NetworkChangeNotifier::AddConnectionTypeObserver(this); | 46 NetworkChangeNotifier::AddConnectionTypeObserver(this); |
| 47 UpdateCurrentNetworkName(); | |
| 32 } | 48 } |
| 33 | 49 |
| 34 NetworkQualityEstimator::~NetworkQualityEstimator() { | 50 NetworkQualityEstimator::~NetworkQualityEstimator() { |
| 35 DCHECK(thread_checker_.CalledOnValidThread()); | 51 DCHECK(thread_checker_.CalledOnValidThread()); |
| 36 NetworkChangeNotifier::RemoveConnectionTypeObserver(this); | 52 NetworkChangeNotifier::RemoveConnectionTypeObserver(this); |
| 37 } | 53 } |
| 38 | 54 |
| 39 void NetworkQualityEstimator::NotifyDataReceived(const URLRequest& request, | 55 void NetworkQualityEstimator::NotifyDataReceived(const URLRequest& request, |
| 40 int64_t prefilter_bytes_read) { | 56 int64_t prefilter_bytes_read) { |
| 41 DCHECK(thread_checker_.CalledOnValidThread()); | 57 DCHECK(thread_checker_.CalledOnValidThread()); |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 104 break; | 120 break; |
| 105 case NetworkChangeNotifier::CONNECTION_NONE: | 121 case NetworkChangeNotifier::CONNECTION_NONE: |
| 106 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.None", | 122 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.None", |
| 107 fastest_RTT_since_last_connection_change_); | 123 fastest_RTT_since_last_connection_change_); |
| 108 break; | 124 break; |
| 109 case NetworkChangeNotifier::CONNECTION_BLUETOOTH: | 125 case NetworkChangeNotifier::CONNECTION_BLUETOOTH: |
| 110 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Bluetooth", | 126 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Bluetooth", |
| 111 fastest_RTT_since_last_connection_change_); | 127 fastest_RTT_since_last_connection_change_); |
| 112 break; | 128 break; |
| 113 default: | 129 default: |
| 114 NOTREACHED(); | 130 NOTREACHED() << "Unexpected connection type = " |
| 131 << current_connection_type_; | |
| 115 break; | 132 break; |
| 116 } | 133 } |
| 117 } | 134 } |
| 118 | 135 |
| 119 if (peak_kbps_since_last_connection_change_) { | 136 if (peak_kbps_since_last_connection_change_) { |
| 120 switch (current_connection_type_) { | 137 switch (current_connection_type_) { |
| 121 case NetworkChangeNotifier::CONNECTION_UNKNOWN: | 138 case NetworkChangeNotifier::CONNECTION_UNKNOWN: |
| 122 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Unknown", | 139 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Unknown", |
| 123 peak_kbps_since_last_connection_change_); | 140 peak_kbps_since_last_connection_change_); |
| 124 break; | 141 break; |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 144 break; | 161 break; |
| 145 case NetworkChangeNotifier::CONNECTION_NONE: | 162 case NetworkChangeNotifier::CONNECTION_NONE: |
| 146 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.None", | 163 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.None", |
| 147 peak_kbps_since_last_connection_change_); | 164 peak_kbps_since_last_connection_change_); |
| 148 break; | 165 break; |
| 149 case NetworkChangeNotifier::CONNECTION_BLUETOOTH: | 166 case NetworkChangeNotifier::CONNECTION_BLUETOOTH: |
| 150 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Bluetooth", | 167 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Bluetooth", |
| 151 peak_kbps_since_last_connection_change_); | 168 peak_kbps_since_last_connection_change_); |
| 152 break; | 169 break; |
| 153 default: | 170 default: |
| 154 NOTREACHED(); | 171 NOTREACHED() << "Unexpected connection type = " |
| 172 << current_connection_type_; | |
| 155 break; | 173 break; |
| 156 } | 174 } |
| 157 } | 175 } |
| 158 | 176 |
| 177 // Write the estimates of the previous network to the cache. | |
| 178 CacheNetworkQualityEstimate(); | |
| 179 | |
| 159 last_connection_change_ = base::TimeTicks::Now(); | 180 last_connection_change_ = base::TimeTicks::Now(); |
| 160 bytes_read_since_last_connection_change_ = false; | 181 bytes_read_since_last_connection_change_ = false; |
| 161 peak_kbps_since_last_connection_change_ = 0; | 182 peak_kbps_since_last_connection_change_ = 0; |
| 162 current_connection_type_ = type; | 183 current_connection_type_ = type; |
| 184 UpdateCurrentNetworkName(); | |
| 185 | |
| 186 // Read any cached estimates for the new network. | |
| 187 ReadCachedNetworkQualityEstimate(); | |
| 188 } | |
| 189 | |
| 190 size_t NetworkQualityEstimator::GetCacheSizeForTests() const { | |
| 191 return cached_network_quality_.size(); | |
| 192 } | |
| 193 | |
| 194 void NetworkQualityEstimator::SetCurrentNetworkNameForTests( | |
| 195 const std::string& network_name) { | |
|
mmenke
2015/06/08 20:34:19
Suggest making UpdateCurrentNetworkName call a vir
tbansal1
2015/06/09 02:23:13
Done.
| |
| 196 current_network_name_ = network_name; | |
| 163 } | 197 } |
| 164 | 198 |
| 165 NetworkQuality NetworkQualityEstimator::GetEstimate() const { | 199 NetworkQuality NetworkQualityEstimator::GetEstimate() const { |
| 166 DCHECK(thread_checker_.CalledOnValidThread()); | 200 DCHECK(thread_checker_.CalledOnValidThread()); |
| 167 | 201 |
| 168 if (!bytes_read_since_last_connection_change_) { | 202 if (!bytes_read_since_last_connection_change_) { |
| 169 return NetworkQuality(fastest_RTT_since_last_connection_change_, 0, | 203 return NetworkQuality(fastest_RTT_since_last_connection_change_, 0, |
| 170 peak_kbps_since_last_connection_change_, 0); | 204 peak_kbps_since_last_connection_change_, 0); |
| 171 } | 205 } |
| 172 if (!peak_kbps_since_last_connection_change_) { | 206 if (!peak_kbps_since_last_connection_change_) { |
| 173 return NetworkQuality(fastest_RTT_since_last_connection_change_, 0.1, | 207 return NetworkQuality(fastest_RTT_since_last_connection_change_, 0.1, |
| 174 peak_kbps_since_last_connection_change_, 0); | 208 peak_kbps_since_last_connection_change_, 0); |
| 175 } | 209 } |
| 176 return NetworkQuality(fastest_RTT_since_last_connection_change_, 0.1, | 210 return NetworkQuality(fastest_RTT_since_last_connection_change_, 0.1, |
| 177 peak_kbps_since_last_connection_change_, 0.1); | 211 peak_kbps_since_last_connection_change_, 0.1); |
| 178 } | 212 } |
| 179 | 213 |
| 214 void NetworkQualityEstimator::UpdateCurrentNetworkName() { | |
| 215 // TODO(tbansal): Add NetworkQualityEstimatorAndroid class that overrides | |
| 216 // this method on Android platform. | |
|
mmenke
2015/06/08 20:34:20
Is there a bug for this that we could link to, to
tbansal1
2015/06/09 02:23:13
Done.
| |
| 217 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 218 | |
| 219 current_network_name_ = std::string(); | |
| 220 switch (current_connection_type_) { | |
| 221 case NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN: | |
| 222 case NetworkChangeNotifier::ConnectionType::CONNECTION_NONE: | |
| 223 case NetworkChangeNotifier::ConnectionType::CONNECTION_BLUETOOTH: | |
| 224 return; | |
| 225 case NetworkChangeNotifier::ConnectionType::CONNECTION_ETHERNET: | |
| 226 current_network_name_ = "ethernet"; | |
|
mmenke
2015/06/08 20:34:20
I don't think this is needed - we're recording net
tbansal1
2015/06/09 02:23:13
Yes, it was not really needed.
| |
| 227 return; | |
| 228 case NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI: | |
| 229 #if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS) | |
| 230 current_network_name_ = GetWifiSSID(); | |
| 231 #endif // defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS) | |
| 232 return; | |
| 233 case NetworkChangeNotifier::ConnectionType::CONNECTION_2G: | |
| 234 case NetworkChangeNotifier::ConnectionType::CONNECTION_3G: | |
| 235 case NetworkChangeNotifier::ConnectionType::CONNECTION_4G: | |
| 236 #if defined(OS_ANDROID) | |
| 237 current_network_name_ = android::GetTelephonyNetworkOperator(); | |
|
mmenke
2015/06/08 20:34:19
Suggest a struct:
struct NetworkID {
Connection
tbansal1
2015/06/09 02:23:13
Done.
| |
| 238 #endif // OS_ANDROID | |
| 239 return; | |
| 240 default: | |
| 241 NOTREACHED() << "Unexpected connection type = " | |
| 242 << current_connection_type_; | |
| 243 } | |
| 244 } | |
| 245 | |
| 246 bool NetworkQualityEstimator::ReadCachedNetworkQualityEstimate() { | |
| 247 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 248 | |
| 249 for (const auto& network_quality : cached_network_quality_) { | |
| 250 if (!network_quality->MatchesNetwork(current_connection_type_, | |
| 251 current_network_name_)) { | |
| 252 continue; | |
| 253 } | |
| 254 | |
| 255 // TOOD(tbansal): Populate these values back into the median computing | |
| 256 // algorithm. | |
| 257 // Add UMA to record how frequently match happens. | |
| 258 // int64 median_kbps = network_quality.median_kbps; | |
| 259 // int64 median_rtt_msec = network_quality.median_rtt_milliseconds; | |
| 260 // Ensure that the estimates read are non-zero before populating them into | |
| 261 // the median computing algorithm. | |
| 262 return true; | |
| 263 } | |
| 264 | |
| 265 return false; | |
| 266 } | |
| 267 | |
| 268 void NetworkQualityEstimator::CacheNetworkQualityEstimate() { | |
| 269 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 270 | |
| 271 // TODO(tbansal): Following variables should be initialized using the median | |
| 272 /// values reported by NetworkQualityEstimator. | |
| 273 int median_kbps = 0; | |
| 274 base::TimeDelta median_rtt; | |
| 275 | |
| 276 // If this network is already in the cache, overwrite that entry. | |
| 277 for (auto& network_quality : cached_network_quality_) { | |
|
mmenke
2015/06/08 20:34:20
Why not just use an std::map?
tbansal1
2015/06/09 02:23:13
Did not do it before because I was not using struc
| |
| 278 if (!network_quality->MatchesNetwork(current_connection_type_, | |
| 279 current_network_name_)) { | |
| 280 continue; | |
| 281 } | |
| 282 | |
| 283 network_quality->UpdateNetworkQuality(median_kbps, median_rtt); | |
| 284 return; | |
| 285 } | |
| 286 | |
| 287 if (cached_network_quality_.size() < kMaximumNetworkQualityCacheSize) { | |
| 288 cached_network_quality_.push_back(new CachedNetworkQuality( | |
| 289 current_connection_type_, current_network_name_, median_kbps, | |
| 290 median_rtt)); | |
|
mmenke
2015/06/08 20:34:20
Rather than duplicate this line, suggest you do:
tbansal1
2015/06/09 02:23:13
Done.
| |
| 291 return; | |
| 292 } | |
| 293 | |
| 294 DCHECK_EQ(kMaximumNetworkQualityCacheSize, cached_network_quality_.size()); | |
|
mmenke
2015/06/08 20:34:19
optional: Suggest a DCHECK_LE(kMaximumNetworkQual
tbansal1
2015/06/09 02:23:13
Done.
| |
| 295 | |
| 296 // Overwrite the oldest entry. | |
| 297 ScopedVector<CachedNetworkQuality>::iterator oldest_entry_iterator = | |
| 298 cached_network_quality_.begin(); | |
| 299 | |
| 300 for (ScopedVector<CachedNetworkQuality>::iterator iterator = | |
| 301 cached_network_quality_.begin(); | |
| 302 iterator != cached_network_quality_.end(); ++iterator) { | |
| 303 if ((*oldest_entry_iterator)->last_update_time() > | |
| 304 (*iterator)->last_update_time()) { | |
| 305 oldest_entry_iterator = iterator; | |
| 306 } | |
| 307 } | |
| 308 | |
| 309 cached_network_quality_.erase(oldest_entry_iterator); | |
| 310 cached_network_quality_.push_back( | |
| 311 new CachedNetworkQuality(current_connection_type_, current_network_name_, | |
| 312 median_kbps, median_rtt)); | |
| 313 | |
| 314 DCHECK_EQ(kMaximumNetworkQualityCacheSize, cached_network_quality_.size()); | |
| 315 } | |
| 316 | |
| 317 NetworkQualityEstimator::CachedNetworkQuality::CachedNetworkQuality( | |
| 318 NetworkChangeNotifier::ConnectionType connection_type, | |
| 319 const std::string& network_name, | |
| 320 uint64_t median_kbps, | |
| 321 const base::TimeDelta& median_rtt) | |
| 322 : median_kbps_(median_kbps), | |
| 323 median_rtt_(median_rtt), | |
| 324 last_update_time_(base::TimeTicks::Now()), | |
| 325 connection_type_(connection_type), | |
| 326 network_name_(network_name) { | |
| 327 DCHECK_GE(median_kbps_, 0U); | |
| 328 DCHECK_GE(median_rtt_, base::TimeDelta()); | |
| 329 } | |
| 330 | |
| 331 NetworkQualityEstimator::CachedNetworkQuality::~CachedNetworkQuality() { | |
| 332 } | |
| 333 | |
| 334 void NetworkQualityEstimator::CachedNetworkQuality::UpdateNetworkQuality( | |
| 335 uint64_t median_kbps, | |
| 336 const base::TimeDelta& median_rtt) { | |
| 337 median_kbps_ = median_kbps; | |
| 338 median_rtt_ = median_rtt; | |
| 339 last_update_time_ = base::TimeTicks::Now(); | |
| 340 DCHECK_GE(median_kbps_, 0U); | |
| 341 DCHECK_GE(median_rtt_, base::TimeDelta()); | |
|
mmenke
2015/06/08 20:34:20
nit: DCHECKs to validate input are generally put
tbansal1
2015/06/09 02:23:12
Done.
| |
| 342 } | |
| 343 | |
| 344 bool NetworkQualityEstimator::CachedNetworkQuality::MatchesNetwork( | |
| 345 NetworkChangeNotifier::ConnectionType connection_type, | |
| 346 const std::string& network_name) const { | |
| 347 return connection_type == connection_type_ && network_name == network_name_; | |
| 348 } | |
| 349 | |
| 180 } // namespace net | 350 } // namespace net |
| OLD | NEW |