• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 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 package org.chromium.components.metrics;
6 
7 import android.content.SharedPreferences;
8 
9 import androidx.annotation.MainThread;
10 
11 import org.jni_zero.CalledByNative;
12 
13 import org.chromium.base.ContextUtils;
14 import org.chromium.base.ThreadUtils;
15 
16 import java.util.Random;
17 
18 /**
19  * Generates a new non-identifying entropy source used to seed persistent activities. Has a static
20  * cache so that the new low entropy source value will only be generated on first access.
21  * Low entropy source is queried by entropy_source.cc that caches it in prefs. On Android, it is
22  * generated in Java so that it can be used by FRE experiments when the native is not available yet.
23  */
24 @MainThread
25 public class LowEntropySource {
26     // Should be equal to the value of EntropyState::kMaxLowEntropySize in C++.
27     public static final int MAX_LOW_ENTROPY_SIZE = 8000;
28 
29     private static final class LazyHolder {
30         private static final int LOW_ENTROPY_SOURCE_STATIC_CACHE = generateInternal();
31     }
32 
33     private static final class LazyHolderForPseudo {
34         private static final int PSEUDO_LOW_ENTROPY_SOURCE_STATIC_CACHE = generateInternal();
35     }
36 
37     private static final String KEY_LOW_ENTROPY_SOURCE_FRE_COMPLETED =
38             "low_entropy_source_fre_completed";
39 
40     /**
41      * Provides access to non-identifying entropy source for FRE trials.
42      * See {@link #generateLowEntropySource()} for details.
43      *
44      * WARNING: This should only be used on first run, as otherwise the value generated by this code
45      * will not correspond to what is used on the C++ side. Calling this method after FRE is
46      * completed will throw an exception.
47      */
generateLowEntropySourceForFirstRunTrial()48     public static int generateLowEntropySourceForFirstRunTrial() {
49         ThreadUtils.assertOnUiThread();
50         SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
51         if (prefs.getBoolean(KEY_LOW_ENTROPY_SOURCE_FRE_COMPLETED, false)) {
52             throw new IllegalStateException(
53                     "LowEntropySource can't be used from Java after FRE has been completed!");
54         }
55 
56         return generateLowEntropySource();
57     }
58 
59     /**
60      * Should be invoked when the FRE is completed to notify LowEntropySource that
61      * {@link #generateLowEntropySourceForFirstRunTrial} can no longer be used.
62      */
markFirstRunComplete()63     public static void markFirstRunComplete() {
64         ThreadUtils.assertOnUiThread();
65         SharedPreferences.Editor editor = ContextUtils.getAppSharedPreferences().edit();
66         editor.putBoolean(KEY_LOW_ENTROPY_SOURCE_FRE_COMPLETED, true);
67         editor.apply();
68     }
69 
70     /**
71      * Generates a new non-identifying entropy source. Has a static cache, so subsequent calls will
72      * return the same value during the process lifetime.
73      */
74     @CalledByNative
generateLowEntropySource()75     private static int generateLowEntropySource() {
76         return LazyHolder.LOW_ENTROPY_SOURCE_STATIC_CACHE;
77     }
78 
79     /**
80      * Generates a new non-identifying low entropy source using the same method that's used for the
81      * actual low entropy source. This one, however, is only used for statistical validation, and
82      * *not* for randomization or experiment assignment.
83      */
84     @CalledByNative
generatePseudoLowEntropySource()85     private static int generatePseudoLowEntropySource() {
86         return LazyHolderForPseudo.PSEUDO_LOW_ENTROPY_SOURCE_STATIC_CACHE;
87     }
88 
generateInternal()89     private static int generateInternal() {
90         return new Random().nextInt(MAX_LOW_ENTROPY_SIZE);
91     }
92 }
93