diff options
Diffstat (limited to 'chromium/components/data_use_measurement')
9 files changed, 606 insertions, 83 deletions
diff --git a/chromium/components/data_use_measurement/core/BUILD.gn b/chromium/components/data_use_measurement/core/BUILD.gn index 29dd799f1de..775b77a2752 100644 --- a/chromium/components/data_use_measurement/core/BUILD.gn +++ b/chromium/components/data_use_measurement/core/BUILD.gn @@ -19,24 +19,32 @@ static_library("ascriber") { "data_use.h", "data_use_measurement.cc", "data_use_measurement.h", + "data_use_pref_names.h", + "data_use_tracker_prefs.cc", + "data_use_tracker_prefs.h", ] deps = [ ":core", "//base", "//components/metrics", + "//components/prefs", "//net", "//services/network/public/cpp:cpp", ] } source_set("unit_tests") { - sources = [ "data_use_measurement_unittest.cc" ] + sources = [ + "data_use_measurement_unittest.cc", + "data_use_tracker_prefs_unittest.cc", + ] testonly = true deps = [ ":ascriber", ":core", "//base/test:test_support", "//components/metrics:metrics", + "//components/prefs:test_support", "//net:test_support", "//services/network:test_support", "//testing/gtest", diff --git a/chromium/components/data_use_measurement/core/DEPS b/chromium/components/data_use_measurement/core/DEPS index 9592232ea5d..c620476d474 100644 --- a/chromium/components/data_use_measurement/core/DEPS +++ b/chromium/components/data_use_measurement/core/DEPS @@ -1,6 +1,7 @@ include_rules = [ "+net", "+components/metrics", + "+components/prefs", "+services/network/public/cpp", "+services/network/test", ] diff --git a/chromium/components/data_use_measurement/core/data_use_measurement.cc b/chromium/components/data_use_measurement/core/data_use_measurement.cc index b570ead3738..51708b685e6 100644 --- a/chromium/components/data_use_measurement/core/data_use_measurement.cc +++ b/chromium/components/data_use_measurement/core/data_use_measurement.cc @@ -12,6 +12,8 @@ #include "base/metrics/histogram_macros.h" #include "base/metrics/sparse_histogram.h" #include "base/strings/stringprintf.h" +#include "base/time/clock.h" +#include "base/time/default_clock.h" #include "build/build_config.h" #include "components/data_use_measurement/core/data_use_user_data.h" #include "net/traffic_annotation/network_traffic_annotation.h" @@ -25,6 +27,20 @@ namespace data_use_measurement { namespace { +#if defined(OS_ANDROID) +bool IsInForeground(base::android::ApplicationState state) { + switch (state) { + case base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES: + return true; + case base::android::APPLICATION_STATE_UNKNOWN: + case base::android::APPLICATION_STATE_HAS_PAUSED_ACTIVITIES: + case base::android::APPLICATION_STATE_HAS_STOPPED_ACTIVITIES: + case base::android::APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES: + return false; + } +} +#endif + // Records the occurrence of |sample| in |name| histogram. Conventional UMA // histograms are not used because the |name| is not static. void RecordUMAHistogramCount(const std::string& name, int64_t sample) { @@ -41,24 +57,13 @@ void RecordUMAHistogramCount(const std::string& name, int64_t sample) { } // namespace DataUseMeasurement::DataUseMeasurement( + PrefService* pref_service, network::NetworkConnectionTracker* network_connection_tracker) - : -#if defined(OS_ANDROID) - app_state_(base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES), - app_listener_(base::android::ApplicationStatusListener::New( - base::BindRepeating(&DataUseMeasurement::OnApplicationStateChange, - base::Unretained(this)))), - rx_bytes_os_(0), - tx_bytes_os_(0), - no_reads_since_background_(false), -#endif - network_connection_tracker_(network_connection_tracker), - connection_type_(network::mojom::ConnectionType::CONNECTION_UNKNOWN) { + : network_connection_tracker_(network_connection_tracker), + data_use_tracker_prefs_(base::DefaultClock::GetInstance(), pref_service) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(network_connection_tracker_); - network_connection_tracker_->AddLeakyNetworkConnectionObserver(this); - #if defined(OS_ANDROID) int64_t bytes = 0; // Query Android traffic stats. @@ -68,10 +73,29 @@ DataUseMeasurement::DataUseMeasurement( if (net::android::traffic_stats::GetCurrentUidTxBytes(&bytes)) tx_bytes_os_ = bytes; #endif + + network_connection_tracker_->AddLeakyNetworkConnectionObserver(this); + + network_connection_tracker_->GetConnectionType( + &connection_type_, + base::BindOnce(&DataUseMeasurement::OnConnectionChanged, + weak_ptr_factory_.GetWeakPtr())); + +#if defined(OS_ANDROID) + app_state_ = base::android::ApplicationStatusListener::GetState(); + + app_listener_ = base::android::ApplicationStatusListener::New( + base::BindRepeating(&DataUseMeasurement::OnApplicationStateChange, + base::Unretained(this))); +#endif } DataUseMeasurement::~DataUseMeasurement() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +#if defined(OS_ANDROID) + if (app_listener_) + app_listener_.reset(); +#endif network_connection_tracker_->RemoveNetworkConnectionObserver(this); DCHECK(!services_data_use_observer_list_.might_have_observers()); } @@ -84,6 +108,13 @@ void DataUseMeasurement::RecordDownstreamUserTrafficSizeMetric( CurrentAppState(), IsCurrentNetworkCellular()), bytes); RecordTabStateHistogram(DOWNSTREAM, CurrentAppState(), is_tab_visible, bytes); + bytes_transferred_since_last_traffic_stats_query_ += bytes; + MaybeRecordNetworkBytesOS(/*force_record_metrics=*/false); + + data_use_tracker_prefs_.ReportNetworkServiceDataUse( + IsCurrentNetworkCellular(), + CurrentAppState() == DataUseUserData::FOREGROUND, + /*is_user_traffic=*/true, bytes); } #if defined(OS_ANDROID) @@ -95,17 +126,18 @@ void DataUseMeasurement::OnApplicationStateChangeForTesting( DataUseUserData::AppState DataUseMeasurement::CurrentAppState() const { #if defined(OS_ANDROID) - if (app_state_ != base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES) - return DataUseUserData::BACKGROUND; + return IsInForeground(app_state_) ? DataUseUserData::FOREGROUND + : DataUseUserData::BACKGROUND; #endif // If the OS is not Android, all the requests are considered Foreground. return DataUseUserData::FOREGROUND; } +// static std::string DataUseMeasurement::GetHistogramNameWithConnectionType( const char* prefix, TrafficDirection dir, - DataUseUserData::AppState app_state) const { + DataUseUserData::AppState app_state) { return base::StringPrintf( "%s.%s.%s", prefix, dir == UPSTREAM ? "Upstream" : "Downstream", app_state == DataUseUserData::UNKNOWN @@ -114,11 +146,12 @@ std::string DataUseMeasurement::GetHistogramNameWithConnectionType( : "Background")); } +// static std::string DataUseMeasurement::GetHistogramName( const char* prefix, TrafficDirection dir, DataUseUserData::AppState app_state, - bool is_connection_cellular) const { + bool is_connection_cellular) { return base::StringPrintf( "%s.%s.%s.%s", prefix, dir == UPSTREAM ? "Upstream" : "Downstream", app_state == DataUseUserData::UNKNOWN @@ -132,24 +165,24 @@ std::string DataUseMeasurement::GetHistogramName( void DataUseMeasurement::OnApplicationStateChange( base::android::ApplicationState application_state) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (app_state_ == application_state) + return; + MaybeRecordNetworkBytesOS(/*force_record_metrics=*/true); app_state_ = application_state; - if (app_state_ != base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES) { - last_app_background_time_ = base::TimeTicks::Now(); - no_reads_since_background_ = true; - MaybeRecordNetworkBytesOS(); - } else { - last_app_background_time_ = base::TimeTicks(); - } } +#endif -void DataUseMeasurement::MaybeRecordNetworkBytesOS() { +void DataUseMeasurement::MaybeRecordNetworkBytesOS(bool force_record_metrics) { +#if defined(OS_ANDROID) // Minimum number of bytes that should be reported by the network delegate // before Android's TrafficStats API is queried (if Chrome is not in // background). This reduces the overhead of repeatedly calling the API. static const int64_t kMinDelegateBytes = 25000; DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (bytes_transferred_since_last_traffic_stats_query_ < kMinDelegateBytes && + if (!force_record_metrics && + bytes_transferred_since_last_traffic_stats_query_ < kMinDelegateBytes && CurrentAppState() == DataUseUserData::FOREGROUND) { return; } @@ -162,9 +195,16 @@ void DataUseMeasurement::MaybeRecordNetworkBytesOS() { if (rx_bytes_os_ != 0) { DCHECK_GE(bytes, rx_bytes_os_); if (bytes > rx_bytes_os_) { + int64_t incremental_bytes = bytes - rx_bytes_os_; // Do not record samples with value 0. - UMA_HISTOGRAM_COUNTS_1M("DataUse.BytesReceived.OS", - bytes - rx_bytes_os_); + UMA_HISTOGRAM_COUNTS_1M("DataUse.BytesReceived.OS", incremental_bytes); + if (IsInForeground(app_state_)) { + UMA_HISTOGRAM_COUNTS_1M("DataUse.BytesReceived.OS.Foreground", + incremental_bytes); + } else { + UMA_HISTOGRAM_COUNTS_1M("DataUse.BytesReceived.OS.Background", + incremental_bytes); + } } } rx_bytes_os_ = bytes; @@ -174,29 +214,49 @@ void DataUseMeasurement::MaybeRecordNetworkBytesOS() { if (tx_bytes_os_ != 0) { DCHECK_GE(bytes, tx_bytes_os_); if (bytes > tx_bytes_os_) { + int64_t incremental_bytes = bytes - tx_bytes_os_; // Do not record samples with value 0. - UMA_HISTOGRAM_COUNTS_1M("DataUse.BytesSent.OS", bytes - tx_bytes_os_); + UMA_HISTOGRAM_COUNTS_1M("DataUse.BytesSent.OS", incremental_bytes); + if (IsInForeground(app_state_)) { + UMA_HISTOGRAM_COUNTS_1M("DataUse.BytesSent.OS.Foreground", + incremental_bytes); + } else { + UMA_HISTOGRAM_COUNTS_1M("DataUse.BytesSent.OS.Background", + incremental_bytes); + } } } tx_bytes_os_ = bytes; } -} #endif +} void DataUseMeasurement::ReportDataUsageServices( int32_t traffic_annotation_hash, TrafficDirection dir, DataUseUserData::AppState app_state, - int64_t message_size_bytes) const { - if (message_size_bytes > 0) { - // Conventional UMA histograms are not used because name is not static. - base::HistogramBase* histogram = base::SparseHistogram::FactoryGet( - GetHistogramNameWithConnectionType("DataUse.AllServicesKB", dir, - app_state), - base::HistogramBase::kUmaTargetedHistogramFlag); - histogram->AddKiB(traffic_annotation_hash, - base::saturated_cast<int>(message_size_bytes)); - } + int64_t message_size_bytes) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (message_size_bytes <= 0) + return; + + // Conventional UMA histograms are not used because name is not static. + base::HistogramBase* histogram = base::SparseHistogram::FactoryGet( + GetHistogramNameWithConnectionType("DataUse.AllServicesKB", dir, + app_state), + base::HistogramBase::kUmaTargetedHistogramFlag); + // AddKiB method takes value in bytes. + histogram->AddKiB(traffic_annotation_hash, + base::saturated_cast<int>(message_size_bytes)); + + bytes_transferred_since_last_traffic_stats_query_ += message_size_bytes; + MaybeRecordNetworkBytesOS(/*force_record_metrics=*/false); + + data_use_tracker_prefs_.ReportNetworkServiceDataUse( + IsCurrentNetworkCellular(), + CurrentAppState() == DataUseUserData::FOREGROUND, + /*is_user_traffic=*/false, message_size_bytes); } void DataUseMeasurement::RecordTabStateHistogram( @@ -204,6 +264,8 @@ void DataUseMeasurement::RecordTabStateHistogram( DataUseUserData::AppState app_state, bool is_tab_visible, int64_t bytes) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (app_state == DataUseUserData::UNKNOWN) return; @@ -284,17 +346,29 @@ bool DataUseMeasurement::IsCurrentNetworkCellular() const { void DataUseMeasurement::OnConnectionChanged( network::mojom::ConnectionType type) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (connection_type_ != network::mojom::ConnectionType::CONNECTION_UNKNOWN) + MaybeRecordNetworkBytesOS(/*force_record_metrics=*/true); + connection_type_ = type; } void DataUseMeasurement::AddServicesDataUseObserver( ServicesDataUseObserver* observer) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); services_data_use_observer_list_.AddObserver(observer); } void DataUseMeasurement::RemoveServicesDataUseObserver( ServicesDataUseObserver* observer) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); services_data_use_observer_list_.RemoveObserver(observer); } +// static +void DataUseMeasurement::RegisterDataUseComponentLocalStatePrefs( + PrefRegistrySimple* registry) { + DataUseTrackerPrefs::RegisterDataUseTrackerLocalStatePrefs(registry); +} + } // namespace data_use_measurement diff --git a/chromium/components/data_use_measurement/core/data_use_measurement.h b/chromium/components/data_use_measurement/core/data_use_measurement.h index 5b331b2d438..6b5d3e84193 100644 --- a/chromium/components/data_use_measurement/core/data_use_measurement.h +++ b/chromium/components/data_use_measurement/core/data_use_measurement.h @@ -12,10 +12,12 @@ #include "base/callback.h" #include "base/macros.h" +#include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/sequence_checker.h" #include "base/time/time.h" #include "build/build_config.h" +#include "components/data_use_measurement/core/data_use_tracker_prefs.h" #include "components/data_use_measurement/core/data_use_user_data.h" #include "services/network/public/cpp/network_connection_tracker.h" @@ -23,6 +25,8 @@ #include "base/android/application_status_listener.h" #endif +class PrefService; + namespace data_use_measurement { // Records the data use of user traffic and various services in UMA histograms. @@ -53,7 +57,10 @@ class DataUseMeasurement static bool IsMetricsServiceRequest( int32_t network_traffic_annotation_hash_id); + // |pref_service| can be used for accessing local state prefs. Can be null. + // |network_connection_tracker| is guaranteed to be non-null. DataUseMeasurement( + PrefService* pref_service, network::NetworkConnectionTracker* network_connection_tracker); ~DataUseMeasurement() override; @@ -88,39 +95,35 @@ class DataUseMeasurement void ReportDataUsageServices(int32_t traffic_annotation_hash, TrafficDirection dir, DataUseUserData::AppState app_state, - int64_t message_size_bytes) const; + int64_t message_size_bytes); // Returns if the current network connection type is cellular. bool IsCurrentNetworkCellular() const; -#if defined(OS_ANDROID) - // Records the count of bytes received and sent by Chrome on the network as - // reported by the operating system. - void MaybeRecordNetworkBytesOS(); - - // Number of bytes received and sent by Chromium as reported by the network - // delegate since the operating system was last queried for traffic - // statistics. - int64_t bytes_transferred_since_last_traffic_stats_query_ = 0; -#endif + static void RegisterDataUseComponentLocalStatePrefs( + PrefRegistrySimple* registry); base::ObserverList<ServicesDataUseObserver>::Unchecked services_data_use_observer_list_; - SEQUENCE_CHECKER(sequence_checker_); - private: friend class DataUseMeasurementTest; + // Records the count of bytes received and sent by Chrome on the network as + // reported by the operating system. If |force_record_metrics| is true, the + // data use metrics are always recorded. If |force_record_metrics| is false, + // data use may be recorded only if it's expected to be high. + void MaybeRecordNetworkBytesOS(bool force_record_metrics); + // Makes the full name of the histogram. It is made from |prefix| and suffix // which is made based on network and application status. suffix is a string // representing whether the data use was on the send ("Upstream") or receive // ("Downstream") path, and whether the app was in the "Foreground" or // "Background". - std::string GetHistogramNameWithConnectionType( + static std::string GetHistogramNameWithConnectionType( const char* prefix, TrafficDirection dir, - DataUseUserData::AppState app_state) const; + DataUseUserData::AppState app_state); // Makes the full name of the histogram. It is made from |prefix| and suffix // which is made based on network and application status. suffix is a string @@ -130,10 +133,10 @@ class DataUseMeasurement // example, "Prefix.Upstream.Foreground.Cellular" is a possible output. // |app_state| indicates the app state which can be foreground, background, or // unknown. - std::string GetHistogramName(const char* prefix, - TrafficDirection dir, - DataUseUserData::AppState app_state, - bool is_connection_cellular) const; + static std::string GetHistogramName(const char* prefix, + TrafficDirection dir, + DataUseUserData::AppState app_state, + bool is_connection_cellular); #if defined(OS_ANDROID) // Called whenever the application transitions from foreground to background @@ -156,32 +159,39 @@ class DataUseMeasurement #if defined(OS_ANDROID) // Application listener store the last known state of the application in this // field. - base::android::ApplicationState app_state_; + base::android::ApplicationState app_state_ = + base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES; // ApplicationStatusListener used to monitor whether the application is in the // foreground or in the background. It is owned by DataUseMeasurement. std::unique_ptr<base::android::ApplicationStatusListener> app_listener_; +#endif // Number of bytes received and sent by Chromium as reported by the operating // system when it was last queried for traffic statistics. Set to 0 if the // operating system was never queried. - int64_t rx_bytes_os_; - int64_t tx_bytes_os_; - - // The time at which Chromium app state changed to background. Can be null if - // app is not in background. - base::TimeTicks last_app_background_time_; - - // True if app is in background and first network read has not yet happened. - bool no_reads_since_background_; -#endif + int64_t rx_bytes_os_ = 0; + int64_t tx_bytes_os_ = 0; // Watches for network connection changes. Global singleton object and // outlives |this| network::NetworkConnectionTracker* network_connection_tracker_; // The current connection type. - network::mojom::ConnectionType connection_type_; + network::mojom::ConnectionType connection_type_ = + network::mojom::ConnectionType::CONNECTION_UNKNOWN; + + // Number of bytes received and sent by Chromium as reported by the network + // delegate since the operating system was last queried for traffic + // statistics. + int64_t bytes_transferred_since_last_traffic_stats_query_ = 0; + + SEQUENCE_CHECKER(sequence_checker_); + + // Records the data usage in prefs. + DataUseTrackerPrefs data_use_tracker_prefs_; + + base::WeakPtrFactory<DataUseMeasurement> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(DataUseMeasurement); }; diff --git a/chromium/components/data_use_measurement/core/data_use_measurement_unittest.cc b/chromium/components/data_use_measurement/core/data_use_measurement_unittest.cc index 26d3a990c7a..f310daf9a66 100644 --- a/chromium/components/data_use_measurement/core/data_use_measurement_unittest.cc +++ b/chromium/components/data_use_measurement/core/data_use_measurement_unittest.cc @@ -11,6 +11,9 @@ #include "base/run_loop.h" #include "base/test/metrics/histogram_tester.h" #include "build/build_config.h" +#include "components/data_use_measurement/core/data_use_pref_names.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/testing_pref_service.h" #include "net/base/network_change_notifier.h" #include "services/network/test/test_network_connection_tracker.h" #include "testing/gtest/include/gtest/gtest.h" @@ -21,10 +24,11 @@ namespace data_use_measurement { -class DataUseMeasurementTest : public testing::Test { +class DataUseMeasurementTest { public: - DataUseMeasurementTest() + explicit DataUseMeasurementTest(TestingPrefServiceSimple* test_prefs_) : data_use_measurement_( + test_prefs_, network::TestNetworkConnectionTracker::GetInstance()) { // During the test it is expected to not have cellular connection. DCHECK(!net::NetworkChangeNotifier::IsConnectionCellular( @@ -59,22 +63,45 @@ class DataUseMeasurementTest : public testing::Test { // foreground or the OS is not Android. // TODO(amohammadkhan): Add tests for Cellular/non-cellular connection types // when support for testing is provided in its class. -TEST_F(DataUseMeasurementTest, UserNotUserTest) { +TEST(DataUseMeasurementTest, UserNotUserTest) { + TestingPrefServiceSimple test_prefs; + + test_prefs.registry()->RegisterDictionaryPref(prefs::kDataUsedUserForeground); + test_prefs.registry()->RegisterDictionaryPref(prefs::kDataUsedUserBackground); + test_prefs.registry()->RegisterDictionaryPref( + prefs::kDataUsedServicesForeground); + test_prefs.registry()->RegisterDictionaryPref( + prefs::kDataUsedServicesBackground); + + DataUseMeasurementTest data_use_measurement_test(&test_prefs); #if defined(OS_ANDROID) - data_use_measurement()->OnApplicationStateChangeForTesting( - base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES); + data_use_measurement_test.data_use_measurement() + ->OnApplicationStateChangeForTesting( + base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES); #endif - TestForAUserRequest("Foreground."); + data_use_measurement_test.TestForAUserRequest("Foreground."); } #if defined(OS_ANDROID) // This test function tests recording of data use information in UMA histogram // when packet is originated from user or services when the app is in the // background and OS is Android. -TEST_F(DataUseMeasurementTest, ApplicationStateTest) { - data_use_measurement()->OnApplicationStateChangeForTesting( - base::android::APPLICATION_STATE_HAS_STOPPED_ACTIVITIES); - TestForAUserRequest("Background."); +TEST(DataUseMeasurementTest, ApplicationStateTest) { + TestingPrefServiceSimple test_prefs; + + test_prefs.registry()->RegisterDictionaryPref(prefs::kDataUsedUserForeground); + test_prefs.registry()->RegisterDictionaryPref(prefs::kDataUsedUserBackground); + test_prefs.registry()->RegisterDictionaryPref( + prefs::kDataUsedServicesForeground); + test_prefs.registry()->RegisterDictionaryPref( + prefs::kDataUsedServicesBackground); + + DataUseMeasurementTest data_use_measurement_test(&test_prefs); + + data_use_measurement_test.data_use_measurement() + ->OnApplicationStateChangeForTesting( + base::android::APPLICATION_STATE_HAS_STOPPED_ACTIVITIES); + data_use_measurement_test.TestForAUserRequest("Background."); } #endif diff --git a/chromium/components/data_use_measurement/core/data_use_pref_names.h b/chromium/components/data_use_measurement/core/data_use_pref_names.h new file mode 100644 index 00000000000..5135a402e03 --- /dev/null +++ b/chromium/components/data_use_measurement/core/data_use_pref_names.h @@ -0,0 +1,30 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_DATA_USE_MEASUREMENT_CORE_DATA_USE_PREF_NAMES_H_ +#define COMPONENTS_DATA_USE_MEASUREMENT_CORE_DATA_USE_PREF_NAMES_H_ + +#include <memory> + +#include "base/macros.h" + +namespace data_use_measurement { + +namespace prefs { +// Dictionary prefs for measuring cellular data used. |key| is +// the date of data usage (stored as string using exploded format). |value| +// stores the data used for that date as a double in kilobytes. +const char kDataUsedUserForeground[] = + "data_use_measurement.data_used.user.foreground"; +const char kDataUsedUserBackground[] = + "data_use_measurement.data_used.user.background"; +const char kDataUsedServicesForeground[] = + "data_use_measurement.data_used.services.foreground"; +const char kDataUsedServicesBackground[] = + "data_use_measurement.data_used.services.background"; +} // namespace prefs + +} // namespace data_use_measurement + +#endif // COMPONENTS_DATA_USE_MEASUREMENT_CORE_DATA_USE_PREF_NAMES_H_ diff --git a/chromium/components/data_use_measurement/core/data_use_tracker_prefs.cc b/chromium/components/data_use_measurement/core/data_use_tracker_prefs.cc new file mode 100644 index 00000000000..5eb549068ca --- /dev/null +++ b/chromium/components/data_use_measurement/core/data_use_tracker_prefs.cc @@ -0,0 +1,130 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/data_use_measurement/core/data_use_tracker_prefs.h" + +#include <string> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/macros.h" +#include "base/metrics/histogram_macros.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/task/post_task.h" +#include "base/task/task_traits.h" +#include "base/time/clock.h" +#include "build/build_config.h" +#include "components/data_use_measurement/core/data_use_pref_names.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/scoped_user_pref_update.h" + +namespace data_use_measurement { + +DataUseTrackerPrefs::DataUseTrackerPrefs(const base::Clock* time_clock, + PrefService* pref_service) + : time_clock_(time_clock), pref_service_(pref_service) { + DCHECK(time_clock_); + + RemoveExpiredEntriesForPref(prefs::kDataUsedUserForeground); + RemoveExpiredEntriesForPref(prefs::kDataUsedUserBackground); + RemoveExpiredEntriesForPref(prefs::kDataUsedServicesForeground); + RemoveExpiredEntriesForPref(prefs::kDataUsedServicesBackground); +} + +void DataUseTrackerPrefs::ReportNetworkServiceDataUse( + bool is_metered_connection, + bool is_app_foreground, + bool is_user_traffic, + int64_t sent_or_recv_bytes) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (!is_metered_connection) + return; + + if (sent_or_recv_bytes <= 0) + return; + + if (is_user_traffic && is_app_foreground) { + UpdateUsagePref(prefs::kDataUsedUserForeground, sent_or_recv_bytes); + } else if (is_user_traffic && !is_app_foreground) { + UpdateUsagePref(prefs::kDataUsedUserBackground, sent_or_recv_bytes); + } else if (!is_user_traffic && is_app_foreground) { + UpdateUsagePref(prefs::kDataUsedServicesForeground, sent_or_recv_bytes); + } else { + UpdateUsagePref(prefs::kDataUsedServicesBackground, sent_or_recv_bytes); + } +} + +base::Time DataUseTrackerPrefs::GetCurrentMeasurementDate() const { + return time_clock_->Now().LocalMidnight(); +} + +void DataUseTrackerPrefs::RemoveExpiredEntriesForPref( + const std::string& pref_name) { + if (!pref_service_) + return; + + const base::DictionaryValue* user_pref_dict = + pref_service_->GetDictionary(pref_name); + const base::Time current_date = GetCurrentMeasurementDate(); + const base::Time last_date = current_date - base::TimeDelta::FromDays(60); + + base::DictionaryValue user_pref_new_dict; + for (const auto& it : user_pref_dict->DictItems()) { + base::Time key_date; + if (base::Time::FromUTCString(it.first.c_str(), &key_date) && + key_date > last_date) { + user_pref_new_dict.Set(it.first, it.second.CreateDeepCopy()); + } + } + pref_service_->Set(pref_name, user_pref_new_dict); +} + +std::string DataUseTrackerPrefs::GetCurrentMeasurementDateAsString() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + base::Time::Exploded today_exploded; + GetCurrentMeasurementDate().LocalExplode(&today_exploded); + std::string date = + base::StringPrintf("%04d-%02d-%02d", today_exploded.year, + today_exploded.month, today_exploded.day_of_month); + return date; +} + +void DataUseTrackerPrefs::UpdateUsagePref(const std::string& pref_name, + int64_t message_size_bytes) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (!pref_service_) + return; + + DictionaryPrefUpdate pref_updater(pref_service_, pref_name); + double todays_traffic = 0; + std::string todays_key = GetCurrentMeasurementDateAsString(); + + const base::DictionaryValue* user_pref_dict = + pref_service_->GetDictionary(pref_name); + user_pref_dict->GetDouble(todays_key, &todays_traffic); + pref_updater->SetDouble( + todays_key, + todays_traffic + (static_cast<double>(message_size_bytes) / 1024.0)); +} + +// static +void DataUseTrackerPrefs::RegisterDataUseTrackerLocalStatePrefs( + PrefRegistrySimple* registry) { + registry->RegisterDictionaryPref(prefs::kDataUsedUserForeground, + PrefRegistry::LOSSY_PREF); + registry->RegisterDictionaryPref(prefs::kDataUsedUserBackground, + PrefRegistry::LOSSY_PREF); + registry->RegisterDictionaryPref(prefs::kDataUsedServicesForeground, + PrefRegistry::LOSSY_PREF); + registry->RegisterDictionaryPref(prefs::kDataUsedServicesBackground, + PrefRegistry::LOSSY_PREF); +} + +} // namespace data_use_measurement diff --git a/chromium/components/data_use_measurement/core/data_use_tracker_prefs.h b/chromium/components/data_use_measurement/core/data_use_tracker_prefs.h new file mode 100644 index 00000000000..96cc18d9ac9 --- /dev/null +++ b/chromium/components/data_use_measurement/core/data_use_tracker_prefs.h @@ -0,0 +1,70 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_DATA_USE_MEASUREMENT_CORE_DATA_USE_TRACKER_PREFS_H_ +#define COMPONENTS_DATA_USE_MEASUREMENT_CORE_DATA_USE_TRACKER_PREFS_H_ + +#include <memory> + +#include "base/macros.h" +#include "base/sequence_checker.h" +#include "base/time/clock.h" +#include "base/time/time.h" + +class PrefRegistrySimple; +class PrefService; + +namespace data_use_measurement { + +// DataUseTrackerPrefs keeps track of the data used over last 60 days. The data +// used is recorded separately based on whether the data use was initiated by a +// Chrome service or a user-initiated request. +class DataUseTrackerPrefs { + public: + // |pref_service| may be null in tests. + DataUseTrackerPrefs(const base::Clock* time_clock, PrefService* pref_service); + + // Move-only class. + DataUseTrackerPrefs(const DataUseTrackerPrefs&) = delete; + DataUseTrackerPrefs& operator=(const DataUseTrackerPrefs&) = delete; + + // Report data used by a service or a user-initiated request. + // |is_metered_connection| should be true if data consumption happened on a + // metered connection. |is_app_foreground| should be true if data was used + // when Chrome app was in foregorund. |is_user_traffic| should be true if data + // was used by a user-initiated request. |sent_or_recv_bytes| should be set to + // the data consumed (in bytes). + void ReportNetworkServiceDataUse(bool is_metered_connection, + bool is_app_foreground, + bool is_user_traffic, + int64_t sent_or_recv_bytes); + + // Register local state prefs. + static void RegisterDataUseTrackerLocalStatePrefs( + PrefRegistrySimple* registry); + + private: + // Returns the current date for measurement. + base::Time GetCurrentMeasurementDate() const; + + // Removes entries from the given |pref_name| if they are too old. + void RemoveExpiredEntriesForPref(const std::string& pref_name); + + // Returns the current date as a string with a proper formatting. + std::string GetCurrentMeasurementDateAsString() const; + + // Updates provided |pref_name| for a current date with the given message + // size. + void UpdateUsagePref(const std::string& pref_name, + int64_t message_size_bytes); + + const base::Clock* time_clock_; + PrefService* pref_service_ = nullptr; + + SEQUENCE_CHECKER(sequence_checker_); +}; + +} // namespace data_use_measurement + +#endif // COMPONENTS_DATA_USE_MEASUREMENT_CORE_DATA_USE_TRACKER_PREFS_H_ diff --git a/chromium/components/data_use_measurement/core/data_use_tracker_prefs_unittest.cc b/chromium/components/data_use_measurement/core/data_use_tracker_prefs_unittest.cc new file mode 100644 index 00000000000..1e0c7e3aae1 --- /dev/null +++ b/chromium/components/data_use_measurement/core/data_use_tracker_prefs_unittest.cc @@ -0,0 +1,173 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/data_use_measurement/core/data_use_tracker_prefs.h" + +#include <string> + +#include "base/macros.h" +#include "base/run_loop.h" +#include "base/test/simple_test_clock.h" +#include "components/data_use_measurement/core/data_use_pref_names.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/testing_pref_service.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace data_use_measurement { + +void RegisterPrefs(TestingPrefServiceSimple* test_prefs) { + test_prefs->registry()->RegisterDictionaryPref( + prefs::kDataUsedUserForeground); + test_prefs->registry()->RegisterDictionaryPref( + prefs::kDataUsedUserBackground); + test_prefs->registry()->RegisterDictionaryPref( + prefs::kDataUsedServicesForeground); + test_prefs->registry()->RegisterDictionaryPref( + prefs::kDataUsedServicesBackground); +} + +class DataUseTrackerPrefsTest { + public: + DataUseTrackerPrefsTest(base::SimpleTestClock* clock, + TestingPrefServiceSimple* test_prefs) + : data_use_tracker_prefs_(clock, test_prefs) { + // Register the prefs before accessing them. + } + + DataUseTrackerPrefsTest(const DataUseTrackerPrefsTest&) = delete; + DataUseTrackerPrefsTest& operator=(const DataUseTrackerPrefsTest&) = delete; + + DataUseTrackerPrefs* data_use_tracker_prefs() { + return &data_use_tracker_prefs_; + } + + private: + DataUseTrackerPrefs data_use_tracker_prefs_; +}; + +// Verifies that the prefs are stored correctly: The date is used as the key +// in the pref and the expired keys are removed. +TEST(DataUseTrackerPrefsTest, PrefsOnMeteredConnection) { + base::SimpleTestClock clock; + clock.SetNow(base::Time::Now()); + TestingPrefServiceSimple test_prefs; + RegisterPrefs(&test_prefs); + + // Report 2 data uses for the same day. + DataUseTrackerPrefsTest tracker_prefs_test_1(&clock, &test_prefs); + tracker_prefs_test_1.data_use_tracker_prefs()->ReportNetworkServiceDataUse( + true, true, true, 10); + EXPECT_EQ(1u, + test_prefs.GetDictionary(prefs::kDataUsedUserForeground)->size()); + tracker_prefs_test_1.data_use_tracker_prefs()->ReportNetworkServiceDataUse( + true, true, true, 10); + EXPECT_EQ(1u, + test_prefs.GetDictionary(prefs::kDataUsedUserForeground)->size()); + + // Verify other prefs are not set. + EXPECT_TRUE( + test_prefs.GetDictionary(prefs::kDataUsedUserBackground)->empty()); + EXPECT_TRUE( + test_prefs.GetDictionary(prefs::kDataUsedServicesForeground)->empty()); + EXPECT_TRUE( + test_prefs.GetDictionary(prefs::kDataUsedServicesBackground)->empty()); + + // Move clock forward 10 days. New data use reported must go in a separate + // entry in the dictionary pref. + clock.Advance(base::TimeDelta::FromDays(10)); + DataUseTrackerPrefsTest tracker_prefs_test_2(&clock, &test_prefs); + EXPECT_EQ(1u, + test_prefs.GetDictionary(prefs::kDataUsedUserForeground)->size()); + tracker_prefs_test_2.data_use_tracker_prefs()->ReportNetworkServiceDataUse( + true, true, true, 10); + EXPECT_EQ(2u, + test_prefs.GetDictionary(prefs::kDataUsedUserForeground)->size()); + + // Move clock forward 55 days. This should clean up the first entry since they + // are now 65 days older (i.e., more than 60 days old). New data use reported + // must go in a separate entry in the dictionary pref. + clock.Advance(base::TimeDelta::FromDays(55)); + DataUseTrackerPrefsTest tracker_prefs_test_3(&clock, &test_prefs); + EXPECT_EQ(1u, + test_prefs.GetDictionary(prefs::kDataUsedUserForeground)->size()); + tracker_prefs_test_2.data_use_tracker_prefs()->ReportNetworkServiceDataUse( + true, true, true, 10); + EXPECT_EQ(2u, + test_prefs.GetDictionary(prefs::kDataUsedUserForeground)->size()); +} + +// Verifies that the prefs are not updated on unmetered connections. +TEST(DataUseTrackerPrefsTest, PrefsOnUnmeteredConnection) { + base::SimpleTestClock clock; + clock.SetNow(base::Time::Now()); + TestingPrefServiceSimple test_prefs; + RegisterPrefs(&test_prefs); + + // Report 2 data uses for the same day. + DataUseTrackerPrefsTest tracker_prefs_test_1(&clock, &test_prefs); + tracker_prefs_test_1.data_use_tracker_prefs()->ReportNetworkServiceDataUse( + /*is_metered_connection=*/false, true, true, 10); + tracker_prefs_test_1.data_use_tracker_prefs()->ReportNetworkServiceDataUse( + /*is_metered_connection=*/false, true, true, 10); + + // Verify prefs are not set. + EXPECT_TRUE( + test_prefs.GetDictionary(prefs::kDataUsedUserForeground)->empty()); + EXPECT_TRUE( + test_prefs.GetDictionary(prefs::kDataUsedUserBackground)->empty()); + EXPECT_TRUE( + test_prefs.GetDictionary(prefs::kDataUsedServicesForeground)->empty()); + EXPECT_TRUE( + test_prefs.GetDictionary(prefs::kDataUsedServicesBackground)->empty()); +} + +TEST(DataUseTrackerPrefsTest, TestBasicUserForeground) { + base::SimpleTestClock clock; + clock.SetNow(base::Time::Now()); + TestingPrefServiceSimple test_prefs; + RegisterPrefs(&test_prefs); + + // Report 2 data uses for the same day. + DataUseTrackerPrefsTest tracker_prefs_test(&clock, &test_prefs); + + const struct { + bool foreground; + bool user_initiated; + std::string pref_expected_as_non_empty; + } tests[] = { + {false, false, prefs::kDataUsedServicesBackground}, + {false, true, prefs::kDataUsedUserBackground}, + {true, false, prefs::kDataUsedServicesForeground}, + {true, true, prefs::kDataUsedUserForeground}, + }; + + for (const auto& test : tests) { + test_prefs.ClearPref(prefs::kDataUsedServicesBackground); + test_prefs.ClearPref(prefs::kDataUsedUserBackground); + test_prefs.ClearPref(prefs::kDataUsedServicesForeground); + test_prefs.ClearPref(prefs::kDataUsedServicesForeground); + + tracker_prefs_test.data_use_tracker_prefs()->ReportNetworkServiceDataUse( + true, test.foreground, test.user_initiated, 10); + // Verify that the expected pref has an entry. + EXPECT_FALSE( + test_prefs.GetDictionary(test.pref_expected_as_non_empty)->empty()); + + // Verify other prefs are not set. + EXPECT_TRUE( + test.pref_expected_as_non_empty == prefs::kDataUsedUserForeground || + test_prefs.GetDictionary(prefs::kDataUsedUserForeground)->empty()); + EXPECT_TRUE( + test.pref_expected_as_non_empty == prefs::kDataUsedUserBackground || + test_prefs.GetDictionary(prefs::kDataUsedUserBackground)->empty()); + EXPECT_TRUE( + test.pref_expected_as_non_empty == prefs::kDataUsedServicesForeground || + test_prefs.GetDictionary(prefs::kDataUsedServicesForeground)->empty()); + EXPECT_TRUE( + test.pref_expected_as_non_empty == prefs::kDataUsedServicesBackground || + test_prefs.GetDictionary(prefs::kDataUsedServicesBackground)->empty()); + } +} + +} // namespace data_use_measurement |