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