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