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

Commit 6bfc62e4 authored by Matt Pape's avatar Matt Pape
Browse files

Add reset and list for device config flags to SettingsProvider to

support command line debug tool.

Test: atest FrameworksCoreTests:SettingsProviderTest
      Further tested manually via command line (see ag/5613024)
Bug:109919982
Bug:113101834
Change-Id: Ib0d9e4c6d806ec3521ac49b8c05fbdad8b5b13d7
parent 579fdf3b
Loading
Loading
Loading
Loading
+14 −3
Original line number Diff line number Diff line
@@ -1680,6 +1680,11 @@ public final class Settings {
     */
    public static final String CALL_METHOD_TAG_KEY = "_tag";
    /**
     * @hide - String argument extra to the fast-path call()-based requests
     */
    public static final String CALL_METHOD_PREFIX_KEY = "_prefix";
    /** @hide - Private call() method to write to 'system' table */
    public static final String CALL_METHOD_PUT_SYSTEM = "PUT_system";
@@ -1701,15 +1706,18 @@ public final class Settings {
    /** @hide - Private call() method to delete from the 'global' table */
    public static final String CALL_METHOD_DELETE_GLOBAL = "DELETE_global";
    /** @hide - Private call() method to reset to defaults the 'configuration' table */
    public static final String CALL_METHOD_DELETE_CONFIG = "DELETE_config";
    /** @hide - Private call() method to reset to defaults the 'secure' table */
    public static final String CALL_METHOD_RESET_SECURE = "RESET_secure";
    /** @hide - Private call() method to reset to defaults the 'global' table */
    public static final String CALL_METHOD_RESET_GLOBAL = "RESET_global";
    /** @hide - Private call() method to reset to defaults the 'configuration' table */
    public static final String CALL_METHOD_RESET_CONFIG = "RESET_config";
    /** @hide - Private call() method to reset to defaults the 'secure' table */
    public static final String CALL_METHOD_RESET_SECURE = "RESET_secure";
    /** @hide - Private call() method to query the 'system' table */
    public static final String CALL_METHOD_LIST_SYSTEM = "LIST_system";
@@ -1719,6 +1727,9 @@ public final class Settings {
    /** @hide - Private call() method to query the 'global' table */
    public static final String CALL_METHOD_LIST_GLOBAL = "LIST_global";
    /** @hide - Private call() method to reset to defaults the 'configuration' table */
    public static final String CALL_METHOD_LIST_CONFIG = "LIST_config";
    /**
     * Activity Extra: Limit available options in launched activity based on the given authority.
     * <p>
+119 −0
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@

package android.provider;

import static org.hamcrest.Matchers.aMapWithSize;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.junit.Assert.assertThat;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -26,6 +30,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.test.AndroidTestCase;
@@ -33,10 +38,19 @@ import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.test.suitebuilder.annotation.Suppress;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/** Unit test for SettingsProvider. */
public class SettingsProviderTest extends AndroidTestCase {
    /**
     * TODO(b/113100523): Move this to DeviceConfig.java when it is added, and expose it as a System
     *     API.
     */
    private static final Uri CONFIG_CONTENT_URI =
            Uri.parse("content://" + Settings.AUTHORITY + "/config");

    @MediumTest
    public void testNameValueCache() {
        ContentResolver r = getContext().getContentResolver();
@@ -379,4 +393,109 @@ public class SettingsProviderTest extends AndroidTestCase {

        assertTrue(ssaid.equals(ssaid2));
    }

    @MediumTest
    public void testCall_putAndGetConfig() {
        ContentResolver r = getContext().getContentResolver();
        String name = "key1";
        String value = "value1";
        String newValue = "value2";
        Bundle args = new Bundle();
        args.putString(Settings.NameValueTable.VALUE, value);

        try {
            // value is empty
            Bundle results =
                    r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
            assertNull(results.get(Settings.NameValueTable.VALUE));

            // save value
            results = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, name, args);
            assertNull(results);

            // value is no longer empty
            results = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
            assertEquals(value, results.get(Settings.NameValueTable.VALUE));

            // save new value
            args.putString(Settings.NameValueTable.VALUE, newValue);
            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, name, args);

            // new value is returned
            results = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
            assertEquals(newValue, results.get(Settings.NameValueTable.VALUE));
        } finally {
            // clean up
            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, name, null);
        }
    }

    @MediumTest
    public void testCall_deleteConfig() {
        ContentResolver r = getContext().getContentResolver();
        String name = "key1";
        String value = "value1";
        Bundle args = new Bundle();
        args.putString(Settings.NameValueTable.VALUE, value);

        try {
            // save value
            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, name, args);

            // get value
            Bundle results =
                    r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
            assertEquals(value, results.get(Settings.NameValueTable.VALUE));

            // delete value
            results = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, name, null);

            // value is empty now
            results = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
            assertNull(results.get(Settings.NameValueTable.VALUE));
        } finally {
            // clean up
            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, name, null);
        }
    }

    @MediumTest
    public void testCall_listConfig() {
        ContentResolver r = getContext().getContentResolver();
        String prefix = "foo";
        String newPrefix = "bar";
        String name = prefix + "/" + "key1";
        String newName = newPrefix + "/" + "key1";
        String value = "value1";
        String newValue = "value2";
        Bundle args = new Bundle();
        args.putString(Settings.NameValueTable.VALUE, value);

        try {
            // save both values
            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, name, args);
            args.putString(Settings.NameValueTable.VALUE, newValue);
            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, newName, args);

            // list all values
            Bundle result = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_LIST_CONFIG,
                    null, null);
            Map<String, String> keyValueMap =
                    (HashMap) result.getSerializable(Settings.NameValueTable.VALUE);
            assertThat(keyValueMap.size(), greaterThanOrEqualTo(2));
            assertEquals(value, keyValueMap.get(name));
            assertEquals(newValue, keyValueMap.get(newName));

            // list values for prefix
            args.putString(Settings.CALL_METHOD_PREFIX_KEY, prefix);
            result = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_LIST_CONFIG, null, args);
            keyValueMap = (HashMap) result.getSerializable(Settings.NameValueTable.VALUE);
            assertThat(keyValueMap, aMapWithSize(1));
            assertEquals(value, keyValueMap.get(name));
        } finally {
            // clean up
            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, name, null);
            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, newName, null);
        }
    }
}
+99 −22
Original line number Diff line number Diff line
@@ -94,6 +94,7 @@ import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
@@ -147,6 +148,7 @@ public class SettingsProvider extends ContentProvider {
    private static final String TABLE_SYSTEM = "system";
    private static final String TABLE_SECURE = "secure";
    private static final String TABLE_GLOBAL = "global";
    private static final String TABLE_CONFIG = "config";

    // Old tables no longer exist.
    private static final String TABLE_FAVORITES = "favorites";
@@ -414,9 +416,8 @@ public class SettingsProvider extends ContentProvider {

            case Settings.CALL_METHOD_PUT_CONFIG: {
                String value = getSettingValue(args);
                String tag = getSettingTag(args);
                final boolean makeDefault = getSettingMakeDefault(args);
                insertConfigSetting(name, value, tag, makeDefault, requestingUserId, false);
                insertConfigSetting(name, value, null, makeDefault, requestingUserId, false);
                break;
            }

@@ -444,8 +445,8 @@ public class SettingsProvider extends ContentProvider {

            case Settings.CALL_METHOD_RESET_CONFIG: {
                final int mode = getResetModeEnforcingPermission(args);
                String tag = getSettingTag(args);
                resetConfigSetting(requestingUserId, mode, tag);
                String prefix = getSettingPrefix(args);
                resetConfigSetting(requestingUserId, mode, prefix);
                break;
            }

@@ -463,8 +464,15 @@ public class SettingsProvider extends ContentProvider {
                break;
            }

            case Settings.CALL_METHOD_DELETE_SYSTEM: {
                int rows = deleteSystemSetting(name, requestingUserId) ? 1 : 0;
            case Settings.CALL_METHOD_DELETE_CONFIG: {
                int rows  = deleteConfigSetting(name, requestingUserId, false) ? 1 : 0;
                Bundle result = new Bundle();
                result.putInt(RESULT_ROWS_DELETED, rows);
                return result;
            }

            case Settings.CALL_METHOD_DELETE_GLOBAL: {
                int rows = deleteGlobalSetting(name, requestingUserId, false) ? 1 : 0;
                Bundle result = new Bundle();
                result.putInt(RESULT_ROWS_DELETED, rows);
                return result;
@@ -477,17 +485,25 @@ public class SettingsProvider extends ContentProvider {
                return result;
            }

            case Settings.CALL_METHOD_DELETE_GLOBAL: {
                int rows = deleteGlobalSetting(name, requestingUserId, false) ? 1 : 0;
            case Settings.CALL_METHOD_DELETE_SYSTEM: {
                int rows = deleteSystemSetting(name, requestingUserId) ? 1 : 0;
                Bundle result = new Bundle();
                result.putInt(RESULT_ROWS_DELETED, rows);
                return result;
            }

            case Settings.CALL_METHOD_LIST_SYSTEM: {
            case Settings.CALL_METHOD_LIST_CONFIG: {
                String prefix = getSettingPrefix(args);
                Bundle result = new Bundle();
                result.putSerializable(
                        Settings.NameValueTable.VALUE, (HashMap) getAllConfigFlags(prefix));
                return result;
            }

            case Settings.CALL_METHOD_LIST_GLOBAL: {
                Bundle result = new Bundle();
                result.putStringArrayList(RESULT_SETTINGS_LIST,
                        buildSettingsList(getAllSystemSettings(requestingUserId, null)));
                        buildSettingsList(getAllGlobalSettings(null)));
                return result;
            }

@@ -498,10 +514,10 @@ public class SettingsProvider extends ContentProvider {
                return result;
            }

            case Settings.CALL_METHOD_LIST_GLOBAL: {
            case Settings.CALL_METHOD_LIST_SYSTEM: {
                Bundle result = new Bundle();
                result.putStringArrayList(RESULT_SETTINGS_LIST,
                        buildSettingsList(getAllGlobalSettings(null)));
                        buildSettingsList(getAllSystemSettings(requestingUserId, null)));
                return result;
            }

@@ -1061,36 +1077,47 @@ public class SettingsProvider extends ContentProvider {
                MUTATION_OPERATION_INSERT, forceNotify, 0);
    }

    private void resetConfigSetting(int requestingUserId, int mode, String tag) {
    private boolean deleteConfigSetting(String name, int requestingUserId, boolean forceNotify) {
        if (DEBUG) {
            Slog.v(LOG_TAG, "deleteConfigSetting(" + name + ", " + requestingUserId
                    + ", " + forceNotify + ")");
        }
        return mutateConfigSetting(name, null, null, false, requestingUserId,
                MUTATION_OPERATION_DELETE, forceNotify, 0);
    }

    private void resetConfigSetting(int requestingUserId, int mode, String prefix) {
        if (DEBUG) {
            Slog.v(LOG_TAG, "resetConfigSetting(" + requestingUserId + ", "
                    + mode + ", " + tag + ")");
                    + mode + ", " + prefix + ")");
        }
        mutateConfigSetting(null, null, tag, false, requestingUserId,
        mutateConfigSetting(null, null, prefix, false, requestingUserId,
                MUTATION_OPERATION_RESET, false, mode);
    }

    private boolean mutateConfigSetting(String name, String value, String tag,
    private boolean mutateConfigSetting(String name, String value, String prefix,
            boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
            int mode) {
        // TODO(b/117663715): check the new permission when it's added.
        // enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);

        // Resolve the userId on whose behalf the call is made.
        final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);

        // Perform the mutation.
        synchronized (mLock) {
            switch (operation) {
                case MUTATION_OPERATION_INSERT: {
                    return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_CONFIG,
                            UserHandle.USER_SYSTEM, name, value, tag, makeDefault,
                            UserHandle.USER_SYSTEM, name, value, null, makeDefault,
                            getCallingPackage(), forceNotify, null);
                }

                case MUTATION_OPERATION_DELETE: {
                    return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_CONFIG,
                            UserHandle.USER_SYSTEM, name, forceNotify, null);
                }

                case MUTATION_OPERATION_RESET: {
                    mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_CONFIG,
                            UserHandle.USER_SYSTEM, getCallingPackage(), mode, tag);
                            UserHandle.USER_SYSTEM, getCallingPackage(), mode, null, prefix);
                } return true;
            }
        }
@@ -1098,6 +1125,34 @@ public class SettingsProvider extends ContentProvider {
        return false;
    }

    private Map<String, String> getAllConfigFlags(@Nullable String prefix) {
        if (DEBUG) {
            Slog.v(LOG_TAG, "getAllConfigFlags() for " + prefix);
        }

        synchronized (mLock) {
            // Get the settings.
            SettingsState settingsState = mSettingsRegistry.getSettingsLocked(
                    SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM);

            List<String> names = getSettingsNamesLocked(SETTINGS_TYPE_CONFIG,
                    UserHandle.USER_SYSTEM);

            final int nameCount = names.size();
            Map<String, String> flagsToValues = new HashMap<>(names.size());

            for (int i = 0; i < nameCount; i++) {
                String name = names.get(i);
                Setting setting = settingsState.getSettingLocked(name);
                if (prefix == null || setting.getName().startsWith(prefix)) {
                    flagsToValues.put(setting.getName(), setting.getValue());
                }
            }

            return flagsToValues;
        }
    }

    private Cursor getAllGlobalSettings(String[] projection) {
        if (DEBUG) {
            Slog.v(LOG_TAG, "getAllGlobalSettings()");
@@ -2085,6 +2140,13 @@ public class SettingsProvider extends ContentProvider {
        return (args != null) ? args.getString(Settings.CALL_METHOD_TAG_KEY) : null;
    }

    private static String getSettingPrefix(Bundle args) {
        String prefix = (args != null) ? args.getString(Settings.CALL_METHOD_PREFIX_KEY) : null;
        // Append '/' to ensure we only match properties with this exact prefix.
        // i.e. "foo" should match "foo/property" but not "foobar/property"
        return prefix != null ? prefix + "/" : null;
    }

    private static boolean getSettingMakeDefault(Bundle args) {
        return (args != null) && args.getBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY);
    }
@@ -2644,6 +2706,11 @@ public class SettingsProvider extends ContentProvider {

        public void resetSettingsLocked(int type, int userId, String packageName, int mode,
                String tag) {
            resetSettingsLocked(type, userId, packageName, mode, tag, null);
        }

        public void resetSettingsLocked(int type, int userId, String packageName, int mode,
                String tag, @Nullable String prefix) {
            final int key = makeKey(type, userId);
            SettingsState settingsState = peekSettingsStateLocked(key);
            if (settingsState == null) {
@@ -2656,7 +2723,8 @@ public class SettingsProvider extends ContentProvider {
                        boolean someSettingChanged = false;
                        Setting setting = settingsState.getSettingLocked(name);
                        if (packageName.equals(setting.getPackageName())) {
                            if (tag != null && !tag.equals(setting.getTag())) {
                            if ((tag != null && !tag.equals(setting.getTag()))
                                    || (prefix != null && !setting.getName().startsWith(prefix))) {
                                continue;
                            }
                            if (settingsState.resetSettingLocked(name)) {
@@ -2676,6 +2744,9 @@ public class SettingsProvider extends ContentProvider {
                        Setting setting = settingsState.getSettingLocked(name);
                        if (!SettingsState.isSystemPackage(getContext(),
                                setting.getPackageName())) {
                            if (prefix != null && !setting.getName().startsWith(prefix)) {
                                continue;
                            }
                            if (settingsState.resetSettingLocked(name)) {
                                someSettingChanged = true;
                                notifyForSettingsChange(key, name);
@@ -2693,6 +2764,9 @@ public class SettingsProvider extends ContentProvider {
                        Setting setting = settingsState.getSettingLocked(name);
                        if (!SettingsState.isSystemPackage(getContext(),
                                setting.getPackageName())) {
                            if (prefix != null && !setting.getName().startsWith(prefix)) {
                                continue;
                            }
                            if (setting.isDefaultFromSystem()) {
                                if (settingsState.resetSettingLocked(name)) {
                                    someSettingChanged = true;
@@ -2713,6 +2787,9 @@ public class SettingsProvider extends ContentProvider {
                    for (String name : settingsState.getSettingNamesLocked()) {
                        Setting setting = settingsState.getSettingLocked(name);
                        boolean someSettingChanged = false;
                        if (prefix != null && !setting.getName().startsWith(prefix)) {
                            continue;
                        }
                        if (setting.isDefaultFromSystem()) {
                            if (settingsState.resetSettingLocked(name)) {
                                someSettingChanged = true;