1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.job.controllers; 18 19 import static android.app.job.JobInfo.BIAS_FOREGROUND_SERVICE; 20 import static android.app.job.JobInfo.BIAS_TOP_APP; 21 import static android.app.job.JobInfo.NETWORK_TYPE_ANY; 22 import static android.app.job.JobInfo.NETWORK_TYPE_CELLULAR; 23 import static android.app.job.JobInfo.NETWORK_TYPE_NONE; 24 import static android.net.NetworkCapabilities.TRANSPORT_WIFI; 25 import static android.text.format.DateUtils.HOUR_IN_MILLIS; 26 import static android.text.format.DateUtils.MINUTE_IN_MILLIS; 27 28 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; 29 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; 30 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 31 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; 32 import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX; 33 import static com.android.server.job.JobSchedulerService.EXEMPTED_INDEX; 34 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; 35 import static com.android.server.job.controllers.FlexibilityController.FLEXIBLE_CONSTRAINTS; 36 import static com.android.server.job.controllers.FlexibilityController.FcConfig.DEFAULT_FALLBACK_FLEXIBILITY_DEADLINES; 37 import static com.android.server.job.controllers.FlexibilityController.FcConfig.DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS; 38 import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_APPLIED_CONSTRAINTS; 39 import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_DEADLINE_PROXIMITY_LIMIT; 40 import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_FALLBACK_FLEXIBILITY_DEADLINE; 41 import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_FALLBACK_FLEXIBILITY_DEADLINES; 42 import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_FALLBACK_FLEXIBILITY_DEADLINE_ADDITIONAL_SCORE_TIME_FACTORS; 43 import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_FALLBACK_FLEXIBILITY_DEADLINE_SCORES; 44 import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS; 45 import static com.android.server.job.controllers.FlexibilityController.SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS; 46 import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NOT_LOW; 47 import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING; 48 import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONNECTIVITY; 49 import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONTENT_TRIGGER; 50 import static com.android.server.job.controllers.JobStatus.CONSTRAINT_FLEXIBLE; 51 import static com.android.server.job.controllers.JobStatus.CONSTRAINT_IDLE; 52 import static com.android.server.job.controllers.JobStatus.NO_LATEST_RUNTIME; 53 54 import static org.junit.Assert.assertArrayEquals; 55 import static org.junit.Assert.assertEquals; 56 import static org.junit.Assert.assertFalse; 57 import static org.junit.Assert.assertTrue; 58 import static org.mockito.ArgumentMatchers.any; 59 import static org.mockito.ArgumentMatchers.anyInt; 60 import static org.mockito.ArgumentMatchers.anyLong; 61 import static org.mockito.ArgumentMatchers.anyString; 62 import static org.mockito.ArgumentMatchers.eq; 63 import static org.mockito.Mockito.atLeast; 64 import static org.mockito.Mockito.clearInvocations; 65 import static org.mockito.Mockito.mock; 66 import static org.mockito.Mockito.verify; 67 import static org.mockito.Mockito.when; 68 69 import android.annotation.Nullable; 70 import android.app.AlarmManager; 71 import android.app.AppGlobals; 72 import android.app.job.JobInfo; 73 import android.content.BroadcastReceiver; 74 import android.content.ComponentName; 75 import android.content.Context; 76 import android.content.Intent; 77 import android.content.pm.IPackageManager; 78 import android.content.pm.PackageManager; 79 import android.net.NetworkRequest; 80 import android.os.Looper; 81 import android.os.PowerManager; 82 import android.os.UserHandle; 83 import android.provider.DeviceConfig; 84 import android.telephony.TelephonyManager; 85 import android.telephony.UiccSlotMapping; 86 import android.util.ArraySet; 87 import android.util.EmptyArray; 88 import android.util.SparseArray; 89 90 import com.android.server.AppSchedulingModuleThread; 91 import com.android.server.DeviceIdleInternal; 92 import com.android.server.LocalServices; 93 import com.android.server.job.JobSchedulerService; 94 import com.android.server.job.JobStore; 95 96 import libcore.junit.util.compat.CoreCompatChangeRule; 97 98 import org.junit.After; 99 import org.junit.Before; 100 import org.junit.Test; 101 import org.mockito.ArgumentCaptor; 102 import org.mockito.ArgumentMatchers; 103 import org.mockito.Mock; 104 import org.mockito.MockitoSession; 105 import org.mockito.quality.Strictness; 106 import org.mockito.stubbing.Answer; 107 108 import java.time.Clock; 109 import java.time.Duration; 110 import java.time.Instant; 111 import java.time.ZoneOffset; 112 import java.util.ArrayList; 113 import java.util.Collection; 114 import java.util.Collections; 115 import java.util.List; 116 import java.util.concurrent.Executor; 117 118 public class FlexibilityControllerTest { 119 private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests"; 120 private static final int SOURCE_USER_ID = 0; 121 private static final long FROZEN_TIME = 100L; 122 123 private MockitoSession mMockingSession; 124 private BroadcastReceiver mBroadcastReceiver; 125 private final SparseArray<ArraySet<String>> mCarrierPrivilegedApps = new SparseArray<>(); 126 private final SparseArray<TelephonyManager.CarrierPrivilegesCallback> 127 mCarrierPrivilegedCallbacks = new SparseArray<>(); 128 private FlexibilityController mFlexibilityController; 129 private DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder; 130 private JobStore mJobStore; 131 private FlexibilityController.FcConfig mFcConfig; 132 private int mSourceUid; 133 134 @Mock 135 private AlarmManager mAlarmManager; 136 @Mock 137 private Context mContext; 138 @Mock 139 private DeviceIdleInternal mDeviceIdleInternal; 140 @Mock 141 private JobSchedulerService mJobSchedulerService; 142 @Mock 143 private PrefetchController mPrefetchController; 144 @Mock 145 private TelephonyManager mTelephonyManager; 146 @Mock 147 private IPackageManager mIPackageManager; 148 @Mock 149 private PackageManager mPackageManager; 150 151 @Before setup()152 public void setup() throws Exception { 153 mMockingSession = mockitoSession() 154 .initMocks(this) 155 .strictness(Strictness.LENIENT) 156 .spyStatic(DeviceConfig.class) 157 .mockStatic(AppGlobals.class) 158 .mockStatic(LocalServices.class) 159 .startMocking(); 160 // Called in StateController constructor. 161 when(mJobSchedulerService.getTestableContext()).thenReturn(mContext); 162 when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService); 163 when(mJobSchedulerService.getConstants()).thenReturn( 164 mock(JobSchedulerService.Constants.class)); 165 // Called in FlexibilityController constructor. 166 when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); 167 when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager); 168 doNothing().when(mAlarmManager).setExact(anyInt(), anyLong(), anyString(), any(), any()); 169 when(mContext.getPackageManager()).thenReturn(mPackageManager); 170 when(mPackageManager.hasSystemFeature( 171 PackageManager.FEATURE_AUTOMOTIVE)).thenReturn(false); 172 when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_EMBEDDED)).thenReturn(false); 173 doReturn(mDeviceIdleInternal) 174 .when(() -> LocalServices.getService(DeviceIdleInternal.class)); 175 // Used in FlexibilityController.FcConstants. 176 doAnswer((Answer<Void>) invocationOnMock -> null) 177 .when(() -> DeviceConfig.addOnPropertiesChangedListener( 178 anyString(), any(Executor.class), 179 any(DeviceConfig.OnPropertiesChangedListener.class))); 180 mDeviceConfigPropertiesBuilder = 181 new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER); 182 doAnswer( 183 (Answer<DeviceConfig.Properties>) invocationOnMock 184 -> mDeviceConfigPropertiesBuilder.build()) 185 .when(() -> DeviceConfig.getProperties( 186 eq(DeviceConfig.NAMESPACE_JOB_SCHEDULER), ArgumentMatchers.<String>any())); 187 // Used in FlexibilityController.SpecialAppTracker. 188 when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager); 189 when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)) 190 .thenReturn(true); 191 //used to get jobs by UID 192 mJobStore = JobStore.initAndGetForTesting(mContext, mContext.getFilesDir()); 193 doReturn(mJobStore).when(mJobSchedulerService).getJobStore(); 194 // Used in JobStatus. 195 doReturn(mIPackageManager).when(AppGlobals::getPackageManager); 196 // Freeze the clocks at a moment in time 197 JobSchedulerService.sSystemClock = 198 Clock.fixed(Instant.ofEpochMilli(FROZEN_TIME), ZoneOffset.UTC); 199 JobSchedulerService.sElapsedRealtimeClock = 200 Clock.fixed(Instant.ofEpochMilli(FROZEN_TIME), ZoneOffset.UTC); 201 // Set empty set of privileged apps. 202 setSimSlotMappings(null); 203 setPowerWhitelistExceptIdle(); 204 // Initialize real objects. 205 doReturn(Long.MAX_VALUE).when(mPrefetchController).getNextEstimatedLaunchTimeLocked(any()); 206 ArgumentCaptor<BroadcastReceiver> receiverCaptor = 207 ArgumentCaptor.forClass(BroadcastReceiver.class); 208 mFlexibilityController = new FlexibilityController(mJobSchedulerService, 209 mPrefetchController); 210 mFcConfig = mFlexibilityController.getFcConfig(); 211 212 mSourceUid = AppGlobals.getPackageManager().getPackageUid(SOURCE_PACKAGE, 0, 0); 213 214 setDeviceConfigString(KEY_PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS, 215 "500=50|60|70|80" 216 + ",400=50|60|70|80" 217 + ",300=50|60|70|80" 218 + ",200=50|60|70|80" 219 + ",100=50|60|70|80"); 220 setDeviceConfigLong(KEY_DEADLINE_PROXIMITY_LIMIT, 0L); 221 setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, FLEXIBLE_CONSTRAINTS); 222 waitForQuietModuleThread(); 223 224 verify(mContext).registerReceiver(receiverCaptor.capture(), 225 ArgumentMatchers.argThat(filter -> 226 filter.hasAction(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED))); 227 mBroadcastReceiver = receiverCaptor.getValue(); 228 } 229 230 @After teardown()231 public void teardown() { 232 if (mMockingSession != null) { 233 mMockingSession.finishMocking(); 234 } 235 } 236 advanceElapsedClock(long incrementMs)237 private void advanceElapsedClock(long incrementMs) { 238 JobSchedulerService.sElapsedRealtimeClock = Clock.offset( 239 sElapsedRealtimeClock, Duration.ofMillis(incrementMs)); 240 } 241 setDeviceConfigInt(String key, int val)242 private void setDeviceConfigInt(String key, int val) { 243 mDeviceConfigPropertiesBuilder.setInt(key, val); 244 updateDeviceConfig(key); 245 } 246 setDeviceConfigLong(String key, Long val)247 private void setDeviceConfigLong(String key, Long val) { 248 mDeviceConfigPropertiesBuilder.setLong(key, val); 249 updateDeviceConfig(key); 250 } 251 setDeviceConfigString(String key, String val)252 private void setDeviceConfigString(String key, String val) { 253 mDeviceConfigPropertiesBuilder.setString(key, val); 254 updateDeviceConfig(key); 255 } 256 updateDeviceConfig(String key)257 private void updateDeviceConfig(String key) { 258 synchronized (mFlexibilityController.mLock) { 259 mFlexibilityController.prepareForUpdatedConstantsLocked(); 260 mFcConfig.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key); 261 mFlexibilityController.onConstantsUpdatedLocked(); 262 } 263 } 264 waitForQuietModuleThread()265 private void waitForQuietModuleThread() { 266 assertTrue("Failed to wait for quiet module thread", 267 AppSchedulingModuleThread.getHandler().runWithScissors(() -> {}, 10_000L)); 268 } 269 createJob(int id)270 private static JobInfo.Builder createJob(int id) { 271 return new JobInfo.Builder(id, new ComponentName("foo", "bar")); 272 } 273 createJobStatus(String testTag, JobInfo.Builder job)274 private JobStatus createJobStatus(String testTag, JobInfo.Builder job) { 275 return createJobStatus(testTag, job, SOURCE_PACKAGE); 276 } 277 createJobStatus(String testTag, JobInfo.Builder job, String sourcePackage)278 private JobStatus createJobStatus(String testTag, JobInfo.Builder job, String sourcePackage) { 279 JobInfo jobInfo = job.build(); 280 JobStatus js = JobStatus.createFromJobInfo( 281 jobInfo, 1000, sourcePackage, SOURCE_USER_ID, "FCTest", testTag); 282 js.enqueueTime = FROZEN_TIME; 283 js.setStandbyBucket(ACTIVE_INDEX); 284 if (js.hasFlexibilityConstraint()) { 285 js.setNumAppliedFlexibleConstraints(Integer.bitCount( 286 mFlexibilityController.getRelevantAppliedConstraintsLocked(js))); 287 } 288 return js; 289 } 290 291 /** 292 * Tests that the there are equally many percents to drop constraints as there are constraints 293 */ 294 @Test testDefaultVariableValues()295 public void testDefaultVariableValues() { 296 SparseArray<int[]> defaultPercentsToDrop = 297 FlexibilityController.FcConfig.DEFAULT_PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS; 298 for (int i = 0; i < defaultPercentsToDrop.size(); ++i) { 299 assertEquals(Integer.bitCount(FLEXIBLE_CONSTRAINTS), 300 defaultPercentsToDrop.valueAt(i).length); 301 } 302 } 303 304 @Test testAppliedConstraints()305 public void testAppliedConstraints() { 306 setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, FLEXIBLE_CONSTRAINTS); 307 308 // Add connectivity to require 4 constraints 309 JobStatus connJs = createJobStatus("testAppliedConstraints", 310 createJob(0).setRequiredNetworkType(NETWORK_TYPE_ANY)); 311 JobStatus nonConnJs = createJobStatus("testAppliedConstraints", 312 createJob(1).setRequiredNetworkType(NETWORK_TYPE_NONE)); 313 314 mFlexibilityController.maybeStartTrackingJobLocked(connJs, null); 315 mFlexibilityController.maybeStartTrackingJobLocked(nonConnJs, null); 316 317 assertEquals(4, connJs.getNumAppliedFlexibleConstraints()); 318 assertEquals(3, nonConnJs.getNumAppliedFlexibleConstraints()); 319 320 mFlexibilityController.setConstraintSatisfied( 321 CONSTRAINT_BATTERY_NOT_LOW, true, 322 JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS); 323 mFlexibilityController.setConstraintSatisfied( 324 CONSTRAINT_CHARGING, false, 325 JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS); 326 mFlexibilityController.setConstraintSatisfied( 327 CONSTRAINT_IDLE, false, 328 JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS); 329 mFlexibilityController.setConstraintSatisfied( 330 CONSTRAINT_CONNECTIVITY, true, 331 JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS); 332 connJs.setTransportAffinitiesSatisfied(true); 333 334 synchronized (mFlexibilityController.mLock) { 335 assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs)); 336 assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs)); 337 } 338 339 setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, 340 CONSTRAINT_BATTERY_NOT_LOW | CONSTRAINT_CONNECTIVITY); 341 waitForQuietModuleThread(); 342 343 // Only battery-not-low (which is satisfied) applies to the non-connectivity job, so it 344 // should be able to run. 345 assertEquals(2, connJs.getNumAppliedFlexibleConstraints()); 346 assertEquals(1, nonConnJs.getNumAppliedFlexibleConstraints()); 347 synchronized (mFlexibilityController.mLock) { 348 assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs)); 349 assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs)); 350 } 351 352 setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, CONSTRAINT_BATTERY_NOT_LOW); 353 waitForQuietModuleThread(); 354 355 assertEquals(1, connJs.getNumAppliedFlexibleConstraints()); 356 assertEquals(1, nonConnJs.getNumAppliedFlexibleConstraints()); 357 synchronized (mFlexibilityController.mLock) { 358 assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs)); 359 assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs)); 360 } 361 362 setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, CONSTRAINT_CONNECTIVITY); 363 waitForQuietModuleThread(); 364 365 // No constraints apply to the non-connectivity job, so it should be able to run. 366 assertEquals(1, connJs.getNumAppliedFlexibleConstraints()); 367 assertEquals(0, nonConnJs.getNumAppliedFlexibleConstraints()); 368 synchronized (mFlexibilityController.mLock) { 369 assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs)); 370 assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs)); 371 } 372 373 setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, CONSTRAINT_CHARGING); 374 waitForQuietModuleThread(); 375 376 assertEquals(1, connJs.getNumAppliedFlexibleConstraints()); 377 assertEquals(1, nonConnJs.getNumAppliedFlexibleConstraints()); 378 synchronized (mFlexibilityController.mLock) { 379 assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs)); 380 assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs)); 381 } 382 383 setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, 0); 384 waitForQuietModuleThread(); 385 386 // No constraints apply, so they should be able to run. 387 assertEquals(0, connJs.getNumAppliedFlexibleConstraints()); 388 assertEquals(0, nonConnJs.getNumAppliedFlexibleConstraints()); 389 synchronized (mFlexibilityController.mLock) { 390 assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs)); 391 assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs)); 392 } 393 394 // Invalid constraint to apply. 395 setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, CONSTRAINT_CONTENT_TRIGGER); 396 waitForQuietModuleThread(); 397 398 // No constraints apply, so they should be able to run. 399 assertEquals(0, connJs.getNumAppliedFlexibleConstraints()); 400 assertEquals(0, nonConnJs.getNumAppliedFlexibleConstraints()); 401 synchronized (mFlexibilityController.mLock) { 402 assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs)); 403 assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs)); 404 } 405 } 406 407 @Test testOnConstantsUpdated_AppliedConstraints()408 public void testOnConstantsUpdated_AppliedConstraints() { 409 JobStatus js = createJobStatus("testDefaultFlexibilityConfig", createJob(0)); 410 setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, 0); 411 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(js)); 412 setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, FLEXIBLE_CONSTRAINTS); 413 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(js)); 414 } 415 416 @Test testOnConstantsUpdated_DeadlineProximity()417 public void testOnConstantsUpdated_DeadlineProximity() { 418 JobStatus js = createJobStatus("testDeadlineProximityConfig", createJob(0)); 419 setDeviceConfigLong(KEY_DEADLINE_PROXIMITY_LIMIT, Long.MAX_VALUE); 420 mFlexibilityController.mFlexibilityAlarmQueue 421 .scheduleDropNumConstraintsAlarm(js, FROZEN_TIME); 422 assertEquals(0, js.getNumRequiredFlexibleConstraints()); 423 } 424 425 @Test testOnConstantsUpdated_FallbackDeadline()426 public void testOnConstantsUpdated_FallbackDeadline() { 427 JobStatus js = createJobStatus("testFallbackDeadlineConfig", createJob(0)); 428 final long nowElapsed = sElapsedRealtimeClock.millis(); 429 assertEquals(DEFAULT_FALLBACK_FLEXIBILITY_DEADLINES.get(JobInfo.PRIORITY_DEFAULT), 430 mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, 0L)); 431 setDeviceConfigLong(KEY_FALLBACK_FLEXIBILITY_DEADLINE, 123L); 432 setDeviceConfigString(KEY_FALLBACK_FLEXIBILITY_DEADLINES, 433 "500=500,400=400,300=300,200=200,100=100"); 434 assertEquals(300L, mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, 0L)); 435 } 436 437 @Test testOnConstantsUpdated_PercentsToDropConstraints()438 public void testOnConstantsUpdated_PercentsToDropConstraints() { 439 final long fallbackDuration = 12 * HOUR_IN_MILLIS; 440 JobInfo.Builder jb = createJob(0) 441 .setOverrideDeadline(HOUR_IN_MILLIS); 442 JobStatus js = createJobStatus("testPercentsToDropConstraintsConfig", jb); 443 // Even though the override deadline is 1 hour, the fallback duration is still used. 444 assertEquals(FROZEN_TIME + fallbackDuration / 10 * 5, 445 mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); 446 setDeviceConfigString(KEY_PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS, 447 "500=1|2|3|4" 448 + ",400=5|6|7|8" 449 + ",300=10|20|30|40" 450 + ",200=50|51|52|53" 451 + ",100=54|55|56|57"); 452 assertArrayEquals( 453 mFlexibilityController.mFcConfig.PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS 454 .get(JobInfo.PRIORITY_MAX), 455 new int[]{1, 2, 3, 4}); 456 assertArrayEquals( 457 mFlexibilityController.mFcConfig.PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS 458 .get(JobInfo.PRIORITY_HIGH), 459 new int[]{5, 6, 7, 8}); 460 assertArrayEquals( 461 mFlexibilityController.mFcConfig.PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS 462 .get(JobInfo.PRIORITY_DEFAULT), 463 new int[]{10, 20, 30, 40}); 464 assertArrayEquals( 465 mFlexibilityController.mFcConfig.PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS 466 .get(JobInfo.PRIORITY_LOW), 467 new int[]{50, 51, 52, 53}); 468 assertArrayEquals( 469 mFlexibilityController.mFcConfig.PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS 470 .get(JobInfo.PRIORITY_MIN), 471 new int[]{54, 55, 56, 57}); 472 assertEquals(FROZEN_TIME + fallbackDuration / 10, 473 mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); 474 js.setNumDroppedFlexibleConstraints(1); 475 assertEquals(FROZEN_TIME + fallbackDuration / 10 * 2, 476 mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); 477 js.setNumDroppedFlexibleConstraints(2); 478 assertEquals(FROZEN_TIME + fallbackDuration / 10 * 3, 479 mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); 480 } 481 482 @Test testOnConstantsUpdated_PercentsToDropConstraintsInvalidValues()483 public void testOnConstantsUpdated_PercentsToDropConstraintsInvalidValues() { 484 // No priority mapping 485 setDeviceConfigString(KEY_PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS, "10,20,30,40"); 486 final SparseArray<int[]> defaultPercentsToDrop = 487 FlexibilityController.FcConfig.DEFAULT_PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS; 488 final SparseArray<int[]> percentsToDrop = 489 mFlexibilityController.mFcConfig.PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS; 490 for (int i = 0; i < defaultPercentsToDrop.size(); ++i) { 491 assertArrayEquals(defaultPercentsToDrop.valueAt(i), percentsToDrop.valueAt(i)); 492 } 493 494 // Invalid priority-percentList string 495 setDeviceConfigString(KEY_PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS, 496 "500=10,20a,030,40" 497 + ",400=20|40|60|80" 498 + ",300=25|50|75|80" 499 + ",200=40|50|60|80" 500 + ",100=20|40|60|80"); 501 for (int i = 0; i < defaultPercentsToDrop.size(); ++i) { 502 assertArrayEquals(defaultPercentsToDrop.valueAt(i), percentsToDrop.valueAt(i)); 503 } 504 505 // Invalid percentList strings 506 setDeviceConfigString(KEY_PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS, 507 "500=10|20a|030|40" // Letters 508 + ",400=10|40" // Not enough 509 + ",300=.|50|_|80" // Characters 510 + ",200=50|40|10|40" // Out of order 511 + ",100=30|60|90|101"); // Over 100 512 for (int i = 0; i < defaultPercentsToDrop.size(); ++i) { 513 assertArrayEquals(defaultPercentsToDrop.valueAt(i), percentsToDrop.valueAt(i)); 514 } 515 516 // Only partially invalid 517 setDeviceConfigString(KEY_PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS, 518 "500=10|20a|030|40" // Letters 519 + ",400=10|40" // Not enough 520 + ",300=.|50|_|80" // Characters 521 + ",200=10|20|30|40" // Valid 522 + ",100=20|40|60|80"); // Valid 523 assertArrayEquals(defaultPercentsToDrop.get(JobInfo.PRIORITY_MAX), 524 percentsToDrop.get(JobInfo.PRIORITY_MAX)); 525 assertArrayEquals(defaultPercentsToDrop.get(JobInfo.PRIORITY_HIGH), 526 percentsToDrop.get(JobInfo.PRIORITY_HIGH)); 527 assertArrayEquals(defaultPercentsToDrop.get(JobInfo.PRIORITY_DEFAULT), 528 percentsToDrop.get(JobInfo.PRIORITY_DEFAULT)); 529 assertArrayEquals(new int[]{10, 20, 30, 40}, percentsToDrop.get(JobInfo.PRIORITY_LOW)); 530 assertArrayEquals(new int[]{20, 40, 60, 80}, percentsToDrop.get(JobInfo.PRIORITY_MIN)); 531 } 532 533 @Test testGetNextConstraintDropTimeElapsedLocked()534 public void testGetNextConstraintDropTimeElapsedLocked() { 535 final long fallbackDuration = 50 * HOUR_IN_MILLIS; 536 setDeviceConfigLong(KEY_FALLBACK_FLEXIBILITY_DEADLINE, 200 * HOUR_IN_MILLIS); 537 setDeviceConfigString(KEY_FALLBACK_FLEXIBILITY_DEADLINES, 538 "500=" + HOUR_IN_MILLIS 539 + ",400=" + 25 * HOUR_IN_MILLIS 540 + ",300=" + fallbackDuration 541 + ",200=" + 100 * HOUR_IN_MILLIS 542 + ",100=" + 200 * HOUR_IN_MILLIS); 543 544 long nextTimeToDropNumConstraints; 545 546 // no delay, deadline 547 JobInfo.Builder jb = createJob(0).setOverrideDeadline(HOUR_IN_MILLIS); 548 JobStatus js = createJobStatus("time", jb); 549 550 assertEquals(JobStatus.NO_EARLIEST_RUNTIME, js.getEarliestRunTime()); 551 assertEquals(HOUR_IN_MILLIS + FROZEN_TIME, js.getLatestRunTimeElapsed()); 552 assertEquals(FROZEN_TIME, js.enqueueTime); 553 554 nextTimeToDropNumConstraints = mFlexibilityController 555 .getNextConstraintDropTimeElapsedLocked(js); 556 assertEquals(FROZEN_TIME + fallbackDuration / 10 * 5, 557 nextTimeToDropNumConstraints); 558 js.setNumDroppedFlexibleConstraints(1); 559 nextTimeToDropNumConstraints = mFlexibilityController 560 .getNextConstraintDropTimeElapsedLocked(js); 561 assertEquals(FROZEN_TIME + fallbackDuration / 10 * 6, 562 nextTimeToDropNumConstraints); 563 js.setNumDroppedFlexibleConstraints(2); 564 nextTimeToDropNumConstraints = mFlexibilityController 565 .getNextConstraintDropTimeElapsedLocked(js); 566 assertEquals(FROZEN_TIME + fallbackDuration / 10 * 7, 567 nextTimeToDropNumConstraints); 568 569 // delay, no deadline 570 jb = createJob(0).setMinimumLatency(800000L); 571 js = createJobStatus("time", jb); 572 573 nextTimeToDropNumConstraints = mFlexibilityController 574 .getNextConstraintDropTimeElapsedLocked(js); 575 assertEquals(FROZEN_TIME + 800000L + (50 * HOUR_IN_MILLIS) / 2, 576 nextTimeToDropNumConstraints); 577 js.setNumDroppedFlexibleConstraints(1); 578 nextTimeToDropNumConstraints = mFlexibilityController 579 .getNextConstraintDropTimeElapsedLocked(js); 580 assertEquals(FROZEN_TIME + 800000L + (50 * HOUR_IN_MILLIS) * 6 / 10, 581 nextTimeToDropNumConstraints); 582 js.setNumDroppedFlexibleConstraints(2); 583 nextTimeToDropNumConstraints = mFlexibilityController 584 .getNextConstraintDropTimeElapsedLocked(js); 585 assertEquals(FROZEN_TIME + 800000L + (50 * HOUR_IN_MILLIS) * 7 / 10, 586 nextTimeToDropNumConstraints); 587 588 // no delay, no deadline 589 jb = createJob(0).setPriority(JobInfo.PRIORITY_LOW); 590 js = createJobStatus("time", jb); 591 592 nextTimeToDropNumConstraints = mFlexibilityController 593 .getNextConstraintDropTimeElapsedLocked(js); 594 assertEquals(FROZEN_TIME + (100 * HOUR_IN_MILLIS) / 2, nextTimeToDropNumConstraints); 595 js.setNumDroppedFlexibleConstraints(1); 596 nextTimeToDropNumConstraints = mFlexibilityController 597 .getNextConstraintDropTimeElapsedLocked(js); 598 assertEquals(FROZEN_TIME + (100 * HOUR_IN_MILLIS) * 6 / 10, nextTimeToDropNumConstraints); 599 js.setNumDroppedFlexibleConstraints(2); 600 nextTimeToDropNumConstraints = mFlexibilityController 601 .getNextConstraintDropTimeElapsedLocked(js); 602 assertEquals(FROZEN_TIME + (100 * HOUR_IN_MILLIS) * 7 / 10, nextTimeToDropNumConstraints); 603 604 // delay, deadline 605 jb = createJob(0) 606 .setOverrideDeadline(2 * HOUR_IN_MILLIS) 607 .setMinimumLatency(HOUR_IN_MILLIS); 608 js = createJobStatus("time", jb); 609 610 final long windowStart = FROZEN_TIME + HOUR_IN_MILLIS; 611 nextTimeToDropNumConstraints = mFlexibilityController 612 .getNextConstraintDropTimeElapsedLocked(js); 613 assertEquals(windowStart + fallbackDuration / 10 * 5, 614 nextTimeToDropNumConstraints); 615 js.setNumDroppedFlexibleConstraints(1); 616 nextTimeToDropNumConstraints = mFlexibilityController 617 .getNextConstraintDropTimeElapsedLocked(js); 618 assertEquals(windowStart + fallbackDuration / 10 * 6, 619 nextTimeToDropNumConstraints); 620 js.setNumDroppedFlexibleConstraints(2); 621 nextTimeToDropNumConstraints = mFlexibilityController 622 .getNextConstraintDropTimeElapsedLocked(js); 623 assertEquals(windowStart + fallbackDuration / 10 * 7, 624 nextTimeToDropNumConstraints); 625 } 626 627 @Test testCurPercent()628 public void testCurPercent() { 629 final long fallbackDuration = 10 * HOUR_IN_MILLIS; 630 setDeviceConfigString(KEY_FALLBACK_FLEXIBILITY_DEADLINES, "300=" + fallbackDuration); 631 long deadline = 100 * MINUTE_IN_MILLIS; 632 long nowElapsed = FROZEN_TIME; 633 JobInfo.Builder jb = createJob(0).setOverrideDeadline(deadline); 634 JobStatus js = createJobStatus("time", jb); 635 636 assertEquals(FROZEN_TIME, mFlexibilityController.getLifeCycleBeginningElapsedLocked(js)); 637 assertEquals(FROZEN_TIME + fallbackDuration, 638 mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, FROZEN_TIME)); 639 nowElapsed = FROZEN_TIME + 6 * HOUR_IN_MILLIS; 640 JobSchedulerService.sElapsedRealtimeClock = 641 Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); 642 assertEquals(60, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); 643 644 nowElapsed = FROZEN_TIME + 13 * HOUR_IN_MILLIS; 645 JobSchedulerService.sElapsedRealtimeClock = 646 Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); 647 assertEquals(100, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); 648 649 nowElapsed = FROZEN_TIME + 9 * HOUR_IN_MILLIS; 650 JobSchedulerService.sElapsedRealtimeClock = 651 Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); 652 assertEquals(90, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); 653 654 nowElapsed = FROZEN_TIME; 655 JobSchedulerService.sElapsedRealtimeClock = 656 Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); 657 long delay = HOUR_IN_MILLIS; 658 deadline = HOUR_IN_MILLIS + 100 * MINUTE_IN_MILLIS; 659 jb = createJob(0).setOverrideDeadline(deadline).setMinimumLatency(delay); 660 js = createJobStatus("time", jb); 661 662 assertEquals(FROZEN_TIME + delay, 663 mFlexibilityController.getLifeCycleBeginningElapsedLocked(js)); 664 assertEquals(FROZEN_TIME + delay + fallbackDuration, 665 mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, 666 FROZEN_TIME + delay)); 667 668 nowElapsed = FROZEN_TIME + delay + 6 * HOUR_IN_MILLIS; 669 JobSchedulerService.sElapsedRealtimeClock = 670 Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); 671 672 assertEquals(60, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); 673 674 nowElapsed = FROZEN_TIME + 13 * HOUR_IN_MILLIS; 675 JobSchedulerService.sElapsedRealtimeClock = 676 Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); 677 assertEquals(100, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); 678 679 nowElapsed = FROZEN_TIME + delay + 9 * HOUR_IN_MILLIS; 680 JobSchedulerService.sElapsedRealtimeClock = 681 Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); 682 assertEquals(90, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); 683 } 684 685 @Test testGetLifeCycleBeginningElapsedLocked_Periodic()686 public void testGetLifeCycleBeginningElapsedLocked_Periodic() { 687 // Periodic with lifecycle 688 JobInfo.Builder jbBasic = createJob(0).setPeriodic(HOUR_IN_MILLIS); 689 JobInfo.Builder jbFlex = createJob(0) 690 .setPeriodic(HOUR_IN_MILLIS, 20 * MINUTE_IN_MILLIS); 691 JobStatus jsBasic = 692 createJobStatus("testGetLifeCycleBeginningElapsedLocked_Periodic", jbBasic); 693 JobStatus jsFlex = 694 createJobStatus("testGetLifeCycleBeginningElapsedLocked_Periodic", jbFlex); 695 696 final long nowElapsed = JobSchedulerService.sElapsedRealtimeClock.millis(); 697 // Base case, no start adjustment 698 assertEquals(nowElapsed, 699 mFlexibilityController.getLifeCycleBeginningElapsedLocked(jsBasic)); 700 assertEquals(nowElapsed + 40 * MINUTE_IN_MILLIS, 701 mFlexibilityController.getLifeCycleBeginningElapsedLocked(jsFlex)); 702 703 // Rescheduled with start adjustment 704 final long adjustmentMs = 4 * MINUTE_IN_MILLIS; 705 jsBasic = new JobStatus(jsBasic, 706 // "True" start is nowElapsed + HOUR_IN_MILLIS 707 nowElapsed + HOUR_IN_MILLIS + adjustmentMs, 708 nowElapsed + 2 * HOUR_IN_MILLIS, 709 0 /* numFailures */, 0 /* numAbandonedFailures */, 0 /* numSystemStops */, 710 JobSchedulerService.sSystemClock.millis() /* lastSuccessfulRunTime */, 711 0, 0); 712 jsFlex = new JobStatus(jsFlex, 713 // "True" start is nowElapsed + 2 * HOUR_IN_MILLIS - 20 * MINUTE_IN_MILLIS 714 nowElapsed + 2 * HOUR_IN_MILLIS - 20 * MINUTE_IN_MILLIS + adjustmentMs, 715 nowElapsed + 2 * HOUR_IN_MILLIS, 716 0 /* numFailures */, 0 /* numAbandonedFailures */, 0 /* numSystemStops */, 717 JobSchedulerService.sSystemClock.millis() /* lastSuccessfulRunTime */, 718 0, 0); 719 720 assertEquals(nowElapsed + HOUR_IN_MILLIS + adjustmentMs / 2, 721 mFlexibilityController.getLifeCycleBeginningElapsedLocked(jsBasic)); 722 assertEquals(nowElapsed + 2 * HOUR_IN_MILLIS - 20 * MINUTE_IN_MILLIS + adjustmentMs / 2, 723 mFlexibilityController.getLifeCycleBeginningElapsedLocked(jsFlex)); 724 725 // Rescheduled for failure 726 jsBasic = new JobStatus(jsBasic, 727 nowElapsed + 30 * MINUTE_IN_MILLIS, 728 NO_LATEST_RUNTIME, 729 1 /* numFailures */, 0 /* numAbandonedFailures */, 1 /* numSystemStops */, 730 JobSchedulerService.sSystemClock.millis() /* lastSuccessfulRunTime */, 731 0, 0); 732 jsFlex = new JobStatus(jsFlex, 733 nowElapsed + 30 * MINUTE_IN_MILLIS, 734 NO_LATEST_RUNTIME, 735 1 /* numFailures */, 0 /* numAbandonedFailures */, 1 /* numSystemStops */, 736 JobSchedulerService.sSystemClock.millis() /* lastSuccessfulRunTime */, 737 0, 0); 738 739 assertEquals(nowElapsed + 30 * MINUTE_IN_MILLIS, 740 mFlexibilityController.getLifeCycleBeginningElapsedLocked(jsBasic)); 741 assertEquals(nowElapsed + 30 * MINUTE_IN_MILLIS, 742 mFlexibilityController.getLifeCycleBeginningElapsedLocked(jsFlex)); 743 } 744 745 @Test testGetLifeCycleBeginningElapsedLocked_Prefetch()746 public void testGetLifeCycleBeginningElapsedLocked_Prefetch() { 747 // prefetch with lifecycle 748 doReturn(700L).when(mPrefetchController).getLaunchTimeThresholdMs(); 749 JobInfo.Builder jb = createJob(0).setPrefetch(true); 750 JobStatus js = createJobStatus("time", jb); 751 doReturn(900L).when(mPrefetchController).getNextEstimatedLaunchTimeLocked(js); 752 assertEquals(900L - 700L, mFlexibilityController.getLifeCycleBeginningElapsedLocked(js)); 753 // prefetch with enqueue 754 jb = createJob(0).setPrefetch(true); 755 js = createJobStatus("time", jb); 756 assertEquals(FROZEN_TIME, mFlexibilityController.getLifeCycleBeginningElapsedLocked(js)); 757 // prefetch with delay 758 jb = createJob(0).setPrefetch(true).setMinimumLatency(200); 759 js = createJobStatus("time", jb); 760 assertEquals(200 + FROZEN_TIME, js.getEarliestRunTime()); 761 assertEquals(js.getEarliestRunTime(), 762 mFlexibilityController.getLifeCycleBeginningElapsedLocked(js)); 763 // prefetch without estimate 764 mFlexibilityController.mPrefetchLifeCycleStart 765 .add(js.getUserId(), js.getSourcePackageName(), 500L); 766 doReturn(Long.MAX_VALUE).when(mPrefetchController).getNextEstimatedLaunchTimeLocked(js); 767 jb = createJob(0).setPrefetch(true); 768 js = createJobStatus("time", jb); 769 assertEquals(500L, mFlexibilityController.getLifeCycleBeginningElapsedLocked(js)); 770 } 771 772 @Test testGetLifeCycleBeginningElapsedLocked_NonPrefetch()773 public void testGetLifeCycleBeginningElapsedLocked_NonPrefetch() { 774 // delay 775 long delay = 100; 776 JobInfo.Builder jb = createJob(0).setMinimumLatency(delay); 777 JobStatus js = createJobStatus("time", jb); 778 assertEquals(delay + FROZEN_TIME, 779 mFlexibilityController.getLifeCycleBeginningElapsedLocked(js)); 780 // no delay 781 jb = createJob(0); 782 js = createJobStatus("time", jb); 783 assertEquals(FROZEN_TIME, 784 mFlexibilityController.getLifeCycleBeginningElapsedLocked(js)); 785 } 786 787 @Test testGetLifeCycleEndElapsedLocked_Prefetch()788 public void testGetLifeCycleEndElapsedLocked_Prefetch() { 789 final long nowElapsed = sElapsedRealtimeClock.millis(); 790 791 // prefetch no estimate 792 JobInfo.Builder jb = createJob(0).setPrefetch(true); 793 JobStatus js = createJobStatus("time", jb); 794 doReturn(Long.MAX_VALUE).when(mPrefetchController).getNextEstimatedLaunchTimeLocked(js); 795 assertEquals(Long.MAX_VALUE, 796 mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, 0)); 797 798 // prefetch with estimate 799 jb = createJob(0).setPrefetch(true); 800 js = createJobStatus("time", jb); 801 doReturn(1000L).when(mPrefetchController).getNextEstimatedLaunchTimeLocked(js); 802 assertEquals(1000L, 803 mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, 0)); 804 } 805 806 @Test testGetLifeCycleEndElapsedLocked_NonPrefetch()807 public void testGetLifeCycleEndElapsedLocked_NonPrefetch() { 808 setDeviceConfigString(KEY_FALLBACK_FLEXIBILITY_DEADLINES, 809 "500=" + HOUR_IN_MILLIS 810 + ",400=" + 2 * HOUR_IN_MILLIS 811 + ",300=" + 3 * HOUR_IN_MILLIS 812 + ",200=" + 4 * HOUR_IN_MILLIS 813 + ",100=" + 5 * HOUR_IN_MILLIS); 814 815 final long nowElapsed = sElapsedRealtimeClock.millis(); 816 817 // deadline 818 JobInfo.Builder jb = createJob(0).setOverrideDeadline(HOUR_IN_MILLIS); 819 JobStatus js = createJobStatus("time", jb); 820 assertEquals(3 * HOUR_IN_MILLIS + js.enqueueTime, 821 mFlexibilityController 822 .getLifeCycleEndElapsedLocked(js, nowElapsed, js.enqueueTime)); 823 824 // no deadline 825 assertEquals(js.enqueueTime + 2 * HOUR_IN_MILLIS, 826 mFlexibilityController.getLifeCycleEndElapsedLocked( 827 createJobStatus("time", createJob(0).setPriority(JobInfo.PRIORITY_HIGH)), 828 nowElapsed, js.enqueueTime)); 829 assertEquals(js.enqueueTime + 3 * HOUR_IN_MILLIS, 830 mFlexibilityController.getLifeCycleEndElapsedLocked( 831 createJobStatus("time", createJob(0).setPriority(JobInfo.PRIORITY_DEFAULT)), 832 nowElapsed, js.enqueueTime)); 833 assertEquals(js.enqueueTime + 4 * HOUR_IN_MILLIS, 834 mFlexibilityController.getLifeCycleEndElapsedLocked( 835 createJobStatus("time", createJob(0).setPriority(JobInfo.PRIORITY_LOW)), 836 nowElapsed, js.enqueueTime)); 837 assertEquals(js.enqueueTime + 5 * HOUR_IN_MILLIS, 838 mFlexibilityController.getLifeCycleEndElapsedLocked( 839 createJobStatus("time", createJob(0).setPriority(JobInfo.PRIORITY_MIN)), 840 nowElapsed, js.enqueueTime)); 841 } 842 843 @Test testGetLifeCycleEndElapsedLocked_Rescheduled()844 public void testGetLifeCycleEndElapsedLocked_Rescheduled() { 845 final long nowElapsed = sElapsedRealtimeClock.millis(); 846 847 JobInfo.Builder jb = createJob(0).setOverrideDeadline(HOUR_IN_MILLIS); 848 JobStatus js = createJobStatus("time", jb); 849 js = new JobStatus( 850 js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 2, 851 0 /* numAbandonedFailures */, /* numSystemStops */ 0, 852 0, FROZEN_TIME, FROZEN_TIME); 853 854 assertEquals(mFcConfig.RESCHEDULED_JOB_DEADLINE_MS, 855 mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, 0)); 856 857 js = new JobStatus( 858 js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 2, 859 0 /* numAbandonedFailures */, /* numSystemStops */ 1, 860 0, FROZEN_TIME, FROZEN_TIME); 861 862 assertEquals(2 * mFcConfig.RESCHEDULED_JOB_DEADLINE_MS, 863 mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, 0)); 864 865 js = new JobStatus( 866 js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 0, 867 0 /* numAbandonedFailures */, /* numSystemStops */ 10, 868 0, FROZEN_TIME, FROZEN_TIME); 869 assertEquals(mFcConfig.MAX_RESCHEDULED_DEADLINE_MS, 870 mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, 0)); 871 } 872 873 @Test testGetLifeCycleEndElapsedLocked_ScoreAddition()874 public void testGetLifeCycleEndElapsedLocked_ScoreAddition() { 875 setDeviceConfigString(KEY_FALLBACK_FLEXIBILITY_DEADLINES, 876 "500=" + HOUR_IN_MILLIS 877 + ",400=" + HOUR_IN_MILLIS 878 + ",300=" + HOUR_IN_MILLIS 879 + ",200=" + HOUR_IN_MILLIS 880 + ",100=" + HOUR_IN_MILLIS); 881 setDeviceConfigString(KEY_FALLBACK_FLEXIBILITY_DEADLINE_SCORES, 882 "500=5,400=4,300=3,200=2,100=1"); 883 setDeviceConfigString(KEY_FALLBACK_FLEXIBILITY_DEADLINE_ADDITIONAL_SCORE_TIME_FACTORS, 884 "500=" + 5 * MINUTE_IN_MILLIS 885 + ",400=" + 4 * MINUTE_IN_MILLIS 886 + ",300=" + 3 * MINUTE_IN_MILLIS 887 + ",200=" + 2 * MINUTE_IN_MILLIS 888 + ",100=" + 1 * MINUTE_IN_MILLIS); 889 890 final long nowElapsed = sElapsedRealtimeClock.millis(); 891 892 JobStatus jsMax = createJobStatus("testScoreCalculation", 893 createJob(0).setExpedited(true).setPriority(JobInfo.PRIORITY_MAX)); 894 JobStatus jsHigh = createJobStatus("testScoreCalculation", 895 createJob(0).setPriority(JobInfo.PRIORITY_HIGH)); 896 JobStatus jsDefault = createJobStatus("testScoreCalculation", 897 createJob(0).setPriority(JobInfo.PRIORITY_DEFAULT)); 898 JobStatus jsLow = createJobStatus("testScoreCalculation", 899 createJob(0).setPriority(JobInfo.PRIORITY_LOW)); 900 JobStatus jsMin = createJobStatus("testScoreCalculation", 901 createJob(0).setPriority(JobInfo.PRIORITY_MIN)); 902 // Make score = 15 903 mFlexibilityController.prepareForExecutionLocked(jsMax); 904 mFlexibilityController.prepareForExecutionLocked(jsHigh); 905 mFlexibilityController.prepareForExecutionLocked(jsDefault); 906 mFlexibilityController.prepareForExecutionLocked(jsLow); 907 mFlexibilityController.prepareForExecutionLocked(jsMin); 908 909 final long longDeadlineMs = 14 * 24 * HOUR_IN_MILLIS; 910 JobInfo.Builder jbWithLongDeadline = createJob(0).setOverrideDeadline(longDeadlineMs); 911 JobStatus jsWithLongDeadline = createJobStatus( 912 "testGetLifeCycleEndElapsedLocked_ScoreAddition", jbWithLongDeadline); 913 JobInfo.Builder jbWithShortDeadline = 914 createJob(0).setOverrideDeadline(15 * MINUTE_IN_MILLIS); 915 JobStatus jsWithShortDeadline = createJobStatus( 916 "testGetLifeCycleEndElapsedLocked_ScoreAddition", jbWithShortDeadline); 917 918 final long earliestMs = 123L; 919 assertEquals(earliestMs + HOUR_IN_MILLIS + 5 * 15 * MINUTE_IN_MILLIS, 920 mFlexibilityController.getLifeCycleEndElapsedLocked( 921 createJobStatus("testGetLifeCycleEndElapsedLocked_ScoreAddition", 922 createJob(0).setExpedited(true).setPriority(JobInfo.PRIORITY_MAX)), 923 nowElapsed, earliestMs)); 924 assertEquals(earliestMs + HOUR_IN_MILLIS + 4 * 15 * MINUTE_IN_MILLIS, 925 mFlexibilityController.getLifeCycleEndElapsedLocked( 926 createJobStatus("testGetLifeCycleEndElapsedLocked_ScoreAddition", 927 createJob(0).setPriority(JobInfo.PRIORITY_HIGH)), 928 nowElapsed, earliestMs)); 929 assertEquals(earliestMs + HOUR_IN_MILLIS + 3 * 15 * MINUTE_IN_MILLIS, 930 mFlexibilityController.getLifeCycleEndElapsedLocked( 931 createJobStatus("testGetLifeCycleEndElapsedLocked_ScoreAddition", 932 createJob(0).setPriority(JobInfo.PRIORITY_DEFAULT)), 933 nowElapsed, earliestMs)); 934 assertEquals(earliestMs + HOUR_IN_MILLIS + 3 * 15 * MINUTE_IN_MILLIS, 935 mFlexibilityController.getLifeCycleEndElapsedLocked( 936 jsWithShortDeadline, nowElapsed, earliestMs)); 937 assertEquals(earliestMs + HOUR_IN_MILLIS + 2 * 15 * MINUTE_IN_MILLIS, 938 mFlexibilityController.getLifeCycleEndElapsedLocked( 939 createJobStatus("testGetLifeCycleEndElapsedLocked_ScoreAddition", 940 createJob(0).setPriority(JobInfo.PRIORITY_LOW)), 941 nowElapsed, earliestMs)); 942 assertEquals(earliestMs + HOUR_IN_MILLIS + 1 * 15 * MINUTE_IN_MILLIS, 943 mFlexibilityController.getLifeCycleEndElapsedLocked( 944 createJobStatus("testGetLifeCycleEndElapsedLocked_ScoreAddition", 945 createJob(0).setPriority(JobInfo.PRIORITY_MIN)), 946 nowElapsed, earliestMs)); 947 assertEquals(jsWithLongDeadline.enqueueTime + longDeadlineMs, 948 mFlexibilityController.getLifeCycleEndElapsedLocked( 949 jsWithLongDeadline, nowElapsed, earliestMs)); 950 } 951 952 @Test testWontStopAlreadyRunningJob()953 public void testWontStopAlreadyRunningJob() { 954 JobStatus js = createJobStatus("testWontStopAlreadyRunningJob", createJob(101)); 955 // Stop satisfied constraints from causing a false positive. 956 js.setNumAppliedFlexibleConstraints(100); 957 synchronized (mFlexibilityController.mLock) { 958 doReturn(true).when(mJobSchedulerService).isCurrentlyRunningLocked(js); 959 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(js)); 960 } 961 } 962 963 @Test testFlexibilityTracker()964 public void testFlexibilityTracker() { 965 setDeviceConfigLong(KEY_FALLBACK_FLEXIBILITY_DEADLINE, 100 * HOUR_IN_MILLIS); 966 setDeviceConfigString(KEY_FALLBACK_FLEXIBILITY_DEADLINES, 967 "500=" + 100 * HOUR_IN_MILLIS 968 + ",400=" + 100 * HOUR_IN_MILLIS 969 + ",300=" + 100 * HOUR_IN_MILLIS 970 + ",200=" + 100 * HOUR_IN_MILLIS 971 + ",100=" + 100 * HOUR_IN_MILLIS); 972 973 FlexibilityController.FlexibilityTracker flexTracker = 974 mFlexibilityController.new FlexibilityTracker(4); 975 // Plus one for jobs with 0 required constraint. 976 assertEquals(4 + 1, flexTracker.size()); 977 JobStatus[] jobs = new JobStatus[4]; 978 JobInfo.Builder jb; 979 for (int i = 0; i < jobs.length; i++) { 980 jb = createJob(i); 981 if (i > 0) { 982 jb.setRequiresDeviceIdle(true); 983 } 984 if (i > 1) { 985 jb.setRequiresBatteryNotLow(true); 986 } 987 if (i > 2) { 988 jb.setRequiresCharging(true); 989 } 990 jobs[i] = createJobStatus("", jb); 991 flexTracker.add(jobs[i]); 992 } 993 994 synchronized (mFlexibilityController.mLock) { 995 ArrayList<ArraySet<JobStatus>> trackedJobs = flexTracker.getArrayList(); 996 assertEquals(1, trackedJobs.get(0).size()); 997 assertEquals(0, trackedJobs.get(1).size()); 998 assertEquals(0, trackedJobs.get(2).size()); 999 assertEquals(3, trackedJobs.get(3).size()); 1000 assertEquals(0, trackedJobs.get(4).size()); 1001 1002 flexTracker.setNumDroppedFlexibleConstraints(jobs[0], 1); 1003 assertEquals(1, trackedJobs.get(0).size()); 1004 assertEquals(0, trackedJobs.get(1).size()); 1005 assertEquals(1, trackedJobs.get(2).size()); 1006 assertEquals(2, trackedJobs.get(3).size()); 1007 assertEquals(0, trackedJobs.get(4).size()); 1008 1009 flexTracker.setNumDroppedFlexibleConstraints(jobs[0], 2); 1010 assertEquals(1, trackedJobs.get(0).size()); 1011 assertEquals(1, trackedJobs.get(1).size()); 1012 assertEquals(0, trackedJobs.get(2).size()); 1013 assertEquals(2, trackedJobs.get(3).size()); 1014 assertEquals(0, trackedJobs.get(4).size()); 1015 1016 flexTracker.setNumDroppedFlexibleConstraints(jobs[0], 3); 1017 assertEquals(2, trackedJobs.get(0).size()); 1018 assertEquals(0, trackedJobs.get(1).size()); 1019 assertEquals(0, trackedJobs.get(2).size()); 1020 assertEquals(2, trackedJobs.get(3).size()); 1021 assertEquals(0, trackedJobs.get(4).size()); 1022 1023 flexTracker.remove(jobs[1]); 1024 assertEquals(2, trackedJobs.get(0).size()); 1025 assertEquals(0, trackedJobs.get(1).size()); 1026 assertEquals(0, trackedJobs.get(2).size()); 1027 assertEquals(1, trackedJobs.get(3).size()); 1028 assertEquals(0, trackedJobs.get(4).size()); 1029 1030 flexTracker.calculateNumDroppedConstraints(jobs[0], FROZEN_TIME); 1031 assertEquals(1, trackedJobs.get(0).size()); 1032 assertEquals(0, trackedJobs.get(1).size()); 1033 assertEquals(0, trackedJobs.get(2).size()); 1034 assertEquals(2, trackedJobs.get(3).size()); 1035 assertEquals(0, trackedJobs.get(4).size()); 1036 1037 flexTracker.setNumDroppedFlexibleConstraints(jobs[0], 2); 1038 assertEquals(1, trackedJobs.get(0).size()); 1039 assertEquals(1, trackedJobs.get(1).size()); 1040 assertEquals(0, trackedJobs.get(2).size()); 1041 assertEquals(1, trackedJobs.get(3).size()); 1042 assertEquals(0, trackedJobs.get(4).size()); 1043 1044 final long nowElapsed = 51 * HOUR_IN_MILLIS; 1045 JobSchedulerService.sElapsedRealtimeClock = 1046 Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); 1047 1048 flexTracker.calculateNumDroppedConstraints(jobs[0], nowElapsed); 1049 assertEquals(1, trackedJobs.get(0).size()); 1050 assertEquals(0, trackedJobs.get(1).size()); 1051 assertEquals(1, trackedJobs.get(2).size()); 1052 assertEquals(1, trackedJobs.get(3).size()); 1053 assertEquals(0, trackedJobs.get(4).size()); 1054 } 1055 } 1056 1057 @Test testExceptions_Expedited()1058 public void testExceptions_Expedited() { 1059 JobInfo.Builder jb = createJob(0); 1060 jb.setExpedited(true); 1061 JobStatus js = createJobStatus("testExceptions_Expedited", jb); 1062 assertFalse(js.hasFlexibilityConstraint()); 1063 } 1064 1065 @Test testExceptions_UserInitiated()1066 public void testExceptions_UserInitiated() { 1067 JobInfo.Builder jb = createJob(0); 1068 jb.setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); 1069 JobStatus js = createJobStatus("testExceptions_UserInitiated", jb); 1070 assertFalse(js.hasFlexibilityConstraint()); 1071 } 1072 1073 @Test 1074 @CoreCompatChangeRule.DisableCompatChanges({JobInfo.ENFORCE_MINIMUM_TIME_WINDOWS}) testExceptions_ShortWindow()1075 public void testExceptions_ShortWindow() { 1076 JobInfo.Builder jb = createJob(0); 1077 jb.setMinimumLatency(1); 1078 jb.setOverrideDeadline(2); 1079 JobStatus js = createJobStatus("testExceptions_ShortWindow", jb); 1080 assertTrue(js.hasFlexibilityConstraint()); 1081 } 1082 1083 @Test testExceptions_NoFlexibleConstraints()1084 public void testExceptions_NoFlexibleConstraints() { 1085 JobInfo.Builder jb = createJob(0); 1086 jb.setRequiresDeviceIdle(true); 1087 jb.setRequiresCharging(true); 1088 jb.setRequiresBatteryNotLow(true); 1089 JobStatus js = createJobStatus("testExceptions_NoFlexibleConstraints", jb); 1090 assertFalse(js.hasFlexibilityConstraint()); 1091 } 1092 1093 @Test testExceptions_RescheduledOnce()1094 public void testExceptions_RescheduledOnce() { 1095 JobInfo.Builder jb = createJob(0); 1096 JobStatus js = createJobStatus("time", jb); 1097 js = new JobStatus( 1098 js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 1, 1099 /* numAbandonedFailures */ 0, /* numSystemStops */ 0, 1100 0, FROZEN_TIME, FROZEN_TIME); 1101 assertFalse(js.hasFlexibilityConstraint()); 1102 js = new JobStatus( 1103 js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 0, 1104 /* numAbandonedFailures */ 0, /* numSystemStops */ 1, 1105 0, FROZEN_TIME, FROZEN_TIME); 1106 assertFalse(js.hasFlexibilityConstraint()); 1107 } 1108 1109 @Test testExceptions_None()1110 public void testExceptions_None() { 1111 JobInfo.Builder jb = createJob(0); 1112 JobStatus js = createJobStatus("testExceptions_None", jb); 1113 assertTrue(js.hasFlexibilityConstraint()); 1114 assertEquals(3, js.getNumRequiredFlexibleConstraints()); 1115 } 1116 1117 @Test testAllowlistedAppBypass()1118 public void testAllowlistedAppBypass() { 1119 mFlexibilityController.onSystemServicesReady(); 1120 1121 JobStatus jsHigh = createJobStatus("testAllowlistedAppBypass", 1122 createJob(0).setPriority(JobInfo.PRIORITY_HIGH)); 1123 JobStatus jsDefault = createJobStatus("testAllowlistedAppBypass", 1124 createJob(0).setPriority(JobInfo.PRIORITY_DEFAULT)); 1125 JobStatus jsLow = createJobStatus("testAllowlistedAppBypass", 1126 createJob(0).setPriority(JobInfo.PRIORITY_LOW)); 1127 JobStatus jsMin = createJobStatus("testAllowlistedAppBypass", 1128 createJob(0).setPriority(JobInfo.PRIORITY_MIN)); 1129 jsHigh.setStandbyBucket(EXEMPTED_INDEX); 1130 jsDefault.setStandbyBucket(EXEMPTED_INDEX); 1131 jsLow.setStandbyBucket(EXEMPTED_INDEX); 1132 jsMin.setStandbyBucket(EXEMPTED_INDEX); 1133 1134 setPowerWhitelistExceptIdle(); 1135 synchronized (mFlexibilityController.mLock) { 1136 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHigh)); 1137 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefault)); 1138 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLow)); 1139 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMin)); 1140 } 1141 1142 setPowerWhitelistExceptIdle(SOURCE_PACKAGE); 1143 synchronized (mFlexibilityController.mLock) { 1144 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHigh)); 1145 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefault)); 1146 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLow)); 1147 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMin)); 1148 } 1149 } 1150 1151 @Test testCarrierPrivilegedAppBypass()1152 public void testCarrierPrivilegedAppBypass() throws Exception { 1153 mFlexibilityController.onSystemServicesReady(); 1154 1155 final String carrier1Pkg1 = "com.test.carrier.1.pkg.1"; 1156 final String carrier1Pkg2 = "com.test.carrier.1.pkg.2"; 1157 final String carrier2Pkg = "com.test.carrier.2.pkg"; 1158 final String nonCarrierPkg = "com.test.normal.pkg"; 1159 1160 setPackageUid(carrier1Pkg1, 1); 1161 setPackageUid(carrier1Pkg2, 11); 1162 setPackageUid(carrier2Pkg, 2); 1163 setPackageUid(nonCarrierPkg, 3); 1164 1165 // Set the second carrier's privileged list before SIM configuration is sent to test 1166 // initialization. 1167 setCarrierPrivilegedAppList(2, carrier2Pkg); 1168 1169 UiccSlotMapping sim1 = mock(UiccSlotMapping.class); 1170 UiccSlotMapping sim2 = mock(UiccSlotMapping.class); 1171 doReturn(1).when(sim1).getLogicalSlotIndex(); 1172 doReturn(2).when(sim2).getLogicalSlotIndex(); 1173 setSimSlotMappings(List.of(sim1, sim2)); 1174 1175 JobStatus jsHighC1P1 = createJobStatus("testCarrierPrivilegedAppBypass", 1176 createJob(0).setPriority(JobInfo.PRIORITY_HIGH), carrier1Pkg1); 1177 JobStatus jsDefaultC1P1 = createJobStatus("testCarrierPrivilegedAppBypass", 1178 createJob(0).setPriority(JobInfo.PRIORITY_DEFAULT), carrier1Pkg1); 1179 JobStatus jsLowC1P1 = createJobStatus("testCarrierPrivilegedAppBypass", 1180 createJob(0).setPriority(JobInfo.PRIORITY_LOW), carrier1Pkg1); 1181 JobStatus jsMinC1P1 = createJobStatus("testCarrierPrivilegedAppBypass", 1182 createJob(0).setPriority(JobInfo.PRIORITY_MIN), carrier1Pkg1); 1183 JobStatus jsHighC1P2 = createJobStatus("testCarrierPrivilegedAppBypass", 1184 createJob(0).setPriority(JobInfo.PRIORITY_HIGH), carrier1Pkg2); 1185 JobStatus jsDefaultC1P2 = createJobStatus("testCarrierPrivilegedAppBypass", 1186 createJob(0).setPriority(JobInfo.PRIORITY_DEFAULT), carrier1Pkg2); 1187 JobStatus jsLowC1P2 = createJobStatus("testCarrierPrivilegedAppBypass", 1188 createJob(0).setPriority(JobInfo.PRIORITY_LOW), carrier1Pkg2); 1189 JobStatus jsMinC1P2 = createJobStatus("testCarrierPrivilegedAppBypass", 1190 createJob(0).setPriority(JobInfo.PRIORITY_MIN), carrier1Pkg2); 1191 JobStatus jsHighC2P = createJobStatus("testCarrierPrivilegedAppBypass", 1192 createJob(0).setPriority(JobInfo.PRIORITY_HIGH), carrier2Pkg); 1193 JobStatus jsDefaultC2P = createJobStatus("testCarrierPrivilegedAppBypass", 1194 createJob(0).setPriority(JobInfo.PRIORITY_DEFAULT), carrier2Pkg); 1195 JobStatus jsLowC2P = createJobStatus("testCarrierPrivilegedAppBypass", 1196 createJob(0).setPriority(JobInfo.PRIORITY_LOW), carrier2Pkg); 1197 JobStatus jsMinC2P = createJobStatus("testCarrierPrivilegedAppBypass", 1198 createJob(0).setPriority(JobInfo.PRIORITY_MIN), carrier2Pkg); 1199 JobStatus jsHighNCP = createJobStatus("testCarrierPrivilegedAppBypass", 1200 createJob(0).setPriority(JobInfo.PRIORITY_HIGH), nonCarrierPkg); 1201 JobStatus jsDefaultNCP = createJobStatus("testCarrierPrivilegedAppBypass", 1202 createJob(0).setPriority(JobInfo.PRIORITY_DEFAULT), nonCarrierPkg); 1203 JobStatus jsLowNCP = createJobStatus("testCarrierPrivilegedAppBypass", 1204 createJob(0).setPriority(JobInfo.PRIORITY_LOW), nonCarrierPkg); 1205 JobStatus jsMinNCP = createJobStatus("testCarrierPrivilegedAppBypass", 1206 createJob(0).setPriority(JobInfo.PRIORITY_MIN), nonCarrierPkg); 1207 1208 setCarrierPrivilegedAppList(1); 1209 synchronized (mFlexibilityController.mLock) { 1210 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P1)); 1211 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P1)); 1212 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P1)); 1213 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P1)); 1214 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P2)); 1215 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P2)); 1216 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P2)); 1217 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P2)); 1218 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC2P)); 1219 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC2P)); 1220 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC2P)); 1221 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC2P)); 1222 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighNCP)); 1223 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultNCP)); 1224 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowNCP)); 1225 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinNCP)); 1226 } 1227 1228 // Only mark the first package of carrier 1 as privileged. Only that app's jobs should 1229 // be exempted. 1230 setCarrierPrivilegedAppList(1, carrier1Pkg1); 1231 synchronized (mFlexibilityController.mLock) { 1232 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P1)); 1233 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P1)); 1234 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P1)); 1235 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P1)); 1236 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P2)); 1237 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P2)); 1238 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P2)); 1239 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P2)); 1240 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC2P)); 1241 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC2P)); 1242 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC2P)); 1243 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC2P)); 1244 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighNCP)); 1245 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultNCP)); 1246 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowNCP)); 1247 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinNCP)); 1248 } 1249 1250 // Add the second package of carrier 1. Both apps' jobs should be exempted. 1251 setCarrierPrivilegedAppList(1, carrier1Pkg1, carrier1Pkg2); 1252 synchronized (mFlexibilityController.mLock) { 1253 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P1)); 1254 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P1)); 1255 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P1)); 1256 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P1)); 1257 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P2)); 1258 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P2)); 1259 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P2)); 1260 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P2)); 1261 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC2P)); 1262 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC2P)); 1263 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC2P)); 1264 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC2P)); 1265 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighNCP)); 1266 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultNCP)); 1267 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowNCP)); 1268 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinNCP)); 1269 } 1270 1271 // Remove a SIM slot. The relevant app's should no longer have exempted jobs. 1272 setSimSlotMappings(List.of(sim1)); 1273 synchronized (mFlexibilityController.mLock) { 1274 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P1)); 1275 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P1)); 1276 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P1)); 1277 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P1)); 1278 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P2)); 1279 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P2)); 1280 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P2)); 1281 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P2)); 1282 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC2P)); 1283 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC2P)); 1284 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC2P)); 1285 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC2P)); 1286 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighNCP)); 1287 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultNCP)); 1288 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowNCP)); 1289 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinNCP)); 1290 } 1291 } 1292 1293 @Test testForegroundAppBypass()1294 public void testForegroundAppBypass() { 1295 JobStatus jsHigh = createJobStatus("testAllowlistedAppBypass", 1296 createJob(0).setPriority(JobInfo.PRIORITY_HIGH)); 1297 JobStatus jsDefault = createJobStatus("testAllowlistedAppBypass", 1298 createJob(0).setPriority(JobInfo.PRIORITY_DEFAULT)); 1299 JobStatus jsLow = createJobStatus("testAllowlistedAppBypass", 1300 createJob(0).setPriority(JobInfo.PRIORITY_LOW)); 1301 JobStatus jsMin = createJobStatus("testAllowlistedAppBypass", 1302 createJob(0).setPriority(JobInfo.PRIORITY_MIN)); 1303 1304 doReturn(JobInfo.BIAS_DEFAULT).when(mJobSchedulerService).getUidBias(mSourceUid); 1305 synchronized (mFlexibilityController.mLock) { 1306 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHigh)); 1307 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefault)); 1308 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLow)); 1309 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMin)); 1310 } 1311 1312 setUidBias(mSourceUid, JobInfo.BIAS_BOUND_FOREGROUND_SERVICE); 1313 synchronized (mFlexibilityController.mLock) { 1314 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHigh)); 1315 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefault)); 1316 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLow)); 1317 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMin)); 1318 } 1319 1320 setUidBias(mSourceUid, JobInfo.BIAS_FOREGROUND_SERVICE); 1321 synchronized (mFlexibilityController.mLock) { 1322 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHigh)); 1323 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefault)); 1324 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLow)); 1325 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMin)); 1326 } 1327 } 1328 1329 @Test testTopAppBypass()1330 public void testTopAppBypass() { 1331 JobInfo.Builder jb = createJob(0).setPriority(JobInfo.PRIORITY_MIN); 1332 JobStatus js = createJobStatus("testTopAppBypass", jb); 1333 mJobStore.add(js); 1334 1335 // Needed because if before and after Uid bias is the same, nothing happens. 1336 doReturn(JobInfo.BIAS_DEFAULT).when(mJobSchedulerService).getUidBias(mSourceUid); 1337 1338 synchronized (mFlexibilityController.mLock) { 1339 mFlexibilityController.maybeStartTrackingJobLocked(js, null); 1340 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(js)); 1341 1342 setUidBias(mSourceUid, JobInfo.BIAS_TOP_APP); 1343 1344 assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(js)); 1345 assertTrue(js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE)); 1346 1347 setUidBias(mSourceUid, JobInfo.BIAS_SYNC_INITIALIZATION); 1348 1349 assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(js)); 1350 assertFalse(js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE)); 1351 } 1352 } 1353 1354 @Test testTransportAffinity()1355 public void testTransportAffinity() { 1356 JobStatus jsAny = createJobStatus("testTransportAffinity", 1357 createJob(0).setRequiredNetworkType(NETWORK_TYPE_ANY)); 1358 JobStatus jsCell = createJobStatus("testTransportAffinity", 1359 createJob(0).setRequiredNetworkType(NETWORK_TYPE_CELLULAR)); 1360 JobStatus jsWifi = createJobStatus("testTransportAffinity", 1361 createJob(0).setRequiredNetwork( 1362 new NetworkRequest.Builder() 1363 .addTransportType(TRANSPORT_WIFI) 1364 .build())); 1365 // Disable the unseen constraint logic. 1366 mFlexibilityController.setConstraintSatisfied( 1367 SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS, true, FROZEN_TIME); 1368 mFlexibilityController.setConstraintSatisfied( 1369 SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS, false, FROZEN_TIME); 1370 // Require only a single constraint 1371 jsAny.setNumAppliedFlexibleConstraints(1); 1372 jsCell.setNumAppliedFlexibleConstraints(1); 1373 jsWifi.setNumAppliedFlexibleConstraints(1); 1374 synchronized (mFlexibilityController.mLock) { 1375 jsAny.setTransportAffinitiesSatisfied(false); 1376 jsCell.setTransportAffinitiesSatisfied(false); 1377 jsWifi.setTransportAffinitiesSatisfied(false); 1378 mFlexibilityController.setConstraintSatisfied( 1379 CONSTRAINT_CONNECTIVITY, false, FROZEN_TIME); 1380 assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsAny)); 1381 assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsCell)); 1382 assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsWifi)); 1383 1384 // A good network exists, but the network hasn't been assigned to any of the jobs 1385 jsAny.setTransportAffinitiesSatisfied(false); 1386 jsCell.setTransportAffinitiesSatisfied(false); 1387 jsWifi.setTransportAffinitiesSatisfied(false); 1388 mFlexibilityController.setConstraintSatisfied( 1389 CONSTRAINT_CONNECTIVITY, true, FROZEN_TIME); 1390 assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsAny)); 1391 assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsCell)); 1392 assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsWifi)); 1393 1394 // The good network has been assigned to the relevant jobs 1395 jsAny.setTransportAffinitiesSatisfied(true); 1396 jsCell.setTransportAffinitiesSatisfied(false); 1397 jsWifi.setTransportAffinitiesSatisfied(true); 1398 assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsAny)); 1399 assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsCell)); 1400 assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsWifi)); 1401 1402 // One job loses access to the network. 1403 jsAny.setTransportAffinitiesSatisfied(true); 1404 jsCell.setTransportAffinitiesSatisfied(false); 1405 jsWifi.setTransportAffinitiesSatisfied(false); 1406 assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsAny)); 1407 assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsCell)); 1408 assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(jsWifi)); 1409 } 1410 } 1411 1412 @Test testSetConstraintSatisfied_Constraints()1413 public void testSetConstraintSatisfied_Constraints() { 1414 mFlexibilityController.setConstraintSatisfied(CONSTRAINT_IDLE, false, FROZEN_TIME); 1415 assertFalse(mFlexibilityController.isConstraintSatisfied(CONSTRAINT_IDLE)); 1416 1417 mFlexibilityController.setConstraintSatisfied(CONSTRAINT_IDLE, true, FROZEN_TIME); 1418 assertTrue(mFlexibilityController.isConstraintSatisfied(CONSTRAINT_IDLE)); 1419 1420 mFlexibilityController.setConstraintSatisfied(CONSTRAINT_IDLE, false, FROZEN_TIME); 1421 assertFalse(mFlexibilityController.isConstraintSatisfied(CONSTRAINT_IDLE)); 1422 } 1423 1424 @Test testSetConstraintSatisfied_Jobs()1425 public void testSetConstraintSatisfied_Jobs() { 1426 JobInfo.Builder jb; 1427 int[] constraintCombinations = { 1428 CONSTRAINT_IDLE & CONSTRAINT_CHARGING & CONSTRAINT_BATTERY_NOT_LOW, 1429 CONSTRAINT_IDLE & CONSTRAINT_BATTERY_NOT_LOW, 1430 CONSTRAINT_IDLE & CONSTRAINT_CHARGING, 1431 CONSTRAINT_CHARGING & CONSTRAINT_BATTERY_NOT_LOW, 1432 CONSTRAINT_IDLE, 1433 CONSTRAINT_CHARGING, 1434 CONSTRAINT_BATTERY_NOT_LOW, 1435 0 1436 }; 1437 1438 int constraints; 1439 for (int i = 0; i < constraintCombinations.length; i++) { 1440 jb = createJob(i); 1441 constraints = constraintCombinations[i]; 1442 jb.setRequiresDeviceIdle((constraints & CONSTRAINT_IDLE) != 0); 1443 jb.setRequiresBatteryNotLow((constraints & CONSTRAINT_BATTERY_NOT_LOW) != 0); 1444 jb.setRequiresCharging((constraints & CONSTRAINT_CHARGING) != 0); 1445 synchronized (mFlexibilityController.mLock) { 1446 mFlexibilityController.maybeStartTrackingJobLocked( 1447 createJobStatus(String.valueOf(i), jb), null); 1448 } 1449 } 1450 mFlexibilityController.setConstraintSatisfied(CONSTRAINT_CHARGING, false, FROZEN_TIME); 1451 mFlexibilityController.setConstraintSatisfied(CONSTRAINT_IDLE, false, FROZEN_TIME); 1452 mFlexibilityController.setConstraintSatisfied( 1453 CONSTRAINT_BATTERY_NOT_LOW, false, FROZEN_TIME); 1454 1455 assertEquals(0, mFlexibilityController.mSatisfiedFlexibleConstraints); 1456 1457 for (int i = 0; i < constraintCombinations.length; i++) { 1458 constraints = constraintCombinations[i]; 1459 mFlexibilityController.setConstraintSatisfied(CONSTRAINT_CHARGING, 1460 (constraints & CONSTRAINT_CHARGING) != 0, FROZEN_TIME); 1461 mFlexibilityController.setConstraintSatisfied(CONSTRAINT_IDLE, 1462 (constraints & CONSTRAINT_IDLE) != 0, FROZEN_TIME); 1463 mFlexibilityController.setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, 1464 (constraints & CONSTRAINT_BATTERY_NOT_LOW) != 0, FROZEN_TIME); 1465 1466 assertEquals(constraints, mFlexibilityController.mSatisfiedFlexibleConstraints); 1467 synchronized (mFlexibilityController.mLock) { 1468 assertSatisfiedJobsMatchSatisfiedConstraints( 1469 mFlexibilityController.mFlexibilityTracker.getArrayList(), constraints); 1470 } 1471 } 1472 } 1473 1474 @Test testHasEnoughSatisfiedConstraints_unseenConstraints_soonAfterBoot()1475 public void testHasEnoughSatisfiedConstraints_unseenConstraints_soonAfterBoot() { 1476 // Add connectivity to require 4 constraints 1477 JobStatus js = createJobStatus("testHasEnoughSatisfiedConstraints", 1478 createJob(0).setRequiredNetworkType(NETWORK_TYPE_ANY)); 1479 1480 // Too soon after boot 1481 JobSchedulerService.sElapsedRealtimeClock = 1482 Clock.fixed(Instant.ofEpochMilli(100 - 1), ZoneOffset.UTC); 1483 synchronized (mFlexibilityController.mLock) { 1484 assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(js)); 1485 } 1486 JobSchedulerService.sElapsedRealtimeClock = 1487 Clock.fixed(Instant.ofEpochMilli(DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS - 1), 1488 ZoneOffset.UTC); 1489 synchronized (mFlexibilityController.mLock) { 1490 assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(js)); 1491 } 1492 1493 // Long after boot 1494 1495 // No constraints ever seen. Don't bother waiting 1496 JobSchedulerService.sElapsedRealtimeClock = 1497 Clock.fixed(Instant.ofEpochMilli(DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS), 1498 ZoneOffset.UTC); 1499 synchronized (mFlexibilityController.mLock) { 1500 assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(js)); 1501 } 1502 } 1503 1504 @Test testHasEnoughSatisfiedConstraints_unseenConstraints_longAfterBoot()1505 public void testHasEnoughSatisfiedConstraints_unseenConstraints_longAfterBoot() { 1506 // Add connectivity to require 4 constraints 1507 JobStatus connJs = createJobStatus("testHasEnoughSatisfiedConstraints", 1508 createJob(0).setRequiredNetworkType(NETWORK_TYPE_ANY)); 1509 JobStatus nonConnJs = createJobStatus("testHasEnoughSatisfiedConstraints", 1510 createJob(0).setRequiredNetworkType(NETWORK_TYPE_NONE)); 1511 1512 mFlexibilityController.setConstraintSatisfied( 1513 CONSTRAINT_BATTERY_NOT_LOW, true, 1514 2 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10); 1515 mFlexibilityController.setConstraintSatisfied( 1516 CONSTRAINT_CHARGING, true, 1517 3 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10); 1518 mFlexibilityController.setConstraintSatisfied( 1519 CONSTRAINT_IDLE, true, 1520 4 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10); 1521 mFlexibilityController.setConstraintSatisfied( 1522 CONSTRAINT_CONNECTIVITY, true, 1523 5 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10); 1524 1525 // Long after boot 1526 // All constraints satisfied right now 1527 JobSchedulerService.sElapsedRealtimeClock = 1528 Clock.fixed(Instant.ofEpochMilli(DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS), 1529 ZoneOffset.UTC); 1530 synchronized (mFlexibilityController.mLock) { 1531 assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs)); 1532 assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs)); 1533 } 1534 1535 // Go down to 2 satisfied 1536 mFlexibilityController.setConstraintSatisfied( 1537 CONSTRAINT_CONNECTIVITY, false, 1538 6 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10); 1539 mFlexibilityController.setConstraintSatisfied( 1540 CONSTRAINT_IDLE, false, 1541 7 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10); 1542 // 3 & 4 constraints were seen recently enough, so the job should wait 1543 synchronized (mFlexibilityController.mLock) { 1544 assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs)); 1545 assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs)); 1546 } 1547 1548 // 4 constraints still in the grace period. Wait. 1549 JobSchedulerService.sElapsedRealtimeClock = 1550 Clock.fixed( 1551 Instant.ofEpochMilli(16 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10), 1552 ZoneOffset.UTC); 1553 synchronized (mFlexibilityController.mLock) { 1554 assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs)); 1555 assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs)); 1556 } 1557 1558 // 3 constraints still in the grace period. Wait. 1559 JobSchedulerService.sElapsedRealtimeClock = 1560 Clock.fixed( 1561 Instant.ofEpochMilli(17 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10), 1562 ZoneOffset.UTC); 1563 synchronized (mFlexibilityController.mLock) { 1564 assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs)); 1565 assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs)); 1566 } 1567 1568 // 3 constraints haven't been seen recently. Don't wait. 1569 JobSchedulerService.sElapsedRealtimeClock = 1570 Clock.fixed( 1571 Instant.ofEpochMilli( 1572 17 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10 + 1), 1573 ZoneOffset.UTC); 1574 synchronized (mFlexibilityController.mLock) { 1575 assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs)); 1576 assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs)); 1577 } 1578 1579 // Add then remove connectivity. Resets expectation of 3 constraints for connectivity jobs. 1580 // Connectivity job should wait while the non-connectivity job can run. 1581 // of getting back to 4 constraints. 1582 mFlexibilityController.setConstraintSatisfied( 1583 CONSTRAINT_CONNECTIVITY, true, 1584 18 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10); 1585 mFlexibilityController.setConstraintSatisfied( 1586 CONSTRAINT_CONNECTIVITY, false, 1587 19 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10); 1588 JobSchedulerService.sElapsedRealtimeClock = 1589 Clock.fixed( 1590 Instant.ofEpochMilli( 1591 19 * DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS / 10 + 1), 1592 ZoneOffset.UTC); 1593 synchronized (mFlexibilityController.mLock) { 1594 assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs)); 1595 assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs)); 1596 } 1597 } 1598 1599 @Test testCalculateNumDroppedConstraints()1600 public void testCalculateNumDroppedConstraints() { 1601 setDeviceConfigString(KEY_FALLBACK_FLEXIBILITY_DEADLINES, 1602 "500=" + HOUR_IN_MILLIS 1603 + ",400=" + 5 * HOUR_IN_MILLIS 1604 + ",300=" + 8 * HOUR_IN_MILLIS 1605 + ",200=" + 10 * HOUR_IN_MILLIS 1606 + ",100=" + 20 * HOUR_IN_MILLIS); 1607 setDeviceConfigString(KEY_PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS, 1608 "500=20|40|60|80" 1609 + ",400=20|40|60|80" 1610 + ",300=25|50|75|80" 1611 + ",200=40|50|60|80" 1612 + ",100=20|40|60|80"); 1613 1614 JobStatus jsHigh = createJobStatus("testCalculateNumDroppedConstraints", 1615 createJob(24).setPriority(JobInfo.PRIORITY_HIGH)); 1616 JobStatus jsDefault = createJobStatus("testCalculateNumDroppedConstraints", 1617 createJob(23).setPriority(JobInfo.PRIORITY_DEFAULT)); 1618 JobStatus jsLow = createJobStatus("testCalculateNumDroppedConstraints", 1619 createJob(22).setPriority(JobInfo.PRIORITY_LOW)); 1620 JobStatus jsMin = createJobStatus("testCalculateNumDroppedConstraints", 1621 createJob(21).setPriority(JobInfo.PRIORITY_MIN)); 1622 final long startElapsed = FROZEN_TIME; 1623 long nowElapsed = startElapsed; 1624 1625 mFlexibilityController.mFlexibilityTracker.add(jsHigh); 1626 mFlexibilityController.mFlexibilityTracker.add(jsDefault); 1627 mFlexibilityController.mFlexibilityTracker.add(jsLow); 1628 mFlexibilityController.mFlexibilityTracker.add(jsMin); 1629 1630 assertEquals(3, jsHigh.getNumRequiredFlexibleConstraints()); 1631 assertEquals(0, jsHigh.getNumDroppedFlexibleConstraints()); 1632 assertEquals(3, jsDefault.getNumRequiredFlexibleConstraints()); 1633 assertEquals(0, jsDefault.getNumDroppedFlexibleConstraints()); 1634 assertEquals(3, jsLow.getNumRequiredFlexibleConstraints()); 1635 assertEquals(0, jsLow.getNumDroppedFlexibleConstraints()); 1636 assertEquals(3, jsMin.getNumRequiredFlexibleConstraints()); 1637 assertEquals(0, jsMin.getNumDroppedFlexibleConstraints()); 1638 assertEquals(4, mFlexibilityController 1639 .mFlexibilityTracker.getJobsByNumRequiredConstraints(3).size()); 1640 1641 nowElapsed = startElapsed + HOUR_IN_MILLIS; 1642 JobSchedulerService.sElapsedRealtimeClock = 1643 Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); 1644 1645 mFlexibilityController.mFlexibilityTracker 1646 .calculateNumDroppedConstraints(jsHigh, nowElapsed); 1647 mFlexibilityController.mFlexibilityTracker 1648 .calculateNumDroppedConstraints(jsDefault, nowElapsed); 1649 mFlexibilityController.mFlexibilityTracker 1650 .calculateNumDroppedConstraints(jsLow, nowElapsed); 1651 mFlexibilityController.mFlexibilityTracker 1652 .calculateNumDroppedConstraints(jsMin, nowElapsed); 1653 1654 assertEquals(2, jsHigh.getNumRequiredFlexibleConstraints()); 1655 assertEquals(1, jsHigh.getNumDroppedFlexibleConstraints()); 1656 assertEquals(3, jsDefault.getNumRequiredFlexibleConstraints()); 1657 assertEquals(0, jsDefault.getNumDroppedFlexibleConstraints()); 1658 assertEquals(3, jsLow.getNumRequiredFlexibleConstraints()); 1659 assertEquals(0, jsLow.getNumDroppedFlexibleConstraints()); 1660 assertEquals(3, jsMin.getNumRequiredFlexibleConstraints()); 1661 assertEquals(0, jsMin.getNumDroppedFlexibleConstraints()); 1662 assertEquals(3, mFlexibilityController 1663 .mFlexibilityTracker.getJobsByNumRequiredConstraints(3).size()); 1664 assertEquals(1, mFlexibilityController 1665 .mFlexibilityTracker.getJobsByNumRequiredConstraints(2).size()); 1666 1667 nowElapsed = startElapsed; 1668 JobSchedulerService.sElapsedRealtimeClock = 1669 Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); 1670 1671 mFlexibilityController.mFlexibilityTracker 1672 .calculateNumDroppedConstraints(jsHigh, nowElapsed); 1673 mFlexibilityController.mFlexibilityTracker 1674 .calculateNumDroppedConstraints(jsDefault, nowElapsed); 1675 mFlexibilityController.mFlexibilityTracker 1676 .calculateNumDroppedConstraints(jsLow, nowElapsed); 1677 mFlexibilityController.mFlexibilityTracker 1678 .calculateNumDroppedConstraints(jsMin, nowElapsed); 1679 1680 assertEquals(3, jsHigh.getNumRequiredFlexibleConstraints()); 1681 assertEquals(0, jsHigh.getNumDroppedFlexibleConstraints()); 1682 assertEquals(3, jsDefault.getNumRequiredFlexibleConstraints()); 1683 assertEquals(0, jsDefault.getNumDroppedFlexibleConstraints()); 1684 assertEquals(3, jsLow.getNumRequiredFlexibleConstraints()); 1685 assertEquals(0, jsLow.getNumDroppedFlexibleConstraints()); 1686 assertEquals(3, jsMin.getNumRequiredFlexibleConstraints()); 1687 assertEquals(0, jsMin.getNumDroppedFlexibleConstraints()); 1688 assertEquals(4, mFlexibilityController 1689 .mFlexibilityTracker.getJobsByNumRequiredConstraints(3).size()); 1690 assertEquals(0, mFlexibilityController 1691 .mFlexibilityTracker.getJobsByNumRequiredConstraints(2).size()); 1692 assertEquals(0, mFlexibilityController 1693 .mFlexibilityTracker.getJobsByNumRequiredConstraints(1).size()); 1694 assertEquals(0, mFlexibilityController 1695 .mFlexibilityTracker.getJobsByNumRequiredConstraints(0).size()); 1696 1697 nowElapsed = startElapsed + 3 * HOUR_IN_MILLIS; 1698 JobSchedulerService.sElapsedRealtimeClock = 1699 Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); 1700 1701 mFlexibilityController.mFlexibilityTracker 1702 .calculateNumDroppedConstraints(jsHigh, nowElapsed); 1703 mFlexibilityController.mFlexibilityTracker 1704 .calculateNumDroppedConstraints(jsDefault, nowElapsed); 1705 mFlexibilityController.mFlexibilityTracker 1706 .calculateNumDroppedConstraints(jsLow, nowElapsed); 1707 mFlexibilityController.mFlexibilityTracker 1708 .calculateNumDroppedConstraints(jsMin, nowElapsed); 1709 1710 assertEquals(0, jsHigh.getNumRequiredFlexibleConstraints()); 1711 assertEquals(3, jsHigh.getNumDroppedFlexibleConstraints()); 1712 assertEquals(2, jsDefault.getNumRequiredFlexibleConstraints()); 1713 assertEquals(1, jsDefault.getNumDroppedFlexibleConstraints()); 1714 assertEquals(3, jsLow.getNumRequiredFlexibleConstraints()); 1715 assertEquals(0, jsLow.getNumDroppedFlexibleConstraints()); 1716 assertEquals(3, jsMin.getNumRequiredFlexibleConstraints()); 1717 assertEquals(0, jsMin.getNumDroppedFlexibleConstraints()); 1718 assertEquals(2, mFlexibilityController 1719 .mFlexibilityTracker.getJobsByNumRequiredConstraints(3).size()); 1720 assertEquals(1, mFlexibilityController 1721 .mFlexibilityTracker.getJobsByNumRequiredConstraints(2).size()); 1722 assertEquals(0, mFlexibilityController 1723 .mFlexibilityTracker.getJobsByNumRequiredConstraints(1).size()); 1724 assertEquals(1, mFlexibilityController 1725 .mFlexibilityTracker.getJobsByNumRequiredConstraints(0).size()); 1726 1727 nowElapsed = startElapsed + 4 * HOUR_IN_MILLIS; 1728 JobSchedulerService.sElapsedRealtimeClock = 1729 Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); 1730 1731 mFlexibilityController.mFlexibilityTracker 1732 .calculateNumDroppedConstraints(jsHigh, nowElapsed); 1733 mFlexibilityController.mFlexibilityTracker 1734 .calculateNumDroppedConstraints(jsDefault, nowElapsed); 1735 mFlexibilityController.mFlexibilityTracker 1736 .calculateNumDroppedConstraints(jsLow, nowElapsed); 1737 mFlexibilityController.mFlexibilityTracker 1738 .calculateNumDroppedConstraints(jsMin, nowElapsed); 1739 1740 assertEquals(0, jsHigh.getNumRequiredFlexibleConstraints()); 1741 assertEquals(3, jsHigh.getNumDroppedFlexibleConstraints()); 1742 assertEquals(1, jsDefault.getNumRequiredFlexibleConstraints()); 1743 assertEquals(2, jsDefault.getNumDroppedFlexibleConstraints()); 1744 assertEquals(2, jsLow.getNumRequiredFlexibleConstraints()); 1745 assertEquals(1, jsLow.getNumDroppedFlexibleConstraints()); 1746 assertEquals(2, jsMin.getNumRequiredFlexibleConstraints()); 1747 assertEquals(1, jsMin.getNumDroppedFlexibleConstraints()); 1748 assertEquals(0, mFlexibilityController 1749 .mFlexibilityTracker.getJobsByNumRequiredConstraints(3).size()); 1750 assertEquals(2, mFlexibilityController 1751 .mFlexibilityTracker.getJobsByNumRequiredConstraints(2).size()); 1752 assertEquals(1, mFlexibilityController 1753 .mFlexibilityTracker.getJobsByNumRequiredConstraints(1).size()); 1754 assertEquals(1, mFlexibilityController 1755 .mFlexibilityTracker.getJobsByNumRequiredConstraints(0).size()); 1756 } 1757 1758 @Test testOnPrefetchCacheUpdated()1759 public void testOnPrefetchCacheUpdated() { 1760 ArraySet<JobStatus> jobs = new ArraySet<JobStatus>(); 1761 JobInfo.Builder jb = createJob(22).setPrefetch(true); 1762 JobStatus js = createJobStatus("onPrefetchCacheUpdated", jb); 1763 jobs.add(js); 1764 doReturn(7 * HOUR_IN_MILLIS).when(mPrefetchController).getLaunchTimeThresholdMs(); 1765 doReturn(1150L + mFlexibilityController.mConstants.PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS) 1766 .when(mPrefetchController).getNextEstimatedLaunchTimeLocked(js); 1767 1768 mFlexibilityController.maybeStartTrackingJobLocked(js, null); 1769 1770 final long nowElapsed = 150L; 1771 JobSchedulerService.sElapsedRealtimeClock = 1772 Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); 1773 1774 mFlexibilityController.mPrefetchChangedListener.onPrefetchCacheUpdated( 1775 jobs, js.getUserId(), js.getSourcePackageName(), Long.MAX_VALUE, 1776 1150L + mFlexibilityController.mConstants.PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS, 1777 nowElapsed); 1778 1779 assertEquals(150L, 1780 (long) mFlexibilityController.mPrefetchLifeCycleStart 1781 .get(js.getSourceUserId(), js.getSourcePackageName())); 1782 assertEquals(150L, mFlexibilityController.getLifeCycleBeginningElapsedLocked(js)); 1783 assertEquals(1150L, 1784 mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, 150L)); 1785 assertEquals(0, mFlexibilityController.getCurPercentOfLifecycleLocked(js, FROZEN_TIME)); 1786 assertEquals(650L, mFlexibilityController 1787 .getNextConstraintDropTimeElapsedLocked(js)); 1788 assertEquals(3, js.getNumRequiredFlexibleConstraints()); 1789 assertEquals(1, mFlexibilityController 1790 .mFlexibilityTracker.getJobsByNumRequiredConstraints(3).size()); 1791 } 1792 1793 @Test testScoreCalculation()1794 public void testScoreCalculation() { 1795 JobStatus jsMax = createJobStatus("testScoreCalculation", 1796 createJob(0).setExpedited(true).setPriority(JobInfo.PRIORITY_MAX)); 1797 JobStatus jsHigh = createJobStatus("testScoreCalculation", 1798 createJob(0).setPriority(JobInfo.PRIORITY_HIGH)); 1799 JobStatus jsDefault = createJobStatus("testScoreCalculation", 1800 createJob(0).setPriority(JobInfo.PRIORITY_DEFAULT)); 1801 JobStatus jsLow = createJobStatus("testScoreCalculation", 1802 createJob(0).setPriority(JobInfo.PRIORITY_LOW)); 1803 JobStatus jsMin = createJobStatus("testScoreCalculation", 1804 createJob(0).setPriority(JobInfo.PRIORITY_MIN)); 1805 1806 long nowElapsed = sElapsedRealtimeClock.millis(); 1807 assertEquals(0, 1808 mFlexibilityController.getScoreLocked(mSourceUid, SOURCE_PACKAGE, nowElapsed)); 1809 1810 mFlexibilityController.prepareForExecutionLocked(jsMax); 1811 assertEquals(5, 1812 mFlexibilityController.getScoreLocked(mSourceUid, SOURCE_PACKAGE, nowElapsed)); 1813 1814 advanceElapsedClock(30 * MINUTE_IN_MILLIS); 1815 nowElapsed += 30 * MINUTE_IN_MILLIS; 1816 mFlexibilityController.prepareForExecutionLocked(jsMax); 1817 assertEquals(10, 1818 mFlexibilityController.getScoreLocked(mSourceUid, SOURCE_PACKAGE, nowElapsed)); 1819 1820 advanceElapsedClock(31 * MINUTE_IN_MILLIS); 1821 nowElapsed += 31 * MINUTE_IN_MILLIS; 1822 mFlexibilityController.prepareForExecutionLocked(jsHigh); 1823 assertEquals(14, 1824 mFlexibilityController.getScoreLocked(mSourceUid, SOURCE_PACKAGE, nowElapsed)); 1825 1826 advanceElapsedClock(2 * HOUR_IN_MILLIS); 1827 nowElapsed += 2 * HOUR_IN_MILLIS; 1828 mFlexibilityController.prepareForExecutionLocked(jsDefault); 1829 assertEquals(17, 1830 mFlexibilityController.getScoreLocked(mSourceUid, SOURCE_PACKAGE, nowElapsed)); 1831 1832 advanceElapsedClock(3 * HOUR_IN_MILLIS); 1833 nowElapsed += 3 * HOUR_IN_MILLIS; 1834 mFlexibilityController.prepareForExecutionLocked(jsLow); 1835 assertEquals(19, 1836 mFlexibilityController.getScoreLocked(mSourceUid, SOURCE_PACKAGE, nowElapsed)); 1837 1838 advanceElapsedClock(3 * HOUR_IN_MILLIS); 1839 nowElapsed += 3 * HOUR_IN_MILLIS; 1840 mFlexibilityController.prepareForExecutionLocked(jsMin); 1841 assertEquals(20, 1842 mFlexibilityController.getScoreLocked(mSourceUid, SOURCE_PACKAGE, nowElapsed)); 1843 1844 advanceElapsedClock(3 * HOUR_IN_MILLIS); 1845 nowElapsed += 3 * HOUR_IN_MILLIS; 1846 mFlexibilityController.prepareForExecutionLocked(jsMax); 1847 assertEquals(25, 1848 mFlexibilityController.getScoreLocked(mSourceUid, SOURCE_PACKAGE, nowElapsed)); 1849 mFlexibilityController.unprepareFromExecutionLocked(jsMax); 1850 assertEquals(20, 1851 mFlexibilityController.getScoreLocked(mSourceUid, SOURCE_PACKAGE, nowElapsed)); 1852 1853 // 24 hours haven't passed yet. The jobs in the first hour bucket should still be included. 1854 advanceElapsedClock(12 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS - 1); 1855 nowElapsed += 12 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS - 1; 1856 assertEquals(20, 1857 mFlexibilityController.getScoreLocked(mSourceUid, SOURCE_PACKAGE, nowElapsed)); 1858 1859 // Passed the 24 hour mark. The jobs in the first hour bucket should no longer be included. 1860 advanceElapsedClock(2); 1861 nowElapsed += 2; 1862 assertEquals(10, 1863 mFlexibilityController.getScoreLocked(mSourceUid, SOURCE_PACKAGE, nowElapsed)); 1864 1865 } 1866 1867 /** 1868 * The beginning of a lifecycle for prefetch jobs includes the cached maximum of the last time 1869 * the estimated launch time was updated and the last time the app was opened. 1870 * When the UID bias updates it means the app might have been opened. 1871 * This tests that the cached value is updated properly. 1872 */ 1873 @Test testUidUpdatesLifeCycle()1874 public void testUidUpdatesLifeCycle() { 1875 JobInfo.Builder jb = createJob(0).setPrefetch(true); 1876 JobStatus js = createJobStatus("uidTest", jb); 1877 mFlexibilityController.maybeStartTrackingJobLocked(js, null); 1878 mJobStore.add(js); 1879 1880 final ArraySet<String> pkgs = new ArraySet<>(); 1881 pkgs.add(js.getSourcePackageName()); 1882 doReturn(pkgs).when(mJobSchedulerService).getPackagesForUidLocked(mSourceUid); 1883 1884 setUidBias(mSourceUid, BIAS_TOP_APP); 1885 setUidBias(mSourceUid, BIAS_FOREGROUND_SERVICE); 1886 assertEquals(100L, (long) mFlexibilityController.mPrefetchLifeCycleStart 1887 .getOrDefault(js.getSourceUserId(), js.getSourcePackageName(), 0L)); 1888 1889 JobSchedulerService.sElapsedRealtimeClock = 1890 Clock.fixed(Instant.ofEpochMilli(50L), ZoneOffset.UTC); 1891 1892 setUidBias(mSourceUid, BIAS_TOP_APP); 1893 setUidBias(mSourceUid, BIAS_FOREGROUND_SERVICE); 1894 assertEquals(100L, (long) mFlexibilityController 1895 .mPrefetchLifeCycleStart.get(js.getSourceUserId(), js.getSourcePackageName())); 1896 } 1897 1898 @Test testUnsupportedDevice_Auto()1899 public void testUnsupportedDevice_Auto() { 1900 runTestUnsupportedDevice(PackageManager.FEATURE_AUTOMOTIVE); 1901 } 1902 1903 @Test testUnsupportedDevice_Embedded()1904 public void testUnsupportedDevice_Embedded() { 1905 runTestUnsupportedDevice(PackageManager.FEATURE_EMBEDDED); 1906 } 1907 runTestUnsupportedDevice(String feature)1908 private void runTestUnsupportedDevice(String feature) { 1909 doReturn(true).when(mPackageManager).hasSystemFeature(feature); 1910 mFlexibilityController = 1911 new FlexibilityController(mJobSchedulerService, mPrefetchController); 1912 assertFalse(mFlexibilityController.isEnabled()); 1913 1914 JobStatus js = createJobStatus("testUnsupportedDevice", createJob(0)); 1915 1916 mFlexibilityController.maybeStartTrackingJobLocked(js, null); 1917 assertTrue(js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE)); 1918 1919 setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, FLEXIBLE_CONSTRAINTS); 1920 assertFalse(mFlexibilityController.isEnabled()); 1921 1922 ArrayList<ArraySet<JobStatus>> jobs = 1923 mFlexibilityController.mFlexibilityTracker.getArrayList(); 1924 for (int i = 0; i < jobs.size(); i++) { 1925 assertEquals(0, jobs.get(i).size()); 1926 } 1927 } 1928 setCarrierPrivilegedAppList(int logicalIndex, String... packages)1929 private void setCarrierPrivilegedAppList(int logicalIndex, String... packages) { 1930 final ArraySet<String> packageSet = packages == null 1931 ? new ArraySet<>() : new ArraySet<>(packages); 1932 mCarrierPrivilegedApps.put(logicalIndex, packageSet); 1933 1934 TelephonyManager.CarrierPrivilegesCallback callback = 1935 mCarrierPrivilegedCallbacks.get(logicalIndex); 1936 if (callback != null) { 1937 callback.onCarrierPrivilegesChanged(packageSet, Collections.emptySet()); 1938 waitForQuietModuleThread(); 1939 } 1940 } 1941 setPackageUid(final String pkgName, final int uid)1942 private void setPackageUid(final String pkgName, final int uid) throws Exception { 1943 doReturn(uid).when(mIPackageManager) 1944 .getPackageUid(eq(pkgName), anyLong(), eq(UserHandle.getUserId(uid))); 1945 } 1946 setPowerWhitelistExceptIdle(String... packages)1947 private void setPowerWhitelistExceptIdle(String... packages) { 1948 doReturn(packages == null ? EmptyArray.STRING : packages) 1949 .when(mDeviceIdleInternal).getFullPowerWhitelistExceptIdle(); 1950 if (mBroadcastReceiver != null) { 1951 mBroadcastReceiver.onReceive(mContext, 1952 new Intent(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED)); 1953 waitForQuietModuleThread(); 1954 } 1955 } 1956 setSimSlotMappings(@ullable Collection<UiccSlotMapping> simSlotMapping)1957 private void setSimSlotMappings(@Nullable Collection<UiccSlotMapping> simSlotMapping) { 1958 clearInvocations(mTelephonyManager); 1959 final Collection<UiccSlotMapping> returnedMapping = simSlotMapping == null 1960 ? Collections.emptyList() : simSlotMapping; 1961 doReturn(returnedMapping).when(mTelephonyManager).getSimSlotMapping(); 1962 if (mBroadcastReceiver != null) { 1963 final Intent intent = new Intent(TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED); 1964 mBroadcastReceiver.onReceive(mContext, intent); 1965 waitForQuietModuleThread(); 1966 } 1967 if (returnedMapping.size() > 0) { 1968 ArgumentCaptor<TelephonyManager.CarrierPrivilegesCallback> callbackCaptor = 1969 ArgumentCaptor.forClass(TelephonyManager.CarrierPrivilegesCallback.class); 1970 ArgumentCaptor<Integer> logicalIndexCaptor = ArgumentCaptor.forClass(Integer.class); 1971 1972 final int minExpectedNewRegistrations = Math.max(0, 1973 returnedMapping.size() - mCarrierPrivilegedCallbacks.size()); 1974 verify(mTelephonyManager, atLeast(minExpectedNewRegistrations)) 1975 .registerCarrierPrivilegesCallback( 1976 logicalIndexCaptor.capture(), any(), callbackCaptor.capture()); 1977 1978 final List<Integer> registeredIndices = logicalIndexCaptor.getAllValues(); 1979 final List<TelephonyManager.CarrierPrivilegesCallback> registeredCallbacks = 1980 callbackCaptor.getAllValues(); 1981 for (int i = 0; i < registeredIndices.size(); ++i) { 1982 final int logicalIndex = registeredIndices.get(i); 1983 final TelephonyManager.CarrierPrivilegesCallback callback = 1984 registeredCallbacks.get(i); 1985 1986 mCarrierPrivilegedCallbacks.put(logicalIndex, callback); 1987 1988 // The API contract promises a callback upon registration with the current list. 1989 final ArraySet<String> cpApps = mCarrierPrivilegedApps.get(logicalIndex); 1990 callback.onCarrierPrivilegesChanged( 1991 cpApps == null ? Collections.emptySet() : cpApps, 1992 Collections.emptySet()); 1993 } 1994 waitForQuietModuleThread(); 1995 } 1996 } 1997 setUidBias(int uid, int bias)1998 private void setUidBias(int uid, int bias) { 1999 int prevBias = mJobSchedulerService.getUidBias(uid); 2000 doReturn(bias).when(mJobSchedulerService).getUidBias(uid); 2001 synchronized (mFlexibilityController.mLock) { 2002 mFlexibilityController.onUidBiasChangedLocked(uid, prevBias, bias); 2003 } 2004 } 2005 assertSatisfiedJobsMatchSatisfiedConstraints( ArrayList<ArraySet<JobStatus>> trackedJobs, int satisfiedConstraints)2006 private void assertSatisfiedJobsMatchSatisfiedConstraints( 2007 ArrayList<ArraySet<JobStatus>> trackedJobs, int satisfiedConstraints) { 2008 int numSatisfiedConstraints; 2009 numSatisfiedConstraints = Integer.bitCount(satisfiedConstraints); 2010 for (int i = 0; i < trackedJobs.size(); i++) { 2011 ArraySet<JobStatus> jobs = trackedJobs.get(i); 2012 for (int j = 0; j < jobs.size(); j++) { 2013 JobStatus js = jobs.valueAt(j); 2014 final int transportAffinitySatisfied = js.canApplyTransportAffinities() 2015 && js.areTransportAffinitiesSatisfied() ? 1 : 0; 2016 assertEquals(js.getNumRequiredFlexibleConstraints() 2017 <= numSatisfiedConstraints + transportAffinitySatisfied, 2018 js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE)); 2019 } 2020 } 2021 } 2022 } 2023