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 <float.h> | 7 #include <float.h> |
| 8 #include <algorithm> | 8 #include <algorithm> |
| 9 #include <cmath> | 9 #include <cmath> |
| 10 #include <limits> | 10 #include <limits> |
| 11 #include <string> | |
| 12 #include <vector> | 11 #include <vector> |
| 13 | 12 |
| 14 #include "base/logging.h" | 13 #include "base/logging.h" |
| 15 #include "base/metrics/histogram.h" | 14 #include "base/metrics/histogram.h" |
| 16 #include "base/metrics/histogram_base.h" | 15 #include "base/metrics/histogram_base.h" |
| 17 #include "base/strings/safe_sprintf.h" | 16 #include "base/strings/safe_sprintf.h" |
| 18 #include "base/strings/string_number_conversions.h" | 17 #include "base/strings/string_number_conversions.h" |
| 18 #include "build/build_config.h" | |
| 19 #include "net/base/load_flags.h" | 19 #include "net/base/load_flags.h" |
| 20 #include "net/base/load_timing_info.h" | 20 #include "net/base/load_timing_info.h" |
| 21 #include "net/base/net_util.h" | 21 #include "net/base/net_util.h" |
| 22 #include "net/base/network_interfaces.h" | |
| 22 #include "net/url_request/url_request.h" | 23 #include "net/url_request/url_request.h" |
| 23 #include "url/gurl.h" | 24 #include "url/gurl.h" |
| 24 | 25 |
| 26 #if defined(OS_ANDROID) | |
| 27 #include "net/android/network_library.h" | |
| 28 #endif // OS_ANDROID | |
| 29 | |
| 25 namespace { | 30 namespace { |
| 26 | 31 |
| 27 // Maximum number of observations that can be held in the ObservationBuffer. | |
| 28 const size_t kMaximumObservationsBufferSize = 300; | |
| 29 | |
| 30 // Default value of the half life (in seconds) for computing time weighted | 32 // Default value of the half life (in seconds) for computing time weighted |
| 31 // percentiles. Every half life, the weight of all observations reduces by | 33 // percentiles. Every half life, the weight of all observations reduces by |
| 32 // half. Lowering the half life would reduce the weight of older values faster. | 34 // half. Lowering the half life would reduce the weight of older values faster. |
| 33 const int kDefaultHalfLifeSeconds = 60; | 35 const int kDefaultHalfLifeSeconds = 60; |
| 34 | 36 |
| 35 // Name of the variation parameter that holds the value of the half life (in | 37 // Name of the variation parameter that holds the value of the half life (in |
| 36 // seconds) of the observations. | 38 // seconds) of the observations. |
| 37 const char kHalfLifeSecondsParamName[] = "HalfLifeSeconds"; | 39 const char kHalfLifeSecondsParamName[] = "HalfLifeSeconds"; |
| 38 | 40 |
| 39 // Returns a descriptive name corresponding to |connection_type|. | 41 // Returns a descriptive name corresponding to |connection_type|. |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 120 : NetworkQualityEstimator(variation_params, false, false) { | 122 : NetworkQualityEstimator(variation_params, false, false) { |
| 121 } | 123 } |
| 122 | 124 |
| 123 NetworkQualityEstimator::NetworkQualityEstimator( | 125 NetworkQualityEstimator::NetworkQualityEstimator( |
| 124 const std::map<std::string, std::string>& variation_params, | 126 const std::map<std::string, std::string>& variation_params, |
| 125 bool allow_local_host_requests_for_tests, | 127 bool allow_local_host_requests_for_tests, |
| 126 bool allow_smaller_responses_for_tests) | 128 bool allow_smaller_responses_for_tests) |
| 127 : allow_localhost_requests_(allow_local_host_requests_for_tests), | 129 : allow_localhost_requests_(allow_local_host_requests_for_tests), |
| 128 allow_small_responses_(allow_smaller_responses_for_tests), | 130 allow_small_responses_(allow_smaller_responses_for_tests), |
| 129 last_connection_change_(base::TimeTicks::Now()), | 131 last_connection_change_(base::TimeTicks::Now()), |
| 130 current_connection_type_(NetworkChangeNotifier::GetConnectionType()), | 132 current_network_id_( |
| 131 fastest_rtt_since_last_connection_change_(NetworkQuality::InvalidRTT()), | 133 NetworkID(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, |
| 132 peak_kbps_since_last_connection_change_( | 134 std::string())), |
| 133 NetworkQuality::kInvalidThroughput), | |
| 134 kbps_observations_(GetWeightMultiplierPerSecond(variation_params)), | 135 kbps_observations_(GetWeightMultiplierPerSecond(variation_params)), |
| 135 rtt_msec_observations_(GetWeightMultiplierPerSecond(variation_params)) { | 136 rtt_msec_observations_(GetWeightMultiplierPerSecond(variation_params)) { |
| 136 static_assert(kMinRequestDurationMicroseconds > 0, | 137 static_assert(kMinRequestDurationMicroseconds > 0, |
| 137 "Minimum request duration must be > 0"); | 138 "Minimum request duration must be > 0"); |
| 138 static_assert(kDefaultHalfLifeSeconds > 0, | 139 static_assert(kDefaultHalfLifeSeconds > 0, |
| 139 "Default half life duration must be > 0"); | 140 "Default half life duration must be > 0"); |
| 141 static_assert(kMaximumNetworkQualityCacheSize > 0, | |
| 142 "Size of the network quality cache must be > 0"); | |
| 143 // This limit should not be increased unless the logic for removing the | |
| 144 // oldest cache entry is rewritten to use a doubly-linked-list LRU queue. | |
| 145 static_assert(kMaximumNetworkQualityCacheSize <= 10, | |
| 146 "Size of the network quality cache must <= 10"); | |
| 140 | 147 |
| 141 ObtainOperatingParams(variation_params); | 148 ObtainOperatingParams(variation_params); |
| 149 NetworkChangeNotifier::AddConnectionTypeObserver(this); | |
| 150 current_network_id_ = GetCurrentNetworkID(); | |
| 142 AddDefaultEstimates(); | 151 AddDefaultEstimates(); |
| 143 NetworkChangeNotifier::AddConnectionTypeObserver(this); | |
| 144 } | 152 } |
| 145 | 153 |
| 146 void NetworkQualityEstimator::ObtainOperatingParams( | 154 void NetworkQualityEstimator::ObtainOperatingParams( |
| 147 const std::map<std::string, std::string>& variation_params) { | 155 const std::map<std::string, std::string>& variation_params) { |
| 148 DCHECK(thread_checker_.CalledOnValidThread()); | 156 DCHECK(thread_checker_.CalledOnValidThread()); |
| 149 | 157 |
| 150 for (size_t i = 0; i <= NetworkChangeNotifier::CONNECTION_LAST; ++i) { | 158 for (size_t i = 0; i <= NetworkChangeNotifier::CONNECTION_LAST; ++i) { |
| 151 NetworkChangeNotifier::ConnectionType type = | 159 NetworkChangeNotifier::ConnectionType type = |
| 152 static_cast<NetworkChangeNotifier::ConnectionType>(i); | 160 static_cast<NetworkChangeNotifier::ConnectionType>(i); |
| 153 int32_t variations_value = kMinimumRTTVariationParameterMsec - 1; | 161 int32_t variations_value = kMinimumRTTVariationParameterMsec - 1; |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 175 base::StringToInt(it->second, &variations_value) && | 183 base::StringToInt(it->second, &variations_value) && |
| 176 variations_value >= kMinimumThroughputVariationParameterKbps) { | 184 variations_value >= kMinimumThroughputVariationParameterKbps) { |
| 177 default_observations_[i] = | 185 default_observations_[i] = |
| 178 NetworkQuality(default_observations_[i].rtt(), variations_value); | 186 NetworkQuality(default_observations_[i].rtt(), variations_value); |
| 179 } | 187 } |
| 180 } | 188 } |
| 181 } | 189 } |
| 182 | 190 |
| 183 void NetworkQualityEstimator::AddDefaultEstimates() { | 191 void NetworkQualityEstimator::AddDefaultEstimates() { |
| 184 DCHECK(thread_checker_.CalledOnValidThread()); | 192 DCHECK(thread_checker_.CalledOnValidThread()); |
| 185 | 193 if (default_observations_[current_network_id_.type].rtt() != |
| 186 if (default_observations_[current_connection_type_].rtt() != | |
| 187 NetworkQuality::InvalidRTT()) { | 194 NetworkQuality::InvalidRTT()) { |
| 188 rtt_msec_observations_.AddObservation(Observation( | 195 rtt_msec_observations_.AddObservation(Observation( |
| 189 default_observations_[current_connection_type_].rtt().InMilliseconds(), | 196 default_observations_[current_network_id_.type].rtt().InMilliseconds(), |
| 190 base::TimeTicks::Now())); | 197 base::TimeTicks::Now())); |
| 191 } | 198 } |
| 192 | 199 if (default_observations_[current_network_id_.type] |
| 193 if (default_observations_[current_connection_type_] | |
| 194 .downstream_throughput_kbps() != NetworkQuality::kInvalidThroughput) { | 200 .downstream_throughput_kbps() != NetworkQuality::kInvalidThroughput) { |
| 195 kbps_observations_.AddObservation( | 201 kbps_observations_.AddObservation( |
| 196 Observation(default_observations_[current_connection_type_] | 202 Observation(default_observations_[current_network_id_.type] |
| 197 .downstream_throughput_kbps(), | 203 .downstream_throughput_kbps(), |
| 198 base::TimeTicks::Now())); | 204 base::TimeTicks::Now())); |
| 199 } | 205 } |
| 200 } | 206 } |
| 201 | 207 |
| 202 NetworkQualityEstimator::~NetworkQualityEstimator() { | 208 NetworkQualityEstimator::~NetworkQualityEstimator() { |
| 203 DCHECK(thread_checker_.CalledOnValidThread()); | 209 DCHECK(thread_checker_.CalledOnValidThread()); |
| 204 NetworkChangeNotifier::RemoveConnectionTypeObserver(this); | 210 NetworkChangeNotifier::RemoveConnectionTypeObserver(this); |
| 205 } | 211 } |
| 206 | 212 |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 243 | 249 |
| 244 // Time when the headers were received. | 250 // Time when the headers were received. |
| 245 base::TimeTicks headers_received_time = load_timing_info.receive_headers_end; | 251 base::TimeTicks headers_received_time = load_timing_info.receive_headers_end; |
| 246 | 252 |
| 247 // Only add RTT observation if this is the first read for this response. | 253 // Only add RTT observation if this is the first read for this response. |
| 248 if (cumulative_prefilter_bytes_read == prefiltered_bytes_read) { | 254 if (cumulative_prefilter_bytes_read == prefiltered_bytes_read) { |
| 249 // Duration between when the resource was requested and when response | 255 // Duration between when the resource was requested and when response |
| 250 // headers were received. | 256 // headers were received. |
| 251 base::TimeDelta observed_rtt = headers_received_time - request_start_time; | 257 base::TimeDelta observed_rtt = headers_received_time - request_start_time; |
| 252 DCHECK_GE(observed_rtt, base::TimeDelta()); | 258 DCHECK_GE(observed_rtt, base::TimeDelta()); |
| 253 if (observed_rtt < fastest_rtt_since_last_connection_change_) | 259 if (observed_rtt < peak_network_quality_.rtt()) { |
| 254 fastest_rtt_since_last_connection_change_ = observed_rtt; | 260 peak_network_quality_ = NetworkQuality( |
| 261 observed_rtt, peak_network_quality_.downstream_throughput_kbps()); | |
| 262 } | |
| 255 | 263 |
| 256 rtt_msec_observations_.AddObservation( | 264 rtt_msec_observations_.AddObservation( |
| 257 Observation(observed_rtt.InMilliseconds(), now)); | 265 Observation(observed_rtt.InMilliseconds(), now)); |
| 258 | 266 |
| 259 // Compare the RTT observation with the estimated value and record it. | 267 // Compare the RTT observation with the estimated value and record it. |
| 260 if (estimated_median_network_quality_.rtt() != | 268 if (estimated_median_network_quality_.rtt() != |
| 261 NetworkQuality::InvalidRTT()) { | 269 NetworkQuality::InvalidRTT()) { |
| 262 RecordRTTUMA(estimated_median_network_quality_.rtt().InMilliseconds(), | 270 RecordRTTUMA(estimated_median_network_quality_.rtt().InMilliseconds(), |
| 263 observed_rtt.InMilliseconds()); | 271 observed_rtt.InMilliseconds()); |
| 264 } | 272 } |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 285 kbps_f = std::numeric_limits<int32_t>::max() - 1; | 293 kbps_f = std::numeric_limits<int32_t>::max() - 1; |
| 286 | 294 |
| 287 int32_t kbps = static_cast<int32_t>(kbps_f); | 295 int32_t kbps = static_cast<int32_t>(kbps_f); |
| 288 | 296 |
| 289 // If the |kbps| is less than 1, we set it to 1 to differentiate from case | 297 // If the |kbps| is less than 1, we set it to 1 to differentiate from case |
| 290 // when there is no connection. | 298 // when there is no connection. |
| 291 if (kbps_f > 0.0 && kbps == 0) | 299 if (kbps_f > 0.0 && kbps == 0) |
| 292 kbps = 1; | 300 kbps = 1; |
| 293 | 301 |
| 294 if (kbps > 0) { | 302 if (kbps > 0) { |
| 295 if (kbps > peak_kbps_since_last_connection_change_) | 303 if (kbps > peak_network_quality_.downstream_throughput_kbps()) { |
| 296 peak_kbps_since_last_connection_change_ = kbps; | 304 peak_network_quality_ = |
| 305 NetworkQuality(peak_network_quality_.rtt(), kbps); | |
| 306 } | |
| 297 | 307 |
| 298 kbps_observations_.AddObservation(Observation(kbps, now)); | 308 kbps_observations_.AddObservation(Observation(kbps, now)); |
| 299 } | 309 } |
| 300 } | 310 } |
| 301 } | 311 } |
| 302 | 312 |
| 303 void NetworkQualityEstimator::RecordRTTUMA(int32_t estimated_value_msec, | 313 void NetworkQualityEstimator::RecordRTTUMA(int32_t estimated_value_msec, |
| 304 int32_t actual_value_msec) const { | 314 int32_t actual_value_msec) const { |
| 305 DCHECK(thread_checker_.CalledOnValidThread()); | 315 DCHECK(thread_checker_.CalledOnValidThread()); |
| 306 | 316 |
| 307 // Record the difference between the actual and the estimated value. | 317 // Record the difference between the actual and the estimated value. |
| 308 if (estimated_value_msec >= actual_value_msec) { | 318 if (estimated_value_msec >= actual_value_msec) { |
| 309 base::HistogramBase* difference_rtt = | 319 base::HistogramBase* difference_rtt = |
| 310 GetHistogram("DifferenceRTTEstimatedAndActual.", | 320 GetHistogram("DifferenceRTTEstimatedAndActual.", |
| 311 current_connection_type_, 10 * 1000); // 10 seconds | 321 current_network_id_.type, 10 * 1000); // 10 seconds |
| 312 difference_rtt->Add(estimated_value_msec - actual_value_msec); | 322 difference_rtt->Add(estimated_value_msec - actual_value_msec); |
| 313 } else { | 323 } else { |
| 314 base::HistogramBase* difference_rtt = | 324 base::HistogramBase* difference_rtt = |
| 315 GetHistogram("DifferenceRTTActualAndEstimated.", | 325 GetHistogram("DifferenceRTTActualAndEstimated.", |
| 316 current_connection_type_, 10 * 1000); // 10 seconds | 326 current_network_id_.type, 10 * 1000); // 10 seconds |
| 317 difference_rtt->Add(actual_value_msec - estimated_value_msec); | 327 difference_rtt->Add(actual_value_msec - estimated_value_msec); |
| 318 } | 328 } |
| 319 | 329 |
| 320 // Record all the RTT observations. | 330 // Record all the RTT observations. |
| 321 base::HistogramBase* rtt_observations = | 331 base::HistogramBase* rtt_observations = |
| 322 GetHistogram("RTTObservations.", current_connection_type_, | 332 GetHistogram("RTTObservations.", current_network_id_.type, |
| 323 10 * 1000); // 10 seconds upper bound | 333 10 * 1000); // 10 seconds upper bound |
| 324 rtt_observations->Add(actual_value_msec); | 334 rtt_observations->Add(actual_value_msec); |
| 325 | 335 |
| 326 if (actual_value_msec == 0) | 336 if (actual_value_msec == 0) |
| 327 return; | 337 return; |
| 328 | 338 |
| 329 int32 ratio = (estimated_value_msec * 100) / actual_value_msec; | 339 int32 ratio = (estimated_value_msec * 100) / actual_value_msec; |
| 330 | 340 |
| 331 // Record the accuracy of estimation by recording the ratio of estimated | 341 // Record the accuracy of estimation by recording the ratio of estimated |
| 332 // value to the actual value. | 342 // value to the actual value. |
| 333 base::HistogramBase* ratio_median_rtt = GetHistogram( | 343 base::HistogramBase* ratio_median_rtt = GetHistogram( |
| 334 "RatioEstimatedToActualRTT.", current_connection_type_, 1000); | 344 "RatioEstimatedToActualRTT.", current_network_id_.type, 1000); |
| 335 ratio_median_rtt->Add(ratio); | 345 ratio_median_rtt->Add(ratio); |
| 336 } | 346 } |
| 337 | 347 |
| 338 void NetworkQualityEstimator::OnConnectionTypeChanged( | 348 void NetworkQualityEstimator::OnConnectionTypeChanged( |
| 339 NetworkChangeNotifier::ConnectionType type) { | 349 NetworkChangeNotifier::ConnectionType type) { |
| 340 DCHECK(thread_checker_.CalledOnValidThread()); | 350 DCHECK(thread_checker_.CalledOnValidThread()); |
| 341 if (fastest_rtt_since_last_connection_change_ != | 351 if (peak_network_quality_.rtt() != NetworkQuality::InvalidRTT()) { |
| 342 NetworkQuality::InvalidRTT()) { | 352 switch (current_network_id_.type) { |
| 343 switch (current_connection_type_) { | |
| 344 case NetworkChangeNotifier::CONNECTION_UNKNOWN: | 353 case NetworkChangeNotifier::CONNECTION_UNKNOWN: |
| 345 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Unknown", | 354 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Unknown", |
| 346 fastest_rtt_since_last_connection_change_); | 355 peak_network_quality_.rtt()); |
| 347 break; | 356 break; |
| 348 case NetworkChangeNotifier::CONNECTION_ETHERNET: | 357 case NetworkChangeNotifier::CONNECTION_ETHERNET: |
| 349 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Ethernet", | 358 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Ethernet", |
| 350 fastest_rtt_since_last_connection_change_); | 359 peak_network_quality_.rtt()); |
| 351 break; | 360 break; |
| 352 case NetworkChangeNotifier::CONNECTION_WIFI: | 361 case NetworkChangeNotifier::CONNECTION_WIFI: |
| 353 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Wifi", | 362 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Wifi", peak_network_quality_.rtt()); |
| 354 fastest_rtt_since_last_connection_change_); | |
| 355 break; | 363 break; |
| 356 case NetworkChangeNotifier::CONNECTION_2G: | 364 case NetworkChangeNotifier::CONNECTION_2G: |
| 357 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.2G", | 365 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.2G", peak_network_quality_.rtt()); |
| 358 fastest_rtt_since_last_connection_change_); | |
| 359 break; | 366 break; |
| 360 case NetworkChangeNotifier::CONNECTION_3G: | 367 case NetworkChangeNotifier::CONNECTION_3G: |
| 361 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.3G", | 368 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.3G", peak_network_quality_.rtt()); |
| 362 fastest_rtt_since_last_connection_change_); | |
| 363 break; | 369 break; |
| 364 case NetworkChangeNotifier::CONNECTION_4G: | 370 case NetworkChangeNotifier::CONNECTION_4G: |
| 365 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.4G", | 371 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.4G", peak_network_quality_.rtt()); |
| 366 fastest_rtt_since_last_connection_change_); | |
| 367 break; | 372 break; |
| 368 case NetworkChangeNotifier::CONNECTION_NONE: | 373 case NetworkChangeNotifier::CONNECTION_NONE: |
| 369 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.None", | 374 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.None", peak_network_quality_.rtt()); |
| 370 fastest_rtt_since_last_connection_change_); | |
| 371 break; | 375 break; |
| 372 case NetworkChangeNotifier::CONNECTION_BLUETOOTH: | 376 case NetworkChangeNotifier::CONNECTION_BLUETOOTH: |
| 373 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Bluetooth", | 377 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Bluetooth", |
| 374 fastest_rtt_since_last_connection_change_); | 378 peak_network_quality_.rtt()); |
| 375 break; | 379 break; |
| 376 default: | 380 default: |
| 377 NOTREACHED(); | 381 NOTREACHED() << "Unexpected connection type = " |
| 382 << current_network_id_.type; | |
| 378 break; | 383 break; |
| 379 } | 384 } |
| 380 } | 385 } |
| 381 | 386 |
| 382 if (peak_kbps_since_last_connection_change_ != | 387 if (peak_network_quality_.downstream_throughput_kbps() != |
| 383 NetworkQuality::kInvalidThroughput) { | 388 NetworkQuality::kInvalidThroughput) { |
| 384 switch (current_connection_type_) { | 389 switch (current_network_id_.type) { |
| 385 case NetworkChangeNotifier::CONNECTION_UNKNOWN: | 390 case NetworkChangeNotifier::CONNECTION_UNKNOWN: |
| 386 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Unknown", | 391 UMA_HISTOGRAM_COUNTS( |
| 387 peak_kbps_since_last_connection_change_); | 392 "NQE.PeakKbps.Unknown", |
| 393 peak_network_quality_.downstream_throughput_kbps()); | |
| 388 break; | 394 break; |
| 389 case NetworkChangeNotifier::CONNECTION_ETHERNET: | 395 case NetworkChangeNotifier::CONNECTION_ETHERNET: |
| 390 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Ethernet", | 396 UMA_HISTOGRAM_COUNTS( |
| 391 peak_kbps_since_last_connection_change_); | 397 "NQE.PeakKbps.Ethernet", |
| 398 peak_network_quality_.downstream_throughput_kbps()); | |
| 392 break; | 399 break; |
| 393 case NetworkChangeNotifier::CONNECTION_WIFI: | 400 case NetworkChangeNotifier::CONNECTION_WIFI: |
| 394 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Wifi", | 401 UMA_HISTOGRAM_COUNTS( |
| 395 peak_kbps_since_last_connection_change_); | 402 "NQE.PeakKbps.Wifi", |
| 403 peak_network_quality_.downstream_throughput_kbps()); | |
| 396 break; | 404 break; |
| 397 case NetworkChangeNotifier::CONNECTION_2G: | 405 case NetworkChangeNotifier::CONNECTION_2G: |
| 398 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.2G", | 406 UMA_HISTOGRAM_COUNTS( |
| 399 peak_kbps_since_last_connection_change_); | 407 "NQE.PeakKbps.2G", |
| 408 peak_network_quality_.downstream_throughput_kbps()); | |
| 400 break; | 409 break; |
| 401 case NetworkChangeNotifier::CONNECTION_3G: | 410 case NetworkChangeNotifier::CONNECTION_3G: |
| 402 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.3G", | 411 UMA_HISTOGRAM_COUNTS( |
| 403 peak_kbps_since_last_connection_change_); | 412 "NQE.PeakKbps.3G", |
| 413 peak_network_quality_.downstream_throughput_kbps()); | |
| 404 break; | 414 break; |
| 405 case NetworkChangeNotifier::CONNECTION_4G: | 415 case NetworkChangeNotifier::CONNECTION_4G: |
| 406 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.4G", | 416 UMA_HISTOGRAM_COUNTS( |
| 407 peak_kbps_since_last_connection_change_); | 417 "NQE.PeakKbps.4G", |
| 418 peak_network_quality_.downstream_throughput_kbps()); | |
| 408 break; | 419 break; |
| 409 case NetworkChangeNotifier::CONNECTION_NONE: | 420 case NetworkChangeNotifier::CONNECTION_NONE: |
| 410 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.None", | 421 UMA_HISTOGRAM_COUNTS( |
| 411 peak_kbps_since_last_connection_change_); | 422 "NQE.PeakKbps.None", |
| 423 peak_network_quality_.downstream_throughput_kbps()); | |
| 412 break; | 424 break; |
| 413 case NetworkChangeNotifier::CONNECTION_BLUETOOTH: | 425 case NetworkChangeNotifier::CONNECTION_BLUETOOTH: |
| 414 UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Bluetooth", | 426 UMA_HISTOGRAM_COUNTS( |
| 415 peak_kbps_since_last_connection_change_); | 427 "NQE.PeakKbps.Bluetooth", |
| 428 peak_network_quality_.downstream_throughput_kbps()); | |
| 416 break; | 429 break; |
| 417 default: | 430 default: |
| 418 NOTREACHED(); | 431 NOTREACHED() << "Unexpected connection type = " |
| 432 << current_network_id_.type; | |
| 419 break; | 433 break; |
| 420 } | 434 } |
| 421 } | 435 } |
| 422 | 436 |
| 423 NetworkQuality network_quality; | 437 NetworkQuality network_quality; |
| 424 if (GetEstimate(&network_quality)) { | 438 if (GetEstimate(&network_quality)) { |
| 425 // Add the 50th percentile value. | 439 // Add the 50th percentile value. |
| 426 base::HistogramBase* rtt_percentile = | 440 base::HistogramBase* rtt_percentile = |
| 427 GetHistogram("RTT.Percentile50.", current_connection_type_, | 441 GetHistogram("RTT.Percentile50.", current_network_id_.type, |
| 428 10 * 1000); // 10 seconds | 442 10 * 1000); // 10 seconds |
| 429 rtt_percentile->Add(network_quality.rtt().InMilliseconds()); | 443 rtt_percentile->Add(network_quality.rtt().InMilliseconds()); |
| 430 | 444 |
| 431 // Add the remaining percentile values. | 445 // Add the remaining percentile values. |
| 432 static const int kPercentiles[] = {0, 10, 90, 100}; | 446 static const int kPercentiles[] = {0, 10, 90, 100}; |
| 433 for (size_t i = 0; i < arraysize(kPercentiles); ++i) { | 447 for (size_t i = 0; i < arraysize(kPercentiles); ++i) { |
| 434 network_quality = GetEstimate(kPercentiles[i]); | 448 network_quality = GetEstimate(kPercentiles[i]); |
| 435 | 449 |
| 436 char percentile_stringified[20]; | 450 char percentile_stringified[20]; |
| 437 base::strings::SafeSPrintf(percentile_stringified, "%d", kPercentiles[i]); | 451 base::strings::SafeSPrintf(percentile_stringified, "%d", kPercentiles[i]); |
| 438 | 452 |
| 439 rtt_percentile = GetHistogram( | 453 rtt_percentile = GetHistogram( |
| 440 "RTT.Percentile" + std::string(percentile_stringified) + ".", | 454 "RTT.Percentile" + std::string(percentile_stringified) + ".", |
|
pauljensen
2015/07/15 15:12:45
std::string(percentile_stringified)->IntToString(k
tbansal1
2015/07/15 17:33:47
Done.
| |
| 441 current_connection_type_, 10 * 1000); // 10 seconds | 455 current_network_id_.type, 10 * 1000); // 10 seconds |
| 442 rtt_percentile->Add(network_quality.rtt().InMilliseconds()); | 456 rtt_percentile->Add(network_quality.rtt().InMilliseconds()); |
| 443 } | 457 } |
| 444 } | 458 } |
| 445 | 459 |
| 460 // Write the estimates of the previous network to the cache. | |
| 461 CacheNetworkQualityEstimate(); | |
| 462 | |
| 463 // Clear the local state. | |
| 446 last_connection_change_ = base::TimeTicks::Now(); | 464 last_connection_change_ = base::TimeTicks::Now(); |
| 447 peak_kbps_since_last_connection_change_ = NetworkQuality::kInvalidThroughput; | 465 peak_network_quality_ = NetworkQuality(); |
| 448 fastest_rtt_since_last_connection_change_ = NetworkQuality::InvalidRTT(); | |
| 449 kbps_observations_.Clear(); | 466 kbps_observations_.Clear(); |
| 450 rtt_msec_observations_.Clear(); | 467 rtt_msec_observations_.Clear(); |
| 451 current_connection_type_ = type; | 468 current_network_id_ = GetCurrentNetworkID(); |
| 452 | 469 |
| 453 AddDefaultEstimates(); | 470 // Read any cached estimates for the new network. If cached estimates are |
| 471 // unavailable, add the default estimates. | |
| 472 if (!ReadCachedNetworkQualityEstimate()) | |
| 473 AddDefaultEstimates(); | |
| 454 estimated_median_network_quality_ = NetworkQuality(); | 474 estimated_median_network_quality_ = NetworkQuality(); |
| 455 } | 475 } |
| 456 | 476 |
| 457 NetworkQuality NetworkQualityEstimator::GetPeakEstimate() const { | 477 NetworkQuality NetworkQualityEstimator::GetPeakEstimate() const { |
| 458 DCHECK(thread_checker_.CalledOnValidThread()); | 478 DCHECK(thread_checker_.CalledOnValidThread()); |
| 459 | 479 |
| 460 return NetworkQuality(fastest_rtt_since_last_connection_change_, | 480 return peak_network_quality_; |
| 461 peak_kbps_since_last_connection_change_); | |
| 462 } | 481 } |
| 463 | 482 |
| 464 bool NetworkQualityEstimator::GetEstimate(NetworkQuality* median) const { | 483 bool NetworkQualityEstimator::GetEstimate(NetworkQuality* median) const { |
| 465 if (kbps_observations_.Size() == 0 || rtt_msec_observations_.Size() == 0) { | 484 if (kbps_observations_.Size() == 0 || rtt_msec_observations_.Size() == 0) { |
| 466 *median = NetworkQuality(); | 485 *median = NetworkQuality(); |
| 467 return false; | 486 return false; |
| 468 } | 487 } |
| 469 *median = GetEstimate(50); | 488 *median = GetEstimate(50); |
| 470 return true; | 489 return true; |
| 471 } | 490 } |
| 472 | 491 |
| 473 size_t NetworkQualityEstimator::GetMaximumObservationBufferSizeForTests() | |
| 474 const { | |
| 475 return kMaximumObservationsBufferSize; | |
| 476 } | |
| 477 | |
| 478 bool NetworkQualityEstimator::VerifyBufferSizeForTests( | |
| 479 size_t expected_size) const { | |
| 480 return kbps_observations_.Size() == expected_size && | |
| 481 rtt_msec_observations_.Size() == expected_size; | |
| 482 } | |
| 483 | |
| 484 NetworkQualityEstimator::Observation::Observation(int32_t value, | 492 NetworkQualityEstimator::Observation::Observation(int32_t value, |
| 485 base::TimeTicks timestamp) | 493 base::TimeTicks timestamp) |
| 486 : value(value), timestamp(timestamp) { | 494 : value(value), timestamp(timestamp) { |
| 487 DCHECK_GE(value, 0); | 495 DCHECK_GE(value, 0); |
| 488 DCHECK(!timestamp.is_null()); | 496 DCHECK(!timestamp.is_null()); |
| 489 } | 497 } |
| 490 | 498 |
| 491 NetworkQualityEstimator::Observation::~Observation() { | 499 NetworkQualityEstimator::Observation::~Observation() { |
| 492 } | 500 } |
| 493 | 501 |
| 494 NetworkQualityEstimator::ObservationBuffer::ObservationBuffer( | 502 NetworkQualityEstimator::ObservationBuffer::ObservationBuffer( |
| 495 double weight_multiplier_per_second) | 503 double weight_multiplier_per_second) |
| 496 : weight_multiplier_per_second_(weight_multiplier_per_second) { | 504 : weight_multiplier_per_second_(weight_multiplier_per_second) { |
| 497 static_assert(kMaximumObservationsBufferSize > 0U, | 505 static_assert(kMaximumObservationsBufferSize > 0U, |
| 498 "Minimum size of observation buffer must be > 0"); | 506 "Minimum size of observation buffer must be > 0"); |
| 499 DCHECK_GE(weight_multiplier_per_second_, 0.0); | 507 DCHECK_GE(weight_multiplier_per_second_, 0.0); |
| 500 DCHECK_LE(weight_multiplier_per_second_, 1.0); | 508 DCHECK_LE(weight_multiplier_per_second_, 1.0); |
| 501 } | 509 } |
| 502 | 510 |
| 503 NetworkQualityEstimator::ObservationBuffer::~ObservationBuffer() { | 511 NetworkQualityEstimator::ObservationBuffer::~ObservationBuffer() { |
| 504 } | 512 } |
| 505 | 513 |
| 506 void NetworkQualityEstimator::ObservationBuffer::AddObservation( | 514 void NetworkQualityEstimator::ObservationBuffer::AddObservation( |
| 507 const Observation& observation) { | 515 const Observation& observation) { |
| 508 DCHECK_LE(observations_.size(), kMaximumObservationsBufferSize); | 516 DCHECK_LE(observations_.size(), |
| 517 static_cast<size_t>(kMaximumObservationsBufferSize)); | |
|
pauljensen
2015/07/15 15:12:45
Why is this static_cast done on lines 517 and 525
tbansal1
2015/07/15 17:33:47
Removed that line, it was giving linking error.
| |
| 509 // Evict the oldest element if the buffer is already full. | 518 // Evict the oldest element if the buffer is already full. |
| 510 if (observations_.size() == kMaximumObservationsBufferSize) | 519 if (observations_.size() == kMaximumObservationsBufferSize) |
| 511 observations_.pop_front(); | 520 observations_.pop_front(); |
| 512 | 521 |
| 513 observations_.push_back(observation); | 522 observations_.push_back(observation); |
| 514 DCHECK_LE(observations_.size(), kMaximumObservationsBufferSize); | 523 DCHECK_LE(observations_.size(), kMaximumObservationsBufferSize); |
| 524 DCHECK_LE(observations_.size(), | |
| 525 static_cast<size_t>(kMaximumObservationsBufferSize)); | |
| 515 } | 526 } |
| 516 | 527 |
| 517 size_t NetworkQualityEstimator::ObservationBuffer::Size() const { | 528 size_t NetworkQualityEstimator::ObservationBuffer::Size() const { |
| 518 return observations_.size(); | 529 return observations_.size(); |
| 519 } | 530 } |
| 520 | 531 |
| 521 void NetworkQualityEstimator::ObservationBuffer::Clear() { | 532 void NetworkQualityEstimator::ObservationBuffer::Clear() { |
| 522 observations_.clear(); | 533 observations_.clear(); |
| 523 DCHECK(observations_.empty()); | 534 DCHECK(observations_.empty()); |
| 524 } | 535 } |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 589 } | 600 } |
| 590 | 601 |
| 591 // Computation may reach here due to floating point errors. This may happen | 602 // Computation may reach here due to floating point errors. This may happen |
| 592 // if |percentile| was 100 (or close to 100), and |desired_weight| was | 603 // if |percentile| was 100 (or close to 100), and |desired_weight| was |
| 593 // slightly larger than |total_weight| (due to floating point errors). | 604 // slightly larger than |total_weight| (due to floating point errors). |
| 594 // In this case, we return the highest |value| among all observations. | 605 // In this case, we return the highest |value| among all observations. |
| 595 // This is same as value of the last observation in the sorted vector. | 606 // This is same as value of the last observation in the sorted vector. |
| 596 return weighted_observations.at(weighted_observations.size() - 1).value; | 607 return weighted_observations.at(weighted_observations.size() - 1).value; |
| 597 } | 608 } |
| 598 | 609 |
| 610 NetworkQualityEstimator::NetworkID | |
| 611 NetworkQualityEstimator::GetCurrentNetworkID() const { | |
| 612 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 613 | |
| 614 // TODO(tbansal): crbug.com/498068 Add NetworkQualityEstimatorAndroid class | |
| 615 // that overrides this method on the Android platform. | |
| 616 | |
| 617 // It is possible that the connection type changed between when | |
| 618 // GetConnectionType() was called and when the API to determine the | |
| 619 // network name was called. Check if that happened and retry until the | |
| 620 // connection type stabilizes. This is an imperfect solution but should | |
| 621 // capture majority of cases, and should not significantly affect estimates | |
| 622 // (that are approximate to begin with). | |
| 623 while (true) { | |
| 624 NetworkQualityEstimator::NetworkID network_id( | |
| 625 NetworkChangeNotifier::GetConnectionType(), std::string()); | |
| 626 | |
| 627 switch (network_id.type) { | |
| 628 case NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN: | |
| 629 case NetworkChangeNotifier::ConnectionType::CONNECTION_NONE: | |
| 630 case NetworkChangeNotifier::ConnectionType::CONNECTION_BLUETOOTH: | |
| 631 break; | |
| 632 case NetworkChangeNotifier::ConnectionType::CONNECTION_ETHERNET: | |
| 633 network_id.id = "Ethernet"; | |
|
pauljensen
2015/07/15 15:12:45
Strikes me as a bit odd that we match Ethernet out
tbansal1
2015/07/15 17:33:47
I am okay with removing Ethernet. MAC or IP based
pauljensen
2015/07/15 17:44:05
I don't think matching via IPs is a good idea (it'
tbansal1
2015/07/15 17:54:18
Done.
| |
| 634 break; | |
| 635 case NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI: | |
| 636 #if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS) | |
| 637 network_id.id = GetWifiSSID(); | |
| 638 #endif | |
| 639 break; | |
| 640 case NetworkChangeNotifier::ConnectionType::CONNECTION_2G: | |
| 641 case NetworkChangeNotifier::ConnectionType::CONNECTION_3G: | |
| 642 case NetworkChangeNotifier::ConnectionType::CONNECTION_4G: | |
| 643 #if defined(OS_ANDROID) | |
| 644 network_id.id = android::GetTelephonyNetworkOperator(); | |
| 645 #endif | |
| 646 break; | |
| 647 default: | |
| 648 NOTREACHED() << "Unexpected connection type = " << network_id.type; | |
| 649 break; | |
| 650 } | |
| 651 | |
| 652 if (network_id.type == NetworkChangeNotifier::GetConnectionType()) | |
| 653 return network_id; | |
| 654 } | |
| 655 NOTREACHED(); | |
| 656 } | |
| 657 | |
| 658 bool NetworkQualityEstimator::ReadCachedNetworkQualityEstimate() { | |
| 659 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 660 | |
| 661 // If the network name is unavailable, caching should not be performed. | |
| 662 if (current_network_id_.id.empty()) | |
| 663 return false; | |
| 664 | |
| 665 CachedNetworkQualities::iterator it = | |
|
pauljensen
2015/07/15 15:12:45
const_iterator?
tbansal1
2015/07/15 17:33:47
Done.
| |
| 666 cached_network_qualities_.find(current_network_id_); | |
| 667 | |
| 668 if (it == cached_network_qualities_.end()) | |
| 669 return false; | |
| 670 | |
| 671 NetworkQuality network_quality( | |
|
pauljensen
2015/07/15 15:12:45
why not network_quality(it->second->network_qualit
tbansal1
2015/07/15 17:33:47
Done.
| |
| 672 it->second->network_quality().rtt(), | |
| 673 it->second->network_quality().downstream_throughput_kbps()); | |
| 674 | |
| 675 DCHECK_NE(NetworkQuality::InvalidRTT(), network_quality.rtt()); | |
| 676 DCHECK_NE(NetworkQuality::kInvalidThroughput, | |
| 677 network_quality.downstream_throughput_kbps()); | |
| 678 | |
| 679 kbps_observations_.AddObservation(Observation( | |
| 680 network_quality.downstream_throughput_kbps(), base::TimeTicks::Now())); | |
| 681 rtt_msec_observations_.AddObservation(Observation( | |
| 682 network_quality.rtt().InMilliseconds(), base::TimeTicks::Now())); | |
| 683 return true; | |
| 684 } | |
| 685 | |
| 686 void NetworkQualityEstimator::CacheNetworkQualityEstimate() { | |
| 687 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 688 DCHECK_LE(cached_network_qualities_.size(), | |
| 689 static_cast<size_t>(kMaximumNetworkQualityCacheSize)); | |
| 690 | |
| 691 // If the network name is unavailable, caching should not be performed. | |
| 692 if (current_network_id_.id.empty()) | |
| 693 return; | |
| 694 | |
| 695 NetworkQuality network_quality; | |
| 696 if (!GetEstimate(&network_quality)) | |
| 697 return; | |
| 698 | |
| 699 DCHECK_NE(NetworkQuality::InvalidRTT(), network_quality.rtt()); | |
| 700 DCHECK_NE(NetworkQuality::kInvalidThroughput, | |
| 701 network_quality.downstream_throughput_kbps()); | |
| 702 | |
| 703 if (cached_network_qualities_.size() == kMaximumNetworkQualityCacheSize) { | |
| 704 // Remove the oldest entry. | |
| 705 CachedNetworkQualities::iterator oldest_entry_iterator = | |
| 706 cached_network_qualities_.begin(); | |
| 707 | |
| 708 for (CachedNetworkQualities::iterator it = | |
|
pauljensen
2015/07/15 15:12:45
const_iterator?
tbansal1
2015/07/15 17:33:47
Done.
| |
| 709 cached_network_qualities_.begin(); | |
| 710 it != cached_network_qualities_.end(); ++it) { | |
| 711 if ((it->second)->OlderThan(*(oldest_entry_iterator->second.get()))) | |
| 712 oldest_entry_iterator = it; | |
| 713 } | |
| 714 cached_network_qualities_.erase(oldest_entry_iterator); | |
| 715 } | |
| 716 DCHECK_LT(cached_network_qualities_.size(), | |
| 717 static_cast<size_t>(kMaximumNetworkQualityCacheSize)); | |
| 718 | |
| 719 cached_network_qualities_[current_network_id_].reset( | |
| 720 new CachedNetworkQuality(network_quality)); | |
| 721 DCHECK_LE(cached_network_qualities_.size(), | |
| 722 static_cast<size_t>(kMaximumNetworkQualityCacheSize)); | |
| 723 } | |
| 724 | |
| 725 NetworkQualityEstimator::CachedNetworkQuality::CachedNetworkQuality( | |
| 726 const NetworkQuality& network_quality) | |
| 727 : last_update_time_(base::TimeTicks::Now()), | |
| 728 network_quality_(network_quality) { | |
| 729 } | |
| 730 | |
| 731 NetworkQualityEstimator::CachedNetworkQuality::~CachedNetworkQuality() { | |
| 732 } | |
| 733 | |
| 734 bool NetworkQualityEstimator::CachedNetworkQuality::OlderThan( | |
| 735 const CachedNetworkQuality& cached_network_quality) const { | |
| 736 return last_update_time_ < cached_network_quality.last_update_time_; | |
| 737 } | |
| 738 | |
| 599 } // namespace net | 739 } // namespace net |
| OLD | NEW |