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