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