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