1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.systemui.util.settings
17 
18 import android.content.ContentResolver
19 import android.database.ContentObserver
20 import android.net.Uri
21 import android.provider.Settings.SettingNotFoundException
22 import com.android.app.tracing.TraceUtils.trace
23 
24 /**
25  * Used to interact with mainly with Settings.Global, but can also be used for Settings.System and
26  * Settings.Secure. To use the per-user System and Secure settings, [UserSettingsProxy] must be used
27  * instead.
28  *
29  * This interface can be implemented to give instance method (instead of static method) versions of
30  * Settings.Global. It can be injected into class constructors and then faked or mocked as needed in
31  * tests.
32  *
33  * You can ask for [GlobalSettings] to be injected as needed.
34  *
35  * This class also provides [.registerContentObserver] methods, normally found on [ContentResolver]
36  * instances, unifying setting related actions in one place.
37  */
38 interface SettingsProxy {
39     /** Returns the [ContentResolver] this instance was constructed with. */
getContentResolvernull40     fun getContentResolver(): ContentResolver
41 
42     /**
43      * Construct the content URI for a particular name/value pair, useful for monitoring changes
44      * with a ContentObserver.
45      *
46      * @param name to look up in the table
47      * @return the corresponding content URI, or null if not present
48      */
49     fun getUriFor(name: String): Uri
50 
51     /**
52      * Convenience wrapper around [ContentResolver.registerContentObserver].'
53      *
54      * Implicitly calls [getUriFor] on the passed in name.
55      */
56     fun registerContentObserverSync(name: String, settingsObserver: ContentObserver) {
57         registerContentObserverSync(getUriFor(name), settingsObserver)
58     }
59 
60     /** Convenience wrapper around [ContentResolver.registerContentObserver].' */
registerContentObserverSyncnull61     fun registerContentObserverSync(uri: Uri, settingsObserver: ContentObserver) =
62         registerContentObserverSync(uri, false, settingsObserver)
63 
64     /**
65      * Convenience wrapper around [ContentResolver.registerContentObserver].'
66      *
67      * Implicitly calls [getUriFor] on the passed in name.
68      */
69     fun registerContentObserverSync(
70         name: String,
71         notifyForDescendants: Boolean,
72         settingsObserver: ContentObserver
73     ) = registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
74 
75     /** Convenience wrapper around [ContentResolver.registerContentObserver].' */
76     fun registerContentObserverSync(
77         uri: Uri,
78         notifyForDescendants: Boolean,
79         settingsObserver: ContentObserver
80     ) {
81         trace({ "SP#registerObserver#[$uri]" }) {
82             getContentResolver()
83                 .registerContentObserver(uri, notifyForDescendants, settingsObserver)
84         }
85     }
86 
87     /** See [ContentResolver.unregisterContentObserver]. */
unregisterContentObserverSyncnull88     fun unregisterContentObserverSync(settingsObserver: ContentObserver) {
89         trace({ "SP#unregisterObserver" }) {
90             getContentResolver().unregisterContentObserver(settingsObserver)
91         }
92     }
93 
94     /**
95      * Look up a name in the database.
96      *
97      * @param name to look up in the table
98      * @return the corresponding value, or null if not present
99      */
getStringnull100     fun getString(name: String): String
101 
102     /**
103      * Store a name/value pair into the database.
104      *
105      * @param name to store
106      * @param value to associate with the name
107      * @return true if the value was set, false on database errors
108      */
109     fun putString(name: String, value: String): Boolean
110 
111     /**
112      * Store a name/value pair into the database.
113      *
114      * The method takes an optional tag to associate with the setting which can be used to clear
115      * only settings made by your package and associated with this tag by passing the tag to
116      * [ ][.resetToDefaults]. Anyone can override the current tag. Also if another package changes
117      * the setting then the tag will be set to the one specified in the set call which can be null.
118      * Also any of the settings setters that do not take a tag as an argument effectively clears the
119      * tag.
120      *
121      * For example, if you set settings A and B with tags T1 and T2 and another app changes setting
122      * A (potentially to the same value), it can assign to it a tag T3 (note that now the package
123      * that changed the setting is not yours). Now if you reset your changes for T1 and T2 only
124      * setting B will be reset and A not (as it was changed by another package) but since A did not
125      * change you are in the desired initial state. Now if the other app changes the value of A
126      * (assuming you registered an observer in the beginning) you would detect that the setting was
127      * changed by another app and handle this appropriately (ignore, set back to some value, etc).
128      *
129      * Also the method takes an argument whether to make the value the default for this setting. If
130      * the system already specified a default value, then the one passed in here will **not** be set
131      * as the default.
132      *
133      * @param name to store.
134      * @param value to associate with the name.
135      * @param tag to associate with the setting.
136      * @param makeDefault whether to make the value the default one.
137      * @return true if the value was set, false on database errors.
138      * @see .resetToDefaults
139      */
140     fun putString(name: String, value: String, tag: String, makeDefault: Boolean): Boolean
141 
142     /**
143      * Convenience function for retrieving a single secure settings value as an integer. Note that
144      * internally setting values are always stored as strings; this function converts the string to
145      * an integer for you. The default value will be returned if the setting is not defined or not
146      * an integer.
147      *
148      * @param name The name of the setting to retrieve.
149      * @param def Value to return if the setting is not defined.
150      * @return The setting's current value, or 'def' if it is not defined or not a valid integer.
151      */
152     fun getInt(name: String, def: Int): Int {
153         val v = getString(name)
154         return try {
155             v.toInt()
156         } catch (e: NumberFormatException) {
157             def
158         }
159     }
160 
161     /**
162      * Convenience function for retrieving a single secure settings value as an integer. Note that
163      * internally setting values are always stored as strings; this function converts the string to
164      * an integer for you.
165      *
166      * This version does not take a default value. If the setting has not been set, or the string
167      * value is not a number, it throws [Settings.SettingNotFoundException].
168      *
169      * @param name The name of the setting to retrieve.
170      * @return The setting's current value.
171      * @throws Settings.SettingNotFoundException Thrown if a setting by the given name can't be
172      *   found or the setting value is not an integer.
173      */
174     @Throws(SettingNotFoundException::class)
getIntnull175     fun getInt(name: String): Int {
176         val v = getString(name)
177         return try {
178             v.toInt()
179         } catch (e: NumberFormatException) {
180             throw SettingNotFoundException(name)
181         }
182     }
183 
184     /**
185      * Convenience function for updating a single settings value as an integer. This will either
186      * create a new entry in the table if the given name does not exist, or modify the value of the
187      * existing row with that name. Note that internally setting values are always stored as
188      * strings, so this function converts the given value to a string before storing it.
189      *
190      * @param name The name of the setting to modify.
191      * @param value The new value for the setting.
192      * @return true if the value was set, false on database errors
193      */
putIntnull194     fun putInt(name: String, value: Int): Boolean {
195         return putString(name, value.toString())
196     }
197 
198     /**
199      * Convenience function for retrieving a single secure settings value as a boolean. Note that
200      * internally setting values are always stored as strings; this function converts the string to
201      * a boolean for you. The default value will be returned if the setting is not defined or not a
202      * boolean.
203      *
204      * @param name The name of the setting to retrieve.
205      * @param def Value to return if the setting is not defined.
206      * @return The setting's current value, or 'def' if it is not defined or not a valid boolean.
207      */
getBoolnull208     fun getBool(name: String, def: Boolean): Boolean {
209         return getInt(name, if (def) 1 else 0) != 0
210     }
211 
212     /**
213      * Convenience function for retrieving a single secure settings value as a boolean. Note that
214      * internally setting values are always stored as strings; this function converts the string to
215      * a boolean for you.
216      *
217      * This version does not take a default value. If the setting has not been set, or the string
218      * value is not a number, it throws [Settings.SettingNotFoundException].
219      *
220      * @param name The name of the setting to retrieve.
221      * @return The setting's current value.
222      * @throws Settings.SettingNotFoundException Thrown if a setting by the given name can't be
223      *   found or the setting value is not a boolean.
224      */
225     @Throws(SettingNotFoundException::class)
getBoolnull226     fun getBool(name: String): Boolean {
227         return getInt(name) != 0
228     }
229 
230     /**
231      * Convenience function for updating a single settings value as a boolean. This will either
232      * create a new entry in the table if the given name does not exist, or modify the value of the
233      * existing row with that name. Note that internally setting values are always stored as
234      * strings, so this function converts the given value to a string before storing it.
235      *
236      * @param name The name of the setting to modify.
237      * @param value The new value for the setting.
238      * @return true if the value was set, false on database errors
239      */
putBoolnull240     fun putBool(name: String, value: Boolean): Boolean {
241         return putInt(name, if (value) 1 else 0)
242     }
243 
244     /**
245      * Convenience function for retrieving a single secure settings value as a `long`. Note that
246      * internally setting values are always stored as strings; this function converts the string to
247      * a `long` for you. The default value will be returned if the setting is not defined or not a
248      * `long`.
249      *
250      * @param name The name of the setting to retrieve.
251      * @param def Value to return if the setting is not defined.
252      * @return The setting's current value, or 'def' if it is not defined or not a valid `long`.
253      */
getLongnull254     fun getLong(name: String, def: Long): Long {
255         val valString = getString(name)
256         return parseLongOrUseDefault(valString, def)
257     }
258 
259     /**
260      * Convenience function for retrieving a single secure settings value as a `long`. Note that
261      * internally setting values are always stored as strings; this function converts the string to
262      * a `long` for you.
263      *
264      * This version does not take a default value. If the setting has not been set, or the string
265      * value is not a number, it throws [Settings.SettingNotFoundException].
266      *
267      * @param name The name of the setting to retrieve.
268      * @return The setting's current value.
269      * @throws Settings.SettingNotFoundException Thrown if a setting by the given name can't be
270      *   found or the setting value is not an integer.
271      */
272     @Throws(SettingNotFoundException::class)
getLongnull273     fun getLong(name: String): Long {
274         val valString = getString(name)
275         return parseLongOrThrow(name, valString)
276     }
277 
278     /**
279      * Convenience function for updating a secure settings value as a long integer. This will either
280      * create a new entry in the table if the given name does not exist, or modify the value of the
281      * existing row with that name. Note that internally setting values are always stored as
282      * strings, so this function converts the given value to a string before storing it.
283      *
284      * @param name The name of the setting to modify.
285      * @param value The new value for the setting.
286      * @return true if the value was set, false on database errors
287      */
putLongnull288     fun putLong(name: String, value: Long): Boolean {
289         return putString(name, value.toString())
290     }
291 
292     /**
293      * Convenience function for retrieving a single secure settings value as a floating point
294      * number. Note that internally setting values are always stored as strings; this function
295      * converts the string to an float for you. The default value will be returned if the setting is
296      * not defined or not a valid float.
297      *
298      * @param name The name of the setting to retrieve.
299      * @param def Value to return if the setting is not defined.
300      * @return The setting's current value, or 'def' if it is not defined or not a valid float.
301      */
getFloatnull302     fun getFloat(name: String, def: Float): Float {
303         val v = getString(name)
304         return parseFloat(v, def)
305     }
306 
307     /**
308      * Convenience function for retrieving a single secure settings value as a float. Note that
309      * internally setting values are always stored as strings; this function converts the string to
310      * a float for you.
311      *
312      * This version does not take a default value. If the setting has not been set, or the string
313      * value is not a number, it throws [Settings.SettingNotFoundException].
314      *
315      * @param name The name of the setting to retrieve.
316      * @return The setting's current value.
317      * @throws Settings.SettingNotFoundException Thrown if a setting by the given name can't be
318      *   found or the setting value is not a float.
319      */
320     @Throws(SettingNotFoundException::class)
getFloatnull321     fun getFloat(name: String): Float {
322         val v = getString(name)
323         return parseFloatOrThrow(name, v)
324     }
325 
326     /**
327      * Convenience function for updating a single settings value as a floating point number. This
328      * will either create a new entry in the table if the given name does not exist, or modify the
329      * value of the existing row with that name. Note that internally setting values are always
330      * stored as strings, so this function converts the given value to a string before storing it.
331      *
332      * @param name The name of the setting to modify.
333      * @param value The new value for the setting.
334      * @return true if the value was set, false on database errors
335      */
putFloatnull336     fun putFloat(name: String, value: Float): Boolean {
337         return putString(name, value.toString())
338     }
339 
340     companion object {
341         /** Convert a string to a long, or uses a default if the string is malformed or null */
342         @JvmStatic
parseLongOrUseDefaultnull343         fun parseLongOrUseDefault(valString: String, def: Long): Long {
344             val value: Long
345             value =
346                 try {
347                     valString.toLong()
348                 } catch (e: NumberFormatException) {
349                     def
350                 }
351             return value
352         }
353 
354         /** Convert a string to a long, or throws an exception if the string is malformed or null */
355         @JvmStatic
356         @Throws(SettingNotFoundException::class)
parseLongOrThrownull357         fun parseLongOrThrow(name: String, valString: String?): Long {
358             if (valString == null) {
359                 throw SettingNotFoundException(name)
360             }
361             return try {
362                 valString.toLong()
363             } catch (e: NumberFormatException) {
364                 throw SettingNotFoundException(name)
365             }
366         }
367 
368         /** Convert a string to a float, or uses a default if the string is malformed or null */
369         @JvmStatic
parseFloatnull370         fun parseFloat(v: String?, def: Float): Float {
371             return try {
372                 v?.toFloat() ?: def
373             } catch (e: NumberFormatException) {
374                 def
375             }
376         }
377 
378         /**
379          * Convert a string to a float, or throws an exception if the string is malformed or null
380          */
381         @JvmStatic
382         @Throws(SettingNotFoundException::class)
parseFloatOrThrownull383         fun parseFloatOrThrow(name: String, v: String?): Float {
384             if (v == null) {
385                 throw SettingNotFoundException(name)
386             }
387             return try {
388                 v.toFloat()
389             } catch (e: NumberFormatException) {
390                 throw SettingNotFoundException(name)
391             }
392         }
393     }
394 }
395