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_RESTRICTED; 22 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; 23 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; 24 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; 25 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; 26 import static android.util.TimeUtils.formatDuration; 27 28 import android.annotation.BytesLong; 29 import android.annotation.IntDef; 30 import android.annotation.NonNull; 31 import android.annotation.Nullable; 32 import android.annotation.RequiresPermission; 33 import android.app.Notification; 34 import android.compat.Compatibility; 35 import android.compat.annotation.ChangeId; 36 import android.compat.annotation.EnabledAfter; 37 import android.compat.annotation.EnabledSince; 38 import android.compat.annotation.UnsupportedAppUsage; 39 import android.content.ClipData; 40 import android.content.ComponentName; 41 import android.net.NetworkRequest; 42 import android.net.NetworkSpecifier; 43 import android.net.Uri; 44 import android.os.BaseBundle; 45 import android.os.Build; 46 import android.os.Bundle; 47 import android.os.Parcel; 48 import android.os.Parcelable; 49 import android.os.PersistableBundle; 50 import android.util.Log; 51 52 import java.lang.annotation.Retention; 53 import java.lang.annotation.RetentionPolicy; 54 import java.util.ArrayList; 55 import java.util.Arrays; 56 import java.util.Objects; 57 58 /** 59 * Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the 60 * parameters required to schedule work against the calling application. These are constructed 61 * using the {@link JobInfo.Builder}. 62 * The goal here is to provide the scheduler with high-level semantics about the work you want to 63 * accomplish. 64 * <p> Prior to Android version {@link Build.VERSION_CODES#Q}, you had to specify at least one 65 * constraint on the JobInfo object that you are creating. Otherwise, the builder would throw an 66 * exception when building. From Android version {@link Build.VERSION_CODES#Q} and onwards, it is 67 * valid to schedule jobs with no constraints. 68 */ 69 public class JobInfo implements Parcelable { 70 private static String TAG = "JobInfo"; 71 72 /** 73 * Disallow setting a deadline (via {@link Builder#setOverrideDeadline(long)}) for prefetch 74 * jobs ({@link Builder#setPrefetch(boolean)}. Prefetch jobs are meant to run close to the next 75 * app launch, so there's no good reason to allow them to have deadlines. 76 * 77 * We don't drop or cancel any previously scheduled prefetch jobs with a deadline. 78 * There's no way for an app to keep a perpetually scheduled prefetch job with a deadline. 79 * Prefetch jobs with a deadline will run and apps under this restriction won't be able to 80 * schedule new prefetch jobs with a deadline. If a job is rescheduled (by providing 81 * {@code true} via {@link JobService#jobFinished(JobParameters, boolean)} or 82 * {@link JobService#onStopJob(JobParameters)}'s return value),the deadline is dropped. 83 * Periodic jobs require all constraints to be met, so there's no issue with their deadlines. 84 * 85 * @hide 86 */ 87 @ChangeId 88 @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) 89 public static final long DISALLOW_DEADLINES_FOR_PREFETCH_JOBS = 194532703L; 90 91 /** 92 * Whether to throw an exception when an app provides an invalid priority value via 93 * {@link Builder#setPriority(int)}. Legacy apps may be incorrectly using the API and 94 * so the call will silently fail for them if they continue using the API. 95 * 96 * @hide 97 */ 98 @ChangeId 99 @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) 100 public static final long THROW_ON_INVALID_PRIORITY_VALUE = 140852299L; 101 102 /** 103 * Require that estimated network bytes are nonnegative. 104 * 105 * @hide 106 */ 107 @ChangeId 108 @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) 109 public static final long REJECT_NEGATIVE_NETWORK_ESTIMATES = 253665015L; 110 111 /** @hide */ 112 @IntDef(prefix = { "NETWORK_TYPE_" }, value = { 113 NETWORK_TYPE_NONE, 114 NETWORK_TYPE_ANY, 115 NETWORK_TYPE_UNMETERED, 116 NETWORK_TYPE_NOT_ROAMING, 117 NETWORK_TYPE_CELLULAR, 118 }) 119 @Retention(RetentionPolicy.SOURCE) 120 public @interface NetworkType {} 121 122 /** Default. */ 123 public static final int NETWORK_TYPE_NONE = 0; 124 /** This job requires network connectivity. */ 125 public static final int NETWORK_TYPE_ANY = 1; 126 /** This job requires network connectivity that is unmetered. */ 127 public static final int NETWORK_TYPE_UNMETERED = 2; 128 /** This job requires network connectivity that is not roaming. */ 129 public static final int NETWORK_TYPE_NOT_ROAMING = 3; 130 /** This job requires network connectivity that is a cellular network. */ 131 public static final int NETWORK_TYPE_CELLULAR = 4; 132 133 /** 134 * This job requires metered connectivity such as most cellular data 135 * networks. 136 * 137 * @deprecated Cellular networks may be unmetered, or Wi-Fi networks may be 138 * metered, so this isn't a good way of selecting a specific 139 * transport. Instead, use {@link #NETWORK_TYPE_CELLULAR} or 140 * {@link android.net.NetworkRequest.Builder#addTransportType(int)} 141 * if your job requires a specific network transport. 142 */ 143 @Deprecated 144 public static final int NETWORK_TYPE_METERED = NETWORK_TYPE_CELLULAR; 145 146 /** Sentinel value indicating that bytes are unknown. */ 147 public static final int NETWORK_BYTES_UNKNOWN = -1; 148 149 /** 150 * Amount of backoff a job has initially by default, in milliseconds. 151 */ 152 public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L; // 30 seconds. 153 154 /** 155 * Maximum backoff we allow for a job, in milliseconds. 156 */ 157 public static final long MAX_BACKOFF_DELAY_MILLIS = 5 * 60 * 60 * 1000; // 5 hours. 158 159 /** @hide */ 160 @IntDef(prefix = { "BACKOFF_POLICY_" }, value = { 161 BACKOFF_POLICY_LINEAR, 162 BACKOFF_POLICY_EXPONENTIAL, 163 }) 164 @Retention(RetentionPolicy.SOURCE) 165 public @interface BackoffPolicy {} 166 167 /** 168 * Linearly back-off a failed job. See 169 * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)} 170 * retry_time(current_time, num_failures) = 171 * current_time + initial_backoff_millis * num_failures, num_failures >= 1 172 */ 173 public static final int BACKOFF_POLICY_LINEAR = 0; 174 175 /** 176 * Exponentially back-off a failed job. See 177 * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)} 178 * 179 * retry_time(current_time, num_failures) = 180 * current_time + initial_backoff_millis * 2 ^ (num_failures - 1), num_failures >= 1 181 */ 182 public static final int BACKOFF_POLICY_EXPONENTIAL = 1; 183 184 /* Minimum interval for a periodic job, in milliseconds. */ 185 private static final long MIN_PERIOD_MILLIS = 15 * 60 * 1000L; // 15 minutes 186 187 /* Minimum flex for a periodic job, in milliseconds. */ 188 private static final long MIN_FLEX_MILLIS = 5 * 60 * 1000L; // 5 minutes 189 190 /** 191 * Minimum backoff interval for a job, in milliseconds 192 * @hide 193 */ 194 public static final long MIN_BACKOFF_MILLIS = 10 * 1000L; // 10 seconds 195 196 /** 197 * Query the minimum interval allowed for periodic scheduled jobs. Attempting 198 * to declare a smaller period than this when scheduling a job will result in a 199 * job that is still periodic, but will run with this effective period. 200 * 201 * @return The minimum available interval for scheduling periodic jobs, in milliseconds. 202 */ getMinPeriodMillis()203 public static final long getMinPeriodMillis() { 204 return MIN_PERIOD_MILLIS; 205 } 206 207 /** 208 * Query the minimum flex time allowed for periodic scheduled jobs. Attempting 209 * to declare a shorter flex time than this when scheduling such a job will 210 * result in this amount as the effective flex time for the job. 211 * 212 * @return The minimum available flex time for scheduling periodic jobs, in milliseconds. 213 */ getMinFlexMillis()214 public static final long getMinFlexMillis() { 215 return MIN_FLEX_MILLIS; 216 } 217 218 /** 219 * Query the minimum automatic-reschedule backoff interval permitted for jobs. 220 * @hide 221 */ getMinBackoffMillis()222 public static final long getMinBackoffMillis() { 223 return MIN_BACKOFF_MILLIS; 224 } 225 226 /** 227 * Default type of backoff. 228 * @hide 229 */ 230 public static final int DEFAULT_BACKOFF_POLICY = BACKOFF_POLICY_EXPONENTIAL; 231 232 /** 233 * Job has minimal value to the user. The user has absolutely no expectation 234 * or knowledge of this task and it has no bearing on the user's perception of 235 * the app whatsoever. JobScheduler <i>may</i> decide to defer these tasks while 236 * there are higher priority tasks in order to ensure there is sufficient quota 237 * available for the higher priority tasks. 238 * A sample task of min priority: uploading analytics 239 */ 240 public static final int PRIORITY_MIN = 100; 241 242 /** 243 * Low priority. The task provides some benefit to users, but is not critical 244 * and is more of a nice-to-have. This is more important than minimum priority 245 * jobs and will be prioritized ahead of them, but may still be deferred in lieu 246 * of higher priority jobs. JobScheduler <i>may</i> decide to defer these tasks 247 * while there are higher priority tasks in order to ensure there is sufficient 248 * quota available for the higher priority tasks. 249 * A sample task of low priority: prefetching data the user hasn't requested 250 */ 251 public static final int PRIORITY_LOW = 200; 252 253 /** 254 * Default value for all regular jobs. As noted in {@link JobScheduler}, 255 * these jobs have a general execution time of 10 minutes. 256 * Receives the standard job management policy. 257 */ 258 public static final int PRIORITY_DEFAULT = 300; 259 260 /** 261 * This task should be ordered ahead of most other tasks. It may be 262 * deferred a little, but if it doesn't run at some point, the user may think 263 * something is wrong. Assuming all constraints remain satisfied 264 * (including ideal system load conditions), these jobs can have an 265 * execution time of at least 4 minutes. Setting all of your jobs to high 266 * priority will not be beneficial to your app and in fact may hurt its 267 * performance in the long run. 268 */ 269 public static final int PRIORITY_HIGH = 400; 270 271 /** 272 * This task should be run ahead of all other tasks. Only Expedited Jobs 273 * {@link Builder#setExpedited(boolean)} can have this priority and as such, 274 * are subject to the same execution time details noted in 275 * {@link Builder#setExpedited(boolean)}. 276 * A sample task of max priority: receiving a text message and processing it to 277 * show a notification 278 */ 279 public static final int PRIORITY_MAX = 500; 280 281 /** @hide */ 282 @IntDef(prefix = {"PRIORITY_"}, value = { 283 PRIORITY_MIN, 284 PRIORITY_LOW, 285 PRIORITY_DEFAULT, 286 PRIORITY_HIGH, 287 PRIORITY_MAX, 288 }) 289 @Retention(RetentionPolicy.SOURCE) 290 public @interface Priority { 291 } 292 293 /** 294 * Default of {@link #getBias}. 295 * @hide 296 */ 297 public static final int BIAS_DEFAULT = 0; 298 299 /** 300 * Value of {@link #getBias} for expedited syncs. 301 * @hide 302 */ 303 public static final int BIAS_SYNC_EXPEDITED = 10; 304 305 /** 306 * Value of {@link #getBias} for first time initialization syncs. 307 * @hide 308 */ 309 public static final int BIAS_SYNC_INITIALIZATION = 20; 310 311 /** 312 * Value of {@link #getBias} for a BFGS app (overrides the supplied 313 * JobInfo bias if it is smaller). 314 * @hide 315 */ 316 public static final int BIAS_BOUND_FOREGROUND_SERVICE = 30; 317 318 /** @hide For backward compatibility. */ 319 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 320 public static final int PRIORITY_FOREGROUND_APP = BIAS_BOUND_FOREGROUND_SERVICE; 321 322 /** 323 * Value of {@link #getBias} for a FG service app (overrides the supplied 324 * JobInfo bias if it is smaller). 325 * @hide 326 */ 327 public static final int BIAS_FOREGROUND_SERVICE = 35; 328 329 /** @hide For backward compatibility. */ 330 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 331 public static final int PRIORITY_FOREGROUND_SERVICE = BIAS_FOREGROUND_SERVICE; 332 333 /** 334 * Value of {@link #getBias} for the current top app (overrides the supplied 335 * JobInfo bias if it is smaller). 336 * @hide 337 */ 338 public static final int BIAS_TOP_APP = 40; 339 340 /** 341 * Adjustment of {@link #getBias} if the app has often (50% or more of the time) 342 * been running jobs. 343 * @hide 344 */ 345 public static final int BIAS_ADJ_OFTEN_RUNNING = -40; 346 347 /** 348 * Adjustment of {@link #getBias} if the app has always (90% or more of the time) 349 * been running jobs. 350 * @hide 351 */ 352 public static final int BIAS_ADJ_ALWAYS_RUNNING = -80; 353 354 /** 355 * Indicates that the implementation of this job will be using 356 * {@link JobService#startForeground(int, android.app.Notification)} to run 357 * in the foreground. 358 * <p> 359 * When set, the internal scheduling of this job will ignore any background 360 * network restrictions for the requesting app. Note that this flag alone 361 * doesn't actually place your {@link JobService} in the foreground; you 362 * still need to post the notification yourself. 363 * <p> 364 * To use this flag, the caller must hold the 365 * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL} permission. 366 * 367 * @hide 368 */ 369 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 370 public static final int FLAG_WILL_BE_FOREGROUND = 1 << 0; 371 372 /** 373 * Allows this job to run despite doze restrictions as long as the app is in the foreground 374 * or on the temporary whitelist 375 * @hide 376 */ 377 public static final int FLAG_IMPORTANT_WHILE_FOREGROUND = 1 << 1; 378 379 /** 380 * @hide 381 */ 382 public static final int FLAG_PREFETCH = 1 << 2; 383 384 /** 385 * This job needs to be exempted from the app standby throttling. Only the system (UID 1000) 386 * can set it. Jobs with a time constraint must not have it. 387 * 388 * @hide 389 */ 390 public static final int FLAG_EXEMPT_FROM_APP_STANDBY = 1 << 3; 391 392 /** 393 * Whether it's an expedited job or not. 394 * 395 * @hide 396 */ 397 public static final int FLAG_EXPEDITED = 1 << 4; 398 399 /** 400 * Whether it's a user initiated job or not. 401 * 402 * @hide 403 */ 404 public static final int FLAG_USER_INITIATED = 1 << 5; 405 406 /** 407 * @hide 408 */ 409 public static final int CONSTRAINT_FLAG_CHARGING = 1 << 0; 410 411 /** 412 * @hide 413 */ 414 public static final int CONSTRAINT_FLAG_BATTERY_NOT_LOW = 1 << 1; 415 416 /** 417 * @hide 418 */ 419 public static final int CONSTRAINT_FLAG_DEVICE_IDLE = 1 << 2; 420 421 /** 422 * @hide 423 */ 424 public static final int CONSTRAINT_FLAG_STORAGE_NOT_LOW = 1 << 3; 425 426 @UnsupportedAppUsage 427 private final int jobId; 428 private final PersistableBundle extras; 429 private final Bundle transientExtras; 430 private final ClipData clipData; 431 private final int clipGrantFlags; 432 @UnsupportedAppUsage 433 private final ComponentName service; 434 private final int constraintFlags; 435 private final int mPreferredConstraintFlags; 436 private final TriggerContentUri[] triggerContentUris; 437 private final long triggerContentUpdateDelay; 438 private final long triggerContentMaxDelay; 439 private final boolean hasEarlyConstraint; 440 private final boolean hasLateConstraint; 441 private final NetworkRequest networkRequest; 442 private final long networkDownloadBytes; 443 private final long networkUploadBytes; 444 private final long minimumNetworkChunkBytes; 445 private final long minLatencyMillis; 446 private final long maxExecutionDelayMillis; 447 private final boolean isPeriodic; 448 private final boolean isPersisted; 449 private final long intervalMillis; 450 private final long flexMillis; 451 private final long initialBackoffMillis; 452 private final int backoffPolicy; 453 private final int mBias; 454 @Priority 455 private final int mPriority; 456 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 457 private final int flags; 458 459 /** 460 * Unique job id associated with this application (uid). This is the same job ID 461 * you supplied in the {@link Builder} constructor. 462 */ getId()463 public int getId() { 464 return jobId; 465 } 466 467 /** 468 * @see JobInfo.Builder#setExtras(PersistableBundle) 469 */ getExtras()470 public @NonNull PersistableBundle getExtras() { 471 return extras; 472 } 473 474 /** 475 * @see JobInfo.Builder#setTransientExtras(Bundle) 476 */ getTransientExtras()477 public @NonNull Bundle getTransientExtras() { 478 return transientExtras; 479 } 480 481 /** 482 * @see JobInfo.Builder#setClipData(ClipData, int) 483 */ getClipData()484 public @Nullable ClipData getClipData() { 485 return clipData; 486 } 487 488 /** 489 * @see JobInfo.Builder#setClipData(ClipData, int) 490 */ getClipGrantFlags()491 public int getClipGrantFlags() { 492 return clipGrantFlags; 493 } 494 495 /** 496 * Name of the service endpoint that will be called back into by the JobScheduler. 497 */ getService()498 public @NonNull ComponentName getService() { 499 return service; 500 } 501 502 /** @hide */ getBias()503 public int getBias() { 504 return mBias; 505 } 506 507 /** 508 * @see JobInfo.Builder#setPriority(int) 509 */ 510 @Priority getPriority()511 public int getPriority() { 512 return mPriority; 513 } 514 515 /** @hide */ getFlags()516 public int getFlags() { 517 return flags; 518 } 519 520 /** @hide */ isExemptedFromAppStandby()521 public boolean isExemptedFromAppStandby() { 522 return ((flags & FLAG_EXEMPT_FROM_APP_STANDBY) != 0) && !isPeriodic(); 523 } 524 525 /** 526 * @hide 527 * @see JobInfo.Builder#setPrefersBatteryNotLow(boolean) 528 */ isPreferBatteryNotLow()529 public boolean isPreferBatteryNotLow() { 530 return (mPreferredConstraintFlags & CONSTRAINT_FLAG_BATTERY_NOT_LOW) != 0; 531 } 532 533 /** 534 * @hide 535 * @see JobInfo.Builder#setPrefersCharging(boolean) 536 */ isPreferCharging()537 public boolean isPreferCharging() { 538 return (mPreferredConstraintFlags & CONSTRAINT_FLAG_CHARGING) != 0; 539 } 540 541 /** 542 * @hide 543 * @see JobInfo.Builder#setPrefersDeviceIdle(boolean) 544 */ isPreferDeviceIdle()545 public boolean isPreferDeviceIdle() { 546 return (mPreferredConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0; 547 } 548 549 /** 550 * @see JobInfo.Builder#setRequiresCharging(boolean) 551 */ isRequireCharging()552 public boolean isRequireCharging() { 553 return (constraintFlags & CONSTRAINT_FLAG_CHARGING) != 0; 554 } 555 556 /** 557 * @see JobInfo.Builder#setRequiresBatteryNotLow(boolean) 558 */ isRequireBatteryNotLow()559 public boolean isRequireBatteryNotLow() { 560 return (constraintFlags & CONSTRAINT_FLAG_BATTERY_NOT_LOW) != 0; 561 } 562 563 /** 564 * @see JobInfo.Builder#setRequiresDeviceIdle(boolean) 565 */ isRequireDeviceIdle()566 public boolean isRequireDeviceIdle() { 567 return (constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0; 568 } 569 570 /** 571 * @see JobInfo.Builder#setRequiresStorageNotLow(boolean) 572 */ isRequireStorageNotLow()573 public boolean isRequireStorageNotLow() { 574 return (constraintFlags & CONSTRAINT_FLAG_STORAGE_NOT_LOW) != 0; 575 } 576 577 /** 578 * @hide 579 */ getConstraintFlags()580 public int getConstraintFlags() { 581 return constraintFlags; 582 } 583 584 /** 585 * @hide 586 */ getPreferredConstraintFlags()587 public int getPreferredConstraintFlags() { 588 return mPreferredConstraintFlags; 589 } 590 591 /** 592 * Which content: URIs must change for the job to be scheduled. Returns null 593 * if there are none required. 594 * @see JobInfo.Builder#addTriggerContentUri(TriggerContentUri) 595 */ getTriggerContentUris()596 public @Nullable TriggerContentUri[] getTriggerContentUris() { 597 return triggerContentUris; 598 } 599 600 /** 601 * When triggering on content URI changes, this is the delay from when a change 602 * is detected until the job is scheduled. 603 * @see JobInfo.Builder#setTriggerContentUpdateDelay(long) 604 */ getTriggerContentUpdateDelay()605 public long getTriggerContentUpdateDelay() { 606 return triggerContentUpdateDelay; 607 } 608 609 /** 610 * When triggering on content URI changes, this is the maximum delay we will 611 * use before scheduling the job. 612 * @see JobInfo.Builder#setTriggerContentMaxDelay(long) 613 */ getTriggerContentMaxDelay()614 public long getTriggerContentMaxDelay() { 615 return triggerContentMaxDelay; 616 } 617 618 /** 619 * Return the basic description of the kind of network this job requires. 620 * 621 * @deprecated This method attempts to map {@link #getRequiredNetwork()} 622 * into the set of simple constants, which results in a loss of 623 * fidelity. Callers should move to using 624 * {@link #getRequiredNetwork()} directly. 625 * @see Builder#setRequiredNetworkType(int) 626 */ 627 @Deprecated getNetworkType()628 public @NetworkType int getNetworkType() { 629 if (networkRequest == null) { 630 return NETWORK_TYPE_NONE; 631 } else if (networkRequest.hasCapability(NET_CAPABILITY_NOT_METERED)) { 632 return NETWORK_TYPE_UNMETERED; 633 } else if (networkRequest.hasCapability(NET_CAPABILITY_NOT_ROAMING)) { 634 return NETWORK_TYPE_NOT_ROAMING; 635 } else if (networkRequest.hasTransport(TRANSPORT_CELLULAR)) { 636 return NETWORK_TYPE_CELLULAR; 637 } else { 638 return NETWORK_TYPE_ANY; 639 } 640 } 641 642 /** 643 * Return the detailed description of the kind of network this job requires, 644 * or {@code null} if no specific kind of network is required. 645 * 646 * @see Builder#setRequiredNetwork(NetworkRequest) 647 */ getRequiredNetwork()648 public @Nullable NetworkRequest getRequiredNetwork() { 649 return networkRequest; 650 } 651 652 /** 653 * Return the estimated size of download traffic that will be performed by 654 * this job, in bytes. 655 * 656 * @return Estimated size of download traffic, or 657 * {@link #NETWORK_BYTES_UNKNOWN} when unknown. 658 * @see Builder#setEstimatedNetworkBytes(long, long) 659 */ getEstimatedNetworkDownloadBytes()660 public @BytesLong long getEstimatedNetworkDownloadBytes() { 661 return networkDownloadBytes; 662 } 663 664 /** 665 * Return the estimated size of upload traffic that will be performed by 666 * this job, in bytes. 667 * 668 * @return Estimated size of upload traffic, or 669 * {@link #NETWORK_BYTES_UNKNOWN} when unknown. 670 * @see Builder#setEstimatedNetworkBytes(long, long) 671 */ getEstimatedNetworkUploadBytes()672 public @BytesLong long getEstimatedNetworkUploadBytes() { 673 return networkUploadBytes; 674 } 675 676 /** 677 * Return the smallest piece of data that cannot be easily paused and resumed, in bytes. 678 * 679 * @return Smallest piece of data that cannot be easily paused and resumed, or 680 * {@link #NETWORK_BYTES_UNKNOWN} when unknown. 681 * @see Builder#setMinimumNetworkChunkBytes(long) 682 */ getMinimumNetworkChunkBytes()683 public @BytesLong long getMinimumNetworkChunkBytes() { 684 return minimumNetworkChunkBytes; 685 } 686 687 /** 688 * Set for a job that does not recur periodically, to specify a delay after which the job 689 * will be eligible for execution. This value is not set if the job recurs periodically. 690 * @see JobInfo.Builder#setMinimumLatency(long) 691 */ getMinLatencyMillis()692 public long getMinLatencyMillis() { 693 return minLatencyMillis; 694 } 695 696 /** 697 * @see JobInfo.Builder#setOverrideDeadline(long) 698 */ getMaxExecutionDelayMillis()699 public long getMaxExecutionDelayMillis() { 700 return maxExecutionDelayMillis; 701 } 702 703 /** 704 * Track whether this job will repeat with a given period. 705 * @see JobInfo.Builder#setPeriodic(long) 706 * @see JobInfo.Builder#setPeriodic(long, long) 707 */ isPeriodic()708 public boolean isPeriodic() { 709 return isPeriodic; 710 } 711 712 /** 713 * @see JobInfo.Builder#setPersisted(boolean) 714 */ isPersisted()715 public boolean isPersisted() { 716 return isPersisted; 717 } 718 719 /** 720 * Set to the interval between occurrences of this job. This value is <b>not</b> set if the 721 * job does not recur periodically. 722 * @see JobInfo.Builder#setPeriodic(long) 723 * @see JobInfo.Builder#setPeriodic(long, long) 724 */ getIntervalMillis()725 public long getIntervalMillis() { 726 return intervalMillis; 727 } 728 729 /** 730 * Flex time for this job. Only valid if this is a periodic job. The job can 731 * execute at any time in a window of flex length at the end of the period. 732 * @see JobInfo.Builder#setPeriodic(long) 733 * @see JobInfo.Builder#setPeriodic(long, long) 734 */ getFlexMillis()735 public long getFlexMillis() { 736 return flexMillis; 737 } 738 739 /** 740 * The amount of time the JobScheduler will wait before rescheduling a failed job. This value 741 * will be increased depending on the backoff policy specified at job creation time. Defaults 742 * to 30 seconds, minimum is currently 10 seconds. 743 * @see JobInfo.Builder#setBackoffCriteria(long, int) 744 */ getInitialBackoffMillis()745 public long getInitialBackoffMillis() { 746 return initialBackoffMillis; 747 } 748 749 /** 750 * Return the backoff policy of this job. 751 * 752 * @see JobInfo.Builder#setBackoffCriteria(long, int) 753 */ getBackoffPolicy()754 public @BackoffPolicy int getBackoffPolicy() { 755 return backoffPolicy; 756 } 757 758 /** 759 * @see JobInfo.Builder#setExpedited(boolean) 760 */ isExpedited()761 public boolean isExpedited() { 762 return (flags & FLAG_EXPEDITED) != 0; 763 } 764 765 /** 766 * @see JobInfo.Builder#setUserInitiated(boolean) 767 */ isUserInitiated()768 public boolean isUserInitiated() { 769 return (flags & FLAG_USER_INITIATED) != 0; 770 } 771 772 /** 773 * @see JobInfo.Builder#setImportantWhileForeground(boolean) 774 */ isImportantWhileForeground()775 public boolean isImportantWhileForeground() { 776 return (flags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0; 777 } 778 779 /** 780 * @see JobInfo.Builder#setPrefetch(boolean) 781 */ isPrefetch()782 public boolean isPrefetch() { 783 return (flags & FLAG_PREFETCH) != 0; 784 } 785 786 /** 787 * User can specify an early constraint of 0L, which is valid, so we keep track of whether the 788 * function was called at all. 789 * @hide 790 */ hasEarlyConstraint()791 public boolean hasEarlyConstraint() { 792 return hasEarlyConstraint; 793 } 794 795 /** 796 * User can specify a late constraint of 0L, which is valid, so we keep track of whether the 797 * function was called at all. 798 * @hide 799 */ hasLateConstraint()800 public boolean hasLateConstraint() { 801 return hasLateConstraint; 802 } 803 804 @Override equals(Object o)805 public boolean equals(Object o) { 806 if (!(o instanceof JobInfo)) { 807 return false; 808 } 809 JobInfo j = (JobInfo) o; 810 if (jobId != j.jobId) { 811 return false; 812 } 813 // XXX won't be correct if one is parcelled and the other not. 814 if (!BaseBundle.kindofEquals(extras, j.extras)) { 815 return false; 816 } 817 // XXX won't be correct if one is parcelled and the other not. 818 if (!BaseBundle.kindofEquals(transientExtras, j.transientExtras)) { 819 return false; 820 } 821 // XXX for now we consider two different clip data objects to be different, 822 // regardless of whether their contents are the same. 823 if (clipData != j.clipData) { 824 return false; 825 } 826 if (clipGrantFlags != j.clipGrantFlags) { 827 return false; 828 } 829 if (!Objects.equals(service, j.service)) { 830 return false; 831 } 832 if (constraintFlags != j.constraintFlags) { 833 return false; 834 } 835 if (mPreferredConstraintFlags != j.mPreferredConstraintFlags) { 836 return false; 837 } 838 if (!Arrays.equals(triggerContentUris, j.triggerContentUris)) { 839 return false; 840 } 841 if (triggerContentUpdateDelay != j.triggerContentUpdateDelay) { 842 return false; 843 } 844 if (triggerContentMaxDelay != j.triggerContentMaxDelay) { 845 return false; 846 } 847 if (hasEarlyConstraint != j.hasEarlyConstraint) { 848 return false; 849 } 850 if (hasLateConstraint != j.hasLateConstraint) { 851 return false; 852 } 853 if (!Objects.equals(networkRequest, j.networkRequest)) { 854 return false; 855 } 856 if (networkDownloadBytes != j.networkDownloadBytes) { 857 return false; 858 } 859 if (networkUploadBytes != j.networkUploadBytes) { 860 return false; 861 } 862 if (minimumNetworkChunkBytes != j.minimumNetworkChunkBytes) { 863 return false; 864 } 865 if (minLatencyMillis != j.minLatencyMillis) { 866 return false; 867 } 868 if (maxExecutionDelayMillis != j.maxExecutionDelayMillis) { 869 return false; 870 } 871 if (isPeriodic != j.isPeriodic) { 872 return false; 873 } 874 if (isPersisted != j.isPersisted) { 875 return false; 876 } 877 if (intervalMillis != j.intervalMillis) { 878 return false; 879 } 880 if (flexMillis != j.flexMillis) { 881 return false; 882 } 883 if (initialBackoffMillis != j.initialBackoffMillis) { 884 return false; 885 } 886 if (backoffPolicy != j.backoffPolicy) { 887 return false; 888 } 889 if (mBias != j.mBias) { 890 return false; 891 } 892 if (mPriority != j.mPriority) { 893 return false; 894 } 895 if (flags != j.flags) { 896 return false; 897 } 898 return true; 899 } 900 901 @Override hashCode()902 public int hashCode() { 903 int hashCode = jobId; 904 if (extras != null) { 905 hashCode = 31 * hashCode + extras.hashCode(); 906 } 907 if (transientExtras != null) { 908 hashCode = 31 * hashCode + transientExtras.hashCode(); 909 } 910 if (clipData != null) { 911 hashCode = 31 * hashCode + clipData.hashCode(); 912 } 913 hashCode = 31*hashCode + clipGrantFlags; 914 if (service != null) { 915 hashCode = 31 * hashCode + service.hashCode(); 916 } 917 hashCode = 31 * hashCode + constraintFlags; 918 hashCode = 31 * hashCode + mPreferredConstraintFlags; 919 if (triggerContentUris != null) { 920 hashCode = 31 * hashCode + Arrays.hashCode(triggerContentUris); 921 } 922 hashCode = 31 * hashCode + Long.hashCode(triggerContentUpdateDelay); 923 hashCode = 31 * hashCode + Long.hashCode(triggerContentMaxDelay); 924 hashCode = 31 * hashCode + Boolean.hashCode(hasEarlyConstraint); 925 hashCode = 31 * hashCode + Boolean.hashCode(hasLateConstraint); 926 if (networkRequest != null) { 927 hashCode = 31 * hashCode + networkRequest.hashCode(); 928 } 929 hashCode = 31 * hashCode + Long.hashCode(networkDownloadBytes); 930 hashCode = 31 * hashCode + Long.hashCode(networkUploadBytes); 931 hashCode = 31 * hashCode + Long.hashCode(minimumNetworkChunkBytes); 932 hashCode = 31 * hashCode + Long.hashCode(minLatencyMillis); 933 hashCode = 31 * hashCode + Long.hashCode(maxExecutionDelayMillis); 934 hashCode = 31 * hashCode + Boolean.hashCode(isPeriodic); 935 hashCode = 31 * hashCode + Boolean.hashCode(isPersisted); 936 hashCode = 31 * hashCode + Long.hashCode(intervalMillis); 937 hashCode = 31 * hashCode + Long.hashCode(flexMillis); 938 hashCode = 31 * hashCode + Long.hashCode(initialBackoffMillis); 939 hashCode = 31 * hashCode + backoffPolicy; 940 hashCode = 31 * hashCode + mBias; 941 hashCode = 31 * hashCode + mPriority; 942 hashCode = 31 * hashCode + flags; 943 return hashCode; 944 } 945 946 @SuppressWarnings("UnsafeParcelApi") JobInfo(Parcel in)947 private JobInfo(Parcel in) { 948 jobId = in.readInt(); 949 final PersistableBundle persistableExtras = in.readPersistableBundle(); 950 extras = persistableExtras != null ? persistableExtras : PersistableBundle.EMPTY; 951 transientExtras = in.readBundle(); 952 if (in.readInt() != 0) { 953 clipData = ClipData.CREATOR.createFromParcel(in); 954 clipGrantFlags = in.readInt(); 955 } else { 956 clipData = null; 957 clipGrantFlags = 0; 958 } 959 service = in.readParcelable(null); 960 constraintFlags = in.readInt(); 961 mPreferredConstraintFlags = in.readInt(); 962 triggerContentUris = in.createTypedArray(TriggerContentUri.CREATOR); 963 triggerContentUpdateDelay = in.readLong(); 964 triggerContentMaxDelay = in.readLong(); 965 if (in.readInt() != 0) { 966 networkRequest = NetworkRequest.CREATOR.createFromParcel(in); 967 } else { 968 networkRequest = null; 969 } 970 networkDownloadBytes = in.readLong(); 971 networkUploadBytes = in.readLong(); 972 minimumNetworkChunkBytes = in.readLong(); 973 minLatencyMillis = in.readLong(); 974 maxExecutionDelayMillis = in.readLong(); 975 isPeriodic = in.readInt() == 1; 976 isPersisted = in.readInt() == 1; 977 intervalMillis = in.readLong(); 978 flexMillis = in.readLong(); 979 initialBackoffMillis = in.readLong(); 980 backoffPolicy = in.readInt(); 981 hasEarlyConstraint = in.readInt() == 1; 982 hasLateConstraint = in.readInt() == 1; 983 mBias = in.readInt(); 984 mPriority = in.readInt(); 985 flags = in.readInt(); 986 } 987 JobInfo(JobInfo.Builder b)988 private JobInfo(JobInfo.Builder b) { 989 jobId = b.mJobId; 990 extras = b.mExtras.deepCopy(); 991 transientExtras = b.mTransientExtras.deepCopy(); 992 clipData = b.mClipData; 993 clipGrantFlags = b.mClipGrantFlags; 994 service = b.mJobService; 995 constraintFlags = b.mConstraintFlags; 996 mPreferredConstraintFlags = b.mPreferredConstraintFlags; 997 triggerContentUris = b.mTriggerContentUris != null 998 ? b.mTriggerContentUris.toArray(new TriggerContentUri[b.mTriggerContentUris.size()]) 999 : null; 1000 triggerContentUpdateDelay = b.mTriggerContentUpdateDelay; 1001 triggerContentMaxDelay = b.mTriggerContentMaxDelay; 1002 networkRequest = b.mNetworkRequest; 1003 networkDownloadBytes = b.mNetworkDownloadBytes; 1004 networkUploadBytes = b.mNetworkUploadBytes; 1005 minimumNetworkChunkBytes = b.mMinimumNetworkChunkBytes; 1006 minLatencyMillis = b.mMinLatencyMillis; 1007 maxExecutionDelayMillis = b.mMaxExecutionDelayMillis; 1008 isPeriodic = b.mIsPeriodic; 1009 isPersisted = b.mIsPersisted; 1010 intervalMillis = b.mIntervalMillis; 1011 flexMillis = b.mFlexMillis; 1012 initialBackoffMillis = b.mInitialBackoffMillis; 1013 backoffPolicy = b.mBackoffPolicy; 1014 hasEarlyConstraint = b.mHasEarlyConstraint; 1015 hasLateConstraint = b.mHasLateConstraint; 1016 mBias = b.mBias; 1017 mPriority = b.mPriority; 1018 flags = b.mFlags; 1019 } 1020 1021 @Override describeContents()1022 public int describeContents() { 1023 return 0; 1024 } 1025 1026 @Override writeToParcel(Parcel out, int flags)1027 public void writeToParcel(Parcel out, int flags) { 1028 out.writeInt(jobId); 1029 out.writePersistableBundle(extras); 1030 out.writeBundle(transientExtras); 1031 if (clipData != null) { 1032 out.writeInt(1); 1033 clipData.writeToParcel(out, flags); 1034 out.writeInt(clipGrantFlags); 1035 } else { 1036 out.writeInt(0); 1037 } 1038 out.writeParcelable(service, flags); 1039 out.writeInt(constraintFlags); 1040 out.writeInt(mPreferredConstraintFlags); 1041 out.writeTypedArray(triggerContentUris, flags); 1042 out.writeLong(triggerContentUpdateDelay); 1043 out.writeLong(triggerContentMaxDelay); 1044 if (networkRequest != null) { 1045 out.writeInt(1); 1046 networkRequest.writeToParcel(out, flags); 1047 } else { 1048 out.writeInt(0); 1049 } 1050 out.writeLong(networkDownloadBytes); 1051 out.writeLong(networkUploadBytes); 1052 out.writeLong(minimumNetworkChunkBytes); 1053 out.writeLong(minLatencyMillis); 1054 out.writeLong(maxExecutionDelayMillis); 1055 out.writeInt(isPeriodic ? 1 : 0); 1056 out.writeInt(isPersisted ? 1 : 0); 1057 out.writeLong(intervalMillis); 1058 out.writeLong(flexMillis); 1059 out.writeLong(initialBackoffMillis); 1060 out.writeInt(backoffPolicy); 1061 out.writeInt(hasEarlyConstraint ? 1 : 0); 1062 out.writeInt(hasLateConstraint ? 1 : 0); 1063 out.writeInt(mBias); 1064 out.writeInt(mPriority); 1065 out.writeInt(this.flags); 1066 } 1067 1068 public static final @android.annotation.NonNull Creator<JobInfo> CREATOR = new Creator<JobInfo>() { 1069 @Override 1070 public JobInfo createFromParcel(Parcel in) { 1071 return new JobInfo(in); 1072 } 1073 1074 @Override 1075 public JobInfo[] newArray(int size) { 1076 return new JobInfo[size]; 1077 } 1078 }; 1079 1080 @Override toString()1081 public String toString() { 1082 return "(job:" + jobId + "/" + service.flattenToShortString() + ")"; 1083 } 1084 1085 /** 1086 * Information about a content URI modification that a job would like to 1087 * trigger on. 1088 */ 1089 public static final class TriggerContentUri implements Parcelable { 1090 private final Uri mUri; 1091 private final int mFlags; 1092 1093 /** @hide */ 1094 @Retention(RetentionPolicy.SOURCE) 1095 @IntDef(flag = true, prefix = { "FLAG_" }, value = { 1096 FLAG_NOTIFY_FOR_DESCENDANTS, 1097 }) 1098 public @interface Flags { } 1099 1100 /** 1101 * Flag for trigger: also trigger if any descendants of the given URI change. 1102 * Corresponds to the <var>notifyForDescendants</var> of 1103 * {@link android.content.ContentResolver#registerContentObserver}. 1104 */ 1105 public static final int FLAG_NOTIFY_FOR_DESCENDANTS = 1<<0; 1106 1107 /** 1108 * Create a new trigger description. 1109 * @param uri The URI to observe. Must be non-null. 1110 * @param flags Flags for the observer. 1111 */ TriggerContentUri(@onNull Uri uri, @Flags int flags)1112 public TriggerContentUri(@NonNull Uri uri, @Flags int flags) { 1113 mUri = Objects.requireNonNull(uri); 1114 mFlags = flags; 1115 } 1116 1117 /** 1118 * Return the Uri this trigger was created for. 1119 */ getUri()1120 public Uri getUri() { 1121 return mUri; 1122 } 1123 1124 /** 1125 * Return the flags supplied for the trigger. 1126 */ getFlags()1127 public @Flags int getFlags() { 1128 return mFlags; 1129 } 1130 1131 @Override equals(Object o)1132 public boolean equals(Object o) { 1133 if (!(o instanceof TriggerContentUri)) { 1134 return false; 1135 } 1136 TriggerContentUri t = (TriggerContentUri) o; 1137 return Objects.equals(t.mUri, mUri) && t.mFlags == mFlags; 1138 } 1139 1140 @Override hashCode()1141 public int hashCode() { 1142 return (mUri == null ? 0 : mUri.hashCode()) ^ mFlags; 1143 } 1144 TriggerContentUri(Parcel in)1145 private TriggerContentUri(Parcel in) { 1146 mUri = Uri.CREATOR.createFromParcel(in); 1147 mFlags = in.readInt(); 1148 } 1149 1150 @Override describeContents()1151 public int describeContents() { 1152 return 0; 1153 } 1154 1155 @Override writeToParcel(Parcel out, int flags)1156 public void writeToParcel(Parcel out, int flags) { 1157 mUri.writeToParcel(out, flags); 1158 out.writeInt(mFlags); 1159 } 1160 1161 public static final @android.annotation.NonNull Creator<TriggerContentUri> CREATOR = new Creator<TriggerContentUri>() { 1162 @Override 1163 public TriggerContentUri createFromParcel(Parcel in) { 1164 return new TriggerContentUri(in); 1165 } 1166 1167 @Override 1168 public TriggerContentUri[] newArray(int size) { 1169 return new TriggerContentUri[size]; 1170 } 1171 }; 1172 } 1173 1174 /** Builder class for constructing {@link JobInfo} objects. */ 1175 public static final class Builder { 1176 private final int mJobId; 1177 private final ComponentName mJobService; 1178 private PersistableBundle mExtras = PersistableBundle.EMPTY; 1179 private Bundle mTransientExtras = Bundle.EMPTY; 1180 private ClipData mClipData; 1181 private int mClipGrantFlags; 1182 private int mBias = BIAS_DEFAULT; 1183 @Priority 1184 private int mPriority = PRIORITY_DEFAULT; 1185 private int mFlags; 1186 // Requirements. 1187 private int mConstraintFlags; 1188 private int mPreferredConstraintFlags; 1189 private NetworkRequest mNetworkRequest; 1190 private long mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN; 1191 private long mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN; 1192 private long mMinimumNetworkChunkBytes = NETWORK_BYTES_UNKNOWN; 1193 private ArrayList<TriggerContentUri> mTriggerContentUris; 1194 private long mTriggerContentUpdateDelay = -1; 1195 private long mTriggerContentMaxDelay = -1; 1196 private boolean mIsPersisted; 1197 // One-off parameters. 1198 private long mMinLatencyMillis; 1199 private long mMaxExecutionDelayMillis; 1200 // Periodic parameters. 1201 private boolean mIsPeriodic; 1202 private boolean mHasEarlyConstraint; 1203 private boolean mHasLateConstraint; 1204 private long mIntervalMillis; 1205 private long mFlexMillis; 1206 // Back-off parameters. 1207 private long mInitialBackoffMillis = DEFAULT_INITIAL_BACKOFF_MILLIS; 1208 private int mBackoffPolicy = DEFAULT_BACKOFF_POLICY; 1209 /** Easy way to track whether the client has tried to set a back-off policy. */ 1210 private boolean mBackoffPolicySet = false; 1211 1212 /** 1213 * Initialize a new Builder to construct a {@link JobInfo}. 1214 * 1215 * @param jobId Application-provided id for this job. Subsequent calls to cancel, or 1216 * jobs created with the same jobId, will update the pre-existing job with 1217 * the same id. This ID must be unique across all clients of the same uid 1218 * (not just the same package). You will want to make sure this is a stable 1219 * id across app updates, so probably not based on a resource ID. 1220 * @param jobService The endpoint that you implement that will receive the callback from the 1221 * JobScheduler. 1222 */ Builder(int jobId, @NonNull ComponentName jobService)1223 public Builder(int jobId, @NonNull ComponentName jobService) { 1224 mJobService = jobService; 1225 mJobId = jobId; 1226 } 1227 1228 /** 1229 * Creates a new Builder of JobInfo from an existing instance. 1230 * @hide 1231 */ Builder(@onNull JobInfo job)1232 public Builder(@NonNull JobInfo job) { 1233 mJobId = job.getId(); 1234 mJobService = job.getService(); 1235 mExtras = job.getExtras(); 1236 mTransientExtras = job.getTransientExtras(); 1237 mClipData = job.getClipData(); 1238 mClipGrantFlags = job.getClipGrantFlags(); 1239 mBias = job.getBias(); 1240 mFlags = job.getFlags(); 1241 mConstraintFlags = job.getConstraintFlags(); 1242 mPreferredConstraintFlags = job.getPreferredConstraintFlags(); 1243 mNetworkRequest = job.getRequiredNetwork(); 1244 mNetworkDownloadBytes = job.getEstimatedNetworkDownloadBytes(); 1245 mNetworkUploadBytes = job.getEstimatedNetworkUploadBytes(); 1246 mMinimumNetworkChunkBytes = job.getMinimumNetworkChunkBytes(); 1247 mTriggerContentUris = job.getTriggerContentUris() != null 1248 ? new ArrayList<>(Arrays.asList(job.getTriggerContentUris())) : null; 1249 mTriggerContentUpdateDelay = job.getTriggerContentUpdateDelay(); 1250 mTriggerContentMaxDelay = job.getTriggerContentMaxDelay(); 1251 mIsPersisted = job.isPersisted(); 1252 mMinLatencyMillis = job.getMinLatencyMillis(); 1253 mMaxExecutionDelayMillis = job.getMaxExecutionDelayMillis(); 1254 mIsPeriodic = job.isPeriodic(); 1255 mHasEarlyConstraint = job.hasEarlyConstraint(); 1256 mHasLateConstraint = job.hasLateConstraint(); 1257 mIntervalMillis = job.getIntervalMillis(); 1258 mFlexMillis = job.getFlexMillis(); 1259 mInitialBackoffMillis = job.getInitialBackoffMillis(); 1260 // mBackoffPolicySet isn't set but it's fine since this is copying from an already valid 1261 // job. 1262 mBackoffPolicy = job.getBackoffPolicy(); 1263 mPriority = job.getPriority(); 1264 } 1265 1266 /** @hide */ 1267 @NonNull setBias(int bias)1268 public Builder setBias(int bias) { 1269 mBias = bias; 1270 return this; 1271 } 1272 1273 /** 1274 * Indicate the priority for this job. The priority set here will be used to sort jobs 1275 * for the calling app and apply slightly different policies based on the priority. 1276 * The priority will <b>NOT</b> be used as a global sorting value to sort between 1277 * different app's jobs. Use this to inform the system about which jobs it should try 1278 * to run before other jobs. Giving the same priority to all of your jobs will result 1279 * in them all being treated the same. The priorities each have slightly different 1280 * behaviors, as noted in their relevant javadoc. 1281 * 1282 * Starting in Android version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, 1283 * the priority will only affect sorting order within the job's namespace. 1284 * 1285 * <b>NOTE:</b> Setting all of your jobs to high priority will not be 1286 * beneficial to your app and in fact may hurt its performance in the 1287 * long run. 1288 * 1289 * In order to prevent starvation, repeatedly retried jobs (because of failures) will slowly 1290 * have their priorities lowered. 1291 * 1292 * @see JobInfo#getPriority() 1293 */ 1294 @NonNull setPriority(@riority int priority)1295 public Builder setPriority(@Priority int priority) { 1296 if (priority > PRIORITY_MAX || priority < PRIORITY_MIN) { 1297 if (Compatibility.isChangeEnabled(THROW_ON_INVALID_PRIORITY_VALUE)) { 1298 throw new IllegalArgumentException("Invalid priority value"); 1299 } 1300 // No-op for invalid calls of apps that are targeting S-. This was an unsupported 1301 // API before Tiramisu, so anyone calling this that isn't targeting T isn't 1302 // guaranteed a behavior change. 1303 return this; 1304 } 1305 mPriority = priority; 1306 return this; 1307 } 1308 1309 /** @hide */ 1310 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) setFlags(int flags)1311 public Builder setFlags(int flags) { 1312 mFlags = flags; 1313 return this; 1314 } 1315 1316 /** 1317 * Set optional extras. This is persisted, so we only allow primitive types. 1318 * @param extras Bundle containing extras you want the scheduler to hold on to for you. 1319 * @see JobInfo#getExtras() 1320 */ setExtras(@onNull PersistableBundle extras)1321 public Builder setExtras(@NonNull PersistableBundle extras) { 1322 mExtras = extras; 1323 return this; 1324 } 1325 1326 /** 1327 * Set optional transient extras. 1328 * 1329 * <p>Because setting this property is not compatible with persisted 1330 * jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when 1331 * {@link android.app.job.JobInfo.Builder#build()} is called.</p> 1332 * 1333 * @param extras Bundle containing extras you want the scheduler to hold on to for you. 1334 * @see JobInfo#getTransientExtras() 1335 */ setTransientExtras(@onNull Bundle extras)1336 public Builder setTransientExtras(@NonNull Bundle extras) { 1337 mTransientExtras = extras; 1338 return this; 1339 } 1340 1341 /** 1342 * Set a {@link ClipData} associated with this Job. 1343 * 1344 * <p>The main purpose of providing a ClipData is to allow granting of 1345 * URI permissions for data associated with the clip. The exact kind 1346 * of permission grant to perform is specified through <var>grantFlags</var>. 1347 * 1348 * <p>If the ClipData contains items that are Intents, any 1349 * grant flags in those Intents will be ignored. Only flags provided as an argument 1350 * to this method are respected, and will be applied to all Uri or 1351 * Intent items in the clip (or sub-items of the clip). 1352 * 1353 * <p>Because setting this property is not compatible with persisted 1354 * jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when 1355 * {@link android.app.job.JobInfo.Builder#build()} is called.</p> 1356 * 1357 * @param clip The new clip to set. May be null to clear the current clip. 1358 * @param grantFlags The desired permissions to grant for any URIs. This should be 1359 * a combination of {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION}, 1360 * {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, and 1361 * {@link android.content.Intent#FLAG_GRANT_PREFIX_URI_PERMISSION}. 1362 * @see JobInfo#getClipData() 1363 * @see JobInfo#getClipGrantFlags() 1364 */ setClipData(@ullable ClipData clip, int grantFlags)1365 public Builder setClipData(@Nullable ClipData clip, int grantFlags) { 1366 mClipData = clip; 1367 mClipGrantFlags = grantFlags; 1368 return this; 1369 } 1370 1371 /** 1372 * Set basic description of the kind of network your job requires. If 1373 * you need more precise control over network capabilities, see 1374 * {@link #setRequiredNetwork(NetworkRequest)}. 1375 * <p> 1376 * If your job doesn't need a network connection, you don't need to call 1377 * this method, as the default value is {@link #NETWORK_TYPE_NONE}. 1378 * <p> 1379 * Calling this method defines network as a strict requirement for your 1380 * job. If the network requested is not available your job will never 1381 * run. See {@link #setOverrideDeadline(long)} to change this behavior. 1382 * Calling this method will override any requirements previously defined 1383 * by {@link #setRequiredNetwork(NetworkRequest)}; you typically only 1384 * want to call one of these methods. 1385 * 1386 * Starting in Android version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, 1387 * an app must hold the 1388 * {@link android.Manifest.permission#ACCESS_NETWORK_STATE} permission to 1389 * schedule a job that requires a network. 1390 * 1391 * <p class="note"> 1392 * When your job executes in 1393 * {@link JobService#onStartJob(JobParameters)}, be sure to use the 1394 * specific network returned by {@link JobParameters#getNetwork()}, 1395 * otherwise you'll use the default network which may not meet this 1396 * constraint. 1397 * 1398 * @see #setRequiredNetwork(NetworkRequest) 1399 * @see JobInfo#getNetworkType() 1400 * @see JobParameters#getNetwork() 1401 */ setRequiredNetworkType(@etworkType int networkType)1402 public Builder setRequiredNetworkType(@NetworkType int networkType) { 1403 if (networkType == NETWORK_TYPE_NONE) { 1404 return setRequiredNetwork(null); 1405 } else { 1406 final NetworkRequest.Builder builder = new NetworkRequest.Builder(); 1407 1408 // All types require validated Internet 1409 builder.addCapability(NET_CAPABILITY_INTERNET); 1410 builder.addCapability(NET_CAPABILITY_VALIDATED); 1411 builder.removeCapability(NET_CAPABILITY_NOT_VPN); 1412 builder.removeCapability(NET_CAPABILITY_NOT_RESTRICTED); 1413 1414 if (networkType == NETWORK_TYPE_ANY) { 1415 // No other capabilities 1416 } else if (networkType == NETWORK_TYPE_UNMETERED) { 1417 builder.addCapability(NET_CAPABILITY_NOT_METERED); 1418 } else if (networkType == NETWORK_TYPE_NOT_ROAMING) { 1419 builder.addCapability(NET_CAPABILITY_NOT_ROAMING); 1420 } else if (networkType == NETWORK_TYPE_CELLULAR) { 1421 builder.addTransportType(TRANSPORT_CELLULAR); 1422 } 1423 1424 return setRequiredNetwork(builder.build()); 1425 } 1426 } 1427 1428 /** 1429 * Set detailed description of the kind of network your job requires. 1430 * <p> 1431 * If your job doesn't need a network connection, you don't need to call 1432 * this method, as the default is {@code null}. 1433 * <p> 1434 * Calling this method defines network as a strict requirement for your 1435 * job. If the network requested is not available your job will never 1436 * run. See {@link #setOverrideDeadline(long)} to change this behavior. 1437 * Calling this method will override any requirements previously defined 1438 * by {@link #setRequiredNetworkType(int)}; you typically only want to 1439 * call one of these methods. 1440 * <p class="note"> 1441 * When your job executes in 1442 * {@link JobService#onStartJob(JobParameters)}, be sure to use the 1443 * specific network returned by {@link JobParameters#getNetwork()}, 1444 * otherwise you'll use the default network which may not meet this 1445 * constraint. 1446 * 1447 * Starting in Android version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, 1448 * an app must hold the 1449 * {@link android.Manifest.permission#ACCESS_NETWORK_STATE} permission to 1450 * schedule a job that requires a network. 1451 * 1452 * @param networkRequest The detailed description of the kind of network 1453 * this job requires, or {@code null} if no specific kind of 1454 * network is required. Defining a {@link NetworkSpecifier} 1455 * is only supported for jobs that aren't persisted. 1456 * @see #setRequiredNetworkType(int) 1457 * @see JobInfo#getRequiredNetwork() 1458 * @see JobParameters#getNetwork() 1459 */ setRequiredNetwork(@ullable NetworkRequest networkRequest)1460 public Builder setRequiredNetwork(@Nullable NetworkRequest networkRequest) { 1461 mNetworkRequest = networkRequest; 1462 return this; 1463 } 1464 1465 /** 1466 * Set the estimated size of network traffic that will be performed by 1467 * this job, in bytes. 1468 * <p> 1469 * Apps are encouraged to provide values that are as accurate as 1470 * possible, but when the exact size isn't available, an 1471 * order-of-magnitude estimate can be provided instead. Here are some 1472 * specific examples: 1473 * <ul> 1474 * <li>A job that is backing up a photo knows the exact size of that 1475 * photo, so it should provide that size as the estimate. 1476 * <li>A job that refreshes top news stories wouldn't know an exact 1477 * size, but if the size is expected to be consistently around 100KB, it 1478 * can provide that order-of-magnitude value as the estimate. 1479 * <li>A job that synchronizes email could end up using an extreme range 1480 * of data, from under 1KB when nothing has changed, to dozens of MB 1481 * when there are new emails with attachments. Jobs that cannot provide 1482 * reasonable estimates should use the sentinel value 1483 * {@link JobInfo#NETWORK_BYTES_UNKNOWN}. 1484 * </ul> 1485 * Note that the system may choose to delay jobs with large network 1486 * usage estimates when the device has a poor network connection, in 1487 * order to save battery and possible network costs. 1488 * Starting from Android version {@link Build.VERSION_CODES#S}, JobScheduler may attempt 1489 * to run large jobs when the device is charging and on an unmetered network, even if the 1490 * network is slow. This gives large jobs an opportunity to make forward progress, even if 1491 * they risk timing out. 1492 * <p> 1493 * The values provided here only reflect the traffic that will be 1494 * performed by the base job; if you're using {@link JobWorkItem} then 1495 * you also need to define the network traffic used by each work item 1496 * when constructing them. 1497 * 1498 * <p class="note"> 1499 * Prior to Android version {@link Build.VERSION_CODES#TIRAMISU}, JobScheduler used the 1500 * estimated transfer numbers in a similar fashion to 1501 * {@link #setMinimumNetworkChunkBytes(long)} (to estimate if the work would complete 1502 * within the time available to job). In other words, JobScheduler treated the transfer as 1503 * all-or-nothing. Starting from Android version {@link Build.VERSION_CODES#TIRAMISU}, 1504 * JobScheduler will only use the estimated transfer numbers in this manner if minimum 1505 * chunk sizes have not been provided via {@link #setMinimumNetworkChunkBytes(long)}. 1506 * 1507 * @param downloadBytes The estimated size of network traffic that will 1508 * be downloaded by this job, in bytes. 1509 * @param uploadBytes The estimated size of network traffic that will be 1510 * uploaded by this job, in bytes. 1511 * @see JobInfo#getEstimatedNetworkDownloadBytes() 1512 * @see JobInfo#getEstimatedNetworkUploadBytes() 1513 * @see JobWorkItem#JobWorkItem(android.content.Intent, long, long) 1514 */ 1515 // TODO(b/255371817): update documentation to reflect how this data will be used setEstimatedNetworkBytes(@ytesLong long downloadBytes, @BytesLong long uploadBytes)1516 public Builder setEstimatedNetworkBytes(@BytesLong long downloadBytes, 1517 @BytesLong long uploadBytes) { 1518 mNetworkDownloadBytes = downloadBytes; 1519 mNetworkUploadBytes = uploadBytes; 1520 return this; 1521 } 1522 1523 /** 1524 * Set the minimum size of non-resumable network traffic this job requires, in bytes. When 1525 * the upload or download can be easily paused and resumed, use this to set the smallest 1526 * size that must be transmitted between start and stop events to be considered successful. 1527 * If the transfer cannot be paused and resumed, then this should be the sum of the values 1528 * provided to {@link JobInfo.Builder#setEstimatedNetworkBytes(long, long)}. 1529 * 1530 * <p> 1531 * Apps are encouraged to provide values that are as accurate as possible since JobScheduler 1532 * will try to run the job at a time when at least the minimum chunk can be transmitted to 1533 * reduce the amount of repetitive data that's transferred. Jobs that cannot provide 1534 * reasonable estimates should use the sentinel value {@link JobInfo#NETWORK_BYTES_UNKNOWN}. 1535 * 1536 * <p> 1537 * The values provided here only reflect the minimum non-resumable traffic that will be 1538 * performed by the base job; if you're using {@link JobWorkItem} then 1539 * you also need to define the network traffic used by each work item 1540 * when constructing them. 1541 * 1542 * @param chunkSizeBytes The smallest piece of data that cannot be easily paused and 1543 * resumed, in bytes. 1544 * @see JobInfo#getMinimumNetworkChunkBytes() 1545 * @see JobWorkItem#JobWorkItem(android.content.Intent, long, long, long) 1546 */ 1547 @NonNull setMinimumNetworkChunkBytes(@ytesLong long chunkSizeBytes)1548 public Builder setMinimumNetworkChunkBytes(@BytesLong long chunkSizeBytes) { 1549 if (chunkSizeBytes != NETWORK_BYTES_UNKNOWN && chunkSizeBytes <= 0) { 1550 throw new IllegalArgumentException("Minimum chunk size must be positive"); 1551 } 1552 mMinimumNetworkChunkBytes = chunkSizeBytes; 1553 return this; 1554 } 1555 1556 /** 1557 * Specify that this job would prefer to be run when the device's battery is not low. 1558 * This defaults to {@code false}. 1559 * 1560 * <p>The system may attempt to delay this job until the device's battery is not low, 1561 * but may choose to run it even if the device's battery is low. JobScheduler will not stop 1562 * this job if this constraint is no longer satisfied after the job has started running. 1563 * If this job must only run when the device's battery is not low, 1564 * use {@link #setRequiresBatteryNotLow(boolean)} instead. 1565 * 1566 * <p> 1567 * Because it doesn't make sense for a constraint to be both preferred and required, 1568 * calling both this and {@link #setRequiresBatteryNotLow(boolean)} with {@code true} 1569 * will result in an {@link java.lang.IllegalArgumentException} when 1570 * {@link android.app.job.JobInfo.Builder#build()} is called. 1571 * 1572 * @param prefersBatteryNotLow Pass {@code true} to prefer that the device's battery level 1573 * not be low in order to run the job. 1574 * @return This object for method chaining 1575 * @see JobInfo#isPreferBatteryNotLow() 1576 * @hide 1577 */ 1578 @NonNull setPrefersBatteryNotLow(boolean prefersBatteryNotLow)1579 public Builder setPrefersBatteryNotLow(boolean prefersBatteryNotLow) { 1580 mPreferredConstraintFlags = 1581 (mPreferredConstraintFlags & ~CONSTRAINT_FLAG_BATTERY_NOT_LOW) 1582 | (prefersBatteryNotLow ? CONSTRAINT_FLAG_BATTERY_NOT_LOW : 0); 1583 return this; 1584 } 1585 1586 /** 1587 * Specify that this job would prefer to be run when the device is charging (or be a 1588 * non-battery-powered device connected to permanent power, such as Android TV 1589 * devices). This defaults to {@code false}. 1590 * 1591 * <p> 1592 * The system may attempt to delay this job until the device is charging, but may 1593 * choose to run it even if the device is not charging. JobScheduler will not stop 1594 * this job if this constraint is no longer satisfied after the job has started running. 1595 * If this job must only run when the device is charging, 1596 * use {@link #setRequiresCharging(boolean)} instead. 1597 * 1598 * <p> 1599 * Because it doesn't make sense for a constraint to be both preferred and required, 1600 * calling both this and {@link #setRequiresCharging(boolean)} with {@code true} 1601 * will result in an {@link java.lang.IllegalArgumentException} when 1602 * {@link android.app.job.JobInfo.Builder#build()} is called. 1603 * 1604 * @param prefersCharging Pass {@code true} to prefer that the device be 1605 * charging in order to run the job. 1606 * @return This object for method chaining 1607 * @see JobInfo#isPreferCharging() 1608 * @hide 1609 */ 1610 @NonNull setPrefersCharging(boolean prefersCharging)1611 public Builder setPrefersCharging(boolean prefersCharging) { 1612 mPreferredConstraintFlags = (mPreferredConstraintFlags & ~CONSTRAINT_FLAG_CHARGING) 1613 | (prefersCharging ? CONSTRAINT_FLAG_CHARGING : 0); 1614 return this; 1615 } 1616 1617 /** 1618 * Specify that this job would prefer to be run when the device is not in active use. 1619 * This defaults to {@code false}. 1620 * 1621 * <p>The system may attempt to delay this job until the device is not in active use, 1622 * but may choose to run it even if the device is not idle. JobScheduler will not stop 1623 * this job if this constraint is no longer satisfied after the job has started running. 1624 * If this job must only run when the device is not in active use, 1625 * use {@link #setRequiresDeviceIdle(boolean)} instead. 1626 * 1627 * <p> 1628 * Because it doesn't make sense for a constraint to be both preferred and required, 1629 * calling both this and {@link #setRequiresDeviceIdle(boolean)} with {@code true} 1630 * will result in an {@link java.lang.IllegalArgumentException} when 1631 * {@link android.app.job.JobInfo.Builder#build()} is called. 1632 * 1633 * <p class="note">Despite the similar naming, this job constraint is <em>not</em> 1634 * related to the system's "device idle" or "doze" states. This constraint only 1635 * determines whether a job is allowed to run while the device is directly in use. 1636 * 1637 * @param prefersDeviceIdle Pass {@code true} to prefer that the device not be in active 1638 * use when running this job. 1639 * @return This object for method chaining 1640 * @see JobInfo#isRequireDeviceIdle() 1641 * @hide 1642 */ 1643 @NonNull setPrefersDeviceIdle(boolean prefersDeviceIdle)1644 public Builder setPrefersDeviceIdle(boolean prefersDeviceIdle) { 1645 mPreferredConstraintFlags = (mPreferredConstraintFlags & ~CONSTRAINT_FLAG_DEVICE_IDLE) 1646 | (prefersDeviceIdle ? CONSTRAINT_FLAG_DEVICE_IDLE : 0); 1647 return this; 1648 } 1649 1650 /** 1651 * Specify that to run this job, the device must be charging (or be a 1652 * non-battery-powered device connected to permanent power, such as Android TV 1653 * devices). This defaults to {@code false}. Setting this to {@code false} <b>DOES NOT</b> 1654 * mean the job will only run when the device is not charging. 1655 * 1656 * <p class="note">For purposes of running jobs, a battery-powered device 1657 * "charging" is not quite the same as simply being connected to power. If the 1658 * device is so busy that the battery is draining despite a power connection, jobs 1659 * with this constraint will <em>not</em> run. This can happen during some 1660 * common use cases such as video chat, particularly if the device is plugged in 1661 * to USB rather than to wall power. 1662 * 1663 * @param requiresCharging Pass {@code true} to require that the device be 1664 * charging in order to run the job. 1665 * @see JobInfo#isRequireCharging() 1666 */ setRequiresCharging(boolean requiresCharging)1667 public Builder setRequiresCharging(boolean requiresCharging) { 1668 mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_CHARGING) 1669 | (requiresCharging ? CONSTRAINT_FLAG_CHARGING : 0); 1670 return this; 1671 } 1672 1673 /** 1674 * Specify that to run this job, the device's battery level must not be low. 1675 * This defaults to false. If true, the job will only run when the battery level 1676 * is not low, which is generally the point where the user is given a "low battery" 1677 * warning. Setting this to {@code false} <b>DOES NOT</b> mean the job will only run 1678 * when the battery is low. 1679 * 1680 * @param batteryNotLow Whether or not the device's battery level must not be low. 1681 * @see JobInfo#isRequireBatteryNotLow() 1682 */ setRequiresBatteryNotLow(boolean batteryNotLow)1683 public Builder setRequiresBatteryNotLow(boolean batteryNotLow) { 1684 mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_BATTERY_NOT_LOW) 1685 | (batteryNotLow ? CONSTRAINT_FLAG_BATTERY_NOT_LOW : 0); 1686 return this; 1687 } 1688 1689 /** 1690 * When set {@code true}, ensure that this job will not run if the device is in active use. 1691 * The default state is {@code false}: that is, the for the job to be runnable even when 1692 * someone is interacting with the device. Setting this to {@code false} <b>DOES NOT</b> 1693 * mean the job will only run when the device is not idle. 1694 * 1695 * <p>This state is a loose definition provided by the system. In general, it means that 1696 * the device is not currently being used interactively, and has not been in use for some 1697 * time. As such, it is a good time to perform resource heavy jobs. Bear in mind that 1698 * battery usage will still be attributed to your application, and surfaced to the user in 1699 * battery stats.</p> 1700 * 1701 * <p class="note">Despite the similar naming, this job constraint is <em>not</em> 1702 * related to the system's "device idle" or "doze" states. This constraint only 1703 * determines whether a job is allowed to run while the device is directly in use. 1704 * 1705 * @param requiresDeviceIdle Pass {@code true} to prevent the job from running 1706 * while the device is being used interactively. 1707 * @see JobInfo#isRequireDeviceIdle() 1708 */ setRequiresDeviceIdle(boolean requiresDeviceIdle)1709 public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) { 1710 mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_DEVICE_IDLE) 1711 | (requiresDeviceIdle ? CONSTRAINT_FLAG_DEVICE_IDLE : 0); 1712 return this; 1713 } 1714 1715 /** 1716 * Specify that to run this job, the device's available storage must not be low. 1717 * This defaults to false. If true, the job will only run when the device is not 1718 * in a low storage state, which is generally the point where the user is given a 1719 * "low storage" warning. 1720 * @param storageNotLow Whether or not the device's available storage must not be low. 1721 * @see JobInfo#isRequireStorageNotLow() 1722 */ setRequiresStorageNotLow(boolean storageNotLow)1723 public Builder setRequiresStorageNotLow(boolean storageNotLow) { 1724 mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_STORAGE_NOT_LOW) 1725 | (storageNotLow ? CONSTRAINT_FLAG_STORAGE_NOT_LOW : 0); 1726 return this; 1727 } 1728 1729 /** 1730 * Add a new content: URI that will be monitored with a 1731 * {@link android.database.ContentObserver}, and will cause the job to execute if changed. 1732 * If you have any trigger content URIs associated with a job, it will not execute until 1733 * there has been a change report for one or more of them. 1734 * 1735 * <p>Note that trigger URIs can not be used in combination with 1736 * {@link #setPeriodic(long)} or {@link #setPersisted(boolean)}. To continually monitor 1737 * for content changes, you need to schedule a new JobInfo using the same job ID and 1738 * observing the same URIs in place of calling 1739 * {@link JobService#jobFinished(JobParameters, boolean)}. Remember that 1740 * {@link JobScheduler#schedule(JobInfo)} stops a running job if it uses the same job ID, 1741 * so only call it after you've finished processing the most recent changes (in other words, 1742 * call {@link JobScheduler#schedule(JobInfo)} where you would have normally called 1743 * {@link JobService#jobFinished(JobParameters, boolean)}. 1744 * Following this pattern will ensure you do not lose any content changes: while your 1745 * job is running, the system will continue monitoring for content changes, and propagate 1746 * any changes it sees over to the next job you schedule, so you do not have to worry 1747 * about missing new changes. <b>Scheduling the new job 1748 * before or during processing will cause the current job to be stopped (as described in 1749 * {@link JobScheduler#schedule(JobInfo)}), meaning the wakelock will be released for the 1750 * current job and your app process may be killed since it will no longer be in a valid 1751 * component lifecycle.</b> 1752 * Since {@link JobScheduler#schedule(JobInfo)} stops the current job, you do not 1753 * need to call {@link JobService#jobFinished(JobParameters, boolean)} if you call 1754 * {@link JobScheduler#schedule(JobInfo)} using the same job ID as the 1755 * currently running job.</p> 1756 * 1757 * <p>Because setting this property is not compatible with periodic or 1758 * persisted jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when 1759 * {@link android.app.job.JobInfo.Builder#build()} is called.</p> 1760 * 1761 * <p>The following example shows how this feature can be used to monitor for changes 1762 * in the photos on a device.</p> 1763 * 1764 * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/PhotosContentJob.java 1765 * job} 1766 * 1767 * @param uri The content: URI to monitor. 1768 * @see JobInfo#getTriggerContentUris() 1769 */ addTriggerContentUri(@onNull TriggerContentUri uri)1770 public Builder addTriggerContentUri(@NonNull TriggerContentUri uri) { 1771 if (mTriggerContentUris == null) { 1772 mTriggerContentUris = new ArrayList<>(); 1773 } 1774 mTriggerContentUris.add(uri); 1775 return this; 1776 } 1777 1778 /** 1779 * Set the delay (in milliseconds) from when a content change is detected until 1780 * the job is scheduled. If there are more changes during that time, the delay 1781 * will be reset to start at the time of the most recent change. 1782 * @param durationMs Delay after most recent content change, in milliseconds. 1783 * @see JobInfo#getTriggerContentUpdateDelay() 1784 */ setTriggerContentUpdateDelay(long durationMs)1785 public Builder setTriggerContentUpdateDelay(long durationMs) { 1786 mTriggerContentUpdateDelay = durationMs; 1787 return this; 1788 } 1789 1790 /** 1791 * Set the maximum total delay (in milliseconds) that is allowed from the first 1792 * time a content change is detected until the job is scheduled. 1793 * @param durationMs Delay after initial content change, in milliseconds. 1794 * @see JobInfo#getTriggerContentMaxDelay() 1795 */ setTriggerContentMaxDelay(long durationMs)1796 public Builder setTriggerContentMaxDelay(long durationMs) { 1797 mTriggerContentMaxDelay = durationMs; 1798 return this; 1799 } 1800 1801 /** 1802 * Specify that this job should recur with the provided interval, not more than once per 1803 * period. You have no control over when within this interval this job will be executed, 1804 * only the guarantee that it will be executed at most once within this interval, as long 1805 * as the constraints are satisfied. If the constraints are not satisfied within this 1806 * interval, the job will wait until the constraints are satisfied. 1807 * Setting this function on the builder with {@link #setMinimumLatency(long)} or 1808 * {@link #setOverrideDeadline(long)} will result in an error. 1809 * @param intervalMillis Millisecond interval for which this job will repeat. 1810 * @see JobInfo#getIntervalMillis() 1811 * @see JobInfo#getFlexMillis() 1812 */ setPeriodic(long intervalMillis)1813 public Builder setPeriodic(long intervalMillis) { 1814 return setPeriodic(intervalMillis, intervalMillis); 1815 } 1816 1817 /** 1818 * Specify that this job should recur with the provided interval and flex. The job can 1819 * execute at any time in a window of flex length at the end of the period. 1820 * @param intervalMillis Millisecond interval for which this job will repeat. A minimum 1821 * value of {@link #getMinPeriodMillis()} is enforced. 1822 * @param flexMillis Millisecond flex for this job. Flex is clamped to be at least 1823 * {@link #getMinFlexMillis()} or 5 percent of the period, whichever is 1824 * higher. 1825 * @see JobInfo#getIntervalMillis() 1826 * @see JobInfo#getFlexMillis() 1827 */ setPeriodic(long intervalMillis, long flexMillis)1828 public Builder setPeriodic(long intervalMillis, long flexMillis) { 1829 final long minPeriod = getMinPeriodMillis(); 1830 if (intervalMillis < minPeriod) { 1831 Log.w(TAG, "Requested interval " + formatDuration(intervalMillis) + " for job " 1832 + mJobId + " is too small; raising to " + formatDuration(minPeriod)); 1833 intervalMillis = minPeriod; 1834 } 1835 1836 final long percentClamp = 5 * intervalMillis / 100; 1837 final long minFlex = Math.max(percentClamp, getMinFlexMillis()); 1838 if (flexMillis < minFlex) { 1839 Log.w(TAG, "Requested flex " + formatDuration(flexMillis) + " for job " + mJobId 1840 + " is too small; raising to " + formatDuration(minFlex)); 1841 flexMillis = minFlex; 1842 } 1843 1844 mIsPeriodic = true; 1845 mIntervalMillis = intervalMillis; 1846 mFlexMillis = flexMillis; 1847 mHasEarlyConstraint = mHasLateConstraint = true; 1848 return this; 1849 } 1850 1851 /** 1852 * Specify that this job should be delayed by the provided amount of time. The job may not 1853 * run the instant the delay has elapsed. JobScheduler will start the job at an 1854 * indeterminate time after the delay has elapsed. 1855 * <p> 1856 * Because it doesn't make sense setting this property on a periodic job, doing so will 1857 * throw an {@link java.lang.IllegalArgumentException} when 1858 * {@link android.app.job.JobInfo.Builder#build()} is called. 1859 * @param minLatencyMillis Milliseconds before which this job will not be considered for 1860 * execution. 1861 * @see JobInfo#getMinLatencyMillis() 1862 */ setMinimumLatency(long minLatencyMillis)1863 public Builder setMinimumLatency(long minLatencyMillis) { 1864 mMinLatencyMillis = minLatencyMillis; 1865 mHasEarlyConstraint = true; 1866 return this; 1867 } 1868 1869 /** 1870 * Set deadline which is the maximum scheduling latency. The job will be run by this 1871 * deadline even if other requirements (including a delay set through 1872 * {@link #setMinimumLatency(long)}) are not met. 1873 * <p> 1874 * Because it doesn't make sense setting this property on a periodic job, doing so will 1875 * throw an {@link java.lang.IllegalArgumentException} when 1876 * {@link android.app.job.JobInfo.Builder#build()} is called. 1877 * @see JobInfo#getMaxExecutionDelayMillis() 1878 */ setOverrideDeadline(long maxExecutionDelayMillis)1879 public Builder setOverrideDeadline(long maxExecutionDelayMillis) { 1880 mMaxExecutionDelayMillis = maxExecutionDelayMillis; 1881 mHasLateConstraint = true; 1882 return this; 1883 } 1884 1885 /** 1886 * Set up the back-off/retry policy. 1887 * This defaults to some respectable values: {30 seconds, Exponential}. We cap back-off at 1888 * 5hrs. 1889 * <p> 1890 * Note that trying to set a backoff criteria for a job with 1891 * {@link #setRequiresDeviceIdle(boolean)} will throw an exception when you call build(). 1892 * This is because back-off typically does not make sense for these types of jobs. See 1893 * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)} 1894 * for more description of the return value for the case of a job executing while in idle 1895 * mode. 1896 * @param initialBackoffMillis Millisecond time interval to wait initially when job has 1897 * failed. 1898 * @see JobInfo#getInitialBackoffMillis() 1899 * @see JobInfo#getBackoffPolicy() 1900 */ setBackoffCriteria(long initialBackoffMillis, @BackoffPolicy int backoffPolicy)1901 public Builder setBackoffCriteria(long initialBackoffMillis, 1902 @BackoffPolicy int backoffPolicy) { 1903 final long minBackoff = getMinBackoffMillis(); 1904 if (initialBackoffMillis < minBackoff) { 1905 Log.w(TAG, "Requested backoff " + formatDuration(initialBackoffMillis) + " for job " 1906 + mJobId + " is too small; raising to " + formatDuration(minBackoff)); 1907 initialBackoffMillis = minBackoff; 1908 } 1909 1910 mBackoffPolicySet = true; 1911 mInitialBackoffMillis = initialBackoffMillis; 1912 mBackoffPolicy = backoffPolicy; 1913 return this; 1914 } 1915 1916 /** 1917 * Setting this to true indicates that this job is important and needs to run as soon as 1918 * possible with stronger guarantees than regular jobs. These "expedited" jobs will: 1919 * <ol> 1920 * <li>Run as soon as possible</li> 1921 * <li>Be less restricted during Doze and battery saver</li> 1922 * <li>Bypass Doze, app standby, and battery saver network restrictions</li> 1923 * <li>Be less likely to be killed than regular jobs</li> 1924 * <li>Be subject to background location throttling</li> 1925 * <li>Be exempt from delay to optimize job execution</li> 1926 * </ol> 1927 * 1928 * <p> 1929 * Since these jobs have stronger guarantees than regular jobs, they will be subject to 1930 * stricter quotas. As long as an app has available expedited quota, jobs scheduled with 1931 * this set to true will run with these guarantees. If an app has run out of available 1932 * expedited quota, any pending expedited jobs will run as regular jobs. 1933 * {@link JobParameters#isExpeditedJob()} can be used to know whether the executing job 1934 * has expedited guarantees or not. In addition, {@link JobScheduler#schedule(JobInfo)} 1935 * will immediately return {@link JobScheduler#RESULT_FAILURE} if the app does not have 1936 * available quota (and the job will not be successfully scheduled). 1937 * 1938 * <p> 1939 * Expedited job quota will replenish over time and as the user interacts with the app, 1940 * so you should not have to worry about running out of quota because of processing from 1941 * frequent user engagement. 1942 * 1943 * <p> 1944 * Expedited jobs may only set network, storage-not-low, and persistence constraints. 1945 * No other constraints are allowed. 1946 * 1947 * <p> 1948 * Assuming all constraints remain satisfied (including ideal system load conditions), 1949 * expedited jobs can have an execution time of at least 1 minute. If your 1950 * app has remaining expedited job quota, then the expedited job <i>may</i> potentially run 1951 * longer until remaining quota is used up. Just like with regular jobs, quota is not 1952 * consumed while the app is on top and visible to the user. 1953 * 1954 * <p class="note"> 1955 * Note: Even though expedited jobs are meant to run as soon as possible, they may be 1956 * deferred if the system is under heavy load or requested constraints are not satisfied. 1957 * This delay may be true for expedited jobs of the foreground app on Android version 1958 * {@link Build.VERSION_CODES#S}, but starting from Android version 1959 * {@link Build.VERSION_CODES#TIRAMISU}, expedited jobs for the foreground app are 1960 * guaranteed to be started before {@link JobScheduler#schedule(JobInfo)} returns (assuming 1961 * all requested constraints are satisfied), similar to foreground services. 1962 * 1963 * @see JobInfo#isExpedited() 1964 */ 1965 @NonNull setExpedited(boolean expedited)1966 public Builder setExpedited(boolean expedited) { 1967 if (expedited) { 1968 mFlags |= FLAG_EXPEDITED; 1969 if (mPriority == PRIORITY_DEFAULT) { 1970 // The default priority for EJs is MAX, but only change this if .setPriority() 1971 // hasn't been called yet. 1972 mPriority = PRIORITY_MAX; 1973 } 1974 } else { 1975 if (mPriority == PRIORITY_MAX && (mFlags & FLAG_EXPEDITED) != 0) { 1976 // Reset the priority for the job, but only change this if .setPriority() 1977 // hasn't been called yet. 1978 mPriority = PRIORITY_DEFAULT; 1979 } 1980 mFlags &= (~FLAG_EXPEDITED); 1981 } 1982 return this; 1983 } 1984 1985 /** 1986 * Indicates that this job is being scheduled to fulfill an explicit user request. 1987 * As such, user-initiated jobs can only be scheduled when the app is in the foreground 1988 * or in a state where launching an activity is allowed, as defined 1989 * <a href= 1990 * "https://developer.android.com/guide/components/activities/background-starts#exceptions"> 1991 * here</a>. Attempting to schedule one outside of these conditions will throw a 1992 * {@link SecurityException}. 1993 * 1994 * <p> 1995 * This should <b>NOT</b> be used for automatic features. 1996 * 1997 * <p> 1998 * All user-initiated jobs must have an associated notification, set via 1999 * {@link JobService#setNotification(JobParameters, int, Notification, int)}, and will be 2000 * shown in the Task Manager when running. These jobs cannot be rescheduled by the app 2001 * if the user stops the job via system provided affordance (such as the Task Manager). 2002 * Thus, it is best practice and recommended to provide action buttons in the 2003 * associated notification to allow the user to stop the job gracefully 2004 * and allow for rescheduling. 2005 * 2006 * <p> 2007 * If the app doesn't hold the {@link android.Manifest.permission#RUN_USER_INITIATED_JOBS} 2008 * permission when scheduling a user-initiated job, JobScheduler will throw a 2009 * {@link SecurityException}. 2010 * 2011 * <p> 2012 * In {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, user-initiated jobs can only 2013 * be used for network data transfers. As such, they must specify a required network via 2014 * {@link #setRequiredNetwork(NetworkRequest)} or {@link #setRequiredNetworkType(int)}. 2015 * 2016 * <p> 2017 * These jobs will not be subject to quotas and will be started immediately once scheduled 2018 * if all constraints are met and the device system health allows for additional tasks. 2019 * 2020 * @see JobInfo#isUserInitiated() 2021 */ 2022 @RequiresPermission(android.Manifest.permission.RUN_USER_INITIATED_JOBS) 2023 @NonNull setUserInitiated(boolean userInitiated)2024 public Builder setUserInitiated(boolean userInitiated) { 2025 if (userInitiated) { 2026 mFlags |= FLAG_USER_INITIATED; 2027 if (mPriority == PRIORITY_DEFAULT) { 2028 // The default priority for UIJs is MAX, but only change this if .setPriority() 2029 // hasn't been called yet. 2030 mPriority = PRIORITY_MAX; 2031 } 2032 } else { 2033 if (mPriority == PRIORITY_MAX && (mFlags & FLAG_USER_INITIATED) != 0) { 2034 // Reset the priority for the job, but only change this if .setPriority() 2035 // hasn't been called yet. 2036 mPriority = PRIORITY_DEFAULT; 2037 } 2038 mFlags &= (~FLAG_USER_INITIATED); 2039 } 2040 return this; 2041 } 2042 2043 /** 2044 * Setting this to true indicates that this job is important while the scheduling app 2045 * is in the foreground or on the temporary whitelist for background restrictions. 2046 * This means that the system will relax doze restrictions on this job during this time. 2047 * 2048 * Apps should use this flag only for short jobs that are essential for the app to function 2049 * properly in the foreground. 2050 * 2051 * Note that once the scheduling app is no longer whitelisted from background restrictions 2052 * and in the background, or the job failed due to unsatisfied constraints, 2053 * this job should be expected to behave like other jobs without this flag. 2054 * 2055 * <p> 2056 * Jobs marked as important-while-foreground are given {@link #PRIORITY_HIGH} by default. 2057 * 2058 * @param importantWhileForeground whether to relax doze restrictions for this job when the 2059 * app is in the foreground. False by default. 2060 * @see JobInfo#isImportantWhileForeground() 2061 * @deprecated Use {@link #setExpedited(boolean)} instead. 2062 */ 2063 @Deprecated setImportantWhileForeground(boolean importantWhileForeground)2064 public Builder setImportantWhileForeground(boolean importantWhileForeground) { 2065 if (importantWhileForeground) { 2066 mFlags |= FLAG_IMPORTANT_WHILE_FOREGROUND; 2067 if (mPriority == PRIORITY_DEFAULT) { 2068 // The default priority for important-while-foreground is HIGH, but only change 2069 // this if .setPriority() hasn't been called yet. 2070 mPriority = PRIORITY_HIGH; 2071 } 2072 } else { 2073 if (mPriority == PRIORITY_HIGH 2074 && (mFlags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0) { 2075 // Reset the priority for the job, but only change this if .setPriority() 2076 // hasn't been called yet. 2077 mPriority = PRIORITY_DEFAULT; 2078 } 2079 mFlags &= (~FLAG_IMPORTANT_WHILE_FOREGROUND); 2080 } 2081 return this; 2082 } 2083 2084 /** 2085 * Setting this to true indicates that this job is designed to prefetch 2086 * content that will make a material improvement to the experience of 2087 * the specific user of this device. For example, fetching top headlines 2088 * of interest to the current user. 2089 * <p> 2090 * Apps targeting Android version {@link Build.VERSION_CODES#TIRAMISU} or later are 2091 * not allowed to have deadlines (set via {@link #setOverrideDeadline(long)} on their 2092 * prefetch jobs. 2093 * <p> 2094 * The system may use this signal to relax the network constraints you 2095 * originally requested, such as allowing a 2096 * {@link JobInfo#NETWORK_TYPE_UNMETERED} job to run over a metered 2097 * network when there is a surplus of metered data available. The system 2098 * may also use this signal in combination with end user usage patterns 2099 * to ensure data is prefetched before the user launches your app. 2100 * @see JobInfo#isPrefetch() 2101 */ setPrefetch(boolean prefetch)2102 public Builder setPrefetch(boolean prefetch) { 2103 if (prefetch) { 2104 mFlags |= FLAG_PREFETCH; 2105 } else { 2106 mFlags &= (~FLAG_PREFETCH); 2107 } 2108 return this; 2109 } 2110 2111 /** 2112 * Set whether or not to persist this job across device reboots. 2113 * 2114 * @param isPersisted True to indicate that the job will be written to 2115 * disk and loaded at boot. 2116 * @see JobInfo#isPersisted() 2117 */ 2118 @RequiresPermission(android.Manifest.permission.RECEIVE_BOOT_COMPLETED) setPersisted(boolean isPersisted)2119 public Builder setPersisted(boolean isPersisted) { 2120 mIsPersisted = isPersisted; 2121 return this; 2122 } 2123 2124 /** 2125 * @return The job object to hand to the JobScheduler. This object is immutable. 2126 */ build()2127 public JobInfo build() { 2128 return build(Compatibility.isChangeEnabled(DISALLOW_DEADLINES_FOR_PREFETCH_JOBS), 2129 Compatibility.isChangeEnabled(REJECT_NEGATIVE_NETWORK_ESTIMATES)); 2130 } 2131 2132 /** @hide */ build(boolean disallowPrefetchDeadlines, boolean rejectNegativeNetworkEstimates)2133 public JobInfo build(boolean disallowPrefetchDeadlines, 2134 boolean rejectNegativeNetworkEstimates) { 2135 // This check doesn't need to be inside enforceValidity. It's an unnecessary legacy 2136 // check that would ideally be phased out instead. 2137 if (mBackoffPolicySet && (mConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) { 2138 throw new IllegalArgumentException("An idle mode job will not respect any" + 2139 " back-off policy, so calling setBackoffCriteria with" + 2140 " setRequiresDeviceIdle is an error."); 2141 } 2142 JobInfo jobInfo = new JobInfo(this); 2143 jobInfo.enforceValidity(disallowPrefetchDeadlines, rejectNegativeNetworkEstimates); 2144 return jobInfo; 2145 } 2146 2147 /** 2148 * @hide 2149 */ summarize()2150 public String summarize() { 2151 final String service = (mJobService != null) 2152 ? mJobService.flattenToShortString() 2153 : "null"; 2154 return "JobInfo.Builder{job:" + mJobId + "/" + service + "}"; 2155 } 2156 } 2157 2158 /** 2159 * @hide 2160 */ enforceValidity(boolean disallowPrefetchDeadlines, boolean rejectNegativeNetworkEstimates)2161 public final void enforceValidity(boolean disallowPrefetchDeadlines, 2162 boolean rejectNegativeNetworkEstimates) { 2163 // Check that network estimates require network type and are reasonable values. 2164 if ((networkDownloadBytes > 0 || networkUploadBytes > 0 || minimumNetworkChunkBytes > 0) 2165 && networkRequest == null) { 2166 throw new IllegalArgumentException( 2167 "Can't provide estimated network usage without requiring a network"); 2168 } 2169 if (networkRequest != null && rejectNegativeNetworkEstimates) { 2170 if (networkUploadBytes != NETWORK_BYTES_UNKNOWN && networkUploadBytes < 0) { 2171 throw new IllegalArgumentException( 2172 "Invalid network upload bytes: " + networkUploadBytes); 2173 } 2174 if (networkDownloadBytes != NETWORK_BYTES_UNKNOWN && networkDownloadBytes < 0) { 2175 throw new IllegalArgumentException( 2176 "Invalid network download bytes: " + networkDownloadBytes); 2177 } 2178 } 2179 final long estimatedTransfer; 2180 if (networkUploadBytes == NETWORK_BYTES_UNKNOWN) { 2181 estimatedTransfer = networkDownloadBytes; 2182 } else { 2183 estimatedTransfer = networkUploadBytes 2184 + (networkDownloadBytes == NETWORK_BYTES_UNKNOWN ? 0 : networkDownloadBytes); 2185 } 2186 if (minimumNetworkChunkBytes != NETWORK_BYTES_UNKNOWN 2187 && estimatedTransfer != NETWORK_BYTES_UNKNOWN 2188 && minimumNetworkChunkBytes > estimatedTransfer) { 2189 throw new IllegalArgumentException( 2190 "Minimum chunk size can't be greater than estimated network usage"); 2191 } 2192 if (minimumNetworkChunkBytes != NETWORK_BYTES_UNKNOWN && minimumNetworkChunkBytes <= 0) { 2193 throw new IllegalArgumentException("Minimum chunk size must be positive"); 2194 } 2195 2196 final boolean hasDeadline = maxExecutionDelayMillis != 0L; 2197 // Check that a deadline was not set on a periodic job. 2198 if (isPeriodic) { 2199 if (hasDeadline) { 2200 throw new IllegalArgumentException( 2201 "Can't call setOverrideDeadline() on a periodic job."); 2202 } 2203 if (minLatencyMillis != 0L) { 2204 throw new IllegalArgumentException( 2205 "Can't call setMinimumLatency() on a periodic job"); 2206 } 2207 if (triggerContentUris != null) { 2208 throw new IllegalArgumentException( 2209 "Can't call addTriggerContentUri() on a periodic job"); 2210 } 2211 } 2212 2213 // Prefetch jobs should not have deadlines 2214 if (disallowPrefetchDeadlines && hasDeadline && (flags & FLAG_PREFETCH) != 0) { 2215 throw new IllegalArgumentException( 2216 "Can't call setOverrideDeadline() on a prefetch job."); 2217 } 2218 2219 if (isPersisted) { 2220 // We can't serialize network specifiers 2221 if (networkRequest != null 2222 && networkRequest.getNetworkSpecifier() != null) { 2223 throw new IllegalArgumentException( 2224 "Network specifiers aren't supported for persistent jobs"); 2225 } 2226 if (triggerContentUris != null) { 2227 throw new IllegalArgumentException( 2228 "Can't call addTriggerContentUri() on a persisted job"); 2229 } 2230 if (!transientExtras.isEmpty()) { 2231 throw new IllegalArgumentException( 2232 "Can't call setTransientExtras() on a persisted job"); 2233 } 2234 if (clipData != null) { 2235 throw new IllegalArgumentException( 2236 "Can't call setClipData() on a persisted job"); 2237 } 2238 } 2239 2240 if ((flags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0) { 2241 if (hasEarlyConstraint) { 2242 throw new IllegalArgumentException( 2243 "An important while foreground job cannot have a time delay"); 2244 } 2245 if (mPriority != PRIORITY_HIGH && mPriority != PRIORITY_DEFAULT) { 2246 throw new IllegalArgumentException( 2247 "An important while foreground job must be high or default priority." 2248 + " Don't mark unimportant tasks as important while foreground."); 2249 } 2250 } 2251 2252 final boolean isExpedited = (flags & FLAG_EXPEDITED) != 0; 2253 final boolean isUserInitiated = (flags & FLAG_USER_INITIATED) != 0; 2254 switch (mPriority) { 2255 case PRIORITY_MAX: 2256 if (!(isExpedited || isUserInitiated)) { 2257 throw new IllegalArgumentException( 2258 "Only expedited or user-initiated jobs can have max priority"); 2259 } 2260 break; 2261 case PRIORITY_HIGH: 2262 if ((flags & FLAG_PREFETCH) != 0) { 2263 throw new IllegalArgumentException("Prefetch jobs cannot be high priority"); 2264 } 2265 if (isPeriodic) { 2266 throw new IllegalArgumentException("Periodic jobs cannot be high priority"); 2267 } 2268 break; 2269 case PRIORITY_DEFAULT: 2270 case PRIORITY_LOW: 2271 case PRIORITY_MIN: 2272 break; 2273 default: 2274 throw new IllegalArgumentException("Invalid priority level provided: " + mPriority); 2275 } 2276 2277 if (isExpedited) { 2278 if (hasEarlyConstraint) { 2279 throw new IllegalArgumentException("An expedited job cannot have a time delay"); 2280 } 2281 if (hasLateConstraint) { 2282 throw new IllegalArgumentException("An expedited job cannot have a deadline"); 2283 } 2284 if (isPeriodic) { 2285 throw new IllegalArgumentException("An expedited job cannot be periodic"); 2286 } 2287 if (isUserInitiated) { 2288 throw new IllegalArgumentException("An expedited job cannot be user-initiated"); 2289 } 2290 if (mPriority != PRIORITY_MAX && mPriority != PRIORITY_HIGH) { 2291 throw new IllegalArgumentException( 2292 "An expedited job must be high or max priority. Don't use expedited jobs" 2293 + " for unimportant tasks."); 2294 } 2295 if ((constraintFlags & ~CONSTRAINT_FLAG_STORAGE_NOT_LOW) != 0 2296 || (flags & ~(FLAG_EXPEDITED | FLAG_EXEMPT_FROM_APP_STANDBY)) != 0) { 2297 throw new IllegalArgumentException( 2298 "An expedited job can only have network and storage-not-low constraints"); 2299 } 2300 if (triggerContentUris != null && triggerContentUris.length > 0) { 2301 throw new IllegalArgumentException( 2302 "Can't call addTriggerContentUri() on an expedited job"); 2303 } 2304 } 2305 2306 if ((constraintFlags & mPreferredConstraintFlags) != 0) { 2307 // Something is marked as both preferred and required. Try to give a clear exception 2308 // reason. 2309 if ((constraintFlags & CONSTRAINT_FLAG_BATTERY_NOT_LOW) != 0 2310 && (mPreferredConstraintFlags & CONSTRAINT_FLAG_BATTERY_NOT_LOW) != 0) { 2311 throw new IllegalArgumentException( 2312 "battery-not-low constraint cannot be both preferred and required"); 2313 } 2314 if ((constraintFlags & CONSTRAINT_FLAG_CHARGING) != 0 2315 && (mPreferredConstraintFlags & CONSTRAINT_FLAG_CHARGING) != 0) { 2316 throw new IllegalArgumentException( 2317 "charging constraint cannot be both preferred and required"); 2318 } 2319 if ((constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0 2320 && (mPreferredConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) { 2321 throw new IllegalArgumentException( 2322 "device idle constraint cannot be both preferred and required"); 2323 } 2324 // Couldn't figure out what the overlap was. Just use a generic message. 2325 throw new IllegalArgumentException( 2326 "constraints cannot be both preferred and required"); 2327 } 2328 2329 if (isUserInitiated) { 2330 if (hasEarlyConstraint) { 2331 throw new IllegalArgumentException("A user-initiated job cannot have a time delay"); 2332 } 2333 if (hasLateConstraint) { 2334 throw new IllegalArgumentException("A user-initiated job cannot have a deadline"); 2335 } 2336 if (isPeriodic) { 2337 throw new IllegalArgumentException("A user-initiated job cannot be periodic"); 2338 } 2339 if ((flags & FLAG_PREFETCH) != 0) { 2340 throw new IllegalArgumentException( 2341 "A user-initiated job cannot also be a prefetch job"); 2342 } 2343 if (mPriority != PRIORITY_MAX) { 2344 throw new IllegalArgumentException("A user-initiated job must be max priority."); 2345 } 2346 if ((constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0 2347 || (mPreferredConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) { 2348 throw new IllegalArgumentException( 2349 "A user-initiated job cannot have a device-idle constraint"); 2350 } 2351 if (triggerContentUris != null && triggerContentUris.length > 0) { 2352 throw new IllegalArgumentException( 2353 "Can't call addTriggerContentUri() on a user-initiated job"); 2354 } 2355 // UIDTs 2356 if (networkRequest == null) { 2357 throw new IllegalArgumentException( 2358 "A user-initiated data transfer job must specify a valid network type"); 2359 } 2360 } 2361 } 2362 2363 /** 2364 * Convert a bias integer into a human readable string for debugging. 2365 * @hide 2366 */ getBiasString(int bias)2367 public static String getBiasString(int bias) { 2368 switch (bias) { 2369 case BIAS_DEFAULT: 2370 return BIAS_DEFAULT + " [DEFAULT]"; 2371 case BIAS_SYNC_EXPEDITED: 2372 return BIAS_SYNC_EXPEDITED + " [SYNC_EXPEDITED]"; 2373 case BIAS_SYNC_INITIALIZATION: 2374 return BIAS_SYNC_INITIALIZATION + " [SYNC_INITIALIZATION]"; 2375 case BIAS_BOUND_FOREGROUND_SERVICE: 2376 return BIAS_BOUND_FOREGROUND_SERVICE + " [BFGS_APP]"; 2377 case BIAS_FOREGROUND_SERVICE: 2378 return BIAS_FOREGROUND_SERVICE + " [FGS_APP]"; 2379 case BIAS_TOP_APP: 2380 return BIAS_TOP_APP + " [TOP_APP]"; 2381 2382 // BIAS_ADJ_* are adjustments and not used as real priorities. 2383 // No need to convert to strings. 2384 } 2385 return bias + " [UNKNOWN]"; 2386 } 2387 2388 /** 2389 * Convert a priority integer into a human readable string for debugging. 2390 * @hide 2391 */ getPriorityString(@riority int priority)2392 public static String getPriorityString(@Priority int priority) { 2393 switch (priority) { 2394 case PRIORITY_MIN: 2395 return priority + " [MIN]"; 2396 case PRIORITY_LOW: 2397 return priority + " [LOW]"; 2398 case PRIORITY_DEFAULT: 2399 return priority + " [DEFAULT]"; 2400 case PRIORITY_HIGH: 2401 return priority + " [HIGH]"; 2402 case PRIORITY_MAX: 2403 return priority + " [MAX]"; 2404 } 2405 return priority + " [UNKNOWN]"; 2406 } 2407 } 2408