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 * <p class="caution"><strong>Note:</strong> Beginning with 813 * {@link android.os.Build.VERSION_CODES#BAKLAVA}, this flag will be ignored and no longer 814 * function effectively, regardless of the calling app's target SDK version. 815 * Calling this method will always return {@code false}. 816 * 817 * @see JobInfo.Builder#setImportantWhileForeground(boolean) 818 * 819 * @deprecated Use {@link #isExpedited()} instead. 820 */ 821 @FlaggedApi(Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND) 822 @Deprecated isImportantWhileForeground()823 public boolean isImportantWhileForeground() { 824 if (Flags.ignoreImportantWhileForeground()) { 825 return false; 826 } 827 return (flags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0; 828 } 829 830 /** 831 * @see JobInfo.Builder#setPrefetch(boolean) 832 */ isPrefetch()833 public boolean isPrefetch() { 834 return (flags & FLAG_PREFETCH) != 0; 835 } 836 837 /** 838 * User can specify an early constraint of 0L, which is valid, so we keep track of whether the 839 * function was called at all. 840 * @hide 841 */ hasEarlyConstraint()842 public boolean hasEarlyConstraint() { 843 return hasEarlyConstraint; 844 } 845 846 /** 847 * User can specify a late constraint of 0L, which is valid, so we keep track of whether the 848 * function was called at all. 849 * @hide 850 */ hasLateConstraint()851 public boolean hasLateConstraint() { 852 return hasLateConstraint; 853 } 854 855 @Override equals(Object o)856 public boolean equals(Object o) { 857 if (!(o instanceof JobInfo)) { 858 return false; 859 } 860 JobInfo j = (JobInfo) o; 861 if (jobId != j.jobId) { 862 return false; 863 } 864 // XXX won't be correct if one is parcelled and the other not. 865 if (!BaseBundle.kindofEquals(extras, j.extras)) { 866 return false; 867 } 868 // XXX won't be correct if one is parcelled and the other not. 869 if (!BaseBundle.kindofEquals(transientExtras, j.transientExtras)) { 870 return false; 871 } 872 // XXX for now we consider two different clip data objects to be different, 873 // regardless of whether their contents are the same. 874 if (clipData != j.clipData) { 875 return false; 876 } 877 if (clipGrantFlags != j.clipGrantFlags) { 878 return false; 879 } 880 if (!Objects.equals(service, j.service)) { 881 return false; 882 } 883 if (constraintFlags != j.constraintFlags) { 884 return false; 885 } 886 if (!Arrays.equals(triggerContentUris, j.triggerContentUris)) { 887 return false; 888 } 889 if (triggerContentUpdateDelay != j.triggerContentUpdateDelay) { 890 return false; 891 } 892 if (triggerContentMaxDelay != j.triggerContentMaxDelay) { 893 return false; 894 } 895 if (hasEarlyConstraint != j.hasEarlyConstraint) { 896 return false; 897 } 898 if (hasLateConstraint != j.hasLateConstraint) { 899 return false; 900 } 901 if (!Objects.equals(networkRequest, j.networkRequest)) { 902 return false; 903 } 904 if (networkDownloadBytes != j.networkDownloadBytes) { 905 return false; 906 } 907 if (networkUploadBytes != j.networkUploadBytes) { 908 return false; 909 } 910 if (minimumNetworkChunkBytes != j.minimumNetworkChunkBytes) { 911 return false; 912 } 913 if (minLatencyMillis != j.minLatencyMillis) { 914 return false; 915 } 916 if (maxExecutionDelayMillis != j.maxExecutionDelayMillis) { 917 return false; 918 } 919 if (isPeriodic != j.isPeriodic) { 920 return false; 921 } 922 if (isPersisted != j.isPersisted) { 923 return false; 924 } 925 if (intervalMillis != j.intervalMillis) { 926 return false; 927 } 928 if (flexMillis != j.flexMillis) { 929 return false; 930 } 931 if (initialBackoffMillis != j.initialBackoffMillis) { 932 return false; 933 } 934 if (backoffPolicy != j.backoffPolicy) { 935 return false; 936 } 937 if (mBias != j.mBias) { 938 return false; 939 } 940 if (mPriority != j.mPriority) { 941 return false; 942 } 943 if (flags != j.flags) { 944 return false; 945 } 946 if (!mDebugTags.equals(j.mDebugTags)) { 947 return false; 948 } 949 if (!Objects.equals(mTraceTag, j.mTraceTag)) { 950 return false; 951 } 952 return true; 953 } 954 955 @Override hashCode()956 public int hashCode() { 957 int hashCode = jobId; 958 if (extras != null) { 959 hashCode = 31 * hashCode + extras.hashCode(); 960 } 961 if (transientExtras != null) { 962 hashCode = 31 * hashCode + transientExtras.hashCode(); 963 } 964 if (clipData != null) { 965 hashCode = 31 * hashCode + clipData.hashCode(); 966 } 967 hashCode = 31*hashCode + clipGrantFlags; 968 if (service != null) { 969 hashCode = 31 * hashCode + service.hashCode(); 970 } 971 hashCode = 31 * hashCode + constraintFlags; 972 if (triggerContentUris != null) { 973 hashCode = 31 * hashCode + Arrays.hashCode(triggerContentUris); 974 } 975 hashCode = 31 * hashCode + Long.hashCode(triggerContentUpdateDelay); 976 hashCode = 31 * hashCode + Long.hashCode(triggerContentMaxDelay); 977 hashCode = 31 * hashCode + Boolean.hashCode(hasEarlyConstraint); 978 hashCode = 31 * hashCode + Boolean.hashCode(hasLateConstraint); 979 if (networkRequest != null) { 980 hashCode = 31 * hashCode + networkRequest.hashCode(); 981 } 982 hashCode = 31 * hashCode + Long.hashCode(networkDownloadBytes); 983 hashCode = 31 * hashCode + Long.hashCode(networkUploadBytes); 984 hashCode = 31 * hashCode + Long.hashCode(minimumNetworkChunkBytes); 985 hashCode = 31 * hashCode + Long.hashCode(minLatencyMillis); 986 hashCode = 31 * hashCode + Long.hashCode(maxExecutionDelayMillis); 987 hashCode = 31 * hashCode + Boolean.hashCode(isPeriodic); 988 hashCode = 31 * hashCode + Boolean.hashCode(isPersisted); 989 hashCode = 31 * hashCode + Long.hashCode(intervalMillis); 990 hashCode = 31 * hashCode + Long.hashCode(flexMillis); 991 hashCode = 31 * hashCode + Long.hashCode(initialBackoffMillis); 992 hashCode = 31 * hashCode + backoffPolicy; 993 hashCode = 31 * hashCode + mBias; 994 hashCode = 31 * hashCode + mPriority; 995 hashCode = 31 * hashCode + flags; 996 if (mDebugTags.size() > 0) { 997 hashCode = 31 * hashCode + mDebugTags.hashCode(); 998 } 999 if (mTraceTag != null) { 1000 hashCode = 31 * hashCode + mTraceTag.hashCode(); 1001 } 1002 return hashCode; 1003 } 1004 1005 @SuppressWarnings("UnsafeParcelApi") JobInfo(Parcel in)1006 private JobInfo(Parcel in) { 1007 jobId = in.readInt(); 1008 final PersistableBundle persistableExtras = in.readPersistableBundle(); 1009 extras = persistableExtras != null ? persistableExtras : PersistableBundle.EMPTY; 1010 transientExtras = in.readBundle(); 1011 if (in.readInt() != 0) { 1012 clipData = ClipData.CREATOR.createFromParcel(in); 1013 clipGrantFlags = in.readInt(); 1014 } else { 1015 clipData = null; 1016 clipGrantFlags = 0; 1017 } 1018 service = in.readParcelable(null); 1019 constraintFlags = in.readInt(); 1020 triggerContentUris = in.createTypedArray(TriggerContentUri.CREATOR); 1021 triggerContentUpdateDelay = in.readLong(); 1022 triggerContentMaxDelay = in.readLong(); 1023 if (in.readInt() != 0) { 1024 networkRequest = NetworkRequest.CREATOR.createFromParcel(in); 1025 } else { 1026 networkRequest = null; 1027 } 1028 networkDownloadBytes = in.readLong(); 1029 networkUploadBytes = in.readLong(); 1030 minimumNetworkChunkBytes = in.readLong(); 1031 minLatencyMillis = in.readLong(); 1032 maxExecutionDelayMillis = in.readLong(); 1033 isPeriodic = in.readInt() == 1; 1034 isPersisted = in.readInt() == 1; 1035 intervalMillis = in.readLong(); 1036 flexMillis = in.readLong(); 1037 initialBackoffMillis = in.readLong(); 1038 backoffPolicy = in.readInt(); 1039 hasEarlyConstraint = in.readInt() == 1; 1040 hasLateConstraint = in.readInt() == 1; 1041 mBias = in.readInt(); 1042 mPriority = in.readInt(); 1043 flags = in.readInt(); 1044 final int numDebugTags = in.readInt(); 1045 mDebugTags = new ArraySet<>(); 1046 for (int i = 0; i < numDebugTags; ++i) { 1047 final String tag = in.readString(); 1048 if (tag == null) { 1049 throw new IllegalStateException("malformed parcel"); 1050 } 1051 mDebugTags.add(tag.intern()); 1052 } 1053 final String traceTag = in.readString(); 1054 mTraceTag = traceTag == null ? null : traceTag.intern(); 1055 } 1056 JobInfo(JobInfo.Builder b)1057 private JobInfo(JobInfo.Builder b) { 1058 jobId = b.mJobId; 1059 extras = b.mExtras.deepCopy(); 1060 transientExtras = b.mTransientExtras.deepCopy(); 1061 clipData = b.mClipData; 1062 clipGrantFlags = b.mClipGrantFlags; 1063 service = b.mJobService; 1064 constraintFlags = b.mConstraintFlags; 1065 triggerContentUris = b.mTriggerContentUris != null 1066 ? b.mTriggerContentUris.toArray(new TriggerContentUri[b.mTriggerContentUris.size()]) 1067 : null; 1068 triggerContentUpdateDelay = b.mTriggerContentUpdateDelay; 1069 triggerContentMaxDelay = b.mTriggerContentMaxDelay; 1070 networkRequest = b.mNetworkRequest; 1071 networkDownloadBytes = b.mNetworkDownloadBytes; 1072 networkUploadBytes = b.mNetworkUploadBytes; 1073 minimumNetworkChunkBytes = b.mMinimumNetworkChunkBytes; 1074 minLatencyMillis = b.mMinLatencyMillis; 1075 maxExecutionDelayMillis = b.mMaxExecutionDelayMillis; 1076 isPeriodic = b.mIsPeriodic; 1077 isPersisted = b.mIsPersisted; 1078 intervalMillis = b.mIntervalMillis; 1079 flexMillis = b.mFlexMillis; 1080 initialBackoffMillis = b.mInitialBackoffMillis; 1081 backoffPolicy = b.mBackoffPolicy; 1082 hasEarlyConstraint = b.mHasEarlyConstraint; 1083 hasLateConstraint = b.mHasLateConstraint; 1084 mBias = b.mBias; 1085 mPriority = b.mPriority; 1086 flags = b.mFlags; 1087 mDebugTags = b.mDebugTags; 1088 mTraceTag = b.mTraceTag; 1089 } 1090 1091 @Override describeContents()1092 public int describeContents() { 1093 return 0; 1094 } 1095 1096 @Override writeToParcel(Parcel out, int flags)1097 public void writeToParcel(Parcel out, int flags) { 1098 out.writeInt(jobId); 1099 out.writePersistableBundle(extras); 1100 out.writeBundle(transientExtras); 1101 if (clipData != null) { 1102 out.writeInt(1); 1103 clipData.writeToParcel(out, flags); 1104 out.writeInt(clipGrantFlags); 1105 } else { 1106 out.writeInt(0); 1107 } 1108 out.writeParcelable(service, flags); 1109 out.writeInt(constraintFlags); 1110 out.writeTypedArray(triggerContentUris, flags); 1111 out.writeLong(triggerContentUpdateDelay); 1112 out.writeLong(triggerContentMaxDelay); 1113 if (networkRequest != null) { 1114 out.writeInt(1); 1115 networkRequest.writeToParcel(out, flags); 1116 } else { 1117 out.writeInt(0); 1118 } 1119 out.writeLong(networkDownloadBytes); 1120 out.writeLong(networkUploadBytes); 1121 out.writeLong(minimumNetworkChunkBytes); 1122 out.writeLong(minLatencyMillis); 1123 out.writeLong(maxExecutionDelayMillis); 1124 out.writeInt(isPeriodic ? 1 : 0); 1125 out.writeInt(isPersisted ? 1 : 0); 1126 out.writeLong(intervalMillis); 1127 out.writeLong(flexMillis); 1128 out.writeLong(initialBackoffMillis); 1129 out.writeInt(backoffPolicy); 1130 out.writeInt(hasEarlyConstraint ? 1 : 0); 1131 out.writeInt(hasLateConstraint ? 1 : 0); 1132 out.writeInt(mBias); 1133 out.writeInt(mPriority); 1134 out.writeInt(this.flags); 1135 // Explicitly write out values here to avoid double looping to intern the strings 1136 // when unparcelling. 1137 final int numDebugTags = mDebugTags.size(); 1138 out.writeInt(numDebugTags); 1139 for (int i = 0; i < numDebugTags; ++i) { 1140 out.writeString(mDebugTags.valueAt(i)); 1141 } 1142 out.writeString(mTraceTag); 1143 } 1144 1145 public static final @android.annotation.NonNull Creator<JobInfo> CREATOR = new Creator<JobInfo>() { 1146 @Override 1147 public JobInfo createFromParcel(Parcel in) { 1148 return new JobInfo(in); 1149 } 1150 1151 @Override 1152 public JobInfo[] newArray(int size) { 1153 return new JobInfo[size]; 1154 } 1155 }; 1156 1157 @Override toString()1158 public String toString() { 1159 return "(job:" + jobId + "/" + service.flattenToShortString() + ")"; 1160 } 1161 1162 /** 1163 * Information about a content URI modification that a job would like to 1164 * trigger on. 1165 */ 1166 public static final class TriggerContentUri implements Parcelable { 1167 private final Uri mUri; 1168 private final int mFlags; 1169 1170 /** @hide */ 1171 @Retention(RetentionPolicy.SOURCE) 1172 @IntDef(flag = true, prefix = { "FLAG_" }, value = { 1173 FLAG_NOTIFY_FOR_DESCENDANTS, 1174 }) 1175 public @interface Flags { } 1176 1177 /** 1178 * Flag for trigger: also trigger if any descendants of the given URI change. 1179 * Corresponds to the <var>notifyForDescendants</var> of 1180 * {@link android.content.ContentResolver#registerContentObserver}. 1181 */ 1182 public static final int FLAG_NOTIFY_FOR_DESCENDANTS = 1<<0; 1183 1184 /** 1185 * Create a new trigger description. 1186 * @param uri The URI to observe. Must be non-null. 1187 * @param flags Flags for the observer. 1188 */ TriggerContentUri(@onNull Uri uri, @Flags int flags)1189 public TriggerContentUri(@NonNull Uri uri, @Flags int flags) { 1190 mUri = Objects.requireNonNull(uri); 1191 mFlags = flags; 1192 } 1193 1194 /** 1195 * Return the Uri this trigger was created for. 1196 */ getUri()1197 public Uri getUri() { 1198 return mUri; 1199 } 1200 1201 /** 1202 * Return the flags supplied for the trigger. 1203 */ getFlags()1204 public @Flags int getFlags() { 1205 return mFlags; 1206 } 1207 1208 @Override equals(Object o)1209 public boolean equals(Object o) { 1210 if (!(o instanceof TriggerContentUri)) { 1211 return false; 1212 } 1213 TriggerContentUri t = (TriggerContentUri) o; 1214 return Objects.equals(t.mUri, mUri) && t.mFlags == mFlags; 1215 } 1216 1217 @Override hashCode()1218 public int hashCode() { 1219 return (mUri == null ? 0 : mUri.hashCode()) ^ mFlags; 1220 } 1221 TriggerContentUri(Parcel in)1222 private TriggerContentUri(Parcel in) { 1223 mUri = Uri.CREATOR.createFromParcel(in); 1224 mFlags = in.readInt(); 1225 } 1226 1227 @Override describeContents()1228 public int describeContents() { 1229 return 0; 1230 } 1231 1232 @Override writeToParcel(Parcel out, int flags)1233 public void writeToParcel(Parcel out, int flags) { 1234 mUri.writeToParcel(out, flags); 1235 out.writeInt(mFlags); 1236 } 1237 1238 public static final @android.annotation.NonNull Creator<TriggerContentUri> CREATOR = new Creator<TriggerContentUri>() { 1239 @Override 1240 public TriggerContentUri createFromParcel(Parcel in) { 1241 return new TriggerContentUri(in); 1242 } 1243 1244 @Override 1245 public TriggerContentUri[] newArray(int size) { 1246 return new TriggerContentUri[size]; 1247 } 1248 }; 1249 } 1250 1251 /** Builder class for constructing {@link JobInfo} objects. */ 1252 public static final class Builder { 1253 private final int mJobId; 1254 private final ComponentName mJobService; 1255 private PersistableBundle mExtras = PersistableBundle.EMPTY; 1256 private Bundle mTransientExtras = Bundle.EMPTY; 1257 private ClipData mClipData; 1258 private int mClipGrantFlags; 1259 private int mBias = BIAS_DEFAULT; 1260 @Priority 1261 private int mPriority = PRIORITY_DEFAULT; 1262 private int mFlags; 1263 // Requirements. 1264 private int mConstraintFlags; 1265 private NetworkRequest mNetworkRequest; 1266 private long mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN; 1267 private long mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN; 1268 private long mMinimumNetworkChunkBytes = NETWORK_BYTES_UNKNOWN; 1269 private ArrayList<TriggerContentUri> mTriggerContentUris; 1270 private long mTriggerContentUpdateDelay = -1; 1271 private long mTriggerContentMaxDelay = -1; 1272 private boolean mIsPersisted; 1273 // One-off parameters. 1274 private long mMinLatencyMillis; 1275 private long mMaxExecutionDelayMillis; 1276 // Periodic parameters. 1277 private boolean mIsPeriodic; 1278 private boolean mHasEarlyConstraint; 1279 private boolean mHasLateConstraint; 1280 private long mIntervalMillis; 1281 private long mFlexMillis; 1282 // Back-off parameters. 1283 private long mInitialBackoffMillis = DEFAULT_INITIAL_BACKOFF_MILLIS; 1284 private int mBackoffPolicy = DEFAULT_BACKOFF_POLICY; 1285 /** Easy way to track whether the client has tried to set a back-off policy. */ 1286 private boolean mBackoffPolicySet = false; 1287 private final ArraySet<String> mDebugTags = new ArraySet<>(); 1288 private String mTraceTag; 1289 1290 /** 1291 * Initialize a new Builder to construct a {@link JobInfo}. 1292 * 1293 * @param jobId Application-provided id for this job. Subsequent calls to cancel, or 1294 * jobs created with the same jobId, will update the pre-existing job with 1295 * the same id. This ID must be unique across all clients of the same uid 1296 * (not just the same package). You will want to make sure this is a stable 1297 * id across app updates, so probably not based on a resource ID. 1298 * @param jobService The endpoint that you implement that will receive the callback from the 1299 * JobScheduler. 1300 */ Builder(int jobId, @NonNull ComponentName jobService)1301 public Builder(int jobId, @NonNull ComponentName jobService) { 1302 mJobService = jobService; 1303 mJobId = jobId; 1304 } 1305 1306 /** 1307 * Creates a new Builder of JobInfo from an existing instance. 1308 * @hide 1309 */ Builder(@onNull JobInfo job)1310 public Builder(@NonNull JobInfo job) { 1311 mJobId = job.getId(); 1312 mJobService = job.getService(); 1313 mExtras = job.getExtras(); 1314 mTransientExtras = job.getTransientExtras(); 1315 mClipData = job.getClipData(); 1316 mClipGrantFlags = job.getClipGrantFlags(); 1317 mBias = job.getBias(); 1318 mFlags = job.getFlags(); 1319 mConstraintFlags = job.getConstraintFlags(); 1320 mNetworkRequest = job.getRequiredNetwork(); 1321 mNetworkDownloadBytes = job.getEstimatedNetworkDownloadBytes(); 1322 mNetworkUploadBytes = job.getEstimatedNetworkUploadBytes(); 1323 mMinimumNetworkChunkBytes = job.getMinimumNetworkChunkBytes(); 1324 mTriggerContentUris = job.getTriggerContentUris() != null 1325 ? new ArrayList<>(Arrays.asList(job.getTriggerContentUris())) : null; 1326 mTriggerContentUpdateDelay = job.getTriggerContentUpdateDelay(); 1327 mTriggerContentMaxDelay = job.getTriggerContentMaxDelay(); 1328 mIsPersisted = job.isPersisted(); 1329 mMinLatencyMillis = job.getMinLatencyMillis(); 1330 mMaxExecutionDelayMillis = job.getMaxExecutionDelayMillis(); 1331 mIsPeriodic = job.isPeriodic(); 1332 mHasEarlyConstraint = job.hasEarlyConstraint(); 1333 mHasLateConstraint = job.hasLateConstraint(); 1334 mIntervalMillis = job.getIntervalMillis(); 1335 mFlexMillis = job.getFlexMillis(); 1336 mInitialBackoffMillis = job.getInitialBackoffMillis(); 1337 // mBackoffPolicySet isn't set but it's fine since this is copying from an already valid 1338 // job. 1339 mBackoffPolicy = job.getBackoffPolicy(); 1340 mPriority = job.getPriority(); 1341 } 1342 1343 /** 1344 * Add a debug tag to help track what this job is for. The tags may show in debug dumps 1345 * or app metrics. Do not put personally identifiable information (PII) in the tag. 1346 * <p> 1347 * Tags have the following requirements: 1348 * <ul> 1349 * <li>Tags cannot be more than 127 characters.</li> 1350 * <li> 1351 * Since leading and trailing whitespace can lead to hard-to-debug issues, 1352 * tags should not include leading or trailing whitespace. 1353 * All tags will be {@link String#trim() trimmed}. 1354 * </li> 1355 * <li>An empty String (after trimming) is not allowed.</li> 1356 * <li>Should not have personally identifiable information (PII).</li> 1357 * <li>A job cannot have more than 32 tags.</li> 1358 * </ul> 1359 * 1360 * @param tag A debug tag that helps describe what the job is for. 1361 * @return This object for method chaining 1362 */ 1363 @FlaggedApi(Flags.FLAG_JOB_DEBUG_INFO_APIS) 1364 @NonNull addDebugTag(@onNull String tag)1365 public Builder addDebugTag(@NonNull String tag) { 1366 mDebugTags.add(validateDebugTag(tag)); 1367 return this; 1368 } 1369 1370 /** @hide */ 1371 @NonNull addDebugTags(@onNull Set<String> tags)1372 public void addDebugTags(@NonNull Set<String> tags) { 1373 mDebugTags.addAll(tags); 1374 } 1375 1376 /** 1377 * Remove a tag set via {@link #addDebugTag(String)}. 1378 * @param tag The tag to remove 1379 * @return This object for method chaining 1380 */ 1381 @FlaggedApi(Flags.FLAG_JOB_DEBUG_INFO_APIS) 1382 @SuppressLint("BuilderSetStyle") 1383 @NonNull removeDebugTag(@onNull String tag)1384 public Builder removeDebugTag(@NonNull String tag) { 1385 mDebugTags.remove(tag); 1386 return this; 1387 } 1388 1389 /** @hide */ 1390 @NonNull 1391 @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) setBias(int bias)1392 public Builder setBias(int bias) { 1393 mBias = bias; 1394 return this; 1395 } 1396 1397 /** 1398 * Indicate the priority for this job. The priority set here will be used to sort jobs 1399 * for the calling app and apply slightly different policies based on the priority. 1400 * The priority will <b>NOT</b> be used as a global sorting value to sort between 1401 * different app's jobs. Use this to inform the system about which jobs it should try 1402 * to run before other jobs. Giving the same priority to all of your jobs will result 1403 * in them all being treated the same. The priorities each have slightly different 1404 * behaviors, as noted in their relevant javadoc. 1405 * 1406 * Starting in Android version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, 1407 * the priority will only affect sorting order within the job's namespace. 1408 * 1409 * <b>NOTE:</b> Setting all of your jobs to high priority will not be 1410 * beneficial to your app and in fact may hurt its performance in the 1411 * long run. 1412 * 1413 * In order to prevent starvation, repeatedly retried jobs (because of failures) will slowly 1414 * have their priorities lowered. 1415 * 1416 * @see JobInfo#getPriority() 1417 */ 1418 @NonNull setPriority(@riority int priority)1419 public Builder setPriority(@Priority int priority) { 1420 if (priority > PRIORITY_MAX || priority < PRIORITY_MIN) { 1421 if (Compatibility.isChangeEnabled(THROW_ON_INVALID_PRIORITY_VALUE)) { 1422 throw new IllegalArgumentException("Invalid priority value"); 1423 } 1424 // No-op for invalid calls of apps that are targeting S-. This was an unsupported 1425 // API before Tiramisu, so anyone calling this that isn't targeting T isn't 1426 // guaranteed a behavior change. 1427 return this; 1428 } 1429 mPriority = priority; 1430 return this; 1431 } 1432 1433 /** @hide */ 1434 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) setFlags(int flags)1435 public Builder setFlags(int flags) { 1436 mFlags = flags; 1437 return this; 1438 } 1439 1440 /** 1441 * Set optional extras. This is persisted, so we only allow primitive types. 1442 * @param extras Bundle containing extras you want the scheduler to hold on to for you. 1443 * @see JobInfo#getExtras() 1444 */ setExtras(@onNull PersistableBundle extras)1445 public Builder setExtras(@NonNull PersistableBundle extras) { 1446 mExtras = extras; 1447 return this; 1448 } 1449 1450 /** 1451 * Set optional transient extras. 1452 * 1453 * <p>Because setting this property is not compatible with persisted 1454 * jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when 1455 * {@link android.app.job.JobInfo.Builder#build()} is called.</p> 1456 * 1457 * @param extras Bundle containing extras you want the scheduler to hold on to for you. 1458 * @see JobInfo#getTransientExtras() 1459 */ setTransientExtras(@onNull Bundle extras)1460 public Builder setTransientExtras(@NonNull Bundle extras) { 1461 mTransientExtras = extras; 1462 return this; 1463 } 1464 1465 /** 1466 * Set a {@link ClipData} associated with this Job. 1467 * 1468 * <p>The main purpose of providing a ClipData is to allow granting of 1469 * URI permissions for data associated with the clip. The exact kind 1470 * of permission grant to perform is specified through <var>grantFlags</var>. 1471 * 1472 * <p>If the ClipData contains items that are Intents, any 1473 * grant flags in those Intents will be ignored. Only flags provided as an argument 1474 * to this method are respected, and will be applied to all Uri or 1475 * Intent items in the clip (or sub-items of the clip). 1476 * 1477 * <p>Because setting this property is not compatible with persisted 1478 * jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when 1479 * {@link android.app.job.JobInfo.Builder#build()} is called.</p> 1480 * 1481 * @param clip The new clip to set. May be null to clear the current clip. 1482 * @param grantFlags The desired permissions to grant for any URIs. This should be 1483 * a combination of {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION}, 1484 * {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, and 1485 * {@link android.content.Intent#FLAG_GRANT_PREFIX_URI_PERMISSION}. 1486 * @see JobInfo#getClipData() 1487 * @see JobInfo#getClipGrantFlags() 1488 */ setClipData(@ullable ClipData clip, int grantFlags)1489 public Builder setClipData(@Nullable ClipData clip, int grantFlags) { 1490 mClipData = clip; 1491 mClipGrantFlags = grantFlags; 1492 return this; 1493 } 1494 1495 /** 1496 * Set basic description of the kind of network your job requires. If 1497 * you need more precise control over network capabilities, see 1498 * {@link #setRequiredNetwork(NetworkRequest)}. 1499 * <p> 1500 * If your job doesn't need a network connection, you don't need to call 1501 * this method, as the default value is {@link #NETWORK_TYPE_NONE}. 1502 * <p> 1503 * Calling this method defines network as a strict requirement for your 1504 * job. If the network requested is not available your job will never 1505 * run. See {@link #setOverrideDeadline(long)} to change this behavior. 1506 * Calling this method will override any requirements previously defined 1507 * by {@link #setRequiredNetwork(NetworkRequest)}; you typically only 1508 * want to call one of these methods. 1509 * 1510 * Starting in Android version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, 1511 * an app must hold the 1512 * {@link android.Manifest.permission#ACCESS_NETWORK_STATE} permission to 1513 * schedule a job that requires a network. 1514 * 1515 * <p> Starting in Android version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, 1516 * {@link JobScheduler} may try to shift the execution of jobs requiring 1517 * {@link #NETWORK_TYPE_ANY} to when there is access to an un-metered network. 1518 * 1519 * <p class="note"> 1520 * When your job executes in 1521 * {@link JobService#onStartJob(JobParameters)}, be sure to use the 1522 * specific network returned by {@link JobParameters#getNetwork()}, 1523 * otherwise you'll use the default network which may not meet this 1524 * constraint. 1525 * 1526 * @see #setRequiredNetwork(NetworkRequest) 1527 * @see JobInfo#getNetworkType() 1528 * @see JobParameters#getNetwork() 1529 */ setRequiredNetworkType(@etworkType int networkType)1530 public Builder setRequiredNetworkType(@NetworkType int networkType) { 1531 if (networkType == NETWORK_TYPE_NONE) { 1532 return setRequiredNetwork(null); 1533 } else { 1534 final NetworkRequest.Builder builder = new NetworkRequest.Builder(); 1535 1536 // All types require validated Internet 1537 builder.addCapability(NET_CAPABILITY_INTERNET); 1538 builder.addCapability(NET_CAPABILITY_VALIDATED); 1539 builder.removeCapability(NET_CAPABILITY_NOT_VPN); 1540 builder.removeCapability(NET_CAPABILITY_NOT_RESTRICTED); 1541 1542 if (networkType == NETWORK_TYPE_ANY) { 1543 // No other capabilities 1544 } else if (networkType == NETWORK_TYPE_UNMETERED) { 1545 builder.addCapability(NET_CAPABILITY_NOT_METERED); 1546 } else if (networkType == NETWORK_TYPE_NOT_ROAMING) { 1547 builder.addCapability(NET_CAPABILITY_NOT_ROAMING); 1548 } else if (networkType == NETWORK_TYPE_CELLULAR) { 1549 builder.addTransportType(TRANSPORT_CELLULAR); 1550 } 1551 1552 return setRequiredNetwork(builder.build()); 1553 } 1554 } 1555 1556 /** 1557 * Set detailed description of the kind of network your job requires. 1558 * <p> 1559 * If your job doesn't need a network connection, you don't need to call 1560 * this method, as the default is {@code null}. 1561 * <p> 1562 * Calling this method defines network as a strict requirement for your 1563 * job. If the network requested is not available your job will never 1564 * run. See {@link #setOverrideDeadline(long)} to change this behavior. 1565 * Calling this method will override any requirements previously defined 1566 * by {@link #setRequiredNetworkType(int)}; you typically only want to 1567 * call one of these methods. 1568 * <p class="note"> 1569 * When your job executes in 1570 * {@link JobService#onStartJob(JobParameters)}, be sure to use the 1571 * specific network returned by {@link JobParameters#getNetwork()}, 1572 * otherwise you'll use the default network which may not meet this 1573 * constraint. 1574 * 1575 * Starting in Android version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, 1576 * an app must hold the 1577 * {@link android.Manifest.permission#ACCESS_NETWORK_STATE} permission to 1578 * schedule a job that requires a network. 1579 * 1580 * @param networkRequest The detailed description of the kind of network 1581 * this job requires, or {@code null} if no specific kind of 1582 * network is required. Defining a {@link NetworkSpecifier} 1583 * is only supported for jobs that aren't persisted. 1584 * @see #setRequiredNetworkType(int) 1585 * @see JobInfo#getRequiredNetwork() 1586 * @see JobParameters#getNetwork() 1587 */ setRequiredNetwork(@ullable NetworkRequest networkRequest)1588 public Builder setRequiredNetwork(@Nullable NetworkRequest networkRequest) { 1589 mNetworkRequest = networkRequest; 1590 return this; 1591 } 1592 1593 /** 1594 * Set the estimated size of network traffic that will be performed by 1595 * this job, in bytes. 1596 * <p> 1597 * Apps are encouraged to provide values that are as accurate as 1598 * possible, but when the exact size isn't available, an 1599 * order-of-magnitude estimate can be provided instead. Here are some 1600 * specific examples: 1601 * <ul> 1602 * <li>A job that is backing up a photo knows the exact size of that 1603 * photo, so it should provide that size as the estimate. 1604 * <li>A job that refreshes top news stories wouldn't know an exact 1605 * size, but if the size is expected to be consistently around 100KB, it 1606 * can provide that order-of-magnitude value as the estimate. 1607 * <li>A job that synchronizes email could end up using an extreme range 1608 * of data, from under 1KB when nothing has changed, to dozens of MB 1609 * when there are new emails with attachments. Jobs that cannot provide 1610 * reasonable estimates should use the sentinel value 1611 * {@link JobInfo#NETWORK_BYTES_UNKNOWN}. 1612 * </ul> 1613 * Note that the system may choose to delay jobs with large network 1614 * usage estimates when the device has a poor network connection, in 1615 * order to save battery and possible network costs. 1616 * Starting from Android version {@link Build.VERSION_CODES#S}, JobScheduler may attempt 1617 * to run large jobs when the device is charging and on an unmetered network, even if the 1618 * network is slow. This gives large jobs an opportunity to make forward progress, even if 1619 * they risk timing out. 1620 * <p> 1621 * The values provided here only reflect the traffic that will be 1622 * performed by the base job; if you're using {@link JobWorkItem} then 1623 * you also need to define the network traffic used by each work item 1624 * when constructing them. 1625 * 1626 * <p class="note"> 1627 * Prior to Android version {@link Build.VERSION_CODES#TIRAMISU}, JobScheduler used the 1628 * estimated transfer numbers in a similar fashion to 1629 * {@link #setMinimumNetworkChunkBytes(long)} (to estimate if the work would complete 1630 * within the time available to job). In other words, JobScheduler treated the transfer as 1631 * all-or-nothing. Starting from Android version {@link Build.VERSION_CODES#TIRAMISU}, 1632 * JobScheduler will only use the estimated transfer numbers in this manner if minimum 1633 * chunk sizes have not been provided via {@link #setMinimumNetworkChunkBytes(long)}. 1634 * 1635 * @param downloadBytes The estimated size of network traffic that will 1636 * be downloaded by this job, in bytes. 1637 * @param uploadBytes The estimated size of network traffic that will be 1638 * uploaded by this job, in bytes. 1639 * @see JobInfo#getEstimatedNetworkDownloadBytes() 1640 * @see JobInfo#getEstimatedNetworkUploadBytes() 1641 * @see JobWorkItem#JobWorkItem(android.content.Intent, long, long) 1642 */ 1643 // TODO(b/255371817): update documentation to reflect how this data will be used setEstimatedNetworkBytes(@ytesLong long downloadBytes, @BytesLong long uploadBytes)1644 public Builder setEstimatedNetworkBytes(@BytesLong long downloadBytes, 1645 @BytesLong long uploadBytes) { 1646 mNetworkDownloadBytes = downloadBytes; 1647 mNetworkUploadBytes = uploadBytes; 1648 return this; 1649 } 1650 1651 /** 1652 * Set the minimum size of non-resumable network traffic this job requires, in bytes. When 1653 * the upload or download can be easily paused and resumed, use this to set the smallest 1654 * size that must be transmitted between start and stop events to be considered successful. 1655 * If the transfer cannot be paused and resumed, then this should be the sum of the values 1656 * provided to {@link JobInfo.Builder#setEstimatedNetworkBytes(long, long)}. 1657 * 1658 * <p> 1659 * Apps are encouraged to provide values that are as accurate as possible since JobScheduler 1660 * will try to run the job at a time when at least the minimum chunk can be transmitted to 1661 * reduce the amount of repetitive data that's transferred. Jobs that cannot provide 1662 * reasonable estimates should use the sentinel value {@link JobInfo#NETWORK_BYTES_UNKNOWN}. 1663 * 1664 * <p> 1665 * The values provided here only reflect the minimum non-resumable traffic that will be 1666 * performed by the base job; if you're using {@link JobWorkItem} then 1667 * you also need to define the network traffic used by each work item 1668 * when constructing them. 1669 * 1670 * @param chunkSizeBytes The smallest piece of data that cannot be easily paused and 1671 * resumed, in bytes. 1672 * @see JobInfo#getMinimumNetworkChunkBytes() 1673 * @see JobWorkItem#JobWorkItem(android.content.Intent, long, long, long) 1674 */ 1675 @NonNull setMinimumNetworkChunkBytes(@ytesLong long chunkSizeBytes)1676 public Builder setMinimumNetworkChunkBytes(@BytesLong long chunkSizeBytes) { 1677 if (chunkSizeBytes != NETWORK_BYTES_UNKNOWN && chunkSizeBytes <= 0) { 1678 throw new IllegalArgumentException("Minimum chunk size must be positive"); 1679 } 1680 mMinimumNetworkChunkBytes = chunkSizeBytes; 1681 return this; 1682 } 1683 1684 /** 1685 * Specify that to run this job, the device must be charging (or be a 1686 * non-battery-powered device connected to permanent power, such as Android TV 1687 * devices). This defaults to {@code false}. Setting this to {@code false} <b>DOES NOT</b> 1688 * mean the job will only run when the device is not charging. 1689 * 1690 * <p class="note">For purposes of running jobs, a battery-powered device 1691 * "charging" is not quite the same as simply being connected to power. If the 1692 * device is so busy that the battery is draining despite a power connection, jobs 1693 * with this constraint will <em>not</em> run. This can happen during some 1694 * common use cases such as video chat, particularly if the device is plugged in 1695 * to USB rather than to wall power. 1696 * 1697 * @param requiresCharging Pass {@code true} to require that the device be 1698 * charging in order to run the job. 1699 * @see JobInfo#isRequireCharging() 1700 */ setRequiresCharging(boolean requiresCharging)1701 public Builder setRequiresCharging(boolean requiresCharging) { 1702 mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_CHARGING) 1703 | (requiresCharging ? CONSTRAINT_FLAG_CHARGING : 0); 1704 return this; 1705 } 1706 1707 /** 1708 * Specify that to run this job, the device's battery level must not be low. 1709 * This defaults to false. If true, the job will only run when the battery level 1710 * is not low, which is generally the point where the user is given a "low battery" 1711 * warning. Setting this to {@code false} <b>DOES NOT</b> mean the job will only run 1712 * when the battery is low. 1713 * 1714 * @param batteryNotLow Whether or not the device's battery level must not be low. 1715 * @see JobInfo#isRequireBatteryNotLow() 1716 */ setRequiresBatteryNotLow(boolean batteryNotLow)1717 public Builder setRequiresBatteryNotLow(boolean batteryNotLow) { 1718 mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_BATTERY_NOT_LOW) 1719 | (batteryNotLow ? CONSTRAINT_FLAG_BATTERY_NOT_LOW : 0); 1720 return this; 1721 } 1722 1723 /** 1724 * When set {@code true}, ensure that this job will not run if the device is in active use. 1725 * The default state is {@code false}: that is, the for the job to be runnable even when 1726 * someone is interacting with the device. Setting this to {@code false} <b>DOES NOT</b> 1727 * mean the job will only run when the device is not idle. 1728 * 1729 * <p>This state is a loose definition provided by the system. In general, it means that 1730 * the device is not currently being used interactively, and has not been in use for some 1731 * time. As such, it is a good time to perform resource heavy jobs. Bear in mind that 1732 * battery usage will still be attributed to your application, and surfaced to the user in 1733 * battery stats.</p> 1734 * 1735 * <p class="note">Despite the similar naming, this job constraint is <em>not</em> 1736 * related to the system's "device idle" or "doze" states. This constraint only 1737 * determines whether a job is allowed to run while the device is directly in use. 1738 * 1739 * @param requiresDeviceIdle Pass {@code true} to prevent the job from running 1740 * while the device is being used interactively. 1741 * @see JobInfo#isRequireDeviceIdle() 1742 */ setRequiresDeviceIdle(boolean requiresDeviceIdle)1743 public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) { 1744 mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_DEVICE_IDLE) 1745 | (requiresDeviceIdle ? CONSTRAINT_FLAG_DEVICE_IDLE : 0); 1746 return this; 1747 } 1748 1749 /** 1750 * Specify that to run this job, the device's available storage must not be low. 1751 * This defaults to false. If true, the job will only run when the device is not 1752 * in a low storage state, which is generally the point where the user is given a 1753 * "low storage" warning. 1754 * @param storageNotLow Whether or not the device's available storage must not be low. 1755 * @see JobInfo#isRequireStorageNotLow() 1756 */ setRequiresStorageNotLow(boolean storageNotLow)1757 public Builder setRequiresStorageNotLow(boolean storageNotLow) { 1758 mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_STORAGE_NOT_LOW) 1759 | (storageNotLow ? CONSTRAINT_FLAG_STORAGE_NOT_LOW : 0); 1760 return this; 1761 } 1762 1763 /** 1764 * Add a new content: URI that will be monitored with a 1765 * {@link android.database.ContentObserver}, and will cause the job to execute if changed. 1766 * If you have any trigger content URIs associated with a job, it will not execute until 1767 * there has been a change report for one or more of them. 1768 * 1769 * <p>Note that trigger URIs can not be used in combination with 1770 * {@link #setPeriodic(long)} or {@link #setPersisted(boolean)}. To continually monitor 1771 * for content changes, you need to schedule a new JobInfo using the same job ID and 1772 * observing the same URIs in place of calling 1773 * {@link JobService#jobFinished(JobParameters, boolean)}. Remember that 1774 * {@link JobScheduler#schedule(JobInfo)} stops a running job if it uses the same job ID, 1775 * so only call it after you've finished processing the most recent changes (in other words, 1776 * call {@link JobScheduler#schedule(JobInfo)} where you would have normally called 1777 * {@link JobService#jobFinished(JobParameters, boolean)}. 1778 * Following this pattern will ensure you do not lose any content changes: while your 1779 * job is running, the system will continue monitoring for content changes, and propagate 1780 * any changes it sees over to the next job you schedule, so you do not have to worry 1781 * about missing new changes. <b>Scheduling the new job 1782 * before or during processing will cause the current job to be stopped (as described in 1783 * {@link JobScheduler#schedule(JobInfo)}), meaning the wakelock will be released for the 1784 * current job and your app process may be killed since it will no longer be in a valid 1785 * component lifecycle.</b> 1786 * Since {@link JobScheduler#schedule(JobInfo)} stops the current job, you do not 1787 * need to call {@link JobService#jobFinished(JobParameters, boolean)} if you call 1788 * {@link JobScheduler#schedule(JobInfo)} using the same job ID as the 1789 * currently running job.</p> 1790 * 1791 * <p>Because setting this property is not compatible with periodic or 1792 * persisted jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when 1793 * {@link android.app.job.JobInfo.Builder#build()} is called.</p> 1794 * 1795 * <p>The following example shows how this feature can be used to monitor for changes 1796 * in the photos on a device.</p> 1797 * 1798 * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/PhotosContentJob.java 1799 * job} 1800 * 1801 * @param uri The content: URI to monitor. 1802 * @see JobInfo#getTriggerContentUris() 1803 */ addTriggerContentUri(@onNull TriggerContentUri uri)1804 public Builder addTriggerContentUri(@NonNull TriggerContentUri uri) { 1805 if (mTriggerContentUris == null) { 1806 mTriggerContentUris = new ArrayList<>(); 1807 } 1808 mTriggerContentUris.add(uri); 1809 return this; 1810 } 1811 1812 /** 1813 * Set the delay (in milliseconds) from when a content change is detected until 1814 * the job is scheduled. If there are more changes during that time, the delay 1815 * will be reset to start at the time of the most recent change. 1816 * @param durationMs Delay after most recent content change, in milliseconds. 1817 * @see JobInfo#getTriggerContentUpdateDelay() 1818 */ setTriggerContentUpdateDelay(long durationMs)1819 public Builder setTriggerContentUpdateDelay(long durationMs) { 1820 mTriggerContentUpdateDelay = durationMs; 1821 return this; 1822 } 1823 1824 /** 1825 * Set the maximum total delay (in milliseconds) that is allowed from the first 1826 * time a content change is detected until the job is scheduled. 1827 * @param durationMs Delay after initial content change, in milliseconds. 1828 * @see JobInfo#getTriggerContentMaxDelay() 1829 */ setTriggerContentMaxDelay(long durationMs)1830 public Builder setTriggerContentMaxDelay(long durationMs) { 1831 mTriggerContentMaxDelay = durationMs; 1832 return this; 1833 } 1834 1835 /** 1836 * Specify that this job should recur with the provided interval, not more than once per 1837 * period. You have no control over when within this interval this job will be executed, 1838 * only the guarantee that it will be executed at most once within this interval, as long 1839 * as the constraints are satisfied. If the constraints are not satisfied within this 1840 * interval, the job will wait until the constraints are satisfied. 1841 * Setting this function on the builder with {@link #setMinimumLatency(long)} or 1842 * {@link #setOverrideDeadline(long)} will result in an error. 1843 * @param intervalMillis Millisecond interval for which this job will repeat. 1844 * @see JobInfo#getIntervalMillis() 1845 * @see JobInfo#getFlexMillis() 1846 */ setPeriodic(long intervalMillis)1847 public Builder setPeriodic(long intervalMillis) { 1848 return setPeriodic(intervalMillis, intervalMillis); 1849 } 1850 1851 /** 1852 * Specify that this job should recur with the provided interval and flex. The job can 1853 * execute at any time in a window of flex length at the end of the period. 1854 * If the constraints are not satisfied within the window, 1855 * the job will wait until the constraints are satisfied. 1856 * @param intervalMillis Millisecond interval for which this job will repeat. A minimum 1857 * value of {@link #getMinPeriodMillis()} is enforced. 1858 * @param flexMillis Millisecond flex for this job. Flex is clamped to be at least 1859 * {@link #getMinFlexMillis()} or 5 percent of the period, whichever is 1860 * higher. 1861 * @see JobInfo#getIntervalMillis() 1862 * @see JobInfo#getFlexMillis() 1863 */ setPeriodic(long intervalMillis, long flexMillis)1864 public Builder setPeriodic(long intervalMillis, long flexMillis) { 1865 final long minPeriod = getMinPeriodMillis(); 1866 if (intervalMillis < minPeriod) { 1867 Log.w(TAG, "Requested interval " + formatDuration(intervalMillis) + " for job " 1868 + mJobId + " is too small; raising to " + formatDuration(minPeriod)); 1869 intervalMillis = minPeriod; 1870 } 1871 1872 final long percentClamp = 5 * intervalMillis / 100; 1873 final long minFlex = Math.max(percentClamp, getMinFlexMillis()); 1874 if (flexMillis < minFlex) { 1875 Log.w(TAG, "Requested flex " + formatDuration(flexMillis) + " for job " + mJobId 1876 + " is too small; raising to " + formatDuration(minFlex)); 1877 flexMillis = minFlex; 1878 } 1879 1880 mIsPeriodic = true; 1881 mIntervalMillis = intervalMillis; 1882 mFlexMillis = flexMillis; 1883 mHasEarlyConstraint = mHasLateConstraint = true; 1884 return this; 1885 } 1886 1887 /** 1888 * Specify that this job should be delayed by the provided amount of time. The job may not 1889 * run the instant the delay has elapsed. JobScheduler will start the job at an 1890 * indeterminate time after the delay has elapsed. 1891 * <p> 1892 * Because it doesn't make sense setting this property on a periodic job, doing so will 1893 * throw an {@link java.lang.IllegalArgumentException} when 1894 * {@link android.app.job.JobInfo.Builder#build()} is called. 1895 * 1896 * Negative latencies also don't make sense for a job and are indicative of an error, 1897 * so starting in Android version {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, 1898 * setting a negative deadline will result in 1899 * {@link android.app.job.JobInfo.Builder#build()} throwing an 1900 * {@link java.lang.IllegalArgumentException}. 1901 * 1902 * @param minLatencyMillis Milliseconds before which this job will not be considered for 1903 * execution. 1904 * @see JobInfo#getMinLatencyMillis() 1905 */ setMinimumLatency(long minLatencyMillis)1906 public Builder setMinimumLatency(long minLatencyMillis) { 1907 mMinLatencyMillis = minLatencyMillis; 1908 mHasEarlyConstraint = true; 1909 return this; 1910 } 1911 1912 /** 1913 * Set a deadline after which all other functional requested constraints will be ignored. 1914 * After the deadline has passed, the job can run even if other requirements (including 1915 * a delay set through {@link #setMinimumLatency(long)}) are not met. 1916 * {@link JobParameters#isOverrideDeadlineExpired()} will return {@code true} if the job's 1917 * deadline has passed. The job's execution may be delayed beyond the set deadline by 1918 * other factors such as Doze mode and system health signals. 1919 * 1920 * <p> 1921 * Because it doesn't make sense setting this property on a periodic job, doing so will 1922 * throw an {@link java.lang.IllegalArgumentException} when 1923 * {@link android.app.job.JobInfo.Builder#build()} is called. 1924 * 1925 * <p> 1926 * Negative deadlines also don't make sense for a job and are indicative of an error, 1927 * so starting in Android version {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, 1928 * setting a negative deadline will result in 1929 * {@link android.app.job.JobInfo.Builder#build()} throwing an 1930 * {@link java.lang.IllegalArgumentException}. 1931 * 1932 * <p class="note"> 1933 * Since a job will run once the deadline has passed regardless of the status of other 1934 * constraints, setting a deadline of 0 (or a {@link #setMinimumLatency(long) delay} equal 1935 * to the deadline) with other constraints makes those constraints 1936 * meaningless when it comes to execution decisions. Since doing so is indicative of an 1937 * error in the logic, starting in Android version 1938 * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, jobs with extremely short 1939 * time windows will fail to build. Time windows are 1940 * defined as the time between a job's {@link #setMinimumLatency(long) minimum latency} 1941 * and its deadline. If the minimum latency is not set, it is assumed to be 0. 1942 * 1943 * Work that must happen immediately should use {@link #setExpedited(boolean)} or 1944 * {@link #setUserInitiated(boolean)} in the appropriate manner. 1945 * 1946 * <p> 1947 * This API aimed to guarantee execution of the job by the deadline only on Android version 1948 * {@link android.os.Build.VERSION_CODES#LOLLIPOP}. That aim and guarantee has not existed 1949 * since {@link android.os.Build.VERSION_CODES#M}. 1950 * 1951 * @see JobInfo#getMaxExecutionDelayMillis() 1952 */ setOverrideDeadline(long maxExecutionDelayMillis)1953 public Builder setOverrideDeadline(long maxExecutionDelayMillis) { 1954 mMaxExecutionDelayMillis = maxExecutionDelayMillis; 1955 mHasLateConstraint = true; 1956 return this; 1957 } 1958 1959 /** 1960 * Set up the back-off/retry policy. 1961 * This defaults to some respectable values: {30 seconds, Exponential}. We cap back-off at 1962 * 5hrs. 1963 * <p> 1964 * Note that trying to set a backoff criteria for a job with 1965 * {@link #setRequiresDeviceIdle(boolean)} will throw an exception when you call build(). 1966 * This is because back-off typically does not make sense for these types of jobs. See 1967 * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)} 1968 * for more description of the return value for the case of a job executing while in idle 1969 * mode. 1970 * @param initialBackoffMillis Millisecond time interval to wait initially when job has 1971 * failed. 1972 * @see JobInfo#getInitialBackoffMillis() 1973 * @see JobInfo#getBackoffPolicy() 1974 */ setBackoffCriteria(long initialBackoffMillis, @BackoffPolicy int backoffPolicy)1975 public Builder setBackoffCriteria(long initialBackoffMillis, 1976 @BackoffPolicy int backoffPolicy) { 1977 final long minBackoff = getMinBackoffMillis(); 1978 if (initialBackoffMillis < minBackoff) { 1979 Log.w(TAG, "Requested backoff " + formatDuration(initialBackoffMillis) + " for job " 1980 + mJobId + " is too small; raising to " + formatDuration(minBackoff)); 1981 initialBackoffMillis = minBackoff; 1982 } 1983 1984 mBackoffPolicySet = true; 1985 mInitialBackoffMillis = initialBackoffMillis; 1986 mBackoffPolicy = backoffPolicy; 1987 return this; 1988 } 1989 1990 /** 1991 * Setting this to true indicates that this job is important and needs to run as soon as 1992 * possible with stronger guarantees than regular jobs. These "expedited" jobs will: 1993 * <ol> 1994 * <li>Run as soon as possible</li> 1995 * <li>Be less restricted during Doze and battery saver</li> 1996 * <li> 1997 * Bypass Doze, app standby, and battery saver network restrictions (if the job 1998 * has a {@link #setRequiredNetwork(NetworkRequest) connectivity constraint}) 1999 * </li> 2000 * <li>Be less likely to be killed than regular jobs</li> 2001 * <li>Be subject to background location throttling</li> 2002 * <li>Be exempt from delay to optimize job execution</li> 2003 * </ol> 2004 * 2005 * <p> 2006 * Expedited jobs are given {@link #PRIORITY_MAX} by default. 2007 * 2008 * <p> 2009 * Since these jobs have stronger guarantees than regular jobs, they will be subject to 2010 * stricter quotas. As long as an app has available expedited quota, jobs scheduled with 2011 * this set to true will run with these guarantees. If an app has run out of available 2012 * expedited quota, any pending expedited jobs will run as regular jobs. 2013 * {@link JobParameters#isExpeditedJob()} can be used to know whether the executing job 2014 * has expedited guarantees or not. In addition, {@link JobScheduler#schedule(JobInfo)} 2015 * will immediately return {@link JobScheduler#RESULT_FAILURE} if the app does not have 2016 * available quota (and the job will not be successfully scheduled). 2017 * 2018 * <p> 2019 * Expedited job quota will replenish over time and as the user interacts with the app, 2020 * so you should not have to worry about running out of quota because of processing from 2021 * frequent user engagement. 2022 * 2023 * <p> 2024 * Expedited jobs may only set network, storage-not-low, and persistence constraints. 2025 * No other constraints are allowed. 2026 * 2027 * <p> 2028 * Assuming all constraints remain satisfied (including ideal system load conditions), 2029 * expedited jobs can have an execution time of at least 1 minute. If your 2030 * app has remaining expedited job quota, then the expedited job <i>may</i> potentially run 2031 * longer until remaining quota is used up. Just like with regular jobs, quota is not 2032 * consumed while the app is on top and visible to the user. 2033 * 2034 * <p class="note"> 2035 * Note: Even though expedited jobs are meant to run as soon as possible, they may be 2036 * deferred if the system is under heavy load or requested constraints are not satisfied. 2037 * This delay may be true for expedited jobs of the foreground app on Android version 2038 * {@link Build.VERSION_CODES#S}, but starting from Android version 2039 * {@link Build.VERSION_CODES#TIRAMISU}, expedited jobs for the foreground app are 2040 * guaranteed to be started before {@link JobScheduler#schedule(JobInfo)} returns (assuming 2041 * all requested constraints are satisfied), similar to foreground services. 2042 * 2043 * @see JobInfo#isExpedited() 2044 */ 2045 @NonNull setExpedited(boolean expedited)2046 public Builder setExpedited(boolean expedited) { 2047 if (expedited) { 2048 mFlags |= FLAG_EXPEDITED; 2049 if (mPriority == PRIORITY_DEFAULT) { 2050 // The default priority for EJs is MAX, but only change this if .setPriority() 2051 // hasn't been called yet. 2052 mPriority = PRIORITY_MAX; 2053 } 2054 } else { 2055 if (mPriority == PRIORITY_MAX && (mFlags & FLAG_EXPEDITED) != 0) { 2056 // Reset the priority for the job, but only change this if .setPriority() 2057 // hasn't been called yet. 2058 mPriority = PRIORITY_DEFAULT; 2059 } 2060 mFlags &= (~FLAG_EXPEDITED); 2061 } 2062 return this; 2063 } 2064 2065 /** 2066 * Indicates that this job is being scheduled to fulfill an explicit user request. 2067 * As such, user-initiated jobs can only be scheduled when the app is in the foreground 2068 * or in a state where launching an activity is allowed, as defined 2069 * <a href= 2070 * "https://developer.android.com/guide/components/activities/background-starts#exceptions"> 2071 * here</a>. Attempting to schedule one outside of these conditions will return a 2072 * {@link JobScheduler#RESULT_FAILURE}. 2073 * 2074 * <p> 2075 * This should <b>NOT</b> be used for automatic features. 2076 * 2077 * <p> 2078 * All user-initiated jobs must have an associated notification, set via 2079 * {@link JobService#setNotification(JobParameters, int, Notification, int)}, and will be 2080 * shown in the Task Manager when running. These jobs cannot be rescheduled by the app 2081 * if the user stops the job via system provided affordance (such as the Task Manager). 2082 * Thus, it is best practice and recommended to provide action buttons in the 2083 * associated notification to allow the user to stop the job gracefully 2084 * and allow for rescheduling. 2085 * 2086 * <p> 2087 * If the app doesn't hold the {@link android.Manifest.permission#RUN_USER_INITIATED_JOBS} 2088 * permission when scheduling a user-initiated job, JobScheduler will throw a 2089 * {@link SecurityException}. 2090 * 2091 * <p> 2092 * In {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, user-initiated jobs can only 2093 * be used for network data transfers. As such, they must specify a required network via 2094 * {@link #setRequiredNetwork(NetworkRequest)} or {@link #setRequiredNetworkType(int)}. 2095 * 2096 * <p> 2097 * These jobs will not be subject to quotas and will be started immediately once scheduled 2098 * if all constraints are met and the device system health allows for additional tasks. 2099 * They are also given {@link #PRIORITY_MAX} by default, and the priority cannot be changed. 2100 * 2101 * @see JobInfo#isUserInitiated() 2102 */ 2103 @RequiresPermission(android.Manifest.permission.RUN_USER_INITIATED_JOBS) 2104 @NonNull setUserInitiated(boolean userInitiated)2105 public Builder setUserInitiated(boolean userInitiated) { 2106 if (userInitiated) { 2107 mFlags |= FLAG_USER_INITIATED; 2108 if (mPriority == PRIORITY_DEFAULT) { 2109 // The default priority for UIJs is MAX, but only change this if .setPriority() 2110 // hasn't been called yet. 2111 mPriority = PRIORITY_MAX; 2112 } 2113 } else { 2114 if (mPriority == PRIORITY_MAX && (mFlags & FLAG_USER_INITIATED) != 0) { 2115 // Reset the priority for the job, but only change this if .setPriority() 2116 // hasn't been called yet. 2117 mPriority = PRIORITY_DEFAULT; 2118 } 2119 mFlags &= (~FLAG_USER_INITIATED); 2120 } 2121 return this; 2122 } 2123 2124 /** 2125 * Setting this to true indicates that this job is important while the scheduling app 2126 * is in the foreground or on the temporary allowlist for background restrictions. 2127 * This means that the system will relax doze restrictions on this job during this time. 2128 * 2129 * Apps should use this flag only for short jobs that are essential for the app to function 2130 * properly in the foreground. 2131 * 2132 * Note that once the scheduling app is no longer allowlisted from background restrictions 2133 * and in the background, or the job failed due to unsatisfied constraints, 2134 * this job should be expected to behave like other jobs without this flag. 2135 * 2136 * <p> 2137 * Jobs marked as important-while-foreground are given {@link #PRIORITY_HIGH} by default. 2138 * 2139 * <p class="caution"><strong>Note:</strong> Beginning with 2140 * {@link android.os.Build.VERSION_CODES#BAKLAVA}, this flag will be ignored and no longer 2141 * function effectively, regardless of the calling app's target SDK version. 2142 * {@link #isImportantWhileForeground()} will always return {@code false}. 2143 * Apps should use {link #setExpedited(boolean)} with {@code true} to indicate 2144 * that this job is important and needs to run as soon as possible. 2145 * 2146 * @param importantWhileForeground whether to relax doze restrictions for this job when the 2147 * app is in the foreground. False by default. 2148 * @see JobInfo#isImportantWhileForeground() 2149 * @deprecated Use {@link #setExpedited(boolean)} instead. 2150 */ 2151 @Deprecated setImportantWhileForeground(boolean importantWhileForeground)2152 public Builder setImportantWhileForeground(boolean importantWhileForeground) { 2153 if (Flags.ignoreImportantWhileForeground()) { 2154 Log.w(TAG, "Requested important-while-foreground flag for job" + mJobId 2155 + " is ignored and takes no effect"); 2156 return this; 2157 } 2158 2159 if (importantWhileForeground) { 2160 mFlags |= FLAG_IMPORTANT_WHILE_FOREGROUND; 2161 if (mPriority == PRIORITY_DEFAULT) { 2162 // The default priority for important-while-foreground is HIGH, but only change 2163 // this if .setPriority() hasn't been called yet. 2164 mPriority = PRIORITY_HIGH; 2165 } 2166 } else { 2167 if (mPriority == PRIORITY_HIGH 2168 && (mFlags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0) { 2169 // Reset the priority for the job, but only change this if .setPriority() 2170 // hasn't been called yet. 2171 mPriority = PRIORITY_DEFAULT; 2172 } 2173 mFlags &= (~FLAG_IMPORTANT_WHILE_FOREGROUND); 2174 } 2175 return this; 2176 } 2177 2178 /** 2179 * Setting this to true indicates that this job is designed to prefetch 2180 * content that will make a material improvement to the experience of 2181 * the specific user of this device. For example, fetching top headlines 2182 * of interest to the current user. 2183 * <p> 2184 * Apps targeting Android version {@link Build.VERSION_CODES#TIRAMISU} or later are 2185 * not allowed to have deadlines (set via {@link #setOverrideDeadline(long)} on their 2186 * prefetch jobs. 2187 * <p> 2188 * The system may use this signal to relax the network constraints you 2189 * originally requested, such as allowing a 2190 * {@link JobInfo#NETWORK_TYPE_UNMETERED} job to run over a metered 2191 * network when there is a surplus of metered data available. The system 2192 * may also use this signal in combination with end user usage patterns 2193 * to ensure data is prefetched before the user launches your app. 2194 * @see JobInfo#isPrefetch() 2195 */ setPrefetch(boolean prefetch)2196 public Builder setPrefetch(boolean prefetch) { 2197 if (prefetch) { 2198 mFlags |= FLAG_PREFETCH; 2199 } else { 2200 mFlags &= (~FLAG_PREFETCH); 2201 } 2202 return this; 2203 } 2204 2205 /** 2206 * Set whether or not to persist this job across device reboots. 2207 * 2208 * @param isPersisted True to indicate that the job will be written to 2209 * disk and loaded at boot. 2210 * @see JobInfo#isPersisted() 2211 */ 2212 @RequiresPermission(android.Manifest.permission.RECEIVE_BOOT_COMPLETED) setPersisted(boolean isPersisted)2213 public Builder setPersisted(boolean isPersisted) { 2214 mIsPersisted = isPersisted; 2215 return this; 2216 } 2217 2218 /** 2219 * Set a tag that will be used in {@link android.os.Trace traces}. 2220 * Since this is a trace tag, it must follow the rules set in 2221 * {@link android.os.Trace#beginSection(String)}, such as it cannot be more 2222 * than 127 Unicode code units. 2223 * Additionally, since leading and trailing whitespace can lead to hard-to-debug issues, 2224 * they will be {@link String#trim() trimmed}. 2225 * An empty String (after trimming) is not allowed. 2226 * @param traceTag The tag to use in traces. 2227 * @return This object for method chaining 2228 */ 2229 @FlaggedApi(Flags.FLAG_JOB_DEBUG_INFO_APIS) 2230 @NonNull setTraceTag(@ullable String traceTag)2231 public Builder setTraceTag(@Nullable String traceTag) { 2232 mTraceTag = validateTraceTag(traceTag); 2233 return this; 2234 } 2235 2236 /** 2237 * @return The job object to hand to the JobScheduler. This object is immutable. 2238 */ build()2239 public JobInfo build() { 2240 return build(Compatibility.isChangeEnabled(DISALLOW_DEADLINES_FOR_PREFETCH_JOBS), 2241 Compatibility.isChangeEnabled(REJECT_NEGATIVE_NETWORK_ESTIMATES), 2242 Compatibility.isChangeEnabled(ENFORCE_MINIMUM_TIME_WINDOWS), 2243 Compatibility.isChangeEnabled(REJECT_NEGATIVE_DELAYS_AND_DEADLINES)); 2244 } 2245 2246 /** @hide */ build(boolean disallowPrefetchDeadlines, boolean rejectNegativeNetworkEstimates, boolean enforceMinimumTimeWindows, boolean rejectNegativeDelaysAndDeadlines)2247 public JobInfo build(boolean disallowPrefetchDeadlines, 2248 boolean rejectNegativeNetworkEstimates, 2249 boolean enforceMinimumTimeWindows, 2250 boolean rejectNegativeDelaysAndDeadlines) { 2251 // This check doesn't need to be inside enforceValidity. It's an unnecessary legacy 2252 // check that would ideally be phased out instead. 2253 if (mBackoffPolicySet && (mConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) { 2254 throw new IllegalArgumentException("An idle mode job will not respect any" + 2255 " back-off policy, so calling setBackoffCriteria with" + 2256 " setRequiresDeviceIdle is an error."); 2257 } 2258 JobInfo jobInfo = new JobInfo(this); 2259 jobInfo.enforceValidity(disallowPrefetchDeadlines, rejectNegativeNetworkEstimates, 2260 enforceMinimumTimeWindows, rejectNegativeDelaysAndDeadlines); 2261 return jobInfo; 2262 } 2263 2264 /** 2265 * @hide 2266 */ summarize()2267 public String summarize() { 2268 final String service = (mJobService != null) 2269 ? mJobService.flattenToShortString() 2270 : "null"; 2271 return "JobInfo.Builder{job:" + mJobId + "/" + service + "}"; 2272 } 2273 } 2274 2275 /** 2276 * @hide 2277 */ enforceValidity(boolean disallowPrefetchDeadlines, boolean rejectNegativeNetworkEstimates, boolean enforceMinimumTimeWindows, boolean rejectNegativeDelaysAndDeadlines)2278 public final void enforceValidity(boolean disallowPrefetchDeadlines, 2279 boolean rejectNegativeNetworkEstimates, 2280 boolean enforceMinimumTimeWindows, 2281 boolean rejectNegativeDelaysAndDeadlines) { 2282 // Check that network estimates require network type and are reasonable values. 2283 if ((networkDownloadBytes > 0 || networkUploadBytes > 0 || minimumNetworkChunkBytes > 0) 2284 && networkRequest == null) { 2285 throw new IllegalArgumentException( 2286 "Can't provide estimated network usage without requiring a network"); 2287 } 2288 if (networkRequest != null && rejectNegativeNetworkEstimates) { 2289 if (networkUploadBytes != NETWORK_BYTES_UNKNOWN && networkUploadBytes < 0) { 2290 throw new IllegalArgumentException( 2291 "Invalid network upload bytes: " + networkUploadBytes); 2292 } 2293 if (networkDownloadBytes != NETWORK_BYTES_UNKNOWN && networkDownloadBytes < 0) { 2294 throw new IllegalArgumentException( 2295 "Invalid network download bytes: " + networkDownloadBytes); 2296 } 2297 } 2298 final long estimatedTransfer; 2299 if (networkUploadBytes == NETWORK_BYTES_UNKNOWN) { 2300 estimatedTransfer = networkDownloadBytes; 2301 } else { 2302 estimatedTransfer = networkUploadBytes 2303 + (networkDownloadBytes == NETWORK_BYTES_UNKNOWN ? 0 : networkDownloadBytes); 2304 } 2305 if (minimumNetworkChunkBytes != NETWORK_BYTES_UNKNOWN 2306 && estimatedTransfer != NETWORK_BYTES_UNKNOWN 2307 && minimumNetworkChunkBytes > estimatedTransfer) { 2308 throw new IllegalArgumentException( 2309 "Minimum chunk size can't be greater than estimated network usage"); 2310 } 2311 if (minimumNetworkChunkBytes != NETWORK_BYTES_UNKNOWN && minimumNetworkChunkBytes <= 0) { 2312 throw new IllegalArgumentException("Minimum chunk size must be positive"); 2313 } 2314 2315 if (rejectNegativeDelaysAndDeadlines) { 2316 if (minLatencyMillis < 0) { 2317 throw new IllegalArgumentException( 2318 "Minimum latency is negative: " + minLatencyMillis); 2319 } 2320 if (maxExecutionDelayMillis < 0) { 2321 throw new IllegalArgumentException( 2322 "Override deadline is negative: " + maxExecutionDelayMillis); 2323 } 2324 } 2325 2326 final boolean hasDeadline = maxExecutionDelayMillis != 0L; 2327 // Check that a deadline was not set on a periodic job. 2328 if (isPeriodic) { 2329 if (hasDeadline) { 2330 throw new IllegalArgumentException( 2331 "Can't call setOverrideDeadline() on a periodic job."); 2332 } 2333 if (minLatencyMillis != 0L) { 2334 throw new IllegalArgumentException( 2335 "Can't call setMinimumLatency() on a periodic job"); 2336 } 2337 if (triggerContentUris != null) { 2338 throw new IllegalArgumentException( 2339 "Can't call addTriggerContentUri() on a periodic job"); 2340 } 2341 } 2342 2343 // Prefetch jobs should not have deadlines 2344 if (disallowPrefetchDeadlines && hasDeadline && (flags & FLAG_PREFETCH) != 0) { 2345 throw new IllegalArgumentException( 2346 "Can't call setOverrideDeadline() on a prefetch job."); 2347 } 2348 2349 if (isPersisted) { 2350 // We can't serialize network specifiers 2351 if (networkRequest != null 2352 && networkRequest.getNetworkSpecifier() != null) { 2353 throw new IllegalArgumentException( 2354 "Network specifiers aren't supported for persistent jobs"); 2355 } 2356 if (triggerContentUris != null) { 2357 throw new IllegalArgumentException( 2358 "Can't call addTriggerContentUri() on a persisted job"); 2359 } 2360 if (!transientExtras.isEmpty()) { 2361 throw new IllegalArgumentException( 2362 "Can't call setTransientExtras() on a persisted job"); 2363 } 2364 if (clipData != null) { 2365 throw new IllegalArgumentException( 2366 "Can't call setClipData() on a persisted job"); 2367 } 2368 } 2369 2370 if ((flags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0) { 2371 if (hasEarlyConstraint) { 2372 throw new IllegalArgumentException( 2373 "An important while foreground job cannot have a time delay"); 2374 } 2375 if (mPriority != PRIORITY_HIGH && mPriority != PRIORITY_DEFAULT) { 2376 throw new IllegalArgumentException( 2377 "An important while foreground job must be high or default priority." 2378 + " Don't mark unimportant tasks as important while foreground."); 2379 } 2380 } 2381 2382 final boolean isExpedited = (flags & FLAG_EXPEDITED) != 0; 2383 final boolean isUserInitiated = (flags & FLAG_USER_INITIATED) != 0; 2384 switch (mPriority) { 2385 case PRIORITY_MAX: 2386 if (!(isExpedited || isUserInitiated)) { 2387 throw new IllegalArgumentException( 2388 "Only expedited or user-initiated jobs can have max priority"); 2389 } 2390 break; 2391 case PRIORITY_HIGH: 2392 if ((flags & FLAG_PREFETCH) != 0) { 2393 throw new IllegalArgumentException("Prefetch jobs cannot be high priority"); 2394 } 2395 if (isPeriodic) { 2396 throw new IllegalArgumentException("Periodic jobs cannot be high priority"); 2397 } 2398 break; 2399 case PRIORITY_DEFAULT: 2400 case PRIORITY_LOW: 2401 case PRIORITY_MIN: 2402 break; 2403 default: 2404 throw new IllegalArgumentException("Invalid priority level provided: " + mPriority); 2405 } 2406 2407 final boolean hasFunctionalConstraint = networkRequest != null 2408 || constraintFlags != 0 2409 || (triggerContentUris != null && triggerContentUris.length > 0); 2410 if (hasLateConstraint && !isPeriodic) { 2411 if (!hasFunctionalConstraint) { 2412 Log.w(TAG, "Job '" + service.flattenToShortString() + "#" + jobId + "'" 2413 + " has a deadline with no functional constraints." 2414 + " The deadline won't improve job execution latency." 2415 + " Consider removing the deadline."); 2416 } else { 2417 final long windowStart = hasEarlyConstraint ? minLatencyMillis : 0; 2418 if (maxExecutionDelayMillis - windowStart < MIN_ALLOWED_TIME_WINDOW_MILLIS) { 2419 if (enforceMinimumTimeWindows 2420 && Flags.enforceMinimumTimeWindows()) { 2421 throw new IllegalArgumentException("Time window too short. Constraints" 2422 + " unlikely to be satisfied. Increase deadline to a reasonable" 2423 + " duration." 2424 + " Job '" + service.flattenToShortString() + "#" + jobId + "'" 2425 + " has delay=" + windowStart 2426 + ", deadline=" + maxExecutionDelayMillis); 2427 } else { 2428 Log.w(TAG, "Job '" + service.flattenToShortString() + "#" + jobId + "'" 2429 + " has a deadline with functional constraints and an extremely" 2430 + " short time window of " 2431 + (maxExecutionDelayMillis - windowStart) + " ms" 2432 + " (delay=" + windowStart 2433 + ", deadline=" + maxExecutionDelayMillis + ")." 2434 + " The functional constraints are not likely to be satisfied when" 2435 + " the job runs."); 2436 } 2437 } 2438 } 2439 } 2440 2441 if (isExpedited) { 2442 if (hasEarlyConstraint) { 2443 throw new IllegalArgumentException("An expedited job cannot have a time delay"); 2444 } 2445 if (hasLateConstraint) { 2446 throw new IllegalArgumentException("An expedited job cannot have a deadline"); 2447 } 2448 if (isPeriodic) { 2449 throw new IllegalArgumentException("An expedited job cannot be periodic"); 2450 } 2451 if (isUserInitiated) { 2452 throw new IllegalArgumentException("An expedited job cannot be user-initiated"); 2453 } 2454 if (mPriority != PRIORITY_MAX && mPriority != PRIORITY_HIGH) { 2455 throw new IllegalArgumentException( 2456 "An expedited job must be high or max priority. Don't use expedited jobs" 2457 + " for unimportant tasks."); 2458 } 2459 if ((constraintFlags & ~CONSTRAINT_FLAG_STORAGE_NOT_LOW) != 0 2460 || (flags & ~(FLAG_EXPEDITED | FLAG_EXEMPT_FROM_APP_STANDBY)) != 0) { 2461 throw new IllegalArgumentException( 2462 "An expedited job can only have network and storage-not-low constraints"); 2463 } 2464 if (triggerContentUris != null && triggerContentUris.length > 0) { 2465 throw new IllegalArgumentException( 2466 "Can't call addTriggerContentUri() on an expedited job"); 2467 } 2468 } 2469 2470 if (isUserInitiated) { 2471 if (hasEarlyConstraint) { 2472 throw new IllegalArgumentException("A user-initiated job cannot have a time delay"); 2473 } 2474 if (hasLateConstraint) { 2475 throw new IllegalArgumentException("A user-initiated job cannot have a deadline"); 2476 } 2477 if (isPeriodic) { 2478 throw new IllegalArgumentException("A user-initiated job cannot be periodic"); 2479 } 2480 if ((flags & FLAG_PREFETCH) != 0) { 2481 throw new IllegalArgumentException( 2482 "A user-initiated job cannot also be a prefetch job"); 2483 } 2484 if (mPriority != PRIORITY_MAX) { 2485 throw new IllegalArgumentException("A user-initiated job must be max priority."); 2486 } 2487 if ((constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) { 2488 throw new IllegalArgumentException( 2489 "A user-initiated job cannot have a device-idle constraint"); 2490 } 2491 if (triggerContentUris != null && triggerContentUris.length > 0) { 2492 throw new IllegalArgumentException( 2493 "Can't call addTriggerContentUri() on a user-initiated job"); 2494 } 2495 // UIDTs 2496 if (networkRequest == null) { 2497 throw new IllegalArgumentException( 2498 "A user-initiated data transfer job must specify a valid network type"); 2499 } 2500 } 2501 2502 if (mDebugTags.size() > MAX_NUM_DEBUG_TAGS) { 2503 throw new IllegalArgumentException( 2504 "Can't have more than " + MAX_NUM_DEBUG_TAGS + " tags"); 2505 } 2506 final ArraySet<String> validatedDebugTags = new ArraySet<>(); 2507 for (int i = 0; i < mDebugTags.size(); ++i) { 2508 validatedDebugTags.add(validateDebugTag(mDebugTags.valueAt(i))); 2509 } 2510 mDebugTags.clear(); 2511 mDebugTags.addAll(validatedDebugTags); 2512 2513 validateTraceTag(mTraceTag); 2514 } 2515 2516 /** 2517 * Returns a sanitized debug tag if valid, or throws an exception if not. 2518 * @hide 2519 */ 2520 @NonNull validateDebugTag(@ullable String debugTag)2521 public static String validateDebugTag(@Nullable String debugTag) { 2522 if (debugTag == null) { 2523 throw new NullPointerException("debug tag cannot be null"); 2524 } 2525 debugTag = debugTag.trim(); 2526 if (debugTag.isEmpty()) { 2527 throw new IllegalArgumentException("debug tag cannot be empty"); 2528 } 2529 if (debugTag.length() > MAX_DEBUG_TAG_LENGTH) { 2530 throw new IllegalArgumentException( 2531 "debug tag cannot be more than " + MAX_DEBUG_TAG_LENGTH + " characters"); 2532 } 2533 return debugTag.intern(); 2534 } 2535 2536 /** 2537 * Returns a sanitized trace tag if valid, or throws an exception if not. 2538 * @hide 2539 */ 2540 @Nullable validateTraceTag(@ullable String traceTag)2541 public static String validateTraceTag(@Nullable String traceTag) { 2542 if (traceTag == null) { 2543 return null; 2544 } 2545 traceTag = traceTag.trim(); 2546 if (traceTag.isEmpty()) { 2547 throw new IllegalArgumentException("trace tag cannot be empty"); 2548 } 2549 if (traceTag.length() > MAX_TRACE_TAG_LENGTH) { 2550 throw new IllegalArgumentException( 2551 "traceTag tag cannot be more than " + MAX_TRACE_TAG_LENGTH + " characters"); 2552 } 2553 if (traceTag.contains("|") || traceTag.contains("\n") || traceTag.contains("\0")) { 2554 throw new IllegalArgumentException("Trace tag cannot contain |, \\n, or \\0"); 2555 } 2556 return traceTag.intern(); 2557 } 2558 2559 /** 2560 * Convert a bias integer into a human readable string for debugging. 2561 * @hide 2562 */ getBiasString(int bias)2563 public static String getBiasString(int bias) { 2564 switch (bias) { 2565 case BIAS_DEFAULT: 2566 return BIAS_DEFAULT + " [DEFAULT]"; 2567 case BIAS_SYNC_EXPEDITED: 2568 return BIAS_SYNC_EXPEDITED + " [SYNC_EXPEDITED]"; 2569 case BIAS_SYNC_INITIALIZATION: 2570 return BIAS_SYNC_INITIALIZATION + " [SYNC_INITIALIZATION]"; 2571 case BIAS_BOUND_FOREGROUND_SERVICE: 2572 return BIAS_BOUND_FOREGROUND_SERVICE + " [BFGS_APP]"; 2573 case BIAS_FOREGROUND_SERVICE: 2574 return BIAS_FOREGROUND_SERVICE + " [FGS_APP]"; 2575 case BIAS_TOP_APP: 2576 return BIAS_TOP_APP + " [TOP_APP]"; 2577 2578 // BIAS_ADJ_* are adjustments and not used as real priorities. 2579 // No need to convert to strings. 2580 } 2581 return bias + " [UNKNOWN]"; 2582 } 2583 2584 /** 2585 * Convert a priority integer into a human readable string for debugging. 2586 * @hide 2587 */ getPriorityString(@riority int priority)2588 public static String getPriorityString(@Priority int priority) { 2589 switch (priority) { 2590 case PRIORITY_MIN: 2591 return priority + " [MIN]"; 2592 case PRIORITY_LOW: 2593 return priority + " [LOW]"; 2594 case PRIORITY_DEFAULT: 2595 return priority + " [DEFAULT]"; 2596 case PRIORITY_HIGH: 2597 return priority + " [HIGH]"; 2598 case PRIORITY_MAX: 2599 return priority + " [MAX]"; 2600 } 2601 return priority + " [UNKNOWN]"; 2602 } 2603 } 2604