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