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