1 /* <lambda>null2 * 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 17 package com.android.systemui.settings 18 19 import android.app.IActivityManager 20 import android.app.IUserSwitchObserver 21 import android.content.Context 22 import android.content.Intent 23 import android.content.IntentFilter 24 import android.content.pm.UserInfo 25 import android.os.Handler 26 import android.os.IRemoteCallback 27 import android.os.UserHandle 28 import android.os.UserManager 29 import androidx.test.filters.SmallTest 30 import com.android.systemui.SysuiTestCase 31 import com.android.systemui.dump.DumpManager 32 import com.android.systemui.flags.FakeFeatureFlagsClassic 33 import com.android.systemui.flags.Flags 34 import com.android.systemui.util.concurrency.FakeExecutor 35 import com.android.systemui.util.mockito.capture 36 import com.android.systemui.util.time.FakeSystemClock 37 import com.google.common.truth.Truth.assertThat 38 import com.google.common.truth.TruthJUnit.assume 39 import kotlinx.coroutines.ExperimentalCoroutinesApi 40 import kotlinx.coroutines.test.StandardTestDispatcher 41 import kotlinx.coroutines.test.TestScope 42 import kotlinx.coroutines.test.runCurrent 43 import kotlinx.coroutines.test.runTest 44 import org.junit.Before 45 import org.junit.Test 46 import org.junit.runner.RunWith 47 import org.junit.runners.Parameterized 48 import org.mockito.ArgumentCaptor 49 import org.mockito.ArgumentMatchers.any 50 import org.mockito.ArgumentMatchers.anyInt 51 import org.mockito.ArgumentMatchers.anyString 52 import org.mockito.ArgumentMatchers.eq 53 import org.mockito.ArgumentMatchers.isNull 54 import org.mockito.Mock 55 import org.mockito.Mockito.never 56 import org.mockito.Mockito.verify 57 import org.mockito.Mockito.`when` 58 import org.mockito.MockitoAnnotations 59 import java.util.concurrent.Executor 60 61 62 @OptIn(ExperimentalCoroutinesApi::class) 63 @SmallTest 64 @RunWith(Parameterized::class) 65 class UserTrackerImplTest : SysuiTestCase() { 66 67 companion object { 68 69 @JvmStatic 70 @Parameterized.Parameters 71 fun isBackgroundUserTrackerEnabled(): Iterable<Boolean> = listOf(true, false) 72 } 73 74 @Mock 75 private lateinit var context: Context 76 77 @Mock 78 private lateinit var userManager: UserManager 79 80 @Mock 81 private lateinit var iActivityManager: IActivityManager 82 83 @Mock 84 private lateinit var userSwitchingReply: IRemoteCallback 85 86 @Mock(stubOnly = true) 87 private lateinit var dumpManager: DumpManager 88 89 @Mock(stubOnly = true) 90 private lateinit var handler: Handler 91 92 @Parameterized.Parameter 93 @JvmField 94 var isBackgroundUserTrackerEnabled: Boolean = false 95 96 private val testScope = TestScope() 97 private val testDispatcher = StandardTestDispatcher(testScope.testScheduler) 98 private val executor = Executor(Runnable::run) 99 private val featureFlags = FakeFeatureFlagsClassic() 100 101 private lateinit var tracker: UserTrackerImpl 102 103 @Before 104 fun setUp() { 105 MockitoAnnotations.initMocks(this) 106 107 `when`(context.userId).thenReturn(UserHandle.USER_SYSTEM) 108 `when`(context.user).thenReturn(UserHandle.SYSTEM) 109 `when`(context.createContextAsUser(any(), anyInt())).thenAnswer { invocation -> 110 val user = invocation.getArgument<UserHandle>(0) 111 `when`(context.user).thenReturn(user) 112 `when`(context.userId).thenReturn(user.identifier) 113 context 114 } 115 `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation -> 116 val info = UserInfo(invocation.getArgument<Int>(0), "", UserInfo.FLAG_FULL) 117 listOf(info) 118 } 119 120 featureFlags.set(Flags.USER_TRACKER_BACKGROUND_CALLBACKS, isBackgroundUserTrackerEnabled) 121 tracker = 122 UserTrackerImpl( 123 context, 124 { featureFlags }, 125 userManager, 126 iActivityManager, 127 dumpManager, 128 testScope.backgroundScope, 129 testDispatcher, 130 handler, 131 ) 132 } 133 134 @Test 135 fun testNotInitialized() = testScope.runTest { 136 assertThat(tracker.initialized).isFalse() 137 } 138 139 @Test(expected = IllegalStateException::class) 140 fun testGetUserIdBeforeInitThrowsException() = testScope.runTest { 141 tracker.userId 142 } 143 144 @Test(expected = IllegalStateException::class) 145 fun testGetUserHandleBeforeInitThrowsException() = testScope.runTest { 146 tracker.userHandle 147 } 148 149 @Test(expected = IllegalStateException::class) 150 fun testGetUserContextBeforeInitThrowsException() = testScope.runTest { 151 tracker.userContext 152 } 153 154 @Test(expected = IllegalStateException::class) 155 fun testGetUserContentResolverBeforeInitThrowsException() = testScope.runTest { 156 tracker.userContentResolver 157 } 158 159 @Test(expected = IllegalStateException::class) 160 fun testGetUserProfilesBeforeInitThrowsException() = testScope.runTest { 161 tracker.userProfiles 162 } 163 164 @Test 165 fun testInitialize() = testScope.runTest { 166 tracker.initialize(0) 167 168 assertThat(tracker.initialized).isTrue() 169 } 170 171 @Test 172 fun testReceiverRegisteredOnInitialize() = testScope.runTest { 173 tracker.initialize(0) 174 175 val captor = ArgumentCaptor.forClass(IntentFilter::class.java) 176 177 verify(context) 178 .registerReceiverForAllUsers(eq(tracker), capture(captor), isNull(), eq(handler)) 179 with(captor.value) { 180 assertThat(countActions()).isEqualTo(11) 181 assertThat(hasAction(Intent.ACTION_LOCALE_CHANGED)).isTrue() 182 assertThat(hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue() 183 assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)).isTrue() 184 assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)).isTrue() 185 assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_ADDED)).isTrue() 186 assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)).isTrue() 187 assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)).isTrue() 188 assertThat(hasAction(Intent.ACTION_PROFILE_ADDED)).isTrue() 189 assertThat(hasAction(Intent.ACTION_PROFILE_REMOVED)).isTrue() 190 assertThat(hasAction(Intent.ACTION_PROFILE_AVAILABLE)).isTrue() 191 assertThat(hasAction(Intent.ACTION_PROFILE_UNAVAILABLE)).isTrue() 192 } 193 } 194 195 @Test 196 fun testInitialValuesSet() = testScope.runTest { 197 val testID = 4 198 tracker.initialize(testID) 199 200 verify(userManager).getProfiles(testID) 201 202 assertThat(tracker.userId).isEqualTo(testID) 203 assertThat(tracker.userHandle).isEqualTo(UserHandle.of(testID)) 204 assertThat(tracker.userContext.userId).isEqualTo(testID) 205 assertThat(tracker.userContext.user).isEqualTo(UserHandle.of(testID)) 206 assertThat(tracker.userProfiles).hasSize(1) 207 208 val info = tracker.userProfiles[0] 209 assertThat(info.id).isEqualTo(testID) 210 } 211 212 @Test 213 fun testUserSwitch() = testScope.runTest { 214 tracker.initialize(0) 215 val newID = 5 216 217 val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java) 218 verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString()) 219 captor.value.onBeforeUserSwitching(newID) 220 captor.value.onUserSwitching(newID, userSwitchingReply) 221 runCurrent() 222 verify(userSwitchingReply).sendResult(any()) 223 224 verify(userManager).getProfiles(newID) 225 226 assertThat(tracker.userId).isEqualTo(newID) 227 assertThat(tracker.userHandle).isEqualTo(UserHandle.of(newID)) 228 assertThat(tracker.userContext.userId).isEqualTo(newID) 229 assertThat(tracker.userContext.user).isEqualTo(UserHandle.of(newID)) 230 assertThat(tracker.userProfiles).hasSize(1) 231 232 val info = tracker.userProfiles[0] 233 assertThat(info.id).isEqualTo(newID) 234 } 235 236 @Test 237 fun testManagedProfileAvailable() = testScope.runTest { 238 tracker.initialize(0) 239 val profileID = tracker.userId + 10 240 241 `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation -> 242 val id = invocation.getArgument<Int>(0) 243 val info = UserInfo(id, "", UserInfo.FLAG_FULL) 244 val infoProfile = UserInfo( 245 id + 10, 246 "", 247 "", 248 UserInfo.FLAG_MANAGED_PROFILE, 249 UserManager.USER_TYPE_PROFILE_MANAGED 250 ) 251 infoProfile.profileGroupId = id 252 listOf(info, infoProfile) 253 } 254 255 val intent = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE) 256 .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID)) 257 tracker.onReceive(context, intent) 258 259 assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId, profileID) 260 } 261 262 @Test 263 fun testManagedProfileUnavailable() = testScope.runTest { 264 tracker.initialize(0) 265 val profileID = tracker.userId + 10 266 267 `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation -> 268 val id = invocation.getArgument<Int>(0) 269 val info = UserInfo(id, "", UserInfo.FLAG_FULL) 270 val infoProfile = UserInfo( 271 id + 10, 272 "", 273 "", 274 UserInfo.FLAG_MANAGED_PROFILE or UserInfo.FLAG_QUIET_MODE, 275 UserManager.USER_TYPE_PROFILE_MANAGED 276 ) 277 infoProfile.profileGroupId = id 278 listOf(info, infoProfile) 279 } 280 281 val intent = Intent(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE) 282 .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID)) 283 tracker.onReceive(context, intent) 284 285 assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId, profileID) 286 } 287 288 @Test 289 fun testManagedProfileStartedAndRemoved() = testScope.runTest { 290 tracker.initialize(0) 291 val profileID = tracker.userId + 10 292 293 `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation -> 294 val id = invocation.getArgument<Int>(0) 295 val info = UserInfo(id, "", UserInfo.FLAG_FULL) 296 val infoProfile = UserInfo( 297 id + 10, 298 "", 299 "", 300 UserInfo.FLAG_MANAGED_PROFILE, 301 UserManager.USER_TYPE_PROFILE_MANAGED 302 ) 303 infoProfile.profileGroupId = id 304 listOf(info, infoProfile) 305 } 306 307 // Managed profile started 308 val intent = Intent(Intent.ACTION_MANAGED_PROFILE_UNLOCKED) 309 .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID)) 310 tracker.onReceive(context, intent) 311 312 assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId, profileID) 313 314 `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation -> 315 listOf(UserInfo(invocation.getArgument(0), "", UserInfo.FLAG_FULL)) 316 } 317 318 val intent2 = Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED) 319 .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID)) 320 tracker.onReceive(context, intent2) 321 322 assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId) 323 } 324 325 @Test 326 fun testCallbackNotCalledOnAdd() = testScope.runTest { 327 tracker.initialize(0) 328 val callback = TestCallback() 329 330 tracker.addCallback(callback, executor) 331 332 assertThat(callback.calledOnProfilesChanged).isEqualTo(0) 333 assertThat(callback.calledOnUserChanged).isEqualTo(0) 334 } 335 336 @Test 337 fun testCallbackCalledOnUserChanging() = testScope.runTest { 338 tracker.initialize(0) 339 val callback = TestCallback() 340 tracker.addCallback(callback, executor) 341 342 val newID = 5 343 344 val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java) 345 verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString()) 346 captor.value.onBeforeUserSwitching(newID) 347 captor.value.onUserSwitching(newID, userSwitchingReply) 348 runCurrent() 349 350 verify(userSwitchingReply).sendResult(any()) 351 assertThat(callback.calledOnUserChanging).isEqualTo(1) 352 assertThat(callback.lastUser).isEqualTo(newID) 353 assertThat(callback.lastUserContext?.userId).isEqualTo(newID) 354 } 355 356 @Test 357 fun testAsyncCallbackWaitsUserToChange() = testScope.runTest { 358 // Skip this test for CountDownLatch variation. The problem is that there would be a 359 // deadlock if the callbacks processing runs on the same thread as the callback (which 360 // is blocked by the latch). Before the change it works because the callbacks are 361 // processed on a binder thread which is always distinct. 362 // This is the issue that this feature addresses. 363 assume().that(isBackgroundUserTrackerEnabled).isTrue() 364 365 tracker.initialize(0) 366 val callback = TestCallback() 367 val callbackExecutor = FakeExecutor(FakeSystemClock()) 368 tracker.addCallback(callback, callbackExecutor) 369 370 val newID = 5 371 372 val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java) 373 verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString()) 374 captor.value.onUserSwitching(newID, userSwitchingReply) 375 376 assertThat(callback.calledOnUserChanging).isEqualTo(0) 377 verify(userSwitchingReply, never()).sendResult(any()) 378 379 FakeExecutor.exhaustExecutors(callbackExecutor) 380 runCurrent() 381 FakeExecutor.exhaustExecutors(callbackExecutor) 382 runCurrent() 383 384 assertThat(callback.calledOnUserChanging).isEqualTo(1) 385 verify(userSwitchingReply).sendResult(any()) 386 } 387 388 @Test 389 fun testCallbackCalledOnUserChanged() = testScope.runTest { 390 tracker.initialize(0) 391 val callback = TestCallback() 392 tracker.addCallback(callback, executor) 393 394 val newID = 5 395 396 val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java) 397 verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString()) 398 captor.value.onBeforeUserSwitching(newID) 399 captor.value.onUserSwitchComplete(newID) 400 runCurrent() 401 402 assertThat(callback.calledOnUserChanged).isEqualTo(1) 403 assertThat(callback.lastUser).isEqualTo(newID) 404 assertThat(callback.lastUserContext?.userId).isEqualTo(newID) 405 assertThat(callback.calledOnProfilesChanged).isEqualTo(1) 406 assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(newID) 407 } 408 409 @Test 410 fun testCallbackCalledOnUserInfoChanged() = testScope.runTest { 411 tracker.initialize(0) 412 val callback = TestCallback() 413 tracker.addCallback(callback, executor) 414 val profileID = tracker.userId + 10 415 416 `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation -> 417 val id = invocation.getArgument<Int>(0) 418 val info = UserInfo(id, "", UserInfo.FLAG_FULL) 419 val infoProfile = UserInfo( 420 id + 10, 421 "", 422 "", 423 UserInfo.FLAG_MANAGED_PROFILE, 424 UserManager.USER_TYPE_PROFILE_MANAGED 425 ) 426 infoProfile.profileGroupId = id 427 listOf(info, infoProfile) 428 } 429 430 val intent = Intent(Intent.ACTION_USER_INFO_CHANGED) 431 .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID)) 432 433 tracker.onReceive(context, intent) 434 435 assertThat(callback.calledOnUserChanged).isEqualTo(0) 436 assertThat(callback.calledOnProfilesChanged).isEqualTo(1) 437 assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(0, profileID) 438 } 439 440 @Test 441 fun testCallbackRemoved() = testScope.runTest { 442 tracker.initialize(0) 443 val newID = 5 444 val profileID = newID + 10 445 446 val callback = TestCallback() 447 tracker.addCallback(callback, executor) 448 tracker.removeCallback(callback) 449 450 val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java) 451 verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString()) 452 captor.value.onUserSwitching(newID, userSwitchingReply) 453 runCurrent() 454 verify(userSwitchingReply).sendResult(any()) 455 captor.value.onUserSwitchComplete(newID) 456 457 val intentProfiles = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE) 458 .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID)) 459 460 tracker.onReceive(context, intentProfiles) 461 462 assertThat(callback.calledOnUserChanging).isEqualTo(0) 463 assertThat(callback.calledOnUserChanged).isEqualTo(0) 464 assertThat(callback.calledOnProfilesChanged).isEqualTo(0) 465 } 466 467 private class TestCallback : UserTracker.Callback { 468 var calledOnUserChanging = 0 469 var calledOnUserChanged = 0 470 var calledOnProfilesChanged = 0 471 var lastUser: Int? = null 472 var lastUserContext: Context? = null 473 var lastUserProfiles = emptyList<UserInfo>() 474 475 override fun onUserChanging(newUser: Int, userContext: Context) { 476 calledOnUserChanging++ 477 lastUser = newUser 478 lastUserContext = userContext 479 } 480 481 override fun onUserChanged(newUser: Int, userContext: Context) { 482 calledOnUserChanged++ 483 lastUser = newUser 484 lastUserContext = userContext 485 } 486 487 override fun onProfilesChanged(profiles: List<UserInfo>) { 488 calledOnProfilesChanged++ 489 lastUserProfiles = profiles 490 } 491 } 492 } 493