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 17 package android.app.job; 18 19 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; 20 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; 21 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; 22 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; 23 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; 24 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; 25 import static android.util.TimeUtils.formatDuration; 26 27 import android.annotation.BytesLong; 28 import android.annotation.IntDef; 29 import android.annotation.NonNull; 30 import android.annotation.Nullable; 31 import android.annotation.RequiresPermission; 32 import android.compat.annotation.UnsupportedAppUsage; 33 import android.content.ClipData; 34 import android.content.ComponentName; 35 import android.net.NetworkRequest; 36 import android.net.NetworkSpecifier; 37 import android.net.Uri; 38 import android.os.BaseBundle; 39 import android.os.Build; 40 import android.os.Bundle; 41 import android.os.Parcel; 42 import android.os.Parcelable; 43 import android.os.PersistableBundle; 44 import android.util.Log; 45 46 import java.lang.annotation.Retention; 47 import java.lang.annotation.RetentionPolicy; 48 import java.util.ArrayList; 49 import java.util.Arrays; 50 import java.util.Objects; 51 52 /** 53 * Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the 54 * parameters required to schedule work against the calling application. These are constructed 55 * using the {@link JobInfo.Builder}. 56 * You must specify at least one sort of constraint on the JobInfo object that you are creating. 57 * The goal here is to provide the scheduler with high-level semantics about the work you want to 58 * accomplish. Doing otherwise with throw an exception in your app. 59 */ 60 public class JobInfo implements Parcelable { 61 private static String TAG = "JobInfo"; 62 63 /** @hide */ 64 @IntDef(prefix = { "NETWORK_TYPE_" }, value = { 65 NETWORK_TYPE_NONE, 66 NETWORK_TYPE_ANY, 67 NETWORK_TYPE_UNMETERED, 68 NETWORK_TYPE_NOT_ROAMING, 69 NETWORK_TYPE_CELLULAR, 70 }) 71 @Retention(RetentionPolicy.SOURCE) 72 public @interface NetworkType {} 73 74 /** Default. */ 75 public static final int NETWORK_TYPE_NONE = 0; 76 /** This job requires network connectivity. */ 77 public static final int NETWORK_TYPE_ANY = 1; 78 /** This job requires network connectivity that is unmetered. */ 79 public static final int NETWORK_TYPE_UNMETERED = 2; 80 /** This job requires network connectivity that is not roaming. */ 81 public static final int NETWORK_TYPE_NOT_ROAMING = 3; 82 /** This job requires network connectivity that is a cellular network. */ 83 public static final int NETWORK_TYPE_CELLULAR = 4; 84 85 /** 86 * This job requires metered connectivity such as most cellular data 87 * networks. 88 * 89 * @deprecated Cellular networks may be unmetered, or Wi-Fi networks may be 90 * metered, so this isn't a good way of selecting a specific 91 * transport. Instead, use {@link #NETWORK_TYPE_CELLULAR} or 92 * {@link android.net.NetworkRequest.Builder#addTransportType(int)} 93 * if your job requires a specific network transport. 94 */ 95 @Deprecated 96 public static final int NETWORK_TYPE_METERED = NETWORK_TYPE_CELLULAR; 97 98 /** Sentinel value indicating that bytes are unknown. */ 99 public static final int NETWORK_BYTES_UNKNOWN = -1; 100 101 /** 102 * Amount of backoff a job has initially by default, in milliseconds. 103 */ 104 public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L; // 30 seconds. 105 106 /** 107 * Maximum backoff we allow for a job, in milliseconds. 108 */ 109 public static final long MAX_BACKOFF_DELAY_MILLIS = 5 * 60 * 60 * 1000; // 5 hours. 110 111 /** @hide */ 112 @IntDef(prefix = { "BACKOFF_POLICY_" }, value = { 113 BACKOFF_POLICY_LINEAR, 114 BACKOFF_POLICY_EXPONENTIAL, 115 }) 116 @Retention(RetentionPolicy.SOURCE) 117 public @interface BackoffPolicy {} 118 119 /** 120 * Linearly back-off a failed job. See 121 * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)} 122 * retry_time(current_time, num_failures) = 123 * current_time + initial_backoff_millis * num_failures, num_failures >= 1 124 */ 125 public static final int BACKOFF_POLICY_LINEAR = 0; 126 127 /** 128 * Exponentially back-off a failed job. See 129 * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)} 130 * 131 * retry_time(current_time, num_failures) = 132 * current_time + initial_backoff_millis * 2 ^ (num_failures - 1), num_failures >= 1 133 */ 134 public static final int BACKOFF_POLICY_EXPONENTIAL = 1; 135 136 /* Minimum interval for a periodic job, in milliseconds. */ 137 private static final long MIN_PERIOD_MILLIS = 15 * 60 * 1000L; // 15 minutes 138 139 /* Minimum flex for a periodic job, in milliseconds. */ 140 private static final long MIN_FLEX_MILLIS = 5 * 60 * 1000L; // 5 minutes 141 142 /** 143 * Minimum backoff interval for a job, in milliseconds 144 * @hide 145 */ 146 public static final long MIN_BACKOFF_MILLIS = 10 * 1000L; // 10 seconds 147 148 /** 149 * Query the minimum interval allowed for periodic scheduled jobs. Attempting 150 * to declare a smaller period that this when scheduling a job will result in a 151 * job that is still periodic, but will run with this effective period. 152 * 153 * @return The minimum available interval for scheduling periodic jobs, in milliseconds. 154 */ getMinPeriodMillis()155 public static final long getMinPeriodMillis() { 156 return MIN_PERIOD_MILLIS; 157 } 158 159 /** 160 * Query the minimum flex time allowed for periodic scheduled jobs. Attempting 161 * to declare a shorter flex time than this when scheduling such a job will 162 * result in this amount as the effective flex time for the job. 163 * 164 * @return The minimum available flex time for scheduling periodic jobs, in milliseconds. 165 */ getMinFlexMillis()166 public static final long getMinFlexMillis() { 167 return MIN_FLEX_MILLIS; 168 } 169 170 /** 171 * Query the minimum automatic-reschedule backoff interval permitted for jobs. 172 * @hide 173 */ getMinBackoffMillis()174 public static final long getMinBackoffMillis() { 175 return MIN_BACKOFF_MILLIS; 176 } 177 178 /** 179 * Default type of backoff. 180 * @hide 181 */ 182 public static final int DEFAULT_BACKOFF_POLICY = BACKOFF_POLICY_EXPONENTIAL; 183 184 /** 185 * Default of {@link #getPriority}. 186 * @hide 187 */ 188 public static final int PRIORITY_DEFAULT = 0; 189 190 /** 191 * Value of {@link #getPriority} for expedited syncs. 192 * @hide 193 */ 194 public static final int PRIORITY_SYNC_EXPEDITED = 10; 195 196 /** 197 * Value of {@link #getPriority} for first time initialization syncs. 198 * @hide 199 */ 200 public static final int PRIORITY_SYNC_INITIALIZATION = 20; 201 202 /** 203 * Value of {@link #getPriority} for a BFGS app (overrides the supplied 204 * JobInfo priority if it is smaller). 205 * @hide 206 */ 207 public static final int PRIORITY_BOUND_FOREGROUND_SERVICE = 30; 208 209 /** @hide For backward compatibility. */ 210 @UnsupportedAppUsage 211 public static final int PRIORITY_FOREGROUND_APP = PRIORITY_BOUND_FOREGROUND_SERVICE; 212 213 /** 214 * Value of {@link #getPriority} for a FG service app (overrides the supplied 215 * JobInfo priority if it is smaller). 216 * @hide 217 */ 218 @UnsupportedAppUsage 219 public static final int PRIORITY_FOREGROUND_SERVICE = 35; 220 221 /** 222 * Value of {@link #getPriority} for the current top app (overrides the supplied 223 * JobInfo priority if it is smaller). 224 * @hide 225 */ 226 public static final int PRIORITY_TOP_APP = 40; 227 228 /** 229 * Adjustment of {@link #getPriority} if the app has often (50% or more of the time) 230 * been running jobs. 231 * @hide 232 */ 233 public static final int PRIORITY_ADJ_OFTEN_RUNNING = -40; 234 235 /** 236 * Adjustment of {@link #getPriority} if the app has always (90% or more of the time) 237 * been running jobs. 238 * @hide 239 */ 240 public static final int PRIORITY_ADJ_ALWAYS_RUNNING = -80; 241 242 /** 243 * Indicates that the implementation of this job will be using 244 * {@link JobService#startForeground(int, android.app.Notification)} to run 245 * in the foreground. 246 * <p> 247 * When set, the internal scheduling of this job will ignore any background 248 * network restrictions for the requesting app. Note that this flag alone 249 * doesn't actually place your {@link JobService} in the foreground; you 250 * still need to post the notification yourself. 251 * <p> 252 * To use this flag, the caller must hold the 253 * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL} permission. 254 * 255 * @hide 256 */ 257 @UnsupportedAppUsage 258 public static final int FLAG_WILL_BE_FOREGROUND = 1 << 0; 259 260 /** 261 * Allows this job to run despite doze restrictions as long as the app is in the foreground 262 * or on the temporary whitelist 263 * @hide 264 */ 265 public static final int FLAG_IMPORTANT_WHILE_FOREGROUND = 1 << 1; 266 267 /** 268 * @hide 269 */ 270 public static final int FLAG_PREFETCH = 1 << 2; 271 272 /** 273 * This job needs to be exempted from the app standby throttling. Only the system (UID 1000) 274 * can set it. Jobs with a time constrant must not have it. 275 * 276 * @hide 277 */ 278 public static final int FLAG_EXEMPT_FROM_APP_STANDBY = 1 << 3; 279 280 /** 281 * @hide 282 */ 283 public static final int CONSTRAINT_FLAG_CHARGING = 1 << 0; 284 285 /** 286 * @hide 287 */ 288 public static final int CONSTRAINT_FLAG_BATTERY_NOT_LOW = 1 << 1; 289 290 /** 291 * @hide 292 */ 293 public static final int CONSTRAINT_FLAG_DEVICE_IDLE = 1 << 2; 294 295 /** 296 * @hide 297 */ 298 public static final int CONSTRAINT_FLAG_STORAGE_NOT_LOW = 1 << 3; 299 300 @UnsupportedAppUsage 301 private final int jobId; 302 private final PersistableBundle extras; 303 private final Bundle transientExtras; 304 private final ClipData clipData; 305 private final int clipGrantFlags; 306 @UnsupportedAppUsage 307 private final ComponentName service; 308 private final int constraintFlags; 309 private final TriggerContentUri[] triggerContentUris; 310 private final long triggerContentUpdateDelay; 311 private final long triggerContentMaxDelay; 312 private final boolean hasEarlyConstraint; 313 private final boolean hasLateConstraint; 314 private final NetworkRequest networkRequest; 315 private final long networkDownloadBytes; 316 private final long networkUploadBytes; 317 private final long minLatencyMillis; 318 private final long maxExecutionDelayMillis; 319 private final boolean isPeriodic; 320 private final boolean isPersisted; 321 private final long intervalMillis; 322 private final long flexMillis; 323 private final long initialBackoffMillis; 324 private final int backoffPolicy; 325 private final int priority; 326 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 327 private final int flags; 328 329 /** 330 * Unique job id associated with this application (uid). This is the same job ID 331 * you supplied in the {@link Builder} constructor. 332 */ getId()333 public int getId() { 334 return jobId; 335 } 336 337 /** 338 * @see JobInfo.Builder#setExtras(PersistableBundle) 339 */ getExtras()340 public @NonNull PersistableBundle getExtras() { 341 return extras; 342 } 343 344 /** 345 * @see JobInfo.Builder#setTransientExtras(Bundle) 346 */ getTransientExtras()347 public @NonNull Bundle getTransientExtras() { 348 return transientExtras; 349 } 350 351 /** 352 * @see JobInfo.Builder#setClipData(ClipData, int) 353 */ getClipData()354 public @Nullable ClipData getClipData() { 355 return clipData; 356 } 357 358 /** 359 * @see JobInfo.Builder#setClipData(ClipData, int) 360 */ getClipGrantFlags()361 public int getClipGrantFlags() { 362 return clipGrantFlags; 363 } 364 365 /** 366 * Name of the service endpoint that will be called back into by the JobScheduler. 367 */ getService()368 public @NonNull ComponentName getService() { 369 return service; 370 } 371 372 /** @hide */ getPriority()373 public int getPriority() { 374 return priority; 375 } 376 377 /** @hide */ getFlags()378 public int getFlags() { 379 return flags; 380 } 381 382 /** @hide */ isExemptedFromAppStandby()383 public boolean isExemptedFromAppStandby() { 384 return ((flags & FLAG_EXEMPT_FROM_APP_STANDBY) != 0) && !isPeriodic(); 385 } 386 387 /** 388 * @see JobInfo.Builder#setRequiresCharging(boolean) 389 */ isRequireCharging()390 public boolean isRequireCharging() { 391 return (constraintFlags & CONSTRAINT_FLAG_CHARGING) != 0; 392 } 393 394 /** 395 * @see JobInfo.Builder#setRequiresBatteryNotLow(boolean) 396 */ isRequireBatteryNotLow()397 public boolean isRequireBatteryNotLow() { 398 return (constraintFlags & CONSTRAINT_FLAG_BATTERY_NOT_LOW) != 0; 399 } 400 401 /** 402 * @see JobInfo.Builder#setRequiresDeviceIdle(boolean) 403 */ isRequireDeviceIdle()404 public boolean isRequireDeviceIdle() { 405 return (constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0; 406 } 407 408 /** 409 * @see JobInfo.Builder#setRequiresStorageNotLow(boolean) 410 */ isRequireStorageNotLow()411 public boolean isRequireStorageNotLow() { 412 return (constraintFlags & CONSTRAINT_FLAG_STORAGE_NOT_LOW) != 0; 413 } 414 415 /** 416 * @hide 417 */ getConstraintFlags()418 public int getConstraintFlags() { 419 return constraintFlags; 420 } 421 422 /** 423 * Which content: URIs must change for the job to be scheduled. Returns null 424 * if there are none required. 425 * @see JobInfo.Builder#addTriggerContentUri(TriggerContentUri) 426 */ getTriggerContentUris()427 public @Nullable TriggerContentUri[] getTriggerContentUris() { 428 return triggerContentUris; 429 } 430 431 /** 432 * When triggering on content URI changes, this is the delay from when a change 433 * is detected until the job is scheduled. 434 * @see JobInfo.Builder#setTriggerContentUpdateDelay(long) 435 */ getTriggerContentUpdateDelay()436 public long getTriggerContentUpdateDelay() { 437 return triggerContentUpdateDelay; 438 } 439 440 /** 441 * When triggering on content URI changes, this is the maximum delay we will 442 * use before scheduling the job. 443 * @see JobInfo.Builder#setTriggerContentMaxDelay(long) 444 */ getTriggerContentMaxDelay()445 public long getTriggerContentMaxDelay() { 446 return triggerContentMaxDelay; 447 } 448 449 /** 450 * Return the basic description of the kind of network this job requires. 451 * 452 * @deprecated This method attempts to map {@link #getRequiredNetwork()} 453 * into the set of simple constants, which results in a loss of 454 * fidelity. Callers should move to using 455 * {@link #getRequiredNetwork()} directly. 456 * @see Builder#setRequiredNetworkType(int) 457 */ 458 @Deprecated getNetworkType()459 public @NetworkType int getNetworkType() { 460 if (networkRequest == null) { 461 return NETWORK_TYPE_NONE; 462 } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) { 463 return NETWORK_TYPE_UNMETERED; 464 } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING)) { 465 return NETWORK_TYPE_NOT_ROAMING; 466 } else if (networkRequest.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) { 467 return NETWORK_TYPE_CELLULAR; 468 } else { 469 return NETWORK_TYPE_ANY; 470 } 471 } 472 473 /** 474 * Return the detailed description of the kind of network this job requires, 475 * or {@code null} if no specific kind of network is required. 476 * 477 * @see Builder#setRequiredNetwork(NetworkRequest) 478 */ getRequiredNetwork()479 public @Nullable NetworkRequest getRequiredNetwork() { 480 return networkRequest; 481 } 482 483 /** 484 * @deprecated replaced by {@link #getEstimatedNetworkDownloadBytes()} and 485 * {@link #getEstimatedNetworkUploadBytes()}. 486 * @removed 487 */ 488 @Deprecated getEstimatedNetworkBytes()489 public @BytesLong long getEstimatedNetworkBytes() { 490 if (networkDownloadBytes == NETWORK_BYTES_UNKNOWN 491 && networkUploadBytes == NETWORK_BYTES_UNKNOWN) { 492 return NETWORK_BYTES_UNKNOWN; 493 } else if (networkDownloadBytes == NETWORK_BYTES_UNKNOWN) { 494 return networkUploadBytes; 495 } else if (networkUploadBytes == NETWORK_BYTES_UNKNOWN) { 496 return networkDownloadBytes; 497 } else { 498 return networkDownloadBytes + networkUploadBytes; 499 } 500 } 501 502 /** 503 * Return the estimated size of download traffic that will be performed by 504 * this job, in bytes. 505 * 506 * @return Estimated size of download traffic, or 507 * {@link #NETWORK_BYTES_UNKNOWN} when unknown. 508 * @see Builder#setEstimatedNetworkBytes(long, long) 509 */ getEstimatedNetworkDownloadBytes()510 public @BytesLong long getEstimatedNetworkDownloadBytes() { 511 return networkDownloadBytes; 512 } 513 514 /** 515 * Return the estimated size of upload traffic that will be performed by 516 * this job, in bytes. 517 * 518 * @return Estimated size of upload traffic, or 519 * {@link #NETWORK_BYTES_UNKNOWN} when unknown. 520 * @see Builder#setEstimatedNetworkBytes(long, long) 521 */ getEstimatedNetworkUploadBytes()522 public @BytesLong long getEstimatedNetworkUploadBytes() { 523 return networkUploadBytes; 524 } 525 526 /** 527 * Set for a job that does not recur periodically, to specify a delay after which the job 528 * will be eligible for execution. This value is not set if the job recurs periodically. 529 * @see JobInfo.Builder#setMinimumLatency(long) 530 */ getMinLatencyMillis()531 public long getMinLatencyMillis() { 532 return minLatencyMillis; 533 } 534 535 /** 536 * @see JobInfo.Builder#setOverrideDeadline(long) 537 */ getMaxExecutionDelayMillis()538 public long getMaxExecutionDelayMillis() { 539 return maxExecutionDelayMillis; 540 } 541 542 /** 543 * Track whether this job will repeat with a given period. 544 * @see JobInfo.Builder#setPeriodic(long) 545 * @see JobInfo.Builder#setPeriodic(long, long) 546 */ isPeriodic()547 public boolean isPeriodic() { 548 return isPeriodic; 549 } 550 551 /** 552 * @see JobInfo.Builder#setPersisted(boolean) 553 */ isPersisted()554 public boolean isPersisted() { 555 return isPersisted; 556 } 557 558 /** 559 * Set to the interval between occurrences of this job. This value is <b>not</b> set if the 560 * job does not recur periodically. 561 * @see JobInfo.Builder#setPeriodic(long) 562 * @see JobInfo.Builder#setPeriodic(long, long) 563 */ getIntervalMillis()564 public long getIntervalMillis() { 565 return intervalMillis; 566 } 567 568 /** 569 * Flex time for this job. Only valid if this is a periodic job. The job can 570 * execute at any time in a window of flex length at the end of the period. 571 * @see JobInfo.Builder#setPeriodic(long) 572 * @see JobInfo.Builder#setPeriodic(long, long) 573 */ getFlexMillis()574 public long getFlexMillis() { 575 return flexMillis; 576 } 577 578 /** 579 * The amount of time the JobScheduler will wait before rescheduling a failed job. This value 580 * will be increased depending on the backoff policy specified at job creation time. Defaults 581 * to 30 seconds, minimum is currently 10 seconds. 582 * @see JobInfo.Builder#setBackoffCriteria(long, int) 583 */ getInitialBackoffMillis()584 public long getInitialBackoffMillis() { 585 return initialBackoffMillis; 586 } 587 588 /** 589 * Return the backoff policy of this job. 590 * @see JobInfo.Builder#setBackoffCriteria(long, int) 591 */ getBackoffPolicy()592 public @BackoffPolicy int getBackoffPolicy() { 593 return backoffPolicy; 594 } 595 596 /** 597 * @see JobInfo.Builder#setImportantWhileForeground(boolean) 598 */ isImportantWhileForeground()599 public boolean isImportantWhileForeground() { 600 return (flags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0; 601 } 602 603 /** 604 * @see JobInfo.Builder#setPrefetch(boolean) 605 */ isPrefetch()606 public boolean isPrefetch() { 607 return (flags & FLAG_PREFETCH) != 0; 608 } 609 610 /** 611 * User can specify an early constraint of 0L, which is valid, so we keep track of whether the 612 * function was called at all. 613 * @hide 614 */ hasEarlyConstraint()615 public boolean hasEarlyConstraint() { 616 return hasEarlyConstraint; 617 } 618 619 /** 620 * User can specify a late constraint of 0L, which is valid, so we keep track of whether the 621 * function was called at all. 622 * @hide 623 */ hasLateConstraint()624 public boolean hasLateConstraint() { 625 return hasLateConstraint; 626 } 627 628 @Override equals(Object o)629 public boolean equals(Object o) { 630 if (!(o instanceof JobInfo)) { 631 return false; 632 } 633 JobInfo j = (JobInfo) o; 634 if (jobId != j.jobId) { 635 return false; 636 } 637 // XXX won't be correct if one is parcelled and the other not. 638 if (!BaseBundle.kindofEquals(extras, j.extras)) { 639 return false; 640 } 641 // XXX won't be correct if one is parcelled and the other not. 642 if (!BaseBundle.kindofEquals(transientExtras, j.transientExtras)) { 643 return false; 644 } 645 // XXX for now we consider two different clip data objects to be different, 646 // regardless of whether their contents are the same. 647 if (clipData != j.clipData) { 648 return false; 649 } 650 if (clipGrantFlags != j.clipGrantFlags) { 651 return false; 652 } 653 if (!Objects.equals(service, j.service)) { 654 return false; 655 } 656 if (constraintFlags != j.constraintFlags) { 657 return false; 658 } 659 if (!Arrays.equals(triggerContentUris, j.triggerContentUris)) { 660 return false; 661 } 662 if (triggerContentUpdateDelay != j.triggerContentUpdateDelay) { 663 return false; 664 } 665 if (triggerContentMaxDelay != j.triggerContentMaxDelay) { 666 return false; 667 } 668 if (hasEarlyConstraint != j.hasEarlyConstraint) { 669 return false; 670 } 671 if (hasLateConstraint != j.hasLateConstraint) { 672 return false; 673 } 674 if (!Objects.equals(networkRequest, j.networkRequest)) { 675 return false; 676 } 677 if (networkDownloadBytes != j.networkDownloadBytes) { 678 return false; 679 } 680 if (networkUploadBytes != j.networkUploadBytes) { 681 return false; 682 } 683 if (minLatencyMillis != j.minLatencyMillis) { 684 return false; 685 } 686 if (maxExecutionDelayMillis != j.maxExecutionDelayMillis) { 687 return false; 688 } 689 if (isPeriodic != j.isPeriodic) { 690 return false; 691 } 692 if (isPersisted != j.isPersisted) { 693 return false; 694 } 695 if (intervalMillis != j.intervalMillis) { 696 return false; 697 } 698 if (flexMillis != j.flexMillis) { 699 return false; 700 } 701 if (initialBackoffMillis != j.initialBackoffMillis) { 702 return false; 703 } 704 if (backoffPolicy != j.backoffPolicy) { 705 return false; 706 } 707 if (priority != j.priority) { 708 return false; 709 } 710 if (flags != j.flags) { 711 return false; 712 } 713 return true; 714 } 715 716 @Override hashCode()717 public int hashCode() { 718 int hashCode = jobId; 719 if (extras != null) { 720 hashCode = 31 * hashCode + extras.hashCode(); 721 } 722 if (transientExtras != null) { 723 hashCode = 31 * hashCode + transientExtras.hashCode(); 724 } 725 if (clipData != null) { 726 hashCode = 31 * hashCode + clipData.hashCode(); 727 } 728 hashCode = 31*hashCode + clipGrantFlags; 729 if (service != null) { 730 hashCode = 31 * hashCode + service.hashCode(); 731 } 732 hashCode = 31 * hashCode + constraintFlags; 733 if (triggerContentUris != null) { 734 hashCode = 31 * hashCode + Arrays.hashCode(triggerContentUris); 735 } 736 hashCode = 31 * hashCode + Long.hashCode(triggerContentUpdateDelay); 737 hashCode = 31 * hashCode + Long.hashCode(triggerContentMaxDelay); 738 hashCode = 31 * hashCode + Boolean.hashCode(hasEarlyConstraint); 739 hashCode = 31 * hashCode + Boolean.hashCode(hasLateConstraint); 740 if (networkRequest != null) { 741 hashCode = 31 * hashCode + networkRequest.hashCode(); 742 } 743 hashCode = 31 * hashCode + Long.hashCode(networkDownloadBytes); 744 hashCode = 31 * hashCode + Long.hashCode(networkUploadBytes); 745 hashCode = 31 * hashCode + Long.hashCode(minLatencyMillis); 746 hashCode = 31 * hashCode + Long.hashCode(maxExecutionDelayMillis); 747 hashCode = 31 * hashCode + Boolean.hashCode(isPeriodic); 748 hashCode = 31 * hashCode + Boolean.hashCode(isPersisted); 749 hashCode = 31 * hashCode + Long.hashCode(intervalMillis); 750 hashCode = 31 * hashCode + Long.hashCode(flexMillis); 751 hashCode = 31 * hashCode + Long.hashCode(initialBackoffMillis); 752 hashCode = 31 * hashCode + backoffPolicy; 753 hashCode = 31 * hashCode + priority; 754 hashCode = 31 * hashCode + flags; 755 return hashCode; 756 } 757 JobInfo(Parcel in)758 private JobInfo(Parcel in) { 759 jobId = in.readInt(); 760 extras = in.readPersistableBundle(); 761 transientExtras = in.readBundle(); 762 if (in.readInt() != 0) { 763 clipData = ClipData.CREATOR.createFromParcel(in); 764 clipGrantFlags = in.readInt(); 765 } else { 766 clipData = null; 767 clipGrantFlags = 0; 768 } 769 service = in.readParcelable(null); 770 constraintFlags = in.readInt(); 771 triggerContentUris = in.createTypedArray(TriggerContentUri.CREATOR); 772 triggerContentUpdateDelay = in.readLong(); 773 triggerContentMaxDelay = in.readLong(); 774 if (in.readInt() != 0) { 775 networkRequest = NetworkRequest.CREATOR.createFromParcel(in); 776 } else { 777 networkRequest = null; 778 } 779 networkDownloadBytes = in.readLong(); 780 networkUploadBytes = in.readLong(); 781 minLatencyMillis = in.readLong(); 782 maxExecutionDelayMillis = in.readLong(); 783 isPeriodic = in.readInt() == 1; 784 isPersisted = in.readInt() == 1; 785 intervalMillis = in.readLong(); 786 flexMillis = in.readLong(); 787 initialBackoffMillis = in.readLong(); 788 backoffPolicy = in.readInt(); 789 hasEarlyConstraint = in.readInt() == 1; 790 hasLateConstraint = in.readInt() == 1; 791 priority = in.readInt(); 792 flags = in.readInt(); 793 } 794 JobInfo(JobInfo.Builder b)795 private JobInfo(JobInfo.Builder b) { 796 jobId = b.mJobId; 797 extras = b.mExtras.deepCopy(); 798 transientExtras = b.mTransientExtras.deepCopy(); 799 clipData = b.mClipData; 800 clipGrantFlags = b.mClipGrantFlags; 801 service = b.mJobService; 802 constraintFlags = b.mConstraintFlags; 803 triggerContentUris = b.mTriggerContentUris != null 804 ? b.mTriggerContentUris.toArray(new TriggerContentUri[b.mTriggerContentUris.size()]) 805 : null; 806 triggerContentUpdateDelay = b.mTriggerContentUpdateDelay; 807 triggerContentMaxDelay = b.mTriggerContentMaxDelay; 808 networkRequest = b.mNetworkRequest; 809 networkDownloadBytes = b.mNetworkDownloadBytes; 810 networkUploadBytes = b.mNetworkUploadBytes; 811 minLatencyMillis = b.mMinLatencyMillis; 812 maxExecutionDelayMillis = b.mMaxExecutionDelayMillis; 813 isPeriodic = b.mIsPeriodic; 814 isPersisted = b.mIsPersisted; 815 intervalMillis = b.mIntervalMillis; 816 flexMillis = b.mFlexMillis; 817 initialBackoffMillis = b.mInitialBackoffMillis; 818 backoffPolicy = b.mBackoffPolicy; 819 hasEarlyConstraint = b.mHasEarlyConstraint; 820 hasLateConstraint = b.mHasLateConstraint; 821 priority = b.mPriority; 822 flags = b.mFlags; 823 } 824 825 @Override describeContents()826 public int describeContents() { 827 return 0; 828 } 829 830 @Override writeToParcel(Parcel out, int flags)831 public void writeToParcel(Parcel out, int flags) { 832 out.writeInt(jobId); 833 out.writePersistableBundle(extras); 834 out.writeBundle(transientExtras); 835 if (clipData != null) { 836 out.writeInt(1); 837 clipData.writeToParcel(out, flags); 838 out.writeInt(clipGrantFlags); 839 } else { 840 out.writeInt(0); 841 } 842 out.writeParcelable(service, flags); 843 out.writeInt(constraintFlags); 844 out.writeTypedArray(triggerContentUris, flags); 845 out.writeLong(triggerContentUpdateDelay); 846 out.writeLong(triggerContentMaxDelay); 847 if (networkRequest != null) { 848 out.writeInt(1); 849 networkRequest.writeToParcel(out, flags); 850 } else { 851 out.writeInt(0); 852 } 853 out.writeLong(networkDownloadBytes); 854 out.writeLong(networkUploadBytes); 855 out.writeLong(minLatencyMillis); 856 out.writeLong(maxExecutionDelayMillis); 857 out.writeInt(isPeriodic ? 1 : 0); 858 out.writeInt(isPersisted ? 1 : 0); 859 out.writeLong(intervalMillis); 860 out.writeLong(flexMillis); 861 out.writeLong(initialBackoffMillis); 862 out.writeInt(backoffPolicy); 863 out.writeInt(hasEarlyConstraint ? 1 : 0); 864 out.writeInt(hasLateConstraint ? 1 : 0); 865 out.writeInt(priority); 866 out.writeInt(this.flags); 867 } 868 869 public static final @android.annotation.NonNull Creator<JobInfo> CREATOR = new Creator<JobInfo>() { 870 @Override 871 public JobInfo createFromParcel(Parcel in) { 872 return new JobInfo(in); 873 } 874 875 @Override 876 public JobInfo[] newArray(int size) { 877 return new JobInfo[size]; 878 } 879 }; 880 881 @Override toString()882 public String toString() { 883 return "(job:" + jobId + "/" + service.flattenToShortString() + ")"; 884 } 885 886 /** 887 * Information about a content URI modification that a job would like to 888 * trigger on. 889 */ 890 public static final class TriggerContentUri implements Parcelable { 891 private final Uri mUri; 892 private final int mFlags; 893 894 /** @hide */ 895 @Retention(RetentionPolicy.SOURCE) 896 @IntDef(flag = true, prefix = { "FLAG_" }, value = { 897 FLAG_NOTIFY_FOR_DESCENDANTS, 898 }) 899 public @interface Flags { } 900 901 /** 902 * Flag for trigger: also trigger if any descendants of the given URI change. 903 * Corresponds to the <var>notifyForDescendants</var> of 904 * {@link android.content.ContentResolver#registerContentObserver}. 905 */ 906 public static final int FLAG_NOTIFY_FOR_DESCENDANTS = 1<<0; 907 908 /** 909 * Create a new trigger description. 910 * @param uri The URI to observe. Must be non-null. 911 * @param flags Flags for the observer. 912 */ TriggerContentUri(@onNull Uri uri, @Flags int flags)913 public TriggerContentUri(@NonNull Uri uri, @Flags int flags) { 914 mUri = uri; 915 mFlags = flags; 916 } 917 918 /** 919 * Return the Uri this trigger was created for. 920 */ getUri()921 public Uri getUri() { 922 return mUri; 923 } 924 925 /** 926 * Return the flags supplied for the trigger. 927 */ getFlags()928 public @Flags int getFlags() { 929 return mFlags; 930 } 931 932 @Override equals(Object o)933 public boolean equals(Object o) { 934 if (!(o instanceof TriggerContentUri)) { 935 return false; 936 } 937 TriggerContentUri t = (TriggerContentUri) o; 938 return Objects.equals(t.mUri, mUri) && t.mFlags == mFlags; 939 } 940 941 @Override hashCode()942 public int hashCode() { 943 return (mUri == null ? 0 : mUri.hashCode()) ^ mFlags; 944 } 945 TriggerContentUri(Parcel in)946 private TriggerContentUri(Parcel in) { 947 mUri = Uri.CREATOR.createFromParcel(in); 948 mFlags = in.readInt(); 949 } 950 951 @Override describeContents()952 public int describeContents() { 953 return 0; 954 } 955 956 @Override writeToParcel(Parcel out, int flags)957 public void writeToParcel(Parcel out, int flags) { 958 mUri.writeToParcel(out, flags); 959 out.writeInt(mFlags); 960 } 961 962 public static final @android.annotation.NonNull Creator<TriggerContentUri> CREATOR = new Creator<TriggerContentUri>() { 963 @Override 964 public TriggerContentUri createFromParcel(Parcel in) { 965 return new TriggerContentUri(in); 966 } 967 968 @Override 969 public TriggerContentUri[] newArray(int size) { 970 return new TriggerContentUri[size]; 971 } 972 }; 973 } 974 975 /** Builder class for constructing {@link JobInfo} objects. */ 976 public static final class Builder { 977 private final int mJobId; 978 private final ComponentName mJobService; 979 private PersistableBundle mExtras = PersistableBundle.EMPTY; 980 private Bundle mTransientExtras = Bundle.EMPTY; 981 private ClipData mClipData; 982 private int mClipGrantFlags; 983 private int mPriority = PRIORITY_DEFAULT; 984 private int mFlags; 985 // Requirements. 986 private int mConstraintFlags; 987 private NetworkRequest mNetworkRequest; 988 private long mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN; 989 private long mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN; 990 private ArrayList<TriggerContentUri> mTriggerContentUris; 991 private long mTriggerContentUpdateDelay = -1; 992 private long mTriggerContentMaxDelay = -1; 993 private boolean mIsPersisted; 994 // One-off parameters. 995 private long mMinLatencyMillis; 996 private long mMaxExecutionDelayMillis; 997 // Periodic parameters. 998 private boolean mIsPeriodic; 999 private boolean mHasEarlyConstraint; 1000 private boolean mHasLateConstraint; 1001 private long mIntervalMillis; 1002 private long mFlexMillis; 1003 // Back-off parameters. 1004 private long mInitialBackoffMillis = DEFAULT_INITIAL_BACKOFF_MILLIS; 1005 private int mBackoffPolicy = DEFAULT_BACKOFF_POLICY; 1006 /** Easy way to track whether the client has tried to set a back-off policy. */ 1007 private boolean mBackoffPolicySet = false; 1008 1009 /** 1010 * Initialize a new Builder to construct a {@link JobInfo}. 1011 * 1012 * @param jobId Application-provided id for this job. Subsequent calls to cancel, or 1013 * jobs created with the same jobId, will update the pre-existing job with 1014 * the same id. This ID must be unique across all clients of the same uid 1015 * (not just the same package). You will want to make sure this is a stable 1016 * id across app updates, so probably not based on a resource ID. 1017 * @param jobService The endpoint that you implement that will receive the callback from the 1018 * JobScheduler. 1019 */ Builder(int jobId, @NonNull ComponentName jobService)1020 public Builder(int jobId, @NonNull ComponentName jobService) { 1021 mJobService = jobService; 1022 mJobId = jobId; 1023 } 1024 1025 /** @hide */ 1026 @UnsupportedAppUsage setPriority(int priority)1027 public Builder setPriority(int priority) { 1028 mPriority = priority; 1029 return this; 1030 } 1031 1032 /** @hide */ 1033 @UnsupportedAppUsage setFlags(int flags)1034 public Builder setFlags(int flags) { 1035 mFlags = flags; 1036 return this; 1037 } 1038 1039 /** 1040 * Set optional extras. This is persisted, so we only allow primitive types. 1041 * @param extras Bundle containing extras you want the scheduler to hold on to for you. 1042 * @see JobInfo#getExtras() 1043 */ setExtras(@onNull PersistableBundle extras)1044 public Builder setExtras(@NonNull PersistableBundle extras) { 1045 mExtras = extras; 1046 return this; 1047 } 1048 1049 /** 1050 * Set optional transient extras. 1051 * 1052 * <p>Because setting this property is not compatible with persisted 1053 * jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when 1054 * {@link android.app.job.JobInfo.Builder#build()} is called.</p> 1055 * 1056 * @param extras Bundle containing extras you want the scheduler to hold on to for you. 1057 * @see JobInfo#getTransientExtras() 1058 */ setTransientExtras(@onNull Bundle extras)1059 public Builder setTransientExtras(@NonNull Bundle extras) { 1060 mTransientExtras = extras; 1061 return this; 1062 } 1063 1064 /** 1065 * Set a {@link ClipData} associated with this Job. 1066 * 1067 * <p>The main purpose of providing a ClipData is to allow granting of 1068 * URI permissions for data associated with the clip. The exact kind 1069 * of permission grant to perform is specified through <var>grantFlags</var>. 1070 * 1071 * <p>If the ClipData contains items that are Intents, any 1072 * grant flags in those Intents will be ignored. Only flags provided as an argument 1073 * to this method are respected, and will be applied to all Uri or 1074 * Intent items in the clip (or sub-items of the clip). 1075 * 1076 * <p>Because setting this property is not compatible with persisted 1077 * jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when 1078 * {@link android.app.job.JobInfo.Builder#build()} is called.</p> 1079 * 1080 * @param clip The new clip to set. May be null to clear the current clip. 1081 * @param grantFlags The desired permissions to grant for any URIs. This should be 1082 * a combination of {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION}, 1083 * {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, and 1084 * {@link android.content.Intent#FLAG_GRANT_PREFIX_URI_PERMISSION}. 1085 * @see JobInfo#getClipData() 1086 * @see JobInfo#getClipGrantFlags() 1087 */ setClipData(@ullable ClipData clip, int grantFlags)1088 public Builder setClipData(@Nullable ClipData clip, int grantFlags) { 1089 mClipData = clip; 1090 mClipGrantFlags = grantFlags; 1091 return this; 1092 } 1093 1094 /** 1095 * Set basic description of the kind of network your job requires. If 1096 * you need more precise control over network capabilities, see 1097 * {@link #setRequiredNetwork(NetworkRequest)}. 1098 * <p> 1099 * If your job doesn't need a network connection, you don't need to call 1100 * this method, as the default value is {@link #NETWORK_TYPE_NONE}. 1101 * <p> 1102 * Calling this method defines network as a strict requirement for your 1103 * job. If the network requested is not available your job will never 1104 * run. See {@link #setOverrideDeadline(long)} to change this behavior. 1105 * Calling this method will override any requirements previously defined 1106 * by {@link #setRequiredNetwork(NetworkRequest)}; you typically only 1107 * want to call one of these methods. 1108 * <p class="note"> 1109 * When your job executes in 1110 * {@link JobService#onStartJob(JobParameters)}, be sure to use the 1111 * specific network returned by {@link JobParameters#getNetwork()}, 1112 * otherwise you'll use the default network which may not meet this 1113 * constraint. 1114 * 1115 * @see #setRequiredNetwork(NetworkRequest) 1116 * @see JobInfo#getNetworkType() 1117 * @see JobParameters#getNetwork() 1118 */ setRequiredNetworkType(@etworkType int networkType)1119 public Builder setRequiredNetworkType(@NetworkType int networkType) { 1120 if (networkType == NETWORK_TYPE_NONE) { 1121 return setRequiredNetwork(null); 1122 } else { 1123 final NetworkRequest.Builder builder = new NetworkRequest.Builder(); 1124 1125 // All types require validated Internet 1126 builder.addCapability(NET_CAPABILITY_INTERNET); 1127 builder.addCapability(NET_CAPABILITY_VALIDATED); 1128 builder.removeCapability(NET_CAPABILITY_NOT_VPN); 1129 1130 if (networkType == NETWORK_TYPE_ANY) { 1131 // No other capabilities 1132 } else if (networkType == NETWORK_TYPE_UNMETERED) { 1133 builder.addCapability(NET_CAPABILITY_NOT_METERED); 1134 } else if (networkType == NETWORK_TYPE_NOT_ROAMING) { 1135 builder.addCapability(NET_CAPABILITY_NOT_ROAMING); 1136 } else if (networkType == NETWORK_TYPE_CELLULAR) { 1137 builder.addTransportType(TRANSPORT_CELLULAR); 1138 } 1139 1140 return setRequiredNetwork(builder.build()); 1141 } 1142 } 1143 1144 /** 1145 * Set detailed description of the kind of network your job requires. 1146 * <p> 1147 * If your job doesn't need a network connection, you don't need to call 1148 * this method, as the default is {@code null}. 1149 * <p> 1150 * Calling this method defines network as a strict requirement for your 1151 * job. If the network requested is not available your job will never 1152 * run. See {@link #setOverrideDeadline(long)} to change this behavior. 1153 * Calling this method will override any requirements previously defined 1154 * by {@link #setRequiredNetworkType(int)}; you typically only want to 1155 * call one of these methods. 1156 * <p class="note"> 1157 * When your job executes in 1158 * {@link JobService#onStartJob(JobParameters)}, be sure to use the 1159 * specific network returned by {@link JobParameters#getNetwork()}, 1160 * otherwise you'll use the default network which may not meet this 1161 * constraint. 1162 * 1163 * @param networkRequest The detailed description of the kind of network 1164 * this job requires, or {@code null} if no specific kind of 1165 * network is required. Defining a {@link NetworkSpecifier} 1166 * is only supported for jobs that aren't persisted. 1167 * @see #setRequiredNetworkType(int) 1168 * @see JobInfo#getRequiredNetwork() 1169 * @see JobParameters#getNetwork() 1170 */ setRequiredNetwork(@ullable NetworkRequest networkRequest)1171 public Builder setRequiredNetwork(@Nullable NetworkRequest networkRequest) { 1172 mNetworkRequest = networkRequest; 1173 return this; 1174 } 1175 1176 /** 1177 * @deprecated replaced by 1178 * {@link #setEstimatedNetworkBytes(long, long)}. 1179 * @removed 1180 */ 1181 @Deprecated setEstimatedNetworkBytes(@ytesLong long networkBytes)1182 public Builder setEstimatedNetworkBytes(@BytesLong long networkBytes) { 1183 return setEstimatedNetworkBytes(networkBytes, NETWORK_BYTES_UNKNOWN); 1184 } 1185 1186 /** 1187 * Set the estimated size of network traffic that will be performed by 1188 * this job, in bytes. 1189 * <p> 1190 * Apps are encouraged to provide values that are as accurate as 1191 * possible, but when the exact size isn't available, an 1192 * order-of-magnitude estimate can be provided instead. Here are some 1193 * specific examples: 1194 * <ul> 1195 * <li>A job that is backing up a photo knows the exact size of that 1196 * photo, so it should provide that size as the estimate. 1197 * <li>A job that refreshes top news stories wouldn't know an exact 1198 * size, but if the size is expected to be consistently around 100KB, it 1199 * can provide that order-of-magnitude value as the estimate. 1200 * <li>A job that synchronizes email could end up using an extreme range 1201 * of data, from under 1KB when nothing has changed, to dozens of MB 1202 * when there are new emails with attachments. Jobs that cannot provide 1203 * reasonable estimates should use the sentinel value 1204 * {@link JobInfo#NETWORK_BYTES_UNKNOWN}. 1205 * </ul> 1206 * Note that the system may choose to delay jobs with large network 1207 * usage estimates when the device has a poor network connection, in 1208 * order to save battery. 1209 * <p> 1210 * The values provided here only reflect the traffic that will be 1211 * performed by the base job; if you're using {@link JobWorkItem} then 1212 * you also need to define the network traffic used by each work item 1213 * when constructing them. 1214 * 1215 * @param downloadBytes The estimated size of network traffic that will 1216 * be downloaded by this job, in bytes. 1217 * @param uploadBytes The estimated size of network traffic that will be 1218 * uploaded by this job, in bytes. 1219 * @see JobInfo#getEstimatedNetworkDownloadBytes() 1220 * @see JobInfo#getEstimatedNetworkUploadBytes() 1221 * @see JobWorkItem#JobWorkItem(android.content.Intent, long, long) 1222 */ setEstimatedNetworkBytes(@ytesLong long downloadBytes, @BytesLong long uploadBytes)1223 public Builder setEstimatedNetworkBytes(@BytesLong long downloadBytes, 1224 @BytesLong long uploadBytes) { 1225 mNetworkDownloadBytes = downloadBytes; 1226 mNetworkUploadBytes = uploadBytes; 1227 return this; 1228 } 1229 1230 /** 1231 * Specify that to run this job, the device must be charging (or be a 1232 * non-battery-powered device connected to permanent power, such as Android TV 1233 * devices). This defaults to {@code false}. 1234 * 1235 * <p class="note">For purposes of running jobs, a battery-powered device 1236 * "charging" is not quite the same as simply being connected to power. If the 1237 * device is so busy that the battery is draining despite a power connection, jobs 1238 * with this constraint will <em>not</em> run. This can happen during some 1239 * common use cases such as video chat, particularly if the device is plugged in 1240 * to USB rather than to wall power. 1241 * 1242 * @param requiresCharging Pass {@code true} to require that the device be 1243 * charging in order to run the job. 1244 * @see JobInfo#isRequireCharging() 1245 */ setRequiresCharging(boolean requiresCharging)1246 public Builder setRequiresCharging(boolean requiresCharging) { 1247 mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_CHARGING) 1248 | (requiresCharging ? CONSTRAINT_FLAG_CHARGING : 0); 1249 return this; 1250 } 1251 1252 /** 1253 * Specify that to run this job, the device's battery level must not be low. 1254 * This defaults to false. If true, the job will only run when the battery level 1255 * is not low, which is generally the point where the user is given a "low battery" 1256 * warning. 1257 * @param batteryNotLow Whether or not the device's battery level must not be low. 1258 * @see JobInfo#isRequireBatteryNotLow() 1259 */ setRequiresBatteryNotLow(boolean batteryNotLow)1260 public Builder setRequiresBatteryNotLow(boolean batteryNotLow) { 1261 mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_BATTERY_NOT_LOW) 1262 | (batteryNotLow ? CONSTRAINT_FLAG_BATTERY_NOT_LOW : 0); 1263 return this; 1264 } 1265 1266 /** 1267 * When set {@code true}, ensure that this job will not run if the device is in active use. 1268 * The default state is {@code false}: that is, the for the job to be runnable even when 1269 * someone is interacting with the device. 1270 * 1271 * <p>This state is a loose definition provided by the system. In general, it means that 1272 * the device is not currently being used interactively, and has not been in use for some 1273 * time. As such, it is a good time to perform resource heavy jobs. Bear in mind that 1274 * battery usage will still be attributed to your application, and surfaced to the user in 1275 * battery stats.</p> 1276 * 1277 * <p class="note">Despite the similar naming, this job constraint is <em>not</em> 1278 * related to the system's "device idle" or "doze" states. This constraint only 1279 * determines whether a job is allowed to run while the device is directly in use. 1280 * 1281 * @param requiresDeviceIdle Pass {@code true} to prevent the job from running 1282 * while the device is being used interactively. 1283 * @see JobInfo#isRequireDeviceIdle() 1284 */ setRequiresDeviceIdle(boolean requiresDeviceIdle)1285 public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) { 1286 mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_DEVICE_IDLE) 1287 | (requiresDeviceIdle ? CONSTRAINT_FLAG_DEVICE_IDLE : 0); 1288 return this; 1289 } 1290 1291 /** 1292 * Specify that to run this job, the device's available storage must not be low. 1293 * This defaults to false. If true, the job will only run when the device is not 1294 * in a low storage state, which is generally the point where the user is given a 1295 * "low storage" warning. 1296 * @param storageNotLow Whether or not the device's available storage must not be low. 1297 * @see JobInfo#isRequireStorageNotLow() 1298 */ setRequiresStorageNotLow(boolean storageNotLow)1299 public Builder setRequiresStorageNotLow(boolean storageNotLow) { 1300 mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_STORAGE_NOT_LOW) 1301 | (storageNotLow ? CONSTRAINT_FLAG_STORAGE_NOT_LOW : 0); 1302 return this; 1303 } 1304 1305 /** 1306 * Add a new content: URI that will be monitored with a 1307 * {@link android.database.ContentObserver}, and will cause the job to execute if changed. 1308 * If you have any trigger content URIs associated with a job, it will not execute until 1309 * there has been a change report for one or more of them. 1310 * 1311 * <p>Note that trigger URIs can not be used in combination with 1312 * {@link #setPeriodic(long)} or {@link #setPersisted(boolean)}. To continually monitor 1313 * for content changes, you need to schedule a new JobInfo observing the same URIs 1314 * before you finish execution of the JobService handling the most recent changes. 1315 * Following this pattern will ensure you do not lost any content changes: while your 1316 * job is running, the system will continue monitoring for content changes, and propagate 1317 * any it sees over to the next job you schedule.</p> 1318 * 1319 * <p>Because setting this property is not compatible with periodic or 1320 * persisted jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when 1321 * {@link android.app.job.JobInfo.Builder#build()} is called.</p> 1322 * 1323 * <p>The following example shows how this feature can be used to monitor for changes 1324 * in the photos on a device.</p> 1325 * 1326 * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/PhotosContentJob.java 1327 * job} 1328 * 1329 * @param uri The content: URI to monitor. 1330 * @see JobInfo#getTriggerContentUris() 1331 */ addTriggerContentUri(@onNull TriggerContentUri uri)1332 public Builder addTriggerContentUri(@NonNull TriggerContentUri uri) { 1333 if (mTriggerContentUris == null) { 1334 mTriggerContentUris = new ArrayList<>(); 1335 } 1336 mTriggerContentUris.add(uri); 1337 return this; 1338 } 1339 1340 /** 1341 * Set the delay (in milliseconds) from when a content change is detected until 1342 * the job is scheduled. If there are more changes during that time, the delay 1343 * will be reset to start at the time of the most recent change. 1344 * @param durationMs Delay after most recent content change, in milliseconds. 1345 * @see JobInfo#getTriggerContentUpdateDelay() 1346 */ setTriggerContentUpdateDelay(long durationMs)1347 public Builder setTriggerContentUpdateDelay(long durationMs) { 1348 mTriggerContentUpdateDelay = durationMs; 1349 return this; 1350 } 1351 1352 /** 1353 * Set the maximum total delay (in milliseconds) that is allowed from the first 1354 * time a content change is detected until the job is scheduled. 1355 * @param durationMs Delay after initial content change, in milliseconds. 1356 * @see JobInfo#getTriggerContentMaxDelay() 1357 */ setTriggerContentMaxDelay(long durationMs)1358 public Builder setTriggerContentMaxDelay(long durationMs) { 1359 mTriggerContentMaxDelay = durationMs; 1360 return this; 1361 } 1362 1363 /** 1364 * Specify that this job should recur with the provided interval, not more than once per 1365 * period. You have no control over when within this interval this job will be executed, 1366 * only the guarantee that it will be executed at most once within this interval. 1367 * Setting this function on the builder with {@link #setMinimumLatency(long)} or 1368 * {@link #setOverrideDeadline(long)} will result in an error. 1369 * @param intervalMillis Millisecond interval for which this job will repeat. 1370 * @see JobInfo#getIntervalMillis() 1371 * @see JobInfo#getFlexMillis() 1372 */ setPeriodic(long intervalMillis)1373 public Builder setPeriodic(long intervalMillis) { 1374 return setPeriodic(intervalMillis, intervalMillis); 1375 } 1376 1377 /** 1378 * Specify that this job should recur with the provided interval and flex. The job can 1379 * execute at any time in a window of flex length at the end of the period. 1380 * @param intervalMillis Millisecond interval for which this job will repeat. A minimum 1381 * value of {@link #getMinPeriodMillis()} is enforced. 1382 * @param flexMillis Millisecond flex for this job. Flex is clamped to be at least 1383 * {@link #getMinFlexMillis()} or 5 percent of the period, whichever is 1384 * higher. 1385 * @see JobInfo#getIntervalMillis() 1386 * @see JobInfo#getFlexMillis() 1387 */ setPeriodic(long intervalMillis, long flexMillis)1388 public Builder setPeriodic(long intervalMillis, long flexMillis) { 1389 final long minPeriod = getMinPeriodMillis(); 1390 if (intervalMillis < minPeriod) { 1391 Log.w(TAG, "Requested interval " + formatDuration(intervalMillis) + " for job " 1392 + mJobId + " is too small; raising to " + formatDuration(minPeriod)); 1393 intervalMillis = minPeriod; 1394 } 1395 1396 final long percentClamp = 5 * intervalMillis / 100; 1397 final long minFlex = Math.max(percentClamp, getMinFlexMillis()); 1398 if (flexMillis < minFlex) { 1399 Log.w(TAG, "Requested flex " + formatDuration(flexMillis) + " for job " + mJobId 1400 + " is too small; raising to " + formatDuration(minFlex)); 1401 flexMillis = minFlex; 1402 } 1403 1404 mIsPeriodic = true; 1405 mIntervalMillis = intervalMillis; 1406 mFlexMillis = flexMillis; 1407 mHasEarlyConstraint = mHasLateConstraint = true; 1408 return this; 1409 } 1410 1411 /** 1412 * Specify that this job should be delayed by the provided amount of time. 1413 * Because it doesn't make sense setting this property on a periodic job, doing so will 1414 * throw an {@link java.lang.IllegalArgumentException} when 1415 * {@link android.app.job.JobInfo.Builder#build()} is called. 1416 * @param minLatencyMillis Milliseconds before which this job will not be considered for 1417 * execution. 1418 * @see JobInfo#getMinLatencyMillis() 1419 */ setMinimumLatency(long minLatencyMillis)1420 public Builder setMinimumLatency(long minLatencyMillis) { 1421 mMinLatencyMillis = minLatencyMillis; 1422 mHasEarlyConstraint = true; 1423 return this; 1424 } 1425 1426 /** 1427 * Set deadline which is the maximum scheduling latency. The job will be run by this 1428 * deadline even if other requirements are not met. Because it doesn't make sense setting 1429 * this property on a periodic job, doing so will throw an 1430 * {@link java.lang.IllegalArgumentException} when 1431 * {@link android.app.job.JobInfo.Builder#build()} is called. 1432 * @see JobInfo#getMaxExecutionDelayMillis() 1433 */ setOverrideDeadline(long maxExecutionDelayMillis)1434 public Builder setOverrideDeadline(long maxExecutionDelayMillis) { 1435 mMaxExecutionDelayMillis = maxExecutionDelayMillis; 1436 mHasLateConstraint = true; 1437 return this; 1438 } 1439 1440 /** 1441 * Set up the back-off/retry policy. 1442 * This defaults to some respectable values: {30 seconds, Exponential}. We cap back-off at 1443 * 5hrs. 1444 * Note that trying to set a backoff criteria for a job with 1445 * {@link #setRequiresDeviceIdle(boolean)} will throw an exception when you call build(). 1446 * This is because back-off typically does not make sense for these types of jobs. See 1447 * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)} 1448 * for more description of the return value for the case of a job executing while in idle 1449 * mode. 1450 * @param initialBackoffMillis Millisecond time interval to wait initially when job has 1451 * failed. 1452 * @see JobInfo#getInitialBackoffMillis() 1453 * @see JobInfo#getBackoffPolicy() 1454 */ setBackoffCriteria(long initialBackoffMillis, @BackoffPolicy int backoffPolicy)1455 public Builder setBackoffCriteria(long initialBackoffMillis, 1456 @BackoffPolicy int backoffPolicy) { 1457 final long minBackoff = getMinBackoffMillis(); 1458 if (initialBackoffMillis < minBackoff) { 1459 Log.w(TAG, "Requested backoff " + formatDuration(initialBackoffMillis) + " for job " 1460 + mJobId + " is too small; raising to " + formatDuration(minBackoff)); 1461 initialBackoffMillis = minBackoff; 1462 } 1463 1464 mBackoffPolicySet = true; 1465 mInitialBackoffMillis = initialBackoffMillis; 1466 mBackoffPolicy = backoffPolicy; 1467 return this; 1468 } 1469 1470 /** 1471 * Setting this to true indicates that this job is important while the scheduling app 1472 * is in the foreground or on the temporary whitelist for background restrictions. 1473 * This means that the system will relax doze restrictions on this job during this time. 1474 * 1475 * Apps should use this flag only for short jobs that are essential for the app to function 1476 * properly in the foreground. 1477 * 1478 * Note that once the scheduling app is no longer whitelisted from background restrictions 1479 * and in the background, or the job failed due to unsatisfied constraints, 1480 * this job should be expected to behave like other jobs without this flag. 1481 * 1482 * @param importantWhileForeground whether to relax doze restrictions for this job when the 1483 * app is in the foreground. False by default. 1484 * @see JobInfo#isImportantWhileForeground() 1485 */ setImportantWhileForeground(boolean importantWhileForeground)1486 public Builder setImportantWhileForeground(boolean importantWhileForeground) { 1487 if (importantWhileForeground) { 1488 mFlags |= FLAG_IMPORTANT_WHILE_FOREGROUND; 1489 } else { 1490 mFlags &= (~FLAG_IMPORTANT_WHILE_FOREGROUND); 1491 } 1492 return this; 1493 } 1494 1495 /** 1496 * @removed 1497 * @deprecated replaced with {@link #setPrefetch(boolean)} 1498 */ 1499 @Deprecated setIsPrefetch(boolean isPrefetch)1500 public Builder setIsPrefetch(boolean isPrefetch) { 1501 return setPrefetch(isPrefetch); 1502 } 1503 1504 /** 1505 * Setting this to true indicates that this job is designed to prefetch 1506 * content that will make a material improvement to the experience of 1507 * the specific user of this device. For example, fetching top headlines 1508 * of interest to the current user. 1509 * <p> 1510 * The system may use this signal to relax the network constraints you 1511 * originally requested, such as allowing a 1512 * {@link JobInfo#NETWORK_TYPE_UNMETERED} job to run over a metered 1513 * network when there is a surplus of metered data available. The system 1514 * may also use this signal in combination with end user usage patterns 1515 * to ensure data is prefetched before the user launches your app. 1516 * @see JobInfo#isPrefetch() 1517 */ setPrefetch(boolean prefetch)1518 public Builder setPrefetch(boolean prefetch) { 1519 if (prefetch) { 1520 mFlags |= FLAG_PREFETCH; 1521 } else { 1522 mFlags &= (~FLAG_PREFETCH); 1523 } 1524 return this; 1525 } 1526 1527 /** 1528 * Set whether or not to persist this job across device reboots. 1529 * 1530 * @param isPersisted True to indicate that the job will be written to 1531 * disk and loaded at boot. 1532 * @see JobInfo#isPersisted() 1533 */ 1534 @RequiresPermission(android.Manifest.permission.RECEIVE_BOOT_COMPLETED) setPersisted(boolean isPersisted)1535 public Builder setPersisted(boolean isPersisted) { 1536 mIsPersisted = isPersisted; 1537 return this; 1538 } 1539 1540 /** 1541 * @return The job object to hand to the JobScheduler. This object is immutable. 1542 */ build()1543 public JobInfo build() { 1544 // Check that network estimates require network type 1545 if ((mNetworkDownloadBytes > 0 || mNetworkUploadBytes > 0) && mNetworkRequest == null) { 1546 throw new IllegalArgumentException( 1547 "Can't provide estimated network usage without requiring a network"); 1548 } 1549 // We can't serialize network specifiers 1550 if (mIsPersisted && mNetworkRequest != null 1551 && mNetworkRequest.networkCapabilities.getNetworkSpecifier() != null) { 1552 throw new IllegalArgumentException( 1553 "Network specifiers aren't supported for persistent jobs"); 1554 } 1555 // Check that a deadline was not set on a periodic job. 1556 if (mIsPeriodic) { 1557 if (mMaxExecutionDelayMillis != 0L) { 1558 throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " + 1559 "periodic job."); 1560 } 1561 if (mMinLatencyMillis != 0L) { 1562 throw new IllegalArgumentException("Can't call setMinimumLatency() on a " + 1563 "periodic job"); 1564 } 1565 if (mTriggerContentUris != null) { 1566 throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " + 1567 "periodic job"); 1568 } 1569 } 1570 if (mIsPersisted) { 1571 if (mTriggerContentUris != null) { 1572 throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " + 1573 "persisted job"); 1574 } 1575 if (!mTransientExtras.isEmpty()) { 1576 throw new IllegalArgumentException("Can't call setTransientExtras() on a " + 1577 "persisted job"); 1578 } 1579 if (mClipData != null) { 1580 throw new IllegalArgumentException("Can't call setClipData() on a " + 1581 "persisted job"); 1582 } 1583 } 1584 if ((mFlags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0 && mHasEarlyConstraint) { 1585 throw new IllegalArgumentException("An important while foreground job cannot " 1586 + "have a time delay"); 1587 } 1588 if (mBackoffPolicySet && (mConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) { 1589 throw new IllegalArgumentException("An idle mode job will not respect any" + 1590 " back-off policy, so calling setBackoffCriteria with" + 1591 " setRequiresDeviceIdle is an error."); 1592 } 1593 return new JobInfo(this); 1594 } 1595 1596 /** 1597 * @hide 1598 */ summarize()1599 public String summarize() { 1600 final String service = (mJobService != null) 1601 ? mJobService.flattenToShortString() 1602 : "null"; 1603 return "JobInfo.Builder{job:" + mJobId + "/" + service + "}"; 1604 } 1605 } 1606 1607 /** 1608 * Convert a priority integer into a human readable string for debugging. 1609 * @hide 1610 */ getPriorityString(int priority)1611 public static String getPriorityString(int priority) { 1612 switch (priority) { 1613 case PRIORITY_DEFAULT: 1614 return PRIORITY_DEFAULT + " [DEFAULT]"; 1615 case PRIORITY_SYNC_EXPEDITED: 1616 return PRIORITY_SYNC_EXPEDITED + " [SYNC_EXPEDITED]"; 1617 case PRIORITY_SYNC_INITIALIZATION: 1618 return PRIORITY_SYNC_INITIALIZATION + " [SYNC_INITIALIZATION]"; 1619 case PRIORITY_BOUND_FOREGROUND_SERVICE: 1620 return PRIORITY_BOUND_FOREGROUND_SERVICE + " [BFGS_APP]"; 1621 case PRIORITY_FOREGROUND_SERVICE: 1622 return PRIORITY_FOREGROUND_SERVICE + " [FGS_APP]"; 1623 case PRIORITY_TOP_APP: 1624 return PRIORITY_TOP_APP + " [TOP_APP]"; 1625 1626 // PRIORITY_ADJ_* are adjustments and not used as real priorities. 1627 // No need to convert to strings. 1628 } 1629 return priority + " [UNKNOWN]"; 1630 } 1631 } 1632