• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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