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