• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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