1 // Copyright 2016 The Chromium Authors
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 "components/metrics/data_use_tracker.h"
6
7 #include <memory>
8 #include <string>
9
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/values.h"
13 #include "build/build_config.h"
14 #include "components/metrics/metrics_pref_names.h"
15 #include "components/prefs/scoped_user_pref_update.h"
16
17 namespace metrics {
18
19 namespace {
20
21 // Default weekly quota and allowed UMA ratio for UMA log uploads for Android.
22 // These defaults will not be used for non-Android as |DataUseTracker| will not
23 // be initialized.
24 const int kDefaultUMAWeeklyQuotaBytes = 200 * 1024; // 200KB.
25 const double kDefaultUMARatio = 0.05;
26
27 } // namespace
28
DataUseTracker(PrefService * local_state)29 DataUseTracker::DataUseTracker(PrefService* local_state)
30 : local_state_(local_state) {}
31
~DataUseTracker()32 DataUseTracker::~DataUseTracker() {}
33
34 // static
Create(PrefService * local_state)35 std::unique_ptr<DataUseTracker> DataUseTracker::Create(
36 PrefService* local_state) {
37 std::unique_ptr<DataUseTracker> data_use_tracker;
38 // Instantiate DataUseTracker only on Android. UpdateMetricsUsagePrefs() honors
39 // this rule too.
40 #if BUILDFLAG(IS_ANDROID)
41 data_use_tracker = std::make_unique<DataUseTracker>(local_state);
42 #endif
43 return data_use_tracker;
44 }
45
46 // static
RegisterPrefs(PrefRegistrySimple * registry)47 void DataUseTracker::RegisterPrefs(PrefRegistrySimple* registry) {
48 registry->RegisterDictionaryPref(metrics::prefs::kUserCellDataUse);
49 registry->RegisterDictionaryPref(metrics::prefs::kUmaCellDataUse);
50 }
51
52 // static
UpdateMetricsUsagePrefs(int message_size,bool is_cellular,bool is_metrics_service_usage,PrefService * local_state)53 void DataUseTracker::UpdateMetricsUsagePrefs(int message_size,
54 bool is_cellular,
55 bool is_metrics_service_usage,
56 PrefService* local_state) {
57 // Instantiate DataUseTracker only on Android. Create() honors this rule too.
58 #if BUILDFLAG(IS_ANDROID)
59 metrics::DataUseTracker tracker(local_state);
60 tracker.UpdateMetricsUsagePrefsInternal(message_size, is_cellular,
61 is_metrics_service_usage);
62 #endif // BUILDFLAG(IS_ANDROID)
63 }
64
UpdateMetricsUsagePrefsInternal(int message_size,bool is_cellular,bool is_metrics_service_usage)65 void DataUseTracker::UpdateMetricsUsagePrefsInternal(
66 int message_size,
67 bool is_cellular,
68 bool is_metrics_service_usage) {
69 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
70
71 if (!is_cellular)
72 return;
73
74 UpdateUsagePref(prefs::kUserCellDataUse, message_size);
75 // TODO(holte): Consider adding seperate tracking for UKM.
76 if (is_metrics_service_usage)
77 UpdateUsagePref(prefs::kUmaCellDataUse, message_size);
78 }
79
ShouldUploadLogOnCellular(int log_bytes)80 bool DataUseTracker::ShouldUploadLogOnCellular(int log_bytes) {
81 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
82
83 RemoveExpiredEntries();
84
85 int uma_total_data_use = ComputeTotalDataUse(prefs::kUmaCellDataUse);
86 int new_uma_total_data_use = log_bytes + uma_total_data_use;
87 // If the new log doesn't increase the total UMA traffic to be above the
88 // allowed quota then the log should be uploaded.
89 if (new_uma_total_data_use <= kDefaultUMAWeeklyQuotaBytes) {
90 return true;
91 }
92
93 int user_total_data_use = ComputeTotalDataUse(prefs::kUserCellDataUse);
94 // If after adding the new log the uma ratio is still under the allowed ratio
95 // then the log should be uploaded and vice versa.
96 return new_uma_total_data_use /
97 static_cast<double>(log_bytes + user_total_data_use) <=
98 kDefaultUMARatio;
99 }
100
UpdateUsagePref(const std::string & pref_name,int message_size)101 void DataUseTracker::UpdateUsagePref(const std::string& pref_name,
102 int message_size) {
103 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
104
105 ScopedDictPrefUpdate pref_updater(local_state_, pref_name);
106 std::string todays_key = GetCurrentMeasurementDateAsString();
107
108 const base::Value::Dict& user_pref_dict = local_state_->GetDict(pref_name);
109 int todays_traffic = user_pref_dict.FindInt(todays_key).value_or(0);
110 pref_updater->Set(todays_key, todays_traffic + message_size);
111 }
112
RemoveExpiredEntries()113 void DataUseTracker::RemoveExpiredEntries() {
114 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
115 RemoveExpiredEntriesForPref(prefs::kUmaCellDataUse);
116 RemoveExpiredEntriesForPref(prefs::kUserCellDataUse);
117 }
118
RemoveExpiredEntriesForPref(const std::string & pref_name)119 void DataUseTracker::RemoveExpiredEntriesForPref(const std::string& pref_name) {
120 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
121
122 const base::Value::Dict& user_pref_dict = local_state_->GetDict(pref_name);
123 const base::Time current_date = GetCurrentMeasurementDate();
124 const base::Time week_ago = current_date - base::Days(7);
125
126 base::Value::Dict user_pref_new_dict;
127 for (const auto it : user_pref_dict) {
128 base::Time key_date;
129 if (base::Time::FromUTCString(it.first.c_str(), &key_date) &&
130 key_date > week_ago) {
131 user_pref_new_dict.Set(it.first, it.second.Clone());
132 }
133 }
134 local_state_->SetDict(pref_name, std::move(user_pref_new_dict));
135 }
136
137 // Note: We compute total data use regardless of what is the current date. In
138 // scenario when user travels back in time zone and current date becomes earlier
139 // than latest registered date in perf, we still count that in total use as user
140 // actually used that data.
ComputeTotalDataUse(const std::string & pref_name)141 int DataUseTracker::ComputeTotalDataUse(const std::string& pref_name) {
142 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
143
144 int total_data_use = 0;
145 const base::Value::Dict& pref_dict = local_state_->GetDict(pref_name);
146 for (const auto it : pref_dict) {
147 total_data_use += it.second.GetIfInt().value_or(0);
148 }
149 return total_data_use;
150 }
151
GetCurrentMeasurementDate() const152 base::Time DataUseTracker::GetCurrentMeasurementDate() const {
153 return base::Time::Now().LocalMidnight();
154 }
155
GetCurrentMeasurementDateAsString() const156 std::string DataUseTracker::GetCurrentMeasurementDateAsString() const {
157 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
158
159 base::Time::Exploded today_exploded;
160 GetCurrentMeasurementDate().LocalExplode(&today_exploded);
161 return base::StringPrintf("%04d-%02d-%02d", today_exploded.year,
162 today_exploded.month, today_exploded.day_of_month);
163 }
164
165 } // namespace metrics
166