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 NON_NEGATIVE_INTEGER_VALIDATOR = new Validator() {
49         @Override
50         public boolean validate(@Nullable String value) {
51             if (value == null) {
52                 return true;
53             }
54             try {
55                 return Integer.parseInt(value) >= 0;
56             } catch (NumberFormatException e) {
57                 return false;
58             }
59         }
60     };
61 
62     public static final Validator ANY_INTEGER_VALIDATOR = new Validator() {
63         @Override
64         public boolean validate(@Nullable String value) {
65             if (value == null) {
66                 return true;
67             }
68             try {
69                 Integer.parseInt(value);
70                 return true;
71             } catch (NumberFormatException e) {
72                 return false;
73             }
74         }
75     };
76 
77     public static final Validator NON_NEGATIVE_FLOAT_VALIDATOR = new Validator() {
78         @Override
79         public boolean validate(@Nullable String value) {
80             if (value == null) {
81                 return true;
82             }
83             try {
84                 return Float.parseFloat(value) >= 0.0f;
85             } catch (NumberFormatException e) {
86                 return false;
87             }
88         }
89     };
90 
91     public static final Validator URI_VALIDATOR = new Validator() {
92         @Override
93         public boolean validate(@Nullable String value) {
94             if (value == null) {
95                 return true;
96             }
97             try {
98                 Uri.decode(value);
99                 return true;
100             } catch (IllegalArgumentException e) {
101                 return false;
102             }
103         }
104     };
105 
106     /**
107      * Does not allow a setting to have a null {@link ComponentName}. Use {@link
108      * SettingsValidators#NULLABLE_COMPONENT_NAME_VALIDATOR} instead if a setting can have a
109      * nullable {@link ComponentName}.
110      */
111     public static final Validator COMPONENT_NAME_VALIDATOR = new Validator() {
112         @Override
113         public boolean validate(@NonNull String value) {
114             return value != null && ComponentName.unflattenFromString(value) != null;
115         }
116     };
117 
118     /**
119      * Allows a setting to have a null {@link ComponentName}.
120      */
121     public static final Validator NULLABLE_COMPONENT_NAME_VALIDATOR = new Validator() {
122         @Override
123         public boolean validate(@Nullable String value) {
124             return value == null || COMPONENT_NAME_VALIDATOR.validate(value);
125         }
126     };
127 
128     public static final Validator PACKAGE_NAME_VALIDATOR = new Validator() {
129         @Override
130         public boolean validate(@NonNull String value) {
131             return value != null && isStringPackageName(value);
132         }
133 
134         private boolean isStringPackageName(@NonNull String value) {
135             // The name may contain uppercase or lowercase letters ('A' through 'Z'), numbers,
136             // and underscores ('_'). However, individual package name parts may only
137             // start with letters.
138             // (https://developer.android.com/guide/topics/manifest/manifest-element.html#package)
139             String[] subparts = value.split("\\.");
140             boolean isValidPackageName = true;
141             for (String subpart : subparts) {
142                 isValidPackageName &= isSubpartValidForPackageName(subpart);
143                 if (!isValidPackageName) break;
144             }
145             return isValidPackageName;
146         }
147 
148         private boolean isSubpartValidForPackageName(String subpart) {
149             if (subpart.length() == 0) return false;
150             boolean isValidSubpart = Character.isLetter(subpart.charAt(0));
151             for (int i = 1; i < subpart.length(); i++) {
152                 isValidSubpart &= (Character.isLetterOrDigit(subpart.charAt(i))
153                                 || (subpart.charAt(i) == '_'));
154                 if (!isValidSubpart) break;
155             }
156             return isValidSubpart;
157         }
158     };
159 
160     public static final Validator LENIENT_IP_ADDRESS_VALIDATOR = new Validator() {
161         private static final int MAX_IPV6_LENGTH = 45;
162 
163         @Override
164         public boolean validate(@Nullable String value) {
165             if (value == null) {
166                 return true;
167             }
168             return value.length() <= MAX_IPV6_LENGTH;
169         }
170     };
171 
172     public static final Validator LOCALE_VALIDATOR = new Validator() {
173         @Override
174         public boolean validate(@Nullable String value) {
175             if (value == null) {
176                 return true;
177             }
178             Locale[] validLocales = Locale.getAvailableLocales();
179             for (Locale locale : validLocales) {
180                 if (value.equals(locale.toString())) {
181                     return true;
182                 }
183             }
184             return false;
185         }
186     };
187 
188     /** {@link Validator} that checks whether a value is a valid {@link JSONObject}. */
189     public static final Validator JSON_OBJECT_VALIDATOR = (value) -> {
190         if (value == null) {
191             return true;
192         }
193         if (TextUtils.isEmpty(value)) {
194             return false;
195         }
196         try {
197             new JSONObject(value);
198             return true;
199         } catch (JSONException e) {
200             return false;
201         }
202     };
203 
204     public static final Validator TTS_LIST_VALIDATOR = new TTSListValidator();
205 
206     public static final Validator TILE_LIST_VALIDATOR = new TileListValidator();
207 
208     static final Validator COLON_SEPARATED_COMPONENT_LIST_VALIDATOR =
209             new ComponentNameListValidator(":");
210 
211     static final Validator COLON_SEPARATED_PACKAGE_LIST_VALIDATOR =
212             new PackageNameListValidator(":");
213 
214     static final Validator COMMA_SEPARATED_COMPONENT_LIST_VALIDATOR =
215             new ComponentNameListValidator(",");
216 
217     static final Validator PERCENTAGE_INTEGER_VALIDATOR =
218             new InclusiveIntegerRangeValidator(0, 100);
219 
220     static final Validator VIBRATION_INTENSITY_VALIDATOR = new InclusiveIntegerRangeValidator(0, 3);
221 
222     static final Validator ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR =
223             new AccessibilityShortcutTargetListValidator();
224 
225     static final Validator NONE_NEGATIVE_LONG_VALIDATOR = new Validator() {
226         @Override
227         public boolean validate(String value) {
228             if (value == null) {
229                 return true;
230             }
231             try {
232                 return Long.parseLong(value) >= 0;
233             } catch (NumberFormatException e) {
234                 return false;
235             }
236         }
237     };
238 }
239