Home | History | Annotate | Download | only in metrics
      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/time/time.h"
     16 #include "components/metrics/cloned_install_detector.h"
     17 #include "components/metrics/machine_id_provider.h"
     18 #include "components/metrics/metrics_pref_names.h"
     19 #include "components/metrics/metrics_switches.h"
     20 #include "components/variations/caching_permuted_entropy_provider.h"
     21 
     22 namespace metrics {
     23 
     24 namespace {
     25 
     26 // The argument used to generate a non-identifying entropy source. We want no
     27 // more than 13 bits of entropy, so use this max to return a number in the range
     28 // [0, 7999] as the entropy source (12.97 bits of entropy).
     29 const int kMaxLowEntropySize = 8000;
     30 
     31 // Default prefs value for prefs::kMetricsLowEntropySource to indicate that
     32 // the value has not yet been set.
     33 const int kLowEntropySourceNotSet = -1;
     34 
     35 // Generates a new non-identifying entropy source used to seed persistent
     36 // activities.
     37 int GenerateLowEntropySource() {
     38   return base::RandInt(0, kMaxLowEntropySize - 1);
     39 }
     40 
     41 }  // namespace
     42 
     43 // static
     44 bool MetricsStateManager::instance_exists_ = false;
     45 
     46 MetricsStateManager::MetricsStateManager(
     47     PrefService* local_state,
     48     const base::Callback<bool(void)>& is_reporting_enabled_callback)
     49     : local_state_(local_state),
     50       is_reporting_enabled_callback_(is_reporting_enabled_callback),
     51       low_entropy_source_(kLowEntropySourceNotSet),
     52       entropy_source_returned_(ENTROPY_SOURCE_NONE) {
     53   ResetMetricsIDsIfNecessary();
     54   if (IsMetricsReportingEnabled())
     55     ForceClientIdCreation();
     56 
     57   DCHECK(!instance_exists_);
     58   instance_exists_ = true;
     59 }
     60 
     61 MetricsStateManager::~MetricsStateManager() {
     62   DCHECK(instance_exists_);
     63   instance_exists_ = false;
     64 }
     65 
     66 bool MetricsStateManager::IsMetricsReportingEnabled() {
     67   return is_reporting_enabled_callback_.Run();
     68 }
     69 
     70 void MetricsStateManager::ForceClientIdCreation() {
     71   if (!client_id_.empty())
     72     return;
     73 
     74   client_id_ = local_state_->GetString(prefs::kMetricsClientID);
     75   if (!client_id_.empty())
     76     return;
     77 
     78   client_id_ = base::GenerateGUID();
     79   local_state_->SetString(prefs::kMetricsClientID, client_id_);
     80 
     81   if (local_state_->GetString(prefs::kMetricsOldClientID).empty()) {
     82     // Record the timestamp of when the user opted in to UMA.
     83     local_state_->SetInt64(prefs::kMetricsReportingEnabledTimestamp,
     84                            base::Time::Now().ToTimeT());
     85   } else {
     86     UMA_HISTOGRAM_BOOLEAN("UMA.ClientIdMigrated", true);
     87   }
     88   local_state_->ClearPref(prefs::kMetricsOldClientID);
     89 }
     90 
     91 void MetricsStateManager::CheckForClonedInstall(
     92     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
     93   DCHECK(!cloned_install_detector_);
     94 
     95   MachineIdProvider* provider = MachineIdProvider::CreateInstance();
     96   if (!provider)
     97     return;
     98 
     99   cloned_install_detector_.reset(new ClonedInstallDetector(provider));
    100   cloned_install_detector_->CheckForClonedInstall(local_state_, task_runner);
    101 }
    102 
    103 scoped_ptr<const base::FieldTrial::EntropyProvider>
    104 MetricsStateManager::CreateEntropyProvider() {
    105   // For metrics reporting-enabled users, we combine the client ID and low
    106   // entropy source to get the final entropy source. Otherwise, only use the low
    107   // entropy source.
    108   // This has two useful properties:
    109   //  1) It makes the entropy source less identifiable for parties that do not
    110   //     know the low entropy source.
    111   //  2) It makes the final entropy source resettable.
    112   const int low_entropy_source_value = GetLowEntropySource();
    113   UMA_HISTOGRAM_SPARSE_SLOWLY("UMA.LowEntropySourceValue",
    114                               low_entropy_source_value);
    115   if (IsMetricsReportingEnabled()) {
    116     if (entropy_source_returned_ == ENTROPY_SOURCE_NONE)
    117       entropy_source_returned_ = ENTROPY_SOURCE_HIGH;
    118     DCHECK_EQ(ENTROPY_SOURCE_HIGH, entropy_source_returned_);
    119     const std::string high_entropy_source =
    120         client_id_ + base::IntToString(low_entropy_source_value);
    121     return scoped_ptr<const base::FieldTrial::EntropyProvider>(
    122         new SHA1EntropyProvider(high_entropy_source));
    123   }
    124 
    125   if (entropy_source_returned_ == ENTROPY_SOURCE_NONE)
    126     entropy_source_returned_ = ENTROPY_SOURCE_LOW;
    127   DCHECK_EQ(ENTROPY_SOURCE_LOW, entropy_source_returned_);
    128 
    129 #if defined(OS_ANDROID) || defined(OS_IOS)
    130   return scoped_ptr<const base::FieldTrial::EntropyProvider>(
    131       new CachingPermutedEntropyProvider(local_state_,
    132                                          low_entropy_source_value,
    133                                          kMaxLowEntropySize));
    134 #else
    135   return scoped_ptr<const base::FieldTrial::EntropyProvider>(
    136       new PermutedEntropyProvider(low_entropy_source_value,
    137                                   kMaxLowEntropySize));
    138 #endif
    139 }
    140 
    141 // static
    142 scoped_ptr<MetricsStateManager> MetricsStateManager::Create(
    143     PrefService* local_state,
    144     const base::Callback<bool(void)>& is_reporting_enabled_callback) {
    145   scoped_ptr<MetricsStateManager> result;
    146   // Note: |instance_exists_| is updated in the constructor and destructor.
    147   if (!instance_exists_) {
    148     result.reset(
    149         new MetricsStateManager(local_state, is_reporting_enabled_callback));
    150   }
    151   return result.Pass();
    152 }
    153 
    154 // static
    155 void MetricsStateManager::RegisterPrefs(PrefRegistrySimple* registry) {
    156   registry->RegisterBooleanPref(prefs::kMetricsResetIds, false);
    157   registry->RegisterStringPref(prefs::kMetricsClientID, std::string());
    158   registry->RegisterInt64Pref(prefs::kMetricsReportingEnabledTimestamp, 0);
    159   registry->RegisterIntegerPref(prefs::kMetricsLowEntropySource,
    160                                 kLowEntropySourceNotSet);
    161 
    162   ClonedInstallDetector::RegisterPrefs(registry);
    163   CachingPermutedEntropyProvider::RegisterPrefs(registry);
    164 
    165   // TODO(asvitkine): Remove these once a couple of releases have passed.
    166   // http://crbug.com/357704
    167   registry->RegisterStringPref(prefs::kMetricsOldClientID, std::string());
    168   registry->RegisterIntegerPref(prefs::kMetricsOldLowEntropySource, 0);
    169 }
    170 
    171 int MetricsStateManager::GetLowEntropySource() {
    172   // Note that the default value for the low entropy source and the default pref
    173   // value are both kLowEntropySourceNotSet, which is used to identify if the
    174   // value has been set or not.
    175   if (low_entropy_source_ != kLowEntropySourceNotSet)
    176     return low_entropy_source_;
    177 
    178   const CommandLine* command_line(CommandLine::ForCurrentProcess());
    179   // Only try to load the value from prefs if the user did not request a
    180   // reset.
    181   // Otherwise, skip to generating a new value.
    182   if (!command_line->HasSwitch(switches::kResetVariationState)) {
    183     int value = local_state_->GetInteger(prefs::kMetricsLowEntropySource);
    184     // If the value is outside the [0, kMaxLowEntropySize) range, re-generate
    185     // it below.
    186     if (value >= 0 && value < kMaxLowEntropySize) {
    187       low_entropy_source_ = value;
    188       UMA_HISTOGRAM_BOOLEAN("UMA.GeneratedLowEntropySource", false);
    189       return low_entropy_source_;
    190     }
    191   }
    192 
    193   UMA_HISTOGRAM_BOOLEAN("UMA.GeneratedLowEntropySource", true);
    194   low_entropy_source_ = GenerateLowEntropySource();
    195   local_state_->SetInteger(prefs::kMetricsLowEntropySource,
    196                            low_entropy_source_);
    197   local_state_->ClearPref(prefs::kMetricsOldLowEntropySource);
    198   CachingPermutedEntropyProvider::ClearCache(local_state_);
    199 
    200   return low_entropy_source_;
    201 }
    202 
    203 void MetricsStateManager::ResetMetricsIDsIfNecessary() {
    204   if (!local_state_->GetBoolean(prefs::kMetricsResetIds))
    205     return;
    206 
    207   UMA_HISTOGRAM_BOOLEAN("UMA.MetricsIDsReset", true);
    208 
    209   DCHECK(client_id_.empty());
    210   DCHECK_EQ(kLowEntropySourceNotSet, low_entropy_source_);
    211 
    212   local_state_->ClearPref(prefs::kMetricsClientID);
    213   local_state_->ClearPref(prefs::kMetricsLowEntropySource);
    214   local_state_->ClearPref(prefs::kMetricsResetIds);
    215 }
    216 
    217 }  // namespace metrics
    218