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