1 /* 2 * Copyright (C) 2014 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 package android.jobscheduler.cts; 17 18 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; 19 import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD; 20 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; 21 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; 22 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; 23 24 import static org.junit.Assert.assertNotEquals; 25 26 import android.annotation.TargetApi; 27 import android.app.ActivityManager; 28 import android.app.compat.CompatChanges; 29 import android.app.compat.PackageOverride; 30 import android.app.job.JobInfo; 31 import android.app.job.JobParameters; 32 import android.content.Context; 33 import android.content.pm.PackageManager; 34 import android.jobscheduler.cts.jobtestapp.TestJobSchedulerReceiver; 35 import android.net.ConnectivityManager; 36 import android.net.NetworkCapabilities; 37 import android.net.NetworkRequest; 38 import android.net.wifi.WifiManager; 39 import android.platform.test.annotations.RequiresDevice; 40 import android.util.Log; 41 42 import com.android.compatibility.common.util.AppStandbyUtils; 43 import com.android.compatibility.common.util.BatteryUtils; 44 import com.android.compatibility.common.util.PollingCheck; 45 import com.android.compatibility.common.util.SystemUtil; 46 import com.android.server.net.Flags; 47 48 import java.util.Collections; 49 import java.util.Map; 50 51 /** 52 * Schedules jobs with the {@link android.app.job.JobScheduler} that have network connectivity 53 * constraints. 54 * Requires manipulating the {@link android.net.wifi.WifiManager} to ensure an unmetered network. 55 * Similarly, requires that the phone be connected to a wifi hotspot, or else the test will fail. 56 */ 57 @TargetApi(21) 58 @RequiresDevice // Emulators don't always have access to wifi/network 59 public class ConnectivityConstraintTest extends BaseJobSchedulerTest { 60 private static final String TAG = "ConnectivityConstraintTest"; 61 62 /** Unique identifier for the job scheduled by this suite of tests. */ 63 public static final int CONNECTIVITY_JOB_ID = ConnectivityConstraintTest.class.hashCode(); 64 /** Wait this long before timing out the test. */ 65 private static final long DEFAULT_TIMEOUT_MILLIS = 30000L; // 30 seconds. 66 67 private NetworkingHelper mNetworkingHelper; 68 private WifiManager mWifiManager; 69 private ConnectivityManager mCm; 70 71 /** Whether the device running these tests supports WiFi. */ 72 private boolean mHasWifi; 73 74 private JobInfo.Builder mBuilder; 75 76 private TestAppInterface mTestAppInterface; 77 78 @Override setUp()79 public void setUp() throws Exception { 80 super.setUp(); 81 82 mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE); 83 mCm = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE); 84 mNetworkingHelper = new NetworkingHelper(getInstrumentation(), getContext()); 85 86 PackageManager packageManager = mContext.getPackageManager(); 87 mHasWifi = packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI); 88 mBuilder = new JobInfo.Builder(CONNECTIVITY_JOB_ID, kJobServiceComponent); 89 90 setDataSaverEnabled(false); 91 mNetworkingHelper.setAllNetworksEnabled(true); 92 } 93 94 @Override tearDown()95 public void tearDown() throws Exception { 96 if (mTestAppInterface != null) { 97 mTestAppInterface.cleanup(); 98 } 99 mJobScheduler.cancel(CONNECTIVITY_JOB_ID); 100 101 BatteryUtils.runDumpsysBatteryReset(); 102 BatteryUtils.resetBatterySaver(); 103 104 // Ensure that we leave WiFi in its previous state. 105 mNetworkingHelper.tearDown(); 106 107 super.tearDown(); 108 } 109 110 // -------------------------------------------------------------------------------------------- 111 // Positives - schedule jobs under conditions that require them to pass. 112 // -------------------------------------------------------------------------------------------- 113 114 /** 115 * Schedule a job that requires a WiFi connection, and assert that it executes when the device 116 * is connected to WiFi. This will fail if a wifi connection is unavailable. 117 */ testUnmeteredConstraintExecutes_withWifi()118 public void testUnmeteredConstraintExecutes_withWifi() throws Exception { 119 if (!mHasWifi) { 120 Log.d(TAG, "Skipping test that requires the device be WiFi enabled."); 121 return; 122 } 123 setWifiMeteredState(false); 124 125 kTestEnvironment.setExpectedExecutions(1); 126 mJobScheduler.schedule( 127 mBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) 128 .build()); 129 130 runSatisfiedJob(CONNECTIVITY_JOB_ID); 131 132 assertTrue("Job with unmetered constraint did not fire on WiFi.", 133 kTestEnvironment.awaitExecution()); 134 } 135 136 /** 137 * Schedule a job with a connectivity constraint, and ensure that it executes on WiFi. 138 */ testConnectivityConstraintExecutes_withWifi()139 public void testConnectivityConstraintExecutes_withWifi() throws Exception { 140 if (!mHasWifi) { 141 Log.d(TAG, "Skipping test that requires the device be WiFi enabled."); 142 return; 143 } 144 setWifiMeteredState(false); 145 146 kTestEnvironment.setExpectedExecutions(1); 147 mJobScheduler.schedule( 148 mBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 149 .build()); 150 151 runSatisfiedJob(CONNECTIVITY_JOB_ID); 152 153 assertTrue("Job with connectivity constraint did not fire on WiFi.", 154 kTestEnvironment.awaitExecution()); 155 } 156 157 /** 158 * Schedule a job with a generic connectivity constraint, and ensure that it executes on WiFi, 159 * even with Data Saver on. 160 */ testConnectivityConstraintExecutes_withWifi_DataSaverOn()161 public void testConnectivityConstraintExecutes_withWifi_DataSaverOn() throws Exception { 162 if (!mHasWifi) { 163 Log.d(TAG, "Skipping test that requires the device be WiFi enabled."); 164 return; 165 } 166 setWifiMeteredState(false); 167 setDataSaverEnabled(true); 168 169 kTestEnvironment.setExpectedExecutions(1); 170 mJobScheduler.schedule( 171 mBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 172 .build()); 173 174 runSatisfiedJob(CONNECTIVITY_JOB_ID); 175 176 assertTrue("Job with connectivity constraint did not fire on unmetered WiFi.", 177 kTestEnvironment.awaitExecution()); 178 } 179 180 /** 181 * Schedule a job with a generic connectivity constraint, and ensure that it executes 182 * on a cellular data connection. 183 */ testConnectivityConstraintExecutes_withMobile()184 public void testConnectivityConstraintExecutes_withMobile() throws Exception { 185 if (!checkDeviceSupportsMobileData()) { 186 return; 187 } 188 disconnectWifiToConnectToMobile(); 189 190 kTestEnvironment.setExpectedExecutions(1); 191 mJobScheduler.schedule( 192 mBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 193 .build()); 194 195 runSatisfiedJob(CONNECTIVITY_JOB_ID); 196 197 assertTrue("Job with connectivity constraint did not fire on mobile.", 198 kTestEnvironment.awaitExecution()); 199 } 200 201 /** 202 * Schedule a job with a generic connectivity constraint, and ensure that it executes 203 * on a metered wifi connection. 204 */ testConnectivityConstraintExecutes_withMeteredWifi()205 public void testConnectivityConstraintExecutes_withMeteredWifi() throws Exception { 206 if (hasEthernetConnection()) { 207 Log.d(TAG, "Skipping test since ethernet is connected."); 208 return; 209 } 210 if (!mHasWifi) { 211 return; 212 } 213 setWifiMeteredState(true); 214 215 kTestEnvironment.setExpectedExecutions(1); 216 mJobScheduler.schedule( 217 mBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).build()); 218 219 runSatisfiedJob(CONNECTIVITY_JOB_ID); 220 221 assertTrue("Job with connectivity constraint did not fire on metered wifi.", 222 kTestEnvironment.awaitExecution()); 223 } 224 225 /** 226 * Schedule a job with a generic connectivity constraint, and ensure that it isn't stopped when 227 * the device transitions to WiFi, but is informed of the network change. 228 */ testConnectivityConstraintExecutes_transitionNetworks()229 public void testConnectivityConstraintExecutes_transitionNetworks() throws Exception { 230 if (!mHasWifi) { 231 Log.d(TAG, "Skipping test that requires the device be WiFi enabled."); 232 return; 233 } 234 if (!checkDeviceSupportsMobileData()) { 235 return; 236 } 237 disconnectWifiToConnectToMobile(); 238 239 kTestEnvironment.setExpectedExecutions(1); 240 kTestEnvironment.setContinueAfterStart(); 241 kTestEnvironment.setExpectedStopped(); 242 kTestEnvironment.setExpectedNetworkChange(); 243 mJobScheduler.schedule( 244 mBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 245 .build()); 246 247 runSatisfiedJob(CONNECTIVITY_JOB_ID); 248 249 assertTrue("Job with connectivity constraint did not fire on mobile.", 250 kTestEnvironment.awaitExecution()); 251 JobParameters startParams = kTestEnvironment.getLastStartJobParameters(); 252 253 connectToWifi(); 254 assertFalse( 255 "Job with connectivity constraint was stopped when network transitioned to WiFi.", 256 kTestEnvironment.awaitStopped()); 257 assertTrue("Job didn't get network change signal when network transitioned to WiFi.", 258 kTestEnvironment.awaitNetworkChange()); 259 JobParameters networkChangedParams = kTestEnvironment.getLastNetworkChangedJobParameters(); 260 assertNotNull(networkChangedParams.getNetwork()); 261 assertNotEquals(startParams.getNetwork(), networkChangedParams.getNetwork()); 262 } 263 264 /** 265 * Schedule a job with a metered connectivity constraint, and ensure that it executes 266 * on a mobile data connection. 267 */ testConnectivityConstraintExecutes_metered_mobile()268 public void testConnectivityConstraintExecutes_metered_mobile() throws Exception { 269 if (!checkDeviceSupportsMobileData()) { 270 return; 271 } 272 disconnectWifiToConnectToMobile(); 273 274 kTestEnvironment.setExpectedExecutions(1); 275 mJobScheduler.schedule( 276 mBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_METERED) 277 .build()); 278 279 runSatisfiedJob(CONNECTIVITY_JOB_ID); 280 assertTrue("Job with metered connectivity constraint did not fire on mobile.", 281 kTestEnvironment.awaitExecution()); 282 } 283 284 /** 285 * Schedule a job with a metered connectivity constraint, and ensure that it executes 286 * on a mobile data connection. 287 */ testConnectivityConstraintExecutes_metered_Wifi()288 public void testConnectivityConstraintExecutes_metered_Wifi() throws Exception { 289 if (hasEthernetConnection()) { 290 Log.d(TAG, "Skipping test since ethernet is connected."); 291 return; 292 } 293 if (!mHasWifi) { 294 return; 295 } 296 setWifiMeteredState(true); 297 298 299 kTestEnvironment.setExpectedExecutions(1); 300 mJobScheduler.schedule( 301 mBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_METERED).build()); 302 303 // Since we equate "metered" to "cellular", the job shouldn't start. 304 runSatisfiedJob(CONNECTIVITY_JOB_ID); 305 assertTrue("Job with metered connectivity constraint fired on a metered wifi network.", 306 kTestEnvironment.awaitTimeout()); 307 } 308 309 /** 310 * Schedule a job with a cellular connectivity constraint, and ensure that it executes 311 * on a mobile data connection and is not stopped when Data Saver is turned on because the app 312 * is in the foreground. 313 */ testCellularConstraintExecutedAndStopped_Foreground()314 public void testCellularConstraintExecutedAndStopped_Foreground() throws Exception { 315 if (hasEthernetConnection()) { 316 Log.d(TAG, "Skipping test since ethernet is connected."); 317 return; 318 } 319 if (mHasWifi) { 320 setWifiMeteredState(true); 321 } else if (checkDeviceSupportsMobileData()) { 322 disconnectWifiToConnectToMobile(); 323 } else { 324 // No mobile or wifi. 325 return; 326 } 327 328 mTestAppInterface = new TestAppInterface(mContext, CONNECTIVITY_JOB_ID); 329 mTestAppInterface.startAndKeepTestActivity(); 330 toggleScreenOn(true); 331 332 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_ANY, false); 333 334 mTestAppInterface.runSatisfiedJob(); 335 assertTrue("Job with metered connectivity constraint did not fire on a metered network.", 336 mTestAppInterface.awaitJobStart(30_000)); 337 338 setDataSaverEnabled(true); 339 assertFalse( 340 "Job with metered connectivity constraint for foreground app was stopped when" 341 + " Data Saver was turned on.", 342 mTestAppInterface.awaitJobStop(30_000)); 343 } 344 345 /** 346 * Schedule an expedited job that requires a network connection, and verify that it runs even 347 * when if an app is idle. 348 */ testExpeditedJobExecutes_IdleApp()349 public void testExpeditedJobExecutes_IdleApp() throws Exception { 350 if (!AppStandbyUtils.isAppStandbyEnabled()) { 351 Log.d(TAG, "App standby not enabled"); 352 return; 353 } 354 if (mHasWifi) { 355 setWifiMeteredState(false); 356 } else if (!hasEthernetConnection()) { 357 // We're skipping this test because we can't force cellular or other networks to be 358 // unmetered. For now, we assume ethernet is always unmetered. 359 Log.d(TAG, "Skipping test that requires an unmetered network."); 360 return; 361 } 362 363 setDeviceConfigFlag("qc_max_session_count_restricted", "0", true); 364 SystemUtil.runShellCommand("am set-standby-bucket " 365 + kJobServiceComponent.getPackageName() + " restricted"); 366 BatteryUtils.runDumpsysBatteryUnplug(); 367 368 kTestEnvironment.setExpectedExecutions(1); 369 mJobScheduler.schedule( 370 mBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 371 .setExpedited(true) 372 .build()); 373 runSatisfiedJob(CONNECTIVITY_JOB_ID); 374 375 assertTrue("Expedited job requiring connectivity did not fire when app was idle.", 376 kTestEnvironment.awaitExecution()); 377 } 378 379 /** 380 * Schedule an expedited job that requires a network connection, and verify that it runs even 381 * when Battery Saver is on. 382 */ testExpeditedJobExecutes_BatterySaverOn()383 public void testExpeditedJobExecutes_BatterySaverOn() throws Exception { 384 if (!BatteryUtils.isBatterySaverSupported()) { 385 Log.d(TAG, "Skipping test that requires battery saver support"); 386 return; 387 } 388 if (hasEthernetConnection()) { 389 // We need to use a metered network but can't control the ethernet connection. 390 return; 391 } 392 if (mHasWifi) { 393 setWifiMeteredState(true); 394 } else if (checkDeviceSupportsMobileData()) { 395 disconnectWifiToConnectToMobile(); 396 } else { 397 Log.d(TAG, "Skipping test that requires a metered."); 398 return; 399 } 400 401 BatteryUtils.runDumpsysBatteryUnplug(); 402 BatteryUtils.enableBatterySaver(true); 403 404 kTestEnvironment.setExpectedExecutions(1); 405 mJobScheduler.schedule( 406 mBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 407 .setExpedited(true) 408 .build()); 409 runSatisfiedJob(CONNECTIVITY_JOB_ID); 410 411 assertTrue( 412 "Expedited job requiring connectivity did not fire with Battery Saver on.", 413 kTestEnvironment.awaitExecution()); 414 } 415 416 /** 417 * Schedule an expedited job that requires a network connection, and verify that it runs 418 * even when Doze is on. 419 */ testExpeditedJobExecutes_DozeOn()420 public void testExpeditedJobExecutes_DozeOn() throws Exception { 421 if (!isDeviceIdleFeatureEnabled()) { 422 // Test requires device idle feature 423 return; 424 } 425 426 mTestAppInterface = new TestAppInterface(mContext, CONNECTIVITY_JOB_ID); 427 428 mNetworkingHelper.setAllNetworksEnabled(true); 429 toggleScreenOn(false); 430 setDeviceIdleState(true); 431 432 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_ANY, true); 433 434 mTestAppInterface.runSatisfiedJob(); 435 assertTrue("UI job requiring connectivity did not fire with Doze on.", 436 mTestAppInterface.awaitJobStart(DEFAULT_TIMEOUT_MILLIS)); 437 } 438 439 /** 440 * Schedule an expedited job that requires a network connection, and verify that it runs even 441 * when Data Saver is on and the device is not connected to WiFi. 442 */ testFgExpeditedJobBypassesDataSaver()443 public void testFgExpeditedJobBypassesDataSaver() throws Exception { 444 if (hasEthernetConnection()) { 445 Log.d(TAG, "Skipping test since ethernet is connected."); 446 return; 447 } 448 if (mHasWifi) { 449 setWifiMeteredState(true); 450 } else if (checkDeviceSupportsMobileData()) { 451 disconnectWifiToConnectToMobile(); 452 } else { 453 Log.d(TAG, "Skipping test that requires a metered network."); 454 return; 455 } 456 setDataSaverEnabled(true); 457 458 mTestAppInterface = new TestAppInterface(mContext, CONNECTIVITY_JOB_ID); 459 mTestAppInterface.startAndKeepTestActivity(); 460 461 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_ANY, true); 462 mTestAppInterface.runSatisfiedJob(); 463 464 assertTrue( 465 "FG expedited job requiring metered connectivity did not fire with Data Saver on.", 466 mTestAppInterface.awaitJobStart(DEFAULT_TIMEOUT_MILLIS)); 467 } 468 469 /** 470 * Schedule an expedited job that requires a network connection, and verify that it runs even 471 * when multiple firewalls are active. 472 */ testExpeditedJobBypassesSimultaneousFirewalls_noDataSaver()473 public void testExpeditedJobBypassesSimultaneousFirewalls_noDataSaver() throws Exception { 474 if (!BatteryUtils.isBatterySaverSupported()) { 475 Log.d(TAG, "Skipping test that requires battery saver support"); 476 return; 477 } 478 if (mHasWifi) { 479 setWifiMeteredState(true); 480 } else if (checkDeviceSupportsMobileData()) { 481 disconnectWifiToConnectToMobile(); 482 } else { 483 Log.d(TAG, "Skipping test that requires a metered network."); 484 return; 485 } 486 if (!AppStandbyUtils.isAppStandbyEnabled()) { 487 Log.d(TAG, "App standby not enabled"); 488 return; 489 } 490 491 mDeviceConfigStateHelper.set("qc_max_session_count_restricted", "0"); 492 SystemUtil.runShellCommand("am set-standby-bucket " 493 + kJobServiceComponent.getPackageName() + " restricted"); 494 BatteryUtils.runDumpsysBatteryUnplug(); 495 BatteryUtils.enableBatterySaver(true); 496 setDataSaverEnabled(false); 497 498 kTestEnvironment.setExpectedExecutions(1); 499 mJobScheduler.schedule( 500 mBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 501 .setExpedited(true) 502 .build()); 503 runSatisfiedJob(CONNECTIVITY_JOB_ID); 504 505 assertTrue("Expedited job requiring connectivity did not fire with multiple firewalls.", 506 kTestEnvironment.awaitExecution()); 507 } 508 509 /** 510 * Schedule a job that requires a network connection, and verify that it runs even after the 511 * scheduling app is killed. 512 * 513 * Note: This is a basic test similar to testConnectivityConstraintExecutes_withWifi, except 514 * that it uses a helper app so the scheduling app's lifecycle and any resulting restrictions 515 * can be managed freely by the system. 516 */ testJobExecutes_afterAppIsKilled()517 public void testJobExecutes_afterAppIsKilled() throws Exception { 518 if (hasEthernetConnection()) { 519 Log.d(TAG, "Skipping test since ethernet is connected."); 520 return; 521 } 522 // To ensure the job doesn't start immediately after scheduling. 523 mNetworkingHelper.setAllNetworksEnabled(false); 524 525 mTestAppInterface = new TestAppInterface(mContext, CONNECTIVITY_JOB_ID); 526 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_ANY, false); 527 528 mTestAppInterface.kill(); 529 if (Flags.networkBlockedForTopSleepingAndAbove()) { 530 PollingCheck.waitFor(DEFAULT_TIMEOUT_MILLIS, 531 mTestAppInterface::isNetworkBlockedByPolicy, 532 "Test app did not lose network access after being stopped"); 533 } 534 // The job should run after network is connected, even though the app does not have access 535 // due to policy right now. 536 mNetworkingHelper.setAllNetworksEnabled(true); 537 538 mTestAppInterface.runSatisfiedJob(); 539 assertTrue( 540 "Job requiring network did not start after the app was killed", 541 mTestAppInterface.awaitJobStart(DEFAULT_TIMEOUT_MILLIS)); 542 } 543 544 /** 545 * Schedule a user-initiated job that requires a network connection, and verify that it runs 546 * even when Battery Saver is on. 547 */ testUserInitiatedJobExecutes_BatterySaverOn()548 public void testUserInitiatedJobExecutes_BatterySaverOn() throws Exception { 549 if (!BatteryUtils.isBatterySaverSupported()) { 550 Log.d(TAG, "Skipping test that requires battery saver support"); 551 return; 552 } 553 554 BatteryUtils.runDumpsysBatteryUnplug(); 555 BatteryUtils.enableBatterySaver(true); 556 557 mTestAppInterface = new TestAppInterface(mContext, CONNECTIVITY_JOB_ID); 558 // Put app in a valid state to schedule a UI job. 559 mTestAppInterface.startAndKeepTestActivity(); 560 toggleScreenOn(true); 561 562 mNetworkingHelper.setAllNetworksEnabled(true); 563 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_ANY, false, true); 564 565 mTestAppInterface.runSatisfiedJob(); 566 assertTrue("UI job requiring connectivity did not fire with Battery Saver on.", 567 mTestAppInterface.awaitJobStart(DEFAULT_TIMEOUT_MILLIS)); 568 } 569 570 /** 571 * Schedule a user-initiated job that requires a network connection, and verify that it runs 572 * even when Doze is on. 573 */ testUserInitiatedJobExecutes_DozeOn()574 public void testUserInitiatedJobExecutes_DozeOn() throws Exception { 575 if (!isDeviceIdleFeatureEnabled()) { 576 // Test requires device idle feature 577 return; 578 } 579 580 try (TestNotificationListener.NotificationHelper notificationHelper = 581 new TestNotificationListener.NotificationHelper( 582 mContext, TestAppInterface.TEST_APP_PACKAGE)) { 583 mTestAppInterface = new TestAppInterface(mContext, CONNECTIVITY_JOB_ID); 584 // Put app in a valid state to schedule a UI job. 585 mNetworkingHelper.setAllNetworksEnabled(true); 586 toggleScreenOn(false); 587 setDeviceIdleState(true); 588 mTestAppInterface.postUiInitiatingNotification( 589 Map.of(TestJobSchedulerReceiver.EXTRA_AS_USER_INITIATED, true), 590 Map.of( 591 TestJobSchedulerReceiver.EXTRA_REQUIRED_NETWORK_TYPE, 592 JobInfo.NETWORK_TYPE_ANY 593 ) 594 ); 595 596 notificationHelper.clickNotification(); 597 598 assertTrue("UI job requiring connectivity did not fire with Doze on.", 599 mTestAppInterface.awaitJobStart(DEFAULT_TIMEOUT_MILLIS)); 600 } 601 } 602 603 // -------------------------------------------------------------------------------------------- 604 // Positives & Negatives - schedule jobs under conditions that require that pass initially and 605 // then fail with a constraint change. 606 // -------------------------------------------------------------------------------------------- 607 608 /** 609 * Schedule a job with a cellular connectivity constraint, and ensure that it executes 610 * on a mobile data connection and is stopped when Data Saver is turned on. 611 */ testCellularConstraintExecutedAndStopped()612 public void testCellularConstraintExecutedAndStopped() throws Exception { 613 if (!checkDeviceSupportsMobileData()) { 614 return; 615 } 616 disconnectWifiToConnectToMobile(); 617 setDataSaverEnabled(false); 618 619 mTestAppInterface = new TestAppInterface(mContext, CONNECTIVITY_JOB_ID); 620 621 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_CELLULAR, false); 622 623 mTestAppInterface.runSatisfiedJob(); 624 assertTrue("Job with cellular constraint did not fire on mobile.", 625 mTestAppInterface.awaitJobStart(DEFAULT_TIMEOUT_MILLIS)); 626 627 setDataSaverEnabled(true); 628 assertTrue( 629 "Job with cellular constraint was not stopped when Data Saver was turned on.", 630 mTestAppInterface.awaitJobStop(DEFAULT_TIMEOUT_MILLIS)); 631 } 632 testJobParametersNetwork()633 public void testJobParametersNetwork() throws Exception { 634 mNetworkingHelper.setAllNetworksEnabled(true); 635 636 // Everything good. 637 final NetworkRequest nr = new NetworkRequest.Builder() 638 .addCapability(NET_CAPABILITY_INTERNET) 639 .addCapability(NET_CAPABILITY_VALIDATED) 640 .build(); 641 JobInfo ji = mBuilder.setRequiredNetwork(nr).build(); 642 643 kTestEnvironment.setExpectedExecutions(1); 644 mJobScheduler.schedule(ji); 645 runSatisfiedJob(CONNECTIVITY_JOB_ID); 646 assertTrue("Job didn't fire immediately", kTestEnvironment.awaitExecution()); 647 648 JobParameters params = kTestEnvironment.getLastStartJobParameters(); 649 assertNotNull(params.getNetwork()); 650 final NetworkCapabilities capabilities = 651 getContext().getSystemService(ConnectivityManager.class) 652 .getNetworkCapabilities(params.getNetwork()); 653 assertTrue(nr.canBeSatisfiedBy(capabilities)); 654 655 if (!hasEthernetConnection()) { 656 // Deadline passed with no network satisfied. 657 mNetworkingHelper.setAllNetworksEnabled(false); 658 659 if (CompatChanges.isChangeEnabled(TestAppInterface.ENFORCE_MINIMUM_TIME_WINDOWS)) { 660 SystemUtil.runWithShellPermissionIdentity( 661 () -> CompatChanges.putPackageOverrides( 662 TestAppInterface.TEST_APP_PACKAGE, 663 Map.of(TestAppInterface.ENFORCE_MINIMUM_TIME_WINDOWS, 664 new PackageOverride.Builder().setEnabled(false).build()) 665 ), 666 OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD, INTERACT_ACROSS_USERS_FULL); 667 } 668 mTestAppInterface = new TestAppInterface(mContext, CONNECTIVITY_JOB_ID); 669 mTestAppInterface.scheduleJob( 670 Collections.emptyMap(), 671 Map.of( 672 TestJobSchedulerReceiver.EXTRA_REQUIRED_NETWORK_TYPE, 673 JobInfo.NETWORK_TYPE_ANY 674 ), 675 Map.of(TestJobSchedulerReceiver.EXTRA_DEADLINE, 0L) 676 ); 677 mTestAppInterface.runSatisfiedJob(); 678 assertTrue("Job didn't fire immediately", 679 mTestAppInterface.awaitJobStart(DEFAULT_TIMEOUT_MILLIS)); 680 params = mTestAppInterface.getLastParams(); 681 assertNull(params.getNetwork()); 682 } 683 684 // No network requested 685 mNetworkingHelper.setAllNetworksEnabled(true); 686 ji = mBuilder.setRequiredNetwork(null).build(); 687 kTestEnvironment.setExpectedExecutions(1); 688 mJobScheduler.schedule(ji); 689 runSatisfiedJob(CONNECTIVITY_JOB_ID); 690 assertTrue("Job didn't fire immediately", kTestEnvironment.awaitExecution()); 691 692 params = kTestEnvironment.getLastStartJobParameters(); 693 assertNull(params.getNetwork()); 694 } 695 testJobUidState()696 public void testJobUidState() throws Exception { 697 // Turn screen off so any lingering activity close processing from previous tests 698 // don't affect this one. 699 toggleScreenOn(false); 700 mTestAppInterface = new TestAppInterface(mContext, CONNECTIVITY_JOB_ID); 701 mTestAppInterface.scheduleJob( 702 Map.of(TestJobSchedulerReceiver.EXTRA_REQUEST_JOB_UID_STATE, true), 703 Map.of( 704 TestJobSchedulerReceiver.EXTRA_REQUIRED_NETWORK_TYPE, 705 JobInfo.NETWORK_TYPE_ANY 706 ) 707 ); 708 mTestAppInterface.forceRunJob(); 709 assertTrue("Job did not start after scheduling", 710 mTestAppInterface.awaitJobStart(DEFAULT_TIMEOUT_MILLIS)); 711 mTestAppInterface.assertJobUidState(ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 712 0, // Regular jobs should not have any privileged network capabilities 713 250 /* ProcessList.PERCEPTIBLE_LOW_APP_ADJ */); 714 } 715 716 // -------------------------------------------------------------------------------------------- 717 // Negatives - schedule jobs under conditions that require that they fail. 718 // -------------------------------------------------------------------------------------------- 719 720 /** 721 * Schedule a job that requires a WiFi connection, and assert that it fails when the device is 722 * connected to a cellular provider. 723 * This test assumes that if the device supports a mobile data connection, then this connection 724 * will be available. 725 */ testUnmeteredConstraintFails_withMobile()726 public void testUnmeteredConstraintFails_withMobile() throws Exception { 727 if (!checkDeviceSupportsMobileData()) { 728 return; 729 } 730 disconnectWifiToConnectToMobile(); 731 732 kTestEnvironment.setExpectedExecutions(0); 733 mJobScheduler.schedule( 734 mBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) 735 .build()); 736 runSatisfiedJob(CONNECTIVITY_JOB_ID); 737 738 assertTrue("Job requiring unmetered connectivity still executed on mobile.", 739 kTestEnvironment.awaitTimeout()); 740 } 741 742 /** 743 * Schedule a job that requires a metered connection, and verify that it does not run when 744 * the device is not connected to WiFi and Data Saver is on. 745 */ testMeteredConstraintFails_withMobile_DataSaverOn()746 public void testMeteredConstraintFails_withMobile_DataSaverOn() throws Exception { 747 if (!checkDeviceSupportsMobileData()) { 748 Log.d(TAG, "Skipping test that requires the device be mobile data enabled."); 749 return; 750 } 751 disconnectWifiToConnectToMobile(); 752 setDataSaverEnabled(true); 753 754 mTestAppInterface = new TestAppInterface(mContext, CONNECTIVITY_JOB_ID); 755 756 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_CELLULAR, false); 757 mTestAppInterface.runSatisfiedJob(); 758 759 assertFalse("Job requiring cellular connectivity executed with Data Saver on", 760 mTestAppInterface.awaitJobStart(DEFAULT_TIMEOUT_MILLIS)); 761 } 762 763 /** 764 * Schedule a job that requires a metered connection, and verify that it does not run when 765 * the device is not connected to WiFi and Data Saver is on. 766 */ testEJMeteredConstraintFails_withMobile_DataSaverOn()767 public void testEJMeteredConstraintFails_withMobile_DataSaverOn() throws Exception { 768 if (!checkDeviceSupportsMobileData()) { 769 Log.d(TAG, "Skipping test that requires the device be mobile data enabled."); 770 return; 771 } 772 disconnectWifiToConnectToMobile(); 773 setDataSaverEnabled(true); 774 775 mTestAppInterface = new TestAppInterface(mContext, CONNECTIVITY_JOB_ID); 776 777 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_CELLULAR, true); 778 mTestAppInterface.runSatisfiedJob(); 779 780 assertFalse("BG expedited job requiring cellular connectivity executed with Data Saver on", 781 mTestAppInterface.awaitJobStart(DEFAULT_TIMEOUT_MILLIS)); 782 } 783 784 /** 785 * Schedule a job that requires a metered connection, and verify that it does not run when 786 * the device is connected to an unmetered WiFi provider. 787 * This test assumes that if the device supports a mobile data connection, then this connection 788 * will be available. 789 */ testMeteredConstraintFails_withWiFi()790 public void testMeteredConstraintFails_withWiFi() throws Exception { 791 if (!mHasWifi) { 792 Log.d(TAG, "Skipping test that requires the device be WiFi enabled."); 793 return; 794 } 795 if (!checkDeviceSupportsMobileData()) { 796 Log.d(TAG, "Skipping test that requires the device be mobile data enabled."); 797 return; 798 } 799 setWifiMeteredState(false); 800 801 kTestEnvironment.setExpectedExecutions(0); 802 mJobScheduler.schedule( 803 mBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_METERED) 804 .build()); 805 runSatisfiedJob(CONNECTIVITY_JOB_ID); 806 807 assertTrue("Job requiring metered connectivity still executed on WiFi.", 808 kTestEnvironment.awaitTimeout()); 809 } 810 811 /** 812 * Schedule a job that requires an unmetered connection, and verify that it does not run when 813 * the device is connected to a metered WiFi provider. 814 */ testUnmeteredConstraintFails_withMeteredWiFi()815 public void testUnmeteredConstraintFails_withMeteredWiFi() throws Exception { 816 if (hasEthernetConnection()) { 817 Log.d(TAG, "Skipping test since ethernet is connected."); 818 return; 819 } 820 if (!mHasWifi) { 821 Log.d(TAG, "Skipping test that requires the device be WiFi enabled."); 822 return; 823 } 824 setWifiMeteredState(true); 825 826 kTestEnvironment.setExpectedExecutions(0); 827 mJobScheduler.schedule( 828 mBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) 829 .build()); 830 runSatisfiedJob(CONNECTIVITY_JOB_ID); 831 832 assertTrue("Job requiring unmetered connectivity still executed on metered WiFi.", 833 kTestEnvironment.awaitTimeout()); 834 } 835 836 /** 837 * Schedule a job that requires a cellular connection, and verify that it does not run when 838 * the device is connected to a WiFi provider. 839 */ testCellularConstraintFails_withWiFi()840 public void testCellularConstraintFails_withWiFi() throws Exception { 841 if (!mHasWifi) { 842 Log.d(TAG, "Skipping test that requires the device be WiFi enabled."); 843 return; 844 } 845 if (!checkDeviceSupportsMobileData()) { 846 Log.d(TAG, "Skipping test that requires the device be mobile data enabled."); 847 return; 848 } 849 setWifiMeteredState(false); 850 851 kTestEnvironment.setExpectedExecutions(0); 852 mJobScheduler.schedule( 853 mBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_CELLULAR).build()); 854 runSatisfiedJob(CONNECTIVITY_JOB_ID); 855 856 assertTrue("Job requiring cellular connectivity still executed on WiFi.", 857 kTestEnvironment.awaitTimeout()); 858 } 859 860 /** 861 * Schedule an expedited job that requires a network connection, and verify that it doesn't run 862 * when Data Saver is on and the device is not connected to WiFi. 863 */ testBgExpeditedJobDoesNotBypassDataSaver()864 public void testBgExpeditedJobDoesNotBypassDataSaver() throws Exception { 865 if (hasEthernetConnection()) { 866 Log.d(TAG, "Skipping test since ethernet is connected."); 867 return; 868 } 869 if (mHasWifi) { 870 setWifiMeteredState(true); 871 } else if (checkDeviceSupportsMobileData()) { 872 disconnectWifiToConnectToMobile(); 873 } else { 874 Log.d(TAG, "Skipping test that requires a metered network."); 875 return; 876 } 877 setDataSaverEnabled(true); 878 879 mTestAppInterface = new TestAppInterface(mContext, CONNECTIVITY_JOB_ID); 880 881 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_ANY, true); 882 mTestAppInterface.runSatisfiedJob(); 883 884 assertFalse("BG expedited job requiring connectivity fired with Data Saver on.", 885 mTestAppInterface.awaitJobStart(DEFAULT_TIMEOUT_MILLIS)); 886 } 887 888 /** 889 * Schedule an expedited job that requires a network connection, and verify that it runs even 890 * when multiple firewalls are active. 891 */ testExpeditedJobDoesNotBypassSimultaneousFirewalls_withDataSaver()892 public void testExpeditedJobDoesNotBypassSimultaneousFirewalls_withDataSaver() 893 throws Exception { 894 if (!BatteryUtils.isBatterySaverSupported()) { 895 Log.d(TAG, "Skipping test that requires battery saver support"); 896 return; 897 } 898 if (mHasWifi) { 899 setWifiMeteredState(true); 900 } else if (checkDeviceSupportsMobileData()) { 901 disconnectWifiToConnectToMobile(); 902 } else { 903 Log.d(TAG, "Skipping test that requires a metered network."); 904 return; 905 } 906 if (!AppStandbyUtils.isAppStandbyEnabled()) { 907 Log.d(TAG, "App standby not enabled"); 908 return; 909 } 910 911 mDeviceConfigStateHelper.set("qc_max_session_count_restricted", "0"); 912 SystemUtil.runShellCommand("am set-standby-bucket " 913 + kJobServiceComponent.getPackageName() + " restricted"); 914 BatteryUtils.runDumpsysBatteryUnplug(); 915 BatteryUtils.enableBatterySaver(true); 916 setDataSaverEnabled(true); 917 918 mTestAppInterface = new TestAppInterface(mContext, CONNECTIVITY_JOB_ID); 919 920 mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_ANY, true); 921 mTestAppInterface.runSatisfiedJob(); 922 923 assertFalse("Expedited job fired with multiple firewalls, including data saver.", 924 mTestAppInterface.awaitJobStart(DEFAULT_TIMEOUT_MILLIS)); 925 } 926 927 /** 928 * Schedule a user-initiated job that requires a network connection, and verify that it runs 929 * even when Data Saver is on and the device is not connected to WiFi. 930 */ testBgUiJobBypassesDataSaver()931 public void testBgUiJobBypassesDataSaver() throws Exception { 932 if (hasEthernetConnection()) { 933 Log.d(TAG, "Skipping test since ethernet is connected."); 934 return; 935 } 936 if (mHasWifi) { 937 setWifiMeteredState(true); 938 } else if (checkDeviceSupportsMobileData()) { 939 disconnectWifiToConnectToMobile(); 940 } else { 941 Log.d(TAG, "Skipping test that requires a metered network."); 942 return; 943 } 944 945 try (TestNotificationListener.NotificationHelper notificationHelper = 946 new TestNotificationListener.NotificationHelper( 947 mContext, TestAppInterface.TEST_APP_PACKAGE)) { 948 setDataSaverEnabled(true); 949 950 mTestAppInterface = new TestAppInterface(mContext, CONNECTIVITY_JOB_ID); 951 mTestAppInterface.closeActivity(true); 952 mTestAppInterface.postUiInitiatingNotification( 953 Map.of(TestJobSchedulerReceiver.EXTRA_AS_USER_INITIATED, true), 954 Map.of( 955 TestJobSchedulerReceiver.EXTRA_REQUIRED_NETWORK_TYPE, 956 JobInfo.NETWORK_TYPE_ANY 957 ) 958 ); 959 960 // Clicking on the notification should put the app into a BAL approved state. 961 notificationHelper.clickNotification(); 962 963 assertTrue("BG UI job requiring connectivity didn't fire with Data Saver on.", 964 mTestAppInterface.awaitJobStart(DEFAULT_TIMEOUT_MILLIS)); 965 } 966 } 967 968 /** 969 * Make sure that regular and expedited jobs don't run during data saver 970 * even if a user-initiated job is running at the same time. 971 */ testBgNonUiJobDoesNotBypassDataSaverWhenUiJobRunning()972 public void testBgNonUiJobDoesNotBypassDataSaverWhenUiJobRunning() throws Exception { 973 if (hasEthernetConnection()) { 974 Log.d(TAG, "Skipping test since ethernet is connected."); 975 return; 976 } 977 if (mHasWifi) { 978 setWifiMeteredState(true); 979 } else if (checkDeviceSupportsMobileData()) { 980 disconnectWifiToConnectToMobile(); 981 } else { 982 Log.d(TAG, "Skipping test that requires a metered network."); 983 return; 984 } 985 986 try (TestNotificationListener.NotificationHelper notificationHelper = 987 new TestNotificationListener.NotificationHelper( 988 mContext, TestAppInterface.TEST_APP_PACKAGE)) { 989 setDataSaverEnabled(true); 990 991 final int uiJobId = CONNECTIVITY_JOB_ID; 992 final int expJobId = CONNECTIVITY_JOB_ID + 1; 993 final int regJobId = CONNECTIVITY_JOB_ID + 2; 994 mTestAppInterface = new TestAppInterface(mContext, CONNECTIVITY_JOB_ID); 995 mTestAppInterface.closeActivity(true); 996 // Regular job 997 mTestAppInterface.scheduleJob( 998 Collections.emptyMap(), 999 Map.of( 1000 TestJobSchedulerReceiver.EXTRA_JOB_ID_KEY, regJobId, 1001 TestJobSchedulerReceiver.EXTRA_REQUIRED_NETWORK_TYPE, 1002 JobInfo.NETWORK_TYPE_ANY 1003 ) 1004 ); 1005 // EJ 1006 mTestAppInterface.scheduleJob( 1007 Map.of(TestJobSchedulerReceiver.EXTRA_AS_EXPEDITED, true), 1008 Map.of( 1009 TestJobSchedulerReceiver.EXTRA_JOB_ID_KEY, expJobId, 1010 TestJobSchedulerReceiver.EXTRA_REQUIRED_NETWORK_TYPE, 1011 JobInfo.NETWORK_TYPE_ANY 1012 ) 1013 ); 1014 // UI job 1015 mTestAppInterface.postUiInitiatingNotification( 1016 Map.of(TestJobSchedulerReceiver.EXTRA_AS_USER_INITIATED, true), 1017 Map.of( 1018 TestJobSchedulerReceiver.EXTRA_JOB_ID_KEY, uiJobId, 1019 TestJobSchedulerReceiver.EXTRA_REQUIRED_NETWORK_TYPE, 1020 JobInfo.NETWORK_TYPE_ANY 1021 ) 1022 ); 1023 1024 // Clicking on the notification should put the app into a BAL approved state. 1025 notificationHelper.clickNotification(); 1026 1027 assertTrue("BG UI job requiring connectivity didn't fire with Data Saver on.", 1028 mTestAppInterface.awaitJobStart(uiJobId, DEFAULT_TIMEOUT_MILLIS)); 1029 // The UI job may have started immediately, so keep the standard timeout for the 1030 // EJ check to give enough time to confirm the job didn't start. 1031 assertFalse("BG EJ requiring connectivity fired with Data Saver on.", 1032 mTestAppInterface.awaitJobStart(expJobId, DEFAULT_TIMEOUT_MILLIS)); 1033 // At this point, there's been enough time for this job to start, so don't have 1034 // a long wait time. 1035 assertFalse("BG job requiring connectivity fired with Data Saver on.", 1036 mTestAppInterface.awaitJobStart(regJobId, 1000)); 1037 } 1038 } 1039 1040 // -------------------------------------------------------------------------------------------- 1041 // Utility methods 1042 // -------------------------------------------------------------------------------------------- 1043 1044 /** 1045 * Determine whether the device running these CTS tests should be subject to tests involving 1046 * mobile data. 1047 * @return True if this device will support a mobile data connection. 1048 */ checkDeviceSupportsMobileData()1049 private boolean checkDeviceSupportsMobileData() throws Exception { 1050 return mNetworkingHelper.hasCellularNetwork(); 1051 } 1052 hasEthernetConnection()1053 private boolean hasEthernetConnection() { 1054 return mNetworkingHelper.hasEthernetConnection(); 1055 } 1056 setWifiMeteredState(boolean metered)1057 private void setWifiMeteredState(boolean metered) throws Exception { 1058 mNetworkingHelper.setWifiMeteredState(metered); 1059 } 1060 1061 /** 1062 * Ensure WiFi is enabled, and block until we've verified that we are in fact connected. 1063 */ connectToWifi()1064 private void connectToWifi() throws Exception { 1065 mNetworkingHelper.setWifiState(true); 1066 } 1067 1068 /** 1069 * Ensure WiFi is disabled, and block until we've verified that we are in fact disconnected. 1070 */ disconnectFromWifi()1071 private void disconnectFromWifi() throws Exception { 1072 mNetworkingHelper.setWifiState(false); 1073 } 1074 1075 /** 1076 * Disconnect from WiFi in an attempt to connect to cellular data. Worth noting that this is 1077 * best effort - there are no public APIs to force connecting to cell data. We disable WiFi 1078 * and wait for a broadcast that we're connected to cell. 1079 * We will not call into this function if the device doesn't support telephony. 1080 * @see NetworkingHelper#hasCellularNetwork 1081 * @see #checkDeviceSupportsMobileData() 1082 */ disconnectWifiToConnectToMobile()1083 private void disconnectWifiToConnectToMobile() throws Exception { 1084 mNetworkingHelper.setAllNetworksEnabled(true); 1085 if (mHasWifi && mWifiManager.isWifiEnabled()) { 1086 NetworkRequest nr = new NetworkRequest.Builder().clearCapabilities().build(); 1087 NetworkCapabilities nc = new NetworkCapabilities.Builder() 1088 .addTransportType(TRANSPORT_CELLULAR) 1089 .build(); 1090 NetworkingHelper.NetworkTracker tracker = 1091 new NetworkingHelper.NetworkTracker(nc, true, mCm); 1092 mCm.registerNetworkCallback(nr, tracker); 1093 1094 disconnectFromWifi(); 1095 1096 assertTrue("Device must have access to a metered network for this test.", 1097 tracker.waitForStateChange()); 1098 1099 mCm.unregisterNetworkCallback(tracker); 1100 } 1101 } 1102 1103 /** 1104 * Ensures that restrict background data usage policy is turned off. 1105 * If the policy is on, it interferes with tests that relies on metered connection. 1106 */ setDataSaverEnabled(boolean enabled)1107 private void setDataSaverEnabled(boolean enabled) throws Exception { 1108 mNetworkingHelper.setDataSaverEnabled(enabled); 1109 } 1110 } 1111