1 /* 2 * Copyright (C) 2017 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 android.jobscheduler.cts; 18 19 import static android.app.job.JobInfo.NETWORK_TYPE_ANY; 20 import static android.app.job.JobInfo.NETWORK_TYPE_NONE; 21 import static android.jobscheduler.cts.TestAppInterface.TEST_APP_PACKAGE; 22 import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED; 23 24 import static com.android.compatibility.common.util.TestUtils.waitUntil; 25 26 import static org.junit.Assert.assertEquals; 27 import static org.junit.Assert.assertFalse; 28 import static org.junit.Assert.assertTrue; 29 import static org.junit.Assume.assumeFalse; 30 import static org.junit.Assume.assumeTrue; 31 32 import android.app.AppOpsManager; 33 import android.app.job.JobInfo; 34 import android.app.job.JobParameters; 35 import android.content.BroadcastReceiver; 36 import android.content.Context; 37 import android.content.Intent; 38 import android.content.IntentFilter; 39 import android.content.pm.PackageManager; 40 import android.jobscheduler.cts.jobtestapp.TestJobSchedulerReceiver; 41 import android.os.PowerManager; 42 import android.os.SystemClock; 43 import android.os.Temperature; 44 import android.os.UserHandle; 45 import android.platform.test.annotations.RequiresDevice; 46 import android.provider.DeviceConfig; 47 import android.provider.Settings; 48 import android.support.test.uiautomator.UiDevice; 49 import android.util.Log; 50 51 import androidx.test.InstrumentationRegistry; 52 import androidx.test.filters.LargeTest; 53 import androidx.test.runner.AndroidJUnit4; 54 55 import com.android.compatibility.common.util.AppOpsUtils; 56 import com.android.compatibility.common.util.AppStandbyUtils; 57 import com.android.compatibility.common.util.BatteryUtils; 58 import com.android.compatibility.common.util.DeviceConfigStateHelper; 59 import com.android.compatibility.common.util.ThermalUtils; 60 61 import org.junit.After; 62 import org.junit.Before; 63 import org.junit.Test; 64 import org.junit.runner.RunWith; 65 66 /** 67 * Tests related to job throttling -- device idle, app standby and battery saver. 68 */ 69 @RunWith(AndroidJUnit4.class) 70 @LargeTest 71 public class JobThrottlingTest { 72 private static final String TAG = JobThrottlingTest.class.getSimpleName(); 73 private static final long BACKGROUND_JOBS_EXPECTED_DELAY = 3_000; 74 private static final long POLL_INTERVAL = 500; 75 private static final long DEFAULT_WAIT_TIMEOUT = 5000; 76 private static final long SHELL_TIMEOUT = 3_000; 77 // TODO: mark Settings.System.SCREEN_OFF_TIMEOUT as @TestApi 78 private static final String SCREEN_OFF_TIMEOUT = "screen_off_timeout"; 79 80 enum Bucket { 81 ACTIVE, 82 WORKING_SET, 83 FREQUENT, 84 RARE, 85 RESTRICTED, 86 NEVER 87 } 88 89 private Context mContext; 90 private UiDevice mUiDevice; 91 private NetworkingHelper mNetworkingHelper; 92 private PowerManager mPowerManager; 93 private int mTestJobId; 94 private int mTestPackageUid; 95 private boolean mDeviceInDoze; 96 private boolean mDeviceIdleEnabled; 97 private boolean mAppStandbyEnabled; 98 private String mInitialDisplayTimeout; 99 private String mInitialRestrictedBucketEnabled; 100 private String mInitialBatteryStatsConstants; 101 private boolean mAutomotiveDevice; 102 private boolean mLeanbackOnly; 103 104 private TestAppInterface mTestAppInterface; 105 private DeviceConfigStateHelper mDeviceConfigStateHelper; 106 private DeviceConfigStateHelper mActivityManagerDeviceConfigStateHelper; 107 108 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 109 @Override 110 public void onReceive(Context context, Intent intent) { 111 Log.d(TAG, "Received action " + intent.getAction()); 112 switch (intent.getAction()) { 113 case ACTION_DEVICE_IDLE_MODE_CHANGED: 114 synchronized (JobThrottlingTest.this) { 115 mDeviceInDoze = mPowerManager.isDeviceIdleMode(); 116 Log.d(TAG, "mDeviceInDoze: " + mDeviceInDoze); 117 } 118 break; 119 } 120 } 121 }; 122 isDeviceIdleEnabled(UiDevice uiDevice)123 private static boolean isDeviceIdleEnabled(UiDevice uiDevice) throws Exception { 124 final String output = uiDevice.executeShellCommand("cmd deviceidle enabled deep").trim(); 125 return Integer.parseInt(output) != 0; 126 } 127 128 @Before setUp()129 public void setUp() throws Exception { 130 mContext = InstrumentationRegistry.getTargetContext(); 131 mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 132 mNetworkingHelper = 133 new NetworkingHelper(InstrumentationRegistry.getInstrumentation(), mContext); 134 mPowerManager = mContext.getSystemService(PowerManager.class); 135 mDeviceInDoze = mPowerManager.isDeviceIdleMode(); 136 mTestPackageUid = mContext.getPackageManager().getPackageUid(TEST_APP_PACKAGE, 0); 137 mTestJobId = (int) (SystemClock.uptimeMillis() / 1000); 138 mTestAppInterface = new TestAppInterface(mContext, mTestJobId); 139 final IntentFilter intentFilter = new IntentFilter(); 140 intentFilter.addAction(ACTION_DEVICE_IDLE_MODE_CHANGED); 141 mContext.registerReceiver(mReceiver, intentFilter); 142 assertFalse("Test package already in temp whitelist", isTestAppTempWhitelisted()); 143 makeTestPackageIdle(); 144 mDeviceIdleEnabled = isDeviceIdleEnabled(mUiDevice); 145 mAppStandbyEnabled = AppStandbyUtils.isAppStandbyEnabled(); 146 if (mAppStandbyEnabled) { 147 setTestPackageStandbyBucket(Bucket.ACTIVE); 148 } else { 149 Log.w(TAG, "App standby not enabled on test device"); 150 } 151 mInitialRestrictedBucketEnabled = Settings.Global.getString(mContext.getContentResolver(), 152 Settings.Global.ENABLE_RESTRICTED_BUCKET); 153 mInitialBatteryStatsConstants = Settings.Global.getString(mContext.getContentResolver(), 154 Settings.Global.BATTERY_STATS_CONSTANTS); 155 // Make sure ACTION_CHARGING is sent immediately. 156 Settings.Global.putString(mContext.getContentResolver(), 157 Settings.Global.BATTERY_STATS_CONSTANTS, "battery_charged_delay_ms=0"); 158 // Make sure test jobs can run regardless of bucket. 159 mDeviceConfigStateHelper = 160 new DeviceConfigStateHelper(DeviceConfig.NAMESPACE_JOB_SCHEDULER); 161 mDeviceConfigStateHelper.set( 162 new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER) 163 .setInt("min_ready_non_active_jobs_count", 0).build()); 164 mActivityManagerDeviceConfigStateHelper = 165 new DeviceConfigStateHelper(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER); 166 toggleAutoRestrictedBucketOnBgRestricted(false); 167 // Make sure the screen doesn't turn off when the test turns it on. 168 mInitialDisplayTimeout = 169 Settings.System.getString(mContext.getContentResolver(), SCREEN_OFF_TIMEOUT); 170 Settings.System.putString(mContext.getContentResolver(), SCREEN_OFF_TIMEOUT, "300000"); 171 172 // In automotive device, always-on screen and endless battery charging are assumed. 173 mAutomotiveDevice = 174 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); 175 // In leanback devices, it is assumed that there is no battery. 176 mLeanbackOnly = 177 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY); 178 if (mAutomotiveDevice || mLeanbackOnly) { 179 setScreenState(true); 180 // TODO(b/159176758): make sure that initial power supply is on. 181 setChargingState(true); 182 } 183 184 // Kill as many things in the background as possible so we avoid LMK interfering with the 185 // test. 186 mUiDevice.executeShellCommand("am kill-all"); 187 } 188 189 @Test testAllowWhileIdleJobInTempwhitelist()190 public void testAllowWhileIdleJobInTempwhitelist() throws Exception { 191 assumeTrue("device idle not enabled", mDeviceIdleEnabled); 192 193 toggleDozeState(true); 194 Thread.sleep(DEFAULT_WAIT_TIMEOUT); 195 sendScheduleJobBroadcast(true); 196 assertFalse("Job started without being tempwhitelisted", 197 mTestAppInterface.awaitJobStart(5_000)); 198 tempWhitelistTestApp(5_000); 199 assertTrue("Job with allow_while_idle flag did not start when the app was tempwhitelisted", 200 mTestAppInterface.awaitJobStart(5_000)); 201 } 202 203 @Test testForegroundJobsStartImmediately()204 public void testForegroundJobsStartImmediately() throws Exception { 205 assumeTrue("device idle not enabled", mDeviceIdleEnabled); 206 207 sendScheduleJobBroadcast(false); 208 runJob(); 209 assertTrue("Job did not start after scheduling", 210 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 211 toggleDozeState(true); 212 assertTrue("Job did not stop on entering doze", 213 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 214 Thread.sleep(TestJobSchedulerReceiver.JOB_INITIAL_BACKOFF); 215 // The adb command will force idle even with the screen on, so we need to turn Doze off 216 // explicitly. 217 toggleDozeState(false); 218 // Turn the screen on to ensure the test app ends up in TOP. 219 setScreenState(true); 220 mTestAppInterface.startAndKeepTestActivity(); 221 assertTrue("Job for foreground app did not start immediately when device exited doze", 222 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 223 } 224 225 @Test testBackgroundJobsDelayed()226 public void testBackgroundJobsDelayed() throws Exception { 227 assumeTrue("device idle not enabled", mDeviceIdleEnabled); 228 229 sendScheduleJobBroadcast(false); 230 runJob(); 231 assertTrue("Job did not start after scheduling", 232 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 233 toggleDozeState(true); 234 assertTrue("Job did not stop on entering doze", 235 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 236 Thread.sleep(TestJobSchedulerReceiver.JOB_INITIAL_BACKOFF); 237 toggleDozeState(false); 238 assertFalse("Job for background app started immediately when device exited doze", 239 mTestAppInterface.awaitJobStart(2000)); 240 Thread.sleep(BACKGROUND_JOBS_EXPECTED_DELAY - 2000); 241 assertTrue("Job for background app did not start after the expected delay of " 242 + BACKGROUND_JOBS_EXPECTED_DELAY + "ms", 243 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 244 } 245 246 @Test testJobStoppedWhenRestricted()247 public void testJobStoppedWhenRestricted() throws Exception { 248 sendScheduleJobBroadcast(false); 249 runJob(); 250 assertTrue("Job did not start after scheduling", 251 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 252 toggleAutoRestrictedBucketOnBgRestricted(true); 253 setTestPackageRestricted(true); 254 assertFalse("Job stopped after test app was restricted with auto-restricted-bucket on", 255 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 256 toggleAutoRestrictedBucketOnBgRestricted(false); 257 assertTrue("Job did not stop after test app was restricted", 258 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 259 assertEquals(JobParameters.STOP_REASON_BACKGROUND_RESTRICTION, 260 mTestAppInterface.getLastParams().getStopReason()); 261 } 262 263 @Test testRestrictedJobStartedWhenUnrestricted()264 public void testRestrictedJobStartedWhenUnrestricted() throws Exception { 265 setTestPackageRestricted(true); 266 sendScheduleJobBroadcast(false); 267 assertFalse("Job started for restricted app", 268 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 269 setTestPackageRestricted(false); 270 assertTrue("Job did not start when app was unrestricted", 271 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 272 } 273 274 @Test testRestrictedJobAllowedWhenUidActive()275 public void testRestrictedJobAllowedWhenUidActive() throws Exception { 276 setTestPackageRestricted(true); 277 sendScheduleJobBroadcast(false); 278 assertFalse("Job started for restricted app", 279 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 280 // Turn the screen on to ensure the app gets into the TOP state. 281 setScreenState(true); 282 mTestAppInterface.startAndKeepTestActivity(true); 283 assertTrue("Job did not start when app had an activity", 284 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 285 286 mTestAppInterface.closeActivity(); 287 // Don't put full minute as the timeout to give some leeway with test timing/processing. 288 assertFalse("Job stopped within grace period after activity closed", 289 mTestAppInterface.awaitJobStop(55_000L)); 290 assertTrue("Job did not stop after grace period ended", 291 mTestAppInterface.awaitJobStop(15_000L)); 292 assertEquals(JobParameters.STOP_REASON_BACKGROUND_RESTRICTION, 293 mTestAppInterface.getLastParams().getStopReason()); 294 } 295 296 @Test testRestrictedJobAllowedWhenAutoRestrictedBucketFeatureOn()297 public void testRestrictedJobAllowedWhenAutoRestrictedBucketFeatureOn() throws Exception { 298 setTestPackageRestricted(true); 299 sendScheduleJobBroadcast(false); 300 assertFalse("Job started for restricted app", 301 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 302 toggleAutoRestrictedBucketOnBgRestricted(true); 303 assertTrue("Job did not start after scheduling", 304 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 305 } 306 307 @Test testEJStoppedWhenRestricted()308 public void testEJStoppedWhenRestricted() throws Exception { 309 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true); 310 runJob(); 311 assertTrue("Job did not start after scheduling", 312 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 313 toggleAutoRestrictedBucketOnBgRestricted(true); 314 setTestPackageRestricted(true); 315 assertFalse("Job stopped after test app was restricted with auto-restricted-bucket on", 316 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 317 toggleAutoRestrictedBucketOnBgRestricted(false); 318 assertTrue("Job did not stop after test app was restricted", 319 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 320 assertEquals(JobParameters.STOP_REASON_BACKGROUND_RESTRICTION, 321 mTestAppInterface.getLastParams().getStopReason()); 322 } 323 324 @Test testRestrictedEJStartedWhenUnrestricted()325 public void testRestrictedEJStartedWhenUnrestricted() throws Exception { 326 setTestPackageRestricted(true); 327 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true); 328 assertFalse("Job started for restricted app", 329 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 330 setTestPackageRestricted(false); 331 assertTrue("Job did not start when app was unrestricted", 332 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 333 } 334 335 @Test testRestrictedEJAllowedWhenUidActive()336 public void testRestrictedEJAllowedWhenUidActive() throws Exception { 337 setTestPackageRestricted(true); 338 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true); 339 assertFalse("Job started for restricted app", 340 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 341 // Turn the screen on to ensure the app gets into the TOP state. 342 setScreenState(true); 343 mTestAppInterface.startAndKeepTestActivity(true); 344 assertTrue("Job did not start when app had an activity", 345 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 346 347 mTestAppInterface.closeActivity(); 348 // Don't put full minute as the timeout to give some leeway with test timing/processing. 349 assertFalse("Job stopped within grace period after activity closed", 350 mTestAppInterface.awaitJobStop(55_000L)); 351 assertTrue("Job did not stop after grace period ended", 352 mTestAppInterface.awaitJobStop(15_000L)); 353 assertEquals(JobParameters.STOP_REASON_BACKGROUND_RESTRICTION, 354 mTestAppInterface.getLastParams().getStopReason()); 355 } 356 357 @Test testRestrictedEJAllowedWhenAutoRestrictedBucketFeatureOn()358 public void testRestrictedEJAllowedWhenAutoRestrictedBucketFeatureOn() throws Exception { 359 setTestPackageRestricted(true); 360 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true); 361 assertFalse("Job started for restricted app", 362 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 363 364 toggleAutoRestrictedBucketOnBgRestricted(true); 365 assertTrue("Job did not start when app was background unrestricted", 366 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 367 } 368 369 @Test testBackgroundRegJobsThermal()370 public void testBackgroundRegJobsThermal() throws Exception { 371 mTestAppInterface.scheduleJob(false, NETWORK_TYPE_NONE, false); 372 runJob(); 373 assertTrue("Job did not start after scheduling", 374 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 375 376 ThermalUtils.overrideThermalStatus(Temperature.THROTTLING_LIGHT); 377 assertFalse("Job stopped below thermal throttling threshold", 378 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 379 380 ThermalUtils.overrideThermalStatus(Temperature.THROTTLING_SEVERE); 381 assertTrue("Job did not stop on thermal throttling", 382 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 383 final long jobStopTime = System.currentTimeMillis(); 384 385 ThermalUtils.overrideThermalStatus(Temperature.THROTTLING_CRITICAL); 386 runJob(); 387 assertFalse("Job started above thermal throttling threshold", 388 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 389 390 ThermalUtils.overrideThermalStatus(Temperature.THROTTLING_EMERGENCY); 391 runJob(); 392 assertFalse("Job started above thermal throttling threshold", 393 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 394 395 Thread.sleep(Math.max(0, TestJobSchedulerReceiver.JOB_INITIAL_BACKOFF 396 - (System.currentTimeMillis() - jobStopTime))); 397 ThermalUtils.overrideThermalNotThrottling(); 398 runJob(); 399 assertTrue("Job did not start back from throttling", 400 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 401 } 402 403 @Test testBackgroundEJsThermal()404 public void testBackgroundEJsThermal() throws Exception { 405 mTestAppInterface.scheduleJob(false, NETWORK_TYPE_NONE, true); 406 runJob(); 407 assertTrue("Job did not start after scheduling", 408 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 409 410 ThermalUtils.overrideThermalStatus(Temperature.THROTTLING_MODERATE); 411 assertFalse("Job stopped below thermal throttling threshold", 412 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 413 414 ThermalUtils.overrideThermalStatus(Temperature.THROTTLING_SEVERE); 415 assertTrue("Job did not stop on thermal throttling", 416 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 417 final long jobStopTime = System.currentTimeMillis(); 418 419 ThermalUtils.overrideThermalStatus(Temperature.THROTTLING_CRITICAL); 420 runJob(); 421 assertFalse("Job started above thermal throttling threshold", 422 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 423 424 ThermalUtils.overrideThermalStatus(Temperature.THROTTLING_EMERGENCY); 425 runJob(); 426 assertFalse("Job started above thermal throttling threshold", 427 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 428 429 Thread.sleep(Math.max(0, TestJobSchedulerReceiver.JOB_INITIAL_BACKOFF 430 - (System.currentTimeMillis() - jobStopTime))); 431 ThermalUtils.overrideThermalNotThrottling(); 432 runJob(); 433 assertTrue("Job did not start back from throttling", 434 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 435 } 436 437 @Test testForegroundJobsThermal()438 public void testForegroundJobsThermal() throws Exception { 439 // Turn the screen on to ensure the app gets into the TOP state. 440 setScreenState(true); 441 mTestAppInterface.startAndKeepTestActivity(true); 442 mTestAppInterface.scheduleJob(false, NETWORK_TYPE_NONE, false); 443 runJob(); 444 assertTrue("Job did not start after scheduling", 445 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 446 447 ThermalUtils.overrideThermalStatus(Temperature.THROTTLING_MODERATE); 448 assertFalse("Job stopped below thermal throttling threshold", 449 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 450 451 ThermalUtils.overrideThermalStatus(Temperature.THROTTLING_SEVERE); 452 assertFalse("Job stopped despite being TOP app", 453 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 454 455 ThermalUtils.overrideThermalStatus(Temperature.THROTTLING_CRITICAL); 456 assertFalse("Job stopped despite being TOP app", 457 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 458 } 459 460 /** Tests that apps in the RESTRICTED bucket still get their one parole session per day. */ 461 @Test testJobsInRestrictedBucket_ParoleSession()462 public void testJobsInRestrictedBucket_ParoleSession() throws Exception { 463 assumeTrue("app standby not enabled", mAppStandbyEnabled); 464 assumeFalse("not testable in automotive device", mAutomotiveDevice); 465 assumeFalse("not testable in leanback device", mLeanbackOnly); 466 467 setRestrictedBucketEnabled(true); 468 469 // Disable coalescing 470 mDeviceConfigStateHelper.set("qc_timing_session_coalescing_duration_ms", "0"); 471 472 setScreenState(true); 473 474 setChargingState(false); 475 setTestPackageStandbyBucket(Bucket.RESTRICTED); 476 Thread.sleep(DEFAULT_WAIT_TIMEOUT); 477 sendScheduleJobBroadcast(false); 478 runJob(); 479 assertTrue("Parole job didn't start in RESTRICTED bucket", 480 mTestAppInterface.awaitJobStart(3_000)); 481 482 sendScheduleJobBroadcast(false); 483 assertFalse("New job started in RESTRICTED bucket", mTestAppInterface.awaitJobStart(3_000)); 484 } 485 486 /** 487 * Tests that apps in the RESTRICTED bucket have their parole sessions properly counted even 488 * when charging (but not idle). 489 */ 490 @Test testJobsInRestrictedBucket_CorrectParoleWhileCharging()491 public void testJobsInRestrictedBucket_CorrectParoleWhileCharging() throws Exception { 492 assumeTrue("app standby not enabled", mAppStandbyEnabled); 493 assumeFalse("not testable in automotive device", mAutomotiveDevice); 494 assumeFalse("not testable in leanback device", mLeanbackOnly); 495 496 setRestrictedBucketEnabled(true); 497 498 // Disable coalescing 499 mDeviceConfigStateHelper.set("qc_timing_session_coalescing_duration_ms", "0"); 500 mDeviceConfigStateHelper.set("qc_max_session_count_restricted", "1"); 501 502 setScreenState(true); 503 setChargingState(true); 504 BatteryUtils.runDumpsysBatterySetLevel(100); 505 506 setTestPackageStandbyBucket(Bucket.RESTRICTED); 507 Thread.sleep(DEFAULT_WAIT_TIMEOUT); 508 sendScheduleJobBroadcast(false); 509 runJob(); 510 assertTrue("Parole job didn't start in RESTRICTED bucket", 511 mTestAppInterface.awaitJobStart(3_000)); 512 513 sendScheduleJobBroadcast(false); 514 assertFalse("New job started in RESTRICTED bucket after parole used", 515 mTestAppInterface.awaitJobStart(3_000)); 516 } 517 518 /** 519 * Tests that apps in the RESTRICTED bucket that have used their one parole session per day 520 * don't get to run again until the device is charging + idle. 521 */ 522 @Test testJobsInRestrictedBucket_DeferredUntilFreeResources()523 public void testJobsInRestrictedBucket_DeferredUntilFreeResources() throws Exception { 524 assumeTrue("app standby not enabled", mAppStandbyEnabled); 525 assumeFalse("not testable in automotive device", mAutomotiveDevice); 526 assumeFalse("not testable in leanback device", mLeanbackOnly); 527 528 setRestrictedBucketEnabled(true); 529 530 // Disable coalescing 531 mDeviceConfigStateHelper.set("qc_timing_session_coalescing_duration_ms", "0"); 532 533 setScreenState(true); 534 535 setChargingState(false); 536 setTestPackageStandbyBucket(Bucket.RESTRICTED); 537 Thread.sleep(DEFAULT_WAIT_TIMEOUT); 538 sendScheduleJobBroadcast(false); 539 runJob(); 540 assertTrue("Parole job didn't start in RESTRICTED bucket", 541 mTestAppInterface.awaitJobStart(3_000)); 542 543 sendScheduleJobBroadcast(false); 544 assertFalse("New job started in RESTRICTED bucket after parole used", 545 mTestAppInterface.awaitJobStart(3_000)); 546 547 setChargingState(true); 548 BatteryUtils.runDumpsysBatterySetLevel(100); 549 assertFalse("New job started in RESTRICTED bucket after parole when charging but not idle", 550 mTestAppInterface.awaitJobStart(3_000)); 551 552 setScreenState(false); 553 triggerJobIdle(); 554 assertTrue("Job didn't start in RESTRICTED bucket when charging + idle", 555 mTestAppInterface.awaitJobStart(3_000)); 556 557 // Make sure job can be stopped and started again when charging + idle 558 sendScheduleJobBroadcast(false); 559 runJob(); 560 assertTrue("Job didn't restart in RESTRICTED bucket when charging + idle", 561 mTestAppInterface.awaitJobStart(3_000)); 562 } 563 564 @Test testJobsInRestrictedBucket_NoRequiredNetwork()565 public void testJobsInRestrictedBucket_NoRequiredNetwork() throws Exception { 566 assumeTrue("app standby not enabled", mAppStandbyEnabled); 567 assumeFalse("not testable in automotive device", mAutomotiveDevice); 568 assumeFalse("not testable in leanback device", mLeanbackOnly); 569 570 setRestrictedBucketEnabled(true); 571 572 // Disable coalescing and the parole session 573 mDeviceConfigStateHelper.set("qc_timing_session_coalescing_duration_ms", "0"); 574 mDeviceConfigStateHelper.set("qc_max_session_count_restricted", "0"); 575 576 mNetworkingHelper.setAllNetworksEnabled(false); 577 setScreenState(true); 578 579 setChargingState(false); 580 setTestPackageStandbyBucket(Bucket.RESTRICTED); 581 Thread.sleep(DEFAULT_WAIT_TIMEOUT); 582 mTestAppInterface.scheduleJob(false, NETWORK_TYPE_NONE, false); 583 assertFalse("New job started in RESTRICTED bucket", mTestAppInterface.awaitJobStart(3_000)); 584 585 // Slowly add back required bucket constraints. 586 587 // Battery charging and high. 588 setChargingState(true); 589 assertFalse("New job started in RESTRICTED bucket", mTestAppInterface.awaitJobStart(3_000)); 590 BatteryUtils.runDumpsysBatterySetLevel(100); 591 assertFalse("New job started in RESTRICTED bucket", mTestAppInterface.awaitJobStart(3_000)); 592 593 // Device is idle. 594 setScreenState(false); 595 assertFalse("New job started in RESTRICTED bucket", mTestAppInterface.awaitJobStart(3_000)); 596 triggerJobIdle(); 597 assertTrue("New job didn't start in RESTRICTED bucket", 598 mTestAppInterface.awaitJobStart(3_000)); 599 } 600 601 @RequiresDevice // Emulators don't always have access to wifi/network 602 @Test testJobsInRestrictedBucket_WithRequiredNetwork()603 public void testJobsInRestrictedBucket_WithRequiredNetwork() throws Exception { 604 assumeTrue("app standby not enabled", mAppStandbyEnabled); 605 assumeFalse("not testable in automotive device", mAutomotiveDevice); 606 assumeFalse("not testable in leanback device", mLeanbackOnly); 607 assumeFalse("not testable, since ethernet is connected", hasEthernetConnection()); 608 assumeTrue(mNetworkingHelper.hasWifiFeature()); 609 mNetworkingHelper.ensureSavedWifiNetwork(); 610 611 setRestrictedBucketEnabled(true); 612 613 // Disable coalescing and the parole session 614 mDeviceConfigStateHelper.set("qc_timing_session_coalescing_duration_ms", "0"); 615 mDeviceConfigStateHelper.set("qc_max_session_count_restricted", "0"); 616 617 mNetworkingHelper.setAllNetworksEnabled(false); 618 setScreenState(true); 619 620 setChargingState(false); 621 setTestPackageStandbyBucket(Bucket.RESTRICTED); 622 Thread.sleep(DEFAULT_WAIT_TIMEOUT); 623 mTestAppInterface.scheduleJob(false, NETWORK_TYPE_ANY, false); 624 runJob(); 625 assertFalse("New job started in RESTRICTED bucket", mTestAppInterface.awaitJobStart(3_000)); 626 627 // Slowly add back required bucket constraints. 628 629 // Battery charging and high. 630 setChargingState(true); 631 runJob(); 632 assertFalse("New job started in RESTRICTED bucket", mTestAppInterface.awaitJobStart(3_000)); 633 BatteryUtils.runDumpsysBatterySetLevel(100); 634 runJob(); 635 assertFalse("New job started in RESTRICTED bucket", mTestAppInterface.awaitJobStart(3_000)); 636 637 // Device is idle. 638 setScreenState(false); 639 runJob(); 640 assertFalse("New job started in RESTRICTED bucket", mTestAppInterface.awaitJobStart(3_000)); 641 triggerJobIdle(); 642 runJob(); 643 assertFalse("New job started in RESTRICTED bucket", mTestAppInterface.awaitJobStart(3_000)); 644 645 // Add network 646 mNetworkingHelper.setAllNetworksEnabled(true); 647 mNetworkingHelper.setWifiMeteredState(false); 648 runJob(); 649 assertTrue("New job didn't start in RESTRICTED bucket", 650 mTestAppInterface.awaitJobStart(5_000)); 651 } 652 653 @Test testJobsInNeverApp()654 public void testJobsInNeverApp() throws Exception { 655 assumeTrue("app standby not enabled", mAppStandbyEnabled); 656 assumeFalse("not testable in automotive device", mAutomotiveDevice); 657 assumeFalse("not testable in leanback device", mLeanbackOnly); 658 659 setChargingState(false); 660 setTestPackageStandbyBucket(Bucket.NEVER); 661 Thread.sleep(DEFAULT_WAIT_TIMEOUT); 662 sendScheduleJobBroadcast(false); 663 assertFalse("New job started in NEVER bucket", mTestAppInterface.awaitJobStart(3_000)); 664 } 665 666 @Test testUidActiveBypassesStandby()667 public void testUidActiveBypassesStandby() throws Exception { 668 assumeFalse("not testable in automotive device", mAutomotiveDevice); 669 assumeFalse("not testable in leanback device", mLeanbackOnly); 670 671 setChargingState(false); 672 setTestPackageStandbyBucket(Bucket.NEVER); 673 tempWhitelistTestApp(6_000); 674 Thread.sleep(DEFAULT_WAIT_TIMEOUT); 675 sendScheduleJobBroadcast(false); 676 assertTrue("New job in uid-active app failed to start in NEVER standby", 677 mTestAppInterface.awaitJobStart(4_000)); 678 } 679 680 @Test testBatterySaverOff()681 public void testBatterySaverOff() throws Exception { 682 assumeFalse("not testable in automotive device", mAutomotiveDevice); 683 assumeFalse("not testable in leanback device", mLeanbackOnly); 684 685 BatteryUtils.assumeBatterySaverFeature(); 686 687 setChargingState(false); 688 BatteryUtils.enableBatterySaver(false); 689 sendScheduleJobBroadcast(false); 690 assertTrue("New job failed to start with battery saver OFF", 691 mTestAppInterface.awaitJobStart(3_000)); 692 } 693 694 @Test testBatterySaverOn()695 public void testBatterySaverOn() throws Exception { 696 assumeFalse("not testable in automotive device", mAutomotiveDevice); 697 assumeFalse("not testable in leanback device", mLeanbackOnly); 698 699 BatteryUtils.assumeBatterySaverFeature(); 700 701 setChargingState(false); 702 BatteryUtils.enableBatterySaver(true); 703 sendScheduleJobBroadcast(false); 704 assertFalse("New job started with battery saver ON", 705 mTestAppInterface.awaitJobStart(3_000)); 706 } 707 708 @Test testUidActiveBypassesBatterySaverOn()709 public void testUidActiveBypassesBatterySaverOn() throws Exception { 710 assumeFalse("not testable in automotive device", mAutomotiveDevice); 711 assumeFalse("not testable in leanback device", mLeanbackOnly); 712 713 BatteryUtils.assumeBatterySaverFeature(); 714 715 setChargingState(false); 716 BatteryUtils.enableBatterySaver(true); 717 tempWhitelistTestApp(6_000); 718 sendScheduleJobBroadcast(false); 719 assertTrue("New job in uid-active app failed to start with battery saver ON", 720 mTestAppInterface.awaitJobStart(3_000)); 721 } 722 723 @Test testBatterySaverOnThenUidActive()724 public void testBatterySaverOnThenUidActive() throws Exception { 725 assumeFalse("not testable in automotive device", mAutomotiveDevice); 726 assumeFalse("not testable in leanback device", mLeanbackOnly); 727 728 BatteryUtils.assumeBatterySaverFeature(); 729 730 // Enable battery saver, and schedule a job. It shouldn't run. 731 setChargingState(false); 732 BatteryUtils.enableBatterySaver(true); 733 sendScheduleJobBroadcast(false); 734 assertFalse("New job started with battery saver ON", 735 mTestAppInterface.awaitJobStart(3_000)); 736 737 // Then make the UID active. Now the job should run. 738 tempWhitelistTestApp(120_000); 739 assertTrue("New job in uid-active app failed to start with battery saver OFF", 740 mTestAppInterface.awaitJobStart(120_000)); 741 } 742 743 @Test testExpeditedJobBypassesBatterySaverOn()744 public void testExpeditedJobBypassesBatterySaverOn() throws Exception { 745 assumeFalse("not testable in automotive device", mAutomotiveDevice); 746 assumeFalse("not testable in leanback device", mLeanbackOnly); 747 748 BatteryUtils.assumeBatterySaverFeature(); 749 750 setChargingState(false); 751 BatteryUtils.enableBatterySaver(true); 752 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true); 753 assertTrue("New expedited job failed to start with battery saver ON", 754 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 755 } 756 757 @Test testExpeditedJobBypassesBatterySaver_toggling()758 public void testExpeditedJobBypassesBatterySaver_toggling() throws Exception { 759 assumeFalse("not testable in automotive device", mAutomotiveDevice); 760 assumeFalse("not testable in leanback device", mLeanbackOnly); 761 762 BatteryUtils.assumeBatterySaverFeature(); 763 764 setChargingState(false); 765 BatteryUtils.enableBatterySaver(false); 766 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true); 767 assertTrue("New expedited job failed to start with battery saver ON", 768 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 769 BatteryUtils.enableBatterySaver(true); 770 assertFalse("Job stopped when battery saver turned on", 771 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 772 } 773 774 @Test testExpeditedJobBypassesDeviceIdle()775 public void testExpeditedJobBypassesDeviceIdle() throws Exception { 776 assumeTrue("device idle not enabled", mDeviceIdleEnabled); 777 778 toggleDozeState(true); 779 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true); 780 runJob(); 781 assertTrue("Job did not start after scheduling", 782 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 783 } 784 785 @Test testExpeditedJobBypassesDeviceIdle_toggling()786 public void testExpeditedJobBypassesDeviceIdle_toggling() throws Exception { 787 assumeTrue("device idle not enabled", mDeviceIdleEnabled); 788 789 toggleDozeState(false); 790 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true); 791 runJob(); 792 assertTrue("Job did not start after scheduling", 793 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 794 toggleDozeState(true); 795 assertFalse("Job stopped when device enabled turned on", 796 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 797 } 798 799 @Test testExpeditedJobDeferredAfterTimeoutInDoze()800 public void testExpeditedJobDeferredAfterTimeoutInDoze() throws Exception { 801 assumeTrue("device idle not enabled", mDeviceIdleEnabled); 802 // Intentionally set a value below 1 minute to ensure the range checks work. 803 mDeviceConfigStateHelper.set("runtime_min_ej_guarantee_ms", Long.toString(30_000L)); 804 805 toggleDozeState(true); 806 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true); 807 runJob(); 808 assertTrue("Job did not start after scheduling", 809 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 810 // Don't put full minute as the timeout to give some leeway with test timing/processing. 811 assertFalse("Job stopped before min runtime limit", 812 mTestAppInterface.awaitJobStop(55_000L)); 813 assertTrue("Job did not stop after timeout", mTestAppInterface.awaitJobStop(15_000L)); 814 assertEquals(JobParameters.STOP_REASON_DEVICE_STATE, 815 mTestAppInterface.getLastParams().getStopReason()); 816 // Should be rescheduled. 817 assertJobNotReady(); 818 assertJobWaiting(); 819 Thread.sleep(TestJobSchedulerReceiver.JOB_INITIAL_BACKOFF); 820 runJob(); 821 assertFalse("Job started after timing out in Doze", 822 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 823 824 // Should start when Doze is turned off. 825 toggleDozeState(false); 826 assertTrue("Job did not start after Doze turned off", 827 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 828 } 829 830 @Test testExpeditedJobDeferredAfterTimeoutInBatterySaver()831 public void testExpeditedJobDeferredAfterTimeoutInBatterySaver() throws Exception { 832 BatteryUtils.assumeBatterySaverFeature(); 833 834 // Intentionally set a value below 1 minute to ensure the range checks work. 835 mDeviceConfigStateHelper.set("runtime_min_ej_guarantee_ms", Long.toString(47_000L)); 836 837 setChargingState(false); 838 BatteryUtils.enableBatterySaver(true); 839 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true); 840 runJob(); 841 assertTrue("Job did not start after scheduling", 842 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 843 // Don't put full minute as the timeout to give some leeway with test timing/processing. 844 assertFalse("Job stopped before min runtime limit", 845 mTestAppInterface.awaitJobStop(55_000L)); 846 assertTrue("Job did not stop after timeout", mTestAppInterface.awaitJobStop(15_000L)); 847 assertEquals(JobParameters.STOP_REASON_DEVICE_STATE, 848 mTestAppInterface.getLastParams().getStopReason()); 849 // Should be rescheduled. 850 assertJobNotReady(); 851 assertJobWaiting(); 852 Thread.sleep(TestJobSchedulerReceiver.JOB_INITIAL_BACKOFF); 853 runJob(); 854 assertFalse("Job started after timing out in battery saver", 855 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 856 857 // Should start when battery saver is turned off. 858 BatteryUtils.enableBatterySaver(false); 859 assertTrue("Job did not start after battery saver turned off", 860 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 861 } 862 863 @Test testExpeditedJobDeferredAfterTimeout_DozeAndBatterySaver()864 public void testExpeditedJobDeferredAfterTimeout_DozeAndBatterySaver() throws Exception { 865 BatteryUtils.assumeBatterySaverFeature(); 866 assumeTrue("device idle not enabled", mDeviceIdleEnabled); 867 mDeviceConfigStateHelper.set("runtime_min_ej_guarantee_ms", Long.toString(60_000L)); 868 869 setChargingState(false); 870 toggleDozeState(true); 871 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true); 872 runJob(); 873 assertTrue("Job did not start after scheduling", 874 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 875 // Don't put full minute as the timeout to give some leeway with test timing/processing. 876 assertFalse("Job stopped before min runtime limit", 877 mTestAppInterface.awaitJobStop(55_000L)); 878 assertTrue("Job did not stop after timeout", mTestAppInterface.awaitJobStop(15_000L)); 879 assertEquals(JobParameters.STOP_REASON_DEVICE_STATE, 880 mTestAppInterface.getLastParams().getStopReason()); 881 // Should be rescheduled. 882 assertJobNotReady(); 883 assertJobWaiting(); 884 // Battery saver kicks in before Doze ends. Job shouldn't start while BS is on. 885 BatteryUtils.enableBatterySaver(true); 886 toggleDozeState(false); 887 Thread.sleep(TestJobSchedulerReceiver.JOB_INITIAL_BACKOFF); 888 runJob(); 889 assertFalse("Job started while power restrictions active after timing out", 890 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 891 892 // Should start when battery saver is turned off. 893 BatteryUtils.enableBatterySaver(false); 894 assertTrue("Job did not start after power restrictions turned off", 895 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 896 } 897 898 @Test testLongExpeditedJobStoppedByDoze()899 public void testLongExpeditedJobStoppedByDoze() throws Exception { 900 assumeTrue("device idle not enabled", mDeviceIdleEnabled); 901 // Intentionally set a value below 1 minute to ensure the range checks work. 902 mDeviceConfigStateHelper.set("runtime_min_ej_guarantee_ms", Long.toString(59_000L)); 903 904 toggleDozeState(false); 905 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true); 906 runJob(); 907 assertTrue("Job did not start after scheduling", 908 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 909 // Should get to run past min runtime. 910 assertFalse("Job stopped after min runtime", mTestAppInterface.awaitJobStop(90_000L)); 911 912 // Should stop when Doze is turned on. 913 toggleDozeState(true); 914 assertTrue("Job did not stop after Doze turned on", 915 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 916 assertEquals(JobParameters.STOP_REASON_DEVICE_STATE, 917 mTestAppInterface.getLastParams().getStopReason()); 918 } 919 920 @Test testLongExpeditedJobStoppedByBatterySaver()921 public void testLongExpeditedJobStoppedByBatterySaver() throws Exception { 922 BatteryUtils.assumeBatterySaverFeature(); 923 924 // Intentionally set a value below 1 minute to ensure the range checks work. 925 mDeviceConfigStateHelper.set("runtime_min_ej_guarantee_ms", Long.toString(0L)); 926 927 setChargingState(false); 928 BatteryUtils.enableBatterySaver(false); 929 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true); 930 runJob(); 931 assertTrue("Job did not start after scheduling", 932 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 933 // Should get to run past min runtime. 934 assertFalse("Job stopped after runtime", mTestAppInterface.awaitJobStop(90_000L)); 935 936 // Should stop when battery saver is turned on. 937 BatteryUtils.enableBatterySaver(true); 938 assertTrue("Job did not stop after battery saver turned on", 939 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 940 assertEquals(JobParameters.STOP_REASON_DEVICE_STATE, 941 mTestAppInterface.getLastParams().getStopReason()); 942 } 943 944 @Test testRestrictingStopReason_RestrictedBucket()945 public void testRestrictingStopReason_RestrictedBucket() throws Exception { 946 assumeTrue("app standby not enabled", mAppStandbyEnabled); 947 assumeFalse("not testable in automotive device", mAutomotiveDevice); 948 assumeFalse("not testable in leanback device", mLeanbackOnly); 949 950 assumeTrue(BatteryUtils.hasBattery()); 951 assumeTrue(mNetworkingHelper.hasWifiFeature()); 952 mNetworkingHelper.ensureSavedWifiNetwork(); 953 954 setRestrictedBucketEnabled(true); 955 setTestPackageStandbyBucket(Bucket.RESTRICTED); 956 957 // Disable coalescing and the parole session 958 mDeviceConfigStateHelper.set("qc_timing_session_coalescing_duration_ms", "0"); 959 mDeviceConfigStateHelper.set("qc_max_session_count_restricted", "0"); 960 961 // Satisfy all additional constraints. 962 mNetworkingHelper.setAllNetworksEnabled(true); 963 mNetworkingHelper.setWifiMeteredState(false); 964 setChargingState(true); 965 BatteryUtils.runDumpsysBatterySetLevel(100); 966 setScreenState(false); 967 triggerJobIdle(); 968 969 // Toggle individual constraints 970 971 // Connectivity 972 mTestAppInterface.scheduleJob(false, NETWORK_TYPE_ANY, false); 973 runJob(); 974 assertTrue("New job didn't start in RESTRICTED bucket", 975 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 976 mNetworkingHelper.setAllNetworksEnabled(false); 977 assertTrue("New job didn't stop when connectivity dropped", 978 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 979 assertEquals(JobParameters.STOP_REASON_CONSTRAINT_CONNECTIVITY, 980 mTestAppInterface.getLastParams().getStopReason()); 981 mNetworkingHelper.setAllNetworksEnabled(true); 982 983 // Idle 984 mTestAppInterface.scheduleJob(false, NETWORK_TYPE_ANY, false); 985 runJob(); 986 assertTrue("New job didn't start in RESTRICTED bucket", 987 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 988 setScreenState(true); 989 assertTrue("New job didn't stop when device no longer idle", 990 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 991 assertEquals(JobParameters.STOP_REASON_APP_STANDBY, 992 mTestAppInterface.getLastParams().getStopReason()); 993 setScreenState(false); 994 triggerJobIdle(); 995 996 // Charging 997 mTestAppInterface.scheduleJob(false, NETWORK_TYPE_ANY, false); 998 runJob(); 999 assertTrue("New job didn't start in RESTRICTED bucket", 1000 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 1001 setChargingState(false); 1002 assertTrue("New job didn't stop when device no longer charging", 1003 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 1004 assertEquals(JobParameters.STOP_REASON_APP_STANDBY, 1005 mTestAppInterface.getLastParams().getStopReason()); 1006 setChargingState(true); 1007 BatteryUtils.runDumpsysBatterySetLevel(100); 1008 1009 // Battery not low 1010 setScreenState(false); 1011 triggerJobIdle(); 1012 mTestAppInterface.scheduleJob(false, NETWORK_TYPE_ANY, false); 1013 runJob(); 1014 assertTrue("New job didn't start in RESTRICTED bucket", 1015 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 1016 BatteryUtils.runDumpsysBatterySetLevel(1); 1017 assertTrue("New job didn't stop when battery too low", 1018 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 1019 assertEquals(JobParameters.STOP_REASON_APP_STANDBY, 1020 mTestAppInterface.getLastParams().getStopReason()); 1021 } 1022 1023 @Test testRestrictingStopReason_Quota()1024 public void testRestrictingStopReason_Quota() throws Exception { 1025 assumeTrue("app standby not enabled", mAppStandbyEnabled); 1026 assumeFalse("not testable in automotive device", mAutomotiveDevice); // Test needs battery 1027 assumeFalse("not testable in leanback device", mLeanbackOnly); // Test needs battery 1028 1029 // Reduce allowed time for testing. 1030 mDeviceConfigStateHelper.set("qc_allowed_time_per_period_rare_ms", "60000"); 1031 setChargingState(false); 1032 setTestPackageStandbyBucket(Bucket.RARE); 1033 1034 sendScheduleJobBroadcast(false); 1035 runJob(); 1036 assertTrue("New job didn't start", 1037 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 1038 1039 Thread.sleep(60000); 1040 1041 assertTrue("New job didn't stop after using up quota", 1042 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 1043 assertEquals(JobParameters.STOP_REASON_QUOTA, 1044 mTestAppInterface.getLastParams().getStopReason()); 1045 } 1046 1047 /* 1048 Tests currently disabled because they require changes inside the framework to lower the minimum 1049 EJ quota to one minute (from 5 minutes). 1050 TODO(224533485): make JS testable enough to enable these tests 1051 1052 @Test 1053 public void testRestrictingStopReason_ExpeditedQuota_startOnCharging() throws Exception { 1054 assumeTrue("app standby not enabled", mAppStandbyEnabled); 1055 assumeFalse("not testable in automotive device", mAutomotiveDevice); // Test needs battery 1056 assumeFalse("not testable in leanback device", mLeanbackOnly); // Test needs battery 1057 1058 // Reduce allowed time for testing. System to cap the time above 30 seconds. 1059 mDeviceConfigStateHelper.set("qc_ej_limit_rare_ms", "30000"); 1060 mDeviceConfigStateHelper.set("runtime_min_ej_guarantee_ms", "30000"); 1061 // Start with charging so JobScheduler thinks the job can run for the maximum amount of 1062 // time. We turn off charging later so quota clearly comes into effect. 1063 setChargingState(true); 1064 setTestPackageStandbyBucket(Bucket.RARE); 1065 1066 mTestAppInterface.scheduleJob(false, NETWORK_TYPE_NONE, true); 1067 runJob(); 1068 assertTrue("New job didn't start", 1069 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 1070 assertTrue(mTestAppInterface.getLastParams().isExpeditedJob()); 1071 setChargingState(false); 1072 1073 assertFalse("Job stopped before using up quota", 1074 mTestAppInterface.awaitJobStop(45_000)); 1075 Thread.sleep(15_000); 1076 1077 assertTrue("Job didn't stop after using up quota", 1078 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 1079 assertEquals(JobParameters.STOP_REASON_QUOTA, 1080 mTestAppInterface.getLastParams().getStopReason()); 1081 } 1082 1083 @Test 1084 public void testRestrictingStopReason_ExpeditedQuota_noCharging() throws Exception { 1085 assumeTrue("app standby not enabled", mAppStandbyEnabled); 1086 assumeFalse("not testable in automotive device", mAutomotiveDevice); // Test needs battery 1087 assumeFalse("not testable in leanback device", mLeanbackOnly); // Test needs battery 1088 1089 // Reduce allowed time for testing. 1090 mDeviceConfigStateHelper.set("qc_ej_limit_rare_ms", "30000"); 1091 mDeviceConfigStateHelper.set("runtime_min_ej_guarantee_ms", "30000"); 1092 setChargingState(false); 1093 setTestPackageStandbyBucket(Bucket.RARE); 1094 1095 mTestAppInterface.scheduleJob(false, NETWORK_TYPE_NONE, true); 1096 runJob(); 1097 assertTrue("New job didn't start", 1098 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 1099 assertTrue(mTestAppInterface.getLastParams().isExpeditedJob()); 1100 1101 assertFalse("Job stopped before using up quota", 1102 mTestAppInterface.awaitJobStop(45_000)); 1103 Thread.sleep(15_000); 1104 1105 assertTrue("Job didn't stop after using up quota", 1106 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 1107 // Charging state was false when the job started, so the trigger the timeout before 1108 // QuotaController officially marks the quota finished. 1109 final int stopReason = mTestAppInterface.getLastParams().getStopReason(); 1110 assertTrue(stopReason == JobParameters.STOP_REASON_TIMEOUT 1111 || stopReason == JobParameters.STOP_REASON_QUOTA); 1112 } 1113 */ 1114 1115 @Test testRestrictingStopReason_BatterySaver()1116 public void testRestrictingStopReason_BatterySaver() throws Exception { 1117 BatteryUtils.assumeBatterySaverFeature(); 1118 1119 setChargingState(false); 1120 BatteryUtils.enableBatterySaver(false); 1121 sendScheduleJobBroadcast(false); 1122 runJob(); 1123 assertTrue("Job did not start after scheduling", 1124 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 1125 1126 BatteryUtils.enableBatterySaver(true); 1127 assertTrue("Job did not stop on entering battery saver", 1128 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 1129 assertEquals(JobParameters.STOP_REASON_DEVICE_STATE, 1130 mTestAppInterface.getLastParams().getStopReason()); 1131 } 1132 1133 @Test testRestrictingStopReason_Doze()1134 public void testRestrictingStopReason_Doze() throws Exception { 1135 assumeTrue("device idle not enabled", mDeviceIdleEnabled); 1136 1137 toggleDozeState(false); 1138 mTestAppInterface.scheduleJob(false, NETWORK_TYPE_NONE, false); 1139 runJob(); 1140 assertTrue("Job did not start after scheduling", 1141 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT)); 1142 1143 toggleDozeState(true); 1144 assertTrue("Job did not stop on entering doze", 1145 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT)); 1146 assertEquals(JobParameters.STOP_REASON_DEVICE_STATE, 1147 mTestAppInterface.getLastParams().getStopReason()); 1148 } 1149 1150 @After tearDown()1151 public void tearDown() throws Exception { 1152 AppOpsUtils.reset(TEST_APP_PACKAGE); 1153 // Lock thermal service to not throttling 1154 ThermalUtils.overrideThermalNotThrottling(); 1155 if (mDeviceIdleEnabled) { 1156 toggleDozeState(false); 1157 } 1158 mTestAppInterface.cleanup(); 1159 mUiDevice.executeShellCommand("cmd jobscheduler monitor-battery off"); 1160 BatteryUtils.runDumpsysBatteryReset(); 1161 BatteryUtils.enableBatterySaver(false); 1162 Settings.Global.putString(mContext.getContentResolver(), 1163 Settings.Global.BATTERY_STATS_CONSTANTS, mInitialBatteryStatsConstants); 1164 removeTestAppFromTempWhitelist(); 1165 1166 mNetworkingHelper.tearDown(); 1167 mDeviceConfigStateHelper.restoreOriginalValues(); 1168 mActivityManagerDeviceConfigStateHelper.restoreOriginalValues(); 1169 Settings.Global.putString(mContext.getContentResolver(), 1170 Settings.Global.ENABLE_RESTRICTED_BUCKET, mInitialRestrictedBucketEnabled); 1171 1172 mUiDevice.executeShellCommand( 1173 "cmd jobscheduler reset-execution-quota -u " + UserHandle.myUserId() 1174 + " " + TEST_APP_PACKAGE); 1175 1176 Settings.System.putString( 1177 mContext.getContentResolver(), SCREEN_OFF_TIMEOUT, mInitialDisplayTimeout); 1178 } 1179 setTestPackageRestricted(boolean restricted)1180 private void setTestPackageRestricted(boolean restricted) throws Exception { 1181 AppOpsUtils.setOpMode(TEST_APP_PACKAGE, "RUN_ANY_IN_BACKGROUND", 1182 restricted ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED); 1183 } 1184 setRestrictedBucketEnabled(boolean enabled)1185 private void setRestrictedBucketEnabled(boolean enabled) { 1186 Settings.Global.putString(mContext.getContentResolver(), 1187 Settings.Global.ENABLE_RESTRICTED_BUCKET, enabled ? "1" : "0"); 1188 } 1189 toggleAutoRestrictedBucketOnBgRestricted(boolean enable)1190 private void toggleAutoRestrictedBucketOnBgRestricted(boolean enable) { 1191 mActivityManagerDeviceConfigStateHelper.set("bg_auto_restricted_bucket_on_bg_restricted", 1192 Boolean.toString(enable)); 1193 } 1194 isTestAppTempWhitelisted()1195 private boolean isTestAppTempWhitelisted() throws Exception { 1196 final String output = mUiDevice.executeShellCommand("cmd deviceidle tempwhitelist").trim(); 1197 for (String line : output.split("\n")) { 1198 if (line.contains("UID=" + mTestPackageUid)) { 1199 return true; 1200 } 1201 } 1202 return false; 1203 } 1204 sendScheduleJobBroadcast(boolean allowWhileIdle)1205 private void sendScheduleJobBroadcast(boolean allowWhileIdle) throws Exception { 1206 mTestAppInterface.scheduleJob(allowWhileIdle, NETWORK_TYPE_NONE, false); 1207 } 1208 toggleDozeState(final boolean idle)1209 private void toggleDozeState(final boolean idle) throws Exception { 1210 mUiDevice.executeShellCommand("cmd deviceidle " + (idle ? "force-idle" : "unforce")); 1211 if (!idle) { 1212 // Make sure the device doesn't stay idle, even after unforcing. 1213 mUiDevice.executeShellCommand("cmd deviceidle motion"); 1214 } 1215 assertTrue("Could not change device idle state to " + idle, 1216 waitUntilTrue(SHELL_TIMEOUT, () -> { 1217 synchronized (JobThrottlingTest.this) { 1218 return mDeviceInDoze == idle; 1219 } 1220 })); 1221 } 1222 tempWhitelistTestApp(long duration)1223 private void tempWhitelistTestApp(long duration) throws Exception { 1224 mUiDevice.executeShellCommand("cmd deviceidle tempwhitelist -d " + duration 1225 + " " + TEST_APP_PACKAGE); 1226 } 1227 makeTestPackageIdle()1228 private void makeTestPackageIdle() throws Exception { 1229 mUiDevice.executeShellCommand("am make-uid-idle --user current " + TEST_APP_PACKAGE); 1230 } 1231 setTestPackageStandbyBucket(Bucket bucket)1232 void setTestPackageStandbyBucket(Bucket bucket) throws Exception { 1233 setTestPackageStandbyBucket(mUiDevice, bucket); 1234 } 1235 setTestPackageStandbyBucket(UiDevice uiDevice, Bucket bucket)1236 static void setTestPackageStandbyBucket(UiDevice uiDevice, Bucket bucket) throws Exception { 1237 final String bucketName; 1238 switch (bucket) { 1239 case ACTIVE: 1240 bucketName = "active"; 1241 break; 1242 case WORKING_SET: 1243 bucketName = "working"; 1244 break; 1245 case FREQUENT: 1246 bucketName = "frequent"; 1247 break; 1248 case RARE: 1249 bucketName = "rare"; 1250 break; 1251 case RESTRICTED: 1252 bucketName = "restricted"; 1253 break; 1254 case NEVER: 1255 bucketName = "never"; 1256 break; 1257 default: 1258 throw new IllegalArgumentException("Requested unknown bucket " + bucket); 1259 } 1260 uiDevice.executeShellCommand("am set-standby-bucket " + TEST_APP_PACKAGE 1261 + " " + bucketName); 1262 } 1263 removeTestAppFromTempWhitelist()1264 private boolean removeTestAppFromTempWhitelist() throws Exception { 1265 mUiDevice.executeShellCommand("cmd deviceidle tempwhitelist -r " + TEST_APP_PACKAGE); 1266 return waitUntilTrue(SHELL_TIMEOUT, () -> !isTestAppTempWhitelisted()); 1267 } 1268 1269 /** 1270 * Set the screen state. 1271 */ setScreenState(boolean on)1272 private void setScreenState(boolean on) throws Exception { 1273 if (on) { 1274 mUiDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP"); 1275 mUiDevice.executeShellCommand("wm dismiss-keyguard"); 1276 } else { 1277 mUiDevice.executeShellCommand("input keyevent KEYCODE_SLEEP"); 1278 } 1279 // Wait a little bit to make sure the screen state has changed. 1280 Thread.sleep(4_000); 1281 } 1282 setChargingState(boolean isCharging)1283 private void setChargingState(boolean isCharging) throws Exception { 1284 mUiDevice.executeShellCommand("cmd jobscheduler monitor-battery on"); 1285 1286 final String command; 1287 if (isCharging) { 1288 mUiDevice.executeShellCommand("cmd battery set ac 1"); 1289 final int curLevel = Integer.parseInt( 1290 mUiDevice.executeShellCommand("dumpsys battery get level").trim()); 1291 command = "cmd battery set -f level " + Math.min(100, curLevel + 1); 1292 } else { 1293 command = "cmd battery unplug -f"; 1294 } 1295 int seq = Integer.parseInt(mUiDevice.executeShellCommand(command).trim()); 1296 1297 // Wait for the battery update to be processed by job scheduler before proceeding. 1298 waitUntil("JobScheduler didn't update charging status to " + isCharging, 15 /* seconds */, 1299 () -> { 1300 int curSeq; 1301 boolean curCharging; 1302 curSeq = Integer.parseInt(mUiDevice.executeShellCommand( 1303 "cmd jobscheduler get-battery-seq").trim()); 1304 curCharging = Boolean.parseBoolean(mUiDevice.executeShellCommand( 1305 "cmd jobscheduler get-battery-charging").trim()); 1306 return curSeq >= seq && curCharging == isCharging; 1307 }); 1308 } 1309 1310 /** 1311 * Trigger job idle (not device idle); 1312 */ triggerJobIdle()1313 private void triggerJobIdle() throws Exception { 1314 mUiDevice.executeShellCommand("cmd activity idle-maintenance"); 1315 // Wait a moment to let that happen before proceeding. 1316 Thread.sleep(2_000); 1317 } 1318 1319 /** Asks (not forces) JobScheduler to run the job if constraints are met. */ runJob()1320 private void runJob() throws Exception { 1321 // Since connectivity is a functional constraint, calling the "run" command without force 1322 // will only get the job to run if the constraint is satisfied. 1323 mUiDevice.executeShellCommand("cmd jobscheduler run -s" 1324 + " -u " + UserHandle.myUserId() + " " + TEST_APP_PACKAGE + " " + mTestJobId); 1325 } 1326 hasEthernetConnection()1327 private boolean hasEthernetConnection() { 1328 return mNetworkingHelper.hasEthernetConnection(); 1329 } 1330 getJobState()1331 private String getJobState() throws Exception { 1332 return mUiDevice.executeShellCommand("cmd jobscheduler get-job-state --user cur " 1333 + TEST_APP_PACKAGE + " " + mTestJobId).trim(); 1334 } 1335 assertJobWaiting()1336 private void assertJobWaiting() throws Exception { 1337 String state = getJobState(); 1338 assertTrue("Job unexpectedly not waiting, in state: " + state, state.contains("waiting")); 1339 } 1340 assertJobNotReady()1341 private void assertJobNotReady() throws Exception { 1342 String state = getJobState(); 1343 assertFalse("Job unexpectedly ready, in state: " + state, state.contains("ready")); 1344 } 1345 assertJobReady()1346 private void assertJobReady() throws Exception { 1347 String state = getJobState(); 1348 assertTrue("Job unexpectedly not ready, in state: " + state, state.contains("ready")); 1349 } 1350 waitUntilTrue(long maxWait, Condition condition)1351 private boolean waitUntilTrue(long maxWait, Condition condition) throws Exception { 1352 final long deadLine = SystemClock.uptimeMillis() + maxWait; 1353 do { 1354 Thread.sleep(POLL_INTERVAL); 1355 } while (!condition.isTrue() && SystemClock.uptimeMillis() < deadLine); 1356 return condition.isTrue(); 1357 } 1358 1359 private interface Condition { isTrue()1360 boolean isTrue() throws Exception; 1361 } 1362 } 1363