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.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 kindofEqualsBundle(BaseBundle a, BaseBundle b)628 private static boolean kindofEqualsBundle(BaseBundle a, BaseBundle b) { 629 return (a == b) || (a != null && a.kindofEquals(b)); 630 } 631 632 @Override equals(Object o)633 public boolean equals(Object o) { 634 if (!(o instanceof JobInfo)) { 635 return false; 636 } 637 JobInfo j = (JobInfo) o; 638 if (jobId != j.jobId) { 639 return false; 640 } 641 // XXX won't be correct if one is parcelled and the other not. 642 if (!kindofEqualsBundle(extras, j.extras)) { 643 return false; 644 } 645 // XXX won't be correct if one is parcelled and the other not. 646 if (!kindofEqualsBundle(transientExtras, j.transientExtras)) { 647 return false; 648 } 649 // XXX for now we consider two different clip data objects to be different, 650 // regardless of whether their contents are the same. 651 if (clipData != j.clipData) { 652 return false; 653 } 654 if (clipGrantFlags != j.clipGrantFlags) { 655 return false; 656 } 657 if (!Objects.equals(service, j.service)) { 658 return false; 659 } 660 if (constraintFlags != j.constraintFlags) { 661 return false; 662 } 663 if (!Arrays.equals(triggerContentUris, j.triggerContentUris)) { 664 return false; 665 } 666 if (triggerContentUpdateDelay != j.triggerContentUpdateDelay) { 667 return false; 668 } 669 if (triggerContentMaxDelay != j.triggerContentMaxDelay) { 670 return false; 671 } 672 if (hasEarlyConstraint != j.hasEarlyConstraint) { 673 return false; 674 } 675 if (hasLateConstraint != j.hasLateConstraint) { 676 return false; 677 } 678 if (!Objects.equals(networkRequest, j.networkRequest)) { 679 return false; 680 } 681 if (networkDownloadBytes != j.networkDownloadBytes) { 682 return false; 683 } 684 if (networkUploadBytes != j.networkUploadBytes) { 685 return false; 686 } 687 if (minLatencyMillis != j.minLatencyMillis) { 688 return false; 689 } 690 if (maxExecutionDelayMillis != j.maxExecutionDelayMillis) { 691 return false; 692 } 693 if (isPeriodic != j.isPeriodic) { 694 return false; 695 } 696 if (isPersisted != j.isPersisted) { 697 return false; 698 } 699 if (intervalMillis != j.intervalMillis) { 700 return false; 701 } 702 if (flexMillis != j.flexMillis) { 703 return false; 704 } 705 if (initialBackoffMillis != j.initialBackoffMillis) { 706 return false; 707 } 708 if (backoffPolicy != j.backoffPolicy) { 709 return false; 710 } 711 if (priority != j.priority) { 712 return false; 713 } 714 if (flags != j.flags) { 715 return false; 716 } 717 return true; 718 } 719 720 @Override hashCode()721 public int hashCode() { 722 int hashCode = jobId; 723 if (extras != null) { 724 hashCode = 31 * hashCode + extras.hashCode(); 725 } 726 if (transientExtras != null) { 727 hashCode = 31 * hashCode + transientExtras.hashCode(); 728 } 729 if (clipData != null) { 730 hashCode = 31 * hashCode + clipData.hashCode(); 731 } 732 hashCode = 31*hashCode + clipGrantFlags; 733 if (service != null) { 734 hashCode = 31 * hashCode + service.hashCode(); 735 } 736 hashCode = 31 * hashCode + constraintFlags; 737 if (triggerContentUris != null) { 738 hashCode = 31 * hashCode + Arrays.hashCode(triggerContentUris); 739 } 740 hashCode = 31 * hashCode + Long.hashCode(triggerContentUpdateDelay); 741 hashCode = 31 * hashCode + Long.hashCode(triggerContentMaxDelay); 742 hashCode = 31 * hashCode + Boolean.hashCode(hasEarlyConstraint); 743 hashCode = 31 * hashCode + Boolean.hashCode(hasLateConstraint); 744 if (networkRequest != null) { 745 hashCode = 31 * hashCode + networkRequest.hashCode(); 746 } 747 hashCode = 31 * hashCode + Long.hashCode(networkDownloadBytes); 748 hashCode = 31 * hashCode + Long.hashCode(networkUploadBytes); 749 hashCode = 31 * hashCode + Long.hashCode(minLatencyMillis); 750 hashCode = 31 * hashCode + Long.hashCode(maxExecutionDelayMillis); 751 hashCode = 31 * hashCode + Boolean.hashCode(isPeriodic); 752 hashCode = 31 * hashCode + Boolean.hashCode(isPersisted); 753 hashCode = 31 * hashCode + Long.hashCode(intervalMillis); 754 hashCode = 31 * hashCode + Long.hashCode(flexMillis); 755 hashCode = 31 * hashCode + Long.hashCode(initialBackoffMillis); 756 hashCode = 31 * hashCode + backoffPolicy; 757 hashCode = 31 * hashCode + priority; 758 hashCode = 31 * hashCode + flags; 759 return hashCode; 760 } 761 JobInfo(Parcel in)762 private JobInfo(Parcel in) { 763 jobId = in.readInt(); 764 extras = in.readPersistableBundle(); 765 transientExtras = in.readBundle(); 766 if (in.readInt() != 0) { 767 clipData = ClipData.CREATOR.createFromParcel(in); 768 clipGrantFlags = in.readInt(); 769 } else { 770 clipData = null; 771 clipGrantFlags = 0; 772 } 773 service = in.readParcelable(null); 774 constraintFlags = in.readInt(); 775 triggerContentUris = in.createTypedArray(TriggerContentUri.CREATOR); 776 triggerContentUpdateDelay = in.readLong(); 777 triggerContentMaxDelay = in.readLong(); 778 if (in.readInt() != 0) { 779 networkRequest = NetworkRequest.CREATOR.createFromParcel(in); 780 } else { 781 networkRequest = null; 782 } 783 networkDownloadBytes = in.readLong(); 784 networkUploadBytes = in.readLong(); 785 minLatencyMillis = in.readLong(); 786 maxExecutionDelayMillis = in.readLong(); 787 isPeriodic = in.readInt() == 1; 788 isPersisted = in.readInt() == 1; 789 intervalMillis = in.readLong(); 790 flexMillis = in.readLong(); 791 initialBackoffMillis = in.readLong(); 792 backoffPolicy = in.readInt(); 793 hasEarlyConstraint = in.readInt() == 1; 794 hasLateConstraint = in.readInt() == 1; 795 priority = in.readInt(); 796 flags = in.readInt(); 797 } 798 JobInfo(JobInfo.Builder b)799 private JobInfo(JobInfo.Builder b) { 800 jobId = b.mJobId; 801 extras = b.mExtras.deepCopy(); 802 transientExtras = b.mTransientExtras.deepCopy(); 803 clipData = b.mClipData; 804 clipGrantFlags = b.mClipGrantFlags; 805 service = b.mJobService; 806 constraintFlags = b.mConstraintFlags; 807 triggerContentUris = b.mTriggerContentUris != null 808 ? b.mTriggerContentUris.toArray(new TriggerContentUri[b.mTriggerContentUris.size()]) 809 : null; 810 triggerContentUpdateDelay = b.mTriggerContentUpdateDelay; 811 triggerContentMaxDelay = b.mTriggerContentMaxDelay; 812 networkRequest = b.mNetworkRequest; 813 networkDownloadBytes = b.mNetworkDownloadBytes; 814 networkUploadBytes = b.mNetworkUploadBytes; 815 minLatencyMillis = b.mMinLatencyMillis; 816 maxExecutionDelayMillis = b.mMaxExecutionDelayMillis; 817 isPeriodic = b.mIsPeriodic; 818 isPersisted = b.mIsPersisted; 819 intervalMillis = b.mIntervalMillis; 820 flexMillis = b.mFlexMillis; 821 initialBackoffMillis = b.mInitialBackoffMillis; 822 backoffPolicy = b.mBackoffPolicy; 823 hasEarlyConstraint = b.mHasEarlyConstraint; 824 hasLateConstraint = b.mHasLateConstraint; 825 priority = b.mPriority; 826 flags = b.mFlags; 827 } 828 829 @Override describeContents()830 public int describeContents() { 831 return 0; 832 } 833 834 @Override writeToParcel(Parcel out, int flags)835 public void writeToParcel(Parcel out, int flags) { 836 out.writeInt(jobId); 837 out.writePersistableBundle(extras); 838 out.writeBundle(transientExtras); 839 if (clipData != null) { 840 out.writeInt(1); 841 clipData.writeToParcel(out, flags); 842 out.writeInt(clipGrantFlags); 843 } else { 844 out.writeInt(0); 845 } 846 out.writeParcelable(service, flags); 847 out.writeInt(constraintFlags); 848 out.writeTypedArray(triggerContentUris, flags); 849 out.writeLong(triggerContentUpdateDelay); 850 out.writeLong(triggerContentMaxDelay); 851 if (networkRequest != null) { 852 out.writeInt(1); 853 networkRequest.writeToParcel(out, flags); 854 } else { 855 out.writeInt(0); 856 } 857 out.writeLong(networkDownloadBytes); 858 out.writeLong(networkUploadBytes); 859 out.writeLong(minLatencyMillis); 860 out.writeLong(maxExecutionDelayMillis); 861 out.writeInt(isPeriodic ? 1 : 0); 862 out.writeInt(isPersisted ? 1 : 0); 863 out.writeLong(intervalMillis); 864 out.writeLong(flexMillis); 865 out.writeLong(initialBackoffMillis); 866 out.writeInt(backoffPolicy); 867 out.writeInt(hasEarlyConstraint ? 1 : 0); 868 out.writeInt(hasLateConstraint ? 1 : 0); 869 out.writeInt(priority); 870 out.writeInt(this.flags); 871 } 872 873 public static final @android.annotation.NonNull Creator<JobInfo> CREATOR = new Creator<JobInfo>() { 874 @Override 875 public JobInfo createFromParcel(Parcel in) { 876 return new JobInfo(in); 877 } 878 879 @Override 880 public JobInfo[] newArray(int size) { 881 return new JobInfo[size]; 882 } 883 }; 884 885 @Override toString()886 public String toString() { 887 return "(job:" + jobId + "/" + service.flattenToShortString() + ")"; 888 } 889 890 /** 891 * Information about a content URI modification that a job would like to 892 * trigger on. 893 */ 894 public static final class TriggerContentUri implements Parcelable { 895 private final Uri mUri; 896 private final int mFlags; 897 898 /** @hide */ 899 @Retention(RetentionPolicy.SOURCE) 900 @IntDef(flag = true, prefix = { "FLAG_" }, value = { 901 FLAG_NOTIFY_FOR_DESCENDANTS, 902 }) 903 public @interface Flags { } 904 905 /** 906 * Flag for trigger: also trigger if any descendants of the given URI change. 907 * Corresponds to the <var>notifyForDescendants</var> of 908 * {@link android.content.ContentResolver#registerContentObserver}. 909 */ 910 public static final int FLAG_NOTIFY_FOR_DESCENDANTS = 1<<0; 911 912 /** 913 * Create a new trigger description. 914 * @param uri The URI to observe. Must be non-null. 915 * @param flags Flags for the observer. 916 */ TriggerContentUri(@onNull Uri uri, @Flags int flags)917 public TriggerContentUri(@NonNull Uri uri, @Flags int flags) { 918 mUri = uri; 919 mFlags = flags; 920 } 921 922 /** 923 * Return the Uri this trigger was created for. 924 */ getUri()925 public Uri getUri() { 926 return mUri; 927 } 928 929 /** 930 * Return the flags supplied for the trigger. 931 */ getFlags()932 public @Flags int getFlags() { 933 return mFlags; 934 } 935 936 @Override equals(Object o)937 public boolean equals(Object o) { 938 if (!(o instanceof TriggerContentUri)) { 939 return false; 940 } 941 TriggerContentUri t = (TriggerContentUri) o; 942 return Objects.equals(t.mUri, mUri) && t.mFlags == mFlags; 943 } 944 945 @Override hashCode()946 public int hashCode() { 947 return (mUri == null ? 0 : mUri.hashCode()) ^ mFlags; 948 } 949 TriggerContentUri(Parcel in)950 private TriggerContentUri(Parcel in) { 951 mUri = Uri.CREATOR.createFromParcel(in); 952 mFlags = in.readInt(); 953 } 954 955 @Override describeContents()956 public int describeContents() { 957 return 0; 958 } 959 960 @Override writeToParcel(Parcel out, int flags)961 public void writeToParcel(Parcel out, int flags) { 962 mUri.writeToParcel(out, flags); 963 out.writeInt(mFlags); 964 } 965 966 public static final @android.annotation.NonNull Creator<TriggerContentUri> CREATOR = new Creator<TriggerContentUri>() { 967 @Override 968 public TriggerContentUri createFromParcel(Parcel in) { 969 return new TriggerContentUri(in); 970 } 971 972 @Override 973 public TriggerContentUri[] newArray(int size) { 974 return new TriggerContentUri[size]; 975 } 976 }; 977 } 978 979 /** Builder class for constructing {@link JobInfo} objects. */ 980 public static final class Builder { 981 private final int mJobId; 982 private final ComponentName mJobService; 983 private PersistableBundle mExtras = PersistableBundle.EMPTY; 984 private Bundle mTransientExtras = Bundle.EMPTY; 985 private ClipData mClipData; 986 private int mClipGrantFlags; 987 private int mPriority = PRIORITY_DEFAULT; 988 private int mFlags; 989 // Requirements. 990 private int mConstraintFlags; 991 private NetworkRequest mNetworkRequest; 992 private long mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN; 993 private long mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN; 994 private ArrayList<TriggerContentUri> mTriggerContentUris; 995 private long mTriggerContentUpdateDelay = -1; 996 private long mTriggerContentMaxDelay = -1; 997 private boolean mIsPersisted; 998 // One-off parameters. 999 private long mMinLatencyMillis; 1000 private long mMaxExecutionDelayMillis; 1001 // Periodic parameters. 1002 private boolean mIsPeriodic; 1003 private boolean mHasEarlyConstraint; 1004 private boolean mHasLateConstraint; 1005 private long mIntervalMillis; 1006 private long mFlexMillis; 1007 // Back-off parameters. 1008 private long mInitialBackoffMillis = DEFAULT_INITIAL_BACKOFF_MILLIS; 1009 private int mBackoffPolicy = DEFAULT_BACKOFF_POLICY; 1010 /** Easy way to track whether the client has tried to set a back-off policy. */ 1011 private boolean mBackoffPolicySet = false; 1012 1013 /** 1014 * Initialize a new Builder to construct a {@link JobInfo}. 1015 * 1016 * @param jobId Application-provided id for this job. Subsequent calls to cancel, or 1017 * jobs created with the same jobId, will update the pre-existing job with 1018 * the same id. This ID must be unique across all clients of the same uid 1019 * (not just the same package). You will want to make sure this is a stable 1020 * id across app updates, so probably not based on a resource ID. 1021 * @param jobService The endpoint that you implement that will receive the callback from the 1022 * JobScheduler. 1023 */ Builder(int jobId, @NonNull ComponentName jobService)1024 public Builder(int jobId, @NonNull ComponentName jobService) { 1025 mJobService = jobService; 1026 mJobId = jobId; 1027 } 1028 1029 /** @hide */ 1030 @UnsupportedAppUsage setPriority(int priority)1031 public Builder setPriority(int priority) { 1032 mPriority = priority; 1033 return this; 1034 } 1035 1036 /** @hide */ 1037 @UnsupportedAppUsage setFlags(int flags)1038 public Builder setFlags(int flags) { 1039 mFlags = flags; 1040 return this; 1041 } 1042 1043 /** 1044 * Set optional extras. This is persisted, so we only allow primitive types. 1045 * @param extras Bundle containing extras you want the scheduler to hold on to for you. 1046 * @see JobInfo#getExtras() 1047 */ setExtras(@onNull PersistableBundle extras)1048 public Builder setExtras(@NonNull PersistableBundle extras) { 1049 mExtras = extras; 1050 return this; 1051 } 1052 1053 /** 1054 * Set optional transient extras. 1055 * 1056 * <p>Because setting this property is not compatible with persisted 1057 * jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when 1058 * {@link android.app.job.JobInfo.Builder#build()} is called.</p> 1059 * 1060 * @param extras Bundle containing extras you want the scheduler to hold on to for you. 1061 * @see JobInfo#getTransientExtras() 1062 */ setTransientExtras(@onNull Bundle extras)1063 public Builder setTransientExtras(@NonNull Bundle extras) { 1064 mTransientExtras = extras; 1065 return this; 1066 } 1067 1068 /** 1069 * Set a {@link ClipData} associated with this Job. 1070 * 1071 * <p>The main purpose of providing a ClipData is to allow granting of 1072 * URI permissions for data associated with the clip. The exact kind 1073 * of permission grant to perform is specified through <var>grantFlags</var>. 1074 * 1075 * <p>If the ClipData contains items that are Intents, any 1076 * grant flags in those Intents will be ignored. Only flags provided as an argument 1077 * to this method are respected, and will be applied to all Uri or 1078 * Intent items in the clip (or sub-items of the clip). 1079 * 1080 * <p>Because setting this property is not compatible with persisted 1081 * jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when 1082 * {@link android.app.job.JobInfo.Builder#build()} is called.</p> 1083 * 1084 * @param clip The new clip to set. May be null to clear the current clip. 1085 * @param grantFlags The desired permissions to grant for any URIs. This should be 1086 * a combination of {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION}, 1087 * {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, and 1088 * {@link android.content.Intent#FLAG_GRANT_PREFIX_URI_PERMISSION}. 1089 * @see JobInfo#getClipData() 1090 * @see JobInfo#getClipGrantFlags() 1091 */ setClipData(@ullable ClipData clip, int grantFlags)1092 public Builder setClipData(@Nullable ClipData clip, int grantFlags) { 1093 mClipData = clip; 1094 mClipGrantFlags = grantFlags; 1095 return this; 1096 } 1097 1098 /** 1099 * Set basic description of the kind of network your job requires. If 1100 * you need more precise control over network capabilities, see 1101 * {@link #setRequiredNetwork(NetworkRequest)}. 1102 * <p> 1103 * If your job doesn't need a network connection, you don't need to call 1104 * this method, as the default value is {@link #NETWORK_TYPE_NONE}. 1105 * <p> 1106 * Calling this method defines network as a strict requirement for your 1107 * job. If the network requested is not available your job will never 1108 * run. See {@link #setOverrideDeadline(long)} to change this behavior. 1109 * Calling this method will override any requirements previously defined 1110 * by {@link #setRequiredNetwork(NetworkRequest)}; you typically only 1111 * want to call one of these methods. 1112 * <p class="note"> 1113 * When your job executes in 1114 * {@link JobService#onStartJob(JobParameters)}, be sure to use the 1115 * specific network returned by {@link JobParameters#getNetwork()}, 1116 * otherwise you'll use the default network which may not meet this 1117 * constraint. 1118 * 1119 * @see #setRequiredNetwork(NetworkRequest) 1120 * @see JobInfo#getNetworkType() 1121 * @see JobParameters#getNetwork() 1122 */ setRequiredNetworkType(@etworkType int networkType)1123 public Builder setRequiredNetworkType(@NetworkType int networkType) { 1124 if (networkType == NETWORK_TYPE_NONE) { 1125 return setRequiredNetwork(null); 1126 } else { 1127 final NetworkRequest.Builder builder = new NetworkRequest.Builder(); 1128 1129 // All types require validated Internet 1130 builder.addCapability(NET_CAPABILITY_INTERNET); 1131 builder.addCapability(NET_CAPABILITY_VALIDATED); 1132 builder.removeCapability(NET_CAPABILITY_NOT_VPN); 1133 1134 if (networkType == NETWORK_TYPE_ANY) { 1135 // No other capabilities 1136 } else if (networkType == NETWORK_TYPE_UNMETERED) { 1137 builder.addCapability(NET_CAPABILITY_NOT_METERED); 1138 } else if (networkType == NETWORK_TYPE_NOT_ROAMING) { 1139 builder.addCapability(NET_CAPABILITY_NOT_ROAMING); 1140 } else if (networkType == NETWORK_TYPE_CELLULAR) { 1141 builder.addTransportType(TRANSPORT_CELLULAR); 1142 } 1143 1144 return setRequiredNetwork(builder.build()); 1145 } 1146 } 1147 1148 /** 1149 * Set detailed description of the kind of network your job requires. 1150 * <p> 1151 * If your job doesn't need a network connection, you don't need to call 1152 * this method, as the default is {@code null}. 1153 * <p> 1154 * Calling this method defines network as a strict requirement for your 1155 * job. If the network requested is not available your job will never 1156 * run. See {@link #setOverrideDeadline(long)} to change this behavior. 1157 * Calling this method will override any requirements previously defined 1158 * by {@link #setRequiredNetworkType(int)}; you typically only want to 1159 * call one of these methods. 1160 * <p class="note"> 1161 * When your job executes in 1162 * {@link JobService#onStartJob(JobParameters)}, be sure to use the 1163 * specific network returned by {@link JobParameters#getNetwork()}, 1164 * otherwise you'll use the default network which may not meet this 1165 * constraint. 1166 * 1167 * @param networkRequest The detailed description of the kind of network 1168 * this job requires, or {@code null} if no specific kind of 1169 * network is required. Defining a {@link NetworkSpecifier} 1170 * is only supported for jobs that aren't persisted. 1171 * @see #setRequiredNetworkType(int) 1172 * @see JobInfo#getRequiredNetwork() 1173 * @see JobParameters#getNetwork() 1174 */ setRequiredNetwork(@ullable NetworkRequest networkRequest)1175 public Builder setRequiredNetwork(@Nullable NetworkRequest networkRequest) { 1176 mNetworkRequest = networkRequest; 1177 return this; 1178 } 1179 1180 /** 1181 * @deprecated replaced by 1182 * {@link #setEstimatedNetworkBytes(long, long)}. 1183 * @removed 1184 */ 1185 @Deprecated setEstimatedNetworkBytes(@ytesLong long networkBytes)1186 public Builder setEstimatedNetworkBytes(@BytesLong long networkBytes) { 1187 return setEstimatedNetworkBytes(networkBytes, NETWORK_BYTES_UNKNOWN); 1188 } 1189 1190 /** 1191 * Set the estimated size of network traffic that will be performed by 1192 * this job, in bytes. 1193 * <p> 1194 * Apps are encouraged to provide values that are as accurate as 1195 * possible, but when the exact size isn't available, an 1196 * order-of-magnitude estimate can be provided instead. Here are some 1197 * specific examples: 1198 * <ul> 1199 * <li>A job that is backing up a photo knows the exact size of that 1200 * photo, so it should provide that size as the estimate. 1201 * <li>A job that refreshes top news stories wouldn't know an exact 1202 * size, but if the size is expected to be consistently around 100KB, it 1203 * can provide that order-of-magnitude value as the estimate. 1204 * <li>A job that synchronizes email could end up using an extreme range 1205 * of data, from under 1KB when nothing has changed, to dozens of MB 1206 * when there are new emails with attachments. Jobs that cannot provide 1207 * reasonable estimates should use the sentinel value 1208 * {@link JobInfo#NETWORK_BYTES_UNKNOWN}. 1209 * </ul> 1210 * Note that the system may choose to delay jobs with large network 1211 * usage estimates when the device has a poor network connection, in 1212 * order to save battery. 1213 * <p> 1214 * The values provided here only reflect the traffic that will be 1215 * performed by the base job; if you're using {@link JobWorkItem} then 1216 * you also need to define the network traffic used by each work item 1217 * when constructing them. 1218 * 1219 * @param downloadBytes The estimated size of network traffic that will 1220 * be downloaded by this job, in bytes. 1221 * @param uploadBytes The estimated size of network traffic that will be 1222 * uploaded by this job, in bytes. 1223 * @see JobInfo#getEstimatedNetworkDownloadBytes() 1224 * @see JobInfo#getEstimatedNetworkUploadBytes() 1225 * @see JobWorkItem#JobWorkItem(android.content.Intent, long, long) 1226 */ setEstimatedNetworkBytes(@ytesLong long downloadBytes, @BytesLong long uploadBytes)1227 public Builder setEstimatedNetworkBytes(@BytesLong long downloadBytes, 1228 @BytesLong long uploadBytes) { 1229 mNetworkDownloadBytes = downloadBytes; 1230 mNetworkUploadBytes = uploadBytes; 1231 return this; 1232 } 1233 1234 /** 1235 * Specify that to run this job, the device must be charging (or be a 1236 * non-battery-powered device connected to permanent power, such as Android TV 1237 * devices). This defaults to {@code false}. 1238 * 1239 * <p class="note">For purposes of running jobs, a battery-powered device 1240 * "charging" is not quite the same as simply being connected to power. If the 1241 * device is so busy that the battery is draining despite a power connection, jobs 1242 * with this constraint will <em>not</em> run. This can happen during some 1243 * common use cases such as video chat, particularly if the device is plugged in 1244 * to USB rather than to wall power. 1245 * 1246 * @param requiresCharging Pass {@code true} to require that the device be 1247 * charging in order to run the job. 1248 * @see JobInfo#isRequireCharging() 1249 */ setRequiresCharging(boolean requiresCharging)1250 public Builder setRequiresCharging(boolean requiresCharging) { 1251 mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_CHARGING) 1252 | (requiresCharging ? CONSTRAINT_FLAG_CHARGING : 0); 1253 return this; 1254 } 1255 1256 /** 1257 * Specify that to run this job, the device's battery level must not be low. 1258 * This defaults to false. If true, the job will only run when the battery level 1259 * is not low, which is generally the point where the user is given a "low battery" 1260 * warning. 1261 * @param batteryNotLow Whether or not the device's battery level must not be low. 1262 * @see JobInfo#isRequireBatteryNotLow() 1263 */ setRequiresBatteryNotLow(boolean batteryNotLow)1264 public Builder setRequiresBatteryNotLow(boolean batteryNotLow) { 1265 mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_BATTERY_NOT_LOW) 1266 | (batteryNotLow ? CONSTRAINT_FLAG_BATTERY_NOT_LOW : 0); 1267 return this; 1268 } 1269 1270 /** 1271 * When set {@code true}, ensure that this job will not run if the device is in active use. 1272 * The default state is {@code false}: that is, the for the job to be runnable even when 1273 * someone is interacting with the device. 1274 * 1275 * <p>This state is a loose definition provided by the system. In general, it means that 1276 * the device is not currently being used interactively, and has not been in use for some 1277 * time. As such, it is a good time to perform resource heavy jobs. Bear in mind that 1278 * battery usage will still be attributed to your application, and surfaced to the user in 1279 * battery stats.</p> 1280 * 1281 * <p class="note">Despite the similar naming, this job constraint is <em>not</em> 1282 * related to the system's "device idle" or "doze" states. This constraint only 1283 * determines whether a job is allowed to run while the device is directly in use. 1284 * 1285 * @param requiresDeviceIdle Pass {@code true} to prevent the job from running 1286 * while the device is being used interactively. 1287 * @see JobInfo#isRequireDeviceIdle() 1288 */ setRequiresDeviceIdle(boolean requiresDeviceIdle)1289 public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) { 1290 mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_DEVICE_IDLE) 1291 | (requiresDeviceIdle ? CONSTRAINT_FLAG_DEVICE_IDLE : 0); 1292 return this; 1293 } 1294 1295 /** 1296 * Specify that to run this job, the device's available storage must not be low. 1297 * This defaults to false. If true, the job will only run when the device is not 1298 * in a low storage state, which is generally the point where the user is given a 1299 * "low storage" warning. 1300 * @param storageNotLow Whether or not the device's available storage must not be low. 1301 * @see JobInfo#isRequireStorageNotLow() 1302 */ setRequiresStorageNotLow(boolean storageNotLow)1303 public Builder setRequiresStorageNotLow(boolean storageNotLow) { 1304 mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_STORAGE_NOT_LOW) 1305 | (storageNotLow ? CONSTRAINT_FLAG_STORAGE_NOT_LOW : 0); 1306 return this; 1307 } 1308 1309 /** 1310 * Add a new content: URI that will be monitored with a 1311 * {@link android.database.ContentObserver}, and will cause the job to execute if changed. 1312 * If you have any trigger content URIs associated with a job, it will not execute until 1313 * there has been a change report for one or more of them. 1314 * 1315 * <p>Note that trigger URIs can not be used in combination with 1316 * {@link #setPeriodic(long)} or {@link #setPersisted(boolean)}. To continually monitor 1317 * for content changes, you need to schedule a new JobInfo observing the same URIs 1318 * before you finish execution of the JobService handling the most recent changes. 1319 * Following this pattern will ensure you do not lost any content changes: while your 1320 * job is running, the system will continue monitoring for content changes, and propagate 1321 * any it sees over to the next job you schedule.</p> 1322 * 1323 * <p>Because setting this property is not compatible with periodic or 1324 * persisted jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when 1325 * {@link android.app.job.JobInfo.Builder#build()} is called.</p> 1326 * 1327 * <p>The following example shows how this feature can be used to monitor for changes 1328 * in the photos on a device.</p> 1329 * 1330 * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/PhotosContentJob.java 1331 * job} 1332 * 1333 * @param uri The content: URI to monitor. 1334 * @see JobInfo#getTriggerContentUris() 1335 */ addTriggerContentUri(@onNull TriggerContentUri uri)1336 public Builder addTriggerContentUri(@NonNull TriggerContentUri uri) { 1337 if (mTriggerContentUris == null) { 1338 mTriggerContentUris = new ArrayList<>(); 1339 } 1340 mTriggerContentUris.add(uri); 1341 return this; 1342 } 1343 1344 /** 1345 * Set the delay (in milliseconds) from when a content change is detected until 1346 * the job is scheduled. If there are more changes during that time, the delay 1347 * will be reset to start at the time of the most recent change. 1348 * @param durationMs Delay after most recent content change, in milliseconds. 1349 * @see JobInfo#getTriggerContentUpdateDelay() 1350 */ setTriggerContentUpdateDelay(long durationMs)1351 public Builder setTriggerContentUpdateDelay(long durationMs) { 1352 mTriggerContentUpdateDelay = durationMs; 1353 return this; 1354 } 1355 1356 /** 1357 * Set the maximum total delay (in milliseconds) that is allowed from the first 1358 * time a content change is detected until the job is scheduled. 1359 * @param durationMs Delay after initial content change, in milliseconds. 1360 * @see JobInfo#getTriggerContentMaxDelay() 1361 */ setTriggerContentMaxDelay(long durationMs)1362 public Builder setTriggerContentMaxDelay(long durationMs) { 1363 mTriggerContentMaxDelay = durationMs; 1364 return this; 1365 } 1366 1367 /** 1368 * Specify that this job should recur with the provided interval, not more than once per 1369 * period. You have no control over when within this interval this job will be executed, 1370 * only the guarantee that it will be executed at most once within this interval. 1371 * Setting this function on the builder with {@link #setMinimumLatency(long)} or 1372 * {@link #setOverrideDeadline(long)} will result in an error. 1373 * @param intervalMillis Millisecond interval for which this job will repeat. 1374 * @see JobInfo#getIntervalMillis() 1375 * @see JobInfo#getFlexMillis() 1376 */ setPeriodic(long intervalMillis)1377 public Builder setPeriodic(long intervalMillis) { 1378 return setPeriodic(intervalMillis, intervalMillis); 1379 } 1380 1381 /** 1382 * Specify that this job should recur with the provided interval and flex. The job can 1383 * execute at any time in a window of flex length at the end of the period. 1384 * @param intervalMillis Millisecond interval for which this job will repeat. A minimum 1385 * value of {@link #getMinPeriodMillis()} is enforced. 1386 * @param flexMillis Millisecond flex for this job. Flex is clamped to be at least 1387 * {@link #getMinFlexMillis()} or 5 percent of the period, whichever is 1388 * higher. 1389 * @see JobInfo#getIntervalMillis() 1390 * @see JobInfo#getFlexMillis() 1391 */ setPeriodic(long intervalMillis, long flexMillis)1392 public Builder setPeriodic(long intervalMillis, long flexMillis) { 1393 final long minPeriod = getMinPeriodMillis(); 1394 if (intervalMillis < minPeriod) { 1395 Log.w(TAG, "Requested interval " + formatDuration(intervalMillis) + " for job " 1396 + mJobId + " is too small; raising to " + formatDuration(minPeriod)); 1397 intervalMillis = minPeriod; 1398 } 1399 1400 final long percentClamp = 5 * intervalMillis / 100; 1401 final long minFlex = Math.max(percentClamp, getMinFlexMillis()); 1402 if (flexMillis < minFlex) { 1403 Log.w(TAG, "Requested flex " + formatDuration(flexMillis) + " for job " + mJobId 1404 + " is too small; raising to " + formatDuration(minFlex)); 1405 flexMillis = minFlex; 1406 } 1407 1408 mIsPeriodic = true; 1409 mIntervalMillis = intervalMillis; 1410 mFlexMillis = flexMillis; 1411 mHasEarlyConstraint = mHasLateConstraint = true; 1412 return this; 1413 } 1414 1415 /** 1416 * Specify that this job should be delayed by the provided amount of time. 1417 * Because it doesn't make sense setting this property on a periodic job, doing so will 1418 * throw an {@link java.lang.IllegalArgumentException} when 1419 * {@link android.app.job.JobInfo.Builder#build()} is called. 1420 * @param minLatencyMillis Milliseconds before which this job will not be considered for 1421 * execution. 1422 * @see JobInfo#getMinLatencyMillis() 1423 */ setMinimumLatency(long minLatencyMillis)1424 public Builder setMinimumLatency(long minLatencyMillis) { 1425 mMinLatencyMillis = minLatencyMillis; 1426 mHasEarlyConstraint = true; 1427 return this; 1428 } 1429 1430 /** 1431 * Set deadline which is the maximum scheduling latency. The job will be run by this 1432 * deadline even if other requirements are not met. Because it doesn't make sense setting 1433 * this property on a periodic job, doing so will throw an 1434 * {@link java.lang.IllegalArgumentException} when 1435 * {@link android.app.job.JobInfo.Builder#build()} is called. 1436 * @see JobInfo#getMaxExecutionDelayMillis() 1437 */ setOverrideDeadline(long maxExecutionDelayMillis)1438 public Builder setOverrideDeadline(long maxExecutionDelayMillis) { 1439 mMaxExecutionDelayMillis = maxExecutionDelayMillis; 1440 mHasLateConstraint = true; 1441 return this; 1442 } 1443 1444 /** 1445 * Set up the back-off/retry policy. 1446 * This defaults to some respectable values: {30 seconds, Exponential}. We cap back-off at 1447 * 5hrs. 1448 * Note that trying to set a backoff criteria for a job with 1449 * {@link #setRequiresDeviceIdle(boolean)} will throw an exception when you call build(). 1450 * This is because back-off typically does not make sense for these types of jobs. See 1451 * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)} 1452 * for more description of the return value for the case of a job executing while in idle 1453 * mode. 1454 * @param initialBackoffMillis Millisecond time interval to wait initially when job has 1455 * failed. 1456 * @see JobInfo#getInitialBackoffMillis() 1457 * @see JobInfo#getBackoffPolicy() 1458 */ setBackoffCriteria(long initialBackoffMillis, @BackoffPolicy int backoffPolicy)1459 public Builder setBackoffCriteria(long initialBackoffMillis, 1460 @BackoffPolicy int backoffPolicy) { 1461 final long minBackoff = getMinBackoffMillis(); 1462 if (initialBackoffMillis < minBackoff) { 1463 Log.w(TAG, "Requested backoff " + formatDuration(initialBackoffMillis) + " for job " 1464 + mJobId + " is too small; raising to " + formatDuration(minBackoff)); 1465 initialBackoffMillis = minBackoff; 1466 } 1467 1468 mBackoffPolicySet = true; 1469 mInitialBackoffMillis = initialBackoffMillis; 1470 mBackoffPolicy = backoffPolicy; 1471 return this; 1472 } 1473 1474 /** 1475 * Setting this to true indicates that this job is important while the scheduling app 1476 * is in the foreground or on the temporary whitelist for background restrictions. 1477 * This means that the system will relax doze restrictions on this job during this time. 1478 * 1479 * Apps should use this flag only for short jobs that are essential for the app to function 1480 * properly in the foreground. 1481 * 1482 * Note that once the scheduling app is no longer whitelisted from background restrictions 1483 * and in the background, or the job failed due to unsatisfied constraints, 1484 * this job should be expected to behave like other jobs without this flag. 1485 * 1486 * @param importantWhileForeground whether to relax doze restrictions for this job when the 1487 * app is in the foreground. False by default. 1488 * @see JobInfo#isImportantWhileForeground() 1489 */ setImportantWhileForeground(boolean importantWhileForeground)1490 public Builder setImportantWhileForeground(boolean importantWhileForeground) { 1491 if (importantWhileForeground) { 1492 mFlags |= FLAG_IMPORTANT_WHILE_FOREGROUND; 1493 } else { 1494 mFlags &= (~FLAG_IMPORTANT_WHILE_FOREGROUND); 1495 } 1496 return this; 1497 } 1498 1499 /** 1500 * @removed 1501 * @deprecated replaced with {@link #setPrefetch(boolean)} 1502 */ 1503 @Deprecated setIsPrefetch(boolean isPrefetch)1504 public Builder setIsPrefetch(boolean isPrefetch) { 1505 return setPrefetch(isPrefetch); 1506 } 1507 1508 /** 1509 * Setting this to true indicates that this job is designed to prefetch 1510 * content that will make a material improvement to the experience of 1511 * the specific user of this device. For example, fetching top headlines 1512 * of interest to the current user. 1513 * <p> 1514 * The system may use this signal to relax the network constraints you 1515 * originally requested, such as allowing a 1516 * {@link JobInfo#NETWORK_TYPE_UNMETERED} job to run over a metered 1517 * network when there is a surplus of metered data available. The system 1518 * may also use this signal in combination with end user usage patterns 1519 * to ensure data is prefetched before the user launches your app. 1520 * @see JobInfo#isPrefetch() 1521 */ setPrefetch(boolean prefetch)1522 public Builder setPrefetch(boolean prefetch) { 1523 if (prefetch) { 1524 mFlags |= FLAG_PREFETCH; 1525 } else { 1526 mFlags &= (~FLAG_PREFETCH); 1527 } 1528 return this; 1529 } 1530 1531 /** 1532 * Set whether or not to persist this job across device reboots. 1533 * 1534 * @param isPersisted True to indicate that the job will be written to 1535 * disk and loaded at boot. 1536 * @see JobInfo#isPersisted() 1537 */ 1538 @RequiresPermission(android.Manifest.permission.RECEIVE_BOOT_COMPLETED) setPersisted(boolean isPersisted)1539 public Builder setPersisted(boolean isPersisted) { 1540 mIsPersisted = isPersisted; 1541 return this; 1542 } 1543 1544 /** 1545 * @return The job object to hand to the JobScheduler. This object is immutable. 1546 */ build()1547 public JobInfo build() { 1548 // Check that network estimates require network type 1549 if ((mNetworkDownloadBytes > 0 || mNetworkUploadBytes > 0) && mNetworkRequest == null) { 1550 throw new IllegalArgumentException( 1551 "Can't provide estimated network usage without requiring a network"); 1552 } 1553 // We can't serialize network specifiers 1554 if (mIsPersisted && mNetworkRequest != null 1555 && mNetworkRequest.networkCapabilities.getNetworkSpecifier() != null) { 1556 throw new IllegalArgumentException( 1557 "Network specifiers aren't supported for persistent jobs"); 1558 } 1559 // Check that a deadline was not set on a periodic job. 1560 if (mIsPeriodic) { 1561 if (mMaxExecutionDelayMillis != 0L) { 1562 throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " + 1563 "periodic job."); 1564 } 1565 if (mMinLatencyMillis != 0L) { 1566 throw new IllegalArgumentException("Can't call setMinimumLatency() on a " + 1567 "periodic job"); 1568 } 1569 if (mTriggerContentUris != null) { 1570 throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " + 1571 "periodic job"); 1572 } 1573 } 1574 if (mIsPersisted) { 1575 if (mTriggerContentUris != null) { 1576 throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " + 1577 "persisted job"); 1578 } 1579 if (!mTransientExtras.isEmpty()) { 1580 throw new IllegalArgumentException("Can't call setTransientExtras() on a " + 1581 "persisted job"); 1582 } 1583 if (mClipData != null) { 1584 throw new IllegalArgumentException("Can't call setClipData() on a " + 1585 "persisted job"); 1586 } 1587 } 1588 if ((mFlags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0 && mHasEarlyConstraint) { 1589 throw new IllegalArgumentException("An important while foreground job cannot " 1590 + "have a time delay"); 1591 } 1592 if (mBackoffPolicySet && (mConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) { 1593 throw new IllegalArgumentException("An idle mode job will not respect any" + 1594 " back-off policy, so calling setBackoffCriteria with" + 1595 " setRequiresDeviceIdle is an error."); 1596 } 1597 return new JobInfo(this); 1598 } 1599 1600 /** 1601 * @hide 1602 */ summarize()1603 public String summarize() { 1604 final String service = (mJobService != null) 1605 ? mJobService.flattenToShortString() 1606 : "null"; 1607 return "JobInfo.Builder{job:" + mJobId + "/" + service + "}"; 1608 } 1609 } 1610 1611 /** 1612 * Convert a priority integer into a human readable string for debugging. 1613 * @hide 1614 */ getPriorityString(int priority)1615 public static String getPriorityString(int priority) { 1616 switch (priority) { 1617 case PRIORITY_DEFAULT: 1618 return PRIORITY_DEFAULT + " [DEFAULT]"; 1619 case PRIORITY_SYNC_EXPEDITED: 1620 return PRIORITY_SYNC_EXPEDITED + " [SYNC_EXPEDITED]"; 1621 case PRIORITY_SYNC_INITIALIZATION: 1622 return PRIORITY_SYNC_INITIALIZATION + " [SYNC_INITIALIZATION]"; 1623 case PRIORITY_BOUND_FOREGROUND_SERVICE: 1624 return PRIORITY_BOUND_FOREGROUND_SERVICE + " [BFGS_APP]"; 1625 case PRIORITY_FOREGROUND_SERVICE: 1626 return PRIORITY_FOREGROUND_SERVICE + " [FGS_APP]"; 1627 case PRIORITY_TOP_APP: 1628 return PRIORITY_TOP_APP + " [TOP_APP]"; 1629 1630 // PRIORITY_ADJ_* are adjustments and not used as real priorities. 1631 // No need to convert to strings. 1632 } 1633 return priority + " [UNKNOWN]"; 1634 } 1635 } 1636