• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.server.job;
18 
19 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
20 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
21 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
22 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
23 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
24 import static com.android.server.job.JobConcurrencyManager.KEY_PKG_CONCURRENCY_LIMIT_EJ;
25 import static com.android.server.job.JobConcurrencyManager.KEY_PKG_CONCURRENCY_LIMIT_REGULAR;
26 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
27 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
28 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER_IMPORTANT;
29 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ;
30 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_FGS;
31 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
32 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
33 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_UI;
34 
35 import static junit.framework.Assert.assertEquals;
36 import static junit.framework.Assert.assertFalse;
37 import static junit.framework.Assert.assertTrue;
38 
39 import static org.junit.Assert.fail;
40 import static org.mockito.ArgumentMatchers.any;
41 import static org.mockito.ArgumentMatchers.anyInt;
42 import static org.mockito.ArgumentMatchers.anyString;
43 import static org.mockito.Mockito.anyLong;
44 import static org.mockito.Mockito.eq;
45 import static org.mockito.Mockito.mock;
46 import static org.mockito.Mockito.when;
47 
48 import android.annotation.Nullable;
49 import android.app.ActivityManager;
50 import android.app.ActivityManagerInternal;
51 import android.app.AppGlobals;
52 import android.app.BackgroundStartPrivileges;
53 import android.app.IActivityManager;
54 import android.app.job.JobInfo;
55 import android.content.ComponentName;
56 import android.content.Context;
57 import android.content.pm.IPackageManager;
58 import android.content.pm.UserInfo;
59 import android.content.res.Resources;
60 import android.os.Looper;
61 import android.os.PowerManager;
62 import android.os.RemoteException;
63 import android.os.UserHandle;
64 import android.provider.DeviceConfig;
65 import android.util.ArrayMap;
66 import android.util.ArraySet;
67 import android.util.SparseIntArray;
68 
69 import androidx.test.filters.SmallTest;
70 import androidx.test.runner.AndroidJUnit4;
71 
72 import com.android.internal.R;
73 import com.android.internal.app.IBatteryStats;
74 import com.android.server.LocalServices;
75 import com.android.server.job.JobConcurrencyManager.GracePeriodObserver;
76 import com.android.server.job.JobConcurrencyManager.WorkTypeConfig;
77 import com.android.server.job.controllers.JobStatus;
78 import com.android.server.pm.UserManagerInternal;
79 
80 import org.junit.After;
81 import org.junit.AfterClass;
82 import org.junit.Before;
83 import org.junit.BeforeClass;
84 import org.junit.Test;
85 import org.junit.runner.RunWith;
86 import org.mockito.Mock;
87 import org.mockito.MockitoSession;
88 import org.mockito.quality.Strictness;
89 import org.mockito.stubbing.Answer;
90 
91 import java.util.ArrayList;
92 import java.util.List;
93 
94 @RunWith(AndroidJUnit4.class)
95 @SmallTest
96 public final class JobConcurrencyManagerTest {
97     private static final int UNAVAILABLE_USER = 0;
98     private JobConcurrencyManager mJobConcurrencyManager;
99     private UserManagerInternal mUserManagerInternal;
100     private ActivityManagerInternal mActivityManagerInternal;
101     private int mNextUserId;
102     private int mDefaultUserId;
103     private GracePeriodObserver mGracePeriodObserver;
104     private Context mContext;
105     private InjectorForTest mInjector;
106     private MockitoSession mMockingSession;
107     private Resources mResources;
108     private PendingJobQueue mPendingJobQueue;
109     private DeviceConfig.Properties.Builder mConfigBuilder;
110 
111     @Mock
112     private IPackageManager mIPackageManager;
113 
114     private static class InjectorForTest extends JobConcurrencyManager.Injector {
115         public final ArrayMap<JobServiceContext, JobStatus> contexts = new ArrayMap<>();
116 
117         @Override
createJobServiceContext(JobSchedulerService service, JobConcurrencyManager concurrencyManager, JobNotificationCoordinator notificationCoordinator, IBatteryStats batteryStats, JobPackageTracker tracker, Looper looper)118         JobServiceContext createJobServiceContext(JobSchedulerService service,
119                 JobConcurrencyManager concurrencyManager,
120                 JobNotificationCoordinator notificationCoordinator, IBatteryStats batteryStats,
121                 JobPackageTracker tracker, Looper looper) {
122             final JobServiceContext context = mock(JobServiceContext.class);
123             doAnswer((Answer<Boolean>) invocationOnMock -> {
124                 Object[] args = invocationOnMock.getArguments();
125                 final JobStatus job = (JobStatus) args[0];
126                 contexts.put(context, job);
127                 doReturn(job).when(context).getRunningJobLocked();
128                 return true;
129             }).when(context).executeRunnableJob(any(), anyInt());
130             contexts.put(context, null);
131             return context;
132         }
133     }
134 
135     @BeforeClass
setUpOnce()136     public static void setUpOnce() {
137         LocalServices.addService(UserManagerInternal.class, mock(UserManagerInternal.class));
138         LocalServices.addService(
139                 ActivityManagerInternal.class, mock(ActivityManagerInternal.class));
140     }
141 
142     @AfterClass
tearDownOnce()143     public static void tearDownOnce() {
144         LocalServices.removeServiceForTest(UserManagerInternal.class);
145         LocalServices.removeServiceForTest(ActivityManagerInternal.class);
146     }
147 
148     @Before
setUp()149     public void setUp() {
150         mMockingSession = mockitoSession()
151                 .initMocks(this)
152                 .mockStatic(AppGlobals.class)
153                 .spyStatic(DeviceConfig.class)
154                 .strictness(Strictness.LENIENT)
155                 .startMocking();
156         final JobSchedulerService jobSchedulerService = mock(JobSchedulerService.class);
157         mContext = mock(Context.class);
158         mResources = mock(Resources.class);
159         doReturn(true).when(mResources).getBoolean(
160                 R.bool.config_jobSchedulerRestrictBackgroundUser);
161         when(mContext.getResources()).thenReturn(mResources);
162         doReturn(mContext).when(jobSchedulerService).getTestableContext();
163         doReturn(jobSchedulerService).when(jobSchedulerService).getLock();
164         mConfigBuilder = new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
165         doAnswer((Answer<DeviceConfig.Properties>) invocationOnMock -> mConfigBuilder.build())
166                 .when(() -> DeviceConfig.getProperties(eq(DeviceConfig.NAMESPACE_JOB_SCHEDULER)));
167         mPendingJobQueue = new PendingJobQueue();
168         doReturn(mPendingJobQueue).when(jobSchedulerService).getPendingJobQueue();
169         doReturn(mIPackageManager).when(AppGlobals::getPackageManager);
170         doReturn(mock(PowerManager.class)).when(mContext).getSystemService(PowerManager.class);
171         mInjector = new InjectorForTest();
172         doAnswer((Answer<Long>) invocationOnMock -> {
173             Object[] args = invocationOnMock.getArguments();
174             final JobStatus job = (JobStatus) args[0];
175             return job.shouldTreatAsExpeditedJob()
176                     ? JobSchedulerService.Constants.DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS
177                     : JobSchedulerService.Constants.DEFAULT_RUNTIME_MIN_GUARANTEE_MS;
178         }).when(jobSchedulerService).getMinJobExecutionGuaranteeMs(any());
179         mJobConcurrencyManager = new JobConcurrencyManager(jobSchedulerService, mInjector);
180         mGracePeriodObserver = mock(GracePeriodObserver.class);
181         mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
182         mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
183         doReturn(BackgroundStartPrivileges.NONE)
184                 .when(mActivityManagerInternal).getBackgroundStartPrivileges(anyInt());
185         mDefaultUserId = mNextUserId;
186         createCurrentUser(true);
187         mNextUserId = 10;
188         mJobConcurrencyManager.mGracePeriodObserver = mGracePeriodObserver;
189 
190         IActivityManager activityManager = ActivityManager.getService();
191         spyOn(activityManager);
192         try {
193             doNothing().when(activityManager).registerUserSwitchObserver(any(), anyString());
194         } catch (RemoteException e) {
195             fail("registerUserSwitchObserver threw exception: " + e.getMessage());
196         }
197 
198         mJobConcurrencyManager.onSystemReady();
199     }
200 
201     @After
tearDown()202     public void tearDown() throws Exception {
203         resetConfig();
204         if (mMockingSession != null) {
205             mMockingSession.finishMocking();
206         }
207     }
208 
209     @Test
testPrepareForAssignmentDetermination_noJobs()210     public void testPrepareForAssignmentDetermination_noJobs() {
211         mPendingJobQueue.clear();
212 
213         final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
214         final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
215         final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
216         final JobConcurrencyManager.AssignmentInfo assignmentInfo =
217                 new JobConcurrencyManager.AssignmentInfo();
218         mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
219                 idle, preferredUidOnly, stoppable, assignmentInfo);
220 
221         assertEquals(JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT, idle.size());
222         assertEquals(0, preferredUidOnly.size());
223         assertEquals(0, stoppable.size());
224         assertEquals(0, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
225         assertEquals(0, assignmentInfo.numRunningImmediacyPrivileged);
226     }
227 
228     @Test
testPrepareForAssignmentDetermination_onlyPendingJobs()229     public void testPrepareForAssignmentDetermination_onlyPendingJobs() {
230         for (int i = 0; i < JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT; ++i) {
231             JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i);
232             mPendingJobQueue.add(job);
233         }
234 
235         final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
236         final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
237         final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
238         final JobConcurrencyManager.AssignmentInfo assignmentInfo =
239                 new JobConcurrencyManager.AssignmentInfo();
240         mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
241                 idle, preferredUidOnly, stoppable, assignmentInfo);
242 
243         assertEquals(JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT, idle.size());
244         assertEquals(0, preferredUidOnly.size());
245         assertEquals(0, stoppable.size());
246         assertEquals(0, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
247         assertEquals(0, assignmentInfo.numRunningImmediacyPrivileged);
248     }
249 
250     @Test
testPrepareForAssignmentDetermination_onlyPreferredUidOnly()251     public void testPrepareForAssignmentDetermination_onlyPreferredUidOnly() {
252         for (int i = 0; i < JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT; ++i) {
253             JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i);
254             mJobConcurrencyManager.addRunningJobForTesting(job);
255         }
256 
257         for (int i = 0; i < mInjector.contexts.size(); ++i) {
258             doReturn(true).when(mInjector.contexts.keyAt(i)).isWithinExecutionGuaranteeTime();
259         }
260 
261         final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
262         final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
263         final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
264         final JobConcurrencyManager.AssignmentInfo assignmentInfo =
265                 new JobConcurrencyManager.AssignmentInfo();
266         mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
267                 idle, preferredUidOnly, stoppable, assignmentInfo);
268 
269         assertEquals(0, idle.size());
270         assertEquals(JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT, preferredUidOnly.size());
271         assertEquals(0, stoppable.size());
272         assertEquals(0, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
273         assertEquals(0, assignmentInfo.numRunningImmediacyPrivileged);
274     }
275 
276     @Test
testPrepareForAssignmentDetermination_onlyStartedWithImmediacyPrivilege()277     public void testPrepareForAssignmentDetermination_onlyStartedWithImmediacyPrivilege() {
278         for (int i = 0; i < JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT; ++i) {
279             JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i);
280             job.startedWithImmediacyPrivilege = true;
281             mJobConcurrencyManager.addRunningJobForTesting(job);
282         }
283 
284         for (int i = 0; i < mInjector.contexts.size(); ++i) {
285             doReturn(i % 2 == 0).when(mInjector.contexts.keyAt(i)).isWithinExecutionGuaranteeTime();
286         }
287 
288         final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
289         final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
290         final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
291         final JobConcurrencyManager.AssignmentInfo assignmentInfo =
292                 new JobConcurrencyManager.AssignmentInfo();
293         mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
294                 idle, preferredUidOnly, stoppable, assignmentInfo);
295 
296         assertEquals(0, idle.size());
297         assertEquals(JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT / 2, preferredUidOnly.size());
298         assertEquals(JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT / 2, stoppable.size());
299         assertEquals(0, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
300         assertEquals(JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT,
301                 assignmentInfo.numRunningImmediacyPrivileged);
302     }
303 
304     @Test
testDetermineAssignments_allRegular()305     public void testDetermineAssignments_allRegular() throws Exception {
306         setConcurrencyConfig(JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT,
307                 new TypeConfig(WORK_TYPE_BG, 0, JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT));
308         final ArraySet<JobStatus> jobs = new ArraySet<>();
309         for (int i = 0; i < JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT; ++i) {
310             final int uid = mDefaultUserId * UserHandle.PER_USER_RANGE + i;
311             final String sourcePkgName = "com.source.package." + UserHandle.getAppId(uid);
312             setPackageUid(sourcePkgName, uid);
313             final JobStatus job = createJob(uid, sourcePkgName);
314             mPendingJobQueue.add(job);
315             jobs.add(job);
316         }
317 
318         final ArraySet<JobConcurrencyManager.ContextAssignment> changed = new ArraySet<>();
319         final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
320         final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
321         final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
322         final JobConcurrencyManager.AssignmentInfo assignmentInfo =
323                 new JobConcurrencyManager.AssignmentInfo();
324         mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
325                 idle, preferredUidOnly, stoppable, assignmentInfo);
326         mJobConcurrencyManager
327                 .determineAssignmentsLocked(changed, idle, preferredUidOnly, stoppable,
328                         assignmentInfo);
329 
330         assertEquals(JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT, changed.size());
331         for (int i = changed.size() - 1; i >= 0; --i) {
332             jobs.remove(changed.valueAt(i).newJob);
333         }
334         assertTrue("Some jobs weren't assigned", jobs.isEmpty());
335     }
336 
337     @Test
testDetermineAssignments_allPreferredUidOnly_shortTimeLeft()338     public void testDetermineAssignments_allPreferredUidOnly_shortTimeLeft() throws Exception {
339         mConfigBuilder.setBoolean(JobConcurrencyManager.KEY_ENABLE_MAX_WAIT_TIME_BYPASS, true);
340         setConcurrencyConfig(JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT,
341                 new TypeConfig(WORK_TYPE_BG, 0, JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT));
342         for (int i = 0; i < JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT * 3; ++i) {
343             final int uid = mDefaultUserId * UserHandle.PER_USER_RANGE + i;
344             final String sourcePkgName = "com.source.package." + UserHandle.getAppId(uid);
345             setPackageUid(sourcePkgName, uid);
346             final JobStatus job = createJob(uid, sourcePkgName);
347             spyOn(job);
348             doReturn(i % 3 == 0).when(job).shouldTreatAsUserInitiatedJob();
349             doReturn(i % 3 == 1).when(job).shouldTreatAsExpeditedJob();
350             if (i < JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT) {
351                 mJobConcurrencyManager.addRunningJobForTesting(job);
352             } else {
353                 mPendingJobQueue.add(job);
354             }
355         }
356 
357         // Waiting time is too short, so we shouldn't create any extra contexts.
358         final long remainingTimeMs = JobConcurrencyManager.DEFAULT_MAX_WAIT_UI_MS / 2;
359         for (int i = 0; i < mInjector.contexts.size(); ++i) {
360             doReturn(true).when(mInjector.contexts.keyAt(i)).isWithinExecutionGuaranteeTime();
361             doReturn(remainingTimeMs)
362                     .when(mInjector.contexts.keyAt(i)).getRemainingGuaranteedTimeMs(anyLong());
363         }
364 
365         final ArraySet<JobConcurrencyManager.ContextAssignment> changed = new ArraySet<>();
366         final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
367         final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
368         final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
369         final JobConcurrencyManager.AssignmentInfo assignmentInfo =
370                 new JobConcurrencyManager.AssignmentInfo();
371 
372         mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
373                 idle, preferredUidOnly, stoppable, assignmentInfo);
374         assertEquals(remainingTimeMs, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
375         assertEquals(JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT, preferredUidOnly.size());
376 
377         mJobConcurrencyManager
378                 .determineAssignmentsLocked(changed, idle, preferredUidOnly, stoppable,
379                         assignmentInfo);
380 
381         assertEquals(JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT, preferredUidOnly.size());
382         assertEquals(0, changed.size());
383     }
384 
385     @Test
testDetermineAssignments_allPreferredUidOnly_mediumTimeLeft_onlyRegRunning()386     public void testDetermineAssignments_allPreferredUidOnly_mediumTimeLeft_onlyRegRunning()
387             throws Exception {
388         mConfigBuilder.setBoolean(JobConcurrencyManager.KEY_ENABLE_MAX_WAIT_TIME_BYPASS, true);
389         // Set the waiting time to be less than an EJ's min execution time.
390         mConfigBuilder.setLong(JobConcurrencyManager.KEY_MAX_WAIT_UI_MS, 2 * 60_000L);
391         setConcurrencyConfig(JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT,
392                 new TypeConfig(WORK_TYPE_BG, 0, JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT));
393         final ArraySet<JobStatus> jobs = new ArraySet<>();
394         for (int i = 0; i < JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT * 4; ++i) {
395             final int uid = mDefaultUserId * UserHandle.PER_USER_RANGE + i;
396             final String sourcePkgName = "com.source.package." + UserHandle.getAppId(uid);
397             setPackageUid(sourcePkgName, uid);
398             final JobStatus job = createJob(uid, sourcePkgName);
399             spyOn(job);
400             doReturn(i % 3 == 0).when(job).shouldTreatAsUserInitiatedJob();
401             doReturn(i % 3 == 1).when(job).shouldTreatAsExpeditedJob();
402             if (i % 3 == 2
403                     && mJobConcurrencyManager.mActiveServices.size()
404                             < JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT) {
405                 mJobConcurrencyManager.addRunningJobForTesting(job);
406             } else {
407                 mPendingJobQueue.add(job);
408                 jobs.add(job);
409             }
410         }
411 
412         // Waiting time is longer than the EJ & UI waiting time, but shorter than regular job
413         // waiting time, so we should only create 2 extra contexts (one for EJ, one for UIJ).
414         final long remainingTimeMs = (JobConcurrencyManager.DEFAULT_MAX_WAIT_EJ_MS
415                 + JobConcurrencyManager.DEFAULT_MAX_WAIT_REGULAR_MS) / 2;
416         for (int i = 0; i < mInjector.contexts.size(); ++i) {
417             doReturn(true).when(mInjector.contexts.keyAt(i)).isWithinExecutionGuaranteeTime();
418             doReturn(remainingTimeMs)
419                     .when(mInjector.contexts.keyAt(i)).getRemainingGuaranteedTimeMs(anyLong());
420         }
421 
422         final ArraySet<JobConcurrencyManager.ContextAssignment> changed = new ArraySet<>();
423         final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
424         final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
425         final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
426         final JobConcurrencyManager.AssignmentInfo assignmentInfo =
427                 new JobConcurrencyManager.AssignmentInfo();
428 
429         mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
430                 idle, preferredUidOnly, stoppable, assignmentInfo);
431         assertEquals(remainingTimeMs, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
432         assertEquals(JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT, preferredUidOnly.size());
433 
434         mJobConcurrencyManager
435                 .determineAssignmentsLocked(changed, idle, preferredUidOnly, stoppable,
436                         assignmentInfo);
437 
438         assertEquals(JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT, preferredUidOnly.size());
439         for (int i = changed.size() - 1; i >= 0; --i) {
440             jobs.remove(changed.valueAt(i).newJob);
441         }
442         // 1 EJ & 1 UIJ removed from the pending list.
443         assertEquals(3 * JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT - 2, jobs.size());
444         assertEquals(2, changed.size());
445         JobStatus assignedJob1 = changed.valueAt(0).newJob;
446         JobStatus assignedJob2 = changed.valueAt(1).newJob;
447         boolean ejFirst = assignedJob1.shouldTreatAsExpeditedJob();
448         if (ejFirst) {
449             assertTrue(assignedJob1.shouldTreatAsExpeditedJob());
450             assertTrue(assignedJob2.shouldTreatAsUserInitiatedJob());
451         } else {
452             assertTrue(assignedJob1.shouldTreatAsUserInitiatedJob());
453             assertTrue(assignedJob2.shouldTreatAsExpeditedJob());
454         }
455     }
456 
457     @Test
testDetermineAssignments_allPreferredUidOnly_mediumTimeLeft_onlyUiRunning()458     public void testDetermineAssignments_allPreferredUidOnly_mediumTimeLeft_onlyUiRunning()
459             throws Exception {
460         mConfigBuilder.setBoolean(JobConcurrencyManager.KEY_ENABLE_MAX_WAIT_TIME_BYPASS, true);
461         // Set the waiting time to be less than an EJ's min execution time.
462         mConfigBuilder.setLong(JobConcurrencyManager.KEY_MAX_WAIT_UI_MS, 2 * 60_000L);
463         setConcurrencyConfig(JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT,
464                 new TypeConfig(WORK_TYPE_BG, 0, JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT));
465         final ArraySet<JobStatus> jobs = new ArraySet<>();
466         for (int i = 0; i < JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT * 4; ++i) {
467             final int uid = mDefaultUserId * UserHandle.PER_USER_RANGE + i;
468             final String sourcePkgName = "com.source.package." + UserHandle.getAppId(uid);
469             setPackageUid(sourcePkgName, uid);
470             final JobStatus job = createJob(uid, sourcePkgName);
471             spyOn(job);
472             doReturn(i % 3 == 0).when(job).shouldTreatAsUserInitiatedJob();
473             doReturn(i % 3 == 1).when(job).shouldTreatAsExpeditedJob();
474             if (i % 3 == 0
475                     && mJobConcurrencyManager.mActiveServices.size()
476                             < JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT) {
477                 mJobConcurrencyManager.addRunningJobForTesting(job);
478             } else {
479                 mPendingJobQueue.add(job);
480                 jobs.add(job);
481             }
482         }
483 
484         // Waiting time is longer than the EJ & UI waiting time, but shorter than regular job
485         // waiting time, so we should only create 2 extra contexts (one for EJ, one for UIJ).
486         final long remainingTimeMs = (JobConcurrencyManager.DEFAULT_MAX_WAIT_EJ_MS
487                 + JobConcurrencyManager.DEFAULT_MAX_WAIT_REGULAR_MS) / 2;
488         for (int i = 0; i < mInjector.contexts.size(); ++i) {
489             doReturn(true).when(mInjector.contexts.keyAt(i)).isWithinExecutionGuaranteeTime();
490             doReturn(remainingTimeMs)
491                     .when(mInjector.contexts.keyAt(i)).getRemainingGuaranteedTimeMs(anyLong());
492         }
493 
494         final ArraySet<JobConcurrencyManager.ContextAssignment> changed = new ArraySet<>();
495         final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
496         final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
497         final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
498         final JobConcurrencyManager.AssignmentInfo assignmentInfo =
499                 new JobConcurrencyManager.AssignmentInfo();
500 
501         mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
502                 idle, preferredUidOnly, stoppable, assignmentInfo);
503         assertEquals(remainingTimeMs, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
504         assertEquals(JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT, preferredUidOnly.size());
505 
506         mJobConcurrencyManager
507                 .determineAssignmentsLocked(changed, idle, preferredUidOnly, stoppable,
508                         assignmentInfo);
509 
510         assertEquals(JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT, preferredUidOnly.size());
511         for (int i = changed.size() - 1; i >= 0; --i) {
512             jobs.remove(changed.valueAt(i).newJob);
513         }
514         // There are already UIJs running, and wait time is too long for regular jobs, so
515         // only 1 EJ removed from the pending list.
516         assertEquals(3 * JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT - 1, jobs.size());
517         assertEquals(1, changed.size());
518         JobStatus assignedJob = changed.valueAt(0).newJob;
519         assertTrue(assignedJob.shouldTreatAsExpeditedJob());
520     }
521 
522     @Test
testDetermineAssignments_allPreferredUidOnly_longTimeLeft()523     public void testDetermineAssignments_allPreferredUidOnly_longTimeLeft() throws Exception {
524         mConfigBuilder.setBoolean(JobConcurrencyManager.KEY_ENABLE_MAX_WAIT_TIME_BYPASS, true);
525         setConcurrencyConfig(JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT,
526                 new TypeConfig(WORK_TYPE_BG, 0, JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT));
527         final ArraySet<JobStatus> jobs = new ArraySet<>();
528         for (int i = 0; i < JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT * 2; ++i) {
529             final int uid = mDefaultUserId * UserHandle.PER_USER_RANGE + i;
530             final String sourcePkgName = "com.source.package." + UserHandle.getAppId(uid);
531             setPackageUid(sourcePkgName, uid);
532             final JobStatus job = createJob(uid, sourcePkgName);
533             spyOn(job);
534             doReturn(i % 3 == 0).when(job).shouldTreatAsUserInitiatedJob();
535             doReturn(i % 3 == 1).when(job).shouldTreatAsExpeditedJob();
536             if (i < JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT) {
537                 mJobConcurrencyManager.addRunningJobForTesting(job);
538             } else {
539                 mPendingJobQueue.add(job);
540                 jobs.add(job);
541             }
542         }
543 
544         // Waiting time is longer than even the regular job waiting time, so we should
545         // create an extra context for an EJ, and potentially one for a regular job.
546         final long remainingTimeMs = 2 * JobConcurrencyManager.DEFAULT_MAX_WAIT_REGULAR_MS;
547         for (int i = 0; i < mInjector.contexts.size(); ++i) {
548             doReturn(true).when(mInjector.contexts.keyAt(i)).isWithinExecutionGuaranteeTime();
549             doReturn(remainingTimeMs)
550                     .when(mInjector.contexts.keyAt(i)).getRemainingGuaranteedTimeMs(anyLong());
551         }
552 
553         final ArraySet<JobConcurrencyManager.ContextAssignment> changed = new ArraySet<>();
554         final ArraySet<JobConcurrencyManager.ContextAssignment> idle = new ArraySet<>();
555         final List<JobConcurrencyManager.ContextAssignment> preferredUidOnly = new ArrayList<>();
556         final List<JobConcurrencyManager.ContextAssignment> stoppable = new ArrayList<>();
557         final JobConcurrencyManager.AssignmentInfo assignmentInfo =
558                 new JobConcurrencyManager.AssignmentInfo();
559 
560         mJobConcurrencyManager.prepareForAssignmentDeterminationLocked(
561                 idle, preferredUidOnly, stoppable, assignmentInfo);
562         assertEquals(remainingTimeMs, assignmentInfo.minPreferredUidOnlyWaitingTimeMs);
563         assertEquals(JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT, preferredUidOnly.size());
564 
565         mJobConcurrencyManager
566                 .determineAssignmentsLocked(changed, idle, preferredUidOnly, stoppable,
567                         assignmentInfo);
568 
569         assertEquals(JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT, preferredUidOnly.size());
570         // Depending on iteration order, we may create 1-3 contexts.
571         final long numAssignedJobs = changed.size();
572         assertTrue(numAssignedJobs > 0);
573         assertTrue(numAssignedJobs <= 3);
574         int numUi = 0, numEj = 0, numReg = 0;
575         for (int i = 0; i < numAssignedJobs; ++i) {
576             JobStatus assignedJob = changed.valueAt(i).newJob;
577             jobs.remove(assignedJob);
578             if (assignedJob.shouldTreatAsUserInitiatedJob()) {
579                 numUi++;
580             } else if (assignedJob.shouldTreatAsExpeditedJob()) {
581                 numEj++;
582             } else {
583                 numReg++;
584             }
585         }
586         assertEquals(numAssignedJobs,
587                 JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT - jobs.size());
588         if (numReg > 0) {
589             assertEquals(1, numReg);
590             assertEquals(1, numEj);
591             assertEquals(1, numUi);
592             assertEquals(3, numAssignedJobs);
593         } else {
594             if (numEj > 0) {
595                 assertEquals(1, numEj);
596             }
597             // If the manager looks at an EJ before a UIJ, the waiting time for the UIJ will drop
598             // to 3 minutes and be below the threshold to create a new context.
599             if (numUi > 0) {
600                 assertEquals(1, numUi);
601             }
602             assertEquals(numEj + numUi, numAssignedJobs);
603         }
604     }
605 
606     @Test
testHasImmediacyPrivilege()607     public void testHasImmediacyPrivilege() {
608         final int uid = mDefaultUserId * UserHandle.PER_USER_RANGE;
609         JobStatus job = createJob(uid, 0);
610         spyOn(job);
611         doReturn(BackgroundStartPrivileges.NONE)
612                 .when(mActivityManagerInternal).getBackgroundStartPrivileges(uid);
613 
614         assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
615 
616         doReturn(false).when(job).shouldTreatAsExpeditedJob();
617         doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
618         job.lastEvaluatedBias = JobInfo.BIAS_TOP_APP;
619         assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
620 
621         doReturn(true).when(job).shouldTreatAsExpeditedJob();
622         doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
623         job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
624         assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
625 
626         doReturn(false).when(job).shouldTreatAsExpeditedJob();
627         doReturn(true).when(job).shouldTreatAsUserInitiatedJob();
628         job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
629         assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
630 
631         doReturn(false).when(job).shouldTreatAsExpeditedJob();
632         doReturn(true).when(job).shouldTreatAsUserInitiatedJob();
633         job.lastEvaluatedBias = JobInfo.BIAS_TOP_APP;
634         assertTrue(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
635 
636         doReturn(true).when(job).shouldTreatAsExpeditedJob();
637         doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
638         job.lastEvaluatedBias = JobInfo.BIAS_TOP_APP;
639         assertTrue(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job,
640                 new SparseIntArray()));
641 
642         doReturn(BackgroundStartPrivileges.ALLOW_FGS)
643                 .when(mActivityManagerInternal).getBackgroundStartPrivileges(uid);
644 
645         doReturn(false).when(job).shouldTreatAsExpeditedJob();
646         doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
647         job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
648         assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
649 
650         doReturn(true).when(job).shouldTreatAsExpeditedJob();
651         doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
652         job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
653         assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
654 
655         doReturn(false).when(job).shouldTreatAsExpeditedJob();
656         doReturn(true).when(job).shouldTreatAsUserInitiatedJob();
657         job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
658         assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
659 
660         doReturn(BackgroundStartPrivileges.ALLOW_BAL)
661                 .when(mActivityManagerInternal).getBackgroundStartPrivileges(uid);
662 
663         doReturn(false).when(job).shouldTreatAsExpeditedJob();
664         doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
665         job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
666         assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
667 
668         doReturn(true).when(job).shouldTreatAsExpeditedJob();
669         doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
670         job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
671         assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
672 
673         doReturn(false).when(job).shouldTreatAsExpeditedJob();
674         doReturn(true).when(job).shouldTreatAsUserInitiatedJob();
675         job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
676         assertTrue(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
677     }
678 
679     @Test
testIsPkgConcurrencyLimited_top()680     public void testIsPkgConcurrencyLimited_top() {
681         final JobStatus topJob = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, 0);
682         topJob.lastEvaluatedBias = JobInfo.BIAS_TOP_APP;
683 
684         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob));
685 
686         // Pending jobs shouldn't affect TOP job's status.
687         for (int i = 1; i <= JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT; ++i) {
688             final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i);
689             mPendingJobQueue.add(job);
690         }
691         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob));
692 
693         // Already running jobs shouldn't affect TOP job's status.
694         for (int i = 1; i <= JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT; ++i) {
695             final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, i);
696             mJobConcurrencyManager.addRunningJobForTesting(job);
697         }
698         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob));
699 
700         // Currently running or staged jobs shouldn't affect TOP job's status.
701         final JobConcurrencyManager.PackageStats packageStats =
702                 mJobConcurrencyManager.getPackageStatsForTesting(
703                         topJob.getSourceUserId(), topJob.getSourcePackageName());
704         packageStats.numStagedEj = mJobConcurrencyManager.getPackageConcurrencyLimitEj();
705         packageStats.numStagedRegular = mJobConcurrencyManager.getPackageConcurrencyLimitRegular();
706         packageStats.numRunningEj = 0;
707         packageStats.numRunningRegular = 0;
708         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob));
709 
710         packageStats.numStagedEj = 0;
711         packageStats.numStagedRegular = 0;
712         packageStats.numRunningEj = mJobConcurrencyManager.getPackageConcurrencyLimitEj();
713         packageStats.numRunningRegular = mJobConcurrencyManager.getPackageConcurrencyLimitRegular();
714         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob));
715     }
716 
717     @Test
testIsPkgConcurrencyLimited_belowTotalLimit()718     public void testIsPkgConcurrencyLimited_belowTotalLimit() throws Exception {
719         final JobStatus testJob = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE);
720 
721         setConcurrencyConfig(8);
722 
723         // Pending jobs below limit shouldn't affect job's status.
724         for (int i = 0; i < 5; ++i) {
725             final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i);
726             mPendingJobQueue.add(job);
727         }
728         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testJob));
729 
730         mPendingJobQueue.clear();
731 
732         // Already running jobs below limit shouldn't affect job's status.
733         for (int i = 0; i < 4; ++i) {
734             final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i);
735             mJobConcurrencyManager.addRunningJobForTesting(job);
736         }
737         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testJob));
738 
739         // Mix of pending + running.
740         for (int i = 4; i < 8; ++i) {
741             final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i);
742             mPendingJobQueue.add(job);
743         }
744         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testJob));
745     }
746 
747     @Test
testIsPkgConcurrencyLimited()748     public void testIsPkgConcurrencyLimited() throws Exception {
749         final JobStatus testReg = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, 0);
750         final JobStatus testEj = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, 1);
751         spyOn(testEj);
752         doReturn(true).when(testEj).shouldTreatAsExpeditedJob();
753 
754         setConcurrencyConfig(JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT);
755 
756         for (int i = 0; i < JobConcurrencyManager.DEFAULT_CONCURRENCY_LIMIT; ++i) {
757             final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i, i + 1);
758             mPendingJobQueue.add(job);
759         }
760 
761         // App has no running jobs, so shouldn't be limited.
762         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
763         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
764 
765         // Already running jobs shouldn't affect TOP job's status.
766         final JobConcurrencyManager.PackageStats packageStats =
767                 mJobConcurrencyManager.getPackageStatsForTesting(
768                         testReg.getSourceUserId(), testReg.getSourcePackageName());
769 
770         // Only running counts
771         packageStats.numStagedEj = 0;
772         packageStats.numStagedRegular = 0;
773         packageStats.numRunningEj = 4;
774         packageStats.numRunningRegular = 4;
775 
776         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
777         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
778         updateDeviceConfig();
779         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
780         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
781 
782         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
783         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 4);
784         updateDeviceConfig();
785         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
786         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
787 
788         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
789         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 3);
790         updateDeviceConfig();
791         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
792         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
793 
794         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 4);
795         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
796         updateDeviceConfig();
797         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
798         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
799 
800         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 3);
801         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
802         updateDeviceConfig();
803         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
804         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
805 
806         // Only staged counts
807         packageStats.numStagedEj = 4;
808         packageStats.numStagedRegular = 4;
809         packageStats.numRunningEj = 0;
810         packageStats.numRunningRegular = 0;
811 
812         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
813         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
814         updateDeviceConfig();
815         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
816         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
817 
818         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
819         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 4);
820         updateDeviceConfig();
821         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
822         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
823 
824         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
825         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 3);
826         updateDeviceConfig();
827         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
828         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
829 
830         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 4);
831         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
832         updateDeviceConfig();
833         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
834         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
835 
836         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 3);
837         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
838         updateDeviceConfig();
839         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
840         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
841 
842         // Running + staged counts
843         packageStats.numStagedEj = 2;
844         packageStats.numStagedRegular = 1;
845         packageStats.numRunningEj = 2;
846         packageStats.numRunningRegular = 3;
847 
848         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
849         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
850         updateDeviceConfig();
851         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
852         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
853 
854         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
855         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 4);
856         updateDeviceConfig();
857         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
858         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
859 
860         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
861         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 3);
862         updateDeviceConfig();
863         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
864         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
865 
866         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 4);
867         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
868         updateDeviceConfig();
869         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
870         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
871 
872         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 3);
873         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
874         updateDeviceConfig();
875         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
876         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
877     }
878 
879     @Test
testShouldRunAsFgUserJob_currentUser()880     public void testShouldRunAsFgUserJob_currentUser() {
881         assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
882                 createJob(createCurrentUser(false))));
883     }
884 
885     @Test
testShouldRunAsFgUserJob_currentProfile()886     public void testShouldRunAsFgUserJob_currentProfile() {
887         assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
888                 createJob(createCurrentUser(true))));
889     }
890 
891     @Test
testShouldRunAsFgUserJob_primaryUser()892     public void testShouldRunAsFgUserJob_primaryUser() {
893         assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
894                 createJob(createPrimaryUser(false))));
895     }
896 
897     @Test
testShouldRunAsFgUserJob_primaryProfile()898     public void testShouldRunAsFgUserJob_primaryProfile() {
899         assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
900                 createJob(createPrimaryUser(true))));
901     }
902 
903     @Test
testShouldRunAsFgUserJob_UnexpiredUser()904     public void testShouldRunAsFgUserJob_UnexpiredUser() {
905         assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
906                 createJob(createUnexpiredUser(false))));
907     }
908 
909     @Test
testShouldRunAsFgUserJob_UnexpiredProfile()910     public void testShouldRunAsFgUserJob_UnexpiredProfile() {
911         assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
912                 createJob(createUnexpiredUser(true))));
913     }
914 
915     @Test
testShouldRunAsFgUserJob_restrictedUser()916     public void testShouldRunAsFgUserJob_restrictedUser() {
917         assertFalse(mJobConcurrencyManager.shouldRunAsFgUserJob(
918                 createJob(createRestrictedUser(false))));
919     }
920 
921     @Test
testShouldRunAsFgUserJob_restrictedProfile()922     public void testShouldRunAsFgUserJob_restrictedProfile() {
923         assertFalse(mJobConcurrencyManager.shouldRunAsFgUserJob(
924                 createJob(createRestrictedUser(true))));
925     }
926 
createCurrentUser(boolean isProfile)927     private UserInfo createCurrentUser(boolean isProfile) {
928         final UserInfo ui = createNewUser();
929         doReturn(ui.id).when(mActivityManagerInternal).getCurrentUserId();
930         return isProfile ? createNewProfile(ui) : ui;
931     }
932 
createPrimaryUser(boolean isProfile)933     private UserInfo createPrimaryUser(boolean isProfile) {
934         final UserInfo ui = createNewUser();
935         doReturn(true).when(ui).isPrimary();
936         return isProfile ? createNewProfile(ui) : ui;
937     }
938 
createUnexpiredUser(boolean isProfile)939     private UserInfo createUnexpiredUser(boolean isProfile) {
940         final UserInfo ui = createNewUser();
941         doReturn(true).when(mGracePeriodObserver).isWithinGracePeriodForUser(ui.id);
942         return isProfile ? createNewProfile(ui) : ui;
943     }
944 
createRestrictedUser(boolean isProfile)945     private UserInfo createRestrictedUser(boolean isProfile) {
946         final UserInfo ui = createNewUser();
947         doReturn(UNAVAILABLE_USER).when(mActivityManagerInternal).getCurrentUserId();
948         doReturn(false).when(ui).isPrimary();
949         doReturn(false).when(mGracePeriodObserver).isWithinGracePeriodForUser(ui.id);
950         return isProfile ? createNewProfile(ui) : ui;
951     }
952 
createNewProfile(UserInfo parent)953     private UserInfo createNewProfile(UserInfo parent) {
954         final UserInfo ui = createNewUser();
955         parent.profileGroupId = parent.id;
956         ui.profileGroupId = parent.id;
957         doReturn(true).when(ui).isProfile();
958         return ui;
959     }
960 
createNewUser()961     private UserInfo createNewUser() {
962         final UserInfo ui = mock(UserInfo.class);
963         ui.id = mNextUserId++;
964         doReturn(ui).when(mUserManagerInternal).getUserInfo(ui.id);
965         ui.profileGroupId = UserInfo.NO_PROFILE_GROUP_ID;
966         return ui;
967     }
968 
createJob(UserInfo userInfo)969     private static JobStatus createJob(UserInfo userInfo) {
970         return createJob(userInfo.id * UserHandle.PER_USER_RANGE);
971     }
972 
createJob(int uid)973     private static JobStatus createJob(int uid) {
974         return createJob(uid, 1, null);
975     }
976 
createJob(int uid, String sourcePackageName)977     private static JobStatus createJob(int uid, String sourcePackageName) {
978         return createJob(uid, 1, sourcePackageName);
979     }
980 
createJob(int uid, int jobId)981     private static JobStatus createJob(int uid, int jobId) {
982         return createJob(uid, jobId, null);
983     }
984 
createJob(int uid, int jobId, @Nullable String sourcePackageName)985     private static JobStatus createJob(int uid, int jobId, @Nullable String sourcePackageName) {
986         return JobStatus.createFromJobInfo(
987                 new JobInfo.Builder(jobId, new ComponentName("foo", "bar")).build(), uid,
988                 sourcePackageName, UserHandle.getUserId(uid), "JobConcurrencyManagerTest", null);
989     }
990 
991     private static final class TypeConfig {
992         public final String workTypeString;
993         public final int min;
994         public final int max;
995 
TypeConfig(@obConcurrencyManager.WorkType int workType, int min, int max)996         private TypeConfig(@JobConcurrencyManager.WorkType int workType, int min, int max) {
997             switch (workType) {
998                 case WORK_TYPE_TOP:
999                     workTypeString = "top";
1000                     break;
1001                 case WORK_TYPE_FGS:
1002                     workTypeString = "fgs";
1003                     break;
1004                 case WORK_TYPE_UI:
1005                     workTypeString = "ui";
1006                     break;
1007                 case WORK_TYPE_EJ:
1008                     workTypeString = "ej";
1009                     break;
1010                 case WORK_TYPE_BG:
1011                     workTypeString = "bg";
1012                     break;
1013                 case WORK_TYPE_BGUSER:
1014                     workTypeString = "bguser";
1015                     break;
1016                 case WORK_TYPE_BGUSER_IMPORTANT:
1017                     workTypeString = "bguser_important";
1018                     break;
1019                 case WORK_TYPE_NONE:
1020                 default:
1021                     throw new IllegalArgumentException("invalid work type: " + workType);
1022             }
1023             this.min = min;
1024             this.max = max;
1025         }
1026     }
1027 
setConcurrencyConfig(int total, TypeConfig... typeConfigs)1028     private void setConcurrencyConfig(int total, TypeConfig... typeConfigs) throws Exception {
1029         // Set the values for all memory states so we don't have to worry about memory on the device
1030         // during testing.
1031         final String[] identifiers = {
1032                 "screen_on_normal", "screen_on_moderate", "screen_on_low", "screen_on_critical",
1033                 "screen_off_normal", "screen_off_moderate", "screen_off_low", "screen_off_critical"
1034         };
1035         for (String identifier : identifiers) {
1036             mConfigBuilder
1037                     .setInt(WorkTypeConfig.KEY_PREFIX_MAX_TOTAL + identifier, total);
1038             for (TypeConfig config : typeConfigs) {
1039                 mConfigBuilder.setFloat(
1040                         WorkTypeConfig.KEY_PREFIX_MAX_RATIO + config.workTypeString + "_"
1041                                 + identifier,
1042                         (float) config.max / total);
1043                 mConfigBuilder.setFloat(
1044                         WorkTypeConfig.KEY_PREFIX_MIN_RATIO + config.workTypeString + "_"
1045                                 + identifier,
1046                         (float) config.min / total);
1047             }
1048         }
1049         updateDeviceConfig();
1050     }
1051 
setPackageUid(final String pkgName, final int uid)1052     private void setPackageUid(final String pkgName, final int uid) throws Exception {
1053         doReturn(uid).when(mIPackageManager)
1054                 .getPackageUid(eq(pkgName), anyLong(), eq(UserHandle.getUserId(uid)));
1055     }
1056 
updateDeviceConfig()1057     private void updateDeviceConfig() throws Exception {
1058         mJobConcurrencyManager.updateConfigLocked();
1059     }
1060 
resetConfig()1061     private void resetConfig() throws Exception {
1062         mConfigBuilder = new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
1063         updateDeviceConfig();
1064     }
1065 }
1066