Home | History | Annotate | Download | only in metrics
      1 // Copyright 2013 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 "chrome/common/metrics/caching_permuted_entropy_provider.h"
      6 
      7 #include <string>
      8 
      9 #include "base/base64.h"
     10 #include "base/logging.h"
     11 #include "base/prefs/pref_registry_simple.h"
     12 #include "base/prefs/pref_service.h"
     13 #include "chrome/common/pref_names.h"
     14 
     15 namespace metrics {
     16 
     17 CachingPermutedEntropyProvider::CachingPermutedEntropyProvider(
     18     PrefService* local_state,
     19     uint16 low_entropy_source,
     20     size_t low_entropy_source_max)
     21     : PermutedEntropyProvider(low_entropy_source, low_entropy_source_max),
     22       local_state_(local_state) {
     23   ReadFromLocalState();
     24 }
     25 
     26 CachingPermutedEntropyProvider::~CachingPermutedEntropyProvider() {
     27 }
     28 
     29 // static
     30 void CachingPermutedEntropyProvider::RegisterPrefs(
     31     PrefRegistrySimple* registry) {
     32   registry->RegisterStringPref(prefs::kMetricsPermutedEntropyCache,
     33                                std::string());
     34 }
     35 
     36 // static
     37 void CachingPermutedEntropyProvider::ClearCache(PrefService* local_state) {
     38   local_state->ClearPref(prefs::kMetricsPermutedEntropyCache);
     39 }
     40 
     41 uint16 CachingPermutedEntropyProvider::GetPermutedValue(
     42     uint32 randomization_seed) const {
     43   DCHECK(thread_checker_.CalledOnValidThread());
     44 
     45   uint16 value = 0;
     46   if (!FindValue(randomization_seed, &value)) {
     47     value = PermutedEntropyProvider::GetPermutedValue(randomization_seed);
     48     AddToCache(randomization_seed, value);
     49   }
     50   return value;
     51 }
     52 
     53 void CachingPermutedEntropyProvider::ReadFromLocalState() const {
     54   const std::string base64_cache_data =
     55       local_state_->GetString(prefs::kMetricsPermutedEntropyCache);
     56   std::string cache_data;
     57   if (!base::Base64Decode(base64_cache_data, &cache_data) ||
     58       !cache_.ParseFromString(cache_data)) {
     59     local_state_->ClearPref(prefs::kMetricsPermutedEntropyCache);
     60     NOTREACHED();
     61   }
     62 }
     63 
     64 void CachingPermutedEntropyProvider::UpdateLocalState() const {
     65   std::string serialized;
     66   cache_.SerializeToString(&serialized);
     67 
     68   std::string base64_encoded;
     69   if (!base::Base64Encode(serialized, &base64_encoded)) {
     70     NOTREACHED();
     71     return;
     72   }
     73   local_state_->SetString(prefs::kMetricsPermutedEntropyCache, base64_encoded);
     74 }
     75 
     76 void CachingPermutedEntropyProvider::AddToCache(uint32 randomization_seed,
     77                                                 uint16 value) const {
     78   PermutedEntropyCache::Entry* entry;
     79   const int kMaxSize = 25;
     80   if (cache_.entry_size() >= kMaxSize) {
     81     // If the cache is full, evict the first entry, swapping later entries in
     82     // to take its place. This effectively creates a FIFO cache, which is good
     83     // enough here because the expectation is that there shouldn't be more than
     84     // |kMaxSize| field trials at any given time, so eviction should happen very
     85     // rarely, only as new trials are introduced, evicting old expired trials.
     86     for (int i = 1; i < kMaxSize; ++i)
     87       cache_.mutable_entry()->SwapElements(i - 1, i);
     88     entry = cache_.mutable_entry(kMaxSize - 1);
     89   } else {
     90     entry = cache_.add_entry();
     91   }
     92 
     93   entry->set_randomization_seed(randomization_seed);
     94   entry->set_value(value);
     95 
     96   UpdateLocalState();
     97 }
     98 
     99 bool CachingPermutedEntropyProvider::FindValue(uint32 randomization_seed,
    100                                                uint16* value) const {
    101   for (int i = 0; i < cache_.entry_size(); ++i) {
    102     if (cache_.entry(i).randomization_seed() == randomization_seed) {
    103       *value = cache_.entry(i).value();
    104       return true;
    105     }
    106   }
    107   return false;
    108 }
    109 
    110 }  // namespace metrics
    111