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 17 package android.app.job; 18 19 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; 20 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; 21 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; 22 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; 23 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; 24 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; 25 import static android.util.TimeUtils.formatDuration; 26 27 import android.annotation.BytesLong; 28 import android.annotation.IntDef; 29 import android.annotation.NonNull; 30 import android.annotation.Nullable; 31 import android.annotation.RequiresPermission; 32 import android.content.ClipData; 33 import android.content.ComponentName; 34 import android.net.NetworkRequest; 35 import android.net.NetworkSpecifier; 36 import android.net.Uri; 37 import android.os.BaseBundle; 38 import android.os.Bundle; 39 import android.os.Parcel; 40 import android.os.Parcelable; 41 import android.os.PersistableBundle; 42 import android.util.Log; 43 44 import java.lang.annotation.Retention; 45 import java.lang.annotation.RetentionPolicy; 46 import java.util.ArrayList; 47 import java.util.Arrays; 48 import java.util.Objects; 49 50 /** 51 * Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the 52 * parameters required to schedule work against the calling application. These are constructed 53 * using the {@link JobInfo.Builder}. 54 * You must specify at least one sort of constraint on the JobInfo object that you are creating. 55 * The goal here is to provide the scheduler with high-level semantics about the work you want to 56 * accomplish. Doing otherwise with throw an exception in your app. 57 */ 58 public class JobInfo implements Parcelable { 59 private static String TAG = "JobInfo"; 60 61 /** @hide */ 62 @IntDef(prefix = { "NETWORK_TYPE_" }, value = { 63 NETWORK_TYPE_NONE, 64 NETWORK_TYPE_ANY, 65 NETWORK_TYPE_UNMETERED, 66 NETWORK_TYPE_NOT_ROAMING, 67 NETWORK_TYPE_CELLULAR, 68 }) 69 @Retention(RetentionPolicy.SOURCE) 70 public @interface NetworkType {} 71 72 /** Default. */ 73 public static final int NETWORK_TYPE_NONE = 0; 74 /** This job requires network connectivity. */ 75 public static final int NETWORK_TYPE_ANY = 1; 76 /** This job requires network connectivity that is unmetered. */ 77 public static final int NETWORK_TYPE_UNMETERED = 2; 78 /** This job requires network connectivity that is not roaming. */ 79 public static final int NETWORK_TYPE_NOT_ROAMING = 3; 80 /** This job requires network connectivity that is a cellular network. */ 81 public static final int NETWORK_TYPE_CELLULAR = 4; 82 83 /** 84 * This job requires metered connectivity such as most cellular data 85 * networks. 86 * 87 * @deprecated Cellular networks may be unmetered, or Wi-Fi networks may be 88 * metered, so this isn't a good way of selecting a specific 89 * transport. Instead, use {@link #NETWORK_TYPE_CELLULAR} or 90 * {@link android.net.NetworkRequest.Builder#addTransportType(int)} 91 * if your job requires a specific network transport. 92 */ 93 @Deprecated 94 public static final int NETWORK_TYPE_METERED = NETWORK_TYPE_CELLULAR; 95 96 /** Sentinel value indicating that bytes are unknown. */ 97 public static final int NETWORK_BYTES_UNKNOWN = -1; 98 99 /** 100 * Amount of backoff a job has initially by default, in milliseconds. 101 */ 102 public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L; // 30 seconds. 103 104 /** 105 * Maximum backoff we allow for a job, in milliseconds. 106 */ 107 public static final long MAX_BACKOFF_DELAY_MILLIS = 5 * 60 * 60 * 1000; // 5 hours. 108 109 /** @hide */ 110 @IntDef(prefix = { "BACKOFF_POLICY_" }, value = { 111 BACKOFF_POLICY_LINEAR, 112 BACKOFF_POLICY_EXPONENTIAL, 113 }) 114 @Retention(RetentionPolicy.SOURCE) 115 public @interface BackoffPolicy {} 116 117 /** 118 * Linearly back-off a failed job. See 119 * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)} 120 * retry_time(current_time, num_failures) = 121 * current_time + initial_backoff_millis * num_failures, num_failures >= 1 122 */ 123 public static final int BACKOFF_POLICY_LINEAR = 0; 124 125 /** 126 * Exponentially back-off a failed job. See 127 * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)} 128 * 129 * retry_time(current_time, num_failures) = 130 * current_time + initial_backoff_millis * 2 ^ (num_failures - 1), num_failures >= 1 131 */ 132 public static final int BACKOFF_POLICY_EXPONENTIAL = 1; 133 134 /* Minimum interval for a periodic job, in milliseconds. */ 135 private static final long MIN_PERIOD_MILLIS = 15 * 60 * 1000L; // 15 minutes 136 137 /* Minimum flex for a periodic job, in milliseconds. */ 138 private static final long MIN_FLEX_MILLIS = 5 * 60 * 1000L; // 5 minutes 139 140 /** 141 * Minimum backoff interval for a job, in milliseconds 142 * @hide 143 */ 144 public static final long MIN_BACKOFF_MILLIS = 10 * 1000L; // 10 seconds 145 146 /** 147 * Query the minimum interval allowed for periodic scheduled jobs. Attempting 148 * to declare a smaller period that this when scheduling a job will result in a 149 * job that is still periodic, but will run with this effective period. 150 * 151 * @return The minimum available interval for scheduling periodic jobs, in milliseconds. 152 */ getMinPeriodMillis()153 public static final long getMinPeriodMillis() { 154 return MIN_PERIOD_MILLIS; 155 } 156 157 /** 158 * Query the minimum flex time allowed for periodic scheduled jobs. Attempting 159 * to declare a shorter flex time than this when scheduling such a job will 160 * result in this amount as the effective flex time for the job. 161 * 162 * @return The minimum available flex time for scheduling periodic jobs, in milliseconds. 163 */ getMinFlexMillis()164 public static final long getMinFlexMillis() { 165 return MIN_FLEX_MILLIS; 166 } 167 168 /** 169 * Query the minimum automatic-reschedule backoff interval permitted for jobs. 170 * @hide 171 */ getMinBackoffMillis()172 public static final long getMinBackoffMillis() { 173 return MIN_BACKOFF_MILLIS; 174 } 175 176 /** 177 * Default type of backoff. 178 * @hide 179 */ 180 public static final int DEFAULT_BACKOFF_POLICY = BACKOFF_POLICY_EXPONENTIAL; 181 182 /** 183 * Default of {@link #getPriority}. 184 * @hide 185 */ 186 public static final int PRIORITY_DEFAULT = 0; 187 188 /** 189 * Value of {@link #getPriority} for expedited syncs. 190 * @hide 191 */ 192 public static final int PRIORITY_SYNC_EXPEDITED = 10; 193 194 /** 195 * Value of {@link #getPriority} for first time initialization syncs. 196 * @hide 197 */ 198 public static final int PRIORITY_SYNC_INITIALIZATION = 20; 199 200 /** 201 * Value of {@link #getPriority} for a foreground app (overrides the supplied 202 * JobInfo priority if it is smaller). 203 * @hide 204 */ 205 public static final int PRIORITY_FOREGROUND_APP = 30; 206 207 /** 208 * Value of {@link #getPriority} for the current top app (overrides the supplied 209 * JobInfo priority if it is smaller). 210 * @hide 211 */ 212 public static final int PRIORITY_TOP_APP = 40; 213 214 /** 215 * Adjustment of {@link #getPriority} if the app has often (50% or more of the time) 216 * been running jobs. 217 * @hide 218 */ 219 public static final int PRIORITY_ADJ_OFTEN_RUNNING = -40; 220 221 /** 222 * Adjustment of {@link #getPriority} if the app has always (90% or more of the time) 223 * been running jobs. 224 * @hide 225 */ 226 public static final int PRIORITY_ADJ_ALWAYS_RUNNING = -80; 227 228 /** 229 * Indicates that the implementation of this job will be using 230 * {@link JobService#startForeground(int, android.app.Notification)} to run 231 * in the foreground. 232 * <p> 233 * When set, the internal scheduling of this job will ignore any background 234 * network restrictions for the requesting app. Note that this flag alone 235 * doesn't actually place your {@link JobService} in the foreground; you 236 * still need to post the notification yourself. 237 * <p> 238 * To use this flag, the caller must hold the 239 * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL} permission. 240 * 241 * @hide 242 */ 243 public static final int FLAG_WILL_BE_FOREGROUND = 1 << 0; 244 245 /** 246 * Allows this job to run despite doze restrictions as long as the app is in the foreground 247 * or on the temporary whitelist 248 * @hide 249 */ 250 public static final int FLAG_IMPORTANT_WHILE_FOREGROUND = 1 << 1; 251 252 /** 253 * @hide 254 */ 255 public static final int FLAG_PREFETCH = 1 << 2; 256 257 /** 258 * This job needs to be exempted from the app standby throttling. Only the system (UID 1000) 259 * can set it. Jobs with a time constrant must not have it. 260 * 261 * @hide 262 */ 263 public static final int FLAG_EXEMPT_FROM_APP_STANDBY = 1 << 3; 264 265 /** 266 * @hide 267 */ 268 public static final int CONSTRAINT_FLAG_CHARGING = 1 << 0; 269 270 /** 271 * @hide 272 */ 273 public static final int CONSTRAINT_FLAG_BATTERY_NOT_LOW = 1 << 1; 274 275 /** 276 * @hide 277 */ 278 public static final int CONSTRAINT_FLAG_DEVICE_IDLE = 1 << 2; 279 280 /** 281 * @hide 282 */ 283 public static final int CONSTRAINT_FLAG_STORAGE_NOT_LOW = 1 << 3; 284 285 private final int jobId; 286 private final PersistableBundle extras; 287 private final Bundle transientExtras; 288 private final ClipData clipData; 289 private final int clipGrantFlags; 290 private final ComponentName service; 291 private final int constraintFlags; 292 private final TriggerContentUri[] triggerContentUris; 293 private final long triggerContentUpdateDelay; 294 private final long triggerContentMaxDelay; 295 private final boolean hasEarlyConstraint; 296 private final boolean hasLateConstraint; 297 private final NetworkRequest networkRequest; 298 private final long networkDownloadBytes; 299 private final long networkUploadBytes; 300 private final long minLatencyMillis; 301 private final long maxExecutionDelayMillis; 302 private final boolean isPeriodic; 303 private final boolean isPersisted; 304 private final long intervalMillis; 305 private final long flexMillis; 306 private final long initialBackoffMillis; 307 private final int backoffPolicy; 308 private final int priority; 309 private final int flags; 310 311 /** 312 * Unique job id associated with this application (uid). This is the same job ID 313 * you supplied in the {@link Builder} constructor. 314 */ getId()315 public int getId() { 316 return jobId; 317 } 318 319 /** 320 * @see JobInfo.Builder#setExtras(PersistableBundle) 321 */ getExtras()322 public @NonNull PersistableBundle getExtras() { 323 return extras; 324 } 325 326 /** 327 * @see JobInfo.Builder#setTransientExtras(Bundle) 328 */ getTransientExtras()329 public @NonNull Bundle getTransientExtras() { 330 return transientExtras; 331 } 332 333 /** 334 * @see JobInfo.Builder#setClipData(ClipData, int) 335 */ getClipData()336 public @Nullable ClipData getClipData() { 337 return clipData; 338 } 339 340 /** 341 * @see JobInfo.Builder#setClipData(ClipData, int) 342 */ getClipGrantFlags()343 public int getClipGrantFlags() { 344 return clipGrantFlags; 345 } 346 347 /** 348 * Name of the service endpoint that will be called back into by the JobScheduler. 349 */ getService()350 public @NonNull ComponentName getService() { 351 return service; 352 } 353 354 /** @hide */ getPriority()355 public int getPriority() { 356 return priority; 357 } 358 359 /** @hide */ getFlags()360 public int getFlags() { 361 return flags; 362 } 363 364 /** @hide */ isExemptedFromAppStandby()365 public boolean isExemptedFromAppStandby() { 366 return ((flags & FLAG_EXEMPT_FROM_APP_STANDBY) != 0) && !isPeriodic(); 367 } 368 369 /** 370 * @see JobInfo.Builder#setRequiresCharging(boolean) 371 */ isRequireCharging()372 public boolean isRequireCharging() { 373 return (constraintFlags & CONSTRAINT_FLAG_CHARGING) != 0; 374 } 375 376 /** 377 * @see JobInfo.Builder#setRequiresBatteryNotLow(boolean) 378 */ isRequireBatteryNotLow()379 public boolean isRequireBatteryNotLow() { 380 return (constraintFlags & CONSTRAINT_FLAG_BATTERY_NOT_LOW) != 0; 381 } 382 383 /** 384 * @see JobInfo.Builder#setRequiresDeviceIdle(boolean) 385 */ isRequireDeviceIdle()386 public boolean isRequireDeviceIdle() { 387 return (constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0; 388 } 389 390 /** 391 * @see JobInfo.Builder#setRequiresStorageNotLow(boolean) 392 */ isRequireStorageNotLow()393 public boolean isRequireStorageNotLow() { 394 return (constraintFlags & CONSTRAINT_FLAG_STORAGE_NOT_LOW) != 0; 395 } 396 397 /** 398 * @hide 399 */ getConstraintFlags()400 public int getConstraintFlags() { 401 return constraintFlags; 402 } 403 404 /** 405 * Which content: URIs must change for the job to be scheduled. Returns null 406 * if there are none required. 407 * @see JobInfo.Builder#addTriggerContentUri(TriggerContentUri) 408 */ getTriggerContentUris()409 public @Nullable TriggerContentUri[] getTriggerContentUris() { 410 return triggerContentUris; 411 } 412 413 /** 414 * When triggering on content URI changes, this is the delay from when a change 415 * is detected until the job is scheduled. 416 * @see JobInfo.Builder#setTriggerContentUpdateDelay(long) 417 */ getTriggerContentUpdateDelay()418 public long getTriggerContentUpdateDelay() { 419 return triggerContentUpdateDelay; 420 } 421 422 /** 423 * When triggering on content URI changes, this is the maximum delay we will 424 * use before scheduling the job. 425 * @see JobInfo.Builder#setTriggerContentMaxDelay(long) 426 */ getTriggerContentMaxDelay()427 public long getTriggerContentMaxDelay() { 428 return triggerContentMaxDelay; 429 } 430 431 /** 432 * Return the basic description of the kind of network this job requires. 433 * 434 * @deprecated This method attempts to map {@link #getRequiredNetwork()} 435 * into the set of simple constants, which results in a loss of 436 * fidelity. Callers should move to using 437 * {@link #getRequiredNetwork()} directly. 438 * @see Builder#setRequiredNetworkType(int) 439 */ 440 @Deprecated getNetworkType()441 public @NetworkType int getNetworkType() { 442 if (networkRequest == null) { 443 return NETWORK_TYPE_NONE; 444 } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) { 445 return NETWORK_TYPE_UNMETERED; 446 } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING)) { 447 return NETWORK_TYPE_NOT_ROAMING; 448 } else if (networkRequest.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) { 449 return NETWORK_TYPE_CELLULAR; 450 } else { 451 return NETWORK_TYPE_ANY; 452 } 453 } 454 455 /** 456 * Return the detailed description of the kind of network this job requires, 457 * or {@code null} if no specific kind of network is required. 458 * 459 * @see Builder#setRequiredNetwork(NetworkRequest) 460 */ getRequiredNetwork()461 public @Nullable NetworkRequest getRequiredNetwork() { 462 return networkRequest; 463 } 464 465 /** 466 * @deprecated replaced by {@link #getEstimatedNetworkDownloadBytes()} and 467 * {@link #getEstimatedNetworkUploadBytes()}. 468 * @removed 469 */ 470 @Deprecated getEstimatedNetworkBytes()471 public @BytesLong long getEstimatedNetworkBytes() { 472 if (networkDownloadBytes == NETWORK_BYTES_UNKNOWN 473 && networkUploadBytes == NETWORK_BYTES_UNKNOWN) { 474 return NETWORK_BYTES_UNKNOWN; 475 } else if (networkDownloadBytes == NETWORK_BYTES_UNKNOWN) { 476 return networkUploadBytes; 477 } else if (networkUploadBytes == NETWORK_BYTES_UNKNOWN) { 478 return networkDownloadBytes; 479 } else { 480 return networkDownloadBytes + networkUploadBytes; 481 } 482 } 483 484 /** 485 * Return the estimated size of download traffic that will be performed by 486 * this job, in bytes. 487 * 488 * @return Estimated size of download traffic, or 489 * {@link #NETWORK_BYTES_UNKNOWN} when unknown. 490 * @see Builder#setEstimatedNetworkBytes(long, long) 491 */ getEstimatedNetworkDownloadBytes()492 public @BytesLong long getEstimatedNetworkDownloadBytes() { 493 return networkDownloadBytes; 494 } 495 496 /** 497 * Return the estimated size of upload traffic that will be performed by 498 * this job, in bytes. 499 * 500 * @return Estimated size of upload traffic, or 501 * {@link #NETWORK_BYTES_UNKNOWN} when unknown. 502 * @see Builder#setEstimatedNetworkBytes(long, long) 503 */ getEstimatedNetworkUploadBytes()504 public @BytesLong long getEstimatedNetworkUploadBytes() { 505 return networkUploadBytes; 506 } 507 508 /** 509 * Set for a job that does not recur periodically, to specify a delay after which the job 510 * will be eligible for execution. This value is not set if the job recurs periodically. 511 * @see JobInfo.Builder#setMinimumLatency(long) 512 */ getMinLatencyMillis()513 public long getMinLatencyMillis() { 514 return minLatencyMillis; 515 } 516 517 /** 518 * @see JobInfo.Builder#setOverrideDeadline(long) 519 */ getMaxExecutionDelayMillis()520 public long getMaxExecutionDelayMillis() { 521 return maxExecutionDelayMillis; 522 } 523 524 /** 525 * Track whether this job will repeat with a given period. 526 * @see JobInfo.Builder#setPeriodic(long) 527 * @see JobInfo.Builder#setPeriodic(long, long) 528 */ isPeriodic()529 public boolean isPeriodic() { 530 return isPeriodic; 531 } 532 533 /** 534 * @see JobInfo.Builder#setPersisted(boolean) 535 */ isPersisted()536 public boolean isPersisted() { 537 return isPersisted; 538 } 539 540 /** 541 * Set to the interval between occurrences of this job. This value is <b>not</b> set if the 542 * job does not recur periodically. 543 * @see JobInfo.Builder#setPeriodic(long) 544 * @see JobInfo.Builder#setPeriodic(long, long) 545 */ getIntervalMillis()546 public long getIntervalMillis() { 547 return intervalMillis; 548 } 549 550 /** 551 * Flex time for this job. Only valid if this is a periodic job. The job can 552 * execute at any time in a window of flex length at the end of the period. 553 * @see JobInfo.Builder#setPeriodic(long) 554 * @see JobInfo.Builder#setPeriodic(long, long) 555 */ getFlexMillis()556 public long getFlexMillis() { 557 return flexMillis; 558 } 559 560 /** 561 * The amount of time the JobScheduler will wait before rescheduling a failed job. This value 562 * will be increased depending on the backoff policy specified at job creation time. Defaults 563 * to 30 seconds, minimum is currently 10 seconds. 564 * @see JobInfo.Builder#setBackoffCriteria(long, int) 565 */ getInitialBackoffMillis()566 public long getInitialBackoffMillis() { 567 return initialBackoffMillis; 568 } 569 570 /** 571 * Return the backoff policy of this job. 572 * @see JobInfo.Builder#setBackoffCriteria(long, int) 573 */ getBackoffPolicy()574 public @BackoffPolicy int getBackoffPolicy() { 575 return backoffPolicy; 576 } 577 578 /** 579 * @see JobInfo.Builder#setImportantWhileForeground(boolean) 580 */ isImportantWhileForeground()581 public boolean isImportantWhileForeground() { 582 return (flags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0; 583 } 584 585 /** 586 * @see JobInfo.Builder#setPrefetch(boolean) 587 */ isPrefetch()588 public boolean isPrefetch() { 589 return (flags & FLAG_PREFETCH) != 0; 590 } 591 592 /** 593 * User can specify an early constraint of 0L, which is valid, so we keep track of whether the 594 * function was called at all. 595 * @hide 596 */ hasEarlyConstraint()597 public boolean hasEarlyConstraint() { 598 return hasEarlyConstraint; 599 } 600 601 /** 602 * User can specify a late constraint of 0L, which is valid, so we keep track of whether the 603 * function was called at all. 604 * @hide 605 */ hasLateConstraint()606 public boolean hasLateConstraint() { 607 return hasLateConstraint; 608 } 609 kindofEqualsBundle(BaseBundle a, BaseBundle b)610 private static boolean kindofEqualsBundle(BaseBundle a, BaseBundle b) { 611 return (a == b) || (a != null && a.kindofEquals(b)); 612 } 613 614 @Override equals(Object o)615 public boolean equals(Object o) { 616 if (!(o instanceof JobInfo)) { 617 return false; 618 } 619 JobInfo j = (JobInfo) o; 620 if (jobId != j.jobId) { 621 return false; 622 } 623 // XXX won't be correct if one is parcelled and the other not. 624 if (!kindofEqualsBundle(extras, j.extras)) { 625 return false; 626 } 627 // XXX won't be correct if one is parcelled and the other not. 628 if (!kindofEqualsBundle(transientExtras, j.transientExtras)) { 629 return false; 630 } 631 // XXX for now we consider two different clip data objects to be different, 632 // regardless of whether their contents are the same. 633 if (clipData != j.clipData) { 634 return false; 635 } 636 if (clipGrantFlags != j.clipGrantFlags) { 637 return false; 638 } 639 if (!Objects.equals(service, j.service)) { 640 return false; 641 } 642 if (constraintFlags != j.constraintFlags) { 643 return false; 644 } 645 if (!Arrays.equals(triggerContentUris, j.triggerContentUris)) { 646 return false; 647 } 648 if (triggerContentUpdateDelay != j.triggerContentUpdateDelay) { 649 return false; 650 } 651 if (triggerContentMaxDelay != j.triggerContentMaxDelay) { 652 return false; 653 } 654 if (hasEarlyConstraint != j.hasEarlyConstraint) { 655 return false; 656 } 657 if (hasLateConstraint != j.hasLateConstraint) { 658 return false; 659 } 660 if (!Objects.equals(networkRequest, j.networkRequest)) { 661 return false; 662 } 663 if (networkDownloadBytes != j.networkDownloadBytes) { 664 return false; 665 } 666 if (networkUploadBytes != j.networkUploadBytes) { 667 return false; 668 } 669 if (minLatencyMillis != j.minLatencyMillis) { 670 return false; 671 } 672 if (maxExecutionDelayMillis != j.maxExecutionDelayMillis) { 673 return false; 674 } 675 if (isPeriodic != j.isPeriodic) { 676 return false; 677 } 678 if (isPersisted != j.isPersisted) { 679 return false; 680 } 681 if (intervalMillis != j.intervalMillis) { 682 return false; 683 } 684 if (flexMillis != j.flexMillis) { 685 return false; 686 } 687 if (initialBackoffMillis != j.initialBackoffMillis) { 688 return false; 689 } 690 if (backoffPolicy != j.backoffPolicy) { 691 return false; 692 } 693 if (priority != j.priority) { 694 return false; 695 } 696 if (flags != j.flags) { 697 return false; 698 } 699 return true; 700 } 701 702 @Override hashCode()703 public int hashCode() { 704 int hashCode = jobId; 705 if (extras != null) { 706 hashCode = 31 * hashCode + extras.hashCode(); 707 } 708 if (transientExtras != null) { 709 hashCode = 31 * hashCode + transientExtras.hashCode(); 710 } 711 if (clipData != null) { 712 hashCode = 31 * hashCode + clipData.hashCode(); 713 } 714 hashCode = 31*hashCode + clipGrantFlags; 715 if (service != null) { 716 hashCode = 31 * hashCode + service.hashCode(); 717 } 718 hashCode = 31 * hashCode + constraintFlags; 719 if (triggerContentUris != null) { 720 hashCode = 31 * hashCode + Arrays.hashCode(triggerContentUris); 721 } 722 hashCode = 31 * hashCode + Long.hashCode(triggerContentUpdateDelay); 723 hashCode = 31 * hashCode + Long.hashCode(triggerContentMaxDelay); 724 hashCode = 31 * hashCode + Boolean.hashCode(hasEarlyConstraint); 725 hashCode = 31 * hashCode + Boolean.hashCode(hasLateConstraint); 726 if (networkRequest != null) { 727 hashCode = 31 * hashCode + networkRequest.hashCode(); 728 } 729 hashCode = 31 * hashCode + Long.hashCode(networkDownloadBytes); 730 hashCode = 31 * hashCode + Long.hashCode(networkUploadBytes); 731 hashCode = 31 * hashCode + Long.hashCode(minLatencyMillis); 732 hashCode = 31 * hashCode + Long.hashCode(maxExecutionDelayMillis); 733 hashCode = 31 * hashCode + Boolean.hashCode(isPeriodic); 734 hashCode = 31 * hashCode + Boolean.hashCode(isPersisted); 735 hashCode = 31 * hashCode + Long.hashCode(intervalMillis); 736 hashCode = 31 * hashCode + Long.hashCode(flexMillis); 737 hashCode = 31 * hashCode + Long.hashCode(initialBackoffMillis); 738 hashCode = 31 * hashCode + backoffPolicy; 739 hashCode = 31 * hashCode + priority; 740 hashCode = 31 * hashCode + flags; 741 return hashCode; 742 } 743 JobInfo(Parcel in)744 private JobInfo(Parcel in) { 745 jobId = in.readInt(); 746 extras = in.readPersistableBundle(); 747 transientExtras = in.readBundle(); 748 if (in.readInt() != 0) { 749 clipData = ClipData.CREATOR.createFromParcel(in); 750 clipGrantFlags = in.readInt(); 751 } else { 752 clipData = null; 753 clipGrantFlags = 0; 754 } 755 service = in.readParcelable(null); 756 constraintFlags = in.readInt(); 757 triggerContentUris = in.createTypedArray(TriggerContentUri.CREATOR); 758 triggerContentUpdateDelay = in.readLong(); 759 triggerContentMaxDelay = in.readLong(); 760 if (in.readInt() != 0) { 761 networkRequest = NetworkRequest.CREATOR.createFromParcel(in); 762 } else { 763 networkRequest = null; 764 } 765 networkDownloadBytes = in.readLong(); 766 networkUploadBytes = in.readLong(); 767 minLatencyMillis = in.readLong(); 768 maxExecutionDelayMillis = in.readLong(); 769 isPeriodic = in.readInt() == 1; 770 isPersisted = in.readInt() == 1; 771 intervalMillis = in.readLong(); 772 flexMillis = in.readLong(); 773 initialBackoffMillis = in.readLong(); 774 backoffPolicy = in.readInt(); 775 hasEarlyConstraint = in.readInt() == 1; 776 hasLateConstraint = in.readInt() == 1; 777 priority = in.readInt(); 778 flags = in.readInt(); 779 } 780 JobInfo(JobInfo.Builder b)781 private JobInfo(JobInfo.Builder b) { 782 jobId = b.mJobId; 783 extras = b.mExtras.deepCopy(); 784 transientExtras = b.mTransientExtras.deepCopy(); 785 clipData = b.mClipData; 786 clipGrantFlags = b.mClipGrantFlags; 787 service = b.mJobService; 788 constraintFlags = b.mConstraintFlags; 789 triggerContentUris = b.mTriggerContentUris != null 790 ? b.mTriggerContentUris.toArray(new TriggerContentUri[b.mTriggerContentUris.size()]) 791 : null; 792 triggerContentUpdateDelay = b.mTriggerContentUpdateDelay; 793 triggerContentMaxDelay = b.mTriggerContentMaxDelay; 794 networkRequest = b.mNetworkRequest; 795 networkDownloadBytes = b.mNetworkDownloadBytes; 796 networkUploadBytes = b.mNetworkUploadBytes; 797 minLatencyMillis = b.mMinLatencyMillis; 798 maxExecutionDelayMillis = b.mMaxExecutionDelayMillis; 799 isPeriodic = b.mIsPeriodic; 800 isPersisted = b.mIsPersisted; 801 intervalMillis = b.mIntervalMillis; 802 flexMillis = b.mFlexMillis; 803 initialBackoffMillis = b.mInitialBackoffMillis; 804 backoffPolicy = b.mBackoffPolicy; 805 hasEarlyConstraint = b.mHasEarlyConstraint; 806 hasLateConstraint = b.mHasLateConstraint; 807 priority = b.mPriority; 808 flags = b.mFlags; 809 } 810 811 @Override describeContents()812 public int describeContents() { 813 return 0; 814 } 815 816 @Override writeToParcel(Parcel out, int flags)817 public void writeToParcel(Parcel out, int flags) { 818 out.writeInt(jobId); 819 out.writePersistableBundle(extras); 820 out.writeBundle(transientExtras); 821 if (clipData != null) { 822 out.writeInt(1); 823 clipData.writeToParcel(out, flags); 824 out.writeInt(clipGrantFlags); 825 } else { 826 out.writeInt(0); 827 } 828 out.writeParcelable(service, flags); 829 out.writeInt(constraintFlags); 830 out.writeTypedArray(triggerContentUris, flags); 831 out.writeLong(triggerContentUpdateDelay); 832 out.writeLong(triggerContentMaxDelay); 833 if (networkRequest != null) { 834 out.writeInt(1); 835 networkRequest.writeToParcel(out, flags); 836 } else { 837 out.writeInt(0); 838 } 839 out.writeLong(networkDownloadBytes); 840 out.writeLong(networkUploadBytes); 841 out.writeLong(minLatencyMillis); 842 out.writeLong(maxExecutionDelayMillis); 843 out.writeInt(isPeriodic ? 1 : 0); 844 out.writeInt(isPersisted ? 1 : 0); 845 out.writeLong(intervalMillis); 846 out.writeLong(flexMillis); 847 out.writeLong(initialBackoffMillis); 848 out.writeInt(backoffPolicy); 849 out.writeInt(hasEarlyConstraint ? 1 : 0); 850 out.writeInt(hasLateConstraint ? 1 : 0); 851 out.writeInt(priority); 852 out.writeInt(this.flags); 853 } 854 855 public static final Creator<JobInfo> CREATOR = new Creator<JobInfo>() { 856 @Override 857 public JobInfo createFromParcel(Parcel in) { 858 return new JobInfo(in); 859 } 860 861 @Override 862 public JobInfo[] newArray(int size) { 863 return new JobInfo[size]; 864 } 865 }; 866 867 @Override toString()868 public String toString() { 869 return "(job:" + jobId + "/" + service.flattenToShortString() + ")"; 870 } 871 872 /** 873 * Information about a content URI modification that a job would like to 874 * trigger on. 875 */ 876 public static final class TriggerContentUri implements Parcelable { 877 private final Uri mUri; 878 private final int mFlags; 879 880 /** @hide */ 881 @Retention(RetentionPolicy.SOURCE) 882 @IntDef(flag = true, prefix = { "FLAG_" }, value = { 883 FLAG_NOTIFY_FOR_DESCENDANTS, 884 }) 885 public @interface Flags { } 886 887 /** 888 * Flag for trigger: also trigger if any descendants of the given URI change. 889 * Corresponds to the <var>notifyForDescendants</var> of 890 * {@link android.content.ContentResolver#registerContentObserver}. 891 */ 892 public static final int FLAG_NOTIFY_FOR_DESCENDANTS = 1<<0; 893 894 /** 895 * Create a new trigger description. 896 * @param uri The URI to observe. Must be non-null. 897 * @param flags Flags for the observer. 898 */ TriggerContentUri(@onNull Uri uri, @Flags int flags)899 public TriggerContentUri(@NonNull Uri uri, @Flags int flags) { 900 mUri = uri; 901 mFlags = flags; 902 } 903 904 /** 905 * Return the Uri this trigger was created for. 906 */ getUri()907 public Uri getUri() { 908 return mUri; 909 } 910 911 /** 912 * Return the flags supplied for the trigger. 913 */ getFlags()914 public @Flags int getFlags() { 915 return mFlags; 916 } 917 918 @Override equals(Object o)919 public boolean equals(Object o) { 920 if (!(o instanceof TriggerContentUri)) { 921 return false; 922 } 923 TriggerContentUri t = (TriggerContentUri) o; 924 return Objects.equals(t.mUri, mUri) && t.mFlags == mFlags; 925 } 926 927 @Override hashCode()928 public int hashCode() { 929 return (mUri == null ? 0 : mUri.hashCode()) ^ mFlags; 930 } 931 TriggerContentUri(Parcel in)932 private TriggerContentUri(Parcel in) { 933 mUri = Uri.CREATOR.createFromParcel(in); 934 mFlags = in.readInt(); 935 } 936 937 @Override describeContents()938 public int describeContents() { 939 return 0; 940 } 941 942 @Override writeToParcel(Parcel out, int flags)943 public void writeToParcel(Parcel out, int flags) { 944 mUri.writeToParcel(out, flags); 945 out.writeInt(mFlags); 946 } 947 948 public static final Creator<TriggerContentUri> CREATOR = new Creator<TriggerContentUri>() { 949 @Override 950 public TriggerContentUri createFromParcel(Parcel in) { 951 return new TriggerContentUri(in); 952 } 953 954 @Override 955 public TriggerContentUri[] newArray(int size) { 956 return new TriggerContentUri[size]; 957 } 958 }; 959 } 960 961 /** Builder class for constructing {@link JobInfo} objects. */ 962 public static final class Builder { 963 private final int mJobId; 964 private final ComponentName mJobService; 965 private PersistableBundle mExtras = PersistableBundle.EMPTY; 966 private Bundle mTransientExtras = Bundle.EMPTY; 967 private ClipData mClipData; 968 private int mClipGrantFlags; 969 private int mPriority = PRIORITY_DEFAULT; 970 private int mFlags; 971 // Requirements. 972 private int mConstraintFlags; 973 private NetworkRequest mNetworkRequest; 974 private long mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN; 975 private long mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN; 976 private ArrayList<TriggerContentUri> mTriggerContentUris; 977 private long mTriggerContentUpdateDelay = -1; 978 private long mTriggerContentMaxDelay = -1; 979 private boolean mIsPersisted; 980 // One-off parameters. 981 private long mMinLatencyMillis; 982 private long mMaxExecutionDelayMillis; 983 // Periodic parameters. 984 private boolean mIsPeriodic; 985 private boolean mHasEarlyConstraint; 986 private boolean mHasLateConstraint; 987 private long mIntervalMillis; 988 private long mFlexMillis; 989 // Back-off parameters. 990 private long mInitialBackoffMillis = DEFAULT_INITIAL_BACKOFF_MILLIS; 991 private int mBackoffPolicy = DEFAULT_BACKOFF_POLICY; 992 /** Easy way to track whether the client has tried to set a back-off policy. */ 993 private boolean mBackoffPolicySet = false; 994 995 /** 996 * Initialize a new Builder to construct a {@link JobInfo}. 997 * 998 * @param jobId Application-provided id for this job. Subsequent calls to cancel, or 999 * jobs created with the same jobId, will update the pre-existing job with 1000 * the same id. This ID must be unique across all clients of the same uid 1001 * (not just the same package). You will want to make sure this is a stable 1002 * id across app updates, so probably not based on a resource ID. 1003 * @param jobService The endpoint that you implement that will receive the callback from the 1004 * JobScheduler. 1005 */ Builder(int jobId, @NonNull ComponentName jobService)1006 public Builder(int jobId, @NonNull ComponentName jobService) { 1007 mJobService = jobService; 1008 mJobId = jobId; 1009 } 1010 1011 /** @hide */ setPriority(int priority)1012 public Builder setPriority(int priority) { 1013 mPriority = priority; 1014 return this; 1015 } 1016 1017 /** @hide */ setFlags(int flags)1018 public Builder setFlags(int flags) { 1019 mFlags = flags; 1020 return this; 1021 } 1022 1023 /** 1024 * Set optional extras. This is persisted, so we only allow primitive types. 1025 * @param extras Bundle containing extras you want the scheduler to hold on to for you. 1026 * @see JobInfo#getExtras() 1027 */ setExtras(@onNull PersistableBundle extras)1028 public Builder setExtras(@NonNull PersistableBundle extras) { 1029 mExtras = extras; 1030 return this; 1031 } 1032 1033 /** 1034 * Set optional transient extras. 1035 * 1036 * <p>Because setting this property is not compatible with persisted 1037 * jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when 1038 * {@link android.app.job.JobInfo.Builder#build()} is called.</p> 1039 * 1040 * @param extras Bundle containing extras you want the scheduler to hold on to for you. 1041 * @see JobInfo#getTransientExtras() 1042 */ setTransientExtras(@onNull Bundle extras)1043 public Builder setTransientExtras(@NonNull Bundle extras) { 1044 mTransientExtras = extras; 1045 return this; 1046 } 1047 1048 /** 1049 * Set a {@link ClipData} associated with this Job. 1050 * 1051 * <p>The main purpose of providing a ClipData is to allow granting of 1052 * URI permissions for data associated with the clip. The exact kind 1053 * of permission grant to perform is specified through <var>grantFlags</var>. 1054 * 1055 * <p>If the ClipData contains items that are Intents, any 1056 * grant flags in those Intents will be ignored. Only flags provided as an argument 1057 * to this method are respected, and will be applied to all Uri or 1058 * Intent items in the clip (or sub-items of the clip). 1059 * 1060 * <p>Because setting this property is not compatible with persisted 1061 * jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when 1062 * {@link android.app.job.JobInfo.Builder#build()} is called.</p> 1063 * 1064 * @param clip The new clip to set. May be null to clear the current clip. 1065 * @param grantFlags The desired permissions to grant for any URIs. This should be 1066 * a combination of {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION}, 1067 * {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, and 1068 * {@link android.content.Intent#FLAG_GRANT_PREFIX_URI_PERMISSION}. 1069 * @see JobInfo#getClipData() 1070 * @see JobInfo#getClipGrantFlags() 1071 */ setClipData(@ullable ClipData clip, int grantFlags)1072 public Builder setClipData(@Nullable ClipData clip, int grantFlags) { 1073 mClipData = clip; 1074 mClipGrantFlags = grantFlags; 1075 return this; 1076 } 1077 1078 /** 1079 * Set basic description of the kind of network your job requires. If 1080 * you need more precise control over network capabilities, see 1081 * {@link #setRequiredNetwork(NetworkRequest)}. 1082 * <p> 1083 * If your job doesn't need a network connection, you don't need to call 1084 * this method, as the default value is {@link #NETWORK_TYPE_NONE}. 1085 * <p> 1086 * Calling this method defines network as a strict requirement for your 1087 * job. If the network requested is not available your job will never 1088 * run. See {@link #setOverrideDeadline(long)} to change this behavior. 1089 * Calling this method will override any requirements previously defined 1090 * by {@link #setRequiredNetwork(NetworkRequest)}; you typically only 1091 * want to call one of these methods. 1092 * <p class="note"> 1093 * When your job executes in 1094 * {@link JobService#onStartJob(JobParameters)}, be sure to use the 1095 * specific network returned by {@link JobParameters#getNetwork()}, 1096 * otherwise you'll use the default network which may not meet this 1097 * constraint. 1098 * 1099 * @see #setRequiredNetwork(NetworkRequest) 1100 * @see JobInfo#getNetworkType() 1101 * @see JobParameters#getNetwork() 1102 */ setRequiredNetworkType(@etworkType int networkType)1103 public Builder setRequiredNetworkType(@NetworkType int networkType) { 1104 if (networkType == NETWORK_TYPE_NONE) { 1105 return setRequiredNetwork(null); 1106 } else { 1107 final NetworkRequest.Builder builder = new NetworkRequest.Builder(); 1108 1109 // All types require validated Internet 1110 builder.addCapability(NET_CAPABILITY_INTERNET); 1111 builder.addCapability(NET_CAPABILITY_VALIDATED); 1112 builder.removeCapability(NET_CAPABILITY_NOT_VPN); 1113 1114 if (networkType == NETWORK_TYPE_ANY) { 1115 // No other capabilities 1116 } else if (networkType == NETWORK_TYPE_UNMETERED) { 1117 builder.addCapability(NET_CAPABILITY_NOT_METERED); 1118 } else if (networkType == NETWORK_TYPE_NOT_ROAMING) { 1119 builder.addCapability(NET_CAPABILITY_NOT_ROAMING); 1120 } else if (networkType == NETWORK_TYPE_CELLULAR) { 1121 builder.addTransportType(TRANSPORT_CELLULAR); 1122 } 1123 1124 return setRequiredNetwork(builder.build()); 1125 } 1126 } 1127 1128 /** 1129 * Set detailed description of the kind of network your job requires. 1130 * <p> 1131 * If your job doesn't need a network connection, you don't need to call 1132 * this method, as the default is {@code null}. 1133 * <p> 1134 * Calling this method defines network as a strict requirement for your 1135 * job. If the network requested is not available your job will never 1136 * run. See {@link #setOverrideDeadline(long)} to change this behavior. 1137 * Calling this method will override any requirements previously defined 1138 * by {@link #setRequiredNetworkType(int)}; you typically only want to 1139 * call one of these methods. 1140 * <p class="note"> 1141 * When your job executes in 1142 * {@link JobService#onStartJob(JobParameters)}, be sure to use the 1143 * specific network returned by {@link JobParameters#getNetwork()}, 1144 * otherwise you'll use the default network which may not meet this 1145 * constraint. 1146 * 1147 * @param networkRequest The detailed description of the kind of network 1148 * this job requires, or {@code null} if no specific kind of 1149 * network is required. Defining a {@link NetworkSpecifier} 1150 * is only supported for jobs that aren't persisted. 1151 * @see #setRequiredNetworkType(int) 1152 * @see JobInfo#getRequiredNetwork() 1153 * @see JobParameters#getNetwork() 1154 */ setRequiredNetwork(@ullable NetworkRequest networkRequest)1155 public Builder setRequiredNetwork(@Nullable NetworkRequest networkRequest) { 1156 mNetworkRequest = networkRequest; 1157 return this; 1158 } 1159 1160 /** 1161 * @deprecated replaced by 1162 * {@link #setEstimatedNetworkBytes(long, long)}. 1163 * @removed 1164 */ 1165 @Deprecated setEstimatedNetworkBytes(@ytesLong long networkBytes)1166 public Builder setEstimatedNetworkBytes(@BytesLong long networkBytes) { 1167 return setEstimatedNetworkBytes(networkBytes, NETWORK_BYTES_UNKNOWN); 1168 } 1169 1170 /** 1171 * Set the estimated size of network traffic that will be performed by 1172 * this job, in bytes. 1173 * <p> 1174 * Apps are encouraged to provide values that are as accurate as 1175 * possible, but when the exact size isn't available, an 1176 * order-of-magnitude estimate can be provided instead. Here are some 1177 * specific examples: 1178 * <ul> 1179 * <li>A job that is backing up a photo knows the exact size of that 1180 * photo, so it should provide that size as the estimate. 1181 * <li>A job that refreshes top news stories wouldn't know an exact 1182 * size, but if the size is expected to be consistently around 100KB, it 1183 * can provide that order-of-magnitude value as the estimate. 1184 * <li>A job that synchronizes email could end up using an extreme range 1185 * of data, from under 1KB when nothing has changed, to dozens of MB 1186 * when there are new emails with attachments. Jobs that cannot provide 1187 * reasonable estimates should use the sentinel value 1188 * {@link JobInfo#NETWORK_BYTES_UNKNOWN}. 1189 * </ul> 1190 * Note that the system may choose to delay jobs with large network 1191 * usage estimates when the device has a poor network connection, in 1192 * order to save battery. 1193 * <p> 1194 * The values provided here only reflect the traffic that will be 1195 * performed by the base job; if you're using {@link JobWorkItem} then 1196 * you also need to define the network traffic used by each work item 1197 * when constructing them. 1198 * 1199 * @param downloadBytes The estimated size of network traffic that will 1200 * be downloaded by this job, in bytes. 1201 * @param uploadBytes The estimated size of network traffic that will be 1202 * uploaded by this job, in bytes. 1203 * @see JobInfo#getEstimatedNetworkDownloadBytes() 1204 * @see JobInfo#getEstimatedNetworkUploadBytes() 1205 * @see JobWorkItem#JobWorkItem(android.content.Intent, long, long) 1206 */ setEstimatedNetworkBytes(@ytesLong long downloadBytes, @BytesLong long uploadBytes)1207 public Builder setEstimatedNetworkBytes(@BytesLong long downloadBytes, 1208 @BytesLong long uploadBytes) { 1209 mNetworkDownloadBytes = downloadBytes; 1210 mNetworkUploadBytes = uploadBytes; 1211 return this; 1212 } 1213 1214 /** 1215 * Specify that to run this job, the device must be charging (or be a 1216 * non-battery-powered device connected to permanent power, such as Android TV 1217 * devices). This defaults to {@code false}. 1218 * 1219 * <p class="note">For purposes of running jobs, a battery-powered device 1220 * "charging" is not quite the same as simply being connected to power. If the 1221 * device is so busy that the battery is draining despite a power connection, jobs 1222 * with this constraint will <em>not</em> run. This can happen during some 1223 * common use cases such as video chat, particularly if the device is plugged in 1224 * to USB rather than to wall power. 1225 * 1226 * @param requiresCharging Pass {@code true} to require that the device be 1227 * charging in order to run the job. 1228 * @see JobInfo#isRequireCharging() 1229 */ setRequiresCharging(boolean requiresCharging)1230 public Builder setRequiresCharging(boolean requiresCharging) { 1231 mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_CHARGING) 1232 | (requiresCharging ? CONSTRAINT_FLAG_CHARGING : 0); 1233 return this; 1234 } 1235 1236 /** 1237 * Specify that to run this job, the device's battery level must not be low. 1238 * This defaults to false. If true, the job will only run when the battery level 1239 * is not low, which is generally the point where the user is given a "low battery" 1240 * warning. 1241 * @param batteryNotLow Whether or not the device's battery level must not be low. 1242 * @see JobInfo#isRequireBatteryNotLow() 1243 */ setRequiresBatteryNotLow(boolean batteryNotLow)1244 public Builder setRequiresBatteryNotLow(boolean batteryNotLow) { 1245 mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_BATTERY_NOT_LOW) 1246 | (batteryNotLow ? CONSTRAINT_FLAG_BATTERY_NOT_LOW : 0); 1247 return this; 1248 } 1249 1250 /** 1251 * When set {@code true}, ensure that this job will not run if the device is in active use. 1252 * The default state is {@code false}: that is, the for the job to be runnable even when 1253 * someone is interacting with the device. 1254 * 1255 * <p>This state is a loose definition provided by the system. In general, it means that 1256 * the device is not currently being used interactively, and has not been in use for some 1257 * time. As such, it is a good time to perform resource heavy jobs. Bear in mind that 1258 * battery usage will still be attributed to your application, and surfaced to the user in 1259 * battery stats.</p> 1260 * 1261 * <p class="note">Despite the similar naming, this job constraint is <em>not</em> 1262 * related to the system's "device idle" or "doze" states. This constraint only 1263 * determines whether a job is allowed to run while the device is directly in use. 1264 * 1265 * @param requiresDeviceIdle Pass {@code true} to prevent the job from running 1266 * while the device is being used interactively. 1267 * @see JobInfo#isRequireDeviceIdle() 1268 */ setRequiresDeviceIdle(boolean requiresDeviceIdle)1269 public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) { 1270 mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_DEVICE_IDLE) 1271 | (requiresDeviceIdle ? CONSTRAINT_FLAG_DEVICE_IDLE : 0); 1272 return this; 1273 } 1274 1275 /** 1276 * Specify that to run this job, the device's available storage must not be low. 1277 * This defaults to false. If true, the job will only run when the device is not 1278 * in a low storage state, which is generally the point where the user is given a 1279 * "low storage" warning. 1280 * @param storageNotLow Whether or not the device's available storage must not be low. 1281 * @see JobInfo#isRequireStorageNotLow() 1282 */ setRequiresStorageNotLow(boolean storageNotLow)1283 public Builder setRequiresStorageNotLow(boolean storageNotLow) { 1284 mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_STORAGE_NOT_LOW) 1285 | (storageNotLow ? CONSTRAINT_FLAG_STORAGE_NOT_LOW : 0); 1286 return this; 1287 } 1288 1289 /** 1290 * Add a new content: URI that will be monitored with a 1291 * {@link android.database.ContentObserver}, and will cause the job to execute if changed. 1292 * If you have any trigger content URIs associated with a job, it will not execute until 1293 * there has been a change report for one or more of them. 1294 * 1295 * <p>Note that trigger URIs can not be used in combination with 1296 * {@link #setPeriodic(long)} or {@link #setPersisted(boolean)}. To continually monitor 1297 * for content changes, you need to schedule a new JobInfo observing the same URIs 1298 * before you finish execution of the JobService handling the most recent changes. 1299 * Following this pattern will ensure you do not lost any content changes: while your 1300 * job is running, the system will continue monitoring for content changes, and propagate 1301 * any it sees over to the next job you schedule.</p> 1302 * 1303 * <p>Because setting this property is not compatible with periodic or 1304 * persisted jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when 1305 * {@link android.app.job.JobInfo.Builder#build()} is called.</p> 1306 * 1307 * <p>The following example shows how this feature can be used to monitor for changes 1308 * in the photos on a device.</p> 1309 * 1310 * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/PhotosContentJob.java 1311 * job} 1312 * 1313 * @param uri The content: URI to monitor. 1314 * @see JobInfo#getTriggerContentUris() 1315 */ addTriggerContentUri(@onNull TriggerContentUri uri)1316 public Builder addTriggerContentUri(@NonNull TriggerContentUri uri) { 1317 if (mTriggerContentUris == null) { 1318 mTriggerContentUris = new ArrayList<>(); 1319 } 1320 mTriggerContentUris.add(uri); 1321 return this; 1322 } 1323 1324 /** 1325 * Set the delay (in milliseconds) from when a content change is detected until 1326 * the job is scheduled. If there are more changes during that time, the delay 1327 * will be reset to start at the time of the most recent change. 1328 * @param durationMs Delay after most recent content change, in milliseconds. 1329 * @see JobInfo#getTriggerContentUpdateDelay() 1330 */ setTriggerContentUpdateDelay(long durationMs)1331 public Builder setTriggerContentUpdateDelay(long durationMs) { 1332 mTriggerContentUpdateDelay = durationMs; 1333 return this; 1334 } 1335 1336 /** 1337 * Set the maximum total delay (in milliseconds) that is allowed from the first 1338 * time a content change is detected until the job is scheduled. 1339 * @param durationMs Delay after initial content change, in milliseconds. 1340 * @see JobInfo#getTriggerContentMaxDelay() 1341 */ setTriggerContentMaxDelay(long durationMs)1342 public Builder setTriggerContentMaxDelay(long durationMs) { 1343 mTriggerContentMaxDelay = durationMs; 1344 return this; 1345 } 1346 1347 /** 1348 * Specify that this job should recur with the provided interval, not more than once per 1349 * period. You have no control over when within this interval this job will be executed, 1350 * only the guarantee that it will be executed at most once within this interval. 1351 * Setting this function on the builder with {@link #setMinimumLatency(long)} or 1352 * {@link #setOverrideDeadline(long)} will result in an error. 1353 * @param intervalMillis Millisecond interval for which this job will repeat. 1354 * @see JobInfo#getIntervalMillis() 1355 * @see JobInfo#getFlexMillis() 1356 */ setPeriodic(long intervalMillis)1357 public Builder setPeriodic(long intervalMillis) { 1358 return setPeriodic(intervalMillis, intervalMillis); 1359 } 1360 1361 /** 1362 * Specify that this job should recur with the provided interval and flex. The job can 1363 * execute at any time in a window of flex length at the end of the period. 1364 * @param intervalMillis Millisecond interval for which this job will repeat. A minimum 1365 * value of {@link #getMinPeriodMillis()} is enforced. 1366 * @param flexMillis Millisecond flex for this job. Flex is clamped to be at least 1367 * {@link #getMinFlexMillis()} or 5 percent of the period, whichever is 1368 * higher. 1369 * @see JobInfo#getIntervalMillis() 1370 * @see JobInfo#getFlexMillis() 1371 */ setPeriodic(long intervalMillis, long flexMillis)1372 public Builder setPeriodic(long intervalMillis, long flexMillis) { 1373 final long minPeriod = getMinPeriodMillis(); 1374 if (intervalMillis < minPeriod) { 1375 Log.w(TAG, "Requested interval " + formatDuration(intervalMillis) + " for job " 1376 + mJobId + " is too small; raising to " + formatDuration(minPeriod)); 1377 intervalMillis = minPeriod; 1378 } 1379 1380 final long percentClamp = 5 * intervalMillis / 100; 1381 final long minFlex = Math.max(percentClamp, getMinFlexMillis()); 1382 if (flexMillis < minFlex) { 1383 Log.w(TAG, "Requested flex " + formatDuration(flexMillis) + " for job " + mJobId 1384 + " is too small; raising to " + formatDuration(minFlex)); 1385 flexMillis = minFlex; 1386 } 1387 1388 mIsPeriodic = true; 1389 mIntervalMillis = intervalMillis; 1390 mFlexMillis = flexMillis; 1391 mHasEarlyConstraint = mHasLateConstraint = true; 1392 return this; 1393 } 1394 1395 /** 1396 * Specify that this job should be delayed by the provided amount of time. 1397 * Because it doesn't make sense setting this property on a periodic job, doing so will 1398 * throw an {@link java.lang.IllegalArgumentException} when 1399 * {@link android.app.job.JobInfo.Builder#build()} is called. 1400 * @param minLatencyMillis Milliseconds before which this job will not be considered for 1401 * execution. 1402 * @see JobInfo#getMinLatencyMillis() 1403 */ setMinimumLatency(long minLatencyMillis)1404 public Builder setMinimumLatency(long minLatencyMillis) { 1405 mMinLatencyMillis = minLatencyMillis; 1406 mHasEarlyConstraint = true; 1407 return this; 1408 } 1409 1410 /** 1411 * Set deadline which is the maximum scheduling latency. The job will be run by this 1412 * deadline even if other requirements are not met. Because it doesn't make sense setting 1413 * this property on a periodic job, doing so will throw an 1414 * {@link java.lang.IllegalArgumentException} when 1415 * {@link android.app.job.JobInfo.Builder#build()} is called. 1416 * @see JobInfo#getMaxExecutionDelayMillis() 1417 */ setOverrideDeadline(long maxExecutionDelayMillis)1418 public Builder setOverrideDeadline(long maxExecutionDelayMillis) { 1419 mMaxExecutionDelayMillis = maxExecutionDelayMillis; 1420 mHasLateConstraint = true; 1421 return this; 1422 } 1423 1424 /** 1425 * Set up the back-off/retry policy. 1426 * This defaults to some respectable values: {30 seconds, Exponential}. We cap back-off at 1427 * 5hrs. 1428 * Note that trying to set a backoff criteria for a job with 1429 * {@link #setRequiresDeviceIdle(boolean)} will throw an exception when you call build(). 1430 * This is because back-off typically does not make sense for these types of jobs. See 1431 * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)} 1432 * for more description of the return value for the case of a job executing while in idle 1433 * mode. 1434 * @param initialBackoffMillis Millisecond time interval to wait initially when job has 1435 * failed. 1436 * @see JobInfo#getInitialBackoffMillis() 1437 * @see JobInfo#getBackoffPolicy() 1438 */ setBackoffCriteria(long initialBackoffMillis, @BackoffPolicy int backoffPolicy)1439 public Builder setBackoffCriteria(long initialBackoffMillis, 1440 @BackoffPolicy int backoffPolicy) { 1441 final long minBackoff = getMinBackoffMillis(); 1442 if (initialBackoffMillis < minBackoff) { 1443 Log.w(TAG, "Requested backoff " + formatDuration(initialBackoffMillis) + " for job " 1444 + mJobId + " is too small; raising to " + formatDuration(minBackoff)); 1445 initialBackoffMillis = minBackoff; 1446 } 1447 1448 mBackoffPolicySet = true; 1449 mInitialBackoffMillis = initialBackoffMillis; 1450 mBackoffPolicy = backoffPolicy; 1451 return this; 1452 } 1453 1454 /** 1455 * Setting this to true indicates that this job is important while the scheduling app 1456 * is in the foreground or on the temporary whitelist for background restrictions. 1457 * This means that the system will relax doze restrictions on this job during this time. 1458 * 1459 * Apps should use this flag only for short jobs that are essential for the app to function 1460 * properly in the foreground. 1461 * 1462 * Note that once the scheduling app is no longer whitelisted from background restrictions 1463 * and in the background, or the job failed due to unsatisfied constraints, 1464 * this job should be expected to behave like other jobs without this flag. 1465 * 1466 * @param importantWhileForeground whether to relax doze restrictions for this job when the 1467 * app is in the foreground. False by default. 1468 * @see JobInfo#isImportantWhileForeground() 1469 */ setImportantWhileForeground(boolean importantWhileForeground)1470 public Builder setImportantWhileForeground(boolean importantWhileForeground) { 1471 if (importantWhileForeground) { 1472 mFlags |= FLAG_IMPORTANT_WHILE_FOREGROUND; 1473 } else { 1474 mFlags &= (~FLAG_IMPORTANT_WHILE_FOREGROUND); 1475 } 1476 return this; 1477 } 1478 1479 /** 1480 * @removed 1481 * @deprecated replaced with {@link #setPrefetch(boolean)} 1482 */ 1483 @Deprecated setIsPrefetch(boolean isPrefetch)1484 public Builder setIsPrefetch(boolean isPrefetch) { 1485 return setPrefetch(isPrefetch); 1486 } 1487 1488 /** 1489 * Setting this to true indicates that this job is designed to prefetch 1490 * content that will make a material improvement to the experience of 1491 * the specific user of this device. For example, fetching top headlines 1492 * of interest to the current user. 1493 * <p> 1494 * The system may use this signal to relax the network constraints you 1495 * originally requested, such as allowing a 1496 * {@link JobInfo#NETWORK_TYPE_UNMETERED} job to run over a metered 1497 * network when there is a surplus of metered data available. The system 1498 * may also use this signal in combination with end user usage patterns 1499 * to ensure data is prefetched before the user launches your app. 1500 * @see JobInfo#isPrefetch() 1501 */ setPrefetch(boolean prefetch)1502 public Builder setPrefetch(boolean prefetch) { 1503 if (prefetch) { 1504 mFlags |= FLAG_PREFETCH; 1505 } else { 1506 mFlags &= (~FLAG_PREFETCH); 1507 } 1508 return this; 1509 } 1510 1511 /** 1512 * Set whether or not to persist this job across device reboots. 1513 * 1514 * @param isPersisted True to indicate that the job will be written to 1515 * disk and loaded at boot. 1516 * @see JobInfo#isPersisted() 1517 */ 1518 @RequiresPermission(android.Manifest.permission.RECEIVE_BOOT_COMPLETED) setPersisted(boolean isPersisted)1519 public Builder setPersisted(boolean isPersisted) { 1520 mIsPersisted = isPersisted; 1521 return this; 1522 } 1523 1524 /** 1525 * @return The job object to hand to the JobScheduler. This object is immutable. 1526 */ build()1527 public JobInfo build() { 1528 // Allow jobs with no constraints - What am I, a database? 1529 if (!mHasEarlyConstraint && !mHasLateConstraint && mConstraintFlags == 0 && 1530 mNetworkRequest == null && 1531 mTriggerContentUris == null) { 1532 throw new IllegalArgumentException("You're trying to build a job with no " + 1533 "constraints, this is not allowed."); 1534 } 1535 // Check that network estimates require network type 1536 if ((mNetworkDownloadBytes > 0 || mNetworkUploadBytes > 0) && mNetworkRequest == null) { 1537 throw new IllegalArgumentException( 1538 "Can't provide estimated network usage without requiring a network"); 1539 } 1540 // We can't serialize network specifiers 1541 if (mIsPersisted && mNetworkRequest != null 1542 && mNetworkRequest.networkCapabilities.getNetworkSpecifier() != null) { 1543 throw new IllegalArgumentException( 1544 "Network specifiers aren't supported for persistent jobs"); 1545 } 1546 // Check that a deadline was not set on a periodic job. 1547 if (mIsPeriodic) { 1548 if (mMaxExecutionDelayMillis != 0L) { 1549 throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " + 1550 "periodic job."); 1551 } 1552 if (mMinLatencyMillis != 0L) { 1553 throw new IllegalArgumentException("Can't call setMinimumLatency() on a " + 1554 "periodic job"); 1555 } 1556 if (mTriggerContentUris != null) { 1557 throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " + 1558 "periodic job"); 1559 } 1560 } 1561 if (mIsPersisted) { 1562 if (mTriggerContentUris != null) { 1563 throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " + 1564 "persisted job"); 1565 } 1566 if (!mTransientExtras.isEmpty()) { 1567 throw new IllegalArgumentException("Can't call setTransientExtras() on a " + 1568 "persisted job"); 1569 } 1570 if (mClipData != null) { 1571 throw new IllegalArgumentException("Can't call setClipData() on a " + 1572 "persisted job"); 1573 } 1574 } 1575 if ((mFlags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0 && mHasEarlyConstraint) { 1576 throw new IllegalArgumentException("An important while foreground job cannot " 1577 + "have a time delay"); 1578 } 1579 if (mBackoffPolicySet && (mConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) { 1580 throw new IllegalArgumentException("An idle mode job will not respect any" + 1581 " back-off policy, so calling setBackoffCriteria with" + 1582 " setRequiresDeviceIdle is an error."); 1583 } 1584 return new JobInfo(this); 1585 } 1586 } 1587 } 1588