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