• Home
  • 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.spyOn;
20 import static com.android.server.job.JobConcurrencyManager.KEY_PKG_CONCURRENCY_LIMIT_EJ;
21 import static com.android.server.job.JobConcurrencyManager.KEY_PKG_CONCURRENCY_LIMIT_REGULAR;
22 
23 import static junit.framework.Assert.assertFalse;
24 import static junit.framework.Assert.assertTrue;
25 
26 import static org.mockito.Mockito.doReturn;
27 import static org.mockito.Mockito.mock;
28 import static org.mockito.Mockito.when;
29 
30 import android.app.ActivityManagerInternal;
31 import android.app.job.JobInfo;
32 import android.content.ComponentName;
33 import android.content.Context;
34 import android.content.pm.UserInfo;
35 import android.content.res.Resources;
36 import android.os.UserHandle;
37 import android.provider.DeviceConfig;
38 
39 import androidx.test.filters.SmallTest;
40 import androidx.test.runner.AndroidJUnit4;
41 
42 import com.android.internal.R;
43 import com.android.server.LocalServices;
44 import com.android.server.job.JobConcurrencyManager.GracePeriodObserver;
45 import com.android.server.job.JobConcurrencyManager.WorkTypeConfig;
46 import com.android.server.job.controllers.JobStatus;
47 import com.android.server.pm.UserManagerInternal;
48 
49 import org.junit.After;
50 import org.junit.AfterClass;
51 import org.junit.Before;
52 import org.junit.BeforeClass;
53 import org.junit.Test;
54 import org.junit.runner.RunWith;
55 
56 @RunWith(AndroidJUnit4.class)
57 @SmallTest
58 public final class JobConcurrencyManagerTest {
59     private static final int UNAVAILABLE_USER = 0;
60     private JobConcurrencyManager mJobConcurrencyManager;
61     private UserManagerInternal mUserManagerInternal;
62     private ActivityManagerInternal mActivityManagerInternal;
63     private int mNextUserId;
64     private int mDefaultUserId;
65     private GracePeriodObserver mGracePeriodObserver;
66     private Context mContext;
67     private Resources mResources;
68     private PendingJobQueue mPendingJobQueue;
69     private DeviceConfig.Properties.Builder mConfigBuilder;
70 
71     @BeforeClass
setUpOnce()72     public static void setUpOnce() {
73         LocalServices.addService(UserManagerInternal.class, mock(UserManagerInternal.class));
74         LocalServices.addService(
75                 ActivityManagerInternal.class, mock(ActivityManagerInternal.class));
76     }
77 
78     @AfterClass
tearDownOnce()79     public static void tearDownOnce() {
80         LocalServices.removeServiceForTest(UserManagerInternal.class);
81         LocalServices.removeServiceForTest(ActivityManagerInternal.class);
82     }
83 
84     @Before
setUp()85     public void setUp() {
86         final JobSchedulerService jobSchedulerService = mock(JobSchedulerService.class);
87         mContext = mock(Context.class);
88         mResources = mock(Resources.class);
89         doReturn(true).when(mResources).getBoolean(
90                 R.bool.config_jobSchedulerRestrictBackgroundUser);
91         when(mContext.getResources()).thenReturn(mResources);
92         doReturn(mContext).when(jobSchedulerService).getTestableContext();
93         mConfigBuilder = new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
94         mPendingJobQueue = new PendingJobQueue();
95         doReturn(mPendingJobQueue).when(jobSchedulerService).getPendingJobQueue();
96         mJobConcurrencyManager = new JobConcurrencyManager(jobSchedulerService);
97         mGracePeriodObserver = mock(GracePeriodObserver.class);
98         mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
99         mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
100         mDefaultUserId = mNextUserId;
101         createCurrentUser(true);
102         mNextUserId = 10;
103         mJobConcurrencyManager.mGracePeriodObserver = mGracePeriodObserver;
104     }
105 
106     @After
tearDown()107     public void tearDown() throws Exception {
108         resetConfig();
109     }
110 
111     @Test
testIsPkgConcurrencyLimited_top()112     public void testIsPkgConcurrencyLimited_top() {
113         final JobStatus topJob = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, 0);
114         topJob.lastEvaluatedBias = JobInfo.BIAS_TOP_APP;
115 
116         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob));
117 
118         // Pending jobs shouldn't affect TOP job's status.
119         for (int i = 1; i <= JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT; ++i) {
120             final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i);
121             mPendingJobQueue.add(job);
122         }
123         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob));
124 
125         // Already running jobs shouldn't affect TOP job's status.
126         for (int i = 1; i <= JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT; ++i) {
127             final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, i);
128             mJobConcurrencyManager.addRunningJobForTesting(job);
129         }
130         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob));
131 
132         // Currently running or staged jobs shouldn't affect TOP job's status.
133         final JobConcurrencyManager.PackageStats packageStats =
134                 mJobConcurrencyManager.getPackageStatsForTesting(
135                         topJob.getSourceUserId(), topJob.getSourcePackageName());
136         packageStats.numStagedEj = mJobConcurrencyManager.getPackageConcurrencyLimitEj();
137         packageStats.numStagedRegular = mJobConcurrencyManager.getPackageConcurrencyLimitRegular();
138         packageStats.numRunningEj = 0;
139         packageStats.numRunningRegular = 0;
140         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob));
141 
142         packageStats.numStagedEj = 0;
143         packageStats.numStagedRegular = 0;
144         packageStats.numRunningEj = mJobConcurrencyManager.getPackageConcurrencyLimitEj();
145         packageStats.numRunningRegular = mJobConcurrencyManager.getPackageConcurrencyLimitRegular();
146         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(topJob));
147     }
148 
149     @Test
testIsPkgConcurrencyLimited_belowTotalLimit()150     public void testIsPkgConcurrencyLimited_belowTotalLimit() throws Exception {
151         final JobStatus testJob = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE);
152 
153         setConcurrencyConfig(8);
154 
155         // Pending jobs below limit shouldn't affect job's status.
156         for (int i = 0; i < 5; ++i) {
157             final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i);
158             mPendingJobQueue.add(job);
159         }
160         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testJob));
161 
162         mPendingJobQueue.clear();
163 
164         // Already running jobs below limit shouldn't affect job's status.
165         for (int i = 0; i < 4; ++i) {
166             final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i);
167             mJobConcurrencyManager.addRunningJobForTesting(job);
168         }
169         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testJob));
170 
171         // Mix of pending + running.
172         for (int i = 4; i < 8; ++i) {
173             final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i);
174             mPendingJobQueue.add(job);
175         }
176         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testJob));
177     }
178 
179     @Test
testIsPkgConcurrencyLimited()180     public void testIsPkgConcurrencyLimited() throws Exception {
181         final JobStatus testReg = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, 0);
182         final JobStatus testEj = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, 1);
183         spyOn(testEj);
184         doReturn(true).when(testEj).shouldTreatAsExpeditedJob();
185 
186         setConcurrencyConfig(JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT);
187 
188         for (int i = 0; i < JobConcurrencyManager.STANDARD_CONCURRENCY_LIMIT; ++i) {
189             final JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE + i, i + 1);
190             mPendingJobQueue.add(job);
191         }
192 
193         // App has no running jobs, so shouldn't be limited.
194         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
195         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
196 
197         // Already running jobs shouldn't affect TOP job's status.
198         final JobConcurrencyManager.PackageStats packageStats =
199                 mJobConcurrencyManager.getPackageStatsForTesting(
200                         testReg.getSourceUserId(), testReg.getSourcePackageName());
201 
202         // Only running counts
203         packageStats.numStagedEj = 0;
204         packageStats.numStagedRegular = 0;
205         packageStats.numRunningEj = 4;
206         packageStats.numRunningRegular = 4;
207 
208         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
209         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
210         updateDeviceConfig();
211         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
212         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
213 
214         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
215         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 4);
216         updateDeviceConfig();
217         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
218         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
219 
220         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
221         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 3);
222         updateDeviceConfig();
223         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
224         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
225 
226         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 4);
227         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
228         updateDeviceConfig();
229         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
230         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
231 
232         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 3);
233         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
234         updateDeviceConfig();
235         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
236         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
237 
238         // Only staged counts
239         packageStats.numStagedEj = 4;
240         packageStats.numStagedRegular = 4;
241         packageStats.numRunningEj = 0;
242         packageStats.numRunningRegular = 0;
243 
244         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
245         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
246         updateDeviceConfig();
247         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
248         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
249 
250         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
251         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 4);
252         updateDeviceConfig();
253         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
254         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
255 
256         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
257         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 3);
258         updateDeviceConfig();
259         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
260         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
261 
262         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 4);
263         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
264         updateDeviceConfig();
265         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
266         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
267 
268         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 3);
269         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
270         updateDeviceConfig();
271         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
272         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
273 
274         // Running + staged counts
275         packageStats.numStagedEj = 2;
276         packageStats.numStagedRegular = 1;
277         packageStats.numRunningEj = 2;
278         packageStats.numRunningRegular = 3;
279 
280         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
281         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
282         updateDeviceConfig();
283         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
284         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
285 
286         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
287         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 4);
288         updateDeviceConfig();
289         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
290         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
291 
292         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 8);
293         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 3);
294         updateDeviceConfig();
295         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
296         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
297 
298         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 4);
299         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
300         updateDeviceConfig();
301         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
302         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
303 
304         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, 3);
305         mConfigBuilder.setInt(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, 8);
306         updateDeviceConfig();
307         assertTrue(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testEj));
308         assertFalse(mJobConcurrencyManager.isPkgConcurrencyLimitedLocked(testReg));
309     }
310 
311     @Test
testShouldRunAsFgUserJob_currentUser()312     public void testShouldRunAsFgUserJob_currentUser() {
313         assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
314                 createJob(createCurrentUser(false))));
315     }
316 
317     @Test
testShouldRunAsFgUserJob_currentProfile()318     public void testShouldRunAsFgUserJob_currentProfile() {
319         assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
320                 createJob(createCurrentUser(true))));
321     }
322 
323     @Test
testShouldRunAsFgUserJob_primaryUser()324     public void testShouldRunAsFgUserJob_primaryUser() {
325         assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
326                 createJob(createPrimaryUser(false))));
327     }
328 
329     @Test
testShouldRunAsFgUserJob_primaryProfile()330     public void testShouldRunAsFgUserJob_primaryProfile() {
331         assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
332                 createJob(createPrimaryUser(true))));
333     }
334 
335     @Test
testShouldRunAsFgUserJob_UnexpiredUser()336     public void testShouldRunAsFgUserJob_UnexpiredUser() {
337         assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
338                 createJob(createUnexpiredUser(false))));
339     }
340 
341     @Test
testShouldRunAsFgUserJob_UnexpiredProfile()342     public void testShouldRunAsFgUserJob_UnexpiredProfile() {
343         assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
344                 createJob(createUnexpiredUser(true))));
345     }
346 
347     @Test
testShouldRunAsFgUserJob_restrictedUser()348     public void testShouldRunAsFgUserJob_restrictedUser() {
349         assertFalse(mJobConcurrencyManager.shouldRunAsFgUserJob(
350                 createJob(createRestrictedUser(false))));
351     }
352 
353     @Test
testShouldRunAsFgUserJob_restrictedProfile()354     public void testShouldRunAsFgUserJob_restrictedProfile() {
355         assertFalse(mJobConcurrencyManager.shouldRunAsFgUserJob(
356                 createJob(createRestrictedUser(true))));
357     }
358 
createCurrentUser(boolean isProfile)359     private UserInfo createCurrentUser(boolean isProfile) {
360         final UserInfo ui = createNewUser();
361         doReturn(ui.id).when(mActivityManagerInternal).getCurrentUserId();
362         return isProfile ? createNewProfile(ui) : ui;
363     }
364 
createPrimaryUser(boolean isProfile)365     private UserInfo createPrimaryUser(boolean isProfile) {
366         final UserInfo ui = createNewUser();
367         doReturn(true).when(ui).isPrimary();
368         return isProfile ? createNewProfile(ui) : ui;
369     }
370 
createUnexpiredUser(boolean isProfile)371     private UserInfo createUnexpiredUser(boolean isProfile) {
372         final UserInfo ui = createNewUser();
373         doReturn(true).when(mGracePeriodObserver).isWithinGracePeriodForUser(ui.id);
374         return isProfile ? createNewProfile(ui) : ui;
375     }
376 
createRestrictedUser(boolean isProfile)377     private UserInfo createRestrictedUser(boolean isProfile) {
378         final UserInfo ui = createNewUser();
379         doReturn(UNAVAILABLE_USER).when(mActivityManagerInternal).getCurrentUserId();
380         doReturn(false).when(ui).isPrimary();
381         doReturn(false).when(mGracePeriodObserver).isWithinGracePeriodForUser(ui.id);
382         return isProfile ? createNewProfile(ui) : ui;
383     }
384 
createNewProfile(UserInfo parent)385     private UserInfo createNewProfile(UserInfo parent) {
386         final UserInfo ui = createNewUser();
387         parent.profileGroupId = parent.id;
388         ui.profileGroupId = parent.id;
389         doReturn(true).when(ui).isProfile();
390         return ui;
391     }
392 
createNewUser()393     private UserInfo createNewUser() {
394         final UserInfo ui = mock(UserInfo.class);
395         ui.id = mNextUserId++;
396         doReturn(ui).when(mUserManagerInternal).getUserInfo(ui.id);
397         ui.profileGroupId = UserInfo.NO_PROFILE_GROUP_ID;
398         return ui;
399     }
400 
createJob(UserInfo userInfo)401     private static JobStatus createJob(UserInfo userInfo) {
402         return createJob(userInfo.id * UserHandle.PER_USER_RANGE);
403     }
404 
createJob(int uid)405     private static JobStatus createJob(int uid) {
406         return createJob(uid, 1);
407     }
408 
createJob(int uid, int jobId)409     private static JobStatus createJob(int uid, int jobId) {
410         return JobStatus.createFromJobInfo(
411                 new JobInfo.Builder(jobId, new ComponentName("foo", "bar")).build(), uid,
412                 null, UserHandle.getUserId(uid), "JobConcurrencyManagerTest");
413     }
414 
setConcurrencyConfig(int total)415     private void setConcurrencyConfig(int total) throws Exception {
416         // Set the values for all memory states so we don't have to worry about memory on the device
417         // during testing.
418         final String[] identifiers = {
419                 "screen_on_normal", "screen_on_moderate", "screen_on_low", "screen_on_critical",
420                 "screen_off_normal", "screen_off_moderate", "screen_off_low", "screen_off_critical"
421         };
422         for (String identifier : identifiers) {
423             mConfigBuilder
424                     .setInt(WorkTypeConfig.KEY_PREFIX_MAX_TOTAL + identifier, total);
425         }
426         updateDeviceConfig();
427     }
428 
updateDeviceConfig()429     private void updateDeviceConfig() throws Exception {
430         DeviceConfig.setProperties(mConfigBuilder.build());
431         mJobConcurrencyManager.updateConfigLocked();
432     }
433 
resetConfig()434     private void resetConfig() throws Exception {
435         mConfigBuilder = new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
436         updateDeviceConfig();
437     }
438 }
439