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.util.TimeUtils.formatDuration; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.content.ComponentName; 24 import android.net.Uri; 25 import android.os.Parcel; 26 import android.os.Parcelable; 27 import android.os.PersistableBundle; 28 import android.util.Log; 29 30 import java.util.ArrayList; 31 import java.util.Objects; 32 33 /** 34 * Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the 35 * parameters required to schedule work against the calling application. These are constructed 36 * using the {@link JobInfo.Builder}. 37 * You must specify at least one sort of constraint on the JobInfo object that you are creating. 38 * The goal here is to provide the scheduler with high-level semantics about the work you want to 39 * accomplish. Doing otherwise with throw an exception in your app. 40 */ 41 public class JobInfo implements Parcelable { 42 private static String TAG = "JobInfo"; 43 /** Default. */ 44 public static final int NETWORK_TYPE_NONE = 0; 45 /** This job requires network connectivity. */ 46 public static final int NETWORK_TYPE_ANY = 1; 47 /** This job requires network connectivity that is unmetered. */ 48 public static final int NETWORK_TYPE_UNMETERED = 2; 49 /** This job requires network connectivity that is not roaming. */ 50 public static final int NETWORK_TYPE_NOT_ROAMING = 3; 51 52 /** 53 * Amount of backoff a job has initially by default, in milliseconds. 54 */ 55 public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L; // 30 seconds. 56 57 /** 58 * Maximum backoff we allow for a job, in milliseconds. 59 */ 60 public static final long MAX_BACKOFF_DELAY_MILLIS = 5 * 60 * 60 * 1000; // 5 hours. 61 62 /** 63 * Linearly back-off a failed job. See 64 * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)} 65 * retry_time(current_time, num_failures) = 66 * current_time + initial_backoff_millis * num_failures, num_failures >= 1 67 */ 68 public static final int BACKOFF_POLICY_LINEAR = 0; 69 70 /** 71 * Exponentially back-off a failed job. See 72 * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)} 73 * 74 * retry_time(current_time, num_failures) = 75 * current_time + initial_backoff_millis * 2 ^ (num_failures - 1), num_failures >= 1 76 */ 77 public static final int BACKOFF_POLICY_EXPONENTIAL = 1; 78 79 /* Minimum interval for a periodic job, in milliseconds. */ 80 private static final long MIN_PERIOD_MILLIS = 15 * 60 * 1000L; // 15 minutes 81 82 /* Minimum flex for a periodic job, in milliseconds. */ 83 private static final long MIN_FLEX_MILLIS = 5 * 60 * 1000L; // 5 minutes 84 85 /** 86 * Query the minimum interval allowed for periodic scheduled jobs. Attempting 87 * to declare a smaller period that this when scheduling a job will result in a 88 * job that is still periodic, but will run with this effective period. 89 * 90 * @return The minimum available interval for scheduling periodic jobs, in milliseconds. 91 */ 92 public static final long getMinPeriodMillis() { 93 return MIN_PERIOD_MILLIS; 94 } 95 96 /** 97 * Query the minimum flex time allowed for periodic scheduled jobs. Attempting 98 * to declare a shorter flex time than this when scheduling such a job will 99 * result in this amount as the effective flex time for the job. 100 * 101 * @return The minimum available flex time for scheduling periodic jobs, in milliseconds. 102 */ 103 public static final long getMinFlexMillis() { 104 return MIN_FLEX_MILLIS; 105 } 106 107 /** 108 * Default type of backoff. 109 * @hide 110 */ 111 public static final int DEFAULT_BACKOFF_POLICY = BACKOFF_POLICY_EXPONENTIAL; 112 113 /** 114 * Default of {@link #getPriority}. 115 * @hide 116 */ 117 public static final int PRIORITY_DEFAULT = 0; 118 119 /** 120 * Value of {@link #getPriority} for expedited syncs. 121 * @hide 122 */ 123 public static final int PRIORITY_SYNC_EXPEDITED = 10; 124 125 /** 126 * Value of {@link #getPriority} for first time initialization syncs. 127 * @hide 128 */ 129 public static final int PRIORITY_SYNC_INITIALIZATION = 20; 130 131 /** 132 * Value of {@link #getPriority} for a foreground app (overrides the supplied 133 * JobInfo priority if it is smaller). 134 * @hide 135 */ 136 public static final int PRIORITY_FOREGROUND_APP = 30; 137 138 /** 139 * Value of {@link #getPriority} for the current top app (overrides the supplied 140 * JobInfo priority if it is smaller). 141 * @hide 142 */ 143 public static final int PRIORITY_TOP_APP = 40; 144 145 /** 146 * Adjustment of {@link #getPriority} if the app has often (50% or more of the time) 147 * been running jobs. 148 * @hide 149 */ 150 public static final int PRIORITY_ADJ_OFTEN_RUNNING = -40; 151 152 /** 153 * Adjustment of {@link #getPriority} if the app has always (90% or more of the time) 154 * been running jobs. 155 * @hide 156 */ 157 public static final int PRIORITY_ADJ_ALWAYS_RUNNING = -80; 158 159 /** 160 * Indicates that the implementation of this job will be using 161 * {@link JobService#startForeground(int, android.app.Notification)} to run 162 * in the foreground. 163 * <p> 164 * When set, the internal scheduling of this job will ignore any background 165 * network restrictions for the requesting app. Note that this flag alone 166 * doesn't actually place your {@link JobService} in the foreground; you 167 * still need to post the notification yourself. 168 * <p> 169 * To use this flag, the caller must hold the 170 * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL} permission. 171 * 172 * @hide 173 */ 174 public static final int FLAG_WILL_BE_FOREGROUND = 1 << 0; 175 176 private final int jobId; 177 private final PersistableBundle extras; 178 private final ComponentName service; 179 private final boolean requireCharging; 180 private final boolean requireDeviceIdle; 181 private final TriggerContentUri[] triggerContentUris; 182 private final long triggerContentUpdateDelay; 183 private final long triggerContentMaxDelay; 184 private final boolean hasEarlyConstraint; 185 private final boolean hasLateConstraint; 186 private final int networkType; 187 private final long minLatencyMillis; 188 private final long maxExecutionDelayMillis; 189 private final boolean isPeriodic; 190 private final boolean isPersisted; 191 private final long intervalMillis; 192 private final long flexMillis; 193 private final long initialBackoffMillis; 194 private final int backoffPolicy; 195 private final int priority; 196 private final int flags; 197 198 /** 199 * Unique job id associated with this application (uid). This is the same job ID 200 * you supplied in the {@link Builder} constructor. 201 */ 202 public int getId() { 203 return jobId; 204 } 205 206 /** 207 * Bundle of extras which are returned to your application at execution time. 208 */ 209 public PersistableBundle getExtras() { 210 return extras; 211 } 212 213 /** 214 * Name of the service endpoint that will be called back into by the JobScheduler. 215 */ 216 public ComponentName getService() { 217 return service; 218 } 219 220 /** @hide */ 221 public int getPriority() { 222 return priority; 223 } 224 225 /** @hide */ 226 public int getFlags() { 227 return flags; 228 } 229 230 /** 231 * Whether this job needs the device to be plugged in. 232 */ 233 public boolean isRequireCharging() { 234 return requireCharging; 235 } 236 237 /** 238 * Whether this job needs the device to be in an Idle maintenance window. 239 */ 240 public boolean isRequireDeviceIdle() { 241 return requireDeviceIdle; 242 } 243 244 /** 245 * Which content: URIs must change for the job to be scheduled. Returns null 246 * if there are none required. 247 */ 248 @Nullable 249 public TriggerContentUri[] getTriggerContentUris() { 250 return triggerContentUris; 251 } 252 253 /** 254 * When triggering on content URI changes, this is the delay from when a change 255 * is detected until the job is scheduled. 256 */ 257 public long getTriggerContentUpdateDelay() { 258 return triggerContentUpdateDelay; 259 } 260 261 /** 262 * When triggering on content URI changes, this is the maximum delay we will 263 * use before scheduling the job. 264 */ 265 public long getTriggerContentMaxDelay() { 266 return triggerContentMaxDelay; 267 } 268 269 /** 270 * One of {@link android.app.job.JobInfo#NETWORK_TYPE_ANY}, 271 * {@link android.app.job.JobInfo#NETWORK_TYPE_NONE}, 272 * {@link android.app.job.JobInfo#NETWORK_TYPE_UNMETERED}, or 273 * {@link android.app.job.JobInfo#NETWORK_TYPE_NOT_ROAMING}. 274 */ 275 public int getNetworkType() { 276 return networkType; 277 } 278 279 /** 280 * Set for a job that does not recur periodically, to specify a delay after which the job 281 * will be eligible for execution. This value is not set if the job recurs periodically. 282 */ 283 public long getMinLatencyMillis() { 284 return minLatencyMillis; 285 } 286 287 /** 288 * See {@link Builder#setOverrideDeadline(long)}. This value is not set if the job recurs 289 * periodically. 290 */ 291 public long getMaxExecutionDelayMillis() { 292 return maxExecutionDelayMillis; 293 } 294 295 /** 296 * Track whether this job will repeat with a given period. 297 */ 298 public boolean isPeriodic() { 299 return isPeriodic; 300 } 301 302 /** 303 * @return Whether or not this job should be persisted across device reboots. 304 */ 305 public boolean isPersisted() { 306 return isPersisted; 307 } 308 309 /** 310 * Set to the interval between occurrences of this job. This value is <b>not</b> set if the 311 * job does not recur periodically. 312 */ 313 public long getIntervalMillis() { 314 return intervalMillis >= getMinPeriodMillis() ? intervalMillis : getMinPeriodMillis(); 315 } 316 317 /** 318 * Flex time for this job. Only valid if this is a periodic job. The job can 319 * execute at any time in a window of flex length at the end of the period. 320 */ 321 public long getFlexMillis() { 322 long interval = getIntervalMillis(); 323 long percentClamp = 5 * interval / 100; 324 long clampedFlex = Math.max(flexMillis, Math.max(percentClamp, getMinFlexMillis())); 325 return clampedFlex <= interval ? clampedFlex : interval; 326 } 327 328 /** 329 * The amount of time the JobScheduler will wait before rescheduling a failed job. This value 330 * will be increased depending on the backoff policy specified at job creation time. Defaults 331 * to 5 seconds. 332 */ 333 public long getInitialBackoffMillis() { 334 return initialBackoffMillis; 335 } 336 337 /** 338 * One of either {@link android.app.job.JobInfo#BACKOFF_POLICY_EXPONENTIAL}, or 339 * {@link android.app.job.JobInfo#BACKOFF_POLICY_LINEAR}, depending on which criteria you set 340 * when creating this job. 341 */ 342 public int getBackoffPolicy() { 343 return backoffPolicy; 344 } 345 346 /** 347 * User can specify an early constraint of 0L, which is valid, so we keep track of whether the 348 * function was called at all. 349 * @hide 350 */ 351 public boolean hasEarlyConstraint() { 352 return hasEarlyConstraint; 353 } 354 355 /** 356 * User can specify a late constraint of 0L, which is valid, so we keep track of whether the 357 * function was called at all. 358 * @hide 359 */ 360 public boolean hasLateConstraint() { 361 return hasLateConstraint; 362 } 363 364 private JobInfo(Parcel in) { 365 jobId = in.readInt(); 366 extras = in.readPersistableBundle(); 367 service = in.readParcelable(null); 368 requireCharging = in.readInt() == 1; 369 requireDeviceIdle = in.readInt() == 1; 370 triggerContentUris = in.createTypedArray(TriggerContentUri.CREATOR); 371 triggerContentUpdateDelay = in.readLong(); 372 triggerContentMaxDelay = in.readLong(); 373 networkType = in.readInt(); 374 minLatencyMillis = in.readLong(); 375 maxExecutionDelayMillis = in.readLong(); 376 isPeriodic = in.readInt() == 1; 377 isPersisted = in.readInt() == 1; 378 intervalMillis = in.readLong(); 379 flexMillis = in.readLong(); 380 initialBackoffMillis = in.readLong(); 381 backoffPolicy = in.readInt(); 382 hasEarlyConstraint = in.readInt() == 1; 383 hasLateConstraint = in.readInt() == 1; 384 priority = in.readInt(); 385 flags = in.readInt(); 386 } 387 388 private JobInfo(JobInfo.Builder b) { 389 jobId = b.mJobId; 390 extras = b.mExtras; 391 service = b.mJobService; 392 requireCharging = b.mRequiresCharging; 393 requireDeviceIdle = b.mRequiresDeviceIdle; 394 triggerContentUris = b.mTriggerContentUris != null 395 ? b.mTriggerContentUris.toArray(new TriggerContentUri[b.mTriggerContentUris.size()]) 396 : null; 397 triggerContentUpdateDelay = b.mTriggerContentUpdateDelay; 398 triggerContentMaxDelay = b.mTriggerContentMaxDelay; 399 networkType = b.mNetworkType; 400 minLatencyMillis = b.mMinLatencyMillis; 401 maxExecutionDelayMillis = b.mMaxExecutionDelayMillis; 402 isPeriodic = b.mIsPeriodic; 403 isPersisted = b.mIsPersisted; 404 intervalMillis = b.mIntervalMillis; 405 flexMillis = b.mFlexMillis; 406 initialBackoffMillis = b.mInitialBackoffMillis; 407 backoffPolicy = b.mBackoffPolicy; 408 hasEarlyConstraint = b.mHasEarlyConstraint; 409 hasLateConstraint = b.mHasLateConstraint; 410 priority = b.mPriority; 411 flags = b.mFlags; 412 } 413 414 @Override 415 public int describeContents() { 416 return 0; 417 } 418 419 @Override 420 public void writeToParcel(Parcel out, int flags) { 421 out.writeInt(jobId); 422 out.writePersistableBundle(extras); 423 out.writeParcelable(service, flags); 424 out.writeInt(requireCharging ? 1 : 0); 425 out.writeInt(requireDeviceIdle ? 1 : 0); 426 out.writeTypedArray(triggerContentUris, flags); 427 out.writeLong(triggerContentUpdateDelay); 428 out.writeLong(triggerContentMaxDelay); 429 out.writeInt(networkType); 430 out.writeLong(minLatencyMillis); 431 out.writeLong(maxExecutionDelayMillis); 432 out.writeInt(isPeriodic ? 1 : 0); 433 out.writeInt(isPersisted ? 1 : 0); 434 out.writeLong(intervalMillis); 435 out.writeLong(flexMillis); 436 out.writeLong(initialBackoffMillis); 437 out.writeInt(backoffPolicy); 438 out.writeInt(hasEarlyConstraint ? 1 : 0); 439 out.writeInt(hasLateConstraint ? 1 : 0); 440 out.writeInt(priority); 441 out.writeInt(this.flags); 442 } 443 444 public static final Creator<JobInfo> CREATOR = new Creator<JobInfo>() { 445 @Override 446 public JobInfo createFromParcel(Parcel in) { 447 return new JobInfo(in); 448 } 449 450 @Override 451 public JobInfo[] newArray(int size) { 452 return new JobInfo[size]; 453 } 454 }; 455 456 @Override 457 public String toString() { 458 return "(job:" + jobId + "/" + service.flattenToShortString() + ")"; 459 } 460 461 /** 462 * Information about a content URI modification that a job would like to 463 * trigger on. 464 */ 465 public static final class TriggerContentUri implements Parcelable { 466 private final Uri mUri; 467 private final int mFlags; 468 469 /** 470 * Flag for trigger: also trigger if any descendants of the given URI change. 471 * Corresponds to the <var>notifyForDescendants</var> of 472 * {@link android.content.ContentResolver#registerContentObserver}. 473 */ 474 public static final int FLAG_NOTIFY_FOR_DESCENDANTS = 1<<0; 475 476 /** 477 * Create a new trigger description. 478 * @param uri The URI to observe. Must be non-null. 479 * @param flags Optional flags for the observer, either 0 or 480 * {@link #FLAG_NOTIFY_FOR_DESCENDANTS}. 481 */ 482 public TriggerContentUri(@NonNull Uri uri, int flags) { 483 mUri = uri; 484 mFlags = flags; 485 } 486 487 /** 488 * Return the Uri this trigger was created for. 489 */ 490 public Uri getUri() { 491 return mUri; 492 } 493 494 /** 495 * Return the flags supplied for the trigger. 496 */ 497 public int getFlags() { 498 return mFlags; 499 } 500 501 @Override 502 public boolean equals(Object o) { 503 if (!(o instanceof TriggerContentUri)) { 504 return false; 505 } 506 TriggerContentUri t = (TriggerContentUri) o; 507 return Objects.equals(t.mUri, mUri) && t.mFlags == mFlags; 508 } 509 510 @Override 511 public int hashCode() { 512 return (mUri == null ? 0 : mUri.hashCode()) ^ mFlags; 513 } 514 515 private TriggerContentUri(Parcel in) { 516 mUri = Uri.CREATOR.createFromParcel(in); 517 mFlags = in.readInt(); 518 } 519 520 @Override 521 public int describeContents() { 522 return 0; 523 } 524 525 @Override 526 public void writeToParcel(Parcel out, int flags) { 527 mUri.writeToParcel(out, flags); 528 out.writeInt(mFlags); 529 } 530 531 public static final Creator<TriggerContentUri> CREATOR = new Creator<TriggerContentUri>() { 532 @Override 533 public TriggerContentUri createFromParcel(Parcel in) { 534 return new TriggerContentUri(in); 535 } 536 537 @Override 538 public TriggerContentUri[] newArray(int size) { 539 return new TriggerContentUri[size]; 540 } 541 }; 542 } 543 544 /** Builder class for constructing {@link JobInfo} objects. */ 545 public static final class Builder { 546 private final int mJobId; 547 private final ComponentName mJobService; 548 private PersistableBundle mExtras = PersistableBundle.EMPTY; 549 private int mPriority = PRIORITY_DEFAULT; 550 private int mFlags; 551 // Requirements. 552 private boolean mRequiresCharging; 553 private boolean mRequiresDeviceIdle; 554 private int mNetworkType; 555 private ArrayList<TriggerContentUri> mTriggerContentUris; 556 private long mTriggerContentUpdateDelay = -1; 557 private long mTriggerContentMaxDelay = -1; 558 private boolean mIsPersisted; 559 // One-off parameters. 560 private long mMinLatencyMillis; 561 private long mMaxExecutionDelayMillis; 562 // Periodic parameters. 563 private boolean mIsPeriodic; 564 private boolean mHasEarlyConstraint; 565 private boolean mHasLateConstraint; 566 private long mIntervalMillis; 567 private long mFlexMillis; 568 // Back-off parameters. 569 private long mInitialBackoffMillis = DEFAULT_INITIAL_BACKOFF_MILLIS; 570 private int mBackoffPolicy = DEFAULT_BACKOFF_POLICY; 571 /** Easy way to track whether the client has tried to set a back-off policy. */ 572 private boolean mBackoffPolicySet = false; 573 574 /** 575 * Initialize a new Builder to construct a {@link JobInfo}. 576 * 577 * @param jobId Application-provided id for this job. Subsequent calls to cancel, or 578 * jobs created with the same jobId, will update the pre-existing job with 579 * the same id. This ID must be unique across all clients of the same uid 580 * (not just the same package). You will want to make sure this is a stable 581 * id across app updates, so probably not based on a resource ID. 582 * @param jobService The endpoint that you implement that will receive the callback from the 583 * JobScheduler. 584 */ 585 public Builder(int jobId, ComponentName jobService) { 586 mJobService = jobService; 587 mJobId = jobId; 588 } 589 590 /** @hide */ 591 public Builder setPriority(int priority) { 592 mPriority = priority; 593 return this; 594 } 595 596 /** @hide */ 597 public Builder setFlags(int flags) { 598 mFlags = flags; 599 return this; 600 } 601 602 /** 603 * Set optional extras. This is persisted, so we only allow primitive types. 604 * @param extras Bundle containing extras you want the scheduler to hold on to for you. 605 */ 606 public Builder setExtras(PersistableBundle extras) { 607 mExtras = extras; 608 return this; 609 } 610 611 /** 612 * Set some description of the kind of network type your job needs to have. 613 * Not calling this function means the network is not necessary, as the default is 614 * {@link #NETWORK_TYPE_NONE}. 615 * Bear in mind that calling this function defines network as a strict requirement for your 616 * job. If the network requested is not available your job will never run. See 617 * {@link #setOverrideDeadline(long)} to change this behaviour. 618 */ 619 public Builder setRequiredNetworkType(int networkType) { 620 mNetworkType = networkType; 621 return this; 622 } 623 624 /** 625 * Specify that to run this job, the device needs to be plugged in. This defaults to 626 * false. 627 * @param requiresCharging Whether or not the device is plugged in. 628 */ 629 public Builder setRequiresCharging(boolean requiresCharging) { 630 mRequiresCharging = requiresCharging; 631 return this; 632 } 633 634 /** 635 * Specify that to run, the job needs the device to be in idle mode. This defaults to 636 * false. 637 * <p>Idle mode is a loose definition provided by the system, which means that the device 638 * is not in use, and has not been in use for some time. As such, it is a good time to 639 * perform resource heavy jobs. Bear in mind that battery usage will still be attributed 640 * to your application, and surfaced to the user in battery stats.</p> 641 * @param requiresDeviceIdle Whether or not the device need be within an idle maintenance 642 * window. 643 */ 644 public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) { 645 mRequiresDeviceIdle = requiresDeviceIdle; 646 return this; 647 } 648 649 /** 650 * Add a new content: URI that will be monitored with a 651 * {@link android.database.ContentObserver}, and will cause the job to execute if changed. 652 * If you have any trigger content URIs associated with a job, it will not execute until 653 * there has been a change report for one or more of them. 654 * <p>Note that trigger URIs can not be used in combination with 655 * {@link #setPeriodic(long)} or {@link #setPersisted(boolean)}. To continually monitor 656 * for content changes, you need to schedule a new JobInfo observing the same URIs 657 * before you finish execution of the JobService handling the most recent changes.</p> 658 * <p>Because because setting this property is not compatible with periodic or 659 * persisted jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when 660 * {@link android.app.job.JobInfo.Builder#build()} is called.</p> 661 * 662 * <p>The following example shows how this feature can be used to monitor for changes 663 * in the photos on a device.</p> 664 * 665 * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/PhotosContentJob.java 666 * job} 667 * 668 * @param uri The content: URI to monitor. 669 */ 670 public Builder addTriggerContentUri(@NonNull TriggerContentUri uri) { 671 if (mTriggerContentUris == null) { 672 mTriggerContentUris = new ArrayList<>(); 673 } 674 mTriggerContentUris.add(uri); 675 return this; 676 } 677 678 /** 679 * Set the delay (in milliseconds) from when a content change is detected until 680 * the job is scheduled. If there are more changes during that time, the delay 681 * will be reset to start at the time of the most recent change. 682 * @param durationMs Delay after most recent content change, in milliseconds. 683 */ 684 public Builder setTriggerContentUpdateDelay(long durationMs) { 685 mTriggerContentUpdateDelay = durationMs; 686 return this; 687 } 688 689 /** 690 * Set the maximum total delay (in milliseconds) that is allowed from the first 691 * time a content change is detected until the job is scheduled. 692 * @param durationMs Delay after initial content change, in milliseconds. 693 */ 694 public Builder setTriggerContentMaxDelay(long durationMs) { 695 mTriggerContentMaxDelay = durationMs; 696 return this; 697 } 698 699 /** 700 * Specify that this job should recur with the provided interval, not more than once per 701 * period. You have no control over when within this interval this job will be executed, 702 * only the guarantee that it will be executed at most once within this interval. 703 * Setting this function on the builder with {@link #setMinimumLatency(long)} or 704 * {@link #setOverrideDeadline(long)} will result in an error. 705 * @param intervalMillis Millisecond interval for which this job will repeat. 706 */ 707 public Builder setPeriodic(long intervalMillis) { 708 return setPeriodic(intervalMillis, intervalMillis); 709 } 710 711 /** 712 * Specify that this job should recur with the provided interval and flex. The job can 713 * execute at any time in a window of flex length at the end of the period. 714 * @param intervalMillis Millisecond interval for which this job will repeat. A minimum 715 * value of {@link #getMinPeriodMillis()} is enforced. 716 * @param flexMillis Millisecond flex for this job. Flex is clamped to be at least 717 * {@link #getMinFlexMillis()} or 5 percent of the period, whichever is 718 * higher. 719 */ 720 public Builder setPeriodic(long intervalMillis, long flexMillis) { 721 mIsPeriodic = true; 722 mIntervalMillis = intervalMillis; 723 mFlexMillis = flexMillis; 724 mHasEarlyConstraint = mHasLateConstraint = true; 725 return this; 726 } 727 728 /** 729 * Specify that this job should be delayed by the provided amount of time. 730 * Because it doesn't make sense setting this property on a periodic job, doing so will 731 * throw an {@link java.lang.IllegalArgumentException} when 732 * {@link android.app.job.JobInfo.Builder#build()} is called. 733 * @param minLatencyMillis Milliseconds before which this job will not be considered for 734 * execution. 735 */ 736 public Builder setMinimumLatency(long minLatencyMillis) { 737 mMinLatencyMillis = minLatencyMillis; 738 mHasEarlyConstraint = true; 739 return this; 740 } 741 742 /** 743 * Set deadline which is the maximum scheduling latency. The job will be run by this 744 * deadline even if other requirements are not met. Because it doesn't make sense setting 745 * this property on a periodic job, doing so will throw an 746 * {@link java.lang.IllegalArgumentException} when 747 * {@link android.app.job.JobInfo.Builder#build()} is called. 748 */ 749 public Builder setOverrideDeadline(long maxExecutionDelayMillis) { 750 mMaxExecutionDelayMillis = maxExecutionDelayMillis; 751 mHasLateConstraint = true; 752 return this; 753 } 754 755 /** 756 * Set up the back-off/retry policy. 757 * This defaults to some respectable values: {30 seconds, Exponential}. We cap back-off at 758 * 5hrs. 759 * Note that trying to set a backoff criteria for a job with 760 * {@link #setRequiresDeviceIdle(boolean)} will throw an exception when you call build(). 761 * This is because back-off typically does not make sense for these types of jobs. See 762 * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)} 763 * for more description of the return value for the case of a job executing while in idle 764 * mode. 765 * @param initialBackoffMillis Millisecond time interval to wait initially when job has 766 * failed. 767 * @param backoffPolicy is one of {@link #BACKOFF_POLICY_LINEAR} or 768 * {@link #BACKOFF_POLICY_EXPONENTIAL} 769 */ 770 public Builder setBackoffCriteria(long initialBackoffMillis, int backoffPolicy) { 771 mBackoffPolicySet = true; 772 mInitialBackoffMillis = initialBackoffMillis; 773 mBackoffPolicy = backoffPolicy; 774 return this; 775 } 776 777 /** 778 * Set whether or not to persist this job across device reboots. This will only have an 779 * effect if your application holds the permission 780 * {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED}. Otherwise an exception will 781 * be thrown. 782 * @param isPersisted True to indicate that the job will be written to disk and loaded at 783 * boot. 784 */ 785 public Builder setPersisted(boolean isPersisted) { 786 mIsPersisted = isPersisted; 787 return this; 788 } 789 790 /** 791 * @return The job object to hand to the JobScheduler. This object is immutable. 792 */ 793 public JobInfo build() { 794 // Allow jobs with no constraints - What am I, a database? 795 if (!mHasEarlyConstraint && !mHasLateConstraint && !mRequiresCharging && 796 !mRequiresDeviceIdle && mNetworkType == NETWORK_TYPE_NONE && 797 mTriggerContentUris == null) { 798 throw new IllegalArgumentException("You're trying to build a job with no " + 799 "constraints, this is not allowed."); 800 } 801 mExtras = new PersistableBundle(mExtras); // Make our own copy. 802 // Check that a deadline was not set on a periodic job. 803 if (mIsPeriodic && (mMaxExecutionDelayMillis != 0L)) { 804 throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " + 805 "periodic job."); 806 } 807 if (mIsPeriodic && (mMinLatencyMillis != 0L)) { 808 throw new IllegalArgumentException("Can't call setMinimumLatency() on a " + 809 "periodic job"); 810 } 811 if (mIsPeriodic && (mTriggerContentUris != null)) { 812 throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " + 813 "periodic job"); 814 } 815 if (mIsPersisted && (mTriggerContentUris != null)) { 816 throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " + 817 "persisted job"); 818 } 819 if (mBackoffPolicySet && mRequiresDeviceIdle) { 820 throw new IllegalArgumentException("An idle mode job will not respect any" + 821 " back-off policy, so calling setBackoffCriteria with" + 822 " setRequiresDeviceIdle is an error."); 823 } 824 JobInfo job = new JobInfo(this); 825 if (job.isPeriodic()) { 826 if (job.intervalMillis != job.getIntervalMillis()) { 827 StringBuilder builder = new StringBuilder(); 828 builder.append("Specified interval for ") 829 .append(String.valueOf(mJobId)) 830 .append(" is "); 831 formatDuration(mIntervalMillis, builder); 832 builder.append(". Clamped to "); 833 formatDuration(job.getIntervalMillis(), builder); 834 Log.w(TAG, builder.toString()); 835 } 836 if (job.flexMillis != job.getFlexMillis()) { 837 StringBuilder builder = new StringBuilder(); 838 builder.append("Specified flex for ") 839 .append(String.valueOf(mJobId)) 840 .append(" is "); 841 formatDuration(mFlexMillis, builder); 842 builder.append(". Clamped to "); 843 formatDuration(job.getFlexMillis(), builder); 844 Log.w(TAG, builder.toString()); 845 } 846 } 847 return job; 848 } 849 } 850 851 } 852