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