Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2c37b085 authored by Michal Karpinski's avatar Michal Karpinski
Browse files

Add SettingsValidators class and a test for validators enforcement

Common validators have been moved out to SettingsValidators class,
as they'll be now shared between Settings.System, Settings.Secure
and Settings.Global.

All validators of Settings.System settings have been verified to be
correct and the missing ones were added.

A unit test that verifies that all settings on SETTINGS_TO_BACKUP
list in Settings.System have validators on VALIDATORS list has been
added. This test will also cover Settings.Secure and Settings.Global
once validators are added for those settings. Also, fail to boot
in that case.

Ref: go/android-p-backed-up-settings
Test: atest frameworks/base/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
Bug: 64988620
Change-Id: I1fe951604010ab0c3f68a66296885a9766690d69
parent 588a06f5
Loading
Loading
Loading
Loading
+138 −162

File changed.

Preview size limit exceeded, changes collapsed.

+130 −0
Original line number Original line Diff line number Diff line
/*
 * 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.content.ComponentName;
import android.net.Uri;

import com.android.internal.util.ArrayUtils;

/**
 * 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 NON_NEGATIVE_INTEGER_VALIDATOR = new Validator() {
        @Override
        public boolean validate(String value) {
            try {
                return Integer.parseInt(value) >= 0;
            } catch (NumberFormatException e) {
                return false;
            }
        }
    };

    public static final Validator URI_VALIDATOR = new Validator() {
        @Override
        public boolean validate(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(String value) {
            return ComponentName.unflattenFromString(value) != null;
        }
    };

    public static final Validator LENIENT_IP_ADDRESS_VALIDATOR = new Validator() {
        private static final int MAX_IPV6_LENGTH = 45;

        @Override
        public boolean validate(String value) {
            return value.length() <= MAX_IPV6_LENGTH;
        }
    };

    public interface Validator {
        boolean validate(String value);
    }

    public static final class DiscreteValueValidator implements Validator {
        private final String[] mValues;

        public DiscreteValueValidator(String[] values) {
            mValues = values;
        }

        @Override
        public boolean validate(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(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(String value) {
            try {
                final float floatValue = Float.parseFloat(value);
                return floatValue >= mMin && floatValue <= mMax;
            } catch (NumberFormatException e) {
                return false;
            }
        }
    }
}
+50 −0
Original line number Original line Diff line number Diff line
/*
 * 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 static org.junit.Assert.fail;

import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

/** Tests that ensure all backed up settings have non-null validators. */
@Presubmit
@RunWith(AndroidJUnit4.class)
@SmallTest
public class SettingsValidatorsTest {

    @Test
    public void ensureAllBackedUpSystemSettingsHaveValidators() {
        StringBuilder offenders = new StringBuilder();
        for (String setting : Settings.System.SETTINGS_TO_BACKUP) {
            if (Settings.System.VALIDATORS.get(setting) == null) {
                offenders.append(setting).append(" ");
            }
        }

        // if there're any offenders fail the test and report them
        String offendersStr = offenders.toString();
        if (offendersStr.length() > 0) {
            fail("All Settings.System settings that are backed up have to have a non-null"
                    + " validator, but those don't: " + offendersStr);
        }
    }
}
+21 −1
Original line number Original line Diff line number Diff line
@@ -60,6 +60,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.os.UserManagerInternal;
import android.provider.Settings;
import android.provider.Settings;
import android.provider.SettingsValidators;
import android.provider.Settings.Global;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
import android.provider.Settings.Secure;
import android.text.TextUtils;
import android.text.TextUtils;
@@ -297,6 +298,10 @@ public class SettingsProvider extends ContentProvider {
    @Override
    @Override
    public boolean onCreate() {
    public boolean onCreate() {
        Settings.setInSystemServer();
        Settings.setInSystemServer();

        // fail to boot if there're any backed up settings that don't have a non-null validator
        ensureAllBackedUpSystemSettingsHaveValidators();

        synchronized (mLock) {
        synchronized (mLock) {
            mUserManager = UserManager.get(getContext());
            mUserManager = UserManager.get(getContext());
            mPackageManager = AppGlobals.getPackageManager();
            mPackageManager = AppGlobals.getPackageManager();
@@ -314,6 +319,21 @@ public class SettingsProvider extends ContentProvider {
        return true;
        return true;
    }
    }


    private void ensureAllBackedUpSystemSettingsHaveValidators() {
        StringBuilder offenders = new StringBuilder();
        for (String setting : Settings.System.SETTINGS_TO_BACKUP) {
            if (Settings.System.VALIDATORS.get(setting) == null) {
                offenders.append(setting).append(" ");
            }
        }

        String offendersStr = offenders.toString();
        if (offendersStr.length() > 0) {
            throw new RuntimeException("All Settings.System settings that are backed up must"
                    + " have a non-null validator, but those don't: " + offendersStr);
        }
    }

    @Override
    @Override
    public Bundle call(String method, String name, Bundle args) {
    public Bundle call(String method, String name, Bundle args) {
        final int requestingUserId = getRequestingUserId(args);
        final int requestingUserId = getRequestingUserId(args);
@@ -1472,7 +1492,7 @@ public class SettingsProvider extends ContentProvider {
    }
    }


    private void validateSystemSettingValue(String name, String value) {
    private void validateSystemSettingValue(String name, String value) {
        Settings.System.Validator validator = Settings.System.VALIDATORS.get(name);
        SettingsValidators.Validator validator = Settings.System.VALIDATORS.get(name);
        if (validator != null && !validator.validate(value)) {
        if (validator != null && !validator.validate(value)) {
            throw new IllegalArgumentException("Invalid value: " + value
            throw new IllegalArgumentException("Invalid value: " + value
                    + " for setting: " + name);
                    + " for setting: " + name);