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