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