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

Commit e7e7f513 authored by Lorenzo Colitti's avatar Lorenzo Colitti
Browse files

Add callback support to FakeSettingsProvider.

Users of FakeSettingsProvider can optionally provide a callback
that will be called whenever a setting's value has changed.
This can be used to implement SettingsObservers.

Bug: 397521796
Flag: TEST_ONLY
Test: new unit tests
Change-Id: Ifebdb41ede706190daa4d7740ceb4f662ae29d25
parent ab40b5f2
Loading
Loading
Loading
Loading
+52 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.internal.util.test;

import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;

import static org.junit.Assert.assertEquals;
@@ -37,6 +38,8 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.ArrayList;

/**
 * Unit tests for FakeSettingsProvider.
 */
@@ -52,10 +55,13 @@ public class FakeSettingsProviderTest {

    private MockContentResolver mCr;

    private ArrayList<String> mCallbacks;

    @Before
    public void setUp() throws Exception {
        FakeSettingsProvider.clearSettingsProvider();
        mCr = new MockContentResolver();
        mCr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
        mCallbacks = new ArrayList<>();
    }

    private void assertSystemSettingNotFound(String name) {
@@ -70,6 +76,7 @@ public class FakeSettingsProviderTest {
    @Test
    @SmallTest
    public void testBasicOperation() throws Exception {
        mCr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
        assertSystemSettingNotFound(SYSTEM_SETTING);

        // Check that fake settings can be written and read back.
@@ -101,6 +108,7 @@ public class FakeSettingsProviderTest {
    @Test
    @SmallTest
    public void testMultiUserOperation() {
        mCr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
        Settings.Secure.putStringForUser(mCr, SECURE_SETTING, "user0Setting", 0);
        Settings.Secure.putStringForUser(mCr, SECURE_SETTING, "user1Setting", 1);
        assertEquals("user0Setting", Settings.Secure.getStringForUser(mCr, SECURE_SETTING, 0));
@@ -135,4 +143,47 @@ public class FakeSettingsProviderTest {
        assertEquals("newGlobalSetting", Settings.Global.getStringForUser(mCr, GLOBAL_SETTING, 42));

    }

    private void assertCallbackReceived(String expectedCallback) {
        assertFalse("No callbacks received", mCallbacks.isEmpty());
        assertEquals(expectedCallback, mCallbacks.removeFirst());
    }

    private void assertNoCallbackReceived() {
        assertEquals(0, mCallbacks.size());
    }

    @Test
    @SmallTest
    public void testCallbacks() {
        mCr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider((userId, uri) ->
                mCallbacks.add(userId + ":" + uri.toString())));

        Settings.Secure.putStringForUser(mCr, SECURE_SETTING, "value", 1);
        assertCallbackReceived("1:content://settings/secure/bluetooth_name");

        Settings.Secure.putStringForUser(mCr, SECURE_SETTING, "newvalue", 1);
        assertCallbackReceived("1:content://settings/secure/bluetooth_name");

        Settings.Secure.putStringForUser(mCr, SECURE_SETTING, "value", 2);
        assertCallbackReceived("2:content://settings/secure/bluetooth_name");

        // Callback is not called if value doesn't change.
        Settings.Secure.putStringForUser(mCr, SECURE_SETTING, "newvalue", 1);
        assertNoCallbackReceived();

        Settings.Secure.putStringForUser(mCr, SECURE_SETTING, null, 2);
        assertCallbackReceived("2:content://settings/secure/bluetooth_name");

        Settings.Secure.putStringForUser(mCr, SECURE_SETTING, null, 1);
        assertCallbackReceived("1:content://settings/secure/bluetooth_name");

        Settings.Secure.putStringForUser(mCr, SECURE_SETTING, null, 1);
        assertNoCallbackReceived();

        final int currentUserId = UserHandle.getUserId(Process.myUid());
        Settings.System.putString(mCr, SYSTEM_SETTING, "value");
        assertCallbackReceived(currentUserId + ":" + "content://settings/system/screen_brightness");

    }
}
+19 −9
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.internal.util.test;

import android.annotation.NonNull;
import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
@@ -25,6 +26,7 @@ import android.util.ArrayMap;
import android.util.Log;

import java.util.Map;
import java.util.Objects;

/**
 * Fake for system settings.
@@ -58,13 +60,6 @@ import java.util.Map;
 * class only fetches the content provider from the passed-in ContentResolver the first time it's
 * used, and after that stores it in a per-process static. If this needs to be used in this case,
 * then call {@link #clearSettingsProvider()} before and after using this.
 *
 * TODO: evaluate implementing settings change notifications. This would require:
 *
 * 1. Making ContentResolver#registerContentObserver non-final and overriding it in
 *    MockContentResolver.
 * 2. Making FakeSettingsProvider take a ContentResolver argument.
 * 3. Calling ContentResolver#notifyChange(getUriFor(table, arg), ...) on every settings change.
 */
public class FakeSettingsProvider extends MockContentProvider {

@@ -81,10 +76,23 @@ public class FakeSettingsProvider extends MockContentProvider {
     */
    private final Map<String, Map<Integer, Map<String, String>>> mDb = new ArrayMap<>();

    public FakeSettingsProvider() {
    public interface Callback {
        /** Called whenever a setting's value changes. */
        void onUriChanged(int userId, Uri uri);
    }

    private final Callback mCallback;

    public FakeSettingsProvider(@NonNull Callback callback) {
        Objects.requireNonNull(callback);
        for (int i = 0; i < TABLES.length; i++) {
            mDb.put(TABLES[i], new ArrayMap<>());
        }
        mCallback = callback;
    }

    public FakeSettingsProvider() {
        this((user, uri) -> {});
    }

    private Uri getUriFor(String table, String key) {
@@ -160,7 +168,8 @@ public class FakeSettingsProvider extends MockContentProvider {
                value = extras.getString(Settings.NameValueTable.VALUE, null);
                final boolean changed;
                if (value != null) {
                    changed = !value.equals(mDb.get(table)
                    changed = !value.equals(
                            mDb.get(table)
                            .computeIfAbsent(userId, (u) -> new ArrayMap<>())
                            .put(arg, value));
                    if (DBG) {
@@ -175,6 +184,7 @@ public class FakeSettingsProvider extends MockContentProvider {
                                arg));
                    }
                }
                if (changed) mCallback.onUriChanged(userId, getUriFor(table, arg));
                break;
            default:
                throw new UnsupportedOperationException("Unknown command " + method);