1 /* 2 * Copyright (C) 2018 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; 18 19 import android.annotation.Nullable; 20 import android.content.ComponentName; 21 import android.net.Uri; 22 23 import com.android.internal.util.ArrayUtils; 24 25 import java.util.Locale; 26 27 /** 28 * This class provides both interface for validation and common validators 29 * used to ensure Settings have meaningful values. 30 * 31 * @hide 32 */ 33 public class SettingsValidators { 34 35 public static final Validator BOOLEAN_VALIDATOR = 36 new DiscreteValueValidator(new String[] {"0", "1"}); 37 38 public static final Validator ANY_STRING_VALIDATOR = new Validator() { 39 @Override 40 public boolean validate(@Nullable String value) { 41 return true; 42 } 43 }; 44 45 public static final Validator NON_NEGATIVE_INTEGER_VALIDATOR = new Validator() { 46 @Override 47 public boolean validate(@Nullable String value) { 48 try { 49 return Integer.parseInt(value) >= 0; 50 } catch (NumberFormatException e) { 51 return false; 52 } 53 } 54 }; 55 56 public static final Validator ANY_INTEGER_VALIDATOR = new Validator() { 57 @Override 58 public boolean validate(@Nullable String value) { 59 try { 60 Integer.parseInt(value); 61 return true; 62 } catch (NumberFormatException e) { 63 return false; 64 } 65 } 66 }; 67 68 public static final Validator URI_VALIDATOR = new Validator() { 69 @Override 70 public boolean validate(@Nullable String value) { 71 try { 72 Uri.decode(value); 73 return true; 74 } catch (IllegalArgumentException e) { 75 return false; 76 } 77 } 78 }; 79 80 /** 81 * Does not allow a setting to have a null {@link ComponentName}. Use {@link 82 * SettingsValidators#NULLABLE_COMPONENT_NAME_VALIDATOR} instead if a setting can have a 83 * nullable {@link ComponentName}. 84 */ 85 public static final Validator COMPONENT_NAME_VALIDATOR = new Validator() { 86 @Override 87 public boolean validate(@Nullable String value) { 88 return value != null && ComponentName.unflattenFromString(value) != null; 89 } 90 }; 91 92 /** 93 * Allows a setting to have a null {@link ComponentName}. 94 */ 95 public static final Validator NULLABLE_COMPONENT_NAME_VALIDATOR = new Validator() { 96 @Override 97 public boolean validate(@Nullable String value) { 98 return value == null || COMPONENT_NAME_VALIDATOR.validate(value); 99 } 100 }; 101 102 public static final Validator PACKAGE_NAME_VALIDATOR = new Validator() { 103 @Override 104 public boolean validate(@Nullable String value) { 105 return value != null && isStringPackageName(value); 106 } 107 108 private boolean isStringPackageName(String value) { 109 // The name may contain uppercase or lowercase letters ('A' through 'Z'), numbers, 110 // and underscores ('_'). However, individual package name parts may only 111 // start with letters. 112 // (https://developer.android.com/guide/topics/manifest/manifest-element.html#package) 113 if (value == null) { 114 return false; 115 } 116 String[] subparts = value.split("\\."); 117 boolean isValidPackageName = true; 118 for (String subpart : subparts) { 119 isValidPackageName &= isSubpartValidForPackageName(subpart); 120 if (!isValidPackageName) break; 121 } 122 return isValidPackageName; 123 } 124 125 private boolean isSubpartValidForPackageName(String subpart) { 126 if (subpart.length() == 0) return false; 127 boolean isValidSubpart = Character.isLetter(subpart.charAt(0)); 128 for (int i = 1; i < subpart.length(); i++) { 129 isValidSubpart &= (Character.isLetterOrDigit(subpart.charAt(i)) 130 || (subpart.charAt(i) == '_')); 131 if (!isValidSubpart) break; 132 } 133 return isValidSubpart; 134 } 135 }; 136 137 public static final Validator LENIENT_IP_ADDRESS_VALIDATOR = new Validator() { 138 private static final int MAX_IPV6_LENGTH = 45; 139 140 @Override 141 public boolean validate(@Nullable String value) { 142 if (value == null) { 143 return false; 144 } 145 return value.length() <= MAX_IPV6_LENGTH; 146 } 147 }; 148 149 public static final Validator LOCALE_VALIDATOR = new Validator() { 150 @Override 151 public boolean validate(@Nullable String value) { 152 if (value == null) { 153 return false; 154 } 155 Locale[] validLocales = Locale.getAvailableLocales(); 156 for (Locale locale : validLocales) { 157 if (value.equals(locale.toString())) { 158 return true; 159 } 160 } 161 return false; 162 } 163 }; 164 165 public interface Validator { 166 /** 167 * Returns whether the input value is valid. Subclasses should handle the case where the 168 * input value is {@code null}. 169 */ validate(@ullable String value)170 boolean validate(@Nullable String value); 171 } 172 173 public static final class DiscreteValueValidator implements Validator { 174 private final String[] mValues; 175 DiscreteValueValidator(String[] values)176 public DiscreteValueValidator(String[] values) { 177 mValues = values; 178 } 179 180 @Override validate(@ullable String value)181 public boolean validate(@Nullable String value) { 182 return ArrayUtils.contains(mValues, value); 183 } 184 } 185 186 public static final class InclusiveIntegerRangeValidator implements Validator { 187 private final int mMin; 188 private final int mMax; 189 InclusiveIntegerRangeValidator(int min, int max)190 public InclusiveIntegerRangeValidator(int min, int max) { 191 mMin = min; 192 mMax = max; 193 } 194 195 @Override validate(@ullable String value)196 public boolean validate(@Nullable String value) { 197 try { 198 final int intValue = Integer.parseInt(value); 199 return intValue >= mMin && intValue <= mMax; 200 } catch (NumberFormatException e) { 201 return false; 202 } 203 } 204 } 205 206 public static final class InclusiveFloatRangeValidator implements Validator { 207 private final float mMin; 208 private final float mMax; 209 InclusiveFloatRangeValidator(float min, float max)210 public InclusiveFloatRangeValidator(float min, float max) { 211 mMin = min; 212 mMax = max; 213 } 214 215 @Override validate(@ullable String value)216 public boolean validate(@Nullable String value) { 217 try { 218 final float floatValue = Float.parseFloat(value); 219 return floatValue >= mMin && floatValue <= mMax; 220 } catch (NumberFormatException | NullPointerException e) { 221 return false; 222 } 223 } 224 } 225 226 public static final class ComponentNameListValidator implements Validator { 227 private final String mSeparator; 228 ComponentNameListValidator(String separator)229 public ComponentNameListValidator(String separator) { 230 mSeparator = separator; 231 } 232 233 @Override validate(@ullable String value)234 public boolean validate(@Nullable String value) { 235 if (value == null) { 236 return false; 237 } 238 String[] elements = value.split(mSeparator); 239 for (String element : elements) { 240 if (!COMPONENT_NAME_VALIDATOR.validate(element)) { 241 return false; 242 } 243 } 244 return true; 245 } 246 } 247 248 public static final class PackageNameListValidator implements Validator { 249 private final String mSeparator; 250 PackageNameListValidator(String separator)251 public PackageNameListValidator(String separator) { 252 mSeparator = separator; 253 } 254 255 @Override validate(@ullable String value)256 public boolean validate(@Nullable String value) { 257 if (value == null) { 258 return false; 259 } 260 String[] elements = value.split(mSeparator); 261 for (String element : elements) { 262 if (!PACKAGE_NAME_VALIDATOR.validate(element)) { 263 return false; 264 } 265 } 266 return true; 267 } 268 } 269 } 270