• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.annotation.SuppressLint
19 import android.annotation.UserIdInt
20 import android.annotation.WorkerThread
21 import android.content.ContentResolver
22 import android.database.ContentObserver
23 import android.net.Uri
24 import android.os.UserHandle
25 import android.provider.Settings.SettingNotFoundException
26 import com.android.app.tracing.TraceUtils.trace
27 import com.android.app.tracing.coroutines.launchTraced as launch
28 import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloat
29 import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloatOrThrow
30 import com.android.systemui.util.settings.SettingsProxy.Companion.parseLongOrThrow
31 import com.android.systemui.util.settings.SettingsProxy.Companion.parseLongOrUseDefault
32 import kotlinx.coroutines.Job
33 
34 /**
35  * Used to interact with per-user Settings.Secure and Settings.System settings (but not
36  * Settings.Global, since those do not vary per-user)
37  *
38  * This interface can be implemented to give instance method (instead of static method) versions of
39  * Settings.Secure and Settings.System. It can be injected into class constructors and then faked or
40  * mocked as needed in tests.
41  *
42  * You can ask for [SecureSettings] or [SystemSettings] to be injected as needed.
43  *
44  * This class also provides [.registerContentObserver] methods, normally found on [ContentResolver]
45  * instances, unifying setting related actions in one place.
46  */
47 @SuppressLint("RegisterContentObserverSyncWarning")
48 public interface UserSettingsProxy : SettingsProxy {
49     public val currentUserProvider: SettingsProxy.CurrentUserIdProvider
50 
51     /** Returns the user id for the associated [ContentResolver]. */
52     public var userId: Int
53         get() = getContentResolver().userId
54         set(_) {
55             throw UnsupportedOperationException(
56                 "userId cannot be set in interface, use setter from an implementation instead."
57             )
58         }
59 
60     /**
61      * Returns the actual current user handle when querying with the current user. Otherwise,
62      * returns the passed in user id.
63      */
getRealUserHandlenull64     public fun getRealUserHandle(userHandle: Int): Int {
65         return if (userHandle != UserHandle.USER_CURRENT) {
66             userHandle
67         } else currentUserProvider.getUserId()
68     }
69 
70     @WorkerThread
registerContentObserverSyncnull71     override fun registerContentObserverSync(uri: Uri, settingsObserver: ContentObserver) {
72         registerContentObserverForUserSync(uri, settingsObserver, userId)
73     }
74 
registerContentObservernull75     override suspend fun registerContentObserver(uri: Uri, settingsObserver: ContentObserver) {
76         executeOnSettingsScopeDispatcher("registerContentObserver-A") {
77             registerContentObserverForUserSync(uri, settingsObserver, userId)
78         }
79     }
80 
registerContentObserverAsyncnull81     override fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver): Job =
82         settingsScope.launch("registerContentObserverAsync-A") {
83             registerContentObserverForUserSync(uri, settingsObserver, userId)
84         }
85 
86     /** Convenience wrapper around [ContentResolver.registerContentObserver].' */
87     @WorkerThread
registerContentObserverSyncnull88     override fun registerContentObserverSync(
89         uri: Uri,
90         notifyForDescendants: Boolean,
91         settingsObserver: ContentObserver,
92     ) {
93         registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId)
94     }
95 
registerContentObservernull96     override suspend fun registerContentObserver(
97         uri: Uri,
98         notifyForDescendants: Boolean,
99         settingsObserver: ContentObserver,
100     ) {
101         executeOnSettingsScopeDispatcher("registerContentObserver-B") {
102             registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId)
103         }
104     }
105 
106     /**
107      * Convenience wrapper around [ContentResolver.registerContentObserver].'
108      *
109      * API corresponding to [registerContentObserverForUser] for Java usage.
110      */
registerContentObserverAsyncnull111     override fun registerContentObserverAsync(
112         uri: Uri,
113         notifyForDescendants: Boolean,
114         settingsObserver: ContentObserver,
115     ): Job =
116         settingsScope.launch("registerContentObserverAsync-B") {
117             registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId)
118         }
119 
120     /**
121      * Convenience wrapper around [ContentResolver.registerContentObserver]
122      *
123      * Implicitly calls [getUriFor] on the passed in name.
124      */
125     @WorkerThread
registerContentObserverForUserSyncnull126     public fun registerContentObserverForUserSync(
127         name: String,
128         settingsObserver: ContentObserver,
129         userHandle: Int,
130     ) {
131         registerContentObserverForUserSync(getUriFor(name), settingsObserver, userHandle)
132     }
133 
134     /**
135      * Convenience wrapper around [ContentResolver.registerContentObserver].'
136      *
137      * suspend API corresponding to [registerContentObserverForUser] to ensure that
138      * [ContentObserver] registration happens on a worker thread. Caller may wrap the API in an
139      * async block if they wish to synchronize execution.
140      */
registerContentObserverForUsernull141     public suspend fun registerContentObserverForUser(
142         name: String,
143         settingsObserver: ContentObserver,
144         userHandle: Int,
145     ) {
146         executeOnSettingsScopeDispatcher("registerContentObserverForUser-A") {
147             registerContentObserverForUserSync(name, settingsObserver, userHandle)
148         }
149     }
150 
151     /**
152      * Convenience wrapper around [ContentResolver.registerContentObserver].'
153      *
154      * API corresponding to [registerContentObserverForUser] for Java usage.
155      */
registerContentObserverForUserAsyncnull156     public fun registerContentObserverForUserAsync(
157         name: String,
158         settingsObserver: ContentObserver,
159         userHandle: Int,
160     ): Job =
161         settingsScope.launch("registerContentObserverForUserAsync-A") {
162             try {
163                 registerContentObserverForUserSync(getUriFor(name), settingsObserver, userHandle)
164             } catch (e: SecurityException) {
165                 throw SecurityException("registerContentObserverForUserAsync-A, name: $name", e)
166             }
167         }
168 
169     /** Convenience wrapper around [ContentResolver.registerContentObserver] */
170     @WorkerThread
registerContentObserverForUserSyncnull171     public fun registerContentObserverForUserSync(
172         uri: Uri,
173         settingsObserver: ContentObserver,
174         userHandle: Int,
175     ) {
176         registerContentObserverForUserSync(uri, false, settingsObserver, userHandle)
177     }
178 
179     /**
180      * Convenience wrapper around [ContentResolver.registerContentObserver].'
181      *
182      * suspend API corresponding to [registerContentObserverForUser] to ensure that
183      * [ContentObserver] registration happens on a worker thread. Caller may wrap the API in an
184      * async block if they wish to synchronize execution.
185      */
registerContentObserverForUsernull186     public suspend fun registerContentObserverForUser(
187         uri: Uri,
188         settingsObserver: ContentObserver,
189         userHandle: Int,
190     ) {
191         executeOnSettingsScopeDispatcher("registerContentObserverForUser-B") {
192             registerContentObserverForUserSync(uri, settingsObserver, userHandle)
193         }
194     }
195 
196     /**
197      * Convenience wrapper around [ContentResolver.registerContentObserver].'
198      *
199      * API corresponding to [registerContentObserverForUser] for Java usage.
200      */
registerContentObserverForUserAsyncnull201     public fun registerContentObserverForUserAsync(
202         uri: Uri,
203         settingsObserver: ContentObserver,
204         userHandle: Int,
205     ): Job =
206         settingsScope.launch("registerContentObserverForUserAsync-B") {
207             try {
208                 registerContentObserverForUserSync(uri, settingsObserver, userHandle)
209             } catch (e: SecurityException) {
210                 throw SecurityException("registerContentObserverForUserAsync-B, uri: $uri", e)
211             }
212         }
213 
214     /**
215      * Convenience wrapper around [ContentResolver.registerContentObserver].'
216      *
217      * API corresponding to [registerContentObserverForUser] for Java usage. After registration is
218      * complete, the callback block is called on the <b>background thread</b> to allow for update of
219      * value.
220      */
registerContentObserverForUserAsyncnull221     public fun registerContentObserverForUserAsync(
222         uri: Uri,
223         settingsObserver: ContentObserver,
224         userHandle: Int,
225         @WorkerThread registered: Runnable,
226     ): Job =
227         settingsScope.launch("registerContentObserverForUserAsync-C") {
228             try {
229                 registerContentObserverForUserSync(uri, settingsObserver, userHandle)
230             } catch (e: SecurityException) {
231                 throw SecurityException("registerContentObserverForUserAsync-C, uri: $uri", e)
232             }
233             registered.run()
234         }
235 
236     /**
237      * Convenience wrapper around [ContentResolver.registerContentObserver]
238      *
239      * Implicitly calls [getUriFor] on the passed in name.
240      */
241     @WorkerThread
registerContentObserverForUserSyncnull242     public fun registerContentObserverForUserSync(
243         name: String,
244         notifyForDescendants: Boolean,
245         settingsObserver: ContentObserver,
246         userHandle: Int,
247     ) {
248         registerContentObserverForUserSync(
249             getUriFor(name),
250             notifyForDescendants,
251             settingsObserver,
252             userHandle,
253         )
254     }
255 
256     /**
257      * Convenience wrapper around [ContentResolver.registerContentObserver].'
258      *
259      * suspend API corresponding to [registerContentObserverForUser] to ensure that
260      * [ContentObserver] registration happens on a worker thread. Caller may wrap the API in an
261      * async block if they wish to synchronize execution.
262      */
registerContentObserverForUsernull263     public suspend fun registerContentObserverForUser(
264         name: String,
265         notifyForDescendants: Boolean,
266         settingsObserver: ContentObserver,
267         userHandle: Int,
268     ) {
269         executeOnSettingsScopeDispatcher("registerContentObserverForUser-C") {
270             registerContentObserverForUserSync(
271                 name,
272                 notifyForDescendants,
273                 settingsObserver,
274                 userHandle,
275             )
276         }
277     }
278 
279     /**
280      * Convenience wrapper around [ContentResolver.registerContentObserver].'
281      *
282      * API corresponding to [registerContentObserverForUser] for Java usage.
283      */
registerContentObserverForUserAsyncnull284     public fun registerContentObserverForUserAsync(
285         name: String,
286         notifyForDescendants: Boolean,
287         settingsObserver: ContentObserver,
288         userHandle: Int,
289     ) {
290         settingsScope.launch("registerContentObserverForUserAsync-D") {
291             try {
292                 registerContentObserverForUserSync(
293                     getUriFor(name),
294                     notifyForDescendants,
295                     settingsObserver,
296                     userHandle,
297                 )
298             } catch (e: SecurityException) {
299                 throw SecurityException("registerContentObserverForUserAsync-D, name: $name", e)
300             }
301         }
302     }
303 
304     /** Convenience wrapper around [ContentResolver.registerContentObserver] */
305     @WorkerThread
registerContentObserverForUserSyncnull306     public fun registerContentObserverForUserSync(
307         uri: Uri,
308         notifyForDescendants: Boolean,
309         settingsObserver: ContentObserver,
310         userHandle: Int,
311     ) {
312         trace({ "USP#registerObserver#[$uri]" }) {
313             getContentResolver()
314                 .registerContentObserver(
315                     uri,
316                     notifyForDescendants,
317                     settingsObserver,
318                     getRealUserHandle(userHandle),
319                 )
320         }
321     }
322 
323     /**
324      * Convenience wrapper around [ContentResolver.registerContentObserver].'
325      *
326      * suspend API corresponding to [registerContentObserverForUser] to ensure that
327      * [ContentObserver] registration happens on a worker thread. Caller may wrap the API in an
328      * async block if they wish to synchronize execution.
329      */
registerContentObserverForUsernull330     public suspend fun registerContentObserverForUser(
331         uri: Uri,
332         notifyForDescendants: Boolean,
333         settingsObserver: ContentObserver,
334         userHandle: Int,
335     ) {
336         executeOnSettingsScopeDispatcher("registerContentObserverForUser-D") {
337             registerContentObserverForUserSync(
338                 uri,
339                 notifyForDescendants,
340                 settingsObserver,
341                 getRealUserHandle(userHandle),
342             )
343         }
344     }
345 
346     /**
347      * Convenience wrapper around [ContentResolver.registerContentObserver].'
348      *
349      * API corresponding to [registerContentObserverForUser] for Java usage.
350      */
registerContentObserverForUserAsyncnull351     public fun registerContentObserverForUserAsync(
352         uri: Uri,
353         notifyForDescendants: Boolean,
354         settingsObserver: ContentObserver,
355         userHandle: Int,
356     ): Job =
357         settingsScope.launch("registerContentObserverForUserAsync-E") {
358             try {
359                 registerContentObserverForUserSync(
360                     uri,
361                     notifyForDescendants,
362                     settingsObserver,
363                     userHandle,
364                 )
365             } catch (e: SecurityException) {
366                 throw SecurityException("registerContentObserverForUserAsync-E, uri: $uri", e)
367             }
368         }
369 
370     /**
371      * Look up a name in the database.
372      *
373      * @param name to look up in the table
374      * @return the corresponding value, or null if not present
375      */
getStringnull376     override fun getString(name: String): String? {
377         return getStringForUser(name, userId)
378     }
379 
380     /** See [getString]. */
getStringForUsernull381     public fun getStringForUser(name: String, userHandle: Int): String?
382 
383     /**
384      * Store a name/value pair into the database. Values written by this method will be overridden
385      * if a restore happens in the future.
386      *
387      * @param name to store
388      * @param value to associate with the name
389      * @return true if the value was set, false on database errors
390      */
391     public fun putString(name: String, value: String?, overrideableByRestore: Boolean): Boolean
392 
393     override fun putString(name: String, value: String?): Boolean {
394         return putStringForUser(name, value, userId)
395     }
396 
397     /** Similar implementation to [putString] for the specified [userHandle]. */
putStringForUsernull398     public fun putStringForUser(name: String, value: String?, userHandle: Int): Boolean
399 
400     /** Similar implementation to [putString] for the specified [userHandle]. */
401     public fun putStringForUser(
402         name: String,
403         value: String?,
404         tag: String?,
405         makeDefault: Boolean,
406         @UserIdInt userHandle: Int,
407         overrideableByRestore: Boolean,
408     ): Boolean
409 
410     override fun getInt(name: String, default: Int): Int {
411         return getIntForUser(name, default, userId)
412     }
413 
414     /** Similar implementation to [getInt] for the specified [userHandle]. */
getIntForUsernull415     public fun getIntForUser(name: String, default: Int, userHandle: Int): Int {
416         val v = getStringForUser(name, userHandle)
417         return try {
418             v?.toInt() ?: default
419         } catch (e: NumberFormatException) {
420             default
421         }
422     }
423 
424     @Throws(SettingNotFoundException::class)
getIntnull425     override fun getInt(name: String): Int = getIntForUser(name, userId)
426 
427     /** Similar implementation to [getInt] for the specified [userHandle]. */
428     @Throws(SettingNotFoundException::class)
429     public fun getIntForUser(name: String, userHandle: Int): Int {
430         val v = getStringForUser(name, userHandle) ?: throw SettingNotFoundException(name)
431         return try {
432             v.toInt()
433         } catch (e: NumberFormatException) {
434             throw SettingNotFoundException(name)
435         }
436     }
437 
putIntnull438     override fun putInt(name: String, value: Int): Boolean = putIntForUser(name, value, userId)
439 
440     /** Similar implementation to [getInt] for the specified [userHandle]. */
441     public fun putIntForUser(name: String, value: Int, userHandle: Int): Boolean =
442         putStringForUser(name, value.toString(), userHandle)
443 
444     override fun getBool(name: String, def: Boolean): Boolean = getBoolForUser(name, def, userId)
445 
446     /** Similar implementation to [getBool] for the specified [userHandle]. */
447     public fun getBoolForUser(name: String, def: Boolean, userHandle: Int): Boolean =
448         getIntForUser(name, if (def) 1 else 0, userHandle) != 0
449 
450     @Throws(SettingNotFoundException::class)
451     override fun getBool(name: String): Boolean = getBoolForUser(name, userId)
452 
453     /** Similar implementation to [getBool] for the specified [userHandle]. */
454     @Throws(SettingNotFoundException::class)
455     public fun getBoolForUser(name: String, userHandle: Int): Boolean {
456         return getIntForUser(name, userHandle) != 0
457     }
458 
putBoolnull459     override fun putBool(name: String, value: Boolean): Boolean {
460         return putBoolForUser(name, value, userId)
461     }
462 
463     /** Similar implementation to [putBool] for the specified [userHandle]. */
putBoolForUsernull464     public fun putBoolForUser(name: String, value: Boolean, userHandle: Int): Boolean =
465         putIntForUser(name, if (value) 1 else 0, userHandle)
466 
467     /** Similar implementation to [getLong] for the specified [userHandle]. */
468     public fun getLongForUser(name: String, def: Long, userHandle: Int): Long {
469         val valString = getStringForUser(name, userHandle)
470         return parseLongOrUseDefault(valString, def)
471     }
472 
473     /** Similar implementation to [getLong] for the specified [userHandle]. */
474     @Throws(SettingNotFoundException::class)
getLongForUsernull475     public fun getLongForUser(name: String, userHandle: Int): Long {
476         val valString = getStringForUser(name, userHandle)
477         return parseLongOrThrow(name, valString)
478     }
479 
480     /** Similar implementation to [putLong] for the specified [userHandle]. */
putLongForUsernull481     public fun putLongForUser(name: String, value: Long, userHandle: Int): Boolean =
482         putStringForUser(name, value.toString(), userHandle)
483 
484     /** Similar implementation to [getFloat] for the specified [userHandle]. */
485     public fun getFloatForUser(name: String, def: Float, userHandle: Int): Float {
486         val v = getStringForUser(name, userHandle)
487         return parseFloat(v, def)
488     }
489 
490     /** Similar implementation to [getFloat] for the specified [userHandle]. */
491     @Throws(SettingNotFoundException::class)
getFloatForUsernull492     public fun getFloatForUser(name: String, userHandle: Int): Float {
493         val v = getStringForUser(name, userHandle)
494         return parseFloatOrThrow(name, v)
495     }
496 
497     /** Similar implementation to [putFloat] for the specified [userHandle]. */
putFloatForUsernull498     public fun putFloatForUser(name: String, value: Float, userHandle: Int): Boolean =
499         putStringForUser(name, value.toString(), userHandle)
500 }
501