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