• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.provider.settings.validators;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.ComponentName;
22 import android.net.Uri;
23 import android.text.TextUtils;
24 
25 import org.json.JSONException;
26 import org.json.JSONObject;
27 
28 import java.util.Locale;
29 
30 /**
31  * This class provides both interface for validation and common validators
32  * used to ensure Settings have meaningful values.
33  *
34  * @hide
35  */
36 public class SettingsValidators {
37 
38     public static final Validator BOOLEAN_VALIDATOR =
39             new DiscreteValueValidator(new String[] {"0", "1"});
40 
41     public static final Validator ANY_STRING_VALIDATOR = new Validator() {
42         @Override
43         public boolean validate(@Nullable String value) {
44             return true;
45         }
46     };
47 
48     public static final Validator FONT_SCALE_VALIDATOR = new InclusiveFloatRangeValidator(0.25f,
49             5.0f);
50 
51     public static final Validator NON_NEGATIVE_INTEGER_VALIDATOR = new Validator() {
52         @Override
53         public boolean validate(@Nullable String value) {
54             if (value == null) {
55                 return true;
56             }
57             try {
58                 return Integer.parseInt(value) >= 0;
59             } catch (NumberFormatException e) {
60                 return false;
61             }
62         }
63     };
64 
65     public static final Validator ANY_INTEGER_VALIDATOR = new Validator() {
66         @Override
67         public boolean validate(@Nullable String value) {
68             if (value == null) {
69                 return true;
70             }
71             try {
72                 Integer.parseInt(value);
73                 return true;
74             } catch (NumberFormatException e) {
75                 return false;
76             }
77         }
78     };
79 
80     public static final Validator NON_NEGATIVE_FLOAT_VALIDATOR = new Validator() {
81         @Override
82         public boolean validate(@Nullable String value) {
83             if (value == null) {
84                 return true;
85             }
86             try {
87                 return Float.parseFloat(value) >= 0.0f;
88             } catch (NumberFormatException e) {
89                 return false;
90             }
91         }
92     };
93 
94     public static final Validator URI_VALIDATOR = new Validator() {
95         @Override
96         public boolean validate(@Nullable String value) {
97             if (value == null) {
98                 return true;
99             }
100             try {
101                 Uri.decode(value);
102                 return true;
103             } catch (IllegalArgumentException e) {
104                 return false;
105             }
106         }
107     };
108 
109     /**
110      * Does not allow a setting to have a null {@link ComponentName}. Use {@link
111      * SettingsValidators#NULLABLE_COMPONENT_NAME_VALIDATOR} instead if a setting can have a
112      * nullable {@link ComponentName}.
113      */
114     public static final Validator COMPONENT_NAME_VALIDATOR = new Validator() {
115         @Override
116         public boolean validate(@NonNull String value) {
117             return value != null && ComponentName.unflattenFromString(value) != null;
118         }
119     };
120 
121     /**
122      * Allows a setting to have a null {@link ComponentName}.
123      */
124     public static final Validator NULLABLE_COMPONENT_NAME_VALIDATOR = new Validator() {
125         @Override
126         public boolean validate(@Nullable String value) {
127             return value == null || COMPONENT_NAME_VALIDATOR.validate(value);
128         }
129     };
130 
131     public static final Validator PACKAGE_NAME_VALIDATOR = new Validator() {
132         @Override
133         public boolean validate(@NonNull String value) {
134             return value != null && isStringPackageName(value);
135         }
136 
137         private boolean isStringPackageName(@NonNull String value) {
138             // The name may contain uppercase or lowercase letters ('A' through 'Z'), numbers,
139             // and underscores ('_'). However, individual package name parts may only
140             // start with letters.
141             // (https://developer.android.com/guide/topics/manifest/manifest-element.html#package)
142             String[] subparts = value.split("\\.");
143             boolean isValidPackageName = true;
144             for (String subpart : subparts) {
145                 isValidPackageName &= isSubpartValidForPackageName(subpart);
146                 if (!isValidPackageName) break;
147             }
148             return isValidPackageName;
149         }
150 
151         private boolean isSubpartValidForPackageName(String subpart) {
152             if (subpart.length() == 0) return false;
153             boolean isValidSubpart = Character.isLetter(subpart.charAt(0));
154             for (int i = 1; i < subpart.length(); i++) {
155                 isValidSubpart &= (Character.isLetterOrDigit(subpart.charAt(i))
156                                 || (subpart.charAt(i) == '_'));
157                 if (!isValidSubpart) break;
158             }
159             return isValidSubpart;
160         }
161     };
162 
163     public static final Validator LENIENT_IP_ADDRESS_VALIDATOR = new Validator() {
164         private static final int MAX_IPV6_LENGTH = 45;
165 
166         @Override
167         public boolean validate(@Nullable String value) {
168             if (value == null) {
169                 return true;
170             }
171             return value.length() <= MAX_IPV6_LENGTH;
172         }
173     };
174 
175     public static final Validator LOCALE_VALIDATOR = new Validator() {
176         @Override
177         public boolean validate(@Nullable String value) {
178             if (value == null) {
179                 return true;
180             }
181             Locale[] validLocales = Locale.getAvailableLocales();
182             for (Locale locale : validLocales) {
183                 if (value.equals(locale.toString())) {
184                     return true;
185                 }
186             }
187             return false;
188         }
189     };
190 
191     /**
192      * Similar to {@link #LOCALE_VALIDATOR} but allows loose/subset matches against the list
193      * from {@link Locale#getAvailableLocales()}.
194      *
195      * <p>Expects that the string is '_'-separated with 1 to 3 parts. Then checks the parts against
196      * the locale list, returning true if any available locale matches the parts provided using
197      * case insensitive string comparison.
198      * <li>Part 1: match against {@link Locale#getLanguage()}</li>
199      * <li>Part 2, if present: match against {@link Locale#getCountry()}</li>
200      * <li>Part 3, if present: match against {@link Locale#getVariant()}</li>
201      */
202     public static final Validator LOCALE_LOOSE_VALIDATOR = value -> {
203         if (value == null) {
204             return true;
205         }
206         String[] parts = value.split("_", 3);
207         Locale[] validLocales = Locale.getAvailableLocales();
208         for (Locale locale : validLocales) {
209             if (!parts[0].equalsIgnoreCase(locale.getLanguage())) {
210                 continue;
211             }
212             if (parts.length >= 2 && !parts[1].equalsIgnoreCase(locale.getCountry())) {
213                 continue;
214             }
215             if (parts.length == 3 && !parts[2].equalsIgnoreCase(locale.getVariant())) {
216                 continue;
217             }
218             return true;
219         }
220         return false;
221     };
222 
223     /** {@link Validator} that checks whether a value is a valid {@link JSONObject}. */
224     public static final Validator JSON_OBJECT_VALIDATOR = (value) -> {
225         if (value == null) {
226             return true;
227         }
228         if (TextUtils.isEmpty(value)) {
229             return false;
230         }
231         try {
232             new JSONObject(value);
233             return true;
234         } catch (JSONException e) {
235             return false;
236         }
237     };
238 
239     public static final Validator TTS_LIST_VALIDATOR = new TTSListValidator();
240 
241     public static final Validator TILE_LIST_VALIDATOR = new TileListValidator();
242 
243     static final Validator COLON_SEPARATED_COMPONENT_LIST_VALIDATOR =
244             new ComponentNameListValidator(":");
245 
246     static final Validator COLON_SEPARATED_PACKAGE_LIST_VALIDATOR =
247             new PackageNameListValidator(":");
248 
249     static final Validator COMMA_SEPARATED_COMPONENT_LIST_VALIDATOR =
250             new ComponentNameListValidator(",");
251 
252     static final Validator PERCENTAGE_INTEGER_VALIDATOR =
253             new InclusiveIntegerRangeValidator(0, 100);
254 
255     static final Validator VIBRATION_INTENSITY_VALIDATOR = new InclusiveIntegerRangeValidator(0, 3);
256 
257     static final Validator ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR =
258             new AccessibilityShortcutTargetListValidator();
259 
260     static final Validator NONE_NEGATIVE_LONG_VALIDATOR = new Validator() {
261         @Override
262         public boolean validate(String value) {
263             if (value == null) {
264                 return true;
265             }
266             try {
267                 return Long.parseLong(value) >= 0;
268             } catch (NumberFormatException e) {
269                 return false;
270             }
271         }
272     };
273 
274     static final Validator ANY_LONG_VALIDATOR = value -> {
275         if (value == null) {
276             return true;
277         }
278         try {
279             Long.parseLong(value);
280             return true;
281         } catch (NumberFormatException e) {
282             return false;
283         }
284     };
285 
286     static final Validator CREDENTIAL_SERVICE_VALIDATOR = new Validator() {
287         @Override
288         public boolean validate(String value) {
289             if (value == null || value.equals("")) {
290                 return true;
291             }
292 
293             return COLON_SEPARATED_COMPONENT_LIST_VALIDATOR.validate(value);
294         }
295     };
296 
297     static final Validator AUTOFILL_SERVICE_VALIDATOR = new Validator() {
298         @Override
299         public boolean validate(String value) {
300             if (value == null || value.equals("")) {
301                 return true;
302             }
303 
304             if (value.equals("credential-provider")) {
305                return true;
306             }
307 
308             return NULLABLE_COMPONENT_NAME_VALIDATOR.validate(value);
309         }
310     };
311 }
312