Loading core/tests/utiltests/src/com/android/internal/util/test/FakeSettingsProviderTest.java +78 −7 Original line number Diff line number Diff line Loading @@ -16,10 +16,14 @@ package com.android.internal.util.test; import static junit.framework.Assert.assertNull; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import android.content.ContentProvider; import android.os.Process; import android.os.UserHandle; import android.platform.test.annotations.DisabledOnRavenwood; import android.platform.test.ravenwood.RavenwoodRule; import android.provider.Settings; Loading @@ -42,6 +46,10 @@ public class FakeSettingsProviderTest { @Rule public final RavenwoodRule mRavenwood = new RavenwoodRule(); private static final String SYSTEM_SETTING = Settings.System.SCREEN_BRIGHTNESS; private static final String SECURE_SETTING = Settings.Secure.BLUETOOTH_NAME; private static final String GLOBAL_SETTING = Settings.Global.MOBILE_DATA_ALWAYS_ON; private MockContentResolver mCr; @Before Loading @@ -50,18 +58,81 @@ public class FakeSettingsProviderTest { mCr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); } private void assertSystemSettingNotFound(String name) { try { Settings.System.getInt(mCr, name); fail("Setting " + name + " unexpectedly present."); } catch (Settings.SettingNotFoundException expected) { // Expected behaviour. } } @Test @SmallTest public void testBasicOperation() throws Exception { String settingName = Settings.System.SCREEN_BRIGHTNESS; assertSystemSettingNotFound(SYSTEM_SETTING); // Check that fake settings can be written and read back. Settings.System.putInt(mCr, SYSTEM_SETTING, 123); assertEquals(123, Settings.System.getInt(mCr, SYSTEM_SETTING)); // Fake settings can be removed. Settings.System.putString(mCr, SYSTEM_SETTING, null); assertSystemSettingNotFound(SYSTEM_SETTING); // Removal is a no-op if the setting does not exist. Settings.System.putString(mCr, SYSTEM_SETTING, null); assertSystemSettingNotFound(SYSTEM_SETTING); // Corner case: removing a setting in a table that never existed. Settings.Secure.putString(mCr, SECURE_SETTING, null); } private void assertUserHandleUnsupported(UserHandle userHandle, String settingName) { try { Settings.System.getInt(mCr, settingName); fail("FakeSettingsProvider should start off empty."); } catch (Settings.SettingNotFoundException expected) {} Settings.Secure.putStringForUser(mCr, settingName, "currentUserSetting", userHandle.getIdentifier()); fail("UserHandle " + userHandle + " is unsupported and should throw"); } catch (UnsupportedOperationException expected) { // Expected behaviour. } } @Test @SmallTest public void testMultiUserOperation() { 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)); assertEquals("user1Setting", Settings.Secure.getStringForUser(mCr, SECURE_SETTING, 1)); assertNull(Settings.Secure.getStringForUser(mCr, SECURE_SETTING, 2)); final int currentUserId = UserHandle.getUserId(Process.myUid()); Settings.Secure.putStringForUser(mCr, SECURE_SETTING, "currentUserSetting", currentUserId); assertEquals("currentUserSetting", Settings.Secure.getString(mCr, SECURE_SETTING)); Settings.Secure.putString(mCr, SECURE_SETTING, "newValue"); assertEquals("newValue", Settings.Secure.getStringForUser(mCr, SECURE_SETTING, currentUserId)); Settings.Secure.putString(mCr, SECURE_SETTING, "newValue2"); assertEquals("newValue2", Settings.Secure.getStringForUser(mCr, SECURE_SETTING, UserHandle.USER_CURRENT)); Settings.Secure.putStringForUser(mCr, SECURE_SETTING, "newValue3", UserHandle.USER_CURRENT); assertEquals("newValue3", Settings.Secure.getString(mCr, SECURE_SETTING)); assertUserHandleUnsupported(UserHandle.ALL, SECURE_SETTING); assertUserHandleUnsupported(UserHandle.CURRENT_OR_SELF, SECURE_SETTING); Settings.Global.putStringForUser(mCr, GLOBAL_SETTING, "globalSetting", 42); assertEquals("globalSetting", Settings.Global.getStringForUser(mCr, GLOBAL_SETTING, 1)); assertEquals("globalSetting", Settings.Global.getStringForUser(mCr, GLOBAL_SETTING, UserHandle.USER_CURRENT)); assertEquals("globalSetting", Settings.Global.getString(mCr, GLOBAL_SETTING)); Settings.Global.putString(mCr, GLOBAL_SETTING, "newGlobalSetting"); assertEquals("newGlobalSetting", Settings.Global.getStringForUser(mCr, GLOBAL_SETTING, 42)); // Check that fake settings can be written and read back. Settings.System.putInt(mCr, settingName, 123); assertEquals(123, Settings.System.getInt(mCr, settingName)); } } tests/utils/testutils/java/com/android/internal/util/test/FakeSettingsProvider.java +51 −14 Original line number Diff line number Diff line Loading @@ -18,11 +18,13 @@ package com.android.internal.util.test; import android.net.Uri; import android.os.Bundle; import android.os.UserHandle; import android.provider.Settings; import android.test.mock.MockContentProvider; import android.util.ArrayMap; import android.util.Log; import java.util.HashMap; import java.util.Map; /** * Fake for system settings. Loading Loading @@ -69,12 +71,19 @@ public class FakeSettingsProvider extends MockContentProvider { private static final String TAG = FakeSettingsProvider.class.getSimpleName(); private static final boolean DBG = false; private static final String[] TABLES = { "system", "secure", "global" }; private static final Map<String, String> EMPTY_MAP = Map.of(); private final HashMap<String, HashMap<String, String>> mTables = new HashMap<>(); /** * Stores settings values. Keyed by: * - Table name (e.g., "system") * - User ID (e.g., USER_SYSTEM) * - Setting name (e.g., "screen_brightness") */ private final Map<String, Map<Integer, Map<String, String>>> mDb = new ArrayMap<>(); public FakeSettingsProvider() { for (int i = 0; i < TABLES.length; i++) { mTables.put(TABLES[i], new HashMap<String, String>()); mDb.put(TABLES[i], new ArrayMap<>()); } } Loading Loading @@ -108,6 +117,24 @@ public class FakeSettingsProvider extends MockContentProvider { Settings.System.clearProviderForTest(); } private int getUserId(String table, Bundle extras) { // Global settings always use USER_SYSTEM, see SettingsProvider#getGlobalSetting. if ("global".equals(table)) { return UserHandle.USER_SYSTEM; } // When running in process, Settings does not set the userId. Default to current userId. final int myUserId = UserHandle.getCallingUserId(); int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, myUserId); // Many tests write settings before initializing users, so treat USER_NULL as current user. if (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_NULL) { return myUserId; } if (userId < 0) { throw new UnsupportedOperationException("userId " + userId + " is not a real user"); } return userId; } public Bundle call(String method, String arg, Bundle extras) { // Methods are "GET_system", "GET_global", "PUT_secure", etc. String[] commands = method.split("_", 2); Loading @@ -116,27 +143,37 @@ public class FakeSettingsProvider extends MockContentProvider { Bundle out = new Bundle(); String value; final int userId = getUserId(table, extras); switch (op) { case "GET": value = mTables.get(table).get(arg); value = mDb.get(table).getOrDefault(userId, EMPTY_MAP).get(arg); if (value != null) { if (DBG) { Log.d(TAG, String.format("Returning fake setting %s.%s = %s", table, arg, value)); } out.putString(Settings.NameValueTable.VALUE, value); } if (DBG) { Log.d(TAG, String.format("Returning fake setting %d:%s.%s = %s", userId, table, arg, (value == null) ? "null" : "\"" + value + "\"")); } break; case "PUT": value = extras.getString(Settings.NameValueTable.VALUE, null); final boolean changed; if (value != null) { changed = !value.equals(mDb.get(table) .computeIfAbsent(userId, (u) -> new ArrayMap<>()) .put(arg, value)); if (DBG) { Log.d(TAG, String.format("Inserting fake setting %s.%s = %s", table, arg, value)); Log.d(TAG, String.format("Inserting fake setting %d:%s.%s = \"%s\"", userId, table, arg, value)); } if (value != null) { mTables.get(table).put(arg, value); } else { mTables.get(table).remove(arg); final Map<String, String> perUserTable = mDb.get(table).get(userId); changed = perUserTable != null && perUserTable.remove(arg) != null; if (DBG) { Log.d(TAG, String.format("Removing fake setting %d:%s.%s", userId, table, arg)); } } break; default: Loading Loading
core/tests/utiltests/src/com/android/internal/util/test/FakeSettingsProviderTest.java +78 −7 Original line number Diff line number Diff line Loading @@ -16,10 +16,14 @@ package com.android.internal.util.test; import static junit.framework.Assert.assertNull; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import android.content.ContentProvider; import android.os.Process; import android.os.UserHandle; import android.platform.test.annotations.DisabledOnRavenwood; import android.platform.test.ravenwood.RavenwoodRule; import android.provider.Settings; Loading @@ -42,6 +46,10 @@ public class FakeSettingsProviderTest { @Rule public final RavenwoodRule mRavenwood = new RavenwoodRule(); private static final String SYSTEM_SETTING = Settings.System.SCREEN_BRIGHTNESS; private static final String SECURE_SETTING = Settings.Secure.BLUETOOTH_NAME; private static final String GLOBAL_SETTING = Settings.Global.MOBILE_DATA_ALWAYS_ON; private MockContentResolver mCr; @Before Loading @@ -50,18 +58,81 @@ public class FakeSettingsProviderTest { mCr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); } private void assertSystemSettingNotFound(String name) { try { Settings.System.getInt(mCr, name); fail("Setting " + name + " unexpectedly present."); } catch (Settings.SettingNotFoundException expected) { // Expected behaviour. } } @Test @SmallTest public void testBasicOperation() throws Exception { String settingName = Settings.System.SCREEN_BRIGHTNESS; assertSystemSettingNotFound(SYSTEM_SETTING); // Check that fake settings can be written and read back. Settings.System.putInt(mCr, SYSTEM_SETTING, 123); assertEquals(123, Settings.System.getInt(mCr, SYSTEM_SETTING)); // Fake settings can be removed. Settings.System.putString(mCr, SYSTEM_SETTING, null); assertSystemSettingNotFound(SYSTEM_SETTING); // Removal is a no-op if the setting does not exist. Settings.System.putString(mCr, SYSTEM_SETTING, null); assertSystemSettingNotFound(SYSTEM_SETTING); // Corner case: removing a setting in a table that never existed. Settings.Secure.putString(mCr, SECURE_SETTING, null); } private void assertUserHandleUnsupported(UserHandle userHandle, String settingName) { try { Settings.System.getInt(mCr, settingName); fail("FakeSettingsProvider should start off empty."); } catch (Settings.SettingNotFoundException expected) {} Settings.Secure.putStringForUser(mCr, settingName, "currentUserSetting", userHandle.getIdentifier()); fail("UserHandle " + userHandle + " is unsupported and should throw"); } catch (UnsupportedOperationException expected) { // Expected behaviour. } } @Test @SmallTest public void testMultiUserOperation() { 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)); assertEquals("user1Setting", Settings.Secure.getStringForUser(mCr, SECURE_SETTING, 1)); assertNull(Settings.Secure.getStringForUser(mCr, SECURE_SETTING, 2)); final int currentUserId = UserHandle.getUserId(Process.myUid()); Settings.Secure.putStringForUser(mCr, SECURE_SETTING, "currentUserSetting", currentUserId); assertEquals("currentUserSetting", Settings.Secure.getString(mCr, SECURE_SETTING)); Settings.Secure.putString(mCr, SECURE_SETTING, "newValue"); assertEquals("newValue", Settings.Secure.getStringForUser(mCr, SECURE_SETTING, currentUserId)); Settings.Secure.putString(mCr, SECURE_SETTING, "newValue2"); assertEquals("newValue2", Settings.Secure.getStringForUser(mCr, SECURE_SETTING, UserHandle.USER_CURRENT)); Settings.Secure.putStringForUser(mCr, SECURE_SETTING, "newValue3", UserHandle.USER_CURRENT); assertEquals("newValue3", Settings.Secure.getString(mCr, SECURE_SETTING)); assertUserHandleUnsupported(UserHandle.ALL, SECURE_SETTING); assertUserHandleUnsupported(UserHandle.CURRENT_OR_SELF, SECURE_SETTING); Settings.Global.putStringForUser(mCr, GLOBAL_SETTING, "globalSetting", 42); assertEquals("globalSetting", Settings.Global.getStringForUser(mCr, GLOBAL_SETTING, 1)); assertEquals("globalSetting", Settings.Global.getStringForUser(mCr, GLOBAL_SETTING, UserHandle.USER_CURRENT)); assertEquals("globalSetting", Settings.Global.getString(mCr, GLOBAL_SETTING)); Settings.Global.putString(mCr, GLOBAL_SETTING, "newGlobalSetting"); assertEquals("newGlobalSetting", Settings.Global.getStringForUser(mCr, GLOBAL_SETTING, 42)); // Check that fake settings can be written and read back. Settings.System.putInt(mCr, settingName, 123); assertEquals(123, Settings.System.getInt(mCr, settingName)); } }
tests/utils/testutils/java/com/android/internal/util/test/FakeSettingsProvider.java +51 −14 Original line number Diff line number Diff line Loading @@ -18,11 +18,13 @@ package com.android.internal.util.test; import android.net.Uri; import android.os.Bundle; import android.os.UserHandle; import android.provider.Settings; import android.test.mock.MockContentProvider; import android.util.ArrayMap; import android.util.Log; import java.util.HashMap; import java.util.Map; /** * Fake for system settings. Loading Loading @@ -69,12 +71,19 @@ public class FakeSettingsProvider extends MockContentProvider { private static final String TAG = FakeSettingsProvider.class.getSimpleName(); private static final boolean DBG = false; private static final String[] TABLES = { "system", "secure", "global" }; private static final Map<String, String> EMPTY_MAP = Map.of(); private final HashMap<String, HashMap<String, String>> mTables = new HashMap<>(); /** * Stores settings values. Keyed by: * - Table name (e.g., "system") * - User ID (e.g., USER_SYSTEM) * - Setting name (e.g., "screen_brightness") */ private final Map<String, Map<Integer, Map<String, String>>> mDb = new ArrayMap<>(); public FakeSettingsProvider() { for (int i = 0; i < TABLES.length; i++) { mTables.put(TABLES[i], new HashMap<String, String>()); mDb.put(TABLES[i], new ArrayMap<>()); } } Loading Loading @@ -108,6 +117,24 @@ public class FakeSettingsProvider extends MockContentProvider { Settings.System.clearProviderForTest(); } private int getUserId(String table, Bundle extras) { // Global settings always use USER_SYSTEM, see SettingsProvider#getGlobalSetting. if ("global".equals(table)) { return UserHandle.USER_SYSTEM; } // When running in process, Settings does not set the userId. Default to current userId. final int myUserId = UserHandle.getCallingUserId(); int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, myUserId); // Many tests write settings before initializing users, so treat USER_NULL as current user. if (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_NULL) { return myUserId; } if (userId < 0) { throw new UnsupportedOperationException("userId " + userId + " is not a real user"); } return userId; } public Bundle call(String method, String arg, Bundle extras) { // Methods are "GET_system", "GET_global", "PUT_secure", etc. String[] commands = method.split("_", 2); Loading @@ -116,27 +143,37 @@ public class FakeSettingsProvider extends MockContentProvider { Bundle out = new Bundle(); String value; final int userId = getUserId(table, extras); switch (op) { case "GET": value = mTables.get(table).get(arg); value = mDb.get(table).getOrDefault(userId, EMPTY_MAP).get(arg); if (value != null) { if (DBG) { Log.d(TAG, String.format("Returning fake setting %s.%s = %s", table, arg, value)); } out.putString(Settings.NameValueTable.VALUE, value); } if (DBG) { Log.d(TAG, String.format("Returning fake setting %d:%s.%s = %s", userId, table, arg, (value == null) ? "null" : "\"" + value + "\"")); } break; case "PUT": value = extras.getString(Settings.NameValueTable.VALUE, null); final boolean changed; if (value != null) { changed = !value.equals(mDb.get(table) .computeIfAbsent(userId, (u) -> new ArrayMap<>()) .put(arg, value)); if (DBG) { Log.d(TAG, String.format("Inserting fake setting %s.%s = %s", table, arg, value)); Log.d(TAG, String.format("Inserting fake setting %d:%s.%s = \"%s\"", userId, table, arg, value)); } if (value != null) { mTables.get(table).put(arg, value); } else { mTables.get(table).remove(arg); final Map<String, String> perUserTable = mDb.get(table).get(userId); changed = perUserTable != null && perUserTable.remove(arg) != null; if (DBG) { Log.d(TAG, String.format("Removing fake setting %d:%s.%s", userId, table, arg)); } } break; default: Loading