• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 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.base.shared_preferences;
6 
7 import android.text.TextUtils;
8 
9 import org.chromium.build.annotations.CheckDiscard;
10 
11 import java.util.Arrays;
12 import java.util.regex.Pattern;
13 
14 /**
15  * Class that checks if given Strings are valid SharedPreferences keys to use.
16  *
17  * Checks that:
18  * 1. Keys are registered as "in use".
19  * 2. The key format is valid, either:
20  *   - "Chrome.[Feature].[Key]"
21  *   - "Chrome.[Feature].[KeyPrefix].[Suffix]"
22  *   - Legacy key prior to this restriction
23  */
24 @CheckDiscard("Validation is performed in tests and in debug builds.")
25 class StrictPreferenceKeyChecker implements PreferenceKeyChecker {
26     // The dynamic part cannot be empty, but otherwise it is anything that does not contain
27     // stars.
28     private static final Pattern DYNAMIC_PART_PATTERN = Pattern.compile("[^\\*]+");
29 
30     private final PreferenceKeyRegistry mRegistry;
31 
StrictPreferenceKeyChecker(PreferenceKeyRegistry registry)32     StrictPreferenceKeyChecker(PreferenceKeyRegistry registry) {
33         mRegistry = registry;
34     }
35 
36     /**
37      * Check that the |key| passed is in use.
38      * @throws RuntimeException if the key is not in use.
39      */
40     @Override
checkIsKeyInUse(String key)41     public void checkIsKeyInUse(String key) {
42         if (!isKeyInUse(key)) {
43             throw new RuntimeException(
44                     "SharedPreferences key \""
45                             + key
46                             + "\" is not registered in PreferenceKeyRegistry.mKeysInUse");
47         }
48         KnownPreferenceKeyRegistries.onRegistryUsed(mRegistry);
49     }
50 
51     /**
52      * @return Whether |key| is in use.
53      */
isKeyInUse(String key)54     private boolean isKeyInUse(String key) {
55         // For non-dynamic legacy keys, a simple map check is enough.
56         if (mRegistry.mLegacyFormatKeys.contains(key)) {
57             return true;
58         }
59 
60         // For dynamic legacy keys, each legacy prefix has to be checked.
61         for (KeyPrefix prefix : mRegistry.mLegacyPrefixes) {
62             if (prefix.hasGenerated(key)) {
63                 return true;
64             }
65         }
66 
67         // If not a format-legacy key, assume it follows the format and find out if it is
68         // a prefixed key.
69         String[] parts = key.split("\\.", 4);
70         if (parts.length < 3) return false;
71         boolean isPrefixed = parts.length >= 4;
72 
73         if (isPrefixed) {
74             // Key with prefix in format "Chrome.[Feature].[KeyPrefix].[Suffix]".
75 
76             // Check if its prefix is registered in |mKeysInUse|.
77             String prefixFormat =
78                     TextUtils.join(".", Arrays.asList(parts[0], parts[1], parts[2], "*"));
79             if (!mRegistry.mKeysInUse.contains(prefixFormat)) return false;
80 
81             // Check if the dynamic part is correctly formed.
82             String dynamicPart = parts[3];
83             return DYNAMIC_PART_PATTERN.matcher(dynamicPart).matches();
84         } else {
85             // Regular key in format "Chrome.[Feature].[Key]" which was not present in |mKeysInUse|.
86             // Just check if it is in [keys in use].
87             return mRegistry.mKeysInUse.contains(key);
88         }
89     }
90 
91     @Override
checkIsPrefixInUse(KeyPrefix prefix)92     public void checkIsPrefixInUse(KeyPrefix prefix) {
93         if (mRegistry.mLegacyPrefixes.contains(prefix)) {
94             return;
95         }
96 
97         if (mRegistry.mKeysInUse.contains(prefix.pattern())) {
98             return;
99         }
100 
101         throw new RuntimeException(
102                 "SharedPreferences KeyPrefix \""
103                         + prefix.pattern()
104                         + "\" is not registered in PreferenceKeyRegistry.mKeysInUse()");
105     }
106 }
107