• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.annotation.UserIdInt
19 import android.content.ContentResolver
20 import android.database.ContentObserver
21 import android.net.Uri
22 import android.os.UserHandle
23 import android.util.Pair
24 import androidx.annotation.VisibleForTesting
25 import com.android.systemui.util.settings.SettingsProxy.CurrentUserIdProvider
26 import kotlinx.coroutines.CoroutineDispatcher
27 import kotlinx.coroutines.CoroutineScope
28 import kotlinx.coroutines.Job
29 import kotlinx.coroutines.test.StandardTestDispatcher
30 import kotlinx.coroutines.test.TestDispatcher
31 
32 class FakeSettings : SecureSettings, SystemSettings, UserSettingsProxy {
33     private val values = mutableMapOf<SettingsKey, String?>()
34     private val contentObservers = mutableMapOf<SettingsKey, MutableList<ContentObserver>>()
35     private val contentObserversAllUsers = mutableMapOf<String, MutableList<ContentObserver>>()
36 
37     override val settingsScope: CoroutineScope
38 
39     @UserIdInt override var userId = UserHandle.USER_CURRENT
40     override val currentUserProvider: CurrentUserIdProvider
41 
42     @Deprecated(
43         """Please use FakeSettings(testDispatcher) to provide the same dispatcher used
44       by main test scope."""
45     )
46     constructor() {
47         settingsScope = CoroutineScope(StandardTestDispatcher(scheduler = null, name = null))
<lambda>null48         currentUserProvider = CurrentUserIdProvider { userId }
49     }
50 
51     constructor(dispatcher: CoroutineDispatcher) {
52         settingsScope = CoroutineScope(dispatcher)
<lambda>null53         currentUserProvider = CurrentUserIdProvider { userId }
54     }
55 
56     constructor(dispatcher: CoroutineDispatcher, currentUserProvider: CurrentUserIdProvider) {
57         settingsScope = CoroutineScope(dispatcher)
58         this.currentUserProvider = currentUserProvider
59     }
60 
61     @VisibleForTesting
62     internal constructor(initialKey: String, initialValue: String) : this() {
63         putString(initialKey, initialValue)
64     }
65 
66     @VisibleForTesting
67     internal constructor(initialValues: Map<String, String>) : this() {
valuenull68         for ((key, value) in initialValues) {
69             putString(key, value)
70         }
71     }
72 
getContentObserversnull73     fun getContentObservers(uri: Uri, userHandle: Int): List<ContentObserver> {
74         if (userHandle == UserHandle.USER_ALL) {
75             return contentObserversAllUsers[uri.toString()] ?: listOf()
76         } else {
77             return contentObservers[SettingsKey(userHandle, uri.toString())] ?: listOf()
78         }
79     }
80 
getContentResolvernull81     override fun getContentResolver(): ContentResolver {
82         throw UnsupportedOperationException("FakeSettings.getContentResolver is not implemented")
83     }
84 
registerContentObserverForUserSyncnull85     override fun registerContentObserverForUserSync(
86         uri: Uri,
87         notifyForDescendants: Boolean,
88         settingsObserver: ContentObserver,
89         userHandle: Int,
90     ) {
91         if (userHandle == UserHandle.USER_ALL) {
92             contentObserversAllUsers
93                 .getOrPut(uri.toString()) { mutableListOf() }
94                 .add(settingsObserver)
95         } else {
96             val key = SettingsKey(userHandle, uri.toString())
97             contentObservers.getOrPut(key) { mutableListOf() }.add(settingsObserver)
98         }
99     }
100 
unregisterContentObserverSyncnull101     override fun unregisterContentObserverSync(settingsObserver: ContentObserver) {
102         contentObservers.values.onEach { it.remove(settingsObserver) }
103         contentObserversAllUsers.values.onEach { it.remove(settingsObserver) }
104     }
105 
registerContentObservernull106     override suspend fun registerContentObserver(uri: Uri, settingsObserver: ContentObserver) =
107         suspendAdvanceDispatcher {
108             super<UserSettingsProxy>.registerContentObserver(uri, settingsObserver)
109         }
110 
registerContentObserverAsyncnull111     override fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver): Job =
112         advanceDispatcher {
113             super<UserSettingsProxy>.registerContentObserverAsync(uri, settingsObserver)
114         }
115 
registerContentObservernull116     override suspend fun registerContentObserver(
117         uri: Uri,
118         notifyForDescendants: Boolean,
119         settingsObserver: ContentObserver,
120     ) = suspendAdvanceDispatcher {
121         super<UserSettingsProxy>.registerContentObserver(
122             uri,
123             notifyForDescendants,
124             settingsObserver,
125         )
126     }
127 
registerContentObserverAsyncnull128     override fun registerContentObserverAsync(
129         uri: Uri,
130         notifyForDescendants: Boolean,
131         settingsObserver: ContentObserver,
132     ): Job = advanceDispatcher {
133         super<UserSettingsProxy>.registerContentObserverAsync(
134             uri,
135             notifyForDescendants,
136             settingsObserver,
137         )
138     }
139 
registerContentObserverForUsernull140     override suspend fun registerContentObserverForUser(
141         name: String,
142         settingsObserver: ContentObserver,
143         userHandle: Int,
144     ) = suspendAdvanceDispatcher {
145         super<UserSettingsProxy>.registerContentObserverForUser(name, settingsObserver, userHandle)
146     }
147 
registerContentObserverForUserAsyncnull148     override fun registerContentObserverForUserAsync(
149         name: String,
150         settingsObserver: ContentObserver,
151         userHandle: Int,
152     ): Job = advanceDispatcher {
153         super<UserSettingsProxy>.registerContentObserverForUserAsync(
154             name,
155             settingsObserver,
156             userHandle,
157         )
158     }
159 
unregisterContentObserverAsyncnull160     override fun unregisterContentObserverAsync(settingsObserver: ContentObserver): Job =
161         advanceDispatcher {
162             super<UserSettingsProxy>.unregisterContentObserverAsync(settingsObserver)
163         }
164 
registerContentObserverForUsernull165     override suspend fun registerContentObserverForUser(
166         uri: Uri,
167         settingsObserver: ContentObserver,
168         userHandle: Int,
169     ) = suspendAdvanceDispatcher {
170         super<UserSettingsProxy>.registerContentObserverForUser(uri, settingsObserver, userHandle)
171     }
172 
registerContentObserverForUserAsyncnull173     override fun registerContentObserverForUserAsync(
174         uri: Uri,
175         settingsObserver: ContentObserver,
176         userHandle: Int,
177     ): Job = advanceDispatcher {
178         super<UserSettingsProxy>.registerContentObserverForUserAsync(
179             uri,
180             settingsObserver,
181             userHandle,
182         )
183     }
184 
registerContentObserverForUserAsyncnull185     override fun registerContentObserverForUserAsync(
186         uri: Uri,
187         settingsObserver: ContentObserver,
188         userHandle: Int,
189         registered: Runnable,
190     ): Job = advanceDispatcher {
191         super<UserSettingsProxy>.registerContentObserverForUserAsync(
192             uri,
193             settingsObserver,
194             userHandle,
195             registered,
196         )
197     }
198 
registerContentObserverForUsernull199     override suspend fun registerContentObserverForUser(
200         name: String,
201         notifyForDescendants: Boolean,
202         settingsObserver: ContentObserver,
203         userHandle: Int,
204     ) = suspendAdvanceDispatcher {
205         super<UserSettingsProxy>.registerContentObserverForUser(
206             name,
207             notifyForDescendants,
208             settingsObserver,
209             userHandle,
210         )
211     }
212 
registerContentObserverForUserAsyncnull213     override fun registerContentObserverForUserAsync(
214         name: String,
215         notifyForDescendants: Boolean,
216         settingsObserver: ContentObserver,
217         userHandle: Int,
218     ) = advanceDispatcher {
219         super<UserSettingsProxy>.registerContentObserverForUserAsync(
220             name,
221             notifyForDescendants,
222             settingsObserver,
223             userHandle,
224         )
225     }
226 
registerContentObserverForUserAsyncnull227     override fun registerContentObserverForUserAsync(
228         uri: Uri,
229         notifyForDescendants: Boolean,
230         settingsObserver: ContentObserver,
231         userHandle: Int,
232     ): Job = advanceDispatcher {
233         super<UserSettingsProxy>.registerContentObserverForUserAsync(
234             uri,
235             notifyForDescendants,
236             settingsObserver,
237             userHandle,
238         )
239     }
240 
getUriFornull241     override fun getUriFor(name: String): Uri {
242         return Uri.withAppendedPath(CONTENT_URI, name)
243     }
244 
getStringnull245     override fun getString(name: String): String? {
246         return getStringForUser(name, userId)
247     }
248 
getStringForUsernull249     override fun getStringForUser(name: String, userHandle: Int): String? {
250         return values[SettingsKey(userHandle, getUriFor(name).toString())]
251     }
252 
putStringnull253     override fun putString(name: String, value: String?, overrideableByRestore: Boolean): Boolean {
254         return putStringForUser(name, value, null, false, userId, overrideableByRestore)
255     }
256 
putStringnull257     override fun putString(name: String, value: String?): Boolean {
258         return putString(name, value, false)
259     }
260 
putStringForUsernull261     override fun putStringForUser(name: String, value: String?, userHandle: Int): Boolean {
262         return putStringForUser(name, value, null, false, userHandle, false)
263     }
264 
putStringForUsernull265     override fun putStringForUser(
266         name: String,
267         value: String?,
268         tag: String?,
269         makeDefault: Boolean,
270         userHandle: Int,
271         overrideableByRestore: Boolean,
272     ): Boolean {
273         val key = SettingsKey(userHandle, getUriFor(name).toString())
274         values[key] = value
275         val uri = getUriFor(name)
276         contentObservers[key]?.onEach { it.dispatchChange(false, listOf(uri), 0, userHandle) }
277         contentObserversAllUsers[uri.toString()]?.onEach {
278             it.dispatchChange(false, listOf(uri), 0, userHandle)
279         }
280         return true
281     }
282 
putStringnull283     override fun putString(
284         name: String,
285         value: String?,
286         tag: String?,
287         makeDefault: Boolean,
288     ): Boolean {
289         return putString(name, value)
290     }
291 
getIntnull292     override fun getInt(name: String): Int {
293         return getIntForUser(name, userId)
294     }
295 
getIntnull296     override fun getInt(name: String, default: Int): Int {
297         return getIntForUser(name, default, userId)
298     }
299 
getIntForUsernull300     override fun getIntForUser(name: String, userHandle: Int): Int {
301         return getIntForUser(name, 0, userHandle)
302     }
303 
getIntForUsernull304     override fun getIntForUser(name: String, default: Int, userHandle: Int): Int {
305         return values[SettingsKey(userHandle, getUriFor(name).toString())]?.toInt() ?: default
306     }
307 
putIntForUsernull308     override fun putIntForUser(name: String, value: Int, userHandle: Int): Boolean {
309         val key = SettingsKey(userHandle, getUriFor(name).toString())
310         values[key] = value.toString()
311         val uri = getUriFor(name)
312         contentObservers[key]?.onEach { it.dispatchChange(false, listOf(uri), 0, userHandle) }
313         contentObserversAllUsers[uri.toString()]?.onEach {
314             it.dispatchChange(false, listOf(uri), 0, userHandle)
315         }
316         return true
317     }
318 
putIntnull319     override fun putInt(name: String, value: Int): Boolean {
320         return putIntForUser(name, value, userId)
321     }
322 
323     /** Runs current jobs on dispatcher after calling the method. */
advanceDispatchernull324     private fun <T> advanceDispatcher(f: () -> T): T {
325         val result = f()
326         testDispatcherRunCurrent()
327         return result
328     }
329 
suspendAdvanceDispatchernull330     private suspend fun <T> suspendAdvanceDispatcher(f: suspend () -> T): T {
331         val result = f()
332         testDispatcherRunCurrent()
333         return result
334     }
335 
336     @OptIn(ExperimentalStdlibApi::class)
testDispatcherRunCurrentnull337     private fun testDispatcherRunCurrent() {
338         val testDispatcher = settingsScope.coroutineContext[CoroutineDispatcher] as? TestDispatcher
339         testDispatcher?.scheduler?.runCurrent()
340     }
341 
342     private data class SettingsKey(val first: Int, val second: String) :
343         Pair<Int, String>(first, second)
344 
345     companion object {
346         val CONTENT_URI = Uri.parse("content://settings/fake")
347     }
348 }
349