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