xref: /aosp_15_r20/frameworks/base/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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