1 /* 2 * Copyright (C) 2020 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.jobscheduler.cts.TestAppInterface.ENFORCE_MINIMUM_TIME_WINDOWS; 20 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; 21 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; 22 import static android.text.format.DateUtils.HOUR_IN_MILLIS; 23 import static android.text.format.DateUtils.MINUTE_IN_MILLIS; 24 25 import android.app.compat.CompatChanges; 26 import android.app.job.Flags; 27 import android.app.job.JobInfo; 28 import android.content.ClipData; 29 import android.content.Intent; 30 import android.net.NetworkCapabilities; 31 import android.net.NetworkRequest; 32 import android.net.Uri; 33 import android.os.Bundle; 34 import android.os.PersistableBundle; 35 import android.platform.test.annotations.RequiresFlagsDisabled; 36 import android.platform.test.annotations.RequiresFlagsEnabled; 37 import android.provider.ContactsContract; 38 import android.provider.MediaStore; 39 import android.util.Log; 40 41 import com.android.compatibility.common.util.SystemUtil; 42 43 import java.lang.reflect.Method; 44 import java.util.Set; 45 46 /** 47 * Tests related to creating and reading JobInfo objects. 48 */ 49 public class JobInfoTest extends BaseJobSchedulerTest { 50 private static final int JOB_ID = JobInfoTest.class.hashCode(); 51 private static final String TAG = JobInfoTest.class.getSimpleName(); 52 53 private static final long REJECT_NEGATIVE_DELAYS_AND_DEADLINES = 323349338L; 54 private static final long THROW_ON_UNSUPPORTED_BIAS_USAGE = 300477393L; 55 56 @Override tearDown()57 public void tearDown() throws Exception { 58 mJobScheduler.cancel(JOB_ID); 59 60 // The super method should be called at the end. 61 super.tearDown(); 62 } 63 testBackoffCriteria()64 public void testBackoffCriteria() { 65 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 66 .setBackoffCriteria(12345, JobInfo.BACKOFF_POLICY_LINEAR) 67 .build(); 68 assertEquals(12345, ji.getInitialBackoffMillis()); 69 assertEquals(JobInfo.BACKOFF_POLICY_LINEAR, ji.getBackoffPolicy()); 70 // Confirm JobScheduler accepts the JobInfo object. 71 mJobScheduler.schedule(ji); 72 73 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 74 .setBackoffCriteria(54321, JobInfo.BACKOFF_POLICY_EXPONENTIAL) 75 .build(); 76 assertEquals(54321, ji.getInitialBackoffMillis()); 77 assertEquals(JobInfo.BACKOFF_POLICY_EXPONENTIAL, ji.getBackoffPolicy()); 78 // Confirm JobScheduler accepts the JobInfo object. 79 mJobScheduler.schedule(ji); 80 } 81 testBatteryNotLow()82 public void testBatteryNotLow() { 83 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 84 .setRequiresBatteryNotLow(true) 85 .build(); 86 assertTrue(ji.isRequireBatteryNotLow()); 87 // Confirm JobScheduler accepts the JobInfo object. 88 mJobScheduler.schedule(ji); 89 90 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 91 .setRequiresBatteryNotLow(false) 92 .build(); 93 assertFalse(ji.isRequireBatteryNotLow()); 94 // Confirm JobScheduler accepts the JobInfo object. 95 mJobScheduler.schedule(ji); 96 } 97 testBias()98 public void testBias() throws Exception { 99 JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, kJobServiceComponent); 100 101 Method setBiasMethod = JobInfo.Builder.class.getDeclaredMethod("setBias", int.class); 102 setBiasMethod.setAccessible(true); 103 setBiasMethod.invoke(builder, 40); 104 105 JobInfo ji = builder.build(); 106 // Confirm JobScheduler rejects the JobInfo object. 107 // TODO(b/309023462): create separate tests for target SDK gated changes 108 if (CompatChanges.isChangeEnabled(THROW_ON_UNSUPPORTED_BIAS_USAGE)) { 109 assertScheduleFailsWithException( 110 "Successfully scheduled a job with a modified bias", 111 ji, SecurityException.class); 112 } else { 113 mJobScheduler.schedule(ji); 114 115 assertEquals("Bias wasn't changed to default", 116 0, getBias(mJobScheduler.getPendingJob(JOB_ID))); 117 } 118 } 119 getBias(JobInfo job)120 private int getBias(JobInfo job) throws Exception { 121 Method getBiasMethod = JobInfo.class.getDeclaredMethod("getBias"); 122 getBiasMethod.setAccessible(true); 123 return (Integer) getBiasMethod.invoke(job); 124 } 125 testCharging()126 public void testCharging() { 127 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 128 .setRequiresCharging(true) 129 .build(); 130 assertTrue(ji.isRequireCharging()); 131 // Confirm JobScheduler accepts the JobInfo object. 132 mJobScheduler.schedule(ji); 133 134 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 135 .setRequiresCharging(false) 136 .build(); 137 assertFalse(ji.isRequireCharging()); 138 // Confirm JobScheduler accepts the JobInfo object. 139 mJobScheduler.schedule(ji); 140 } 141 testClipData()142 public void testClipData() { 143 final ClipData clipData = ClipData.newPlainText("test", "testText"); 144 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 145 .setClipData(clipData, Intent.FLAG_GRANT_READ_URI_PERMISSION) 146 .build(); 147 assertEquals(clipData, ji.getClipData()); 148 assertEquals(Intent.FLAG_GRANT_READ_URI_PERMISSION, ji.getClipGrantFlags()); 149 // Confirm JobScheduler accepts the JobInfo object. 150 mJobScheduler.schedule(ji); 151 152 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 153 .setClipData(null, 0) 154 .build(); 155 assertNull(ji.getClipData()); 156 assertEquals(0, ji.getClipGrantFlags()); 157 // Confirm JobScheduler accepts the JobInfo object. 158 mJobScheduler.schedule(ji); 159 } 160 161 // TODO(b/315035390): migrate to JUnit4 162 @RequiresFlagsEnabled(Flags.FLAG_JOB_DEBUG_INFO_APIS) // Doesn't work for JUnit3 testDebugTags()163 public void testDebugTags() { 164 if (!isAconfigFlagEnabled(Flags.FLAG_JOB_DEBUG_INFO_APIS)) { 165 return; 166 } 167 // Confirm defaults 168 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent).build(); 169 assertEquals(0, ji.getDebugTags().size()); 170 // Confirm JobScheduler accepts the JobInfo object. 171 mJobScheduler.schedule(ji); 172 173 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 174 .addDebugTag("a") 175 .addDebugTag("b") 176 .addDebugTag("c") 177 .build(); 178 assertEquals(Set.of("a", "b", "c"), ji.getDebugTags()); 179 // Confirm JobScheduler accepts the JobInfo object. 180 mJobScheduler.schedule(ji); 181 182 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 183 .addDebugTag("a") 184 .addDebugTag("b") 185 .addDebugTag("c") 186 .removeDebugTag("b") 187 .build(); 188 assertEquals(Set.of("a", "c"), ji.getDebugTags()); 189 // Confirm JobScheduler accepts the JobInfo object. 190 mJobScheduler.schedule(ji); 191 192 // Tag is at the character limit 193 final String maxLengthDebugTag = 194 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" 195 + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-"; 196 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 197 .addDebugTag(maxLengthDebugTag) 198 .build(); 199 assertEquals(Set.of(maxLengthDebugTag), ji.getDebugTags()); 200 // Confirm JobScheduler accepts the JobInfo object. 201 mJobScheduler.schedule(ji); 202 203 try { 204 new JobInfo.Builder(JOB_ID, kJobServiceComponent).addDebugTag(null).build(); 205 fail("Successfully built a JobInfo with a null debug tag"); 206 } catch (Exception e) { 207 // Success 208 } 209 try { 210 new JobInfo.Builder(JOB_ID, kJobServiceComponent).addDebugTag("").build(); 211 fail("Successfully built a JobInfo with an empty debug tag"); 212 } catch (Exception e) { 213 // Success 214 } 215 try { 216 new JobInfo.Builder(JOB_ID, kJobServiceComponent).addDebugTag(" ").build(); 217 fail("Successfully built a JobInfo with a whitespace-only debug tag"); 218 } catch (Exception e) { 219 // Success 220 } 221 try { 222 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 223 .setTraceTag(maxLengthDebugTag + "x").build(); 224 fail("Successfully built a JobInfo with a long debug tag"); 225 } catch (Exception e) { 226 // Success 227 } 228 JobInfo.Builder jiBuilder = new JobInfo.Builder(JOB_ID, kJobServiceComponent); 229 for (int i = 0; i < 33; ++i) { 230 jiBuilder.addDebugTag(Integer.toString(i)); 231 } 232 assertBuildFails("Successfully built a JobInfo with too many debug tags", jiBuilder); 233 } 234 testDeviceIdle()235 public void testDeviceIdle() { 236 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 237 .setRequiresDeviceIdle(true) 238 .build(); 239 assertTrue(ji.isRequireDeviceIdle()); 240 // Confirm JobScheduler accepts the JobInfo object. 241 mJobScheduler.schedule(ji); 242 243 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 244 .setRequiresDeviceIdle(false) 245 .build(); 246 assertFalse(ji.isRequireDeviceIdle()); 247 // Confirm JobScheduler accepts the JobInfo object. 248 mJobScheduler.schedule(ji); 249 } 250 testEstimatedNetworkBytes()251 public void testEstimatedNetworkBytes() { 252 assertBuildFails( 253 "Successfully built a JobInfo specifying estimated network bytes without" 254 + " requesting network", 255 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 256 .setEstimatedNetworkBytes(500, 1000)); 257 258 try { 259 assertBuildFails( 260 "Successfully built a JobInfo specifying a negative download bytes value", 261 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 262 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 263 .setEstimatedNetworkBytes(-500, JobInfo.NETWORK_BYTES_UNKNOWN)); 264 } catch (IllegalArgumentException expected) { 265 // Success. setMinimumNetworkChunkBytes() should throw the exception. 266 } 267 268 try { 269 assertBuildFails( 270 "Successfully built a JobInfo specifying a negative upload bytes value", 271 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 272 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 273 .setEstimatedNetworkBytes(JobInfo.NETWORK_BYTES_UNKNOWN, -500)); 274 } catch (IllegalArgumentException expected) { 275 // Success. setMinimumNetworkChunkBytes() should throw the exception. 276 } 277 278 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 279 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 280 .setEstimatedNetworkBytes(500, 1000) 281 .build(); 282 assertEquals(500, ji.getEstimatedNetworkDownloadBytes()); 283 assertEquals(1000, ji.getEstimatedNetworkUploadBytes()); 284 // Confirm JobScheduler accepts the JobInfo object. 285 mJobScheduler.schedule(ji); 286 287 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 288 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 289 .setEstimatedNetworkBytes( 290 JobInfo.NETWORK_BYTES_UNKNOWN, JobInfo.NETWORK_BYTES_UNKNOWN) 291 .build(); 292 assertEquals(JobInfo.NETWORK_BYTES_UNKNOWN, ji.getEstimatedNetworkDownloadBytes()); 293 assertEquals(JobInfo.NETWORK_BYTES_UNKNOWN, ji.getEstimatedNetworkUploadBytes()); 294 // Confirm JobScheduler accepts the JobInfo object. 295 mJobScheduler.schedule(ji); 296 } 297 testExtras()298 public void testExtras() { 299 final PersistableBundle pb = new PersistableBundle(); 300 pb.putInt("random_key", 42); 301 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 302 .setPersisted(true) 303 .setExtras(pb) 304 .build(); 305 final PersistableBundle extras = ji.getExtras(); 306 assertNotNull(extras); 307 assertEquals(1, extras.keySet().size()); 308 assertEquals(42, extras.getInt("random_key")); 309 // Confirm JobScheduler accepts the JobInfo object. 310 mJobScheduler.schedule(ji); 311 } 312 testExpeditedJob()313 public void testExpeditedJob() { 314 // Test all allowed constraints. 315 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 316 .setExpedited(true) 317 .setPriority(JobInfo.PRIORITY_HIGH) 318 .setPersisted(true) 319 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 320 .setRequiresStorageNotLow(true) 321 .build(); 322 assertTrue(ji.isExpedited()); 323 // Confirm JobScheduler accepts the JobInfo object. 324 mJobScheduler.schedule(ji); 325 326 // Confirm default priority for EJs. 327 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 328 .setExpedited(true) 329 .build(); 330 assertEquals(JobInfo.PRIORITY_MAX, ji.getPriority()); 331 // Confirm JobScheduler accepts the JobInfo object. 332 mJobScheduler.schedule(ji); 333 334 // Test disallowed constraints. 335 final String failureMessage = 336 "Successfully built an expedited JobInfo object with disallowed constraints"; 337 assertBuildFails(failureMessage, 338 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 339 .setExpedited(true) 340 .setMinimumLatency(100)); 341 assertBuildFails(failureMessage, 342 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 343 .setExpedited(true) 344 .setOverrideDeadline(24 * HOUR_IN_MILLIS)); 345 assertBuildFails(failureMessage, 346 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 347 .setExpedited(true) 348 .setPeriodic(15 * 60_000)); 349 assertBuildFails(failureMessage, 350 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 351 .setExpedited(true) 352 .setPriority(JobInfo.PRIORITY_LOW)); 353 assertBuildFails(failureMessage, 354 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 355 .setExpedited(true) 356 .setPriority(JobInfo.PRIORITY_DEFAULT)); 357 assertBuildFails(failureMessage, 358 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 359 .setExpedited(true) 360 .setPrefetch(true)); 361 assertBuildFails(failureMessage, 362 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 363 .setExpedited(true) 364 .setRequiresDeviceIdle(true)); 365 assertBuildFails(failureMessage, 366 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 367 .setExpedited(true) 368 .setRequiresBatteryNotLow(true)); 369 assertBuildFails(failureMessage, 370 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 371 .setExpedited(true) 372 .setRequiresCharging(true)); 373 assertBuildFails(failureMessage, 374 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 375 .setExpedited(true) 376 .setUserInitiated(true)); 377 final JobInfo.TriggerContentUri tcu = new JobInfo.TriggerContentUri( 378 Uri.parse("content://" + MediaStore.AUTHORITY + "/"), 379 JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS); 380 assertBuildFails(failureMessage, 381 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 382 .setExpedited(true) 383 .addTriggerContentUri(tcu)); 384 } 385 386 // TODO(b/315035390): migrate to JUnit4 387 @SuppressWarnings("deprecation") 388 @RequiresFlagsDisabled(Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND) testImportantWhileForeground_Legacy()389 public void testImportantWhileForeground_Legacy() { 390 if (isAconfigFlagEnabled(Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND)) { 391 return; 392 } 393 // Assert the default value is false 394 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 395 .build(); 396 assertFalse(ji.isImportantWhileForeground()); 397 // Confirm JobScheduler accepts the JobInfo object. 398 mJobScheduler.schedule(ji); 399 400 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 401 .setImportantWhileForeground(true) 402 .build(); 403 assertTrue(ji.isImportantWhileForeground()); 404 assertEquals(JobInfo.PRIORITY_HIGH, ji.getPriority()); 405 // Confirm JobScheduler accepts the JobInfo object. 406 mJobScheduler.schedule(ji); 407 408 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 409 .setImportantWhileForeground(false) 410 .build(); 411 assertFalse(ji.isImportantWhileForeground()); 412 // Confirm JobScheduler accepts the JobInfo object. 413 mJobScheduler.schedule(ji); 414 415 416 //noinspection deprecation 417 assertBuildFails("Successfully built a low-priority JobInfo object with" 418 + " disallowed important while foreground flag", 419 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 420 .setPriority(JobInfo.PRIORITY_LOW) 421 .setImportantWhileForeground(true)); 422 423 assertBuildFails("Successfully built a user-initiated JobInfo object with" 424 + " disallowed important while foreground flag", 425 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 426 .setUserInitiated(true) 427 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 428 .setImportantWhileForeground(true)); 429 430 assertBuildFails("Successfully built an expedited JobInfo object with" 431 + " disallowed important while foreground flag", 432 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 433 .setExpedited(true) 434 .setImportantWhileForeground(true)); 435 } 436 437 @SuppressWarnings("deprecation") 438 @RequiresFlagsEnabled(android.app.job.Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND) testImportantWhileForeground_Ignored()439 public void testImportantWhileForeground_Ignored() { 440 if (!isAconfigFlagEnabled(Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND)) { 441 return; 442 } 443 444 // Assert the value is false always 445 final JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 446 .setImportantWhileForeground(true) 447 .setPriority(JobInfo.PRIORITY_LOW) 448 .build(); 449 assertFalse(ji.isImportantWhileForeground()); 450 // No priority change. 451 assertEquals(JobInfo.PRIORITY_LOW, ji.getPriority()); 452 // Confirm JobScheduler accepts the JobInfo object. 453 mJobScheduler.schedule(ji); 454 } 455 testMinimumChunkSizeBytes()456 public void testMinimumChunkSizeBytes() { 457 assertBuildFails( 458 "Successfully built a JobInfo specifying minimum chunk bytes without" 459 + " requesting network", 460 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 461 .setMinimumNetworkChunkBytes(500)); 462 try { 463 assertBuildFails( 464 "Successfully built a JobInfo specifying minimum chunk bytes a negative value", 465 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 466 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 467 .setMinimumNetworkChunkBytes(-500)); 468 } catch (IllegalArgumentException expected) { 469 // Success. setMinimumNetworkChunkBytes() should throw the exception. 470 } 471 472 assertBuildFails( 473 "Successfully built a JobInfo with a higher minimum chunk size than total" 474 + " transfer size", 475 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 476 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 477 .setMinimumNetworkChunkBytes(500) 478 .setEstimatedNetworkBytes(5, 5)); 479 assertBuildFails( 480 "Successfully built a JobInfo with a higher minimum chunk size than total" 481 + " transfer size", 482 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 483 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 484 .setMinimumNetworkChunkBytes(500) 485 .setEstimatedNetworkBytes(JobInfo.NETWORK_BYTES_UNKNOWN, 5)); 486 assertBuildFails( 487 "Successfully built a JobInfo with a higher minimum chunk size than total" 488 + " transfer size", 489 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 490 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 491 .setMinimumNetworkChunkBytes(500) 492 .setEstimatedNetworkBytes(5, JobInfo.NETWORK_BYTES_UNKNOWN)); 493 494 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 495 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 496 .setMinimumNetworkChunkBytes(500) 497 .setEstimatedNetworkBytes( 498 JobInfo.NETWORK_BYTES_UNKNOWN, JobInfo.NETWORK_BYTES_UNKNOWN) 499 .build(); 500 assertEquals(500, ji.getMinimumNetworkChunkBytes()); 501 // Confirm JobScheduler accepts the JobInfo object. 502 mJobScheduler.schedule(ji); 503 } 504 testMinimumLatency()505 public void testMinimumLatency() { 506 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 507 .setMinimumLatency(1337) 508 .build(); 509 assertEquals(1337, ji.getMinLatencyMillis()); 510 // Confirm JobScheduler accepts the JobInfo object. 511 mJobScheduler.schedule(ji); 512 } 513 testMinimumLatency_negative()514 public void testMinimumLatency_negative() { 515 JobInfo.Builder jiBuilder = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 516 .setMinimumLatency(-1); 517 518 // TODO(b/309023462): create separate tests for target SDK gated changes 519 if (CompatChanges.isChangeEnabled(REJECT_NEGATIVE_DELAYS_AND_DEADLINES)) { 520 assertBuildFails("Successfully scheduled a job with a negative latency", jiBuilder); 521 } else { 522 // Confirm JobScheduler accepts the JobInfo object. 523 JobInfo ji = jiBuilder.build(); 524 assertEquals(0, ji.getMinLatencyMillis()); 525 mJobScheduler.schedule(ji); 526 } 527 } 528 testOverrideDeadline()529 public void testOverrideDeadline() { 530 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 531 .setOverrideDeadline(HOUR_IN_MILLIS) 532 .build(); 533 // ...why are the set/get methods named differently?? >.> 534 assertEquals(HOUR_IN_MILLIS, ji.getMaxExecutionDelayMillis()); 535 // Confirm JobScheduler accepts the JobInfo object. 536 mJobScheduler.schedule(ji); 537 } 538 testOverrideDeadline_minimumTimeWindows()539 public void testOverrideDeadline_minimumTimeWindows() throws Exception { 540 JobInfo.Builder jiBuilderShortFunctional = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 541 .setRequiresCharging(true) 542 .setMinimumLatency(MINUTE_IN_MILLIS) 543 .setOverrideDeadline(16 * MINUTE_IN_MILLIS - 1); 544 JobInfo.Builder jiBuilderShortNonfunctional = 545 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 546 .setMinimumLatency(MINUTE_IN_MILLIS) 547 .setOverrideDeadline(16 * MINUTE_IN_MILLIS - 1); 548 JobInfo.Builder jiBuilderLongFunctional = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 549 .setRequiresCharging(true) 550 .setMinimumLatency(MINUTE_IN_MILLIS) 551 .setOverrideDeadline(16 * MINUTE_IN_MILLIS); 552 JobInfo.Builder jiBuilderLongNonfunctional = 553 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 554 .setMinimumLatency(MINUTE_IN_MILLIS) 555 .setOverrideDeadline(16 * MINUTE_IN_MILLIS); 556 557 // TODO(b/309023462): create separate tests for target SDK gated changes 558 if (CompatChanges.isChangeEnabled(ENFORCE_MINIMUM_TIME_WINDOWS) && isAconfigFlagEnabled( 559 "android.app.job.enforce_minimum_time_windows")) { 560 // Confirm JobScheduler rejects the bad JobInfo objects. 561 assertBuildFails( 562 "Successfully scheduled a job with a short deadline and functional constraints", 563 jiBuilderShortFunctional); 564 } else { 565 // Confirm JobScheduler accepts the JobInfo objects. 566 mJobScheduler.schedule(jiBuilderShortFunctional.build()); 567 } 568 // Confirm JobScheduler accepts the good JobInfo objects. 569 mJobScheduler.schedule(jiBuilderShortNonfunctional.build()); 570 mJobScheduler.schedule(jiBuilderLongFunctional.build()); 571 mJobScheduler.schedule(jiBuilderLongNonfunctional.build()); 572 } 573 testOverrideDeadline_negative()574 public void testOverrideDeadline_negative() { 575 JobInfo.Builder jiBuilder = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 576 .setOverrideDeadline(-1); 577 578 // TODO(b/309023462): create separate tests for target SDK gated changes 579 if (CompatChanges.isChangeEnabled(REJECT_NEGATIVE_DELAYS_AND_DEADLINES)) { 580 assertBuildFails("Successfully scheduled a job with a negative deadline", jiBuilder); 581 } else { 582 // Confirm JobScheduler accepts the JobInfo object. 583 JobInfo ji = jiBuilder.build(); 584 assertTrue(ji.getMaxExecutionDelayMillis() >= 0); 585 mJobScheduler.schedule(ji); 586 } 587 } 588 testPeriodic()589 public void testPeriodic() { 590 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 591 .setPeriodic(60 * 60 * 1000L) 592 .build(); 593 assertTrue(ji.isPeriodic()); 594 assertEquals(60 * 60 * 1000L, ji.getIntervalMillis()); 595 assertEquals(60 * 60 * 1000L, ji.getFlexMillis()); 596 // Confirm JobScheduler accepts the JobInfo object. 597 mJobScheduler.schedule(ji); 598 599 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 600 .setPeriodic(120 * 60 * 1000L, 20 * 60 * 1000L) 601 .build(); 602 assertTrue(ji.isPeriodic()); 603 assertEquals(120 * 60 * 1000L, ji.getIntervalMillis()); 604 assertEquals(20 * 60 * 1000L, ji.getFlexMillis()); 605 // Confirm JobScheduler accepts the JobInfo object. 606 mJobScheduler.schedule(ji); 607 } 608 testPersisted()609 public void testPersisted() { 610 // Assert the default value is false 611 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 612 .build(); 613 assertFalse(ji.isPersisted()); 614 // Confirm JobScheduler accepts the JobInfo object. 615 mJobScheduler.schedule(ji); 616 617 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 618 .setPersisted(true) 619 .build(); 620 assertTrue(ji.isPersisted()); 621 // Confirm JobScheduler accepts the JobInfo object. 622 mJobScheduler.schedule(ji); 623 624 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 625 .setPersisted(false) 626 .build(); 627 assertFalse(ji.isPersisted()); 628 // Confirm JobScheduler accepts the JobInfo object. 629 mJobScheduler.schedule(ji); 630 } 631 testPrefetch()632 public void testPrefetch() { 633 // Assert the default value is false 634 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 635 .build(); 636 assertFalse(ji.isPrefetch()); 637 // Confirm JobScheduler accepts the JobInfo object. 638 mJobScheduler.schedule(ji); 639 640 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 641 .setPrefetch(true) 642 .build(); 643 assertTrue(ji.isPrefetch()); 644 // Confirm JobScheduler accepts the JobInfo object. 645 mJobScheduler.schedule(ji); 646 647 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 648 .setPrefetch(false) 649 .build(); 650 assertFalse(ji.isPrefetch()); 651 // Confirm JobScheduler accepts the JobInfo object. 652 mJobScheduler.schedule(ji); 653 654 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 655 .setMinimumLatency(60_000L) 656 .setPrefetch(true) 657 .build(); 658 assertTrue(ji.isPrefetch()); 659 // Confirm JobScheduler accepts the JobInfo object. 660 mJobScheduler.schedule(ji); 661 662 // CTS naturally targets latest SDK version. Compat change should be enabled by default. 663 assertBuildFails("Modern prefetch jobs can't have a deadline", 664 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 665 .setMinimumLatency(60_000L) 666 .setOverrideDeadline(24 * HOUR_IN_MILLIS) 667 .setPrefetch(true)); 668 } 669 testPriority()670 public void testPriority() { 671 // Assert the default value is DEFAULT 672 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 673 .build(); 674 assertEquals(JobInfo.PRIORITY_DEFAULT, ji.getPriority()); 675 // Confirm JobScheduler accepts the JobInfo object. 676 mJobScheduler.schedule(ji); 677 678 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 679 .setPriority(JobInfo.PRIORITY_LOW) 680 .build(); 681 assertEquals(JobInfo.PRIORITY_LOW, ji.getPriority()); 682 // Confirm JobScheduler accepts the JobInfo object. 683 mJobScheduler.schedule(ji); 684 685 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 686 .setPriority(JobInfo.PRIORITY_MIN) 687 .build(); 688 assertEquals(JobInfo.PRIORITY_MIN, ji.getPriority()); 689 // Confirm JobScheduler accepts the JobInfo object. 690 mJobScheduler.schedule(ji); 691 692 // Attempt an invalid number 693 try { 694 // It's over 9000!!! 695 new JobInfo.Builder(JOB_ID, kJobServiceComponent).setPriority(9001).build(); 696 fail("Successfully built a job with an invalid priority level"); 697 } catch (Exception e) { 698 // Success 699 } 700 try { 701 new JobInfo.Builder(JOB_ID, kJobServiceComponent).setPriority(-1).build(); 702 fail("Successfully built a job with an invalid priority level"); 703 } catch (Exception e) { 704 // Success 705 } 706 try { 707 new JobInfo.Builder(JOB_ID, kJobServiceComponent).setPriority(123).build(); 708 fail("Successfully built a job with an invalid priority level"); 709 } catch (Exception e) { 710 // Success 711 } 712 713 // Test other invalid configurations. 714 final String failureMessage = 715 "Successfully built a JobInfo object with disallowed priority configurations"; 716 assertBuildFails(failureMessage, 717 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 718 .setPriority(JobInfo.PRIORITY_MAX)); 719 assertBuildFails(failureMessage, 720 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 721 .setPriority(JobInfo.PRIORITY_HIGH) 722 .setPrefetch(true)); 723 assertBuildFails(failureMessage, 724 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 725 .setPriority(JobInfo.PRIORITY_HIGH) 726 .setPeriodic(JobInfo.getMinPeriodMillis())); 727 } 728 testRequiredNetwork()729 public void testRequiredNetwork() { 730 final NetworkRequest nr = new NetworkRequest.Builder() 731 .addCapability(NET_CAPABILITY_INTERNET) 732 .addCapability(NET_CAPABILITY_VALIDATED) 733 .build(); 734 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 735 .setRequiredNetwork(nr) 736 .build(); 737 assertEquals(nr, ji.getRequiredNetwork()); 738 // Confirm JobScheduler accepts the JobInfo object. 739 mJobScheduler.schedule(ji); 740 741 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 742 .setRequiredNetwork(null) 743 .build(); 744 assertNull(ji.getRequiredNetwork()); 745 // Confirm JobScheduler accepts the JobInfo object. 746 mJobScheduler.schedule(ji); 747 } 748 749 @SuppressWarnings("deprecation") testRequiredNetworkType()750 public void testRequiredNetworkType() { 751 // Assert the default value is NONE 752 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 753 .build(); 754 assertEquals(JobInfo.NETWORK_TYPE_NONE, ji.getNetworkType()); 755 assertNull(ji.getRequiredNetwork()); 756 // Confirm JobScheduler accepts the JobInfo object. 757 mJobScheduler.schedule(ji); 758 759 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 760 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 761 .build(); 762 assertEquals(JobInfo.NETWORK_TYPE_ANY, ji.getNetworkType()); 763 assertTrue(ji.getRequiredNetwork() 764 .hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)); 765 assertTrue(ji.getRequiredNetwork() 766 .hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)); 767 assertFalse(ji.getRequiredNetwork() 768 .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)); 769 assertFalse(ji.getRequiredNetwork() 770 .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)); 771 // Confirm JobScheduler accepts the JobInfo object. 772 mJobScheduler.schedule(ji); 773 774 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 775 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) 776 .build(); 777 assertEquals(JobInfo.NETWORK_TYPE_UNMETERED, ji.getNetworkType()); 778 assertTrue(ji.getRequiredNetwork() 779 .hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)); 780 assertTrue(ji.getRequiredNetwork() 781 .hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)); 782 assertFalse(ji.getRequiredNetwork() 783 .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)); 784 assertFalse(ji.getRequiredNetwork() 785 .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)); 786 // Confirm JobScheduler accepts the JobInfo object. 787 mJobScheduler.schedule(ji); 788 789 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 790 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NOT_ROAMING) 791 .build(); 792 assertEquals(JobInfo.NETWORK_TYPE_NOT_ROAMING, ji.getNetworkType()); 793 assertTrue(ji.getRequiredNetwork() 794 .hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)); 795 assertTrue(ji.getRequiredNetwork() 796 .hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)); 797 assertFalse(ji.getRequiredNetwork() 798 .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)); 799 assertFalse(ji.getRequiredNetwork() 800 .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)); 801 // Confirm JobScheduler accepts the JobInfo object. 802 mJobScheduler.schedule(ji); 803 804 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 805 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_CELLULAR) 806 .build(); 807 assertEquals(JobInfo.NETWORK_TYPE_CELLULAR, ji.getNetworkType()); 808 assertTrue(ji.getRequiredNetwork() 809 .hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)); 810 assertTrue(ji.getRequiredNetwork() 811 .hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)); 812 assertFalse(ji.getRequiredNetwork() 813 .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)); 814 assertFalse(ji.getRequiredNetwork() 815 .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)); 816 // Confirm JobScheduler accepts the JobInfo object. 817 mJobScheduler.schedule(ji); 818 819 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 820 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE) 821 .build(); 822 assertEquals(JobInfo.NETWORK_TYPE_NONE, ji.getNetworkType()); 823 assertNull(ji.getRequiredNetwork()); 824 // Confirm JobScheduler accepts the JobInfo object. 825 mJobScheduler.schedule(ji); 826 } 827 testStorageNotLow()828 public void testStorageNotLow() { 829 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 830 .setRequiresStorageNotLow(true) 831 .build(); 832 assertTrue(ji.isRequireStorageNotLow()); 833 // Confirm JobScheduler accepts the JobInfo object. 834 mJobScheduler.schedule(ji); 835 836 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 837 .setRequiresStorageNotLow(false) 838 .build(); 839 assertFalse(ji.isRequireStorageNotLow()); 840 // Confirm JobScheduler accepts the JobInfo object. 841 mJobScheduler.schedule(ji); 842 } 843 844 // TODO(b/315035390): migrate to JUnit4 845 @RequiresFlagsEnabled(Flags.FLAG_JOB_DEBUG_INFO_APIS) // Doesn't work for JUnit3 testTraceTag()846 public void testTraceTag() { 847 if (!isAconfigFlagEnabled(Flags.FLAG_JOB_DEBUG_INFO_APIS)) { 848 return; 849 } 850 // Confirm defaults 851 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent).build(); 852 assertNull(ji.getTraceTag()); 853 // Confirm JobScheduler accepts the JobInfo object. 854 mJobScheduler.schedule(ji); 855 856 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent).setTraceTag("tracing").build(); 857 assertEquals("tracing", ji.getTraceTag()); 858 // Confirm JobScheduler accepts the JobInfo object. 859 mJobScheduler.schedule(ji); 860 861 // Tag is at the character limit 862 final String maxLengthTraceTag = 863 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" 864 + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-"; 865 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 866 .setTraceTag(maxLengthTraceTag) 867 .build(); 868 assertEquals(maxLengthTraceTag, ji.getTraceTag()); 869 // Confirm JobScheduler accepts the JobInfo object. 870 mJobScheduler.schedule(ji); 871 872 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 873 .setTraceTag(null) 874 .build(); 875 assertNull(null, ji.getTraceTag()); 876 // Confirm JobScheduler accepts the JobInfo object. 877 mJobScheduler.schedule(ji); 878 879 try { 880 new JobInfo.Builder(JOB_ID, kJobServiceComponent).setTraceTag("").build(); 881 fail("Successfully built a JobInfo with an empty trace tag"); 882 } catch (Exception e) { 883 // Success 884 } 885 try { 886 new JobInfo.Builder(JOB_ID, kJobServiceComponent).setTraceTag(" ").build(); 887 fail("Successfully built a JobInfo with a whitespace-only trace tag"); 888 } catch (Exception e) { 889 // Success 890 } 891 try { 892 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 893 .setTraceTag(maxLengthTraceTag + "x").build(); 894 fail("Successfully built a JobInfo with a long trace tag"); 895 } catch (Exception e) { 896 // Success 897 } 898 } 899 testTransientExtras()900 public void testTransientExtras() { 901 final Bundle b = new Bundle(); 902 b.putBoolean("random_bool", true); 903 assertBuildFails("Successfully built a persisted JobInfo object with transient extras", 904 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 905 .setPersisted(true) 906 .setTransientExtras(b)); 907 908 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 909 .setTransientExtras(b) 910 .build(); 911 assertEquals(b.size(), ji.getTransientExtras().size()); 912 for (String key : b.keySet()) { 913 assertEquals(b.get(key), ji.getTransientExtras().get(key)); 914 } 915 // Confirm JobScheduler accepts the JobInfo object. 916 mJobScheduler.schedule(ji); 917 } 918 testTriggerContentMaxDelay()919 public void testTriggerContentMaxDelay() { 920 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 921 .setTriggerContentMaxDelay(1337) 922 .build(); 923 assertEquals(1337, ji.getTriggerContentMaxDelay()); 924 // Confirm JobScheduler accepts the JobInfo object. 925 mJobScheduler.schedule(ji); 926 } 927 testTriggerContentUpdateDelay()928 public void testTriggerContentUpdateDelay() { 929 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 930 .setTriggerContentUpdateDelay(1337) 931 .build(); 932 assertEquals(1337, ji.getTriggerContentUpdateDelay()); 933 // Confirm JobScheduler accepts the JobInfo object. 934 mJobScheduler.schedule(ji); 935 } 936 testTriggerContentUri()937 public void testTriggerContentUri() { 938 final Uri u = Uri.parse("content://" + MediaStore.AUTHORITY + "/"); 939 final JobInfo.TriggerContentUri tcu = new JobInfo.TriggerContentUri( 940 u, JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS); 941 assertEquals(u, tcu.getUri()); 942 assertEquals(JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS, tcu.getFlags()); 943 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 944 .addTriggerContentUri(tcu) 945 .build(); 946 assertEquals(1, ji.getTriggerContentUris().length); 947 assertEquals(tcu, ji.getTriggerContentUris()[0]); 948 // Confirm JobScheduler accepts the JobInfo object. 949 mJobScheduler.schedule(ji); 950 951 final Uri u2 = Uri.parse("content://" + ContactsContract.AUTHORITY + "/"); 952 final JobInfo.TriggerContentUri tcu2 = new JobInfo.TriggerContentUri(u2, 0); 953 assertEquals(u2, tcu2.getUri()); 954 assertEquals(0, tcu2.getFlags()); 955 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 956 .addTriggerContentUri(tcu) 957 .addTriggerContentUri(tcu2) 958 .build(); 959 assertEquals(2, ji.getTriggerContentUris().length); 960 assertEquals(tcu, ji.getTriggerContentUris()[0]); 961 assertEquals(tcu2, ji.getTriggerContentUris()[1]); 962 // Confirm JobScheduler accepts the JobInfo object. 963 mJobScheduler.schedule(ji); 964 } 965 testUserInitiatedJob()966 public void testUserInitiatedJob() { 967 // Test all allowed constraints. 968 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 969 .setUserInitiated(true) 970 .setBackoffCriteria(0, JobInfo.BACKOFF_POLICY_EXPONENTIAL) 971 .setPriority(JobInfo.PRIORITY_MAX) 972 .setPersisted(true) 973 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 974 .setRequiresStorageNotLow(true) 975 .setRequiresBatteryNotLow(true) 976 .setRequiresCharging(true) 977 .build(); 978 assertTrue(ji.isUserInitiated()); 979 // Confirm JobScheduler accepts the JobInfo object. 980 mJobScheduler.schedule(ji); 981 982 // Confirm default priority for UIJs. 983 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 984 .setUserInitiated(true) 985 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 986 .build(); 987 assertEquals(JobInfo.PRIORITY_MAX, ji.getPriority()); 988 // Confirm JobScheduler accepts the JobInfo object. 989 mJobScheduler.schedule(ji); 990 991 // Confirm linear backoff allowed 992 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 993 .setUserInitiated(true) 994 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 995 .setBackoffCriteria(0, JobInfo.BACKOFF_POLICY_LINEAR) 996 .build(); 997 assertTrue(ji.isUserInitiated()); 998 // Confirm JobScheduler accepts the JobInfo object. 999 mJobScheduler.schedule(ji); 1000 1001 // Test disallowed constraints. 1002 final String failureMessage = 1003 "Successfully built a user-initiated JobInfo object with disallowed constraints"; 1004 1005 assertBuildFails(failureMessage, 1006 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 1007 .setUserInitiated(true)); 1008 assertBuildFails(failureMessage, 1009 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 1010 .setUserInitiated(true) 1011 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 1012 .setMinimumLatency(100)); 1013 assertBuildFails(failureMessage, 1014 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 1015 .setUserInitiated(true) 1016 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 1017 .setOverrideDeadline(24 * HOUR_IN_MILLIS)); 1018 assertBuildFails(failureMessage, 1019 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 1020 .setUserInitiated(true) 1021 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 1022 .setPeriodic(15 * 60_000)); 1023 assertBuildFails(failureMessage, 1024 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 1025 .setUserInitiated(true) 1026 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 1027 .setPriority(JobInfo.PRIORITY_LOW)); 1028 assertBuildFails(failureMessage, 1029 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 1030 .setUserInitiated(true) 1031 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 1032 .setPriority(JobInfo.PRIORITY_HIGH)); 1033 assertBuildFails(failureMessage, 1034 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 1035 .setUserInitiated(true) 1036 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 1037 .setPriority(JobInfo.PRIORITY_DEFAULT)); 1038 assertBuildFails(failureMessage, 1039 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 1040 .setUserInitiated(true) 1041 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 1042 .setPrefetch(true)); 1043 assertBuildFails(failureMessage, 1044 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 1045 .setUserInitiated(true) 1046 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 1047 .setRequiresDeviceIdle(true)); 1048 assertBuildFails(failureMessage, 1049 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 1050 .setExpedited(true) 1051 .setUserInitiated(true) 1052 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); 1053 final JobInfo.TriggerContentUri tcu = new JobInfo.TriggerContentUri( 1054 Uri.parse("content://" + MediaStore.AUTHORITY + "/"), 1055 JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS); 1056 assertBuildFails(failureMessage, 1057 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 1058 .setUserInitiated(true) 1059 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 1060 .addTriggerContentUri(tcu)); 1061 } 1062 assertBuildFails(String message, JobInfo.Builder builder)1063 private void assertBuildFails(String message, JobInfo.Builder builder) { 1064 try { 1065 builder.build(); 1066 fail(message); 1067 } catch (IllegalArgumentException e) { 1068 // Expected 1069 } 1070 } 1071 assertScheduleFailsWithException( String message, JobInfo jobInfo, Class<? extends Exception> expectedExceptionClass)1072 private void assertScheduleFailsWithException( 1073 String message, JobInfo jobInfo, Class<? extends Exception> expectedExceptionClass) { 1074 try { 1075 mJobScheduler.schedule(jobInfo); 1076 fail(message); 1077 } catch (Exception e) { 1078 if (expectedExceptionClass.isInstance(e)) { 1079 // Expected 1080 } else { 1081 fail("Scheduling failed with wrong exception class." 1082 + " Got " + e.getClass().getSimpleName() 1083 + ", wanted " + expectedExceptionClass.getSimpleName()); 1084 } 1085 } 1086 } 1087 isAconfigFlagEnabled(String fullFlagName)1088 private boolean isAconfigFlagEnabled(String fullFlagName) { 1089 final String ogValue = SystemUtil.runShellCommand( 1090 "cmd jobscheduler get-aconfig-flag-state " + fullFlagName).trim(); 1091 final boolean enabled = Boolean.parseBoolean(ogValue); 1092 Log.d(TAG, fullFlagName + "=" + ogValue + " ... enabled=" + enabled); 1093 return enabled; 1094 } 1095 } 1096