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_ROAMING;
22 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
23 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
24 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
25 import static android.util.TimeUtils.formatDuration;
26 
27 import android.annotation.BytesLong;
28 import android.annotation.IntDef;
29 import android.annotation.NonNull;
30 import android.annotation.Nullable;
31 import android.annotation.RequiresPermission;
32 import android.content.ClipData;
33 import android.content.ComponentName;
34 import android.net.NetworkRequest;
35 import android.net.NetworkSpecifier;
36 import android.net.Uri;
37 import android.os.BaseBundle;
38 import android.os.Bundle;
39 import android.os.Parcel;
40 import android.os.Parcelable;
41 import android.os.PersistableBundle;
42 import android.util.Log;
43 
44 import java.lang.annotation.Retention;
45 import java.lang.annotation.RetentionPolicy;
46 import java.util.ArrayList;
47 import java.util.Arrays;
48 import java.util.Objects;
49 
50 /**
51  * Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the
52  * parameters required to schedule work against the calling application. These are constructed
53  * using the {@link JobInfo.Builder}.
54  * You must specify at least one sort of constraint on the JobInfo object that you are creating.
55  * The goal here is to provide the scheduler with high-level semantics about the work you want to
56  * accomplish. Doing otherwise with throw an exception in your app.
57  */
58 public class JobInfo implements Parcelable {
59     private static String TAG = "JobInfo";
60 
61     /** @hide */
62     @IntDef(prefix = { "NETWORK_TYPE_" }, value = {
63             NETWORK_TYPE_NONE,
64             NETWORK_TYPE_ANY,
65             NETWORK_TYPE_UNMETERED,
66             NETWORK_TYPE_NOT_ROAMING,
67             NETWORK_TYPE_CELLULAR,
68     })
69     @Retention(RetentionPolicy.SOURCE)
70     public @interface NetworkType {}
71 
72     /** Default. */
73     public static final int NETWORK_TYPE_NONE = 0;
74     /** This job requires network connectivity. */
75     public static final int NETWORK_TYPE_ANY = 1;
76     /** This job requires network connectivity that is unmetered. */
77     public static final int NETWORK_TYPE_UNMETERED = 2;
78     /** This job requires network connectivity that is not roaming. */
79     public static final int NETWORK_TYPE_NOT_ROAMING = 3;
80     /** This job requires network connectivity that is a cellular network. */
81     public static final int NETWORK_TYPE_CELLULAR = 4;
82 
83     /**
84      * This job requires metered connectivity such as most cellular data
85      * networks.
86      *
87      * @deprecated Cellular networks may be unmetered, or Wi-Fi networks may be
88      *             metered, so this isn't a good way of selecting a specific
89      *             transport. Instead, use {@link #NETWORK_TYPE_CELLULAR} or
90      *             {@link android.net.NetworkRequest.Builder#addTransportType(int)}
91      *             if your job requires a specific network transport.
92      */
93     @Deprecated
94     public static final int NETWORK_TYPE_METERED = NETWORK_TYPE_CELLULAR;
95 
96     /** Sentinel value indicating that bytes are unknown. */
97     public static final int NETWORK_BYTES_UNKNOWN = -1;
98 
99     /**
100      * Amount of backoff a job has initially by default, in milliseconds.
101      */
102     public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L;  // 30 seconds.
103 
104     /**
105      * Maximum backoff we allow for a job, in milliseconds.
106      */
107     public static final long MAX_BACKOFF_DELAY_MILLIS = 5 * 60 * 60 * 1000;  // 5 hours.
108 
109     /** @hide */
110     @IntDef(prefix = { "BACKOFF_POLICY_" }, value = {
111             BACKOFF_POLICY_LINEAR,
112             BACKOFF_POLICY_EXPONENTIAL,
113     })
114     @Retention(RetentionPolicy.SOURCE)
115     public @interface BackoffPolicy {}
116 
117     /**
118      * Linearly back-off a failed job. See
119      * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)}
120      * retry_time(current_time, num_failures) =
121      *     current_time + initial_backoff_millis * num_failures, num_failures >= 1
122      */
123     public static final int BACKOFF_POLICY_LINEAR = 0;
124 
125     /**
126      * Exponentially back-off a failed job. See
127      * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)}
128      *
129      * retry_time(current_time, num_failures) =
130      *     current_time + initial_backoff_millis * 2 ^ (num_failures - 1), num_failures >= 1
131      */
132     public static final int BACKOFF_POLICY_EXPONENTIAL = 1;
133 
134     /* Minimum interval for a periodic job, in milliseconds. */
135     private static final long MIN_PERIOD_MILLIS = 15 * 60 * 1000L;   // 15 minutes
136 
137     /* Minimum flex for a periodic job, in milliseconds. */
138     private static final long MIN_FLEX_MILLIS = 5 * 60 * 1000L; // 5 minutes
139 
140     /**
141      * Minimum backoff interval for a job, in milliseconds
142      * @hide
143      */
144     public static final long MIN_BACKOFF_MILLIS = 10 * 1000L;      // 10 seconds
145 
146     /**
147      * Query the minimum interval allowed for periodic scheduled jobs.  Attempting
148      * to declare a smaller period that this when scheduling a job will result in a
149      * job that is still periodic, but will run with this effective period.
150      *
151      * @return The minimum available interval for scheduling periodic jobs, in milliseconds.
152      */
getMinPeriodMillis()153     public static final long getMinPeriodMillis() {
154         return MIN_PERIOD_MILLIS;
155     }
156 
157     /**
158      * Query the minimum flex time allowed for periodic scheduled jobs.  Attempting
159      * to declare a shorter flex time than this when scheduling such a job will
160      * result in this amount as the effective flex time for the job.
161      *
162      * @return The minimum available flex time for scheduling periodic jobs, in milliseconds.
163      */
getMinFlexMillis()164     public static final long getMinFlexMillis() {
165         return MIN_FLEX_MILLIS;
166     }
167 
168     /**
169      * Query the minimum automatic-reschedule backoff interval permitted for jobs.
170      * @hide
171      */
getMinBackoffMillis()172     public static final long getMinBackoffMillis() {
173         return MIN_BACKOFF_MILLIS;
174     }
175 
176     /**
177      * Default type of backoff.
178      * @hide
179      */
180     public static final int DEFAULT_BACKOFF_POLICY = BACKOFF_POLICY_EXPONENTIAL;
181 
182     /**
183      * Default of {@link #getPriority}.
184      * @hide
185      */
186     public static final int PRIORITY_DEFAULT = 0;
187 
188     /**
189      * Value of {@link #getPriority} for expedited syncs.
190      * @hide
191      */
192     public static final int PRIORITY_SYNC_EXPEDITED = 10;
193 
194     /**
195      * Value of {@link #getPriority} for first time initialization syncs.
196      * @hide
197      */
198     public static final int PRIORITY_SYNC_INITIALIZATION = 20;
199 
200     /**
201      * Value of {@link #getPriority} for a foreground app (overrides the supplied
202      * JobInfo priority if it is smaller).
203      * @hide
204      */
205     public static final int PRIORITY_FOREGROUND_APP = 30;
206 
207     /**
208      * Value of {@link #getPriority} for the current top app (overrides the supplied
209      * JobInfo priority if it is smaller).
210      * @hide
211      */
212     public static final int PRIORITY_TOP_APP = 40;
213 
214     /**
215      * Adjustment of {@link #getPriority} if the app has often (50% or more of the time)
216      * been running jobs.
217      * @hide
218      */
219     public static final int PRIORITY_ADJ_OFTEN_RUNNING = -40;
220 
221     /**
222      * Adjustment of {@link #getPriority} if the app has always (90% or more of the time)
223      * been running jobs.
224      * @hide
225      */
226     public static final int PRIORITY_ADJ_ALWAYS_RUNNING = -80;
227 
228     /**
229      * Indicates that the implementation of this job will be using
230      * {@link JobService#startForeground(int, android.app.Notification)} to run
231      * in the foreground.
232      * <p>
233      * When set, the internal scheduling of this job will ignore any background
234      * network restrictions for the requesting app. Note that this flag alone
235      * doesn't actually place your {@link JobService} in the foreground; you
236      * still need to post the notification yourself.
237      * <p>
238      * To use this flag, the caller must hold the
239      * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL} permission.
240      *
241      * @hide
242      */
243     public static final int FLAG_WILL_BE_FOREGROUND = 1 << 0;
244 
245     /**
246      * Allows this job to run despite doze restrictions as long as the app is in the foreground
247      * or on the temporary whitelist
248      * @hide
249      */
250     public static final int FLAG_IMPORTANT_WHILE_FOREGROUND = 1 << 1;
251 
252     /**
253      * @hide
254      */
255     public static final int FLAG_PREFETCH = 1 << 2;
256 
257     /**
258      * This job needs to be exempted from the app standby throttling. Only the system (UID 1000)
259      * can set it. Jobs with a time constrant must not have it.
260      *
261      * @hide
262      */
263     public static final int FLAG_EXEMPT_FROM_APP_STANDBY = 1 << 3;
264 
265     /**
266      * @hide
267      */
268     public static final int CONSTRAINT_FLAG_CHARGING = 1 << 0;
269 
270     /**
271      * @hide
272      */
273     public static final int CONSTRAINT_FLAG_BATTERY_NOT_LOW = 1 << 1;
274 
275     /**
276      * @hide
277      */
278     public static final int CONSTRAINT_FLAG_DEVICE_IDLE = 1 << 2;
279 
280     /**
281      * @hide
282      */
283     public static final int CONSTRAINT_FLAG_STORAGE_NOT_LOW = 1 << 3;
284 
285     private final int jobId;
286     private final PersistableBundle extras;
287     private final Bundle transientExtras;
288     private final ClipData clipData;
289     private final int clipGrantFlags;
290     private final ComponentName service;
291     private final int constraintFlags;
292     private final TriggerContentUri[] triggerContentUris;
293     private final long triggerContentUpdateDelay;
294     private final long triggerContentMaxDelay;
295     private final boolean hasEarlyConstraint;
296     private final boolean hasLateConstraint;
297     private final NetworkRequest networkRequest;
298     private final long networkDownloadBytes;
299     private final long networkUploadBytes;
300     private final long minLatencyMillis;
301     private final long maxExecutionDelayMillis;
302     private final boolean isPeriodic;
303     private final boolean isPersisted;
304     private final long intervalMillis;
305     private final long flexMillis;
306     private final long initialBackoffMillis;
307     private final int backoffPolicy;
308     private final int priority;
309     private final int flags;
310 
311     /**
312      * Unique job id associated with this application (uid).  This is the same job ID
313      * you supplied in the {@link Builder} constructor.
314      */
getId()315     public int getId() {
316         return jobId;
317     }
318 
319     /**
320      * @see JobInfo.Builder#setExtras(PersistableBundle)
321      */
getExtras()322     public @NonNull PersistableBundle getExtras() {
323         return extras;
324     }
325 
326     /**
327      * @see JobInfo.Builder#setTransientExtras(Bundle)
328      */
getTransientExtras()329     public @NonNull Bundle getTransientExtras() {
330         return transientExtras;
331     }
332 
333     /**
334      * @see JobInfo.Builder#setClipData(ClipData, int)
335      */
getClipData()336     public @Nullable ClipData getClipData() {
337         return clipData;
338     }
339 
340     /**
341      * @see JobInfo.Builder#setClipData(ClipData, int)
342      */
getClipGrantFlags()343     public int getClipGrantFlags() {
344         return clipGrantFlags;
345     }
346 
347     /**
348      * Name of the service endpoint that will be called back into by the JobScheduler.
349      */
getService()350     public @NonNull ComponentName getService() {
351         return service;
352     }
353 
354     /** @hide */
getPriority()355     public int getPriority() {
356         return priority;
357     }
358 
359     /** @hide */
getFlags()360     public int getFlags() {
361         return flags;
362     }
363 
364     /** @hide */
isExemptedFromAppStandby()365     public boolean isExemptedFromAppStandby() {
366         return ((flags & FLAG_EXEMPT_FROM_APP_STANDBY) != 0) && !isPeriodic();
367     }
368 
369     /**
370      * @see JobInfo.Builder#setRequiresCharging(boolean)
371      */
isRequireCharging()372     public boolean isRequireCharging() {
373         return (constraintFlags & CONSTRAINT_FLAG_CHARGING) != 0;
374     }
375 
376     /**
377      * @see JobInfo.Builder#setRequiresBatteryNotLow(boolean)
378      */
isRequireBatteryNotLow()379     public boolean isRequireBatteryNotLow() {
380         return (constraintFlags & CONSTRAINT_FLAG_BATTERY_NOT_LOW) != 0;
381     }
382 
383     /**
384      * @see JobInfo.Builder#setRequiresDeviceIdle(boolean)
385      */
isRequireDeviceIdle()386     public boolean isRequireDeviceIdle() {
387         return (constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0;
388     }
389 
390     /**
391      * @see JobInfo.Builder#setRequiresStorageNotLow(boolean)
392      */
isRequireStorageNotLow()393     public boolean isRequireStorageNotLow() {
394         return (constraintFlags & CONSTRAINT_FLAG_STORAGE_NOT_LOW) != 0;
395     }
396 
397     /**
398      * @hide
399      */
getConstraintFlags()400     public int getConstraintFlags() {
401         return constraintFlags;
402     }
403 
404     /**
405      * Which content: URIs must change for the job to be scheduled.  Returns null
406      * if there are none required.
407      * @see JobInfo.Builder#addTriggerContentUri(TriggerContentUri)
408      */
getTriggerContentUris()409     public @Nullable TriggerContentUri[] getTriggerContentUris() {
410         return triggerContentUris;
411     }
412 
413     /**
414      * When triggering on content URI changes, this is the delay from when a change
415      * is detected until the job is scheduled.
416      * @see JobInfo.Builder#setTriggerContentUpdateDelay(long)
417      */
getTriggerContentUpdateDelay()418     public long getTriggerContentUpdateDelay() {
419         return triggerContentUpdateDelay;
420     }
421 
422     /**
423      * When triggering on content URI changes, this is the maximum delay we will
424      * use before scheduling the job.
425      * @see JobInfo.Builder#setTriggerContentMaxDelay(long)
426      */
getTriggerContentMaxDelay()427     public long getTriggerContentMaxDelay() {
428         return triggerContentMaxDelay;
429     }
430 
431     /**
432      * Return the basic description of the kind of network this job requires.
433      *
434      * @deprecated This method attempts to map {@link #getRequiredNetwork()}
435      *             into the set of simple constants, which results in a loss of
436      *             fidelity. Callers should move to using
437      *             {@link #getRequiredNetwork()} directly.
438      * @see Builder#setRequiredNetworkType(int)
439      */
440     @Deprecated
getNetworkType()441     public @NetworkType int getNetworkType() {
442         if (networkRequest == null) {
443             return NETWORK_TYPE_NONE;
444         } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
445             return NETWORK_TYPE_UNMETERED;
446         } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING)) {
447             return NETWORK_TYPE_NOT_ROAMING;
448         } else if (networkRequest.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
449             return NETWORK_TYPE_CELLULAR;
450         } else {
451             return NETWORK_TYPE_ANY;
452         }
453     }
454 
455     /**
456      * Return the detailed description of the kind of network this job requires,
457      * or {@code null} if no specific kind of network is required.
458      *
459      * @see Builder#setRequiredNetwork(NetworkRequest)
460      */
getRequiredNetwork()461     public @Nullable NetworkRequest getRequiredNetwork() {
462         return networkRequest;
463     }
464 
465     /**
466      * @deprecated replaced by {@link #getEstimatedNetworkDownloadBytes()} and
467      *             {@link #getEstimatedNetworkUploadBytes()}.
468      * @removed
469      */
470     @Deprecated
getEstimatedNetworkBytes()471     public @BytesLong long getEstimatedNetworkBytes() {
472         if (networkDownloadBytes == NETWORK_BYTES_UNKNOWN
473                 && networkUploadBytes == NETWORK_BYTES_UNKNOWN) {
474             return NETWORK_BYTES_UNKNOWN;
475         } else if (networkDownloadBytes == NETWORK_BYTES_UNKNOWN) {
476             return networkUploadBytes;
477         } else if (networkUploadBytes == NETWORK_BYTES_UNKNOWN) {
478             return networkDownloadBytes;
479         } else {
480             return networkDownloadBytes + networkUploadBytes;
481         }
482     }
483 
484     /**
485      * Return the estimated size of download traffic that will be performed by
486      * this job, in bytes.
487      *
488      * @return Estimated size of download traffic, or
489      *         {@link #NETWORK_BYTES_UNKNOWN} when unknown.
490      * @see Builder#setEstimatedNetworkBytes(long, long)
491      */
getEstimatedNetworkDownloadBytes()492     public @BytesLong long getEstimatedNetworkDownloadBytes() {
493         return networkDownloadBytes;
494     }
495 
496     /**
497      * Return the estimated size of upload traffic that will be performed by
498      * this job, in bytes.
499      *
500      * @return Estimated size of upload traffic, or
501      *         {@link #NETWORK_BYTES_UNKNOWN} when unknown.
502      * @see Builder#setEstimatedNetworkBytes(long, long)
503      */
getEstimatedNetworkUploadBytes()504     public @BytesLong long getEstimatedNetworkUploadBytes() {
505         return networkUploadBytes;
506     }
507 
508     /**
509      * Set for a job that does not recur periodically, to specify a delay after which the job
510      * will be eligible for execution. This value is not set if the job recurs periodically.
511      * @see JobInfo.Builder#setMinimumLatency(long)
512      */
getMinLatencyMillis()513     public long getMinLatencyMillis() {
514         return minLatencyMillis;
515     }
516 
517     /**
518      * @see JobInfo.Builder#setOverrideDeadline(long)
519      */
getMaxExecutionDelayMillis()520     public long getMaxExecutionDelayMillis() {
521         return maxExecutionDelayMillis;
522     }
523 
524     /**
525      * Track whether this job will repeat with a given period.
526      * @see JobInfo.Builder#setPeriodic(long)
527      * @see JobInfo.Builder#setPeriodic(long, long)
528      */
isPeriodic()529     public boolean isPeriodic() {
530         return isPeriodic;
531     }
532 
533     /**
534      * @see JobInfo.Builder#setPersisted(boolean)
535      */
isPersisted()536     public boolean isPersisted() {
537         return isPersisted;
538     }
539 
540     /**
541      * Set to the interval between occurrences of this job. This value is <b>not</b> set if the
542      * job does not recur periodically.
543      * @see JobInfo.Builder#setPeriodic(long)
544      * @see JobInfo.Builder#setPeriodic(long, long)
545      */
getIntervalMillis()546     public long getIntervalMillis() {
547         return intervalMillis;
548     }
549 
550     /**
551      * Flex time for this job. Only valid if this is a periodic job.  The job can
552      * execute at any time in a window of flex length at the end of the period.
553      * @see JobInfo.Builder#setPeriodic(long)
554      * @see JobInfo.Builder#setPeriodic(long, long)
555      */
getFlexMillis()556     public long getFlexMillis() {
557         return flexMillis;
558     }
559 
560     /**
561      * The amount of time the JobScheduler will wait before rescheduling a failed job. This value
562      * will be increased depending on the backoff policy specified at job creation time. Defaults
563      * to 30 seconds, minimum is currently 10 seconds.
564      * @see JobInfo.Builder#setBackoffCriteria(long, int)
565      */
getInitialBackoffMillis()566     public long getInitialBackoffMillis() {
567         return initialBackoffMillis;
568     }
569 
570     /**
571      * Return the backoff policy of this job.
572      * @see JobInfo.Builder#setBackoffCriteria(long, int)
573      */
getBackoffPolicy()574     public @BackoffPolicy int getBackoffPolicy() {
575         return backoffPolicy;
576     }
577 
578     /**
579      * @see JobInfo.Builder#setImportantWhileForeground(boolean)
580      */
isImportantWhileForeground()581     public boolean isImportantWhileForeground() {
582         return (flags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0;
583     }
584 
585     /**
586      * @see JobInfo.Builder#setPrefetch(boolean)
587      */
isPrefetch()588     public boolean isPrefetch() {
589         return (flags & FLAG_PREFETCH) != 0;
590     }
591 
592     /**
593      * User can specify an early constraint of 0L, which is valid, so we keep track of whether the
594      * function was called at all.
595      * @hide
596      */
hasEarlyConstraint()597     public boolean hasEarlyConstraint() {
598         return hasEarlyConstraint;
599     }
600 
601     /**
602      * User can specify a late constraint of 0L, which is valid, so we keep track of whether the
603      * function was called at all.
604      * @hide
605      */
hasLateConstraint()606     public boolean hasLateConstraint() {
607         return hasLateConstraint;
608     }
609 
kindofEqualsBundle(BaseBundle a, BaseBundle b)610     private static boolean kindofEqualsBundle(BaseBundle a, BaseBundle b) {
611         return (a == b) || (a != null && a.kindofEquals(b));
612     }
613 
614     @Override
equals(Object o)615     public boolean equals(Object o) {
616         if (!(o instanceof JobInfo)) {
617             return false;
618         }
619         JobInfo j = (JobInfo) o;
620         if (jobId != j.jobId) {
621             return false;
622         }
623         // XXX won't be correct if one is parcelled and the other not.
624         if (!kindofEqualsBundle(extras, j.extras)) {
625             return false;
626         }
627         // XXX won't be correct if one is parcelled and the other not.
628         if (!kindofEqualsBundle(transientExtras, j.transientExtras)) {
629             return false;
630         }
631         // XXX for now we consider two different clip data objects to be different,
632         // regardless of whether their contents are the same.
633         if (clipData != j.clipData) {
634             return false;
635         }
636         if (clipGrantFlags != j.clipGrantFlags) {
637             return false;
638         }
639         if (!Objects.equals(service, j.service)) {
640             return false;
641         }
642         if (constraintFlags != j.constraintFlags) {
643             return false;
644         }
645         if (!Arrays.equals(triggerContentUris, j.triggerContentUris)) {
646             return false;
647         }
648         if (triggerContentUpdateDelay != j.triggerContentUpdateDelay) {
649             return false;
650         }
651         if (triggerContentMaxDelay != j.triggerContentMaxDelay) {
652             return false;
653         }
654         if (hasEarlyConstraint != j.hasEarlyConstraint) {
655             return false;
656         }
657         if (hasLateConstraint != j.hasLateConstraint) {
658             return false;
659         }
660         if (!Objects.equals(networkRequest, j.networkRequest)) {
661             return false;
662         }
663         if (networkDownloadBytes != j.networkDownloadBytes) {
664             return false;
665         }
666         if (networkUploadBytes != j.networkUploadBytes) {
667             return false;
668         }
669         if (minLatencyMillis != j.minLatencyMillis) {
670             return false;
671         }
672         if (maxExecutionDelayMillis != j.maxExecutionDelayMillis) {
673             return false;
674         }
675         if (isPeriodic != j.isPeriodic) {
676             return false;
677         }
678         if (isPersisted != j.isPersisted) {
679             return false;
680         }
681         if (intervalMillis != j.intervalMillis) {
682             return false;
683         }
684         if (flexMillis != j.flexMillis) {
685             return false;
686         }
687         if (initialBackoffMillis != j.initialBackoffMillis) {
688             return false;
689         }
690         if (backoffPolicy != j.backoffPolicy) {
691             return false;
692         }
693         if (priority != j.priority) {
694             return false;
695         }
696         if (flags != j.flags) {
697             return false;
698         }
699         return true;
700     }
701 
702     @Override
hashCode()703     public int hashCode() {
704         int hashCode = jobId;
705         if (extras != null) {
706             hashCode = 31 * hashCode + extras.hashCode();
707         }
708         if (transientExtras != null) {
709             hashCode = 31 * hashCode + transientExtras.hashCode();
710         }
711         if (clipData != null) {
712             hashCode = 31 * hashCode + clipData.hashCode();
713         }
714         hashCode = 31*hashCode + clipGrantFlags;
715         if (service != null) {
716             hashCode = 31 * hashCode + service.hashCode();
717         }
718         hashCode = 31 * hashCode + constraintFlags;
719         if (triggerContentUris != null) {
720             hashCode = 31 * hashCode + Arrays.hashCode(triggerContentUris);
721         }
722         hashCode = 31 * hashCode + Long.hashCode(triggerContentUpdateDelay);
723         hashCode = 31 * hashCode + Long.hashCode(triggerContentMaxDelay);
724         hashCode = 31 * hashCode + Boolean.hashCode(hasEarlyConstraint);
725         hashCode = 31 * hashCode + Boolean.hashCode(hasLateConstraint);
726         if (networkRequest != null) {
727             hashCode = 31 * hashCode + networkRequest.hashCode();
728         }
729         hashCode = 31 * hashCode + Long.hashCode(networkDownloadBytes);
730         hashCode = 31 * hashCode + Long.hashCode(networkUploadBytes);
731         hashCode = 31 * hashCode + Long.hashCode(minLatencyMillis);
732         hashCode = 31 * hashCode + Long.hashCode(maxExecutionDelayMillis);
733         hashCode = 31 * hashCode + Boolean.hashCode(isPeriodic);
734         hashCode = 31 * hashCode + Boolean.hashCode(isPersisted);
735         hashCode = 31 * hashCode + Long.hashCode(intervalMillis);
736         hashCode = 31 * hashCode + Long.hashCode(flexMillis);
737         hashCode = 31 * hashCode + Long.hashCode(initialBackoffMillis);
738         hashCode = 31 * hashCode + backoffPolicy;
739         hashCode = 31 * hashCode + priority;
740         hashCode = 31 * hashCode + flags;
741         return hashCode;
742     }
743 
JobInfo(Parcel in)744     private JobInfo(Parcel in) {
745         jobId = in.readInt();
746         extras = in.readPersistableBundle();
747         transientExtras = in.readBundle();
748         if (in.readInt() != 0) {
749             clipData = ClipData.CREATOR.createFromParcel(in);
750             clipGrantFlags = in.readInt();
751         } else {
752             clipData = null;
753             clipGrantFlags = 0;
754         }
755         service = in.readParcelable(null);
756         constraintFlags = in.readInt();
757         triggerContentUris = in.createTypedArray(TriggerContentUri.CREATOR);
758         triggerContentUpdateDelay = in.readLong();
759         triggerContentMaxDelay = in.readLong();
760         if (in.readInt() != 0) {
761             networkRequest = NetworkRequest.CREATOR.createFromParcel(in);
762         } else {
763             networkRequest = null;
764         }
765         networkDownloadBytes = in.readLong();
766         networkUploadBytes = in.readLong();
767         minLatencyMillis = in.readLong();
768         maxExecutionDelayMillis = in.readLong();
769         isPeriodic = in.readInt() == 1;
770         isPersisted = in.readInt() == 1;
771         intervalMillis = in.readLong();
772         flexMillis = in.readLong();
773         initialBackoffMillis = in.readLong();
774         backoffPolicy = in.readInt();
775         hasEarlyConstraint = in.readInt() == 1;
776         hasLateConstraint = in.readInt() == 1;
777         priority = in.readInt();
778         flags = in.readInt();
779     }
780 
JobInfo(JobInfo.Builder b)781     private JobInfo(JobInfo.Builder b) {
782         jobId = b.mJobId;
783         extras = b.mExtras.deepCopy();
784         transientExtras = b.mTransientExtras.deepCopy();
785         clipData = b.mClipData;
786         clipGrantFlags = b.mClipGrantFlags;
787         service = b.mJobService;
788         constraintFlags = b.mConstraintFlags;
789         triggerContentUris = b.mTriggerContentUris != null
790                 ? b.mTriggerContentUris.toArray(new TriggerContentUri[b.mTriggerContentUris.size()])
791                 : null;
792         triggerContentUpdateDelay = b.mTriggerContentUpdateDelay;
793         triggerContentMaxDelay = b.mTriggerContentMaxDelay;
794         networkRequest = b.mNetworkRequest;
795         networkDownloadBytes = b.mNetworkDownloadBytes;
796         networkUploadBytes = b.mNetworkUploadBytes;
797         minLatencyMillis = b.mMinLatencyMillis;
798         maxExecutionDelayMillis = b.mMaxExecutionDelayMillis;
799         isPeriodic = b.mIsPeriodic;
800         isPersisted = b.mIsPersisted;
801         intervalMillis = b.mIntervalMillis;
802         flexMillis = b.mFlexMillis;
803         initialBackoffMillis = b.mInitialBackoffMillis;
804         backoffPolicy = b.mBackoffPolicy;
805         hasEarlyConstraint = b.mHasEarlyConstraint;
806         hasLateConstraint = b.mHasLateConstraint;
807         priority = b.mPriority;
808         flags = b.mFlags;
809     }
810 
811     @Override
describeContents()812     public int describeContents() {
813         return 0;
814     }
815 
816     @Override
writeToParcel(Parcel out, int flags)817     public void writeToParcel(Parcel out, int flags) {
818         out.writeInt(jobId);
819         out.writePersistableBundle(extras);
820         out.writeBundle(transientExtras);
821         if (clipData != null) {
822             out.writeInt(1);
823             clipData.writeToParcel(out, flags);
824             out.writeInt(clipGrantFlags);
825         } else {
826             out.writeInt(0);
827         }
828         out.writeParcelable(service, flags);
829         out.writeInt(constraintFlags);
830         out.writeTypedArray(triggerContentUris, flags);
831         out.writeLong(triggerContentUpdateDelay);
832         out.writeLong(triggerContentMaxDelay);
833         if (networkRequest != null) {
834             out.writeInt(1);
835             networkRequest.writeToParcel(out, flags);
836         } else {
837             out.writeInt(0);
838         }
839         out.writeLong(networkDownloadBytes);
840         out.writeLong(networkUploadBytes);
841         out.writeLong(minLatencyMillis);
842         out.writeLong(maxExecutionDelayMillis);
843         out.writeInt(isPeriodic ? 1 : 0);
844         out.writeInt(isPersisted ? 1 : 0);
845         out.writeLong(intervalMillis);
846         out.writeLong(flexMillis);
847         out.writeLong(initialBackoffMillis);
848         out.writeInt(backoffPolicy);
849         out.writeInt(hasEarlyConstraint ? 1 : 0);
850         out.writeInt(hasLateConstraint ? 1 : 0);
851         out.writeInt(priority);
852         out.writeInt(this.flags);
853     }
854 
855     public static final Creator<JobInfo> CREATOR = new Creator<JobInfo>() {
856         @Override
857         public JobInfo createFromParcel(Parcel in) {
858             return new JobInfo(in);
859         }
860 
861         @Override
862         public JobInfo[] newArray(int size) {
863             return new JobInfo[size];
864         }
865     };
866 
867     @Override
toString()868     public String toString() {
869         return "(job:" + jobId + "/" + service.flattenToShortString() + ")";
870     }
871 
872     /**
873      * Information about a content URI modification that a job would like to
874      * trigger on.
875      */
876     public static final class TriggerContentUri implements Parcelable {
877         private final Uri mUri;
878         private final int mFlags;
879 
880         /** @hide */
881         @Retention(RetentionPolicy.SOURCE)
882         @IntDef(flag = true, prefix = { "FLAG_" }, value = {
883                 FLAG_NOTIFY_FOR_DESCENDANTS,
884         })
885         public @interface Flags { }
886 
887         /**
888          * Flag for trigger: also trigger if any descendants of the given URI change.
889          * Corresponds to the <var>notifyForDescendants</var> of
890          * {@link android.content.ContentResolver#registerContentObserver}.
891          */
892         public static final int FLAG_NOTIFY_FOR_DESCENDANTS = 1<<0;
893 
894         /**
895          * Create a new trigger description.
896          * @param uri The URI to observe.  Must be non-null.
897          * @param flags Flags for the observer.
898          */
TriggerContentUri(@onNull Uri uri, @Flags int flags)899         public TriggerContentUri(@NonNull Uri uri, @Flags int flags) {
900             mUri = uri;
901             mFlags = flags;
902         }
903 
904         /**
905          * Return the Uri this trigger was created for.
906          */
getUri()907         public Uri getUri() {
908             return mUri;
909         }
910 
911         /**
912          * Return the flags supplied for the trigger.
913          */
getFlags()914         public @Flags int getFlags() {
915             return mFlags;
916         }
917 
918         @Override
equals(Object o)919         public boolean equals(Object o) {
920             if (!(o instanceof TriggerContentUri)) {
921                 return false;
922             }
923             TriggerContentUri t = (TriggerContentUri) o;
924             return Objects.equals(t.mUri, mUri) && t.mFlags == mFlags;
925         }
926 
927         @Override
hashCode()928         public int hashCode() {
929             return (mUri == null ? 0 : mUri.hashCode()) ^ mFlags;
930         }
931 
TriggerContentUri(Parcel in)932         private TriggerContentUri(Parcel in) {
933             mUri = Uri.CREATOR.createFromParcel(in);
934             mFlags = in.readInt();
935         }
936 
937         @Override
describeContents()938         public int describeContents() {
939             return 0;
940         }
941 
942         @Override
writeToParcel(Parcel out, int flags)943         public void writeToParcel(Parcel out, int flags) {
944             mUri.writeToParcel(out, flags);
945             out.writeInt(mFlags);
946         }
947 
948         public static final Creator<TriggerContentUri> CREATOR = new Creator<TriggerContentUri>() {
949             @Override
950             public TriggerContentUri createFromParcel(Parcel in) {
951                 return new TriggerContentUri(in);
952             }
953 
954             @Override
955             public TriggerContentUri[] newArray(int size) {
956                 return new TriggerContentUri[size];
957             }
958         };
959     }
960 
961     /** Builder class for constructing {@link JobInfo} objects. */
962     public static final class Builder {
963         private final int mJobId;
964         private final ComponentName mJobService;
965         private PersistableBundle mExtras = PersistableBundle.EMPTY;
966         private Bundle mTransientExtras = Bundle.EMPTY;
967         private ClipData mClipData;
968         private int mClipGrantFlags;
969         private int mPriority = PRIORITY_DEFAULT;
970         private int mFlags;
971         // Requirements.
972         private int mConstraintFlags;
973         private NetworkRequest mNetworkRequest;
974         private long mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN;
975         private long mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN;
976         private ArrayList<TriggerContentUri> mTriggerContentUris;
977         private long mTriggerContentUpdateDelay = -1;
978         private long mTriggerContentMaxDelay = -1;
979         private boolean mIsPersisted;
980         // One-off parameters.
981         private long mMinLatencyMillis;
982         private long mMaxExecutionDelayMillis;
983         // Periodic parameters.
984         private boolean mIsPeriodic;
985         private boolean mHasEarlyConstraint;
986         private boolean mHasLateConstraint;
987         private long mIntervalMillis;
988         private long mFlexMillis;
989         // Back-off parameters.
990         private long mInitialBackoffMillis = DEFAULT_INITIAL_BACKOFF_MILLIS;
991         private int mBackoffPolicy = DEFAULT_BACKOFF_POLICY;
992         /** Easy way to track whether the client has tried to set a back-off policy. */
993         private boolean mBackoffPolicySet = false;
994 
995         /**
996          * Initialize a new Builder to construct a {@link JobInfo}.
997          *
998          * @param jobId Application-provided id for this job. Subsequent calls to cancel, or
999          * jobs created with the same jobId, will update the pre-existing job with
1000          * the same id.  This ID must be unique across all clients of the same uid
1001          * (not just the same package).  You will want to make sure this is a stable
1002          * id across app updates, so probably not based on a resource ID.
1003          * @param jobService The endpoint that you implement that will receive the callback from the
1004          * JobScheduler.
1005          */
Builder(int jobId, @NonNull ComponentName jobService)1006         public Builder(int jobId, @NonNull ComponentName jobService) {
1007             mJobService = jobService;
1008             mJobId = jobId;
1009         }
1010 
1011         /** @hide */
setPriority(int priority)1012         public Builder setPriority(int priority) {
1013             mPriority = priority;
1014             return this;
1015         }
1016 
1017         /** @hide */
setFlags(int flags)1018         public Builder setFlags(int flags) {
1019             mFlags = flags;
1020             return this;
1021         }
1022 
1023         /**
1024          * Set optional extras. This is persisted, so we only allow primitive types.
1025          * @param extras Bundle containing extras you want the scheduler to hold on to for you.
1026          * @see JobInfo#getExtras()
1027          */
setExtras(@onNull PersistableBundle extras)1028         public Builder setExtras(@NonNull PersistableBundle extras) {
1029             mExtras = extras;
1030             return this;
1031         }
1032 
1033         /**
1034          * Set optional transient extras.
1035          *
1036          * <p>Because setting this property is not compatible with persisted
1037          * jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when
1038          * {@link android.app.job.JobInfo.Builder#build()} is called.</p>
1039          *
1040          * @param extras Bundle containing extras you want the scheduler to hold on to for you.
1041          * @see JobInfo#getTransientExtras()
1042          */
setTransientExtras(@onNull Bundle extras)1043         public Builder setTransientExtras(@NonNull Bundle extras) {
1044             mTransientExtras = extras;
1045             return this;
1046         }
1047 
1048         /**
1049          * Set a {@link ClipData} associated with this Job.
1050          *
1051          * <p>The main purpose of providing a ClipData is to allow granting of
1052          * URI permissions for data associated with the clip.  The exact kind
1053          * of permission grant to perform is specified through <var>grantFlags</var>.
1054          *
1055          * <p>If the ClipData contains items that are Intents, any
1056          * grant flags in those Intents will be ignored.  Only flags provided as an argument
1057          * to this method are respected, and will be applied to all Uri or
1058          * Intent items in the clip (or sub-items of the clip).
1059          *
1060          * <p>Because setting this property is not compatible with persisted
1061          * jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when
1062          * {@link android.app.job.JobInfo.Builder#build()} is called.</p>
1063          *
1064          * @param clip The new clip to set.  May be null to clear the current clip.
1065          * @param grantFlags The desired permissions to grant for any URIs.  This should be
1066          * a combination of {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION},
1067          * {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, and
1068          * {@link android.content.Intent#FLAG_GRANT_PREFIX_URI_PERMISSION}.
1069          * @see JobInfo#getClipData()
1070          * @see JobInfo#getClipGrantFlags()
1071          */
setClipData(@ullable ClipData clip, int grantFlags)1072         public Builder setClipData(@Nullable ClipData clip, int grantFlags) {
1073             mClipData = clip;
1074             mClipGrantFlags = grantFlags;
1075             return this;
1076         }
1077 
1078         /**
1079          * Set basic description of the kind of network your job requires. If
1080          * you need more precise control over network capabilities, see
1081          * {@link #setRequiredNetwork(NetworkRequest)}.
1082          * <p>
1083          * If your job doesn't need a network connection, you don't need to call
1084          * this method, as the default value is {@link #NETWORK_TYPE_NONE}.
1085          * <p>
1086          * Calling this method defines network as a strict requirement for your
1087          * job. If the network requested is not available your job will never
1088          * run. See {@link #setOverrideDeadline(long)} to change this behavior.
1089          * Calling this method will override any requirements previously defined
1090          * by {@link #setRequiredNetwork(NetworkRequest)}; you typically only
1091          * want to call one of these methods.
1092          * <p class="note">
1093          * When your job executes in
1094          * {@link JobService#onStartJob(JobParameters)}, be sure to use the
1095          * specific network returned by {@link JobParameters#getNetwork()},
1096          * otherwise you'll use the default network which may not meet this
1097          * constraint.
1098          *
1099          * @see #setRequiredNetwork(NetworkRequest)
1100          * @see JobInfo#getNetworkType()
1101          * @see JobParameters#getNetwork()
1102          */
setRequiredNetworkType(@etworkType int networkType)1103         public Builder setRequiredNetworkType(@NetworkType int networkType) {
1104             if (networkType == NETWORK_TYPE_NONE) {
1105                 return setRequiredNetwork(null);
1106             } else {
1107                 final NetworkRequest.Builder builder = new NetworkRequest.Builder();
1108 
1109                 // All types require validated Internet
1110                 builder.addCapability(NET_CAPABILITY_INTERNET);
1111                 builder.addCapability(NET_CAPABILITY_VALIDATED);
1112                 builder.removeCapability(NET_CAPABILITY_NOT_VPN);
1113 
1114                 if (networkType == NETWORK_TYPE_ANY) {
1115                     // No other capabilities
1116                 } else if (networkType == NETWORK_TYPE_UNMETERED) {
1117                     builder.addCapability(NET_CAPABILITY_NOT_METERED);
1118                 } else if (networkType == NETWORK_TYPE_NOT_ROAMING) {
1119                     builder.addCapability(NET_CAPABILITY_NOT_ROAMING);
1120                 } else if (networkType == NETWORK_TYPE_CELLULAR) {
1121                     builder.addTransportType(TRANSPORT_CELLULAR);
1122                 }
1123 
1124                 return setRequiredNetwork(builder.build());
1125             }
1126         }
1127 
1128         /**
1129          * Set detailed description of the kind of network your job requires.
1130          * <p>
1131          * If your job doesn't need a network connection, you don't need to call
1132          * this method, as the default is {@code null}.
1133          * <p>
1134          * Calling this method defines network as a strict requirement for your
1135          * job. If the network requested is not available your job will never
1136          * run. See {@link #setOverrideDeadline(long)} to change this behavior.
1137          * Calling this method will override any requirements previously defined
1138          * by {@link #setRequiredNetworkType(int)}; you typically only want to
1139          * call one of these methods.
1140          * <p class="note">
1141          * When your job executes in
1142          * {@link JobService#onStartJob(JobParameters)}, be sure to use the
1143          * specific network returned by {@link JobParameters#getNetwork()},
1144          * otherwise you'll use the default network which may not meet this
1145          * constraint.
1146          *
1147          * @param networkRequest The detailed description of the kind of network
1148          *            this job requires, or {@code null} if no specific kind of
1149          *            network is required. Defining a {@link NetworkSpecifier}
1150          *            is only supported for jobs that aren't persisted.
1151          * @see #setRequiredNetworkType(int)
1152          * @see JobInfo#getRequiredNetwork()
1153          * @see JobParameters#getNetwork()
1154          */
setRequiredNetwork(@ullable NetworkRequest networkRequest)1155         public Builder setRequiredNetwork(@Nullable NetworkRequest networkRequest) {
1156             mNetworkRequest = networkRequest;
1157             return this;
1158         }
1159 
1160         /**
1161          * @deprecated replaced by
1162          *             {@link #setEstimatedNetworkBytes(long, long)}.
1163          * @removed
1164          */
1165         @Deprecated
setEstimatedNetworkBytes(@ytesLong long networkBytes)1166         public Builder setEstimatedNetworkBytes(@BytesLong long networkBytes) {
1167             return setEstimatedNetworkBytes(networkBytes, NETWORK_BYTES_UNKNOWN);
1168         }
1169 
1170         /**
1171          * Set the estimated size of network traffic that will be performed by
1172          * this job, in bytes.
1173          * <p>
1174          * Apps are encouraged to provide values that are as accurate as
1175          * possible, but when the exact size isn't available, an
1176          * order-of-magnitude estimate can be provided instead. Here are some
1177          * specific examples:
1178          * <ul>
1179          * <li>A job that is backing up a photo knows the exact size of that
1180          * photo, so it should provide that size as the estimate.
1181          * <li>A job that refreshes top news stories wouldn't know an exact
1182          * size, but if the size is expected to be consistently around 100KB, it
1183          * can provide that order-of-magnitude value as the estimate.
1184          * <li>A job that synchronizes email could end up using an extreme range
1185          * of data, from under 1KB when nothing has changed, to dozens of MB
1186          * when there are new emails with attachments. Jobs that cannot provide
1187          * reasonable estimates should use the sentinel value
1188          * {@link JobInfo#NETWORK_BYTES_UNKNOWN}.
1189          * </ul>
1190          * Note that the system may choose to delay jobs with large network
1191          * usage estimates when the device has a poor network connection, in
1192          * order to save battery.
1193          * <p>
1194          * The values provided here only reflect the traffic that will be
1195          * performed by the base job; if you're using {@link JobWorkItem} then
1196          * you also need to define the network traffic used by each work item
1197          * when constructing them.
1198          *
1199          * @param downloadBytes The estimated size of network traffic that will
1200          *            be downloaded by this job, in bytes.
1201          * @param uploadBytes The estimated size of network traffic that will be
1202          *            uploaded by this job, in bytes.
1203          * @see JobInfo#getEstimatedNetworkDownloadBytes()
1204          * @see JobInfo#getEstimatedNetworkUploadBytes()
1205          * @see JobWorkItem#JobWorkItem(android.content.Intent, long, long)
1206          */
setEstimatedNetworkBytes(@ytesLong long downloadBytes, @BytesLong long uploadBytes)1207         public Builder setEstimatedNetworkBytes(@BytesLong long downloadBytes,
1208                 @BytesLong long uploadBytes) {
1209             mNetworkDownloadBytes = downloadBytes;
1210             mNetworkUploadBytes = uploadBytes;
1211             return this;
1212         }
1213 
1214         /**
1215          * Specify that to run this job, the device must be charging (or be a
1216          * non-battery-powered device connected to permanent power, such as Android TV
1217          * devices). This defaults to {@code false}.
1218          *
1219          * <p class="note">For purposes of running jobs, a battery-powered device
1220          * "charging" is not quite the same as simply being connected to power.  If the
1221          * device is so busy that the battery is draining despite a power connection, jobs
1222          * with this constraint will <em>not</em> run.  This can happen during some
1223          * common use cases such as video chat, particularly if the device is plugged in
1224          * to USB rather than to wall power.
1225          *
1226          * @param requiresCharging Pass {@code true} to require that the device be
1227          *     charging in order to run the job.
1228          * @see JobInfo#isRequireCharging()
1229          */
setRequiresCharging(boolean requiresCharging)1230         public Builder setRequiresCharging(boolean requiresCharging) {
1231             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_CHARGING)
1232                     | (requiresCharging ? CONSTRAINT_FLAG_CHARGING : 0);
1233             return this;
1234         }
1235 
1236         /**
1237          * Specify that to run this job, the device's battery level must not be low.
1238          * This defaults to false.  If true, the job will only run when the battery level
1239          * is not low, which is generally the point where the user is given a "low battery"
1240          * warning.
1241          * @param batteryNotLow Whether or not the device's battery level must not be low.
1242          * @see JobInfo#isRequireBatteryNotLow()
1243          */
setRequiresBatteryNotLow(boolean batteryNotLow)1244         public Builder setRequiresBatteryNotLow(boolean batteryNotLow) {
1245             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_BATTERY_NOT_LOW)
1246                     | (batteryNotLow ? CONSTRAINT_FLAG_BATTERY_NOT_LOW : 0);
1247             return this;
1248         }
1249 
1250         /**
1251          * When set {@code true}, ensure that this job will not run if the device is in active use.
1252          * The default state is {@code false}: that is, the for the job to be runnable even when
1253          * someone is interacting with the device.
1254          *
1255          * <p>This state is a loose definition provided by the system. In general, it means that
1256          * the device is not currently being used interactively, and has not been in use for some
1257          * time. As such, it is a good time to perform resource heavy jobs. Bear in mind that
1258          * battery usage will still be attributed to your application, and surfaced to the user in
1259          * battery stats.</p>
1260          *
1261          * <p class="note">Despite the similar naming, this job constraint is <em>not</em>
1262          * related to the system's "device idle" or "doze" states.  This constraint only
1263          * determines whether a job is allowed to run while the device is directly in use.
1264          *
1265          * @param requiresDeviceIdle Pass {@code true} to prevent the job from running
1266          *     while the device is being used interactively.
1267          * @see JobInfo#isRequireDeviceIdle()
1268          */
setRequiresDeviceIdle(boolean requiresDeviceIdle)1269         public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) {
1270             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_DEVICE_IDLE)
1271                     | (requiresDeviceIdle ? CONSTRAINT_FLAG_DEVICE_IDLE : 0);
1272             return this;
1273         }
1274 
1275         /**
1276          * Specify that to run this job, the device's available storage must not be low.
1277          * This defaults to false.  If true, the job will only run when the device is not
1278          * in a low storage state, which is generally the point where the user is given a
1279          * "low storage" warning.
1280          * @param storageNotLow Whether or not the device's available storage must not be low.
1281          * @see JobInfo#isRequireStorageNotLow()
1282          */
setRequiresStorageNotLow(boolean storageNotLow)1283         public Builder setRequiresStorageNotLow(boolean storageNotLow) {
1284             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_STORAGE_NOT_LOW)
1285                     | (storageNotLow ? CONSTRAINT_FLAG_STORAGE_NOT_LOW : 0);
1286             return this;
1287         }
1288 
1289         /**
1290          * Add a new content: URI that will be monitored with a
1291          * {@link android.database.ContentObserver}, and will cause the job to execute if changed.
1292          * If you have any trigger content URIs associated with a job, it will not execute until
1293          * there has been a change report for one or more of them.
1294          *
1295          * <p>Note that trigger URIs can not be used in combination with
1296          * {@link #setPeriodic(long)} or {@link #setPersisted(boolean)}.  To continually monitor
1297          * for content changes, you need to schedule a new JobInfo observing the same URIs
1298          * before you finish execution of the JobService handling the most recent changes.
1299          * Following this pattern will ensure you do not lost any content changes: while your
1300          * job is running, the system will continue monitoring for content changes, and propagate
1301          * any it sees over to the next job you schedule.</p>
1302          *
1303          * <p>Because setting this property is not compatible with periodic or
1304          * persisted jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when
1305          * {@link android.app.job.JobInfo.Builder#build()} is called.</p>
1306          *
1307          * <p>The following example shows how this feature can be used to monitor for changes
1308          * in the photos on a device.</p>
1309          *
1310          * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/PhotosContentJob.java
1311          *      job}
1312          *
1313          * @param uri The content: URI to monitor.
1314          * @see JobInfo#getTriggerContentUris()
1315          */
addTriggerContentUri(@onNull TriggerContentUri uri)1316         public Builder addTriggerContentUri(@NonNull TriggerContentUri uri) {
1317             if (mTriggerContentUris == null) {
1318                 mTriggerContentUris = new ArrayList<>();
1319             }
1320             mTriggerContentUris.add(uri);
1321             return this;
1322         }
1323 
1324         /**
1325          * Set the delay (in milliseconds) from when a content change is detected until
1326          * the job is scheduled.  If there are more changes during that time, the delay
1327          * will be reset to start at the time of the most recent change.
1328          * @param durationMs Delay after most recent content change, in milliseconds.
1329          * @see JobInfo#getTriggerContentUpdateDelay()
1330          */
setTriggerContentUpdateDelay(long durationMs)1331         public Builder setTriggerContentUpdateDelay(long durationMs) {
1332             mTriggerContentUpdateDelay = durationMs;
1333             return this;
1334         }
1335 
1336         /**
1337          * Set the maximum total delay (in milliseconds) that is allowed from the first
1338          * time a content change is detected until the job is scheduled.
1339          * @param durationMs Delay after initial content change, in milliseconds.
1340          * @see JobInfo#getTriggerContentMaxDelay()
1341          */
setTriggerContentMaxDelay(long durationMs)1342         public Builder setTriggerContentMaxDelay(long durationMs) {
1343             mTriggerContentMaxDelay = durationMs;
1344             return this;
1345         }
1346 
1347         /**
1348          * Specify that this job should recur with the provided interval, not more than once per
1349          * period. You have no control over when within this interval this job will be executed,
1350          * only the guarantee that it will be executed at most once within this interval.
1351          * Setting this function on the builder with {@link #setMinimumLatency(long)} or
1352          * {@link #setOverrideDeadline(long)} will result in an error.
1353          * @param intervalMillis Millisecond interval for which this job will repeat.
1354          * @see JobInfo#getIntervalMillis()
1355          * @see JobInfo#getFlexMillis()
1356          */
setPeriodic(long intervalMillis)1357         public Builder setPeriodic(long intervalMillis) {
1358             return setPeriodic(intervalMillis, intervalMillis);
1359         }
1360 
1361         /**
1362          * Specify that this job should recur with the provided interval and flex. The job can
1363          * execute at any time in a window of flex length at the end of the period.
1364          * @param intervalMillis Millisecond interval for which this job will repeat. A minimum
1365          *                       value of {@link #getMinPeriodMillis()} is enforced.
1366          * @param flexMillis Millisecond flex for this job. Flex is clamped to be at least
1367          *                   {@link #getMinFlexMillis()} or 5 percent of the period, whichever is
1368          *                   higher.
1369          * @see JobInfo#getIntervalMillis()
1370          * @see JobInfo#getFlexMillis()
1371          */
setPeriodic(long intervalMillis, long flexMillis)1372         public Builder setPeriodic(long intervalMillis, long flexMillis) {
1373             final long minPeriod = getMinPeriodMillis();
1374             if (intervalMillis < minPeriod) {
1375                 Log.w(TAG, "Requested interval " + formatDuration(intervalMillis) + " for job "
1376                         + mJobId + " is too small; raising to " + formatDuration(minPeriod));
1377                 intervalMillis = minPeriod;
1378             }
1379 
1380             final long percentClamp = 5 * intervalMillis / 100;
1381             final long minFlex = Math.max(percentClamp, getMinFlexMillis());
1382             if (flexMillis < minFlex) {
1383                 Log.w(TAG, "Requested flex " + formatDuration(flexMillis) + " for job " + mJobId
1384                         + " is too small; raising to " + formatDuration(minFlex));
1385                 flexMillis = minFlex;
1386             }
1387 
1388             mIsPeriodic = true;
1389             mIntervalMillis = intervalMillis;
1390             mFlexMillis = flexMillis;
1391             mHasEarlyConstraint = mHasLateConstraint = true;
1392             return this;
1393         }
1394 
1395         /**
1396          * Specify that this job should be delayed by the provided amount of time.
1397          * Because it doesn't make sense setting this property on a periodic job, doing so will
1398          * throw an {@link java.lang.IllegalArgumentException} when
1399          * {@link android.app.job.JobInfo.Builder#build()} is called.
1400          * @param minLatencyMillis Milliseconds before which this job will not be considered for
1401          *                         execution.
1402          * @see JobInfo#getMinLatencyMillis()
1403          */
setMinimumLatency(long minLatencyMillis)1404         public Builder setMinimumLatency(long minLatencyMillis) {
1405             mMinLatencyMillis = minLatencyMillis;
1406             mHasEarlyConstraint = true;
1407             return this;
1408         }
1409 
1410         /**
1411          * Set deadline which is the maximum scheduling latency. The job will be run by this
1412          * deadline even if other requirements are not met. Because it doesn't make sense setting
1413          * this property on a periodic job, doing so will throw an
1414          * {@link java.lang.IllegalArgumentException} when
1415          * {@link android.app.job.JobInfo.Builder#build()} is called.
1416          * @see JobInfo#getMaxExecutionDelayMillis()
1417          */
setOverrideDeadline(long maxExecutionDelayMillis)1418         public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
1419             mMaxExecutionDelayMillis = maxExecutionDelayMillis;
1420             mHasLateConstraint = true;
1421             return this;
1422         }
1423 
1424         /**
1425          * Set up the back-off/retry policy.
1426          * This defaults to some respectable values: {30 seconds, Exponential}. We cap back-off at
1427          * 5hrs.
1428          * Note that trying to set a backoff criteria for a job with
1429          * {@link #setRequiresDeviceIdle(boolean)} will throw an exception when you call build().
1430          * This is because back-off typically does not make sense for these types of jobs. See
1431          * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)}
1432          * for more description of the return value for the case of a job executing while in idle
1433          * mode.
1434          * @param initialBackoffMillis Millisecond time interval to wait initially when job has
1435          *                             failed.
1436          * @see JobInfo#getInitialBackoffMillis()
1437          * @see JobInfo#getBackoffPolicy()
1438          */
setBackoffCriteria(long initialBackoffMillis, @BackoffPolicy int backoffPolicy)1439         public Builder setBackoffCriteria(long initialBackoffMillis,
1440                 @BackoffPolicy int backoffPolicy) {
1441             final long minBackoff = getMinBackoffMillis();
1442             if (initialBackoffMillis < minBackoff) {
1443                 Log.w(TAG, "Requested backoff " + formatDuration(initialBackoffMillis) + " for job "
1444                         + mJobId + " is too small; raising to " + formatDuration(minBackoff));
1445                 initialBackoffMillis = minBackoff;
1446             }
1447 
1448             mBackoffPolicySet = true;
1449             mInitialBackoffMillis = initialBackoffMillis;
1450             mBackoffPolicy = backoffPolicy;
1451             return this;
1452         }
1453 
1454         /**
1455          * Setting this to true indicates that this job is important while the scheduling app
1456          * is in the foreground or on the temporary whitelist for background restrictions.
1457          * This means that the system will relax doze restrictions on this job during this time.
1458          *
1459          * Apps should use this flag only for short jobs that are essential for the app to function
1460          * properly in the foreground.
1461          *
1462          * Note that once the scheduling app is no longer whitelisted from background restrictions
1463          * and in the background, or the job failed due to unsatisfied constraints,
1464          * this job should be expected to behave like other jobs without this flag.
1465          *
1466          * @param importantWhileForeground whether to relax doze restrictions for this job when the
1467          *                                 app is in the foreground. False by default.
1468          * @see JobInfo#isImportantWhileForeground()
1469          */
setImportantWhileForeground(boolean importantWhileForeground)1470         public Builder setImportantWhileForeground(boolean importantWhileForeground) {
1471             if (importantWhileForeground) {
1472                 mFlags |= FLAG_IMPORTANT_WHILE_FOREGROUND;
1473             } else {
1474                 mFlags &= (~FLAG_IMPORTANT_WHILE_FOREGROUND);
1475             }
1476             return this;
1477         }
1478 
1479         /**
1480          * @removed
1481          * @deprecated replaced with {@link #setPrefetch(boolean)}
1482          */
1483         @Deprecated
setIsPrefetch(boolean isPrefetch)1484         public Builder setIsPrefetch(boolean isPrefetch) {
1485             return setPrefetch(isPrefetch);
1486         }
1487 
1488         /**
1489          * Setting this to true indicates that this job is designed to prefetch
1490          * content that will make a material improvement to the experience of
1491          * the specific user of this device. For example, fetching top headlines
1492          * of interest to the current user.
1493          * <p>
1494          * The system may use this signal to relax the network constraints you
1495          * originally requested, such as allowing a
1496          * {@link JobInfo#NETWORK_TYPE_UNMETERED} job to run over a metered
1497          * network when there is a surplus of metered data available. The system
1498          * may also use this signal in combination with end user usage patterns
1499          * to ensure data is prefetched before the user launches your app.
1500          * @see JobInfo#isPrefetch()
1501          */
setPrefetch(boolean prefetch)1502         public Builder setPrefetch(boolean prefetch) {
1503             if (prefetch) {
1504                 mFlags |= FLAG_PREFETCH;
1505             } else {
1506                 mFlags &= (~FLAG_PREFETCH);
1507             }
1508             return this;
1509         }
1510 
1511         /**
1512          * Set whether or not to persist this job across device reboots.
1513          *
1514          * @param isPersisted True to indicate that the job will be written to
1515          *            disk and loaded at boot.
1516          * @see JobInfo#isPersisted()
1517          */
1518         @RequiresPermission(android.Manifest.permission.RECEIVE_BOOT_COMPLETED)
setPersisted(boolean isPersisted)1519         public Builder setPersisted(boolean isPersisted) {
1520             mIsPersisted = isPersisted;
1521             return this;
1522         }
1523 
1524         /**
1525          * @return The job object to hand to the JobScheduler. This object is immutable.
1526          */
build()1527         public JobInfo build() {
1528             // Allow jobs with no constraints - What am I, a database?
1529             if (!mHasEarlyConstraint && !mHasLateConstraint && mConstraintFlags == 0 &&
1530                     mNetworkRequest == null &&
1531                     mTriggerContentUris == null) {
1532                 throw new IllegalArgumentException("You're trying to build a job with no " +
1533                         "constraints, this is not allowed.");
1534             }
1535             // Check that network estimates require network type
1536             if ((mNetworkDownloadBytes > 0 || mNetworkUploadBytes > 0) && mNetworkRequest == null) {
1537                 throw new IllegalArgumentException(
1538                         "Can't provide estimated network usage without requiring a network");
1539             }
1540             // We can't serialize network specifiers
1541             if (mIsPersisted && mNetworkRequest != null
1542                     && mNetworkRequest.networkCapabilities.getNetworkSpecifier() != null) {
1543                 throw new IllegalArgumentException(
1544                         "Network specifiers aren't supported for persistent jobs");
1545             }
1546             // Check that a deadline was not set on a periodic job.
1547             if (mIsPeriodic) {
1548                 if (mMaxExecutionDelayMillis != 0L) {
1549                     throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " +
1550                             "periodic job.");
1551                 }
1552                 if (mMinLatencyMillis != 0L) {
1553                     throw new IllegalArgumentException("Can't call setMinimumLatency() on a " +
1554                             "periodic job");
1555                 }
1556                 if (mTriggerContentUris != null) {
1557                     throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " +
1558                             "periodic job");
1559                 }
1560             }
1561             if (mIsPersisted) {
1562                 if (mTriggerContentUris != null) {
1563                     throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " +
1564                             "persisted job");
1565                 }
1566                 if (!mTransientExtras.isEmpty()) {
1567                     throw new IllegalArgumentException("Can't call setTransientExtras() on a " +
1568                             "persisted job");
1569                 }
1570                 if (mClipData != null) {
1571                     throw new IllegalArgumentException("Can't call setClipData() on a " +
1572                             "persisted job");
1573                 }
1574             }
1575             if ((mFlags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0 && mHasEarlyConstraint) {
1576                 throw new IllegalArgumentException("An important while foreground job cannot "
1577                         + "have a time delay");
1578             }
1579             if (mBackoffPolicySet && (mConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) {
1580                 throw new IllegalArgumentException("An idle mode job will not respect any" +
1581                         " back-off policy, so calling setBackoffCriteria with" +
1582                         " setRequiresDeviceIdle is an error.");
1583             }
1584             return new JobInfo(this);
1585         }
1586     }
1587 }
1588