Convert SettingsProxy and UserSettingsProxy to Kotlin.

Conversion to Kotlin is the first step towards migrating
register/unregister content observer APIs to background thread.

Test: atest / manual flash
Bug: 330299944
Flag: NONE Java to Kotlin migration, flag not needed.
Change-Id: I793d4bd9ad3d6ac9e39c3e97b7e4edc5b794ba84
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 8b60ed0..c4929a1 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -766,6 +766,7 @@
     ],
     static_libs: [
         "RoboTestLibraries",
+        "mockito-kotlin2",
     ],
     libs: [
         "android.test.runner",
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
deleted file mode 100644
index aeed78a..0000000
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
+++ /dev/null
@@ -1,432 +0,0 @@
-/*
- * Copyright (C) 2020 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 com.android.systemui.util.settings;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ContentResolver;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.provider.Settings;
-
-/**
- * Used to interact with mainly with Settings.Global, but can also be used for Settings.System
- * and Settings.Secure. To use the per-user System and Secure settings, {@link UserSettingsProxy}
- * must be used instead.
- * <p>
- * This interface can be implemented to give instance method (instead of static method) versions
- * of Settings.Global. It can be injected into class constructors and then faked or mocked as needed
- * in tests.
- * <p>
- * You can ask for {@link GlobalSettings} to be injected as needed.
- * <p>
- * This class also provides {@link #registerContentObserver(String, ContentObserver)} methods,
- * normally found on {@link ContentResolver} instances, unifying setting related actions in one
- * place.
- */
-public interface SettingsProxy {
-
-    /**
-     * Returns the {@link ContentResolver} this instance was constructed with.
-     */
-    ContentResolver getContentResolver();
-
-    /**
-     * Construct the content URI for a particular name/value pair,
-     * useful for monitoring changes with a ContentObserver.
-     * @param name to look up in the table
-     * @return the corresponding content URI, or null if not present
-     */
-    Uri getUriFor(String name);
-
-    /**
-     * Convenience wrapper around
-     * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.'
-     * <p>
-     * Implicitly calls {@link #getUriFor(String)} on the passed in name.
-     */
-    default void registerContentObserver(String name, ContentObserver settingsObserver) {
-        registerContentObserver(getUriFor(name), settingsObserver);
-    }
-
-    /**
-     * Convenience wrapper around
-     * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.'
-     */
-    default void registerContentObserver(Uri uri, ContentObserver settingsObserver) {
-        registerContentObserver(uri, false, settingsObserver);
-    }
-
-    /**
-     * Convenience wrapper around
-     * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.'
-     * <p>
-     * Implicitly calls {@link #getUriFor(String)} on the passed in name.
-     */
-    default void registerContentObserver(String name, boolean notifyForDescendants,
-            ContentObserver settingsObserver) {
-        registerContentObserver(getUriFor(name), notifyForDescendants, settingsObserver);
-    }
-
-    /**
-     * Convenience wrapper around
-     * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.'
-     */
-    default void registerContentObserver(Uri uri, boolean notifyForDescendants,
-            ContentObserver settingsObserver) {
-        getContentResolver().registerContentObserver(
-                uri, notifyForDescendants, settingsObserver);
-    }
-
-    /** See {@link ContentResolver#unregisterContentObserver(ContentObserver)}. */
-    default void unregisterContentObserver(ContentObserver settingsObserver) {
-        getContentResolver().unregisterContentObserver(settingsObserver);
-    }
-
-    /**
-     * Look up a name in the database.
-     * @param name to look up in the table
-     * @return the corresponding value, or null if not present
-     */
-    @Nullable
-    String getString(String name);
-
-    /**
-     * Store a name/value pair into the database.
-     * @param name to store
-     * @param value to associate with the name
-     * @return true if the value was set, false on database errors
-     */
-    boolean putString(String name, String value);
-
-    /**
-     * Store a name/value pair into the database.
-     * <p>
-     * The method takes an optional tag to associate with the setting
-     * which can be used to clear only settings made by your package and
-     * associated with this tag by passing the tag to {@link
-     * #resetToDefaults(String)}. Anyone can override
-     * the current tag. Also if another package changes the setting
-     * then the tag will be set to the one specified in the set call
-     * which can be null. Also any of the settings setters that do not
-     * take a tag as an argument effectively clears the tag.
-     * </p><p>
-     * For example, if you set settings A and B with tags T1 and T2 and
-     * another app changes setting A (potentially to the same value), it
-     * can assign to it a tag T3 (note that now the package that changed
-     * the setting is not yours). Now if you reset your changes for T1 and
-     * T2 only setting B will be reset and A not (as it was changed by
-     * another package) but since A did not change you are in the desired
-     * initial state. Now if the other app changes the value of A (assuming
-     * you registered an observer in the beginning) you would detect that
-     * the setting was changed by another app and handle this appropriately
-     * (ignore, set back to some value, etc).
-     * </p><p>
-     * Also the method takes an argument whether to make the value the
-     * default for this setting. If the system already specified a default
-     * value, then the one passed in here will <strong>not</strong>
-     * be set as the default.
-     * </p>
-     *
-     * @param name to store.
-     * @param value to associate with the name.
-     * @param tag to associate with the setting.
-     * @param makeDefault whether to make the value the default one.
-     * @return true if the value was set, false on database errors.
-     *
-     * @see #resetToDefaults(String)
-     *
-     */
-    boolean putString(@NonNull String name, @Nullable String value, @Nullable String tag,
-            boolean makeDefault);
-
-    /**
-     * Convenience function for retrieving a single secure settings value
-     * as an integer.  Note that internally setting values are always
-     * stored as strings; this function converts the string to an integer
-     * for you.  The default value will be returned if the setting is
-     * not defined or not an integer.
-     *
-     * @param name The name of the setting to retrieve.
-     * @param def Value to return if the setting is not defined.
-     *
-     * @return The setting's current value, or 'def' if it is not defined
-     * or not a valid integer.
-     */
-    default int getInt(String name, int def) {
-        String v = getString(name);
-        try {
-            return v != null ? Integer.parseInt(v) : def;
-        } catch (NumberFormatException e) {
-            return def;
-        }
-    }
-
-    /**
-     * Convenience function for retrieving a single secure settings value
-     * as an integer.  Note that internally setting values are always
-     * stored as strings; this function converts the string to an integer
-     * for you.
-     * <p>
-     * This version does not take a default value.  If the setting has not
-     * been set, or the string value is not a number,
-     * it throws {@link Settings.SettingNotFoundException}.
-     *
-     * @param name The name of the setting to retrieve.
-     *
-     * @throws Settings.SettingNotFoundException Thrown if a setting by the given
-     * name can't be found or the setting value is not an integer.
-     *
-     * @return The setting's current value.
-     */
-    default int getInt(String name)
-            throws Settings.SettingNotFoundException {
-        String v = getString(name);
-        try {
-            return Integer.parseInt(v);
-        } catch (NumberFormatException e) {
-            throw new Settings.SettingNotFoundException(name);
-        }
-    }
-
-    /**
-     * Convenience function for updating a single settings value as an
-     * integer. This will either create a new entry in the table if the
-     * given name does not exist, or modify the value of the existing row
-     * with that name.  Note that internally setting values are always
-     * stored as strings, so this function converts the given value to a
-     * string before storing it.
-     *
-     * @param name The name of the setting to modify.
-     * @param value The new value for the setting.
-     * @return true if the value was set, false on database errors
-     */
-    default boolean putInt(String name, int value) {
-        return putString(name, Integer.toString(value));
-    }
-
-    /**
-     * Convenience function for retrieving a single secure settings value
-     * as a boolean.  Note that internally setting values are always
-     * stored as strings; this function converts the string to a boolean
-     * for you.  The default value will be returned if the setting is
-     * not defined or not a boolean.
-     *
-     * @param name The name of the setting to retrieve.
-     * @param def Value to return if the setting is not defined.
-     *
-     * @return The setting's current value, or 'def' if it is not defined
-     * or not a valid boolean.
-     */
-    default boolean getBool(String name, boolean def) {
-        return getInt(name, def ? 1 : 0) != 0;
-    }
-
-    /**
-     * Convenience function for retrieving a single secure settings value
-     * as a boolean.  Note that internally setting values are always
-     * stored as strings; this function converts the string to a boolean
-     * for you.
-     * <p>
-     * This version does not take a default value.  If the setting has not
-     * been set, or the string value is not a number,
-     * it throws {@link Settings.SettingNotFoundException}.
-     *
-     * @param name The name of the setting to retrieve.
-     *
-     * @throws Settings.SettingNotFoundException Thrown if a setting by the given
-     * name can't be found or the setting value is not a boolean.
-     *
-     * @return The setting's current value.
-     */
-    default boolean getBool(String name)
-            throws Settings.SettingNotFoundException {
-        return getInt(name) != 0;
-    }
-
-    /**
-     * Convenience function for updating a single settings value as a
-     * boolean. This will either create a new entry in the table if the
-     * given name does not exist, or modify the value of the existing row
-     * with that name.  Note that internally setting values are always
-     * stored as strings, so this function converts the given value to a
-     * string before storing it.
-     *
-     * @param name The name of the setting to modify.
-     * @param value The new value for the setting.
-     * @return true if the value was set, false on database errors
-     */
-    default boolean putBool(String name, boolean value) {
-        return putInt(name, value ? 1 : 0);
-    }
-
-    /**
-     * Convenience function for retrieving a single secure settings value
-     * as a {@code long}.  Note that internally setting values are always
-     * stored as strings; this function converts the string to a {@code long}
-     * for you.  The default value will be returned if the setting is
-     * not defined or not a {@code long}.
-     *
-     * @param name The name of the setting to retrieve.
-     * @param def Value to return if the setting is not defined.
-     *
-     * @return The setting's current value, or 'def' if it is not defined
-     * or not a valid {@code long}.
-     */
-    default long getLong(String name, long def) {
-        String valString = getString(name);
-        return parseLongOrUseDefault(valString, def);
-    }
-
-    /** Convert a string to a long, or uses a default if the string is malformed or null */
-    static long parseLongOrUseDefault(String valString, long def) {
-        long value;
-        try {
-            value = valString != null ? Long.parseLong(valString) : def;
-        } catch (NumberFormatException e) {
-            value = def;
-        }
-        return value;
-    }
-
-    /**
-     * Convenience function for retrieving a single secure settings value
-     * as a {@code long}.  Note that internally setting values are always
-     * stored as strings; this function converts the string to a {@code long}
-     * for you.
-     * <p>
-     * This version does not take a default value.  If the setting has not
-     * been set, or the string value is not a number,
-     * it throws {@link Settings.SettingNotFoundException}.
-     *
-     * @param name The name of the setting to retrieve.
-     *
-     * @return The setting's current value.
-     * @throws Settings.SettingNotFoundException Thrown if a setting by the given
-     * name can't be found or the setting value is not an integer.
-     */
-    default long getLong(String name)
-            throws Settings.SettingNotFoundException {
-        String valString = getString(name);
-        return parseLongOrThrow(name, valString);
-    }
-
-    /** Convert a string to a long, or throws an exception if the string is malformed or null */
-    static long parseLongOrThrow(String name, String valString)
-            throws Settings.SettingNotFoundException {
-        try {
-            return Long.parseLong(valString);
-        } catch (NumberFormatException e) {
-            throw new Settings.SettingNotFoundException(name);
-        }
-    }
-
-    /**
-     * Convenience function for updating a secure settings value as a long
-     * integer. This will either create a new entry in the table if the
-     * given name does not exist, or modify the value of the existing row
-     * with that name.  Note that internally setting values are always
-     * stored as strings, so this function converts the given value to a
-     * string before storing it.
-     *
-     * @param name The name of the setting to modify.
-     * @param value The new value for the setting.
-     * @return true if the value was set, false on database errors
-     */
-    default boolean putLong(String name, long value) {
-        return putString(name, Long.toString(value));
-    }
-
-    /**
-     * Convenience function for retrieving a single secure settings value
-     * as a floating point number.  Note that internally setting values are
-     * always stored as strings; this function converts the string to an
-     * float for you. The default value will be returned if the setting
-     * is not defined or not a valid float.
-     *
-     * @param name The name of the setting to retrieve.
-     * @param def Value to return if the setting is not defined.
-     *
-     * @return The setting's current value, or 'def' if it is not defined
-     * or not a valid float.
-     */
-    default float getFloat(String name, float def) {
-        String v = getString(name);
-        return parseFloat(v, def);
-    }
-
-    /** Convert a string to a float, or uses a default if the string is malformed or null */
-    static float parseFloat(String v, float def) {
-        try {
-            return v != null ? Float.parseFloat(v) : def;
-        } catch (NumberFormatException e) {
-            return def;
-        }
-    }
-
-    /**
-     * Convenience function for retrieving a single secure settings value
-     * as a float.  Note that internally setting values are always
-     * stored as strings; this function converts the string to a float
-     * for you.
-     * <p>
-     * This version does not take a default value.  If the setting has not
-     * been set, or the string value is not a number,
-     * it throws {@link Settings.SettingNotFoundException}.
-     *
-     * @param name The name of the setting to retrieve.
-     *
-     * @throws Settings.SettingNotFoundException Thrown if a setting by the given
-     * name can't be found or the setting value is not a float.
-     *
-     * @return The setting's current value.
-     */
-    default float getFloat(String name)
-            throws Settings.SettingNotFoundException {
-        String v = getString(name);
-        return parseFloatOrThrow(name, v);
-    }
-
-    /** Convert a string to a float, or throws an exception if the string is malformed or null */
-    static float parseFloatOrThrow(String name, String v)
-            throws Settings.SettingNotFoundException {
-        if (v == null) {
-            throw new Settings.SettingNotFoundException(name);
-        }
-        try {
-            return Float.parseFloat(v);
-        } catch (NumberFormatException e) {
-            throw new Settings.SettingNotFoundException(name);
-        }
-    }
-
-    /**
-     * Convenience function for updating a single settings value as a
-     * floating point number. This will either create a new entry in the
-     * table if the given name does not exist, or modify the value of the
-     * existing row with that name.  Note that internally setting values
-     * are always stored as strings, so this function converts the given
-     * value to a string before storing it.
-     *
-     * @param name The name of the setting to modify.
-     * @param value The new value for the setting.
-     * @return true if the value was set, false on database errors
-     */
-    default boolean putFloat(String name, float value) {
-        return putString(name, Float.toString(value));
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
new file mode 100644
index 0000000..ec89610
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2020 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 com.android.systemui.util.settings
+
+import android.content.ContentResolver
+import android.database.ContentObserver
+import android.net.Uri
+import android.provider.Settings.SettingNotFoundException
+
+/**
+ * Used to interact with mainly with Settings.Global, but can also be used for Settings.System and
+ * Settings.Secure. To use the per-user System and Secure settings, [UserSettingsProxy] must be used
+ * instead.
+ *
+ * This interface can be implemented to give instance method (instead of static method) versions of
+ * Settings.Global. It can be injected into class constructors and then faked or mocked as needed in
+ * tests.
+ *
+ * You can ask for [GlobalSettings] to be injected as needed.
+ *
+ * This class also provides [.registerContentObserver] methods, normally found on [ContentResolver]
+ * instances, unifying setting related actions in one place.
+ */
+interface SettingsProxy {
+    /** Returns the [ContentResolver] this instance was constructed with. */
+    fun getContentResolver(): ContentResolver
+
+    /**
+     * Construct the content URI for a particular name/value pair, useful for monitoring changes
+     * with a ContentObserver.
+     *
+     * @param name to look up in the table
+     * @return the corresponding content URI, or null if not present
+     */
+    fun getUriFor(name: String): Uri
+
+    /**
+     * Convenience wrapper around [ContentResolver.registerContentObserver].'
+     *
+     * Implicitly calls [getUriFor] on the passed in name.
+     */
+    fun registerContentObserver(name: String, settingsObserver: ContentObserver) {
+        registerContentObserver(getUriFor(name), settingsObserver)
+    }
+
+    /** Convenience wrapper around [ContentResolver.registerContentObserver].' */
+    fun registerContentObserver(uri: Uri, settingsObserver: ContentObserver) =
+        registerContentObserver(uri, false, settingsObserver)
+
+    /**
+     * Convenience wrapper around [ContentResolver.registerContentObserver].'
+     *
+     * Implicitly calls [getUriFor] on the passed in name.
+     */
+    fun registerContentObserver(
+        name: String,
+        notifyForDescendants: Boolean,
+        settingsObserver: ContentObserver
+    ) = registerContentObserver(getUriFor(name), notifyForDescendants, settingsObserver)
+
+    /** Convenience wrapper around [ContentResolver.registerContentObserver].' */
+    fun registerContentObserver(
+        uri: Uri,
+        notifyForDescendants: Boolean,
+        settingsObserver: ContentObserver
+    ) = getContentResolver().registerContentObserver(uri, notifyForDescendants, settingsObserver)
+
+    /** See [ContentResolver.unregisterContentObserver]. */
+    fun unregisterContentObserver(settingsObserver: ContentObserver) =
+        getContentResolver().unregisterContentObserver(settingsObserver)
+
+    /**
+     * Look up a name in the database.
+     *
+     * @param name to look up in the table
+     * @return the corresponding value, or null if not present
+     */
+    fun getString(name: String): String
+
+    /**
+     * Store a name/value pair into the database.
+     *
+     * @param name to store
+     * @param value to associate with the name
+     * @return true if the value was set, false on database errors
+     */
+    fun putString(name: String, value: String): Boolean
+
+    /**
+     * Store a name/value pair into the database.
+     *
+     * The method takes an optional tag to associate with the setting which can be used to clear
+     * only settings made by your package and associated with this tag by passing the tag to
+     * [ ][.resetToDefaults]. Anyone can override the current tag. Also if another package changes
+     * the setting then the tag will be set to the one specified in the set call which can be null.
+     * Also any of the settings setters that do not take a tag as an argument effectively clears the
+     * tag.
+     *
+     * For example, if you set settings A and B with tags T1 and T2 and another app changes setting
+     * A (potentially to the same value), it can assign to it a tag T3 (note that now the package
+     * that changed the setting is not yours). Now if you reset your changes for T1 and T2 only
+     * setting B will be reset and A not (as it was changed by another package) but since A did not
+     * change you are in the desired initial state. Now if the other app changes the value of A
+     * (assuming you registered an observer in the beginning) you would detect that the setting was
+     * changed by another app and handle this appropriately (ignore, set back to some value, etc).
+     *
+     * Also the method takes an argument whether to make the value the default for this setting. If
+     * the system already specified a default value, then the one passed in here will **not** be set
+     * as the default.
+     *
+     * @param name to store.
+     * @param value to associate with the name.
+     * @param tag to associate with the setting.
+     * @param makeDefault whether to make the value the default one.
+     * @return true if the value was set, false on database errors.
+     * @see .resetToDefaults
+     */
+    fun putString(name: String, value: String, tag: String, makeDefault: Boolean): Boolean
+
+    /**
+     * Convenience function for retrieving a single secure settings value as an integer. Note that
+     * internally setting values are always stored as strings; this function converts the string to
+     * an integer for you. The default value will be returned if the setting is not defined or not
+     * an integer.
+     *
+     * @param name The name of the setting to retrieve.
+     * @param def Value to return if the setting is not defined.
+     * @return The setting's current value, or 'def' if it is not defined or not a valid integer.
+     */
+    fun getInt(name: String, def: Int): Int {
+        val v = getString(name)
+        return try {
+            v.toInt()
+        } catch (e: NumberFormatException) {
+            def
+        }
+    }
+
+    /**
+     * Convenience function for retrieving a single secure settings value as an integer. Note that
+     * internally setting values are always stored as strings; this function converts the string to
+     * an integer for you.
+     *
+     * This version does not take a default value. If the setting has not been set, or the string
+     * value is not a number, it throws [Settings.SettingNotFoundException].
+     *
+     * @param name The name of the setting to retrieve.
+     * @return The setting's current value.
+     * @throws Settings.SettingNotFoundException Thrown if a setting by the given name can't be
+     *   found or the setting value is not an integer.
+     */
+    @Throws(SettingNotFoundException::class)
+    fun getInt(name: String): Int {
+        val v = getString(name)
+        return try {
+            v.toInt()
+        } catch (e: NumberFormatException) {
+            throw SettingNotFoundException(name)
+        }
+    }
+
+    /**
+     * Convenience function for updating a single settings value as an integer. This will either
+     * create a new entry in the table if the given name does not exist, or modify the value of the
+     * existing row with that name. Note that internally setting values are always stored as
+     * strings, so this function converts the given value to a string before storing it.
+     *
+     * @param name The name of the setting to modify.
+     * @param value The new value for the setting.
+     * @return true if the value was set, false on database errors
+     */
+    fun putInt(name: String, value: Int): Boolean {
+        return putString(name, value.toString())
+    }
+
+    /**
+     * Convenience function for retrieving a single secure settings value as a boolean. Note that
+     * internally setting values are always stored as strings; this function converts the string to
+     * a boolean for you. The default value will be returned if the setting is not defined or not a
+     * boolean.
+     *
+     * @param name The name of the setting to retrieve.
+     * @param def Value to return if the setting is not defined.
+     * @return The setting's current value, or 'def' if it is not defined or not a valid boolean.
+     */
+    fun getBool(name: String, def: Boolean): Boolean {
+        return getInt(name, if (def) 1 else 0) != 0
+    }
+
+    /**
+     * Convenience function for retrieving a single secure settings value as a boolean. Note that
+     * internally setting values are always stored as strings; this function converts the string to
+     * a boolean for you.
+     *
+     * This version does not take a default value. If the setting has not been set, or the string
+     * value is not a number, it throws [Settings.SettingNotFoundException].
+     *
+     * @param name The name of the setting to retrieve.
+     * @return The setting's current value.
+     * @throws Settings.SettingNotFoundException Thrown if a setting by the given name can't be
+     *   found or the setting value is not a boolean.
+     */
+    @Throws(SettingNotFoundException::class)
+    fun getBool(name: String): Boolean {
+        return getInt(name) != 0
+    }
+
+    /**
+     * Convenience function for updating a single settings value as a boolean. This will either
+     * create a new entry in the table if the given name does not exist, or modify the value of the
+     * existing row with that name. Note that internally setting values are always stored as
+     * strings, so this function converts the given value to a string before storing it.
+     *
+     * @param name The name of the setting to modify.
+     * @param value The new value for the setting.
+     * @return true if the value was set, false on database errors
+     */
+    fun putBool(name: String, value: Boolean): Boolean {
+        return putInt(name, if (value) 1 else 0)
+    }
+
+    /**
+     * Convenience function for retrieving a single secure settings value as a `long`. Note that
+     * internally setting values are always stored as strings; this function converts the string to
+     * a `long` for you. The default value will be returned if the setting is not defined or not a
+     * `long`.
+     *
+     * @param name The name of the setting to retrieve.
+     * @param def Value to return if the setting is not defined.
+     * @return The setting's current value, or 'def' if it is not defined or not a valid `long`.
+     */
+    fun getLong(name: String, def: Long): Long {
+        val valString = getString(name)
+        return parseLongOrUseDefault(valString, def)
+    }
+
+    /**
+     * Convenience function for retrieving a single secure settings value as a `long`. Note that
+     * internally setting values are always stored as strings; this function converts the string to
+     * a `long` for you.
+     *
+     * This version does not take a default value. If the setting has not been set, or the string
+     * value is not a number, it throws [Settings.SettingNotFoundException].
+     *
+     * @param name The name of the setting to retrieve.
+     * @return The setting's current value.
+     * @throws Settings.SettingNotFoundException Thrown if a setting by the given name can't be
+     *   found or the setting value is not an integer.
+     */
+    @Throws(SettingNotFoundException::class)
+    fun getLong(name: String): Long {
+        val valString = getString(name)
+        return parseLongOrThrow(name, valString)
+    }
+
+    /**
+     * Convenience function for updating a secure settings value as a long integer. This will either
+     * create a new entry in the table if the given name does not exist, or modify the value of the
+     * existing row with that name. Note that internally setting values are always stored as
+     * strings, so this function converts the given value to a string before storing it.
+     *
+     * @param name The name of the setting to modify.
+     * @param value The new value for the setting.
+     * @return true if the value was set, false on database errors
+     */
+    fun putLong(name: String, value: Long): Boolean {
+        return putString(name, value.toString())
+    }
+
+    /**
+     * Convenience function for retrieving a single secure settings value as a floating point
+     * number. Note that internally setting values are always stored as strings; this function
+     * converts the string to an float for you. The default value will be returned if the setting is
+     * not defined or not a valid float.
+     *
+     * @param name The name of the setting to retrieve.
+     * @param def Value to return if the setting is not defined.
+     * @return The setting's current value, or 'def' if it is not defined or not a valid float.
+     */
+    fun getFloat(name: String, def: Float): Float {
+        val v = getString(name)
+        return parseFloat(v, def)
+    }
+
+    /**
+     * Convenience function for retrieving a single secure settings value as a float. Note that
+     * internally setting values are always stored as strings; this function converts the string to
+     * a float for you.
+     *
+     * This version does not take a default value. If the setting has not been set, or the string
+     * value is not a number, it throws [Settings.SettingNotFoundException].
+     *
+     * @param name The name of the setting to retrieve.
+     * @return The setting's current value.
+     * @throws Settings.SettingNotFoundException Thrown if a setting by the given name can't be
+     *   found or the setting value is not a float.
+     */
+    @Throws(SettingNotFoundException::class)
+    fun getFloat(name: String): Float {
+        val v = getString(name)
+        return parseFloatOrThrow(name, v)
+    }
+
+    /**
+     * Convenience function for updating a single settings value as a floating point number. This
+     * will either create a new entry in the table if the given name does not exist, or modify the
+     * value of the existing row with that name. Note that internally setting values are always
+     * stored as strings, so this function converts the given value to a string before storing it.
+     *
+     * @param name The name of the setting to modify.
+     * @param value The new value for the setting.
+     * @return true if the value was set, false on database errors
+     */
+    fun putFloat(name: String, value: Float): Boolean {
+        return putString(name, value.toString())
+    }
+
+    companion object {
+        /** Convert a string to a long, or uses a default if the string is malformed or null */
+        @JvmStatic
+        fun parseLongOrUseDefault(valString: String, def: Long): Long {
+            val value: Long
+            value =
+                try {
+                    valString.toLong()
+                } catch (e: NumberFormatException) {
+                    def
+                }
+            return value
+        }
+
+        /** Convert a string to a long, or throws an exception if the string is malformed or null */
+        @JvmStatic
+        @Throws(SettingNotFoundException::class)
+        fun parseLongOrThrow(name: String, valString: String?): Long {
+            if (valString == null) {
+                throw SettingNotFoundException(name)
+            }
+            return try {
+                valString.toLong()
+            } catch (e: NumberFormatException) {
+                throw SettingNotFoundException(name)
+            }
+        }
+
+        /** Convert a string to a float, or uses a default if the string is malformed or null */
+        @JvmStatic
+        fun parseFloat(v: String?, def: Float): Float {
+            return try {
+                v?.toFloat() ?: def
+            } catch (e: NumberFormatException) {
+                def
+            }
+        }
+
+        /**
+         * Convert a string to a float, or throws an exception if the string is malformed or null
+         */
+        @JvmStatic
+        @Throws(SettingNotFoundException::class)
+        fun parseFloatOrThrow(name: String, v: String?): Float {
+            if (v == null) {
+                throw SettingNotFoundException(name)
+            }
+            return try {
+                v.toFloat()
+            } catch (e: NumberFormatException) {
+                throw SettingNotFoundException(name)
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.java b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.java
deleted file mode 100644
index 10cf082..0000000
--- a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2023 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 com.android.systemui.util.settings;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.content.ContentResolver;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.UserHandle;
-import android.provider.Settings;
-
-import com.android.app.tracing.TraceUtils;
-import com.android.systemui.settings.UserTracker;
-
-import kotlin.Unit;
-
-/**
- * Used to interact with per-user Settings.Secure and Settings.System settings (but not
- * Settings.Global, since those do not vary per-user)
- * <p>
- * This interface can be implemented to give instance method (instead of static method) versions
- * of Settings.Secure and Settings.System. It can be injected into class constructors and then
- * faked or mocked as needed in tests.
- * <p>
- * You can ask for {@link SecureSettings} or {@link SystemSettings} to be injected as needed.
- * <p>
- * This class also provides {@link #registerContentObserver(String, ContentObserver)} methods,
- * normally found on {@link ContentResolver} instances, unifying setting related actions in one
- * place.
- */
-public interface UserSettingsProxy extends SettingsProxy {
-
-    /**
-     * Returns that {@link UserTracker} this instance was constructed with.
-     */
-    UserTracker getUserTracker();
-
-    /**
-     * Returns the user id for the associated {@link ContentResolver}.
-     */
-    default int getUserId() {
-        return getContentResolver().getUserId();
-    }
-
-    /**
-     * Returns the actual current user handle when querying with the current user. Otherwise,
-     * returns the passed in user id.
-     */
-    default int getRealUserHandle(int userHandle) {
-        if (userHandle != UserHandle.USER_CURRENT) {
-            return userHandle;
-        }
-        return getUserTracker().getUserId();
-    }
-
-    @Override
-    default void registerContentObserver(Uri uri, ContentObserver settingsObserver) {
-        registerContentObserverForUser(uri, settingsObserver, getUserId());
-    }
-
-    /**
-     * Convenience wrapper around
-     * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.'
-     */
-    @Override
-    default void registerContentObserver(Uri uri, boolean notifyForDescendants,
-            ContentObserver settingsObserver) {
-        registerContentObserverForUser(uri, notifyForDescendants, settingsObserver, getUserId());
-    }
-
-    /**
-     * Convenience wrapper around
-     * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
-     *
-     * Implicitly calls {@link #getUriFor(String)} on the passed in name.
-     */
-    default void registerContentObserverForUser(
-            String name, ContentObserver settingsObserver, int userHandle) {
-        registerContentObserverForUser(
-                getUriFor(name), settingsObserver, userHandle);
-    }
-
-    /**
-     * Convenience wrapper around
-     * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
-     */
-    default void registerContentObserverForUser(
-            Uri uri, ContentObserver settingsObserver, int userHandle) {
-        registerContentObserverForUser(
-                uri, false, settingsObserver, userHandle);
-    }
-
-    /**
-     * Convenience wrapper around
-     * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
-     *
-     * Implicitly calls {@link #getUriFor(String)} on the passed in name.
-     */
-    default void registerContentObserverForUser(
-            String name, boolean notifyForDescendants, ContentObserver settingsObserver,
-            int userHandle) {
-        registerContentObserverForUser(
-                getUriFor(name), notifyForDescendants, settingsObserver, userHandle);
-    }
-
-    /**
-     * Convenience wrapper around
-     * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
-     */
-    default void registerContentObserverForUser(
-            Uri uri, boolean notifyForDescendants, ContentObserver settingsObserver,
-            int userHandle) {
-        TraceUtils.trace(
-                () -> {
-                    // The limit for trace tags length is 127 chars, which leaves us 90 for Uri.
-                    return "USP#registerObserver#[" + uri.toString() + "]";
-                }, () -> {
-                    getContentResolver().registerContentObserver(
-                            uri, notifyForDescendants, settingsObserver,
-                            getRealUserHandle(userHandle));
-                    return Unit.INSTANCE;
-                });
-    }
-
-    /**
-     * Look up a name in the database.
-     * @param name to look up in the table
-     * @return the corresponding value, or null if not present
-     */
-    @Override
-    default String getString(String name) {
-        return getStringForUser(name, getUserId());
-    }
-
-    /**See {@link #getString(String)}. */
-    String getStringForUser(String name, int userHandle);
-
-    /**
-     * Store a name/value pair into the database. Values written by this method will be
-     * overridden if a restore happens in the future.
-     *
-     * @param name to store
-     * @param value to associate with the name
-     * @return true if the value was set, false on database errors
-     */
-    boolean putString(String name, String value, boolean overrideableByRestore);
-
-    @Override
-    default boolean putString(String name, String value) {
-        return putStringForUser(name, value, getUserId());
-    }
-
-    /** See {@link #putString(String, String)}. */
-    boolean putStringForUser(String name, String value, int userHandle);
-
-    /** See {@link #putString(String, String)}. */
-    boolean putStringForUser(@NonNull String name, @Nullable String value, @Nullable String tag,
-            boolean makeDefault, @UserIdInt int userHandle, boolean overrideableByRestore);
-
-    @Override
-    default int getInt(String name, int def) {
-        return getIntForUser(name, def, getUserId());
-    }
-
-    /** See {@link #getInt(String, int)}. */
-    default int getIntForUser(String name, int def, int userHandle) {
-        String v = getStringForUser(name, userHandle);
-        try {
-            return v != null ? Integer.parseInt(v) : def;
-        } catch (NumberFormatException e) {
-            return def;
-        }
-    }
-
-    @Override
-    default int getInt(String name) throws Settings.SettingNotFoundException {
-        return getIntForUser(name, getUserId());
-    }
-
-    /** See {@link #getInt(String)}. */
-    default int getIntForUser(String name, int userHandle)
-            throws Settings.SettingNotFoundException {
-        String v = getStringForUser(name, userHandle);
-        try {
-            return Integer.parseInt(v);
-        } catch (NumberFormatException e) {
-            throw new Settings.SettingNotFoundException(name);
-        }
-    }
-
-    @Override
-    default boolean putInt(String name, int value) {
-        return putIntForUser(name, value, getUserId());
-    }
-
-    /** See {@link #putInt(String, int)}. */
-    default boolean putIntForUser(String name, int value, int userHandle) {
-        return putStringForUser(name, Integer.toString(value), userHandle);
-    }
-
-    @Override
-    default boolean getBool(String name, boolean def) {
-        return getBoolForUser(name, def, getUserId());
-    }
-
-    /** See {@link #getBool(String, boolean)}. */
-    default boolean getBoolForUser(String name, boolean def, int userHandle) {
-        return getIntForUser(name, def ? 1 : 0, userHandle) != 0;
-    }
-
-    @Override
-    default boolean getBool(String name) throws Settings.SettingNotFoundException {
-        return getBoolForUser(name, getUserId());
-    }
-
-    /** See {@link #getBool(String)}. */
-    default boolean getBoolForUser(String name, int userHandle)
-            throws Settings.SettingNotFoundException {
-        return getIntForUser(name, userHandle) != 0;
-    }
-
-    @Override
-    default boolean putBool(String name, boolean value) {
-        return putBoolForUser(name, value, getUserId());
-    }
-
-    /** See {@link #putBool(String, boolean)}. */
-    default boolean putBoolForUser(String name, boolean value, int userHandle) {
-        return putIntForUser(name, value ? 1 : 0, userHandle);
-    }
-
-    /** See {@link #getLong(String, long)}. */
-    default long getLongForUser(String name, long def, int userHandle) {
-        String valString = getStringForUser(name, userHandle);
-        return SettingsProxy.parseLongOrUseDefault(valString, def);
-    }
-
-    /** See {@link #getLong(String)}. */
-    default long getLongForUser(String name, int userHandle)
-            throws Settings.SettingNotFoundException {
-        String valString = getStringForUser(name, userHandle);
-        return SettingsProxy.parseLongOrThrow(name, valString);
-    }
-
-    /** See {@link #putLong(String, long)}. */
-    default boolean putLongForUser(String name, long value, int userHandle) {
-        return putStringForUser(name, Long.toString(value), userHandle);
-    }
-
-    /** See {@link #getFloat(String)}. */
-    default float getFloatForUser(String name, float def, int userHandle) {
-        String v = getStringForUser(name, userHandle);
-        return SettingsProxy.parseFloat(v, def);
-    }
-
-    /** See {@link #getFloat(String, float)}. */
-    default float getFloatForUser(String name, int userHandle)
-            throws Settings.SettingNotFoundException {
-        String v = getStringForUser(name, userHandle);
-        return SettingsProxy.parseFloatOrThrow(name, v);
-    }
-
-    /** See {@link #putFloat(String, float)} */
-    default boolean putFloatForUser(String name, float value, int userHandle) {
-        return putStringForUser(name, Float.toString(value), userHandle);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
new file mode 100644
index 0000000..2285270
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2023 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 com.android.systemui.util.settings
+
+import android.annotation.UserIdInt
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.UserHandle
+import android.provider.Settings.SettingNotFoundException
+import com.android.app.tracing.TraceUtils.trace
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloat
+import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloatOrThrow
+import com.android.systemui.util.settings.SettingsProxy.Companion.parseLongOrThrow
+import com.android.systemui.util.settings.SettingsProxy.Companion.parseLongOrUseDefault
+
+/**
+ * Used to interact with per-user Settings.Secure and Settings.System settings (but not
+ * Settings.Global, since those do not vary per-user)
+ *
+ * This interface can be implemented to give instance method (instead of static method) versions of
+ * Settings.Secure and Settings.System. It can be injected into class constructors and then faked or
+ * mocked as needed in tests.
+ *
+ * You can ask for [SecureSettings] or [SystemSettings] to be injected as needed.
+ *
+ * This class also provides [.registerContentObserver] methods, normally found on [ContentResolver]
+ * instances, unifying setting related actions in one place.
+ */
+interface UserSettingsProxy : SettingsProxy {
+
+    /** Returns that [UserTracker] this instance was constructed with. */
+    val userTracker: UserTracker
+
+    /** Returns the user id for the associated [ContentResolver]. */
+    var userId: Int
+        get() = getContentResolver().userId
+        set(_) {
+            throw UnsupportedOperationException(
+                "userId cannot be set in interface, use setter from an implementation instead."
+            )
+        }
+
+    /**
+     * Returns the actual current user handle when querying with the current user. Otherwise,
+     * returns the passed in user id.
+     */
+    fun getRealUserHandle(userHandle: Int): Int {
+        return if (userHandle != UserHandle.USER_CURRENT) {
+            userHandle
+        } else userTracker.userId
+    }
+
+    override fun registerContentObserver(uri: Uri, settingsObserver: ContentObserver) {
+        registerContentObserverForUser(uri, settingsObserver, userId)
+    }
+
+    /** Convenience wrapper around [ContentResolver.registerContentObserver].' */
+    override fun registerContentObserver(
+        uri: Uri,
+        notifyForDescendants: Boolean,
+        settingsObserver: ContentObserver
+    ) {
+        registerContentObserverForUser(uri, notifyForDescendants, settingsObserver, userId)
+    }
+
+    /**
+     * Convenience wrapper around [ContentResolver.registerContentObserver]
+     *
+     * Implicitly calls [getUriFor] on the passed in name.
+     */
+    fun registerContentObserverForUser(
+        name: String,
+        settingsObserver: ContentObserver,
+        userHandle: Int
+    ) {
+        registerContentObserverForUser(getUriFor(name), settingsObserver, userHandle)
+    }
+
+    /** Convenience wrapper around [ContentResolver.registerContentObserver] */
+    fun registerContentObserverForUser(
+        uri: Uri,
+        settingsObserver: ContentObserver,
+        userHandle: Int
+    ) {
+        registerContentObserverForUser(uri, false, settingsObserver, userHandle)
+    }
+
+    /**
+     * Convenience wrapper around [ContentResolver.registerContentObserver]
+     *
+     * Implicitly calls [getUriFor] on the passed in name.
+     */
+    fun registerContentObserverForUser(
+        name: String,
+        notifyForDescendants: Boolean,
+        settingsObserver: ContentObserver,
+        userHandle: Int
+    ) {
+        registerContentObserverForUser(
+            getUriFor(name),
+            notifyForDescendants,
+            settingsObserver,
+            userHandle
+        )
+    }
+
+    /** Convenience wrapper around [ContentResolver.registerContentObserver] */
+    fun registerContentObserverForUser(
+        uri: Uri,
+        notifyForDescendants: Boolean,
+        settingsObserver: ContentObserver,
+        userHandle: Int
+    ) {
+        trace({ "USP#registerObserver#[$uri]" }) {
+            getContentResolver()
+                .registerContentObserver(
+                    uri,
+                    notifyForDescendants,
+                    settingsObserver,
+                    getRealUserHandle(userHandle)
+                )
+            Unit
+        }
+    }
+
+    /**
+     * Look up a name in the database.
+     *
+     * @param name to look up in the table
+     * @return the corresponding value, or null if not present
+     */
+    override fun getString(name: String): String {
+        return getStringForUser(name, userId)
+    }
+
+    /** See [getString]. */
+    fun getStringForUser(name: String, userHandle: Int): String
+
+    /**
+     * Store a name/value pair into the database. Values written by this method will be overridden
+     * if a restore happens in the future.
+     *
+     * @param name to store
+     * @param value to associate with the name
+     * @return true if the value was set, false on database errors
+     */
+    fun putString(name: String, value: String, overrideableByRestore: Boolean): Boolean
+    override fun putString(name: String, value: String): Boolean {
+        return putStringForUser(name, value, userId)
+    }
+
+    /** Similar implementation to [putString] for the specified [userHandle]. */
+    fun putStringForUser(name: String, value: String, userHandle: Int): Boolean
+
+    /** Similar implementation to [putString] for the specified [userHandle]. */
+    fun putStringForUser(
+        name: String,
+        value: String,
+        tag: String?,
+        makeDefault: Boolean,
+        @UserIdInt userHandle: Int,
+        overrideableByRestore: Boolean
+    ): Boolean
+
+    override fun getInt(name: String, def: Int): Int {
+        return getIntForUser(name, def, userId)
+    }
+
+    /** Similar implementation to [getInt] for the specified [userHandle]. */
+    fun getIntForUser(name: String, def: Int, userHandle: Int): Int {
+        val v = getStringForUser(name, userHandle)
+        return try {
+            v.toInt()
+        } catch (e: NumberFormatException) {
+            def
+        }
+    }
+
+    @Throws(SettingNotFoundException::class)
+    override fun getInt(name: String) = getIntForUser(name, userId)
+
+    /** Similar implementation to [getInt] for the specified [userHandle]. */
+    @Throws(SettingNotFoundException::class)
+    fun getIntForUser(name: String, userHandle: Int): Int {
+        val v = getStringForUser(name, userHandle)
+        return try {
+            v.toInt()
+        } catch (e: NumberFormatException) {
+            throw SettingNotFoundException(name)
+        }
+    }
+
+    override fun putInt(name: String, value: Int) = putIntForUser(name, value, userId)
+
+    /** Similar implementation to [getInt] for the specified [userHandle]. */
+    fun putIntForUser(name: String, value: Int, userHandle: Int) =
+        putStringForUser(name, value.toString(), userHandle)
+
+    override fun getBool(name: String, def: Boolean) = getBoolForUser(name, def, userId)
+
+    /** Similar implementation to [getBool] for the specified [userHandle]. */
+    fun getBoolForUser(name: String, def: Boolean, userHandle: Int) =
+        getIntForUser(name, if (def) 1 else 0, userHandle) != 0
+
+    @Throws(SettingNotFoundException::class)
+    override fun getBool(name: String) = getBoolForUser(name, userId)
+
+    /** Similar implementation to [getBool] for the specified [userHandle]. */
+    @Throws(SettingNotFoundException::class)
+    fun getBoolForUser(name: String, userHandle: Int): Boolean {
+        return getIntForUser(name, userHandle) != 0
+    }
+
+    override fun putBool(name: String, value: Boolean): Boolean {
+        return putBoolForUser(name, value, userId)
+    }
+
+    /** Similar implementation to [putBool] for the specified [userHandle]. */
+    fun putBoolForUser(name: String, value: Boolean, userHandle: Int) =
+        putIntForUser(name, if (value) 1 else 0, userHandle)
+
+    /** Similar implementation to [getLong] for the specified [userHandle]. */
+    fun getLongForUser(name: String, def: Long, userHandle: Int): Long {
+        val valString = getStringForUser(name, userHandle)
+        return parseLongOrUseDefault(valString, def)
+    }
+
+    /** Similar implementation to [getLong] for the specified [userHandle]. */
+    @Throws(SettingNotFoundException::class)
+    fun getLongForUser(name: String, userHandle: Int): Long {
+        val valString = getStringForUser(name, userHandle)
+        return parseLongOrThrow(name, valString)
+    }
+
+    /** Similar implementation to [putLong] for the specified [userHandle]. */
+    fun putLongForUser(name: String, value: Long, userHandle: Int) =
+        putStringForUser(name, value.toString(), userHandle)
+
+    /** Similar implementation to [getFloat] for the specified [userHandle]. */
+    fun getFloatForUser(name: String, def: Float, userHandle: Int): Float {
+        val v = getStringForUser(name, userHandle)
+        return parseFloat(v, def)
+    }
+
+    /** Similar implementation to [getFloat] for the specified [userHandle]. */
+    @Throws(SettingNotFoundException::class)
+    fun getFloatForUser(name: String, userHandle: Int): Float {
+        val v = getStringForUser(name, userHandle)
+        return parseFloatOrThrow(name, v)
+    }
+
+    /** Similar implementation to [putFloat] for the specified [userHandle]. */
+    fun putFloatForUser(name: String, value: Float, userHandle: Int) =
+        putStringForUser(name, value.toString(), userHandle)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt
index 0df4fbf..9ba56d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt
@@ -36,12 +36,12 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
 
 private const val USER_ID = 8
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
index 7856f9b..a89139b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
@@ -196,7 +196,7 @@
         verify(globalSettings)
             .registerContentObserver(
                 eq(Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE)),
-                settingsObserverCaptor.capture()
+                capture(settingsObserverCaptor)
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
new file mode 100644
index 0000000..ab95707
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2024 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 com.android.systemui.util.settings
+
+import android.content.ContentResolver
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import android.os.Looper
+import android.provider.Settings.SettingNotFoundException
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.eq
+
+/** Tests for [SettingsProxy]. */
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@TestableLooper.RunWithLooper
+class SettingsProxyTest : SysuiTestCase() {
+
+    private lateinit var mSettings: SettingsProxy
+    private lateinit var mContentObserver: ContentObserver
+
+    @Before
+    fun setUp() {
+        mSettings = FakeSettingsProxy()
+        mContentObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {}
+    }
+
+    @Test
+    fun registerContentObserver_inputString_success() {
+        mSettings.registerContentObserver(TEST_SETTING, mContentObserver)
+        verify(mSettings.getContentResolver())
+            .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver))
+    }
+
+    @Test
+    fun registerContentObserver_inputString_notifyForDescendants_true() {
+        mSettings.registerContentObserver(
+            TEST_SETTING,
+            notifyForDescendants = true,
+            mContentObserver
+        )
+        verify(mSettings.getContentResolver())
+            .registerContentObserver(eq(TEST_SETTING_URI), eq(true), eq(mContentObserver))
+    }
+
+    @Test
+    fun registerContentObserver_inputUri_success() {
+        mSettings.registerContentObserver(TEST_SETTING_URI, mContentObserver)
+        verify(mSettings.getContentResolver())
+            .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver))
+    }
+
+    @Test
+    fun registerContentObserver_inputUri_notifyForDescendants_true() {
+        mSettings.registerContentObserver(
+            TEST_SETTING_URI,
+            notifyForDescendants = true,
+            mContentObserver
+        )
+        verify(mSettings.getContentResolver())
+            .registerContentObserver(eq(TEST_SETTING_URI), eq(true), eq(mContentObserver))
+    }
+
+    @Test
+    fun unregisterContentObserver() {
+        mSettings.unregisterContentObserver(mContentObserver)
+        verify(mSettings.getContentResolver()).unregisterContentObserver(eq(mContentObserver))
+    }
+
+    @Test
+    fun getString_keyPresent_returnValidValue() {
+        mSettings.putString(TEST_SETTING, "test")
+        assertThat(mSettings.getString(TEST_SETTING)).isEqualTo("test")
+    }
+
+    @Test
+    fun getString_keyAbsent_returnEmptyValue() {
+        assertThat(mSettings.getString(TEST_SETTING)).isEmpty()
+    }
+
+    @Test
+    fun getInt_keyPresent_returnValidValue() {
+        mSettings.putInt(TEST_SETTING, 2)
+        assertThat(mSettings.getInt(TEST_SETTING)).isEqualTo(2)
+    }
+
+    @Test
+    fun getInt_keyPresent_nonIntegerValue_throwException() {
+        assertThrows(SettingNotFoundException::class.java) {
+            mSettings.putString(TEST_SETTING, "test")
+            mSettings.getInt(TEST_SETTING)
+        }
+    }
+
+    @Test
+    fun getInt_keyAbsent_throwException() {
+        assertThrows(SettingNotFoundException::class.java) { mSettings.getInt(TEST_SETTING) }
+    }
+
+    @Test
+    fun getInt_keyAbsent_returnDefaultValue() {
+        assertThat(mSettings.getInt(TEST_SETTING, 5)).isEqualTo(5)
+    }
+
+    @Test
+    fun getBool_keyPresent_returnValidValue() {
+        mSettings.putBool(TEST_SETTING, true)
+        assertThat(mSettings.getBool(TEST_SETTING)).isTrue()
+    }
+
+    @Test
+    fun getBool_keyPresent_nonBooleanValue_throwException() {
+        assertThrows(SettingNotFoundException::class.java) {
+            mSettings.putString(TEST_SETTING, "test")
+            mSettings.getBool(TEST_SETTING)
+        }
+    }
+
+    @Test
+    fun getBool_keyAbsent_throwException() {
+        assertThrows(SettingNotFoundException::class.java) { mSettings.getBool(TEST_SETTING) }
+    }
+
+    @Test
+    fun getBool_keyAbsent_returnDefaultValue() {
+        assertThat(mSettings.getBool(TEST_SETTING, false)).isEqualTo(false)
+    }
+
+    @Test
+    fun getLong_keyPresent_returnValidValue() {
+        mSettings.putLong(TEST_SETTING, 1L)
+        assertThat(mSettings.getLong(TEST_SETTING)).isEqualTo(1L)
+    }
+
+    @Test
+    fun getLong_keyPresent_nonLongValue_throwException() {
+        assertThrows(SettingNotFoundException::class.java) {
+            mSettings.putString(TEST_SETTING, "test")
+            mSettings.getLong(TEST_SETTING)
+        }
+    }
+
+    @Test
+    fun getLong_keyAbsent_throwException() {
+        assertThrows(SettingNotFoundException::class.java) { mSettings.getLong(TEST_SETTING) }
+    }
+
+    @Test
+    fun getLong_keyAbsent_returnDefaultValue() {
+        assertThat(mSettings.getLong(TEST_SETTING, 2L)).isEqualTo(2L)
+    }
+
+    @Test
+    fun getFloat_keyPresent_returnValidValue() {
+        mSettings.putFloat(TEST_SETTING, 2.5F)
+        assertThat(mSettings.getFloat(TEST_SETTING)).isEqualTo(2.5F)
+    }
+
+    @Test
+    fun getFloat_keyPresent_nonFloatValue_throwException() {
+        assertThrows(SettingNotFoundException::class.java) {
+            mSettings.putString(TEST_SETTING, "test")
+            mSettings.getFloat(TEST_SETTING)
+        }
+    }
+
+    @Test
+    fun getFloat_keyAbsent_throwException() {
+        assertThrows(SettingNotFoundException::class.java) { mSettings.getFloat(TEST_SETTING) }
+    }
+
+    @Test
+    fun getFloat_keyAbsent_returnDefaultValue() {
+        assertThat(mSettings.getFloat(TEST_SETTING, 2.5F)).isEqualTo(2.5F)
+    }
+
+    private class FakeSettingsProxy : SettingsProxy {
+
+        private val mContentResolver = mock(ContentResolver::class.java)
+        private val settingToValueMap: MutableMap<String, String> = mutableMapOf()
+
+        override fun getContentResolver() = mContentResolver
+
+        override fun getUriFor(name: String) =
+            Uri.parse(StringBuilder().append("content://settings/").append(name).toString())
+
+        override fun getString(name: String): String {
+            return settingToValueMap[name] ?: ""
+        }
+
+        override fun putString(name: String, value: String): Boolean {
+            settingToValueMap[name] = value
+            return true
+        }
+
+        override fun putString(
+            name: String,
+            value: String,
+            tag: String,
+            makeDefault: Boolean
+        ): Boolean {
+            settingToValueMap[name] = value
+            return true
+        }
+    }
+
+    companion object {
+        private const val TEST_SETTING = "test_setting"
+        private val TEST_SETTING_URI = Uri.parse("content://settings/test_setting")
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
new file mode 100644
index 0000000..56328b9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2024 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 com.android.systemui.util.settings
+
+import android.content.ContentResolver
+import android.content.pm.UserInfo
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import android.os.Looper
+import android.provider.Settings
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.settings.UserTracker
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.eq
+
+/** Tests for [UserSettingsProxy]. */
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@TestableLooper.RunWithLooper
+class UserSettingsProxyTest : SysuiTestCase() {
+
+    private var mUserTracker = FakeUserTracker()
+    private var mSettings: UserSettingsProxy = FakeUserSettingsProxy(mUserTracker)
+    private var mContentObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {}
+
+    @Before
+    fun setUp() {
+        mUserTracker.set(
+            listOf(UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_MAIN)),
+            selectedUserIndex = 0
+        )
+    }
+
+    @Test
+    fun registerContentObserverForUser_inputString_success() {
+        mSettings.registerContentObserverForUser(
+            TEST_SETTING,
+            mContentObserver,
+            mUserTracker.userId
+        )
+        verify(mSettings.getContentResolver())
+            .registerContentObserver(
+                eq(TEST_SETTING_URI),
+                eq(false),
+                eq(mContentObserver),
+                eq(MAIN_USER_ID)
+            )
+    }
+
+    @Test
+    fun registerContentObserverForUser_inputString_notifyForDescendants_true() {
+        mSettings.registerContentObserverForUser(
+            TEST_SETTING,
+            notifyForDescendants = true,
+            mContentObserver,
+            mUserTracker.userId
+        )
+        verify(mSettings.getContentResolver())
+            .registerContentObserver(
+                eq(TEST_SETTING_URI),
+                eq(true),
+                eq(mContentObserver),
+                eq(MAIN_USER_ID)
+            )
+    }
+
+    @Test
+    fun registerContentObserverForUser_inputUri_success() {
+        mSettings.registerContentObserverForUser(
+            TEST_SETTING_URI,
+            mContentObserver,
+            mUserTracker.userId
+        )
+        verify(mSettings.getContentResolver())
+            .registerContentObserver(
+                eq(TEST_SETTING_URI),
+                eq(false),
+                eq(mContentObserver),
+                eq(MAIN_USER_ID)
+            )
+    }
+
+    @Test
+    fun registerContentObserverForUser_inputUri_notifyForDescendants_true() {
+        mSettings.registerContentObserverForUser(
+            TEST_SETTING_URI,
+            notifyForDescendants = true,
+            mContentObserver,
+            mUserTracker.userId
+        )
+        verify(mSettings.getContentResolver())
+            .registerContentObserver(
+                eq(TEST_SETTING_URI),
+                eq(true),
+                eq(mContentObserver),
+                eq(MAIN_USER_ID)
+            )
+    }
+
+    @Test
+    fun registerContentObserver_inputUri_success() {
+        mSettings.registerContentObserver(TEST_SETTING_URI, mContentObserver)
+        verify(mSettings.getContentResolver())
+            .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver), eq(0))
+    }
+
+    @Test
+    fun registerContentObserver_inputUri_notifyForDescendants_true() {
+        mSettings.registerContentObserver(
+            TEST_SETTING_URI,
+            notifyForDescendants = true,
+            mContentObserver
+        )
+        verify(mSettings.getContentResolver())
+            .registerContentObserver(eq(TEST_SETTING_URI), eq(true), eq(mContentObserver), eq(0))
+    }
+
+    @Test
+    fun getString_keyPresent_returnValidValue() {
+        mSettings.putString(TEST_SETTING, "test")
+        assertThat(mSettings.getString(TEST_SETTING)).isEqualTo("test")
+    }
+
+    @Test
+    fun getString_keyAbsent_returnEmptyValue() {
+        assertThat(mSettings.getString(TEST_SETTING)).isEmpty()
+    }
+
+    @Test
+    fun getStringForUser_multipleUsers_validResult() {
+        mSettings.putStringForUser(TEST_SETTING, "test", MAIN_USER_ID)
+        mSettings.putStringForUser(TEST_SETTING, "test1", SECONDARY_USER_ID)
+        assertThat(mSettings.getStringForUser(TEST_SETTING, MAIN_USER_ID)).isEqualTo("test")
+        assertThat(mSettings.getStringForUser(TEST_SETTING, SECONDARY_USER_ID)).isEqualTo("test1")
+    }
+
+    @Test
+    fun getInt_keyPresent_returnValidValue() {
+        mSettings.putInt(TEST_SETTING, 2)
+        assertThat(mSettings.getInt(TEST_SETTING)).isEqualTo(2)
+    }
+
+    @Test
+    fun getInt_keyPresent_nonIntegerValue_throwException() {
+        assertThrows(Settings.SettingNotFoundException::class.java) {
+            mSettings.putString(TEST_SETTING, "test")
+            mSettings.getInt(TEST_SETTING)
+        }
+    }
+
+    @Test
+    fun getInt_keyAbsent_throwException() {
+        assertThrows(Settings.SettingNotFoundException::class.java) {
+            mSettings.getInt(TEST_SETTING)
+        }
+    }
+
+    @Test
+    fun getInt_keyAbsent_returnDefaultValue() {
+        assertThat(mSettings.getInt(TEST_SETTING, 5)).isEqualTo(5)
+    }
+
+    @Test
+    fun getIntForUser_multipleUsers__validResult() {
+        mSettings.putIntForUser(TEST_SETTING, 1, MAIN_USER_ID)
+        mSettings.putIntForUser(TEST_SETTING, 2, SECONDARY_USER_ID)
+        assertThat(mSettings.getIntForUser(TEST_SETTING, MAIN_USER_ID)).isEqualTo(1)
+        assertThat(mSettings.getIntForUser(TEST_SETTING, SECONDARY_USER_ID)).isEqualTo(2)
+    }
+
+    @Test
+    fun getBool_keyPresent_returnValidValue() {
+        mSettings.putBool(TEST_SETTING, true)
+        assertThat(mSettings.getBool(TEST_SETTING)).isTrue()
+    }
+
+    @Test
+    fun getBool_keyPresent_nonBooleanValue_throwException() {
+        assertThrows(Settings.SettingNotFoundException::class.java) {
+            mSettings.putString(TEST_SETTING, "test")
+            mSettings.getBool(TEST_SETTING)
+        }
+    }
+
+    @Test
+    fun getBool_keyAbsent_throwException() {
+        assertThrows(Settings.SettingNotFoundException::class.java) {
+            mSettings.getBool(TEST_SETTING)
+        }
+    }
+
+    @Test
+    fun getBool_keyAbsent_returnDefaultValue() {
+        assertThat(mSettings.getBool(TEST_SETTING, false)).isEqualTo(false)
+    }
+
+    @Test
+    fun getBoolForUser_multipleUsers__validResult() {
+        mSettings.putBoolForUser(TEST_SETTING, true, MAIN_USER_ID)
+        mSettings.putBoolForUser(TEST_SETTING, false, SECONDARY_USER_ID)
+        assertThat(mSettings.getBoolForUser(TEST_SETTING, MAIN_USER_ID)).isEqualTo(true)
+        assertThat(mSettings.getBoolForUser(TEST_SETTING, SECONDARY_USER_ID)).isEqualTo(false)
+    }
+
+    @Test
+    fun getLong_keyPresent_returnValidValue() {
+        mSettings.putLong(TEST_SETTING, 1L)
+        assertThat(mSettings.getLong(TEST_SETTING)).isEqualTo(1L)
+    }
+
+    @Test
+    fun getLong_keyPresent_nonLongValue_throwException() {
+        assertThrows(Settings.SettingNotFoundException::class.java) {
+            mSettings.putString(TEST_SETTING, "test")
+            mSettings.getLong(TEST_SETTING)
+        }
+    }
+
+    @Test
+    fun getLong_keyAbsent_throwException() {
+        assertThrows(Settings.SettingNotFoundException::class.java) {
+            mSettings.getLong(TEST_SETTING)
+        }
+    }
+
+    @Test
+    fun getLong_keyAbsent_returnDefaultValue() {
+        assertThat(mSettings.getLong(TEST_SETTING, 2L)).isEqualTo(2L)
+    }
+
+    @Test
+    fun getLongForUser_multipleUsers__validResult() {
+        mSettings.putLongForUser(TEST_SETTING, 1L, MAIN_USER_ID)
+        mSettings.putLongForUser(TEST_SETTING, 2L, SECONDARY_USER_ID)
+        assertThat(mSettings.getLongForUser(TEST_SETTING, MAIN_USER_ID)).isEqualTo(1L)
+        assertThat(mSettings.getLongForUser(TEST_SETTING, SECONDARY_USER_ID)).isEqualTo(2L)
+    }
+
+    @Test
+    fun getFloat_keyPresent_returnValidValue() {
+        mSettings.putFloat(TEST_SETTING, 2.5F)
+        assertThat(mSettings.getFloat(TEST_SETTING)).isEqualTo(2.5F)
+    }
+
+    @Test
+    fun getFloat_keyPresent_nonFloatValue_throwException() {
+        assertThrows(Settings.SettingNotFoundException::class.java) {
+            mSettings.putString(TEST_SETTING, "test")
+            mSettings.getFloat(TEST_SETTING)
+        }
+    }
+
+    @Test
+    fun getFloat_keyAbsent_throwException() {
+        assertThrows(Settings.SettingNotFoundException::class.java) {
+            mSettings.getFloat(TEST_SETTING)
+        }
+    }
+
+    @Test
+    fun getFloat_keyAbsent_returnDefaultValue() {
+        assertThat(mSettings.getFloat(TEST_SETTING, 2.5F)).isEqualTo(2.5F)
+    }
+
+    @Test
+    fun getFloatForUser_multipleUsers__validResult() {
+        mSettings.putFloatForUser(TEST_SETTING, 1F, MAIN_USER_ID)
+        mSettings.putFloatForUser(TEST_SETTING, 2F, SECONDARY_USER_ID)
+        assertThat(mSettings.getFloatForUser(TEST_SETTING, MAIN_USER_ID)).isEqualTo(1F)
+        assertThat(mSettings.getFloatForUser(TEST_SETTING, SECONDARY_USER_ID)).isEqualTo(2F)
+    }
+
+    /**
+     * Fake implementation of [UserSettingsProxy].
+     *
+     * This class uses a mock of [ContentResolver] to test the [ContentObserver] registration APIs.
+     */
+    private class FakeUserSettingsProxy(override val userTracker: UserTracker) : UserSettingsProxy {
+
+        private val mContentResolver = mock(ContentResolver::class.java)
+        private val userIdToSettingsValueMap: MutableMap<Int, MutableMap<String, String>> =
+            mutableMapOf()
+
+        override fun getContentResolver() = mContentResolver
+
+        override fun getUriFor(name: String) =
+            Uri.parse(StringBuilder().append(URI_PREFIX).append(name).toString())
+
+        override fun getStringForUser(name: String, userHandle: Int) =
+            userIdToSettingsValueMap[userHandle]?.get(name) ?: ""
+
+        override fun putString(
+            name: String,
+            value: String,
+            overrideableByRestore: Boolean
+        ): Boolean {
+            userIdToSettingsValueMap[DEFAULT_USER_ID]?.put(name, value)
+            return true
+        }
+
+        override fun putString(
+            name: String,
+            value: String,
+            tag: String,
+            makeDefault: Boolean
+        ): Boolean {
+            putStringForUser(name, value, DEFAULT_USER_ID)
+            return true
+        }
+
+        override fun putStringForUser(name: String, value: String, userHandle: Int): Boolean {
+            userIdToSettingsValueMap[userHandle] = mutableMapOf(Pair(name, value))
+            return true
+        }
+
+        override fun putStringForUser(
+            name: String,
+            value: String,
+            tag: String?,
+            makeDefault: Boolean,
+            userHandle: Int,
+            overrideableByRestore: Boolean
+        ): Boolean {
+            userIdToSettingsValueMap[userHandle]?.set(name, value)
+            return true
+        }
+
+        private companion object {
+            const val DEFAULT_USER_ID = 0
+            const val URI_PREFIX = "content://settings/"
+        }
+    }
+
+    private companion object {
+        const val MAIN_USER_ID = 10
+        const val SECONDARY_USER_ID = 20
+        const val TEST_SETTING = "test_setting"
+        val TEST_SETTING_URI = Uri.parse("content://settings/test_setting")
+    }
+}