/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.provider; import android.annotation.Nullable; import android.content.ComponentName; import android.net.Uri; import com.android.internal.util.ArrayUtils; import java.util.Locale; /** * This class provides both interface for validation and common validators * used to ensure Settings have meaningful values. * * @hide */ public class SettingsValidators { public static final Validator BOOLEAN_VALIDATOR = new DiscreteValueValidator(new String[] {"0", "1"}); public static final Validator ANY_STRING_VALIDATOR = new Validator() { @Override public boolean validate(@Nullable String value) { return true; } }; public static final Validator NON_NEGATIVE_INTEGER_VALIDATOR = new Validator() { @Override public boolean validate(@Nullable String value) { try { return Integer.parseInt(value) >= 0; } catch (NumberFormatException e) { return false; } } }; public static final Validator ANY_INTEGER_VALIDATOR = new Validator() { @Override public boolean validate(@Nullable String value) { try { Integer.parseInt(value); return true; } catch (NumberFormatException e) { return false; } } }; public static final Validator URI_VALIDATOR = new Validator() { @Override public boolean validate(@Nullable String value) { try { Uri.decode(value); return true; } catch (IllegalArgumentException e) { return false; } } }; public static final Validator COMPONENT_NAME_VALIDATOR = new Validator() { @Override public boolean validate(@Nullable String value) { return value != null && ComponentName.unflattenFromString(value) != null; } }; public static final Validator PACKAGE_NAME_VALIDATOR = new Validator() { @Override public boolean validate(@Nullable String value) { return value != null && isStringPackageName(value); } private boolean isStringPackageName(String value) { // The name may contain uppercase or lowercase letters ('A' through 'Z'), numbers, // and underscores ('_'). However, individual package name parts may only // start with letters. // (https://developer.android.com/guide/topics/manifest/manifest-element.html#package) if (value == null) { return false; } String[] subparts = value.split("\\."); boolean isValidPackageName = true; for (String subpart : subparts) { isValidPackageName &= isSubpartValidForPackageName(subpart); if (!isValidPackageName) break; } return isValidPackageName; } private boolean isSubpartValidForPackageName(String subpart) { if (subpart.length() == 0) return false; boolean isValidSubpart = Character.isLetter(subpart.charAt(0)); for (int i = 1; i < subpart.length(); i++) { isValidSubpart &= (Character.isLetterOrDigit(subpart.charAt(i)) || (subpart.charAt(i) == '_')); if (!isValidSubpart) break; } return isValidSubpart; } }; public static final Validator LENIENT_IP_ADDRESS_VALIDATOR = new Validator() { private static final int MAX_IPV6_LENGTH = 45; @Override public boolean validate(@Nullable String value) { if (value == null) { return false; } return value.length() <= MAX_IPV6_LENGTH; } }; public static final Validator LOCALE_VALIDATOR = new Validator() { @Override public boolean validate(@Nullable String value) { if (value == null) { return false; } Locale[] validLocales = Locale.getAvailableLocales(); for (Locale locale : validLocales) { if (value.equals(locale.toString())) { return true; } } return false; } }; public interface Validator { /** * Returns whether the input value is valid. Subclasses should handle the case where the * input value is {@code null}. */ boolean validate(@Nullable String value); } public static final class DiscreteValueValidator implements Validator { private final String[] mValues; public DiscreteValueValidator(String[] values) { mValues = values; } @Override public boolean validate(@Nullable String value) { return ArrayUtils.contains(mValues, value); } } public static final class InclusiveIntegerRangeValidator implements Validator { private final int mMin; private final int mMax; public InclusiveIntegerRangeValidator(int min, int max) { mMin = min; mMax = max; } @Override public boolean validate(@Nullable String value) { try { final int intValue = Integer.parseInt(value); return intValue >= mMin && intValue <= mMax; } catch (NumberFormatException e) { return false; } } } public static final class InclusiveFloatRangeValidator implements Validator { private final float mMin; private final float mMax; public InclusiveFloatRangeValidator(float min, float max) { mMin = min; mMax = max; } @Override public boolean validate(@Nullable String value) { try { final float floatValue = Float.parseFloat(value); return floatValue >= mMin && floatValue <= mMax; } catch (NumberFormatException | NullPointerException e) { return false; } } } public static final class ComponentNameListValidator implements Validator { private final String mSeparator; public ComponentNameListValidator(String separator) { mSeparator = separator; } @Override public boolean validate(@Nullable String value) { if (value == null) { return false; } String[] elements = value.split(mSeparator); for (String element : elements) { if (!COMPONENT_NAME_VALIDATOR.validate(element)) { return false; } } return true; } } public static final class PackageNameListValidator implements Validator { private final String mSeparator; public PackageNameListValidator(String separator) { mSeparator = separator; } @Override public boolean validate(@Nullable String value) { if (value == null) { return false; } String[] elements = value.split(mSeparator); for (String element : elements) { if (!PACKAGE_NAME_VALIDATOR.validate(element)) { return false; } } return true; } } }