| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "net/base/network_quality_estimator.h" | |
| 6 | |
| 7 #include <float.h> | |
| 8 #include <algorithm> | |
| 9 #include <cmath> | |
| 10 #include <limits> | |
| 11 #include <utility> | |
| 12 #include <vector> | |
| 13 | |
| 14 #include "base/logging.h" | |
| 15 #include "base/metrics/histogram.h" | |
| 16 #include "base/metrics/histogram_base.h" | |
| 17 #include "base/strings/string_number_conversions.h" | |
| 18 #include "base/thread_task_runner_handle.h" | |
| 19 #include "base/trace_event/trace_event.h" | |
| 20 #include "build/build_config.h" | |
| 21 #include "net/base/load_flags.h" | |
| 22 #include "net/base/load_timing_info.h" | |
| 23 #include "net/base/network_interfaces.h" | |
| 24 #include "net/base/socket_performance_watcher.h" | |
| 25 #include "net/base/url_util.h" | |
| 26 #include "net/url_request/url_request.h" | |
| 27 #include "url/gurl.h" | |
| 28 | |
| 29 #if defined(OS_ANDROID) | |
| 30 #include "net/android/network_library.h" | |
| 31 #endif // OS_ANDROID | |
| 32 | |
| 33 namespace { | |
| 34 | |
| 35 // Default value of the half life (in seconds) for computing time weighted | |
| 36 // percentiles. Every half life, the weight of all observations reduces by | |
| 37 // half. Lowering the half life would reduce the weight of older values faster. | |
| 38 const int kDefaultHalfLifeSeconds = 60; | |
| 39 | |
| 40 // Name of the variation parameter that holds the value of the half life (in | |
| 41 // seconds) of the observations. | |
| 42 const char kHalfLifeSecondsParamName[] = "HalfLifeSeconds"; | |
| 43 | |
| 44 // Returns a descriptive name corresponding to |connection_type|. | |
| 45 const char* GetNameForConnectionType( | |
| 46 net::NetworkChangeNotifier::ConnectionType connection_type) { | |
| 47 switch (connection_type) { | |
| 48 case net::NetworkChangeNotifier::CONNECTION_UNKNOWN: | |
| 49 return "Unknown"; | |
| 50 case net::NetworkChangeNotifier::CONNECTION_ETHERNET: | |
| 51 return "Ethernet"; | |
| 52 case net::NetworkChangeNotifier::CONNECTION_WIFI: | |
| 53 return "WiFi"; | |
| 54 case net::NetworkChangeNotifier::CONNECTION_2G: | |
| 55 return "2G"; | |
| 56 case net::NetworkChangeNotifier::CONNECTION_3G: | |
| 57 return "3G"; | |
| 58 case net::NetworkChangeNotifier::CONNECTION_4G: | |
| 59 return "4G"; | |
| 60 case net::NetworkChangeNotifier::CONNECTION_NONE: | |
| 61 return "None"; | |
| 62 case net::NetworkChangeNotifier::CONNECTION_BLUETOOTH: | |
| 63 return "Bluetooth"; | |
| 64 default: | |
| 65 NOTREACHED(); | |
| 66 break; | |
| 67 } | |
| 68 return ""; | |
| 69 } | |
| 70 | |
| 71 // Suffix of the name of the variation parameter that contains the default RTT | |
| 72 // observation (in milliseconds). Complete name of the variation parameter | |
| 73 // would be |ConnectionType|.|kDefaultRTTMsecObservationSuffix| where | |
| 74 // |ConnectionType| is from |kConnectionTypeNames|. For example, variation | |
| 75 // parameter for Wi-Fi would be "WiFi.DefaultMedianRTTMsec". | |
| 76 const char kDefaultRTTMsecObservationSuffix[] = ".DefaultMedianRTTMsec"; | |
| 77 | |
| 78 // Suffix of the name of the variation parameter that contains the default | |
| 79 // downstream throughput observation (in Kbps). Complete name of the variation | |
| 80 // parameter would be |ConnectionType|.|kDefaultKbpsObservationSuffix| where | |
| 81 // |ConnectionType| is from |kConnectionTypeNames|. For example, variation | |
| 82 // parameter for Wi-Fi would be "WiFi.DefaultMedianKbps". | |
| 83 const char kDefaultKbpsObservationSuffix[] = ".DefaultMedianKbps"; | |
| 84 | |
| 85 // Computes and returns the weight multiplier per second. | |
| 86 // |variation_params| is the map containing all field trial parameters | |
| 87 // related to NetworkQualityEstimator field trial. | |
| 88 double GetWeightMultiplierPerSecond( | |
| 89 const std::map<std::string, std::string>& variation_params) { | |
| 90 int half_life_seconds = kDefaultHalfLifeSeconds; | |
| 91 int32_t variations_value = 0; | |
| 92 auto it = variation_params.find(kHalfLifeSecondsParamName); | |
| 93 if (it != variation_params.end() && | |
| 94 base::StringToInt(it->second, &variations_value) && | |
| 95 variations_value >= 1) { | |
| 96 half_life_seconds = variations_value; | |
| 97 } | |
| 98 DCHECK_GT(half_life_seconds, 0); | |
| 99 return exp(log(0.5) / half_life_seconds); | |
| 100 } | |
| 101 | |
| 102 // Returns the histogram that should be used to record the given statistic. | |
| 103 // |max_limit| is the maximum value that can be stored in the histogram. | |
| 104 base::HistogramBase* GetHistogram( | |
| 105 const std::string& statistic_name, | |
| 106 net::NetworkChangeNotifier::ConnectionType type, | |
| 107 int32_t max_limit) { | |
| 108 const base::LinearHistogram::Sample kLowerLimit = 1; | |
| 109 DCHECK_GT(max_limit, kLowerLimit); | |
| 110 const size_t kBucketCount = 50; | |
| 111 | |
| 112 // Prefix of network quality estimator histograms. | |
| 113 const char prefix[] = "NQE."; | |
| 114 return base::Histogram::FactoryGet( | |
| 115 prefix + statistic_name + GetNameForConnectionType(type), kLowerLimit, | |
| 116 max_limit, kBucketCount, base::HistogramBase::kUmaTargetedHistogramFlag); | |
| 117 } | |
| 118 | |
| 119 } // namespace | |
| 120 | |
| 121 namespace net { | |
| 122 | |
| 123 // SocketWatcher implements SocketPerformanceWatcher, and notifies | |
| 124 // NetworkQualityEstimator of various socket performance events. SocketWatcher | |
| 125 // is not thread-safe. | |
| 126 class NetworkQualityEstimator::SocketWatcher : public SocketPerformanceWatcher { | |
| 127 public: | |
| 128 SocketWatcher( | |
| 129 SocketPerformanceWatcherFactory::Protocol protocol, | |
| 130 scoped_refptr<base::SingleThreadTaskRunner> task_runner, | |
| 131 const base::WeakPtr<NetworkQualityEstimator>& network_quality_estimator) | |
| 132 : protocol_(protocol), | |
| 133 task_runner_(std::move(task_runner)), | |
| 134 network_quality_estimator_(network_quality_estimator) {} | |
| 135 | |
| 136 ~SocketWatcher() override {} | |
| 137 | |
| 138 // SocketPerformanceWatcher implementation: | |
| 139 bool ShouldNotifyUpdatedRTT() const override { | |
| 140 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 141 | |
| 142 return true; | |
| 143 } | |
| 144 | |
| 145 void OnUpdatedRTTAvailable(const base::TimeDelta& rtt) override { | |
| 146 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 147 | |
| 148 task_runner_->PostTask( | |
| 149 FROM_HERE, base::Bind(&NetworkQualityEstimator::OnUpdatedRTTAvailable, | |
| 150 network_quality_estimator_, protocol_, rtt)); | |
| 151 } | |
| 152 | |
| 153 void OnConnectionChanged() override { | |
| 154 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 155 } | |
| 156 | |
| 157 private: | |
| 158 // Transport layer protocol used by the socket that |this| is watching. | |
| 159 const SocketPerformanceWatcherFactory::Protocol protocol_; | |
| 160 | |
| 161 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | |
| 162 | |
| 163 base::WeakPtr<NetworkQualityEstimator> network_quality_estimator_; | |
| 164 | |
| 165 base::ThreadChecker thread_checker_; | |
| 166 | |
| 167 DISALLOW_COPY_AND_ASSIGN(SocketWatcher); | |
| 168 }; | |
| 169 | |
| 170 // SocketWatcherFactory implements SocketPerformanceWatcherFactory, and is | |
| 171 // owned by NetworkQualityEstimator. SocketWatcherFactory is thread safe. | |
| 172 class NetworkQualityEstimator::SocketWatcherFactory | |
| 173 : public SocketPerformanceWatcherFactory { | |
| 174 public: | |
| 175 SocketWatcherFactory( | |
| 176 scoped_refptr<base::SingleThreadTaskRunner> task_runner, | |
| 177 const base::WeakPtr<NetworkQualityEstimator>& network_quality_estimator) | |
| 178 : task_runner_(std::move(task_runner)), | |
| 179 network_quality_estimator_(network_quality_estimator) {} | |
| 180 | |
| 181 ~SocketWatcherFactory() override {} | |
| 182 | |
| 183 // SocketPerformanceWatcherFactory implementation: | |
| 184 scoped_ptr<SocketPerformanceWatcher> CreateSocketPerformanceWatcher( | |
| 185 const Protocol protocol) override { | |
| 186 return scoped_ptr<SocketPerformanceWatcher>( | |
| 187 new SocketWatcher(protocol, task_runner_, network_quality_estimator_)); | |
| 188 } | |
| 189 | |
| 190 private: | |
| 191 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | |
| 192 | |
| 193 base::WeakPtr<NetworkQualityEstimator> network_quality_estimator_; | |
| 194 | |
| 195 DISALLOW_COPY_AND_ASSIGN(SocketWatcherFactory); | |
| 196 }; | |
| 197 | |
| 198 const int32_t NetworkQualityEstimator::kInvalidThroughput = 0; | |
| 199 | |
| 200 NetworkQualityEstimator::NetworkQualityEstimator( | |
| 201 scoped_ptr<ExternalEstimateProvider> external_estimates_provider, | |
| 202 const std::map<std::string, std::string>& variation_params) | |
| 203 : NetworkQualityEstimator(std::move(external_estimates_provider), | |
| 204 variation_params, | |
| 205 false, | |
| 206 false) {} | |
| 207 | |
| 208 NetworkQualityEstimator::NetworkQualityEstimator( | |
| 209 scoped_ptr<ExternalEstimateProvider> external_estimates_provider, | |
| 210 const std::map<std::string, std::string>& variation_params, | |
| 211 bool allow_local_host_requests_for_tests, | |
| 212 bool allow_smaller_responses_for_tests) | |
| 213 : allow_localhost_requests_(allow_local_host_requests_for_tests), | |
| 214 allow_small_responses_(allow_smaller_responses_for_tests), | |
| 215 last_connection_change_(base::TimeTicks::Now()), | |
| 216 current_network_id_( | |
| 217 NetworkID(NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, | |
| 218 std::string())), | |
| 219 downstream_throughput_kbps_observations_( | |
| 220 GetWeightMultiplierPerSecond(variation_params)), | |
| 221 rtt_observations_(GetWeightMultiplierPerSecond(variation_params)), | |
| 222 external_estimate_provider_(std::move(external_estimates_provider)), | |
| 223 weak_ptr_factory_(this) { | |
| 224 static_assert(kMinRequestDurationMicroseconds > 0, | |
| 225 "Minimum request duration must be > 0"); | |
| 226 static_assert(kDefaultHalfLifeSeconds > 0, | |
| 227 "Default half life duration must be > 0"); | |
| 228 static_assert(kMaximumNetworkQualityCacheSize > 0, | |
| 229 "Size of the network quality cache must be > 0"); | |
| 230 // This limit should not be increased unless the logic for removing the | |
| 231 // oldest cache entry is rewritten to use a doubly-linked-list LRU queue. | |
| 232 static_assert(kMaximumNetworkQualityCacheSize <= 10, | |
| 233 "Size of the network quality cache must <= 10"); | |
| 234 | |
| 235 ObtainOperatingParams(variation_params); | |
| 236 NetworkChangeNotifier::AddConnectionTypeObserver(this); | |
| 237 if (external_estimate_provider_) { | |
| 238 RecordExternalEstimateProviderMetrics( | |
| 239 EXTERNAL_ESTIMATE_PROVIDER_STATUS_AVAILABLE); | |
| 240 external_estimate_provider_->SetUpdatedEstimateDelegate(this); | |
| 241 QueryExternalEstimateProvider(); | |
| 242 } else { | |
| 243 RecordExternalEstimateProviderMetrics( | |
| 244 EXTERNAL_ESTIMATE_PROVIDER_STATUS_NOT_AVAILABLE); | |
| 245 } | |
| 246 current_network_id_ = GetCurrentNetworkID(); | |
| 247 AddDefaultEstimates(); | |
| 248 | |
| 249 watcher_factory_.reset(new SocketWatcherFactory( | |
| 250 base::ThreadTaskRunnerHandle::Get(), weak_ptr_factory_.GetWeakPtr())); | |
| 251 } | |
| 252 | |
| 253 // static | |
| 254 const base::TimeDelta NetworkQualityEstimator::InvalidRTT() { | |
| 255 return base::TimeDelta::Max(); | |
| 256 } | |
| 257 | |
| 258 void NetworkQualityEstimator::ObtainOperatingParams( | |
| 259 const std::map<std::string, std::string>& variation_params) { | |
| 260 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 261 | |
| 262 for (size_t i = 0; i <= NetworkChangeNotifier::CONNECTION_LAST; ++i) { | |
| 263 NetworkChangeNotifier::ConnectionType type = | |
| 264 static_cast<NetworkChangeNotifier::ConnectionType>(i); | |
| 265 DCHECK_EQ(InvalidRTT(), default_observations_[i].rtt()); | |
| 266 DCHECK_EQ(kInvalidThroughput, | |
| 267 default_observations_[i].downstream_throughput_kbps()); | |
| 268 int32_t variations_value = kMinimumRTTVariationParameterMsec - 1; | |
| 269 // Name of the parameter that holds the RTT value for this connection type. | |
| 270 std::string rtt_parameter_name = | |
| 271 std::string(GetNameForConnectionType(type)) | |
| 272 .append(kDefaultRTTMsecObservationSuffix); | |
| 273 auto it = variation_params.find(rtt_parameter_name); | |
| 274 if (it != variation_params.end() && | |
| 275 base::StringToInt(it->second, &variations_value) && | |
| 276 variations_value >= kMinimumRTTVariationParameterMsec) { | |
| 277 default_observations_[i] = | |
| 278 NetworkQuality(base::TimeDelta::FromMilliseconds(variations_value), | |
| 279 default_observations_[i].downstream_throughput_kbps()); | |
| 280 } | |
| 281 | |
| 282 variations_value = kMinimumThroughputVariationParameterKbps - 1; | |
| 283 // Name of the parameter that holds the Kbps value for this connection | |
| 284 // type. | |
| 285 std::string kbps_parameter_name = | |
| 286 std::string(GetNameForConnectionType(type)) | |
| 287 .append(kDefaultKbpsObservationSuffix); | |
| 288 it = variation_params.find(kbps_parameter_name); | |
| 289 if (it != variation_params.end() && | |
| 290 base::StringToInt(it->second, &variations_value) && | |
| 291 variations_value >= kMinimumThroughputVariationParameterKbps) { | |
| 292 default_observations_[i] = | |
| 293 NetworkQuality(default_observations_[i].rtt(), variations_value); | |
| 294 } | |
| 295 } | |
| 296 } | |
| 297 | |
| 298 void NetworkQualityEstimator::AddDefaultEstimates() { | |
| 299 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 300 if (default_observations_[current_network_id_.type].rtt() != InvalidRTT()) { | |
| 301 RttObservation rtt_observation( | |
| 302 default_observations_[current_network_id_.type].rtt(), | |
| 303 base::TimeTicks::Now(), DEFAULT_FROM_PLATFORM); | |
| 304 rtt_observations_.AddObservation(rtt_observation); | |
| 305 NotifyObserversOfRTT(rtt_observation); | |
| 306 } | |
| 307 if (default_observations_[current_network_id_.type] | |
| 308 .downstream_throughput_kbps() != kInvalidThroughput) { | |
| 309 ThroughputObservation throughput_observation( | |
| 310 default_observations_[current_network_id_.type] | |
| 311 .downstream_throughput_kbps(), | |
| 312 base::TimeTicks::Now(), DEFAULT_FROM_PLATFORM); | |
| 313 downstream_throughput_kbps_observations_.AddObservation( | |
| 314 throughput_observation); | |
| 315 NotifyObserversOfThroughput(throughput_observation); | |
| 316 } | |
| 317 } | |
| 318 | |
| 319 NetworkQualityEstimator::~NetworkQualityEstimator() { | |
| 320 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 321 NetworkChangeNotifier::RemoveConnectionTypeObserver(this); | |
| 322 } | |
| 323 | |
| 324 void NetworkQualityEstimator::NotifyHeadersReceived(const URLRequest& request) { | |
| 325 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"), | |
| 326 "NetworkQualityEstimator::NotifyHeadersReceived"); | |
| 327 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 328 | |
| 329 if (!RequestProvidesUsefulObservations(request)) | |
| 330 return; | |
| 331 | |
| 332 // Update |estimated_median_network_quality_| if this is a main frame request. | |
| 333 if (request.load_flags() & LOAD_MAIN_FRAME) { | |
| 334 estimated_median_network_quality_ = NetworkQuality( | |
| 335 GetURLRequestRTTEstimateInternal(base::TimeTicks(), 50), | |
| 336 GetDownlinkThroughputKbpsEstimateInternal(base::TimeTicks(), 50)); | |
| 337 } | |
| 338 | |
| 339 base::TimeTicks now = base::TimeTicks::Now(); | |
| 340 LoadTimingInfo load_timing_info; | |
| 341 request.GetLoadTimingInfo(&load_timing_info); | |
| 342 | |
| 343 // If the load timing info is unavailable, it probably means that the request | |
| 344 // did not go over the network. | |
| 345 if (load_timing_info.send_start.is_null() || | |
| 346 load_timing_info.receive_headers_end.is_null()) { | |
| 347 return; | |
| 348 } | |
| 349 | |
| 350 // Time when the resource was requested. | |
| 351 base::TimeTicks request_start_time = load_timing_info.send_start; | |
| 352 | |
| 353 // Time when the headers were received. | |
| 354 base::TimeTicks headers_received_time = load_timing_info.receive_headers_end; | |
| 355 | |
| 356 // Duration between when the resource was requested and when response | |
| 357 // headers were received. | |
| 358 base::TimeDelta observed_rtt = headers_received_time - request_start_time; | |
| 359 DCHECK_GE(observed_rtt, base::TimeDelta()); | |
| 360 if (observed_rtt < peak_network_quality_.rtt()) { | |
| 361 peak_network_quality_ = NetworkQuality( | |
| 362 observed_rtt, peak_network_quality_.downstream_throughput_kbps()); | |
| 363 } | |
| 364 | |
| 365 RttObservation rtt_observation(observed_rtt, now, URL_REQUEST); | |
| 366 rtt_observations_.AddObservation(rtt_observation); | |
| 367 NotifyObserversOfRTT(rtt_observation); | |
| 368 | |
| 369 // Compare the RTT observation with the estimated value and record it. | |
| 370 if (estimated_median_network_quality_.rtt() != InvalidRTT()) { | |
| 371 RecordRTTUMA(estimated_median_network_quality_.rtt().InMilliseconds(), | |
| 372 observed_rtt.InMilliseconds()); | |
| 373 } | |
| 374 } | |
| 375 | |
| 376 void NetworkQualityEstimator::NotifyRequestCompleted( | |
| 377 const URLRequest& request) { | |
| 378 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"), | |
| 379 "NetworkQualityEstimator::NotifyRequestCompleted"); | |
| 380 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 381 | |
| 382 if (!RequestProvidesUsefulObservations(request)) | |
| 383 return; | |
| 384 | |
| 385 base::TimeTicks now = base::TimeTicks::Now(); | |
| 386 LoadTimingInfo load_timing_info; | |
| 387 request.GetLoadTimingInfo(&load_timing_info); | |
| 388 | |
| 389 // If the load timing info is unavailable, it probably means that the request | |
| 390 // did not go over the network. | |
| 391 if (load_timing_info.send_start.is_null() || | |
| 392 load_timing_info.receive_headers_end.is_null()) { | |
| 393 return; | |
| 394 } | |
| 395 | |
| 396 // Time since the resource was requested. | |
| 397 // TODO(tbansal): Change the start time to receive_headers_end, once we use | |
| 398 // NetworkActivityMonitor. | |
| 399 base::TimeDelta request_start_to_completed = | |
| 400 now - load_timing_info.send_start; | |
| 401 DCHECK_GE(request_start_to_completed, base::TimeDelta()); | |
| 402 | |
| 403 // Ignore tiny transfers which will not produce accurate rates. | |
| 404 // Ignore short duration transfers. | |
| 405 // Skip the checks if |allow_small_responses_| is true. | |
| 406 if (!allow_small_responses_ && | |
| 407 (request.GetTotalReceivedBytes() < kMinTransferSizeInBytes || | |
| 408 request_start_to_completed < base::TimeDelta::FromMicroseconds( | |
| 409 kMinRequestDurationMicroseconds))) { | |
| 410 return; | |
| 411 } | |
| 412 | |
| 413 double downstream_kbps = request.GetTotalReceivedBytes() * 8.0 / 1000.0 / | |
| 414 request_start_to_completed.InSecondsF(); | |
| 415 DCHECK_GE(downstream_kbps, 0.0); | |
| 416 | |
| 417 // Check overflow errors. This may happen if the downstream_kbps is more than | |
| 418 // 2 * 10^9 (= 2000 Gbps). | |
| 419 if (downstream_kbps >= std::numeric_limits<int32_t>::max()) | |
| 420 downstream_kbps = std::numeric_limits<int32_t>::max(); | |
| 421 | |
| 422 int32_t downstream_kbps_as_integer = static_cast<int32_t>(downstream_kbps); | |
| 423 | |
| 424 // Round up |downstream_kbps_as_integer|. If the |downstream_kbps_as_integer| | |
| 425 // is less than 1, it is set to 1 to differentiate from case when there is no | |
| 426 // connection. | |
| 427 if (downstream_kbps - downstream_kbps_as_integer > 0) | |
| 428 downstream_kbps_as_integer++; | |
| 429 | |
| 430 DCHECK_GT(downstream_kbps_as_integer, 0.0); | |
| 431 if (downstream_kbps_as_integer > | |
| 432 peak_network_quality_.downstream_throughput_kbps()) | |
| 433 peak_network_quality_ = | |
| 434 NetworkQuality(peak_network_quality_.rtt(), downstream_kbps_as_integer); | |
| 435 | |
| 436 ThroughputObservation throughput_observation(downstream_kbps_as_integer, now, | |
| 437 URL_REQUEST); | |
| 438 downstream_throughput_kbps_observations_.AddObservation( | |
| 439 throughput_observation); | |
| 440 NotifyObserversOfThroughput(throughput_observation); | |
| 441 } | |
| 442 | |
| 443 void NetworkQualityEstimator::AddRTTObserver(RTTObserver* rtt_observer) { | |
| 444 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 445 rtt_observer_list_.AddObserver(rtt_observer); | |
| 446 } | |
| 447 | |
| 448 void NetworkQualityEstimator::RemoveRTTObserver(RTTObserver* rtt_observer) { | |
| 449 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 450 rtt_observer_list_.RemoveObserver(rtt_observer); | |
| 451 } | |
| 452 | |
| 453 void NetworkQualityEstimator::AddThroughputObserver( | |
| 454 ThroughputObserver* throughput_observer) { | |
| 455 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 456 throughput_observer_list_.AddObserver(throughput_observer); | |
| 457 } | |
| 458 | |
| 459 void NetworkQualityEstimator::RemoveThroughputObserver( | |
| 460 ThroughputObserver* throughput_observer) { | |
| 461 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 462 throughput_observer_list_.RemoveObserver(throughput_observer); | |
| 463 } | |
| 464 | |
| 465 SocketPerformanceWatcherFactory* | |
| 466 NetworkQualityEstimator::GetSocketPerformanceWatcherFactory() { | |
| 467 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 468 | |
| 469 return watcher_factory_.get(); | |
| 470 } | |
| 471 | |
| 472 void NetworkQualityEstimator::RecordRTTUMA(int32_t estimated_value_msec, | |
| 473 int32_t actual_value_msec) const { | |
| 474 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 475 | |
| 476 // Record the difference between the actual and the estimated value. | |
| 477 if (estimated_value_msec >= actual_value_msec) { | |
| 478 base::HistogramBase* difference_rtt = | |
| 479 GetHistogram("DifferenceRTTEstimatedAndActual.", | |
| 480 current_network_id_.type, 10 * 1000); // 10 seconds | |
| 481 difference_rtt->Add(estimated_value_msec - actual_value_msec); | |
| 482 } else { | |
| 483 base::HistogramBase* difference_rtt = | |
| 484 GetHistogram("DifferenceRTTActualAndEstimated.", | |
| 485 current_network_id_.type, 10 * 1000); // 10 seconds | |
| 486 difference_rtt->Add(actual_value_msec - estimated_value_msec); | |
| 487 } | |
| 488 | |
| 489 // Record all the RTT observations. | |
| 490 base::HistogramBase* rtt_observations = | |
| 491 GetHistogram("RTTObservations.", current_network_id_.type, | |
| 492 10 * 1000); // 10 seconds upper bound | |
| 493 rtt_observations->Add(actual_value_msec); | |
| 494 | |
| 495 if (actual_value_msec == 0) | |
| 496 return; | |
| 497 | |
| 498 int32_t ratio = (estimated_value_msec * 100) / actual_value_msec; | |
| 499 | |
| 500 // Record the accuracy of estimation by recording the ratio of estimated | |
| 501 // value to the actual value. | |
| 502 base::HistogramBase* ratio_median_rtt = GetHistogram( | |
| 503 "RatioEstimatedToActualRTT.", current_network_id_.type, 1000); | |
| 504 ratio_median_rtt->Add(ratio); | |
| 505 } | |
| 506 | |
| 507 bool NetworkQualityEstimator::RequestProvidesUsefulObservations( | |
| 508 const URLRequest& request) const { | |
| 509 return request.url().is_valid() && | |
| 510 (allow_localhost_requests_ || !IsLocalhost(request.url().host())) && | |
| 511 request.url().SchemeIsHTTPOrHTTPS() && | |
| 512 // Verify that response headers are received, so it can be ensured that | |
| 513 // response is not cached. | |
| 514 !request.response_info().response_time.is_null() && | |
| 515 !request.was_cached() && | |
| 516 request.creation_time() >= last_connection_change_; | |
| 517 } | |
| 518 | |
| 519 void NetworkQualityEstimator::RecordExternalEstimateProviderMetrics( | |
| 520 NQEExternalEstimateProviderStatus status) const { | |
| 521 UMA_HISTOGRAM_ENUMERATION("NQE.ExternalEstimateProviderStatus", status, | |
| 522 EXTERNAL_ESTIMATE_PROVIDER_STATUS_BOUNDARY); | |
| 523 } | |
| 524 | |
| 525 void NetworkQualityEstimator::OnConnectionTypeChanged( | |
| 526 NetworkChangeNotifier::ConnectionType type) { | |
| 527 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 528 if (peak_network_quality_.rtt() != InvalidRTT()) { | |
| 529 switch (current_network_id_.type) { | |
| 530 case NetworkChangeNotifier::CONNECTION_UNKNOWN: | |
| 531 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Unknown", | |
| 532 peak_network_quality_.rtt()); | |
| 533 break; | |
| 534 case NetworkChangeNotifier::CONNECTION_ETHERNET: | |
| 535 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Ethernet", | |
| 536 peak_network_quality_.rtt()); | |
| 537 break; | |
| 538 case NetworkChangeNotifier::CONNECTION_WIFI: | |
| 539 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Wifi", peak_network_quality_.rtt()); | |
| 540 break; | |
| 541 case NetworkChangeNotifier::CONNECTION_2G: | |
| 542 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.2G", peak_network_quality_.rtt()); | |
| 543 break; | |
| 544 case NetworkChangeNotifier::CONNECTION_3G: | |
| 545 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.3G", peak_network_quality_.rtt()); | |
| 546 break; | |
| 547 case NetworkChangeNotifier::CONNECTION_4G: | |
| 548 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.4G", peak_network_quality_.rtt()); | |
| 549 break; | |
| 550 case NetworkChangeNotifier::CONNECTION_NONE: | |
| 551 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.None", peak_network_quality_.rtt()); | |
| 552 break; | |
| 553 case NetworkChangeNotifier::CONNECTION_BLUETOOTH: | |
| 554 UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Bluetooth", | |
| 555 peak_network_quality_.rtt()); | |
| 556 break; | |
| 557 default: | |
| 558 NOTREACHED() << "Unexpected connection type = " | |
| 559 << current_network_id_.type; | |
| 560 break; | |
| 561 } | |
| 562 } | |
| 563 | |
| 564 if (peak_network_quality_.downstream_throughput_kbps() != | |
| 565 kInvalidThroughput) { | |
| 566 switch (current_network_id_.type) { | |
| 567 case NetworkChangeNotifier::CONNECTION_UNKNOWN: | |
| 568 UMA_HISTOGRAM_COUNTS( | |
| 569 "NQE.PeakKbps.Unknown", | |
| 570 peak_network_quality_.downstream_throughput_kbps()); | |
| 571 break; | |
| 572 case NetworkChangeNotifier::CONNECTION_ETHERNET: | |
| 573 UMA_HISTOGRAM_COUNTS( | |
| 574 "NQE.PeakKbps.Ethernet", | |
| 575 peak_network_quality_.downstream_throughput_kbps()); | |
| 576 break; | |
| 577 case NetworkChangeNotifier::CONNECTION_WIFI: | |
| 578 UMA_HISTOGRAM_COUNTS( | |
| 579 "NQE.PeakKbps.Wifi", | |
| 580 peak_network_quality_.downstream_throughput_kbps()); | |
| 581 break; | |
| 582 case NetworkChangeNotifier::CONNECTION_2G: | |
| 583 UMA_HISTOGRAM_COUNTS( | |
| 584 "NQE.PeakKbps.2G", | |
| 585 peak_network_quality_.downstream_throughput_kbps()); | |
| 586 break; | |
| 587 case NetworkChangeNotifier::CONNECTION_3G: | |
| 588 UMA_HISTOGRAM_COUNTS( | |
| 589 "NQE.PeakKbps.3G", | |
| 590 peak_network_quality_.downstream_throughput_kbps()); | |
| 591 break; | |
| 592 case NetworkChangeNotifier::CONNECTION_4G: | |
| 593 UMA_HISTOGRAM_COUNTS( | |
| 594 "NQE.PeakKbps.4G", | |
| 595 peak_network_quality_.downstream_throughput_kbps()); | |
| 596 break; | |
| 597 case NetworkChangeNotifier::CONNECTION_NONE: | |
| 598 UMA_HISTOGRAM_COUNTS( | |
| 599 "NQE.PeakKbps.None", | |
| 600 peak_network_quality_.downstream_throughput_kbps()); | |
| 601 break; | |
| 602 case NetworkChangeNotifier::CONNECTION_BLUETOOTH: | |
| 603 UMA_HISTOGRAM_COUNTS( | |
| 604 "NQE.PeakKbps.Bluetooth", | |
| 605 peak_network_quality_.downstream_throughput_kbps()); | |
| 606 break; | |
| 607 default: | |
| 608 NOTREACHED() << "Unexpected connection type = " | |
| 609 << current_network_id_.type; | |
| 610 break; | |
| 611 } | |
| 612 } | |
| 613 | |
| 614 base::TimeDelta rtt = GetURLRequestRTTEstimateInternal(base::TimeTicks(), 50); | |
| 615 if (rtt != InvalidRTT()) { | |
| 616 // Add the 50th percentile value. | |
| 617 base::HistogramBase* rtt_percentile = | |
| 618 GetHistogram("RTT.Percentile50.", current_network_id_.type, | |
| 619 10 * 1000); // 10 seconds | |
| 620 rtt_percentile->Add(rtt.InMilliseconds()); | |
| 621 | |
| 622 // Add the remaining percentile values. | |
| 623 static const int kPercentiles[] = {0, 10, 90, 100}; | |
| 624 for (size_t i = 0; i < arraysize(kPercentiles); ++i) { | |
| 625 rtt = | |
| 626 GetURLRequestRTTEstimateInternal(base::TimeTicks(), kPercentiles[i]); | |
| 627 | |
| 628 rtt_percentile = GetHistogram( | |
| 629 "RTT.Percentile" + base::IntToString(kPercentiles[i]) + ".", | |
| 630 current_network_id_.type, 10 * 1000); // 10 seconds | |
| 631 rtt_percentile->Add(rtt.InMilliseconds()); | |
| 632 } | |
| 633 } | |
| 634 | |
| 635 // Write the estimates of the previous network to the cache. | |
| 636 CacheNetworkQualityEstimate(); | |
| 637 | |
| 638 // Clear the local state. | |
| 639 last_connection_change_ = base::TimeTicks::Now(); | |
| 640 peak_network_quality_ = NetworkQuality(); | |
| 641 downstream_throughput_kbps_observations_.Clear(); | |
| 642 rtt_observations_.Clear(); | |
| 643 current_network_id_ = GetCurrentNetworkID(); | |
| 644 | |
| 645 QueryExternalEstimateProvider(); | |
| 646 | |
| 647 // Read any cached estimates for the new network. If cached estimates are | |
| 648 // unavailable, add the default estimates. | |
| 649 if (!ReadCachedNetworkQualityEstimate()) | |
| 650 AddDefaultEstimates(); | |
| 651 estimated_median_network_quality_ = NetworkQuality(); | |
| 652 } | |
| 653 | |
| 654 bool NetworkQualityEstimator::GetURLRequestRTTEstimate( | |
| 655 base::TimeDelta* rtt) const { | |
| 656 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 657 DCHECK(rtt); | |
| 658 if (rtt_observations_.Size() == 0) { | |
| 659 *rtt = InvalidRTT(); | |
| 660 return false; | |
| 661 } | |
| 662 *rtt = GetURLRequestRTTEstimateInternal(base::TimeTicks(), 50); | |
| 663 return (*rtt != InvalidRTT()); | |
| 664 } | |
| 665 | |
| 666 bool NetworkQualityEstimator::GetDownlinkThroughputKbpsEstimate( | |
| 667 int32_t* kbps) const { | |
| 668 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 669 DCHECK(kbps); | |
| 670 if (downstream_throughput_kbps_observations_.Size() == 0) { | |
| 671 *kbps = kInvalidThroughput; | |
| 672 return false; | |
| 673 } | |
| 674 *kbps = GetDownlinkThroughputKbpsEstimateInternal(base::TimeTicks(), 50); | |
| 675 return (*kbps != kInvalidThroughput); | |
| 676 } | |
| 677 | |
| 678 bool NetworkQualityEstimator::GetRecentURLRequestRTTMedian( | |
| 679 const base::TimeTicks& begin_timestamp, | |
| 680 base::TimeDelta* rtt) const { | |
| 681 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 682 DCHECK(rtt); | |
| 683 *rtt = GetURLRequestRTTEstimateInternal(begin_timestamp, 50); | |
| 684 return (*rtt != InvalidRTT()); | |
| 685 } | |
| 686 | |
| 687 bool NetworkQualityEstimator::GetRecentMedianDownlinkThroughputKbps( | |
| 688 const base::TimeTicks& begin_timestamp, | |
| 689 int32_t* kbps) const { | |
| 690 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 691 DCHECK(kbps); | |
| 692 *kbps = GetDownlinkThroughputKbpsEstimateInternal(begin_timestamp, 50); | |
| 693 return (*kbps != kInvalidThroughput); | |
| 694 } | |
| 695 | |
| 696 template <typename ValueType> | |
| 697 NetworkQualityEstimator::ObservationBuffer<ValueType>::ObservationBuffer( | |
| 698 double weight_multiplier_per_second) | |
| 699 : weight_multiplier_per_second_(weight_multiplier_per_second) { | |
| 700 static_assert(kMaximumObservationsBufferSize > 0U, | |
| 701 "Minimum size of observation buffer must be > 0"); | |
| 702 DCHECK_GE(weight_multiplier_per_second_, 0.0); | |
| 703 DCHECK_LE(weight_multiplier_per_second_, 1.0); | |
| 704 } | |
| 705 | |
| 706 template <typename ValueType> | |
| 707 NetworkQualityEstimator::ObservationBuffer<ValueType>::~ObservationBuffer() {} | |
| 708 | |
| 709 base::TimeDelta NetworkQualityEstimator::GetURLRequestRTTEstimateInternal( | |
| 710 const base::TimeTicks& begin_timestamp, | |
| 711 int percentile) const { | |
| 712 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 713 DCHECK_GE(percentile, 0); | |
| 714 DCHECK_LE(percentile, 100); | |
| 715 if (rtt_observations_.Size() == 0) | |
| 716 return InvalidRTT(); | |
| 717 | |
| 718 // RTT observations are sorted by duration from shortest to longest, thus | |
| 719 // a higher percentile RTT will have a longer RTT than a lower percentile. | |
| 720 base::TimeDelta rtt = InvalidRTT(); | |
| 721 std::vector<ObservationSource> disallowed_observation_sources; | |
| 722 disallowed_observation_sources.push_back(TCP); | |
| 723 disallowed_observation_sources.push_back(QUIC); | |
| 724 rtt_observations_.GetPercentile(begin_timestamp, &rtt, percentile, | |
| 725 disallowed_observation_sources); | |
| 726 return rtt; | |
| 727 } | |
| 728 | |
| 729 int32_t NetworkQualityEstimator::GetDownlinkThroughputKbpsEstimateInternal( | |
| 730 const base::TimeTicks& begin_timestamp, | |
| 731 int percentile) const { | |
| 732 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 733 DCHECK_GE(percentile, 0); | |
| 734 DCHECK_LE(percentile, 100); | |
| 735 if (downstream_throughput_kbps_observations_.Size() == 0) | |
| 736 return kInvalidThroughput; | |
| 737 | |
| 738 // Throughput observations are sorted by kbps from slowest to fastest, | |
| 739 // thus a higher percentile throughput will be faster than a lower one. | |
| 740 int32_t kbps = kInvalidThroughput; | |
| 741 downstream_throughput_kbps_observations_.GetPercentile( | |
| 742 begin_timestamp, &kbps, 100 - percentile, | |
| 743 std::vector<ObservationSource>()); | |
| 744 return kbps; | |
| 745 } | |
| 746 | |
| 747 template <typename ValueType> | |
| 748 void NetworkQualityEstimator::ObservationBuffer<ValueType>:: | |
| 749 ComputeWeightedObservations( | |
| 750 const base::TimeTicks& begin_timestamp, | |
| 751 std::vector<WeightedObservation<ValueType>>& weighted_observations, | |
| 752 double* total_weight, | |
| 753 const std::vector<ObservationSource>& disallowed_observation_sources) | |
| 754 const { | |
| 755 weighted_observations.clear(); | |
| 756 double total_weight_observations = 0.0; | |
| 757 base::TimeTicks now = base::TimeTicks::Now(); | |
| 758 | |
| 759 for (const auto& observation : observations_) { | |
| 760 if (observation.timestamp < begin_timestamp) | |
| 761 continue; | |
| 762 bool disallowed = false; | |
| 763 for (const auto& disallowed_source : disallowed_observation_sources) { | |
| 764 if (disallowed_source == observation.source) | |
| 765 disallowed = true; | |
| 766 } | |
| 767 if (disallowed) | |
| 768 continue; | |
| 769 base::TimeDelta time_since_sample_taken = now - observation.timestamp; | |
| 770 double weight = | |
| 771 pow(weight_multiplier_per_second_, time_since_sample_taken.InSeconds()); | |
| 772 weight = std::max(DBL_MIN, std::min(1.0, weight)); | |
| 773 | |
| 774 weighted_observations.push_back( | |
| 775 WeightedObservation<ValueType>(observation.value, weight)); | |
| 776 total_weight_observations += weight; | |
| 777 } | |
| 778 | |
| 779 // Sort the samples by value in ascending order. | |
| 780 std::sort(weighted_observations.begin(), weighted_observations.end()); | |
| 781 *total_weight = total_weight_observations; | |
| 782 } | |
| 783 | |
| 784 template <typename ValueType> | |
| 785 bool NetworkQualityEstimator::ObservationBuffer<ValueType>::GetPercentile( | |
| 786 const base::TimeTicks& begin_timestamp, | |
| 787 ValueType* result, | |
| 788 int percentile, | |
| 789 const std::vector<ObservationSource>& disallowed_observation_sources) | |
| 790 const { | |
| 791 DCHECK(result); | |
| 792 // Stores WeightedObservation in increasing order of value. | |
| 793 std::vector<WeightedObservation<ValueType>> weighted_observations; | |
| 794 | |
| 795 // Total weight of all observations in |weighted_observations|. | |
| 796 double total_weight = 0.0; | |
| 797 | |
| 798 ComputeWeightedObservations(begin_timestamp, weighted_observations, | |
| 799 &total_weight, disallowed_observation_sources); | |
| 800 if (weighted_observations.empty()) | |
| 801 return false; | |
| 802 | |
| 803 DCHECK(!weighted_observations.empty()); | |
| 804 DCHECK_GT(total_weight, 0.0); | |
| 805 | |
| 806 // weighted_observations may have a smaller size than observations_ since the | |
| 807 // former contains only the observations later than begin_timestamp. | |
| 808 DCHECK_GE(observations_.size(), weighted_observations.size()); | |
| 809 | |
| 810 double desired_weight = percentile / 100.0 * total_weight; | |
| 811 | |
| 812 double cumulative_weight_seen_so_far = 0.0; | |
| 813 for (const auto& weighted_observation : weighted_observations) { | |
| 814 cumulative_weight_seen_so_far += weighted_observation.weight; | |
| 815 | |
| 816 if (cumulative_weight_seen_so_far >= desired_weight) { | |
| 817 *result = weighted_observation.value; | |
| 818 return true; | |
| 819 } | |
| 820 } | |
| 821 | |
| 822 // Computation may reach here due to floating point errors. This may happen | |
| 823 // if |percentile| was 100 (or close to 100), and |desired_weight| was | |
| 824 // slightly larger than |total_weight| (due to floating point errors). | |
| 825 // In this case, we return the highest |value| among all observations. | |
| 826 // This is same as value of the last observation in the sorted vector. | |
| 827 *result = weighted_observations.at(weighted_observations.size() - 1).value; | |
| 828 return true; | |
| 829 } | |
| 830 | |
| 831 NetworkQualityEstimator::NetworkID | |
| 832 NetworkQualityEstimator::GetCurrentNetworkID() const { | |
| 833 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 834 | |
| 835 // TODO(tbansal): crbug.com/498068 Add NetworkQualityEstimatorAndroid class | |
| 836 // that overrides this method on the Android platform. | |
| 837 | |
| 838 // It is possible that the connection type changed between when | |
| 839 // GetConnectionType() was called and when the API to determine the | |
| 840 // network name was called. Check if that happened and retry until the | |
| 841 // connection type stabilizes. This is an imperfect solution but should | |
| 842 // capture majority of cases, and should not significantly affect estimates | |
| 843 // (that are approximate to begin with). | |
| 844 while (true) { | |
| 845 NetworkQualityEstimator::NetworkID network_id( | |
| 846 NetworkChangeNotifier::GetConnectionType(), std::string()); | |
| 847 | |
| 848 switch (network_id.type) { | |
| 849 case NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN: | |
| 850 case NetworkChangeNotifier::ConnectionType::CONNECTION_NONE: | |
| 851 case NetworkChangeNotifier::ConnectionType::CONNECTION_BLUETOOTH: | |
| 852 case NetworkChangeNotifier::ConnectionType::CONNECTION_ETHERNET: | |
| 853 break; | |
| 854 case NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI: | |
| 855 #if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS) || \ | |
| 856 defined(OS_WIN) | |
| 857 network_id.id = GetWifiSSID(); | |
| 858 #endif | |
| 859 break; | |
| 860 case NetworkChangeNotifier::ConnectionType::CONNECTION_2G: | |
| 861 case NetworkChangeNotifier::ConnectionType::CONNECTION_3G: | |
| 862 case NetworkChangeNotifier::ConnectionType::CONNECTION_4G: | |
| 863 #if defined(OS_ANDROID) | |
| 864 network_id.id = android::GetTelephonyNetworkOperator(); | |
| 865 #endif | |
| 866 break; | |
| 867 default: | |
| 868 NOTREACHED() << "Unexpected connection type = " << network_id.type; | |
| 869 break; | |
| 870 } | |
| 871 | |
| 872 if (network_id.type == NetworkChangeNotifier::GetConnectionType()) | |
| 873 return network_id; | |
| 874 } | |
| 875 NOTREACHED(); | |
| 876 } | |
| 877 | |
| 878 bool NetworkQualityEstimator::ReadCachedNetworkQualityEstimate() { | |
| 879 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 880 | |
| 881 // If the network name is unavailable, caching should not be performed. | |
| 882 if (current_network_id_.id.empty()) | |
| 883 return false; | |
| 884 | |
| 885 CachedNetworkQualities::const_iterator it = | |
| 886 cached_network_qualities_.find(current_network_id_); | |
| 887 | |
| 888 if (it == cached_network_qualities_.end()) | |
| 889 return false; | |
| 890 | |
| 891 NetworkQuality network_quality(it->second.network_quality()); | |
| 892 | |
| 893 DCHECK_NE(InvalidRTT(), network_quality.rtt()); | |
| 894 DCHECK_NE(kInvalidThroughput, network_quality.downstream_throughput_kbps()); | |
| 895 | |
| 896 ThroughputObservation througphput_observation( | |
| 897 network_quality.downstream_throughput_kbps(), base::TimeTicks::Now(), | |
| 898 CACHED_ESTIMATE); | |
| 899 downstream_throughput_kbps_observations_.AddObservation( | |
| 900 througphput_observation); | |
| 901 NotifyObserversOfThroughput(througphput_observation); | |
| 902 | |
| 903 RttObservation rtt_observation(network_quality.rtt(), base::TimeTicks::Now(), | |
| 904 CACHED_ESTIMATE); | |
| 905 rtt_observations_.AddObservation(rtt_observation); | |
| 906 NotifyObserversOfRTT(rtt_observation); | |
| 907 | |
| 908 return true; | |
| 909 } | |
| 910 | |
| 911 void NetworkQualityEstimator::OnUpdatedEstimateAvailable() { | |
| 912 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 913 DCHECK(external_estimate_provider_); | |
| 914 | |
| 915 RecordExternalEstimateProviderMetrics( | |
| 916 EXTERNAL_ESTIMATE_PROVIDER_STATUS_CALLBACK); | |
| 917 QueryExternalEstimateProvider(); | |
| 918 } | |
| 919 | |
| 920 void NetworkQualityEstimator::QueryExternalEstimateProvider() { | |
| 921 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 922 | |
| 923 if (!external_estimate_provider_) | |
| 924 return; | |
| 925 RecordExternalEstimateProviderMetrics( | |
| 926 EXTERNAL_ESTIMATE_PROVIDER_STATUS_QUERIED); | |
| 927 | |
| 928 base::TimeDelta time_since_last_update; | |
| 929 | |
| 930 // Request a new estimate if estimate is not available, or if the available | |
| 931 // estimate is not fresh. | |
| 932 if (!external_estimate_provider_->GetTimeSinceLastUpdate( | |
| 933 &time_since_last_update) || | |
| 934 time_since_last_update > | |
| 935 base::TimeDelta::FromMilliseconds( | |
| 936 kExternalEstimateProviderFreshnessDurationMsec)) { | |
| 937 // Request the external estimate provider for updated estimates. When the | |
| 938 // updates estimates are available, OnUpdatedEstimateAvailable() will be | |
| 939 // called. | |
| 940 external_estimate_provider_->Update(); | |
| 941 return; | |
| 942 } | |
| 943 | |
| 944 RecordExternalEstimateProviderMetrics( | |
| 945 EXTERNAL_ESTIMATE_PROVIDER_STATUS_QUERY_SUCCESSFUL); | |
| 946 base::TimeDelta rtt; | |
| 947 if (external_estimate_provider_->GetRTT(&rtt)) { | |
| 948 rtt_observations_.AddObservation( | |
| 949 RttObservation(rtt, base::TimeTicks::Now(), EXTERNAL_ESTIMATE)); | |
| 950 } | |
| 951 | |
| 952 int32_t downstream_throughput_kbps; | |
| 953 if (external_estimate_provider_->GetDownstreamThroughputKbps( | |
| 954 &downstream_throughput_kbps)) { | |
| 955 downstream_throughput_kbps_observations_.AddObservation( | |
| 956 ThroughputObservation(downstream_throughput_kbps, | |
| 957 base::TimeTicks::Now(), EXTERNAL_ESTIMATE)); | |
| 958 } | |
| 959 } | |
| 960 | |
| 961 void NetworkQualityEstimator::CacheNetworkQualityEstimate() { | |
| 962 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 963 DCHECK_LE(cached_network_qualities_.size(), | |
| 964 static_cast<size_t>(kMaximumNetworkQualityCacheSize)); | |
| 965 | |
| 966 // If the network name is unavailable, caching should not be performed. | |
| 967 if (current_network_id_.id.empty()) | |
| 968 return; | |
| 969 | |
| 970 NetworkQuality network_quality = NetworkQuality( | |
| 971 GetURLRequestRTTEstimateInternal(base::TimeTicks(), 50), | |
| 972 GetDownlinkThroughputKbpsEstimateInternal(base::TimeTicks(), 50)); | |
| 973 if (network_quality.rtt() == InvalidRTT() || | |
| 974 network_quality.downstream_throughput_kbps() == kInvalidThroughput) { | |
| 975 return; | |
| 976 } | |
| 977 | |
| 978 if (cached_network_qualities_.size() == kMaximumNetworkQualityCacheSize) { | |
| 979 // Remove the oldest entry. | |
| 980 CachedNetworkQualities::iterator oldest_entry_iterator = | |
| 981 cached_network_qualities_.begin(); | |
| 982 | |
| 983 for (CachedNetworkQualities::iterator it = | |
| 984 cached_network_qualities_.begin(); | |
| 985 it != cached_network_qualities_.end(); ++it) { | |
| 986 if ((it->second).OlderThan(oldest_entry_iterator->second)) | |
| 987 oldest_entry_iterator = it; | |
| 988 } | |
| 989 cached_network_qualities_.erase(oldest_entry_iterator); | |
| 990 } | |
| 991 DCHECK_LT(cached_network_qualities_.size(), | |
| 992 static_cast<size_t>(kMaximumNetworkQualityCacheSize)); | |
| 993 | |
| 994 cached_network_qualities_.insert(std::make_pair( | |
| 995 current_network_id_, CachedNetworkQuality(network_quality))); | |
| 996 DCHECK_LE(cached_network_qualities_.size(), | |
| 997 static_cast<size_t>(kMaximumNetworkQualityCacheSize)); | |
| 998 } | |
| 999 | |
| 1000 void NetworkQualityEstimator::OnUpdatedRTTAvailable( | |
| 1001 SocketPerformanceWatcherFactory::Protocol protocol, | |
| 1002 const base::TimeDelta& rtt) { | |
| 1003 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1004 | |
| 1005 switch (protocol) { | |
| 1006 case SocketPerformanceWatcherFactory::PROTOCOL_TCP: | |
| 1007 NotifyObserversOfRTT(RttObservation(rtt, base::TimeTicks::Now(), TCP)); | |
| 1008 return; | |
| 1009 case SocketPerformanceWatcherFactory::PROTOCOL_QUIC: | |
| 1010 NotifyObserversOfRTT(RttObservation(rtt, base::TimeTicks::Now(), QUIC)); | |
| 1011 return; | |
| 1012 default: | |
| 1013 NOTREACHED(); | |
| 1014 } | |
| 1015 } | |
| 1016 | |
| 1017 void NetworkQualityEstimator::NotifyObserversOfRTT( | |
| 1018 const RttObservation& observation) { | |
| 1019 FOR_EACH_OBSERVER( | |
| 1020 RTTObserver, rtt_observer_list_, | |
| 1021 OnRTTObservation(observation.value.InMilliseconds(), | |
| 1022 observation.timestamp, observation.source)); | |
| 1023 } | |
| 1024 | |
| 1025 void NetworkQualityEstimator::NotifyObserversOfThroughput( | |
| 1026 const ThroughputObservation& observation) { | |
| 1027 FOR_EACH_OBSERVER( | |
| 1028 ThroughputObserver, throughput_observer_list_, | |
| 1029 OnThroughputObservation(observation.value, observation.timestamp, | |
| 1030 observation.source)); | |
| 1031 } | |
| 1032 | |
| 1033 NetworkQualityEstimator::CachedNetworkQuality::CachedNetworkQuality( | |
| 1034 const NetworkQuality& network_quality) | |
| 1035 : last_update_time_(base::TimeTicks::Now()), | |
| 1036 network_quality_(network_quality) { | |
| 1037 } | |
| 1038 | |
| 1039 NetworkQualityEstimator::CachedNetworkQuality::CachedNetworkQuality( | |
| 1040 const CachedNetworkQuality& other) | |
| 1041 : last_update_time_(other.last_update_time_), | |
| 1042 network_quality_(other.network_quality_) { | |
| 1043 } | |
| 1044 | |
| 1045 NetworkQualityEstimator::CachedNetworkQuality::~CachedNetworkQuality() { | |
| 1046 } | |
| 1047 | |
| 1048 bool NetworkQualityEstimator::CachedNetworkQuality::OlderThan( | |
| 1049 const CachedNetworkQuality& cached_network_quality) const { | |
| 1050 return last_update_time_ < cached_network_quality.last_update_time_; | |
| 1051 } | |
| 1052 | |
| 1053 NetworkQualityEstimator::NetworkQuality::NetworkQuality() | |
| 1054 : NetworkQuality(NetworkQualityEstimator::InvalidRTT(), | |
| 1055 NetworkQualityEstimator::kInvalidThroughput) {} | |
| 1056 | |
| 1057 NetworkQualityEstimator::NetworkQuality::NetworkQuality( | |
| 1058 const base::TimeDelta& rtt, | |
| 1059 int32_t downstream_throughput_kbps) | |
| 1060 : rtt_(rtt), downstream_throughput_kbps_(downstream_throughput_kbps) { | |
| 1061 DCHECK_GE(rtt_, base::TimeDelta()); | |
| 1062 DCHECK_GE(downstream_throughput_kbps_, 0); | |
| 1063 } | |
| 1064 | |
| 1065 NetworkQualityEstimator::NetworkQuality::NetworkQuality( | |
| 1066 const NetworkQuality& other) | |
| 1067 : NetworkQuality(other.rtt_, other.downstream_throughput_kbps_) {} | |
| 1068 | |
| 1069 NetworkQualityEstimator::NetworkQuality::~NetworkQuality() {} | |
| 1070 | |
| 1071 NetworkQualityEstimator::NetworkQuality& | |
| 1072 NetworkQualityEstimator::NetworkQuality:: | |
| 1073 operator=(const NetworkQuality& other) { | |
| 1074 rtt_ = other.rtt_; | |
| 1075 downstream_throughput_kbps_ = other.downstream_throughput_kbps_; | |
| 1076 return *this; | |
| 1077 } | |
| 1078 | |
| 1079 } // namespace net | |
| OLD | NEW |