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