Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / components / metrics / metrics_state_manager.cc
1 // Copyright 2014 The Chromium Authors. All rights reserved.
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/metrics_state_manager.h"
6
7 #include "base/command_line.h"
8 #include "base/guid.h"
9 #include "base/metrics/histogram.h"
10 #include "base/metrics/sparse_histogram.h"
11 #include "base/prefs/pref_registry_simple.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/rand_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/threading/thread_restrictions.h"
16 #include "base/time/time.h"
17 #include "components/metrics/cloned_install_detector.h"
18 #include "components/metrics/machine_id_provider.h"
19 #include "components/metrics/metrics_pref_names.h"
20 #include "components/metrics/metrics_switches.h"
21 #include "components/variations/caching_permuted_entropy_provider.h"
22
23 namespace metrics {
24
25 namespace {
26
27 // The argument used to generate a non-identifying entropy source. We want no
28 // more than 13 bits of entropy, so use this max to return a number in the range
29 // [0, 7999] as the entropy source (12.97 bits of entropy).
30 const int kMaxLowEntropySize = 8000;
31
32 // Default prefs value for prefs::kMetricsLowEntropySource to indicate that
33 // the value has not yet been set.
34 const int kLowEntropySourceNotSet = -1;
35
36 // Generates a new non-identifying entropy source used to seed persistent
37 // activities.
38 int GenerateLowEntropySource() {
39   return base::RandInt(0, kMaxLowEntropySize - 1);
40 }
41
42 }  // namespace
43
44 // static
45 bool MetricsStateManager::instance_exists_ = false;
46
47 MetricsStateManager::MetricsStateManager(
48     PrefService* local_state,
49     const base::Callback<bool(void)>& is_reporting_enabled_callback,
50     const StoreClientInfoCallback& store_client_info,
51     const LoadClientInfoCallback& retrieve_client_info)
52     : local_state_(local_state),
53       is_reporting_enabled_callback_(is_reporting_enabled_callback),
54       store_client_info_(store_client_info),
55       load_client_info_(retrieve_client_info),
56       low_entropy_source_(kLowEntropySourceNotSet),
57       entropy_source_returned_(ENTROPY_SOURCE_NONE) {
58   ResetMetricsIDsIfNecessary();
59   if (IsMetricsReportingEnabled())
60     ForceClientIdCreation();
61
62   DCHECK(!instance_exists_);
63   instance_exists_ = true;
64 }
65
66 MetricsStateManager::~MetricsStateManager() {
67   DCHECK(instance_exists_);
68   instance_exists_ = false;
69 }
70
71 bool MetricsStateManager::IsMetricsReportingEnabled() {
72   return is_reporting_enabled_callback_.Run();
73 }
74
75 void MetricsStateManager::ForceClientIdCreation() {
76   if (!client_id_.empty())
77     return;
78
79   client_id_ = local_state_->GetString(prefs::kMetricsClientID);
80   if (!client_id_.empty()) {
81     // It is technically sufficient to only save a backup of the client id when
82     // it is initially generated below, but since the backup was only introduced
83     // in M38, seed it explicitly from here for some time.
84     BackUpCurrentClientInfo();
85     return;
86   }
87
88   const scoped_ptr<ClientInfo> client_info_backup =
89       LoadClientInfoAndMaybeMigrate();
90   if (client_info_backup) {
91     client_id_ = client_info_backup->client_id;
92
93     const base::Time now = base::Time::Now();
94
95     // Save the recovered client id and also try to reinstantiate the backup
96     // values for the dates corresponding with that client id in order to avoid
97     // weird scenarios where we could report an old client id with a recent
98     // install date.
99     local_state_->SetString(prefs::kMetricsClientID, client_id_);
100     local_state_->SetInt64(prefs::kInstallDate,
101                            client_info_backup->installation_date != 0
102                                ? client_info_backup->installation_date
103                                : now.ToTimeT());
104     local_state_->SetInt64(prefs::kMetricsReportingEnabledTimestamp,
105                            client_info_backup->reporting_enabled_date != 0
106                                ? client_info_backup->reporting_enabled_date
107                                : now.ToTimeT());
108
109     base::TimeDelta recovered_installation_age;
110     if (client_info_backup->installation_date != 0) {
111       recovered_installation_age =
112           now - base::Time::FromTimeT(client_info_backup->installation_date);
113     }
114     UMA_HISTOGRAM_COUNTS_10000("UMA.ClientIdBackupRecoveredWithAge",
115                                recovered_installation_age.InHours());
116
117     // Flush the backup back to persistent storage in case we re-generated
118     // missing data above.
119     BackUpCurrentClientInfo();
120     return;
121   }
122
123   // Failing attempts at getting an existing client ID, generate a new one.
124   client_id_ = base::GenerateGUID();
125   local_state_->SetString(prefs::kMetricsClientID, client_id_);
126
127   if (local_state_->GetString(prefs::kMetricsOldClientID).empty()) {
128     // Record the timestamp of when the user opted in to UMA.
129     local_state_->SetInt64(prefs::kMetricsReportingEnabledTimestamp,
130                            base::Time::Now().ToTimeT());
131   } else {
132     UMA_HISTOGRAM_BOOLEAN("UMA.ClientIdMigrated", true);
133   }
134   local_state_->ClearPref(prefs::kMetricsOldClientID);
135
136   BackUpCurrentClientInfo();
137 }
138
139 void MetricsStateManager::CheckForClonedInstall(
140     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
141   DCHECK(!cloned_install_detector_);
142
143   MachineIdProvider* provider = MachineIdProvider::CreateInstance();
144   if (!provider)
145     return;
146
147   cloned_install_detector_.reset(new ClonedInstallDetector(provider));
148   cloned_install_detector_->CheckForClonedInstall(local_state_, task_runner);
149 }
150
151 scoped_ptr<const base::FieldTrial::EntropyProvider>
152 MetricsStateManager::CreateEntropyProvider() {
153   // For metrics reporting-enabled users, we combine the client ID and low
154   // entropy source to get the final entropy source. Otherwise, only use the low
155   // entropy source.
156   // This has two useful properties:
157   //  1) It makes the entropy source less identifiable for parties that do not
158   //     know the low entropy source.
159   //  2) It makes the final entropy source resettable.
160   const int low_entropy_source_value = GetLowEntropySource();
161   UMA_HISTOGRAM_SPARSE_SLOWLY("UMA.LowEntropySourceValue",
162                               low_entropy_source_value);
163   if (IsMetricsReportingEnabled()) {
164     if (entropy_source_returned_ == ENTROPY_SOURCE_NONE)
165       entropy_source_returned_ = ENTROPY_SOURCE_HIGH;
166     const std::string high_entropy_source =
167         client_id_ + base::IntToString(low_entropy_source_value);
168     return scoped_ptr<const base::FieldTrial::EntropyProvider>(
169         new SHA1EntropyProvider(high_entropy_source));
170   }
171
172   if (entropy_source_returned_ == ENTROPY_SOURCE_NONE)
173     entropy_source_returned_ = ENTROPY_SOURCE_LOW;
174
175 #if defined(OS_ANDROID) || defined(OS_IOS)
176   return scoped_ptr<const base::FieldTrial::EntropyProvider>(
177       new CachingPermutedEntropyProvider(local_state_,
178                                          low_entropy_source_value,
179                                          kMaxLowEntropySize));
180 #else
181   return scoped_ptr<const base::FieldTrial::EntropyProvider>(
182       new PermutedEntropyProvider(low_entropy_source_value,
183                                   kMaxLowEntropySize));
184 #endif
185 }
186
187 // static
188 scoped_ptr<MetricsStateManager> MetricsStateManager::Create(
189     PrefService* local_state,
190     const base::Callback<bool(void)>& is_reporting_enabled_callback,
191     const StoreClientInfoCallback& store_client_info,
192     const LoadClientInfoCallback& retrieve_client_info) {
193   scoped_ptr<MetricsStateManager> result;
194   // Note: |instance_exists_| is updated in the constructor and destructor.
195   if (!instance_exists_) {
196     result.reset(new MetricsStateManager(local_state,
197                                          is_reporting_enabled_callback,
198                                          store_client_info,
199                                          retrieve_client_info));
200   }
201   return result.Pass();
202 }
203
204 // static
205 void MetricsStateManager::RegisterPrefs(PrefRegistrySimple* registry) {
206   registry->RegisterBooleanPref(prefs::kMetricsResetIds, false);
207   registry->RegisterStringPref(prefs::kMetricsClientID, std::string());
208   registry->RegisterInt64Pref(prefs::kMetricsReportingEnabledTimestamp, 0);
209   registry->RegisterIntegerPref(prefs::kMetricsLowEntropySource,
210                                 kLowEntropySourceNotSet);
211
212   ClonedInstallDetector::RegisterPrefs(registry);
213   CachingPermutedEntropyProvider::RegisterPrefs(registry);
214
215   // TODO(asvitkine): Remove these once a couple of releases have passed.
216   // http://crbug.com/357704
217   registry->RegisterStringPref(prefs::kMetricsOldClientID, std::string());
218   registry->RegisterIntegerPref(prefs::kMetricsOldLowEntropySource, 0);
219 }
220
221 void MetricsStateManager::BackUpCurrentClientInfo() {
222   // TODO(gayane): Eliminate use of ScopedAllowIO. crbug.com/413783
223   base::ThreadRestrictions::ScopedAllowIO allow_io;
224
225   ClientInfo client_info;
226   client_info.client_id = client_id_;
227   client_info.installation_date = local_state_->GetInt64(prefs::kInstallDate);
228   client_info.reporting_enabled_date =
229       local_state_->GetInt64(prefs::kMetricsReportingEnabledTimestamp);
230   store_client_info_.Run(client_info);
231 }
232
233 scoped_ptr<ClientInfo> MetricsStateManager::LoadClientInfoAndMaybeMigrate() {
234   scoped_ptr<metrics::ClientInfo> client_info = load_client_info_.Run();
235
236   // Prior to 2014-07, the client ID was stripped of its dashes before being
237   // saved. Migrate back to a proper GUID if this is the case. This migration
238   // code can be removed in M41+.
239   const size_t kGUIDLengthWithoutDashes = 32U;
240   if (client_info &&
241       client_info->client_id.length() == kGUIDLengthWithoutDashes) {
242     DCHECK(client_info->client_id.find('-') == std::string::npos);
243
244     std::string client_id_with_dashes;
245     client_id_with_dashes.reserve(kGUIDLengthWithoutDashes + 4U);
246     std::string::const_iterator client_id_it = client_info->client_id.begin();
247     for (size_t i = 0; i < kGUIDLengthWithoutDashes + 4U; ++i) {
248       if (i == 8U || i == 13U || i == 18U || i == 23U) {
249         client_id_with_dashes.push_back('-');
250       } else {
251         client_id_with_dashes.push_back(*client_id_it);
252         ++client_id_it;
253       }
254     }
255     DCHECK(client_id_it == client_info->client_id.end());
256     client_info->client_id.assign(client_id_with_dashes);
257   }
258
259   // The GUID retrieved (and possibly fixed above) should be valid unless
260   // retrieval failed.
261   DCHECK(!client_info || base::IsValidGUID(client_info->client_id));
262
263   return client_info.Pass();
264 }
265
266 int MetricsStateManager::GetLowEntropySource() {
267   // Note that the default value for the low entropy source and the default pref
268   // value are both kLowEntropySourceNotSet, which is used to identify if the
269   // value has been set or not.
270   if (low_entropy_source_ != kLowEntropySourceNotSet)
271     return low_entropy_source_;
272
273   const CommandLine* command_line(CommandLine::ForCurrentProcess());
274   // Only try to load the value from prefs if the user did not request a
275   // reset.
276   // Otherwise, skip to generating a new value.
277   if (!command_line->HasSwitch(switches::kResetVariationState)) {
278     int value = local_state_->GetInteger(prefs::kMetricsLowEntropySource);
279     // If the value is outside the [0, kMaxLowEntropySize) range, re-generate
280     // it below.
281     if (value >= 0 && value < kMaxLowEntropySize) {
282       low_entropy_source_ = value;
283       UMA_HISTOGRAM_BOOLEAN("UMA.GeneratedLowEntropySource", false);
284       return low_entropy_source_;
285     }
286   }
287
288   UMA_HISTOGRAM_BOOLEAN("UMA.GeneratedLowEntropySource", true);
289   low_entropy_source_ = GenerateLowEntropySource();
290   local_state_->SetInteger(prefs::kMetricsLowEntropySource,
291                            low_entropy_source_);
292   local_state_->ClearPref(prefs::kMetricsOldLowEntropySource);
293   CachingPermutedEntropyProvider::ClearCache(local_state_);
294
295   return low_entropy_source_;
296 }
297
298 void MetricsStateManager::ResetMetricsIDsIfNecessary() {
299   if (!local_state_->GetBoolean(prefs::kMetricsResetIds))
300     return;
301
302   UMA_HISTOGRAM_BOOLEAN("UMA.MetricsIDsReset", true);
303
304   DCHECK(client_id_.empty());
305   DCHECK_EQ(kLowEntropySourceNotSet, low_entropy_source_);
306
307   local_state_->ClearPref(prefs::kMetricsClientID);
308   local_state_->ClearPref(prefs::kMetricsLowEntropySource);
309   local_state_->ClearPref(prefs::kMetricsResetIds);
310
311   // Also clear the backed up client info.
312   store_client_info_.Run(ClientInfo());
313 }
314
315 }  // namespace metrics