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