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