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