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