1 /*
2  * Copyright (C) 2022 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.qs;
18 
19 import static android.os.PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE;
20 
21 import static org.mockito.ArgumentMatchers.any;
22 import static org.mockito.ArgumentMatchers.anyInt;
23 import static org.mockito.ArgumentMatchers.argThat;
24 import static org.mockito.ArgumentMatchers.eq;
25 import static org.mockito.ArgumentMatchers.isNull;
26 import static org.mockito.Mockito.mock;
27 import static org.mockito.Mockito.never;
28 import static org.mockito.Mockito.times;
29 import static org.mockito.Mockito.verify;
30 
31 import android.app.IActivityManager;
32 import android.app.IForegroundServiceObserver;
33 import android.app.job.IUserVisibleJobObserver;
34 import android.app.job.JobScheduler;
35 import android.app.job.UserVisibleJobSummary;
36 import android.content.BroadcastReceiver;
37 import android.content.Context;
38 import android.content.Intent;
39 import android.content.pm.PackageManager;
40 import android.content.pm.UserInfo;
41 import android.os.Binder;
42 import android.os.RemoteException;
43 import android.os.UserHandle;
44 import android.provider.DeviceConfig;
45 import android.testing.AndroidTestingRunner;
46 import android.testing.TestableLooper;
47 
48 import androidx.test.filters.SmallTest;
49 
50 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
51 import com.android.systemui.SysuiTestCase;
52 import com.android.systemui.animation.DialogLaunchAnimator;
53 import com.android.systemui.broadcast.BroadcastDispatcher;
54 import com.android.systemui.dump.DumpManager;
55 import com.android.systemui.settings.UserTracker;
56 import com.android.systemui.util.DeviceConfigProxyFake;
57 import com.android.systemui.util.concurrency.FakeExecutor;
58 import com.android.systemui.util.time.FakeSystemClock;
59 
60 import org.junit.Assert;
61 import org.junit.Before;
62 import org.junit.Test;
63 import org.junit.runner.RunWith;
64 import org.mockito.ArgumentCaptor;
65 import org.mockito.ArgumentMatchers;
66 import org.mockito.InOrder;
67 import org.mockito.Mock;
68 import org.mockito.Mockito;
69 import org.mockito.MockitoAnnotations;
70 
71 import java.util.ArrayList;
72 import java.util.List;
73 
74 @RunWith(AndroidTestingRunner.class)
75 @TestableLooper.RunWithLooper
76 @SmallTest
77 public class FgsManagerControllerTest extends SysuiTestCase {
78 
79     FakeSystemClock mSystemClock;
80     FakeExecutor mMainExecutor;
81     FakeExecutor mBackgroundExecutor;
82     DeviceConfigProxyFake mDeviceConfigProxyFake;
83 
84     @Mock
85     IActivityManager mIActivityManager;
86     @Mock
87     JobScheduler mJobScheduler;
88     @Mock
89     PackageManager mPackageManager;
90     @Mock
91     UserTracker mUserTracker;
92     @Mock
93     DialogLaunchAnimator mDialogLaunchAnimator;
94     @Mock
95     BroadcastDispatcher mBroadcastDispatcher;
96     @Mock
97     DumpManager mDumpManager;
98 
99     private FgsManagerController mFmc;
100 
101     private IForegroundServiceObserver mIForegroundServiceObserver;
102     private IUserVisibleJobObserver mIUserVisibleJobObserver;
103     private UserTracker.Callback mUserTrackerCallback;
104     private BroadcastReceiver mShowFgsManagerReceiver;
105     private InOrder mJobSchedulerInOrder;
106 
107     private List<UserInfo> mUserProfiles;
108 
109     @Before
setUp()110     public void setUp() throws RemoteException {
111         MockitoAnnotations.initMocks(this);
112 
113         mDeviceConfigProxyFake = new DeviceConfigProxyFake();
114         mSystemClock = new FakeSystemClock();
115         mMainExecutor = new FakeExecutor(mSystemClock);
116         mBackgroundExecutor = new FakeExecutor(mSystemClock);
117 
118         mUserProfiles = new ArrayList<>();
119         Mockito.doReturn(mUserProfiles).when(mUserTracker).getUserProfiles();
120 
121         mJobSchedulerInOrder = Mockito.inOrder(mJobScheduler);
122 
123         mFmc = createFgsManagerController();
124     }
125 
126     @Test
testNumPackages()127     public void testNumPackages() throws RemoteException {
128         setUserProfiles(0);
129 
130         Binder b1 = new Binder();
131         Binder b2 = new Binder();
132         Assert.assertEquals(0, mFmc.getNumRunningPackages());
133         mIForegroundServiceObserver.onForegroundStateChanged(b1, "pkg1", 0, true);
134         Assert.assertEquals(1, mFmc.getNumRunningPackages());
135         mIForegroundServiceObserver.onForegroundStateChanged(b2, "pkg2", 0, true);
136         Assert.assertEquals(2, mFmc.getNumRunningPackages());
137         mIForegroundServiceObserver.onForegroundStateChanged(b1, "pkg1", 0, false);
138         Assert.assertEquals(1, mFmc.getNumRunningPackages());
139         mIForegroundServiceObserver.onForegroundStateChanged(b2, "pkg2", 0, false);
140         Assert.assertEquals(0, mFmc.getNumRunningPackages());
141     }
142 
143     @Test
testNumPackages_jobs()144     public void testNumPackages_jobs() throws RemoteException {
145         setUserProfiles(0);
146         setShowUserVisibleJobs(true);
147 
148         UserVisibleJobSummary j1 = new UserVisibleJobSummary(0, "pkg1", 0, "pkg1", null, 0);
149         UserVisibleJobSummary j2 = new UserVisibleJobSummary(1, "pkg2", 0, "pkg2", null, 1);
150         // pkg2 is performing work on behalf of pkg3. Since pkg2 will show the notification
151         // It should be the one shown in TaskManager.
152         UserVisibleJobSummary j3 = new UserVisibleJobSummary(1, "pkg2", 0, "pkg3", null, 3);
153         Assert.assertEquals(0, mFmc.getNumRunningPackages());
154         mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j1, true);
155         Assert.assertEquals(1, mFmc.getNumRunningPackages());
156         mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j2, true);
157         Assert.assertEquals(2, mFmc.getNumRunningPackages());
158         // Job3 starts running. The source package (pkg3) shouldn't matter. Since pkg2 is
159         // already running, the running package count shouldn't increase.
160         mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j3, true);
161         Assert.assertEquals(2, mFmc.getNumRunningPackages());
162         mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j1, false);
163         Assert.assertEquals(1, mFmc.getNumRunningPackages());
164         mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j2, false);
165         Assert.assertEquals(1, mFmc.getNumRunningPackages());
166         mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j3, false);
167         Assert.assertEquals(0, mFmc.getNumRunningPackages());
168     }
169 
170     @Test
testNumPackages_FgsAndJobs()171     public void testNumPackages_FgsAndJobs() throws RemoteException {
172         setUserProfiles(0);
173         setShowUserVisibleJobs(true);
174 
175         Binder b1 = new Binder();
176         Binder b2 = new Binder();
177         UserVisibleJobSummary j1 = new UserVisibleJobSummary(0, "pkg1", 0, "pkg1", null, 0);
178         UserVisibleJobSummary j3 = new UserVisibleJobSummary(1, "pkg3", 0, "pkg3", null, 1);
179         Assert.assertEquals(0, mFmc.getNumRunningPackages());
180         mIForegroundServiceObserver.onForegroundStateChanged(b1, "pkg1", 0, true);
181         Assert.assertEquals(1, mFmc.getNumRunningPackages());
182         mIForegroundServiceObserver.onForegroundStateChanged(b2, "pkg2", 0, true);
183         Assert.assertEquals(2, mFmc.getNumRunningPackages());
184         mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j1, true);
185         Assert.assertEquals(2, mFmc.getNumRunningPackages());
186         mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j3, true);
187         Assert.assertEquals(3, mFmc.getNumRunningPackages());
188         mIForegroundServiceObserver.onForegroundStateChanged(b2, "pkg2", 0, false);
189         Assert.assertEquals(2, mFmc.getNumRunningPackages());
190         mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j3, false);
191         Assert.assertEquals(1, mFmc.getNumRunningPackages());
192         mIForegroundServiceObserver.onForegroundStateChanged(b1, "pkg1", 0, false);
193         Assert.assertEquals(1, mFmc.getNumRunningPackages());
194         mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j1, false);
195         Assert.assertEquals(0, mFmc.getNumRunningPackages());
196     }
197 
198     @Test
testNumPackagesDoesNotChangeWhenSecondFgsIsStarted()199     public void testNumPackagesDoesNotChangeWhenSecondFgsIsStarted() throws RemoteException {
200         setUserProfiles(0);
201 
202         // Different tokens == different services
203         Binder b1 = new Binder();
204         Binder b2 = new Binder();
205         Assert.assertEquals(0, mFmc.getNumRunningPackages());
206         mIForegroundServiceObserver.onForegroundStateChanged(b1, "pkg1", 0, true);
207         Assert.assertEquals(1, mFmc.getNumRunningPackages());
208         mIForegroundServiceObserver.onForegroundStateChanged(b2, "pkg1", 0, true);
209         Assert.assertEquals(1, mFmc.getNumRunningPackages());
210         mIForegroundServiceObserver.onForegroundStateChanged(b1, "pkg1", 0, false);
211         Assert.assertEquals(1, mFmc.getNumRunningPackages());
212         mIForegroundServiceObserver.onForegroundStateChanged(b2, "pkg1", 0, false);
213         Assert.assertEquals(0, mFmc.getNumRunningPackages());
214     }
215 
216     @Test
testNumPackagesListener()217     public void testNumPackagesListener() throws RemoteException {
218         setUserProfiles(0);
219 
220         FgsManagerController.OnNumberOfPackagesChangedListener onNumberOfPackagesChangedListener =
221                 Mockito.mock(FgsManagerController.OnNumberOfPackagesChangedListener.class);
222 
223         mFmc.addOnNumberOfPackagesChangedListener(onNumberOfPackagesChangedListener);
224 
225         Binder b1 = new Binder();
226         Binder b2 = new Binder();
227 
228         verify(onNumberOfPackagesChangedListener, never()).onNumberOfPackagesChanged(anyInt());
229 
230         mIForegroundServiceObserver.onForegroundStateChanged(b1, "pkg1", 0, true);
231         mBackgroundExecutor.advanceClockToLast();
232         mBackgroundExecutor.runAllReady();
233         verify(onNumberOfPackagesChangedListener).onNumberOfPackagesChanged(1);
234 
235         mIForegroundServiceObserver.onForegroundStateChanged(b2, "pkg2", 0, true);
236         mBackgroundExecutor.advanceClockToLast();
237         mBackgroundExecutor.runAllReady();
238         verify(onNumberOfPackagesChangedListener).onNumberOfPackagesChanged(2);
239 
240         mIForegroundServiceObserver.onForegroundStateChanged(b1, "pkg1", 0, false);
241         mBackgroundExecutor.advanceClockToLast();
242         mBackgroundExecutor.runAllReady();
243         verify(onNumberOfPackagesChangedListener, times(2)).onNumberOfPackagesChanged(1);
244 
245         mIForegroundServiceObserver.onForegroundStateChanged(b2, "pkg2", 0, false);
246         mBackgroundExecutor.advanceClockToLast();
247         mBackgroundExecutor.runAllReady();
248         verify(onNumberOfPackagesChangedListener).onNumberOfPackagesChanged(0);
249     }
250 
251     @Test
testChangesSinceLastDialog()252     public void testChangesSinceLastDialog() throws RemoteException {
253         setUserProfiles(0);
254 
255         Assert.assertFalse(mFmc.getNewChangesSinceDialogWasDismissed());
256         mIForegroundServiceObserver.onForegroundStateChanged(new Binder(), "pkg", 0, true);
257         Assert.assertTrue(mFmc.getNewChangesSinceDialogWasDismissed());
258     }
259 
260     @Test
testProfilePackagesCounted()261     public void testProfilePackagesCounted() throws RemoteException {
262         setUserProfiles(0, 10);
263 
264         mIForegroundServiceObserver.onForegroundStateChanged(new Binder(), "pkg1", 0, true);
265         mIForegroundServiceObserver.onForegroundStateChanged(new Binder(), "pkg2", 10, true);
266         Assert.assertEquals(2, mFmc.getNumRunningPackages());
267     }
268 
269     @Test
testSecondaryUserPackagesAreNotCounted()270     public void testSecondaryUserPackagesAreNotCounted() throws RemoteException {
271         setUserProfiles(0);
272 
273         mIForegroundServiceObserver.onForegroundStateChanged(new Binder(), "pkg1", 0, true);
274         mIForegroundServiceObserver.onForegroundStateChanged(new Binder(), "pkg2", 10, true);
275         Assert.assertEquals(1, mFmc.getNumRunningPackages());
276     }
277 
278     @Test
testSecondaryUserPackagesAreCountedWhenUserSwitch()279     public void testSecondaryUserPackagesAreCountedWhenUserSwitch() throws RemoteException {
280         setUserProfiles(0);
281 
282         mIForegroundServiceObserver.onForegroundStateChanged(new Binder(), "pkg1", 0, true);
283         mIForegroundServiceObserver.onForegroundStateChanged(new Binder(), "pkg2", 10, true);
284         mIForegroundServiceObserver.onForegroundStateChanged(new Binder(), "pkg3", 10, true);
285 
286         Assert.assertEquals(1, mFmc.getNumRunningPackages());
287 
288         setUserProfiles(10);
289         Assert.assertEquals(2, mFmc.getNumRunningPackages());
290     }
291 
292     @Test
testButtonVisibilityOnShowAllowlistButtonFlagChange()293     public void testButtonVisibilityOnShowAllowlistButtonFlagChange() throws Exception {
294         setUserProfiles(0);
295         setBackgroundRestrictionExemptionReason("pkg", 12345, REASON_ALLOWLISTED_PACKAGE);
296 
297         final Binder binder = new Binder();
298         setShowStopButtonForUserAllowlistedApps(true);
299         mIForegroundServiceObserver.onForegroundStateChanged(binder, "pkg", 0, true);
300         Assert.assertEquals(1, mFmc.visibleButtonsCount());
301 
302         mIForegroundServiceObserver.onForegroundStateChanged(binder, "pkg", 0, false);
303         Assert.assertEquals(0, mFmc.visibleButtonsCount());
304 
305         setShowStopButtonForUserAllowlistedApps(false);
306         mIForegroundServiceObserver.onForegroundStateChanged(binder, "pkg", 0, true);
307         Assert.assertEquals(0, mFmc.visibleButtonsCount());
308     }
309 
310     @Test
testShowUserVisibleJobsOnCreation()311     public void testShowUserVisibleJobsOnCreation() {
312         // Test when the default is on.
313         mDeviceConfigProxyFake.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
314                 SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS,
315                 "true", false);
316         FgsManagerController fmc = new FgsManagerControllerImpl(
317                 mContext,
318                 mMainExecutor,
319                 mBackgroundExecutor,
320                 mSystemClock,
321                 mIActivityManager,
322                 mJobScheduler,
323                 mPackageManager,
324                 mUserTracker,
325                 mDeviceConfigProxyFake,
326                 mDialogLaunchAnimator,
327                 mBroadcastDispatcher,
328                 mDumpManager
329         );
330         fmc.init();
331         Assert.assertTrue(fmc.getIncludesUserVisibleJobs());
332         ArgumentCaptor<IUserVisibleJobObserver> iUserVisibleJobObserverArgumentCaptor =
333                 ArgumentCaptor.forClass(IUserVisibleJobObserver.class);
334         mJobSchedulerInOrder.verify(mJobScheduler)
335                 .registerUserVisibleJobObserver(iUserVisibleJobObserverArgumentCaptor.capture());
336         Assert.assertNotNull(iUserVisibleJobObserverArgumentCaptor.getValue());
337 
338         // Test when the default is off.
339         mDeviceConfigProxyFake.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
340                 SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS,
341                 "false", false);
342         fmc = new FgsManagerControllerImpl(
343                 mContext,
344                 mMainExecutor,
345                 mBackgroundExecutor,
346                 mSystemClock,
347                 mIActivityManager,
348                 mJobScheduler,
349                 mPackageManager,
350                 mUserTracker,
351                 mDeviceConfigProxyFake,
352                 mDialogLaunchAnimator,
353                 mBroadcastDispatcher,
354                 mDumpManager
355         );
356         fmc.init();
357         Assert.assertFalse(fmc.getIncludesUserVisibleJobs());
358         mJobSchedulerInOrder.verify(mJobScheduler, never()).registerUserVisibleJobObserver(any());
359     }
360 
361     @Test
testShowUserVisibleJobsToggling()362     public void testShowUserVisibleJobsToggling() throws Exception {
363         setUserProfiles(0);
364         setShowUserVisibleJobs(true);
365 
366         // pkg1 has only job
367         // pkg2 has both job and fgs
368         // pkg3 has only fgs
369         UserVisibleJobSummary j1 = new UserVisibleJobSummary(0, "pkg1", 0, "pkg1", null, 0);
370         UserVisibleJobSummary j2 = new UserVisibleJobSummary(1, "pkg2", 0, "pkg2", null, 1);
371         Binder b2 = new Binder();
372         Binder b3 = new Binder();
373 
374         Assert.assertEquals(0, mFmc.getNumRunningPackages());
375         mIForegroundServiceObserver.onForegroundStateChanged(b2, "pkg2", 0, true);
376         mIForegroundServiceObserver.onForegroundStateChanged(b3, "pkg3", 0, true);
377         Assert.assertEquals(2, mFmc.getNumRunningPackages());
378         mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j1, true);
379         mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j2, true);
380         Assert.assertEquals(3, mFmc.getNumRunningPackages());
381 
382         // Turn off the flag, confirm the number of packages is updated properly.
383         setShowUserVisibleJobs(false);
384         // Only pkg1 should be removed since the other two have fgs
385         Assert.assertEquals(2, mFmc.getNumRunningPackages());
386 
387         setShowUserVisibleJobs(true);
388 
389         Assert.assertEquals(2, mFmc.getNumRunningPackages());
390         mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j1, true);
391         mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j2, true);
392         Assert.assertEquals(3, mFmc.getNumRunningPackages());
393     }
394 
setShowStopButtonForUserAllowlistedApps(boolean enable)395     private void setShowStopButtonForUserAllowlistedApps(boolean enable) {
396         mDeviceConfigProxyFake.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
397                 SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS,
398                 enable ? "true" : "false", false);
399         mBackgroundExecutor.advanceClockToLast();
400         mBackgroundExecutor.runAllReady();
401     }
402 
setShowUserVisibleJobs(boolean enable)403     private void setShowUserVisibleJobs(boolean enable) {
404         if (mFmc.getIncludesUserVisibleJobs() == enable) {
405             // No change.
406             return;
407         }
408 
409         mDeviceConfigProxyFake.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
410                 SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS,
411                 enable ? "true" : "false", false);
412         mBackgroundExecutor.advanceClockToLast();
413         mBackgroundExecutor.runAllReady();
414 
415         ArgumentCaptor<IUserVisibleJobObserver> iUserVisibleJobObserverArgumentCaptor =
416                 ArgumentCaptor.forClass(IUserVisibleJobObserver.class);
417         if (enable) {
418             mJobSchedulerInOrder.verify(mJobScheduler).registerUserVisibleJobObserver(
419                     iUserVisibleJobObserverArgumentCaptor.capture()
420             );
421             mIUserVisibleJobObserver = iUserVisibleJobObserverArgumentCaptor.getValue();
422         } else {
423             mJobSchedulerInOrder.verify(mJobScheduler).unregisterUserVisibleJobObserver(
424                     eq(mIUserVisibleJobObserver)
425             );
426             mIUserVisibleJobObserver = null;
427         }
428     }
429 
setBackgroundRestrictionExemptionReason(String pkgName, int uid, int reason)430     private void setBackgroundRestrictionExemptionReason(String pkgName, int uid, int reason)
431             throws Exception {
432         Mockito.doReturn(uid)
433                 .when(mPackageManager)
434                 .getPackageUidAsUser(pkgName, UserHandle.getUserId(uid));
435         Mockito.doReturn(reason)
436                 .when(mIActivityManager)
437                 .getBackgroundRestrictionExemptionReason(uid);
438     }
439 
createFgsManagerController()440     FgsManagerController createFgsManagerController() throws RemoteException {
441         ArgumentCaptor<IForegroundServiceObserver> iForegroundServiceObserverArgumentCaptor =
442                 ArgumentCaptor.forClass(IForegroundServiceObserver.class);
443         ArgumentCaptor<UserTracker.Callback> userTrackerCallbackArgumentCaptor =
444                 ArgumentCaptor.forClass(UserTracker.Callback.class);
445         ArgumentCaptor<BroadcastReceiver> showFgsManagerReceiverArgumentCaptor =
446                 ArgumentCaptor.forClass(BroadcastReceiver.class);
447 
448         FgsManagerController result = new FgsManagerControllerImpl(
449                 mContext,
450                 mMainExecutor,
451                 mBackgroundExecutor,
452                 mSystemClock,
453                 mIActivityManager,
454                 mJobScheduler,
455                 mPackageManager,
456                 mUserTracker,
457                 mDeviceConfigProxyFake,
458                 mDialogLaunchAnimator,
459                 mBroadcastDispatcher,
460                 mDumpManager
461         );
462         result.init();
463 
464         verify(mIActivityManager).registerForegroundServiceObserver(
465                 iForegroundServiceObserverArgumentCaptor.capture()
466         );
467         verify(mUserTracker).addCallback(
468                 userTrackerCallbackArgumentCaptor.capture(),
469                 ArgumentMatchers.eq(mBackgroundExecutor)
470         );
471         verify(mBroadcastDispatcher).registerReceiver(
472                 showFgsManagerReceiverArgumentCaptor.capture(),
473                 argThat(fltr -> fltr.matchAction(Intent.ACTION_SHOW_FOREGROUND_SERVICE_MANAGER)),
474                 eq(mMainExecutor),
475                 isNull(),
476                 eq(Context.RECEIVER_NOT_EXPORTED),
477                 isNull()
478         );
479 
480         mIForegroundServiceObserver = iForegroundServiceObserverArgumentCaptor.getValue();
481         mUserTrackerCallback = userTrackerCallbackArgumentCaptor.getValue();
482         mShowFgsManagerReceiver = showFgsManagerReceiverArgumentCaptor.getValue();
483 
484         if (result.getIncludesUserVisibleJobs()) {
485             ArgumentCaptor<IUserVisibleJobObserver> iUserVisibleJobObserverArgumentCaptor =
486                     ArgumentCaptor.forClass(IUserVisibleJobObserver.class);
487             mJobSchedulerInOrder.verify(mJobScheduler).registerUserVisibleJobObserver(
488                     iUserVisibleJobObserverArgumentCaptor.capture()
489             );
490             mIUserVisibleJobObserver = iUserVisibleJobObserverArgumentCaptor.getValue();
491         }
492 
493         return result;
494     }
495 
setUserProfiles(int current, int... profileUserIds)496     private void setUserProfiles(int current, int... profileUserIds) {
497         mUserProfiles.clear();
498         mUserProfiles.add(new UserInfo(current, "current:" + current, 0));
499         for (int id : profileUserIds) {
500             mUserProfiles.add(new UserInfo(id, "profile:" + id, 0));
501         }
502 
503         if (mUserTrackerCallback != null) {
504             mUserTrackerCallback.onUserChanged(current, mock(Context.class));
505             mUserTrackerCallback.onProfilesChanged(mUserProfiles);
506         }
507     }
508 }
509