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.
5 #ifndef NET_BASE_NETWORK_QUALITY_ESTIMATOR_H_
6 #define NET_BASE_NETWORK_QUALITY_ESTIMATOR_H_
14 #include "base/gtest_prod_util.h"
15 #include "base/macros.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/threading/thread_checker.h"
18 #include "base/time/time.h"
19 #include "net/base/external_estimate_provider.h"
20 #include "net/base/net_export.h"
21 #include "net/base/network_change_notifier.h"
22 #include "net/base/socket_performance_watcher.h"
23 #include "net/base/socket_performance_watcher_factory.h"
29 // NetworkQualityEstimator provides network quality estimates (quality of the
30 // full paths to all origins that have been connected to).
31 // The estimates are based on the observed organic traffic.
32 // A NetworkQualityEstimator instance is attached to URLRequestContexts and
33 // observes the traffic of URLRequests spawned from the URLRequestContexts.
34 // A single instance of NQE can be attached to multiple URLRequestContexts,
35 // thereby increasing the single NQE instance's accuracy by providing more
36 // observed traffic characteristics.
37 class NET_EXPORT_PRIVATE NetworkQualityEstimator
38 : public NetworkChangeNotifier::ConnectionTypeObserver
,
39 public ExternalEstimateProvider::UpdatedEstimateDelegate
,
40 public SocketPerformanceWatcherFactory
{
42 // Creates a new NetworkQualityEstimator.
43 // |variation_params| is the map containing all field trial parameters
44 // related to NetworkQualityEstimator field trial.
45 // |external_estimates_provider| may be NULL.
46 NetworkQualityEstimator(
47 scoped_ptr
<ExternalEstimateProvider
> external_estimates_provider
,
48 const std::map
<std::string
, std::string
>& variation_params
);
50 ~NetworkQualityEstimator() override
;
52 // Returns true if RTT is available and sets |rtt| to estimated RTT.
53 // Virtualized for testing. |rtt| should not be null.
54 virtual bool GetRTTEstimate(base::TimeDelta
* rtt
) const;
56 // Returns true if downlink throughput is available and sets |kbps| to
57 // estimated downlink throughput (in Kilobits per second).
58 // Virtualized for testing. |kbps| should not be null.
59 virtual bool GetDownlinkThroughputKbpsEstimate(int32_t* kbps
) const;
61 // Notifies NetworkQualityEstimator that the response header of |request| has
63 void NotifyHeadersReceived(const URLRequest
& request
);
65 // Notifies NetworkQualityEstimator that the response body of |request| has
67 void NotifyRequestCompleted(const URLRequest
& request
);
69 // Returns true if median RTT is available and sets |rtt| to the median of
70 // RTT observations since |begin_timestamp|.
71 // Virtualized for testing. |rtt| should not be null.
72 virtual bool GetRecentMedianRTT(const base::TimeTicks
& begin_timestamp
,
73 base::TimeDelta
* rtt
) const;
75 // Returns true if median downstream throughput is available and sets |kbps|
76 // to the median of downstream Kbps observations since |begin_timestamp|.
77 // Virtualized for testing. |kbps| should not be null.
78 virtual bool GetRecentMedianDownlinkThroughputKbps(
79 const base::TimeTicks
& begin_timestamp
,
82 // SocketPerformanceWatcherFactory implementation:
83 scoped_ptr
<SocketPerformanceWatcher
> CreateTCPSocketPerformanceWatcher()
85 scoped_ptr
<SocketPerformanceWatcher
> CreateUDPSocketPerformanceWatcher()
89 // NetworkID is used to uniquely identify a network.
90 // For the purpose of network quality estimation and caching, a network is
91 // uniquely identified by a combination of |type| and
92 // |id|. This approach is unable to distinguish networks with
93 // same name (e.g., different Wi-Fi networks with same SSID).
94 // This is a protected member to expose it to tests.
95 struct NET_EXPORT_PRIVATE NetworkID
{
96 NetworkID(NetworkChangeNotifier::ConnectionType type
, const std::string
& id
)
97 : type(type
), id(id
) {}
98 NetworkID(const NetworkID
& other
) : type(other
.type
), id(other
.id
) {}
101 NetworkID
& operator=(const NetworkID
& other
) {
107 // Overloaded because NetworkID is used as key in a map.
108 bool operator<(const NetworkID
& other
) const {
109 return type
< other
.type
|| (type
== other
.type
&& id
< other
.id
);
112 // Connection type of the network.
113 NetworkChangeNotifier::ConnectionType type
;
115 // Name of this network. This is set to:
116 // - Wi-Fi SSID if the device is connected to a Wi-Fi access point and the
117 // SSID name is available, or
118 // - MCC/MNC code of the cellular carrier if the device is connected to a
119 // cellular network, or
120 // - "Ethernet" in case the device is connected to ethernet.
121 // - An empty string in all other cases or if the network name is not
122 // exposed by platform APIs.
126 // Construct a NetworkQualityEstimator instance allowing for test
127 // configuration. Registers for network type change notifications so estimates
128 // can be kept network specific.
129 // |external_estimates_provider| may be NULL.
130 // |variation_params| is the map containing all field trial parameters for the
131 // network quality estimator field trial.
132 // |allow_local_host_requests_for_tests| should only be true when testing
133 // against local HTTP server and allows the requests to local host to be
134 // used for network quality estimation.
135 // |allow_smaller_responses_for_tests| should only be true when testing.
136 // Allows the responses smaller than |kMinTransferSizeInBytes| or shorter than
137 // |kMinRequestDurationMicroseconds| to be used for network quality
139 NetworkQualityEstimator(
140 scoped_ptr
<ExternalEstimateProvider
> external_estimates_provider
,
141 const std::map
<std::string
, std::string
>& variation_params
,
142 bool allow_local_host_requests_for_tests
,
143 bool allow_smaller_responses_for_tests
);
145 // Returns true if the cached network quality estimate was successfully read.
146 bool ReadCachedNetworkQualityEstimate();
148 // NetworkChangeNotifier::ConnectionTypeObserver implementation:
149 void OnConnectionTypeChanged(
150 NetworkChangeNotifier::ConnectionType type
) override
;
153 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest
, StoreObservations
);
154 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest
, TestKbpsRTTUpdates
);
155 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest
, TestAddObservation
);
156 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest
, ObtainOperatingParams
);
157 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest
, HalfLifeParam
);
158 FRIEND_TEST_ALL_PREFIXES(URLRequestTestHTTP
, NetworkQualityEstimator
);
159 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest
,
160 PercentileSameTimestamps
);
161 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest
,
162 PercentileDifferentTimestamps
);
163 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest
, ComputedPercentiles
);
164 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest
, TestCaching
);
165 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest
,
166 TestLRUCacheMaximumSize
);
167 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest
, TestGetMedianRTTSince
);
169 // NetworkQuality is used to cache the quality of a network connection.
170 class NET_EXPORT_PRIVATE NetworkQuality
{
173 // |rtt| is the estimate of the round trip time.
174 // |downstream_throughput_kbps| is the estimate of the downstream
176 NetworkQuality(const base::TimeDelta
& rtt
,
177 int32_t downstream_throughput_kbps
);
178 NetworkQuality(const NetworkQuality
& other
);
181 NetworkQuality
& operator=(const NetworkQuality
& other
);
183 // Returns the estimate of the round trip time.
184 const base::TimeDelta
& rtt() const { return rtt_
; }
186 // Returns the estimate of the downstream throughput in Kbps (Kilo bits per
188 int32_t downstream_throughput_kbps() const {
189 return downstream_throughput_kbps_
;
193 // Estimated round trip time.
194 base::TimeDelta rtt_
;
196 // Estimated downstream throughput in Kbps.
197 int32_t downstream_throughput_kbps_
;
200 // CachedNetworkQuality stores the quality of a previously seen network.
201 class NET_EXPORT_PRIVATE CachedNetworkQuality
{
203 explicit CachedNetworkQuality(const NetworkQuality
& network_quality
);
204 CachedNetworkQuality(const CachedNetworkQuality
& other
);
205 ~CachedNetworkQuality();
207 // Returns the network quality associated with this cached entry.
208 const NetworkQuality
& network_quality() const { return network_quality_
; }
210 // Returns true if this cache entry was updated before
211 // |cached_network_quality|.
212 bool OlderThan(const CachedNetworkQuality
& cached_network_quality
) const;
214 // Time when this cache entry was last updated.
215 const base::TimeTicks last_update_time_
;
217 // Quality of this cached network.
218 const NetworkQuality network_quality_
;
221 DISALLOW_ASSIGN(CachedNetworkQuality
);
224 // Records the round trip time or throughput observation, along with the time
225 // the observation was made.
226 struct NET_EXPORT_PRIVATE Observation
{
227 Observation(int32_t value
, base::TimeTicks timestamp
);
230 // Value of the observation.
233 // Time when the observation was taken.
234 const base::TimeTicks timestamp
;
237 // Holds an observation and its weight.
238 struct NET_EXPORT_PRIVATE WeightedObservation
{
239 WeightedObservation(int32_t value
, double weight
)
240 : value(value
), weight(weight
) {}
241 WeightedObservation(const WeightedObservation
& other
)
242 : WeightedObservation(other
.value
, other
.weight
) {}
244 WeightedObservation
& operator=(const WeightedObservation
& other
) {
246 weight
= other
.weight
;
250 // Required for sorting the samples in the ascending order of values.
251 bool operator<(const WeightedObservation
& other
) const {
252 return (value
< other
.value
);
255 // Value of the sample.
258 // Weight of the sample. This is computed based on how much time has passed
259 // since the sample was taken.
263 // Stores observations sorted by time.
264 class NET_EXPORT_PRIVATE ObservationBuffer
{
266 explicit ObservationBuffer(double weight_multiplier_per_second
);
267 ~ObservationBuffer();
269 // Adds |observation| to the buffer. The oldest observation in the buffer
270 // will be evicted to make room if the buffer is already full.
271 void AddObservation(const Observation
& observation
);
273 // Returns the number of observations in this buffer.
276 // Clears the observations stored in this buffer.
279 // Returns true iff the |percentile| value of the observations in this
280 // buffer is available. Sets |result| to the computed |percentile|
281 // value among all observations since |begin_timestamp|. If the value is
282 // unavailable, false is returned and |result| is not modified. Percentile
283 // value is unavailable if all the values in observation buffer are older
284 // than |begin_timestamp|.
285 // |result| must not be null.
286 bool GetPercentile(const base::TimeTicks
& begin_timestamp
,
288 int percentile
) const;
291 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest
, StoreObservations
);
292 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest
,
293 ObtainOperatingParams
);
294 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest
, HalfLifeParam
);
296 // Computes the weighted observations and stores them in
297 // |weighted_observations| sorted by ascending |WeightedObservation.value|.
298 // Only the observations with timestamp later than |begin_timestamp| are
299 // considered. Also, sets |total_weight| to the total weight of all
300 // observations. Should be called only when there is at least one
301 // observation in the buffer.
302 void ComputeWeightedObservations(
303 const base::TimeTicks
& begin_timestamp
,
304 std::vector
<WeightedObservation
>& weighted_observations
,
305 double* total_weight
) const;
307 // Holds observations sorted by time, with the oldest observation at the
308 // front of the queue.
309 std::deque
<Observation
> observations_
;
311 // The factor by which the weight of an observation reduces every second.
312 // For example, if an observation is 6 seconds old, its weight would be:
313 // weight_multiplier_per_second_ ^ 6
314 // Calculated from |kHalfLifeSeconds| by solving the following equation:
315 // weight_multiplier_per_second_ ^ kHalfLifeSeconds = 0.5
316 const double weight_multiplier_per_second_
;
318 DISALLOW_COPY_AND_ASSIGN(ObservationBuffer
);
321 // This does not use a unordered_map or hash_map for code simplicity (key just
322 // implements operator<, rather than hash and equality) and because the map is
324 typedef std::map
<NetworkID
, CachedNetworkQuality
> CachedNetworkQualities
;
326 // Throughput is set to |kInvalidThroughput| if a valid value is
327 // unavailable. Readers should discard throughput value if it is set to
328 // |kInvalidThroughput|.
329 static const int32_t kInvalidThroughput
;
331 // Tiny transfer sizes may give inaccurate throughput results.
332 // Minimum size of the transfer over which the throughput is computed.
333 static const int kMinTransferSizeInBytes
= 10000;
335 // Minimum duration (in microseconds) of the transfer over which the
336 // throughput is computed.
337 static const int kMinRequestDurationMicroseconds
= 1000;
339 // Minimum valid value of the variation parameter that holds RTT (in
340 // milliseconds) values.
341 static const int kMinimumRTTVariationParameterMsec
= 1;
343 // Minimum valid value of the variation parameter that holds throughput (in
345 static const int kMinimumThroughputVariationParameterKbps
= 1;
347 // Maximum size of the cache that holds network quality estimates.
348 // Smaller size may reduce the cache hit rate due to frequent evictions.
349 // Larger size may affect performance.
350 static const size_t kMaximumNetworkQualityCacheSize
= 10;
352 // Maximum number of observations that can be held in the ObservationBuffer.
353 static const size_t kMaximumObservationsBufferSize
= 300;
355 // Returns the RTT value to be used when the valid RTT is unavailable. Readers
356 // should discard RTT if it is set to the value returned by |InvalidRTT()|.
357 static const base::TimeDelta
InvalidRTT();
359 // ExternalEstimateProvider::UpdatedEstimateObserver implementation:
360 void OnUpdatedEstimateAvailable() override
;
362 // Obtains operating parameters from the field trial parameters.
363 void ObtainOperatingParams(
364 const std::map
<std::string
, std::string
>& variation_params
);
366 // Adds the default median RTT and downstream throughput estimate for the
367 // current connection type to the observation buffer.
368 void AddDefaultEstimates();
370 // Returns an estimate of network quality at the specified |percentile|.
371 // Only the observations later than |begin_timestamp| are taken into account.
372 // |percentile| must be between 0 and 100 (both inclusive) with higher
373 // percentiles indicating less performant networks. For example, if
374 // |percentile| is 90, then the network is expected to be faster than the
375 // returned estimate with 0.9 probability. Similarly, network is expected to
376 // be slower than the returned estimate with 0.1 probability.
377 base::TimeDelta
GetRTTEstimateInternal(const base::TimeTicks
& begin_timestamp
,
378 int percentile
) const;
379 int32_t GetDownlinkThroughputKbpsEstimateInternal(
380 const base::TimeTicks
& begin_timestamp
,
381 int percentile
) const;
383 // Returns the current network ID checking by calling the platform APIs.
384 // Virtualized for testing.
385 virtual NetworkID
GetCurrentNetworkID() const;
387 // Writes the estimated quality of the current network to the cache.
388 void CacheNetworkQualityEstimate();
390 // Records the UMA related to RTT.
391 void RecordRTTUMA(int32_t estimated_value_msec
,
392 int32_t actual_value_msec
) const;
394 // Returns true only if |request| can be used for network quality estimation.
395 // Only the requests that go over network are considered to provide useful
397 bool RequestProvidesUsefulObservations(const URLRequest
& request
) const;
399 // Determines if the requests to local host can be used in estimating the
400 // network quality. Set to true only for tests.
401 const bool allow_localhost_requests_
;
403 // Determines if the responses smaller than |kMinTransferSizeInBytes|
404 // or shorter than |kMinTransferSizeInBytes| can be used in estimating the
405 // network quality. Set to true only for tests.
406 const bool allow_small_responses_
;
408 // Time when last connection change was observed.
409 base::TimeTicks last_connection_change_
;
411 // ID of the current network.
412 NetworkID current_network_id_
;
414 // Peak network quality (fastest round-trip-time (RTT) and highest
415 // downstream throughput) measured since last connectivity change. RTT is
416 // measured from time the request is sent until the first byte received.
417 // The accuracy is decreased by ignoring these factors:
418 // 1) Multiple URLRequests can occur concurrently.
419 // 2) Includes server processing time.
420 NetworkQuality peak_network_quality_
;
422 // Cache that stores quality of previously seen networks.
423 CachedNetworkQualities cached_network_qualities_
;
425 // Buffer that holds Kbps observations sorted by timestamp.
426 ObservationBuffer downstream_throughput_kbps_observations_
;
428 // Buffer that holds RTT (in milliseconds) observations sorted by timestamp.
429 ObservationBuffer rtt_msec_observations_
;
431 // Default network quality observations obtained from the network quality
432 // estimator field trial parameters. The observations are indexed by
435 default_observations_
[NetworkChangeNotifier::CONNECTION_LAST
+ 1];
437 // Estimated network quality. Updated on mainframe requests.
438 NetworkQuality estimated_median_network_quality_
;
440 // ExternalEstimateProvider that provides network quality using operating
441 // system APIs. May be NULL.
442 const scoped_ptr
<ExternalEstimateProvider
> external_estimates_provider_
;
444 base::ThreadChecker thread_checker_
;
446 DISALLOW_COPY_AND_ASSIGN(NetworkQualityEstimator
);
451 #endif // NET_BASE_NETWORK_QUALITY_ESTIMATOR_H_