| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #ifndef NET_NQE_NETWORK_QUALITY_ESTIMATOR_H_ | 5 #ifndef NET_NQE_NETWORK_QUALITY_ESTIMATOR_H_ |
| 6 #define NET_NQE_NETWORK_QUALITY_ESTIMATOR_H_ | 6 #define NET_NQE_NETWORK_QUALITY_ESTIMATOR_H_ |
| 7 | 7 |
| 8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 | 9 |
| 10 #include <map> | 10 #include <map> |
| 11 #include <memory> | 11 #include <memory> |
| 12 #include <string> | 12 #include <string> |
| 13 #include <tuple> | |
| 14 | 13 |
| 15 #include "base/compiler_specific.h" | 14 #include "base/compiler_specific.h" |
| 16 #include "base/gtest_prod_util.h" | 15 #include "base/gtest_prod_util.h" |
| 17 #include "base/macros.h" | 16 #include "base/macros.h" |
| 18 #include "base/memory/ref_counted.h" | 17 #include "base/memory/ref_counted.h" |
| 19 #include "base/memory/weak_ptr.h" | 18 #include "base/memory/weak_ptr.h" |
| 20 #include "base/observer_list.h" | 19 #include "base/observer_list.h" |
| 21 #include "base/threading/thread_checker.h" | 20 #include "base/threading/thread_checker.h" |
| 22 #include "base/time/time.h" | 21 #include "base/time/time.h" |
| 23 #include "net/base/net_export.h" | 22 #include "net/base/net_export.h" |
| 24 #include "net/base/network_change_notifier.h" | 23 #include "net/base/network_change_notifier.h" |
| 25 #include "net/nqe/cached_network_quality.h" | 24 #include "net/nqe/cached_network_quality.h" |
| 26 #include "net/nqe/external_estimate_provider.h" | 25 #include "net/nqe/external_estimate_provider.h" |
| 26 #include "net/nqe/network_id.h" |
| 27 #include "net/nqe/network_quality.h" | 27 #include "net/nqe/network_quality.h" |
| 28 #include "net/nqe/network_quality_observation.h" | 28 #include "net/nqe/network_quality_observation.h" |
| 29 #include "net/nqe/network_quality_observation_source.h" | 29 #include "net/nqe/network_quality_observation_source.h" |
| 30 #include "net/nqe/network_quality_store.h" |
| 30 #include "net/nqe/observation_buffer.h" | 31 #include "net/nqe/observation_buffer.h" |
| 31 #include "net/socket/socket_performance_watcher_factory.h" | 32 #include "net/socket/socket_performance_watcher_factory.h" |
| 32 | 33 |
| 33 namespace base { | 34 namespace base { |
| 34 class TickClock; | 35 class TickClock; |
| 35 } // namespace base | 36 } // namespace base |
| 36 | 37 |
| 37 namespace net { | 38 namespace net { |
| 38 | 39 |
| 39 namespace nqe { | 40 namespace nqe { |
| (...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 236 // Allows the responses smaller than |kMinTransferSizeInBits| to be used for | 237 // Allows the responses smaller than |kMinTransferSizeInBits| to be used for |
| 237 // network quality estimation. | 238 // network quality estimation. |
| 238 void SetUseSmallResponsesForTesting(bool use_small_responses); | 239 void SetUseSmallResponsesForTesting(bool use_small_responses); |
| 239 | 240 |
| 240 // Reports |effective_connection_type| to all | 241 // Reports |effective_connection_type| to all |
| 241 // EffectiveConnectionTypeObservers. | 242 // EffectiveConnectionTypeObservers. |
| 242 void ReportEffectiveConnectionTypeForTesting( | 243 void ReportEffectiveConnectionTypeForTesting( |
| 243 EffectiveConnectionType effective_connection_type); | 244 EffectiveConnectionType effective_connection_type); |
| 244 | 245 |
| 245 protected: | 246 protected: |
| 246 // NetworkID is used to uniquely identify a network. | |
| 247 // For the purpose of network quality estimation and caching, a network is | |
| 248 // uniquely identified by a combination of |type| and | |
| 249 // |id|. This approach is unable to distinguish networks with | |
| 250 // same name (e.g., different Wi-Fi networks with same SSID). | |
| 251 // This is a protected member to expose it to tests. | |
| 252 struct NET_EXPORT_PRIVATE NetworkID { | |
| 253 NetworkID(NetworkChangeNotifier::ConnectionType type, const std::string& id) | |
| 254 : type(type), id(id) {} | |
| 255 NetworkID(const NetworkID& other) : type(other.type), id(other.id) {} | |
| 256 ~NetworkID() {} | |
| 257 | |
| 258 NetworkID& operator=(const NetworkID& other) { | |
| 259 type = other.type; | |
| 260 id = other.id; | |
| 261 return *this; | |
| 262 } | |
| 263 | |
| 264 // Overloaded because NetworkID is used as key in a map. | |
| 265 bool operator<(const NetworkID& other) const { | |
| 266 return std::tie(type, id) < std::tie(other.type, other.id); | |
| 267 } | |
| 268 | |
| 269 // Connection type of the network. | |
| 270 NetworkChangeNotifier::ConnectionType type; | |
| 271 | |
| 272 // Name of this network. This is set to: | |
| 273 // - Wi-Fi SSID if the device is connected to a Wi-Fi access point and the | |
| 274 // SSID name is available, or | |
| 275 // - MCC/MNC code of the cellular carrier if the device is connected to a | |
| 276 // cellular network, or | |
| 277 // - "Ethernet" in case the device is connected to ethernet. | |
| 278 // - An empty string in all other cases or if the network name is not | |
| 279 // exposed by platform APIs. | |
| 280 std::string id; | |
| 281 }; | |
| 282 | |
| 283 // Returns true if the cached network quality estimate was successfully read. | |
| 284 bool ReadCachedNetworkQualityEstimate(); | |
| 285 | |
| 286 // NetworkChangeNotifier::ConnectionTypeObserver implementation: | 247 // NetworkChangeNotifier::ConnectionTypeObserver implementation: |
| 287 void OnConnectionTypeChanged( | 248 void OnConnectionTypeChanged( |
| 288 NetworkChangeNotifier::ConnectionType type) override; | 249 NetworkChangeNotifier::ConnectionType type) override; |
| 289 | 250 |
| 290 // ExternalEstimateProvider::UpdatedEstimateObserver implementation. | 251 // ExternalEstimateProvider::UpdatedEstimateObserver implementation. |
| 291 void OnUpdatedEstimateAvailable(const base::TimeDelta& rtt, | 252 void OnUpdatedEstimateAvailable(const base::TimeDelta& rtt, |
| 292 int32_t downstream_throughput_kbps, | 253 int32_t downstream_throughput_kbps, |
| 293 int32_t upstream_throughput_kbps) override; | 254 int32_t upstream_throughput_kbps) override; |
| 294 | 255 |
| 295 // Returns true if the RTT is available and sets |rtt| to the RTT estimated at | 256 // Returns true if the RTT is available and sets |rtt| to the RTT estimated at |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 353 virtual double RandDouble() const; | 314 virtual double RandDouble() const; |
| 354 | 315 |
| 355 private: | 316 private: |
| 356 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest, StoreObservations); | 317 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest, StoreObservations); |
| 357 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest, TestAddObservation); | 318 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest, TestAddObservation); |
| 358 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest, ObtainOperatingParams); | 319 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest, ObtainOperatingParams); |
| 359 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest, | 320 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest, |
| 360 ObtainAlgorithmToUseFromParams); | 321 ObtainAlgorithmToUseFromParams); |
| 361 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest, HalfLifeParam); | 322 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest, HalfLifeParam); |
| 362 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest, ComputedPercentiles); | 323 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest, ComputedPercentiles); |
| 363 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest, TestCaching); | |
| 364 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest, | |
| 365 TestLRUCacheMaximumSize); | |
| 366 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest, TestGetMetricsSince); | 324 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest, TestGetMetricsSince); |
| 367 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest, | 325 FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest, |
| 368 TestExternalEstimateProviderMergeEstimates); | 326 TestExternalEstimateProviderMergeEstimates); |
| 369 | 327 |
| 370 // Value of round trip time observations is in base::TimeDelta. | 328 // Value of round trip time observations is in base::TimeDelta. |
| 371 typedef nqe::internal::Observation<base::TimeDelta> RttObservation; | 329 typedef nqe::internal::Observation<base::TimeDelta> RttObservation; |
| 372 typedef nqe::internal::ObservationBuffer<base::TimeDelta> | 330 typedef nqe::internal::ObservationBuffer<base::TimeDelta> |
| 373 RttObservationBuffer; | 331 RttObservationBuffer; |
| 374 | 332 |
| 375 // Value of throughput observations is in kilobits per second. | 333 // Value of throughput observations is in kilobits per second. |
| 376 typedef nqe::internal::Observation<int32_t> ThroughputObservation; | 334 typedef nqe::internal::Observation<int32_t> ThroughputObservation; |
| 377 typedef nqe::internal::ObservationBuffer<int32_t> ThroughputObservationBuffer; | 335 typedef nqe::internal::ObservationBuffer<int32_t> ThroughputObservationBuffer; |
| 378 | 336 |
| 379 // This does not use a unordered_map or hash_map for code simplicity (key just | |
| 380 // implements operator<, rather than hash and equality) and because the map is | |
| 381 // tiny. | |
| 382 typedef std::map<NetworkID, nqe::internal::CachedNetworkQuality> | |
| 383 CachedNetworkQualities; | |
| 384 | |
| 385 // Algorithms supported by network quality estimator for computing effective | 337 // Algorithms supported by network quality estimator for computing effective |
| 386 // connection type. | 338 // connection type. |
| 387 enum class EffectiveConnectionTypeAlgorithm { | 339 enum class EffectiveConnectionTypeAlgorithm { |
| 388 HTTP_RTT_AND_DOWNSTREAM_THROUGHOUT = 0, | 340 HTTP_RTT_AND_DOWNSTREAM_THROUGHOUT = 0, |
| 389 TRANSPORT_RTT_OR_DOWNSTREAM_THROUGHOUT, | 341 TRANSPORT_RTT_OR_DOWNSTREAM_THROUGHOUT, |
| 390 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST | 342 EFFECTIVE_CONNECTION_TYPE_ALGORITHM_LAST |
| 391 }; | 343 }; |
| 392 | 344 |
| 393 // Defines how a metric (e.g, transport RTT) should be used when computing | 345 // Defines how a metric (e.g, transport RTT) should be used when computing |
| 394 // the effective connection type. | 346 // the effective connection type. |
| (...skipping 22 matching lines...) Expand all Loading... |
| 417 EffectiveConnectionTypeAlgorithm::HTTP_RTT_AND_DOWNSTREAM_THROUGHOUT; | 369 EffectiveConnectionTypeAlgorithm::HTTP_RTT_AND_DOWNSTREAM_THROUGHOUT; |
| 418 | 370 |
| 419 // Minimum valid value of the variation parameter that holds RTT (in | 371 // Minimum valid value of the variation parameter that holds RTT (in |
| 420 // milliseconds) values. | 372 // milliseconds) values. |
| 421 static const int kMinimumRTTVariationParameterMsec = 1; | 373 static const int kMinimumRTTVariationParameterMsec = 1; |
| 422 | 374 |
| 423 // Minimum valid value of the variation parameter that holds throughput (in | 375 // Minimum valid value of the variation parameter that holds throughput (in |
| 424 // kilobits per second) values. | 376 // kilobits per second) values. |
| 425 static const int kMinimumThroughputVariationParameterKbps = 1; | 377 static const int kMinimumThroughputVariationParameterKbps = 1; |
| 426 | 378 |
| 427 // Maximum size of the cache that holds network quality estimates. | |
| 428 // Smaller size may reduce the cache hit rate due to frequent evictions. | |
| 429 // Larger size may affect performance. | |
| 430 static const size_t kMaximumNetworkQualityCacheSize = 10; | |
| 431 | |
| 432 // Returns the RTT value to be used when the valid RTT is unavailable. Readers | 379 // Returns the RTT value to be used when the valid RTT is unavailable. Readers |
| 433 // should discard RTT if it is set to the value returned by |InvalidRTT()|. | 380 // should discard RTT if it is set to the value returned by |InvalidRTT()|. |
| 434 static const base::TimeDelta InvalidRTT(); | 381 static const base::TimeDelta InvalidRTT(); |
| 435 | 382 |
| 436 // Records UMA when there is a change in connection type. | 383 // Records UMA when there is a change in connection type. |
| 437 void RecordMetricsOnConnectionTypeChanged() const; | 384 void RecordMetricsOnConnectionTypeChanged() const; |
| 438 | 385 |
| 439 // Records UMA on whether the NetworkID was available or not. Called right | 386 // Records UMA on whether the NetworkID was available or not. Called right |
| 440 // after a network change event. | 387 // after a network change event. |
| 441 void RecordNetworkIDAvailability() const; | 388 void RecordNetworkIDAvailability() const; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 480 const std::vector<NetworkQualityObservationSource>& | 427 const std::vector<NetworkQualityObservationSource>& |
| 481 disallowed_observation_sources, | 428 disallowed_observation_sources, |
| 482 const base::TimeTicks& start_time, | 429 const base::TimeTicks& start_time, |
| 483 int percentile) const; | 430 int percentile) const; |
| 484 int32_t GetDownlinkThroughputKbpsEstimateInternal( | 431 int32_t GetDownlinkThroughputKbpsEstimateInternal( |
| 485 const base::TimeTicks& start_time, | 432 const base::TimeTicks& start_time, |
| 486 int percentile) const; | 433 int percentile) const; |
| 487 | 434 |
| 488 // Returns the current network ID checking by calling the platform APIs. | 435 // Returns the current network ID checking by calling the platform APIs. |
| 489 // Virtualized for testing. | 436 // Virtualized for testing. |
| 490 virtual NetworkID GetCurrentNetworkID() const; | 437 virtual nqe::internal::NetworkID GetCurrentNetworkID() const; |
| 491 | |
| 492 // Writes the estimated quality of the current network to the cache. | |
| 493 void CacheNetworkQualityEstimate(); | |
| 494 | 438 |
| 495 void NotifyObserversOfRTT(const RttObservation& observation); | 439 void NotifyObserversOfRTT(const RttObservation& observation); |
| 496 | 440 |
| 497 void NotifyObserversOfThroughput(const ThroughputObservation& observation); | 441 void NotifyObserversOfThroughput(const ThroughputObservation& observation); |
| 498 | 442 |
| 499 // Returns true only if the |request| can be used for RTT estimation. | 443 // Returns true only if the |request| can be used for RTT estimation. |
| 500 bool RequestProvidesRTTObservation(const URLRequest& request) const; | 444 bool RequestProvidesRTTObservation(const URLRequest& request) const; |
| 501 | 445 |
| 502 // Recomputes effective connection type, if it was computed more than the | 446 // Recomputes effective connection type, if it was computed more than the |
| 503 // specified duration ago, or if there has been a connection change recently. | 447 // specified duration ago, or if there has been a connection change recently. |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 539 EXTERNAL_ESTIMATE_PROVIDER_STATUS_CALLBACK, | 483 EXTERNAL_ESTIMATE_PROVIDER_STATUS_CALLBACK, |
| 540 EXTERNAL_ESTIMATE_PROVIDER_STATUS_RTT_AVAILABLE, | 484 EXTERNAL_ESTIMATE_PROVIDER_STATUS_RTT_AVAILABLE, |
| 541 EXTERNAL_ESTIMATE_PROVIDER_STATUS_DOWNLINK_BANDWIDTH_AVAILABLE, | 485 EXTERNAL_ESTIMATE_PROVIDER_STATUS_DOWNLINK_BANDWIDTH_AVAILABLE, |
| 542 EXTERNAL_ESTIMATE_PROVIDER_STATUS_BOUNDARY | 486 EXTERNAL_ESTIMATE_PROVIDER_STATUS_BOUNDARY |
| 543 }; | 487 }; |
| 544 | 488 |
| 545 // Records the metrics related to external estimate provider. | 489 // Records the metrics related to external estimate provider. |
| 546 void RecordExternalEstimateProviderMetrics( | 490 void RecordExternalEstimateProviderMetrics( |
| 547 NQEExternalEstimateProviderStatus status) const; | 491 NQEExternalEstimateProviderStatus status) const; |
| 548 | 492 |
| 493 // Returns true if the cached network quality estimate was successfully read. |
| 494 bool ReadCachedNetworkQualityEstimate(); |
| 495 |
| 549 // Records a correlation metric that can be used for computing the correlation | 496 // Records a correlation metric that can be used for computing the correlation |
| 550 // between HTTP-layer RTT, transport-layer RTT, throughput and the time | 497 // between HTTP-layer RTT, transport-layer RTT, throughput and the time |
| 551 // taken to complete |request|. | 498 // taken to complete |request|. |
| 552 void RecordCorrelationMetric(const URLRequest& request) const; | 499 void RecordCorrelationMetric(const URLRequest& request) const; |
| 553 | 500 |
| 554 // Returns true if transport RTT should be used for computing the effective | 501 // Returns true if transport RTT should be used for computing the effective |
| 555 // connection type. | 502 // connection type. |
| 556 bool UseTransportRTT() const; | 503 bool UseTransportRTT() const; |
| 557 | 504 |
| 558 // Determines if the requests to local host can be used in estimating the | 505 // Determines if the requests to local host can be used in estimating the |
| (...skipping 25 matching lines...) Expand all Loading... |
| 584 base::TimeTicks last_effective_connection_type_computation_; | 531 base::TimeTicks last_effective_connection_type_computation_; |
| 585 | 532 |
| 586 // Intervals after the main frame request arrives at which accuracy of network | 533 // Intervals after the main frame request arrives at which accuracy of network |
| 587 // quality prediction is recorded. | 534 // quality prediction is recorded. |
| 588 std::vector<base::TimeDelta> accuracy_recording_intervals_; | 535 std::vector<base::TimeDelta> accuracy_recording_intervals_; |
| 589 | 536 |
| 590 // Time when last connection change was observed. | 537 // Time when last connection change was observed. |
| 591 base::TimeTicks last_connection_change_; | 538 base::TimeTicks last_connection_change_; |
| 592 | 539 |
| 593 // ID of the current network. | 540 // ID of the current network. |
| 594 NetworkID current_network_id_; | 541 nqe::internal::NetworkID current_network_id_; |
| 595 | 542 |
| 596 // Peak network quality (fastest round-trip-time (RTT) and highest | 543 // Peak network quality (fastest round-trip-time (RTT) and highest |
| 597 // downstream throughput) measured since last connectivity change. RTT is | 544 // downstream throughput) measured since last connectivity change. RTT is |
| 598 // measured from time the request is sent until the first byte received. | 545 // measured from time the request is sent until the first byte received. |
| 599 // The accuracy is decreased by ignoring these factors: | 546 // The accuracy is decreased by ignoring these factors: |
| 600 // 1) Multiple URLRequests can occur concurrently. | 547 // 1) Multiple URLRequests can occur concurrently. |
| 601 // 2) Includes server processing time. | 548 // 2) Includes server processing time. |
| 602 nqe::internal::NetworkQuality peak_network_quality_; | 549 nqe::internal::NetworkQuality peak_network_quality_; |
| 603 | 550 |
| 604 // Cache that stores quality of previously seen networks. | |
| 605 CachedNetworkQualities cached_network_qualities_; | |
| 606 | |
| 607 // Buffer that holds throughput observations (in kilobits per second) sorted | 551 // Buffer that holds throughput observations (in kilobits per second) sorted |
| 608 // by timestamp. | 552 // by timestamp. |
| 609 ThroughputObservationBuffer downstream_throughput_kbps_observations_; | 553 ThroughputObservationBuffer downstream_throughput_kbps_observations_; |
| 610 | 554 |
| 611 // Buffer that holds RTT observations sorted by timestamp. | 555 // Buffer that holds RTT observations sorted by timestamp. |
| 612 RttObservationBuffer rtt_observations_; | 556 RttObservationBuffer rtt_observations_; |
| 613 | 557 |
| 614 // Default network quality observations obtained from the network quality | 558 // Default network quality observations obtained from the network quality |
| 615 // estimator field trial parameters. The observations are indexed by | 559 // estimator field trial parameters. The observations are indexed by |
| 616 // ConnectionType. | 560 // ConnectionType. |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 663 int32_t min_signal_strength_since_connection_change_; | 607 int32_t min_signal_strength_since_connection_change_; |
| 664 int32_t max_signal_strength_since_connection_change_; | 608 int32_t max_signal_strength_since_connection_change_; |
| 665 | 609 |
| 666 // It is costlier to add values to a sparse histogram. So, the correlation UMA | 610 // It is costlier to add values to a sparse histogram. So, the correlation UMA |
| 667 // is recorded with |correlation_uma_logging_probability_| since recording it | 611 // is recorded with |correlation_uma_logging_probability_| since recording it |
| 668 // in a sparse histogram for each request is unnecessary and cost-prohibitive. | 612 // in a sparse histogram for each request is unnecessary and cost-prohibitive. |
| 669 // e.g., if it is 0.0, then the UMA will never be recorded. On the other hand, | 613 // e.g., if it is 0.0, then the UMA will never be recorded. On the other hand, |
| 670 // if it is 1.0, then it will be recorded for all valid HTTP requests. | 614 // if it is 1.0, then it will be recorded for all valid HTTP requests. |
| 671 const double correlation_uma_logging_probability_; | 615 const double correlation_uma_logging_probability_; |
| 672 | 616 |
| 617 // Stores the qualities of different networks. |
| 618 nqe::internal::NetworkQualityStore network_quality_store_; |
| 619 |
| 673 base::ThreadChecker thread_checker_; | 620 base::ThreadChecker thread_checker_; |
| 674 | 621 |
| 675 base::WeakPtrFactory<NetworkQualityEstimator> weak_ptr_factory_; | 622 base::WeakPtrFactory<NetworkQualityEstimator> weak_ptr_factory_; |
| 676 | 623 |
| 677 DISALLOW_COPY_AND_ASSIGN(NetworkQualityEstimator); | 624 DISALLOW_COPY_AND_ASSIGN(NetworkQualityEstimator); |
| 678 }; | 625 }; |
| 679 | 626 |
| 680 } // namespace net | 627 } // namespace net |
| 681 | 628 |
| 682 #endif // NET_NQE_NETWORK_QUALITY_ESTIMATOR_H_ | 629 #endif // NET_NQE_NETWORK_QUALITY_ESTIMATOR_H_ |
| OLD | NEW |