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