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