• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 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/entropy_state.h"
6 
7 #include "base/command_line.h"
8 #include "base/metrics/histogram_functions.h"
9 #include "base/rand_util.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "components/metrics/metrics_pref_names.h"
12 #include "components/metrics/metrics_switches.h"
13 #include "components/prefs/pref_service.h"
14 
15 #if BUILDFLAG(IS_ANDROID)
16 #include "base/android/jni_android.h"
17 #include "components/metrics/jni_headers/LowEntropySource_jni.h"
18 #endif  // BUILDFLAG(IS_ANDROID)
19 
20 namespace metrics {
21 
22 namespace {
23 
24 // Generates a new non-identifying entropy source used to seed persistent
25 // activities. Make it static so that the new low entropy source value will
26 // only be generated on first access. And thus, even though we may write the
27 // new low entropy source value to prefs multiple times, it stays the same
28 // value.
GenerateLowEntropySource()29 int GenerateLowEntropySource() {
30 #if BUILDFLAG(IS_ANDROID)
31   // Note: As in the non-Android case below, the Java implementation also uses
32   // a static cache, so subsequent invocations will return the same value.
33   JNIEnv* env = base::android::AttachCurrentThread();
34   return Java_LowEntropySource_generateLowEntropySource(env);
35 #else
36   static const int low_entropy_source =
37       base::RandInt(0, EntropyState::kMaxLowEntropySize - 1);
38   return low_entropy_source;
39 #endif  // BUILDFLAG(IS_ANDROID)
40 }
41 
42 // Generates a new non-identifying low entropy source using the same method
43 // that's used for the actual low entropy source. This one, however, is only
44 // used for statistical validation, and *not* for randomization or experiment
45 // assignment.
GeneratePseudoLowEntropySource()46 int GeneratePseudoLowEntropySource() {
47 #if BUILDFLAG(IS_ANDROID)
48   // Note: As in the non-Android case below, the Java implementation also uses
49   // a static cache, so subsequent invocations will return the same value.
50   JNIEnv* env = base::android::AttachCurrentThread();
51   return Java_LowEntropySource_generatePseudoLowEntropySource(env);
52 #else
53   static const int pseudo_low_entropy_source =
54       base::RandInt(0, EntropyState::kMaxLowEntropySize - 1);
55   return pseudo_low_entropy_source;
56 #endif  // BUILDFLAG(IS_ANDROID)
57 }
58 
59 }  // namespace
60 
EntropyState(PrefService * local_state)61 EntropyState::EntropyState(PrefService* local_state)
62     : local_state_(local_state) {}
63 
64 // static
65 constexpr int EntropyState::kLowEntropySourceNotSet;
66 
67 // static
ClearPrefs(PrefService * local_state)68 void EntropyState::ClearPrefs(PrefService* local_state) {
69   local_state->ClearPref(prefs::kMetricsLowEntropySource);
70   local_state->ClearPref(prefs::kMetricsOldLowEntropySource);
71   local_state->ClearPref(prefs::kMetricsPseudoLowEntropySource);
72 }
73 
74 // static
RegisterPrefs(PrefRegistrySimple * registry)75 void EntropyState::RegisterPrefs(PrefRegistrySimple* registry) {
76   registry->RegisterIntegerPref(prefs::kMetricsLowEntropySource,
77                                 kLowEntropySourceNotSet);
78   registry->RegisterIntegerPref(prefs::kMetricsOldLowEntropySource,
79                                 kLowEntropySourceNotSet);
80   registry->RegisterIntegerPref(prefs::kMetricsPseudoLowEntropySource,
81                                 kLowEntropySourceNotSet);
82 }
83 
GetHighEntropySource(const std::string & initial_client_id)84 std::string EntropyState::GetHighEntropySource(
85     const std::string& initial_client_id) {
86   DCHECK(!initial_client_id.empty());
87   // For metrics reporting-enabled users, we combine the client ID and low
88   // entropy source to get the final entropy source.
89   // This has two useful properties:
90   //  1) It makes the entropy source less identifiable for parties that do not
91   //     know the low entropy source.
92   //  2) It makes the final entropy source resettable.
93 
94   // If this install has an old low entropy source, continue using it, to avoid
95   // changing the group assignments of studies using high entropy. New installs
96   // only have the new low entropy source. If the number of installs with old
97   // sources ever becomes small enough (see UMA.LowEntropySourceValue), we could
98   // remove it, and just use the new source here.
99   int low_entropy_source = GetOldLowEntropySource();
100   if (low_entropy_source == kLowEntropySourceNotSet)
101     low_entropy_source = GetLowEntropySource();
102 
103   return initial_client_id + base::NumberToString(low_entropy_source);
104 }
105 
GetLowEntropySource()106 int EntropyState::GetLowEntropySource() {
107   UpdateLowEntropySources();
108   return low_entropy_source_;
109 }
110 
GetPseudoLowEntropySource()111 int EntropyState::GetPseudoLowEntropySource() {
112   UpdateLowEntropySources();
113   return pseudo_low_entropy_source_;
114 }
115 
GetOldLowEntropySource()116 int EntropyState::GetOldLowEntropySource() {
117   UpdateLowEntropySources();
118   return old_low_entropy_source_;
119 }
120 
UpdateLowEntropySources()121 void EntropyState::UpdateLowEntropySources() {
122   // The default value for |low_entropy_source_| and the default pref value are
123   // both |kLowEntropySourceNotSet|, which indicates the value has not been set.
124   if (low_entropy_source_ != kLowEntropySourceNotSet &&
125       pseudo_low_entropy_source_ != kLowEntropySourceNotSet)
126     return;
127 
128   const base::CommandLine* command_line(base::CommandLine::ForCurrentProcess());
129   // Only try to load the value from prefs if the user did not request a reset.
130   // Otherwise, skip to generating a new value. We would have already returned
131   // if both |low_entropy_source_| and |pseudo_low_entropy_source_| were set,
132   // ensuring we only do this reset on the first call to
133   // UpdateLowEntropySources().
134   if (!command_line->HasSwitch(switches::kResetVariationState)) {
135     int new_pref = local_state_->GetInteger(prefs::kMetricsLowEntropySource);
136     if (IsValidLowEntropySource(new_pref))
137       low_entropy_source_ = new_pref;
138     int old_pref = local_state_->GetInteger(prefs::kMetricsOldLowEntropySource);
139     if (IsValidLowEntropySource(old_pref))
140       old_low_entropy_source_ = old_pref;
141     int pseudo_pref =
142         local_state_->GetInteger(prefs::kMetricsPseudoLowEntropySource);
143     if (IsValidLowEntropySource(pseudo_pref))
144       pseudo_low_entropy_source_ = pseudo_pref;
145   }
146 
147   // If the new source is missing or corrupt (or requested to be reset), then
148   // (re)create it. Don't bother recreating the old source if it's corrupt,
149   // because we only keep the old source around for consistency, and we can't
150   // maintain a consistent value if we recreate it.
151   if (low_entropy_source_ == kLowEntropySourceNotSet) {
152     low_entropy_source_ = GenerateLowEntropySource();
153     DCHECK(IsValidLowEntropySource(low_entropy_source_));
154     local_state_->SetInteger(prefs::kMetricsLowEntropySource,
155                              low_entropy_source_);
156   }
157 
158   // If the pseudo source is missing or corrupt (or requested to be reset), then
159   // (re)create it. Don't bother recreating the old source if it's corrupt,
160   // because we only keep the old source around for consistency, and we can't
161   // maintain a consistent value if we recreate it.
162   if (pseudo_low_entropy_source_ == kLowEntropySourceNotSet) {
163     pseudo_low_entropy_source_ = GeneratePseudoLowEntropySource();
164     DCHECK(IsValidLowEntropySource(pseudo_low_entropy_source_));
165     local_state_->SetInteger(prefs::kMetricsPseudoLowEntropySource,
166                              pseudo_low_entropy_source_);
167   }
168 
169   // If the old source was present but corrupt (or requested to be reset), then
170   // we'll never use it again, so delete it.
171   if (old_low_entropy_source_ == kLowEntropySourceNotSet &&
172       local_state_->HasPrefPath(prefs::kMetricsOldLowEntropySource)) {
173     local_state_->ClearPref(prefs::kMetricsOldLowEntropySource);
174   }
175 
176   DCHECK_NE(low_entropy_source_, kLowEntropySourceNotSet);
177 }
178 
179 // static
IsValidLowEntropySource(int value)180 bool EntropyState::IsValidLowEntropySource(int value) {
181   return value >= 0 && value < kMaxLowEntropySize;
182 }
183 
184 }  // namespace metrics
185