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
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
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
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 constrant must not have it.
278      *
279      * @hide
280      */
281     public static final int FLAG_EXEMPT_FROM_APP_STANDBY = 1 << 3;
282 
283     /**
284      * @hide
285      */
286     public static final int CONSTRAINT_FLAG_CHARGING = 1 << 0;
287 
288     /**
289      * @hide
290      */
291     public static final int CONSTRAINT_FLAG_BATTERY_NOT_LOW = 1 << 1;
292 
293     /**
294      * @hide
295      */
296     public static final int CONSTRAINT_FLAG_DEVICE_IDLE = 1 << 2;
297 
298     /**
299      * @hide
300      */
301     public static final int CONSTRAINT_FLAG_STORAGE_NOT_LOW = 1 << 3;
302 
303     @UnsupportedAppUsage
304     private final int jobId;
305     private final PersistableBundle extras;
306     private final Bundle transientExtras;
307     private final ClipData clipData;
308     private final int clipGrantFlags;
309     @UnsupportedAppUsage
310     private final ComponentName service;
311     private final int constraintFlags;
312     private final TriggerContentUri[] triggerContentUris;
313     private final long triggerContentUpdateDelay;
314     private final long triggerContentMaxDelay;
315     private final boolean hasEarlyConstraint;
316     private final boolean hasLateConstraint;
317     private final NetworkRequest networkRequest;
318     private final long networkDownloadBytes;
319     private final long networkUploadBytes;
320     private final long minLatencyMillis;
321     private final long maxExecutionDelayMillis;
322     private final boolean isPeriodic;
323     private final boolean isPersisted;
324     private final long intervalMillis;
325     private final long flexMillis;
326     private final long initialBackoffMillis;
327     private final int backoffPolicy;
328     private final int priority;
329     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
330     private final int flags;
331 
332     /**
333      * Unique job id associated with this application (uid).  This is the same job ID
334      * you supplied in the {@link Builder} constructor.
335      */
getId()336     public int getId() {
337         return jobId;
338     }
339 
340     /**
341      * @see JobInfo.Builder#setExtras(PersistableBundle)
342      */
getExtras()343     public @NonNull PersistableBundle getExtras() {
344         return extras;
345     }
346 
347     /**
348      * @see JobInfo.Builder#setTransientExtras(Bundle)
349      */
getTransientExtras()350     public @NonNull Bundle getTransientExtras() {
351         return transientExtras;
352     }
353 
354     /**
355      * @see JobInfo.Builder#setClipData(ClipData, int)
356      */
getClipData()357     public @Nullable ClipData getClipData() {
358         return clipData;
359     }
360 
361     /**
362      * @see JobInfo.Builder#setClipData(ClipData, int)
363      */
getClipGrantFlags()364     public int getClipGrantFlags() {
365         return clipGrantFlags;
366     }
367 
368     /**
369      * Name of the service endpoint that will be called back into by the JobScheduler.
370      */
getService()371     public @NonNull ComponentName getService() {
372         return service;
373     }
374 
375     /** @hide */
getPriority()376     public int getPriority() {
377         return priority;
378     }
379 
380     /** @hide */
getFlags()381     public int getFlags() {
382         return flags;
383     }
384 
385     /** @hide */
isExemptedFromAppStandby()386     public boolean isExemptedFromAppStandby() {
387         return ((flags & FLAG_EXEMPT_FROM_APP_STANDBY) != 0) && !isPeriodic();
388     }
389 
390     /**
391      * @see JobInfo.Builder#setRequiresCharging(boolean)
392      */
isRequireCharging()393     public boolean isRequireCharging() {
394         return (constraintFlags & CONSTRAINT_FLAG_CHARGING) != 0;
395     }
396 
397     /**
398      * @see JobInfo.Builder#setRequiresBatteryNotLow(boolean)
399      */
isRequireBatteryNotLow()400     public boolean isRequireBatteryNotLow() {
401         return (constraintFlags & CONSTRAINT_FLAG_BATTERY_NOT_LOW) != 0;
402     }
403 
404     /**
405      * @see JobInfo.Builder#setRequiresDeviceIdle(boolean)
406      */
isRequireDeviceIdle()407     public boolean isRequireDeviceIdle() {
408         return (constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0;
409     }
410 
411     /**
412      * @see JobInfo.Builder#setRequiresStorageNotLow(boolean)
413      */
isRequireStorageNotLow()414     public boolean isRequireStorageNotLow() {
415         return (constraintFlags & CONSTRAINT_FLAG_STORAGE_NOT_LOW) != 0;
416     }
417 
418     /**
419      * @hide
420      */
getConstraintFlags()421     public int getConstraintFlags() {
422         return constraintFlags;
423     }
424 
425     /**
426      * Which content: URIs must change for the job to be scheduled.  Returns null
427      * if there are none required.
428      * @see JobInfo.Builder#addTriggerContentUri(TriggerContentUri)
429      */
getTriggerContentUris()430     public @Nullable TriggerContentUri[] getTriggerContentUris() {
431         return triggerContentUris;
432     }
433 
434     /**
435      * When triggering on content URI changes, this is the delay from when a change
436      * is detected until the job is scheduled.
437      * @see JobInfo.Builder#setTriggerContentUpdateDelay(long)
438      */
getTriggerContentUpdateDelay()439     public long getTriggerContentUpdateDelay() {
440         return triggerContentUpdateDelay;
441     }
442 
443     /**
444      * When triggering on content URI changes, this is the maximum delay we will
445      * use before scheduling the job.
446      * @see JobInfo.Builder#setTriggerContentMaxDelay(long)
447      */
getTriggerContentMaxDelay()448     public long getTriggerContentMaxDelay() {
449         return triggerContentMaxDelay;
450     }
451 
452     /**
453      * Return the basic description of the kind of network this job requires.
454      *
455      * @deprecated This method attempts to map {@link #getRequiredNetwork()}
456      *             into the set of simple constants, which results in a loss of
457      *             fidelity. Callers should move to using
458      *             {@link #getRequiredNetwork()} directly.
459      * @see Builder#setRequiredNetworkType(int)
460      */
461     @Deprecated
getNetworkType()462     public @NetworkType int getNetworkType() {
463         if (networkRequest == null) {
464             return NETWORK_TYPE_NONE;
465         } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
466             return NETWORK_TYPE_UNMETERED;
467         } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING)) {
468             return NETWORK_TYPE_NOT_ROAMING;
469         } else if (networkRequest.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
470             return NETWORK_TYPE_CELLULAR;
471         } else {
472             return NETWORK_TYPE_ANY;
473         }
474     }
475 
476     /**
477      * Return the detailed description of the kind of network this job requires,
478      * or {@code null} if no specific kind of network is required.
479      *
480      * @see Builder#setRequiredNetwork(NetworkRequest)
481      */
getRequiredNetwork()482     public @Nullable NetworkRequest getRequiredNetwork() {
483         return networkRequest;
484     }
485 
486     /**
487      * Return the estimated size of download traffic that will be performed by
488      * this job, in bytes.
489      *
490      * @return Estimated size of download traffic, or
491      *         {@link #NETWORK_BYTES_UNKNOWN} when unknown.
492      * @see Builder#setEstimatedNetworkBytes(long, long)
493      */
getEstimatedNetworkDownloadBytes()494     public @BytesLong long getEstimatedNetworkDownloadBytes() {
495         return networkDownloadBytes;
496     }
497 
498     /**
499      * Return the estimated size of upload traffic that will be performed by
500      * this job, in bytes.
501      *
502      * @return Estimated size of upload traffic, or
503      *         {@link #NETWORK_BYTES_UNKNOWN} when unknown.
504      * @see Builder#setEstimatedNetworkBytes(long, long)
505      */
getEstimatedNetworkUploadBytes()506     public @BytesLong long getEstimatedNetworkUploadBytes() {
507         return networkUploadBytes;
508     }
509 
510     /**
511      * Set for a job that does not recur periodically, to specify a delay after which the job
512      * will be eligible for execution. This value is not set if the job recurs periodically.
513      * @see JobInfo.Builder#setMinimumLatency(long)
514      */
getMinLatencyMillis()515     public long getMinLatencyMillis() {
516         return minLatencyMillis;
517     }
518 
519     /**
520      * @see JobInfo.Builder#setOverrideDeadline(long)
521      */
getMaxExecutionDelayMillis()522     public long getMaxExecutionDelayMillis() {
523         return maxExecutionDelayMillis;
524     }
525 
526     /**
527      * Track whether this job will repeat with a given period.
528      * @see JobInfo.Builder#setPeriodic(long)
529      * @see JobInfo.Builder#setPeriodic(long, long)
530      */
isPeriodic()531     public boolean isPeriodic() {
532         return isPeriodic;
533     }
534 
535     /**
536      * @see JobInfo.Builder#setPersisted(boolean)
537      */
isPersisted()538     public boolean isPersisted() {
539         return isPersisted;
540     }
541 
542     /**
543      * Set to the interval between occurrences of this job. This value is <b>not</b> set if the
544      * job does not recur periodically.
545      * @see JobInfo.Builder#setPeriodic(long)
546      * @see JobInfo.Builder#setPeriodic(long, long)
547      */
getIntervalMillis()548     public long getIntervalMillis() {
549         return intervalMillis;
550     }
551 
552     /**
553      * Flex time for this job. Only valid if this is a periodic job.  The job can
554      * execute at any time in a window of flex length at the end of the period.
555      * @see JobInfo.Builder#setPeriodic(long)
556      * @see JobInfo.Builder#setPeriodic(long, long)
557      */
getFlexMillis()558     public long getFlexMillis() {
559         return flexMillis;
560     }
561 
562     /**
563      * The amount of time the JobScheduler will wait before rescheduling a failed job. This value
564      * will be increased depending on the backoff policy specified at job creation time. Defaults
565      * to 30 seconds, minimum is currently 10 seconds.
566      * @see JobInfo.Builder#setBackoffCriteria(long, int)
567      */
getInitialBackoffMillis()568     public long getInitialBackoffMillis() {
569         return initialBackoffMillis;
570     }
571 
572     /**
573      * Return the backoff policy of this job.
574      * @see JobInfo.Builder#setBackoffCriteria(long, int)
575      */
getBackoffPolicy()576     public @BackoffPolicy int getBackoffPolicy() {
577         return backoffPolicy;
578     }
579 
580     /**
581      * @see JobInfo.Builder#setImportantWhileForeground(boolean)
582      */
isImportantWhileForeground()583     public boolean isImportantWhileForeground() {
584         return (flags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0;
585     }
586 
587     /**
588      * @see JobInfo.Builder#setPrefetch(boolean)
589      */
isPrefetch()590     public boolean isPrefetch() {
591         return (flags & FLAG_PREFETCH) != 0;
592     }
593 
594     /**
595      * User can specify an early constraint of 0L, which is valid, so we keep track of whether the
596      * function was called at all.
597      * @hide
598      */
hasEarlyConstraint()599     public boolean hasEarlyConstraint() {
600         return hasEarlyConstraint;
601     }
602 
603     /**
604      * User can specify a late constraint of 0L, which is valid, so we keep track of whether the
605      * function was called at all.
606      * @hide
607      */
hasLateConstraint()608     public boolean hasLateConstraint() {
609         return hasLateConstraint;
610     }
611 
612     @Override
equals(Object o)613     public boolean equals(Object o) {
614         if (!(o instanceof JobInfo)) {
615             return false;
616         }
617         JobInfo j = (JobInfo) o;
618         if (jobId != j.jobId) {
619             return false;
620         }
621         // XXX won't be correct if one is parcelled and the other not.
622         if (!BaseBundle.kindofEquals(extras, j.extras)) {
623             return false;
624         }
625         // XXX won't be correct if one is parcelled and the other not.
626         if (!BaseBundle.kindofEquals(transientExtras, j.transientExtras)) {
627             return false;
628         }
629         // XXX for now we consider two different clip data objects to be different,
630         // regardless of whether their contents are the same.
631         if (clipData != j.clipData) {
632             return false;
633         }
634         if (clipGrantFlags != j.clipGrantFlags) {
635             return false;
636         }
637         if (!Objects.equals(service, j.service)) {
638             return false;
639         }
640         if (constraintFlags != j.constraintFlags) {
641             return false;
642         }
643         if (!Arrays.equals(triggerContentUris, j.triggerContentUris)) {
644             return false;
645         }
646         if (triggerContentUpdateDelay != j.triggerContentUpdateDelay) {
647             return false;
648         }
649         if (triggerContentMaxDelay != j.triggerContentMaxDelay) {
650             return false;
651         }
652         if (hasEarlyConstraint != j.hasEarlyConstraint) {
653             return false;
654         }
655         if (hasLateConstraint != j.hasLateConstraint) {
656             return false;
657         }
658         if (!Objects.equals(networkRequest, j.networkRequest)) {
659             return false;
660         }
661         if (networkDownloadBytes != j.networkDownloadBytes) {
662             return false;
663         }
664         if (networkUploadBytes != j.networkUploadBytes) {
665             return false;
666         }
667         if (minLatencyMillis != j.minLatencyMillis) {
668             return false;
669         }
670         if (maxExecutionDelayMillis != j.maxExecutionDelayMillis) {
671             return false;
672         }
673         if (isPeriodic != j.isPeriodic) {
674             return false;
675         }
676         if (isPersisted != j.isPersisted) {
677             return false;
678         }
679         if (intervalMillis != j.intervalMillis) {
680             return false;
681         }
682         if (flexMillis != j.flexMillis) {
683             return false;
684         }
685         if (initialBackoffMillis != j.initialBackoffMillis) {
686             return false;
687         }
688         if (backoffPolicy != j.backoffPolicy) {
689             return false;
690         }
691         if (priority != j.priority) {
692             return false;
693         }
694         if (flags != j.flags) {
695             return false;
696         }
697         return true;
698     }
699 
700     @Override
hashCode()701     public int hashCode() {
702         int hashCode = jobId;
703         if (extras != null) {
704             hashCode = 31 * hashCode + extras.hashCode();
705         }
706         if (transientExtras != null) {
707             hashCode = 31 * hashCode + transientExtras.hashCode();
708         }
709         if (clipData != null) {
710             hashCode = 31 * hashCode + clipData.hashCode();
711         }
712         hashCode = 31*hashCode + clipGrantFlags;
713         if (service != null) {
714             hashCode = 31 * hashCode + service.hashCode();
715         }
716         hashCode = 31 * hashCode + constraintFlags;
717         if (triggerContentUris != null) {
718             hashCode = 31 * hashCode + Arrays.hashCode(triggerContentUris);
719         }
720         hashCode = 31 * hashCode + Long.hashCode(triggerContentUpdateDelay);
721         hashCode = 31 * hashCode + Long.hashCode(triggerContentMaxDelay);
722         hashCode = 31 * hashCode + Boolean.hashCode(hasEarlyConstraint);
723         hashCode = 31 * hashCode + Boolean.hashCode(hasLateConstraint);
724         if (networkRequest != null) {
725             hashCode = 31 * hashCode + networkRequest.hashCode();
726         }
727         hashCode = 31 * hashCode + Long.hashCode(networkDownloadBytes);
728         hashCode = 31 * hashCode + Long.hashCode(networkUploadBytes);
729         hashCode = 31 * hashCode + Long.hashCode(minLatencyMillis);
730         hashCode = 31 * hashCode + Long.hashCode(maxExecutionDelayMillis);
731         hashCode = 31 * hashCode + Boolean.hashCode(isPeriodic);
732         hashCode = 31 * hashCode + Boolean.hashCode(isPersisted);
733         hashCode = 31 * hashCode + Long.hashCode(intervalMillis);
734         hashCode = 31 * hashCode + Long.hashCode(flexMillis);
735         hashCode = 31 * hashCode + Long.hashCode(initialBackoffMillis);
736         hashCode = 31 * hashCode + backoffPolicy;
737         hashCode = 31 * hashCode + priority;
738         hashCode = 31 * hashCode + flags;
739         return hashCode;
740     }
741 
JobInfo(Parcel in)742     private JobInfo(Parcel in) {
743         jobId = in.readInt();
744         extras = in.readPersistableBundle();
745         transientExtras = in.readBundle();
746         if (in.readInt() != 0) {
747             clipData = ClipData.CREATOR.createFromParcel(in);
748             clipGrantFlags = in.readInt();
749         } else {
750             clipData = null;
751             clipGrantFlags = 0;
752         }
753         service = in.readParcelable(null);
754         constraintFlags = in.readInt();
755         triggerContentUris = in.createTypedArray(TriggerContentUri.CREATOR);
756         triggerContentUpdateDelay = in.readLong();
757         triggerContentMaxDelay = in.readLong();
758         if (in.readInt() != 0) {
759             networkRequest = NetworkRequest.CREATOR.createFromParcel(in);
760         } else {
761             networkRequest = null;
762         }
763         networkDownloadBytes = in.readLong();
764         networkUploadBytes = in.readLong();
765         minLatencyMillis = in.readLong();
766         maxExecutionDelayMillis = in.readLong();
767         isPeriodic = in.readInt() == 1;
768         isPersisted = in.readInt() == 1;
769         intervalMillis = in.readLong();
770         flexMillis = in.readLong();
771         initialBackoffMillis = in.readLong();
772         backoffPolicy = in.readInt();
773         hasEarlyConstraint = in.readInt() == 1;
774         hasLateConstraint = in.readInt() == 1;
775         priority = in.readInt();
776         flags = in.readInt();
777     }
778 
JobInfo(JobInfo.Builder b)779     private JobInfo(JobInfo.Builder b) {
780         jobId = b.mJobId;
781         extras = b.mExtras.deepCopy();
782         transientExtras = b.mTransientExtras.deepCopy();
783         clipData = b.mClipData;
784         clipGrantFlags = b.mClipGrantFlags;
785         service = b.mJobService;
786         constraintFlags = b.mConstraintFlags;
787         triggerContentUris = b.mTriggerContentUris != null
788                 ? b.mTriggerContentUris.toArray(new TriggerContentUri[b.mTriggerContentUris.size()])
789                 : null;
790         triggerContentUpdateDelay = b.mTriggerContentUpdateDelay;
791         triggerContentMaxDelay = b.mTriggerContentMaxDelay;
792         networkRequest = b.mNetworkRequest;
793         networkDownloadBytes = b.mNetworkDownloadBytes;
794         networkUploadBytes = b.mNetworkUploadBytes;
795         minLatencyMillis = b.mMinLatencyMillis;
796         maxExecutionDelayMillis = b.mMaxExecutionDelayMillis;
797         isPeriodic = b.mIsPeriodic;
798         isPersisted = b.mIsPersisted;
799         intervalMillis = b.mIntervalMillis;
800         flexMillis = b.mFlexMillis;
801         initialBackoffMillis = b.mInitialBackoffMillis;
802         backoffPolicy = b.mBackoffPolicy;
803         hasEarlyConstraint = b.mHasEarlyConstraint;
804         hasLateConstraint = b.mHasLateConstraint;
805         priority = b.mPriority;
806         flags = b.mFlags;
807     }
808 
809     @Override
describeContents()810     public int describeContents() {
811         return 0;
812     }
813 
814     @Override
writeToParcel(Parcel out, int flags)815     public void writeToParcel(Parcel out, int flags) {
816         out.writeInt(jobId);
817         out.writePersistableBundle(extras);
818         out.writeBundle(transientExtras);
819         if (clipData != null) {
820             out.writeInt(1);
821             clipData.writeToParcel(out, flags);
822             out.writeInt(clipGrantFlags);
823         } else {
824             out.writeInt(0);
825         }
826         out.writeParcelable(service, flags);
827         out.writeInt(constraintFlags);
828         out.writeTypedArray(triggerContentUris, flags);
829         out.writeLong(triggerContentUpdateDelay);
830         out.writeLong(triggerContentMaxDelay);
831         if (networkRequest != null) {
832             out.writeInt(1);
833             networkRequest.writeToParcel(out, flags);
834         } else {
835             out.writeInt(0);
836         }
837         out.writeLong(networkDownloadBytes);
838         out.writeLong(networkUploadBytes);
839         out.writeLong(minLatencyMillis);
840         out.writeLong(maxExecutionDelayMillis);
841         out.writeInt(isPeriodic ? 1 : 0);
842         out.writeInt(isPersisted ? 1 : 0);
843         out.writeLong(intervalMillis);
844         out.writeLong(flexMillis);
845         out.writeLong(initialBackoffMillis);
846         out.writeInt(backoffPolicy);
847         out.writeInt(hasEarlyConstraint ? 1 : 0);
848         out.writeInt(hasLateConstraint ? 1 : 0);
849         out.writeInt(priority);
850         out.writeInt(this.flags);
851     }
852 
853     public static final @android.annotation.NonNull Creator<JobInfo> CREATOR = new Creator<JobInfo>() {
854         @Override
855         public JobInfo createFromParcel(Parcel in) {
856             return new JobInfo(in);
857         }
858 
859         @Override
860         public JobInfo[] newArray(int size) {
861             return new JobInfo[size];
862         }
863     };
864 
865     @Override
toString()866     public String toString() {
867         return "(job:" + jobId + "/" + service.flattenToShortString() + ")";
868     }
869 
870     /**
871      * Information about a content URI modification that a job would like to
872      * trigger on.
873      */
874     public static final class TriggerContentUri implements Parcelable {
875         private final Uri mUri;
876         private final int mFlags;
877 
878         /** @hide */
879         @Retention(RetentionPolicy.SOURCE)
880         @IntDef(flag = true, prefix = { "FLAG_" }, value = {
881                 FLAG_NOTIFY_FOR_DESCENDANTS,
882         })
883         public @interface Flags { }
884 
885         /**
886          * Flag for trigger: also trigger if any descendants of the given URI change.
887          * Corresponds to the <var>notifyForDescendants</var> of
888          * {@link android.content.ContentResolver#registerContentObserver}.
889          */
890         public static final int FLAG_NOTIFY_FOR_DESCENDANTS = 1<<0;
891 
892         /**
893          * Create a new trigger description.
894          * @param uri The URI to observe.  Must be non-null.
895          * @param flags Flags for the observer.
896          */
TriggerContentUri(@onNull Uri uri, @Flags int flags)897         public TriggerContentUri(@NonNull Uri uri, @Flags int flags) {
898             mUri = Objects.requireNonNull(uri);
899             mFlags = flags;
900         }
901 
902         /**
903          * Return the Uri this trigger was created for.
904          */
getUri()905         public Uri getUri() {
906             return mUri;
907         }
908 
909         /**
910          * Return the flags supplied for the trigger.
911          */
getFlags()912         public @Flags int getFlags() {
913             return mFlags;
914         }
915 
916         @Override
equals(Object o)917         public boolean equals(Object o) {
918             if (!(o instanceof TriggerContentUri)) {
919                 return false;
920             }
921             TriggerContentUri t = (TriggerContentUri) o;
922             return Objects.equals(t.mUri, mUri) && t.mFlags == mFlags;
923         }
924 
925         @Override
hashCode()926         public int hashCode() {
927             return (mUri == null ? 0 : mUri.hashCode()) ^ mFlags;
928         }
929 
TriggerContentUri(Parcel in)930         private TriggerContentUri(Parcel in) {
931             mUri = Uri.CREATOR.createFromParcel(in);
932             mFlags = in.readInt();
933         }
934 
935         @Override
describeContents()936         public int describeContents() {
937             return 0;
938         }
939 
940         @Override
writeToParcel(Parcel out, int flags)941         public void writeToParcel(Parcel out, int flags) {
942             mUri.writeToParcel(out, flags);
943             out.writeInt(mFlags);
944         }
945 
946         public static final @android.annotation.NonNull Creator<TriggerContentUri> CREATOR = new Creator<TriggerContentUri>() {
947             @Override
948             public TriggerContentUri createFromParcel(Parcel in) {
949                 return new TriggerContentUri(in);
950             }
951 
952             @Override
953             public TriggerContentUri[] newArray(int size) {
954                 return new TriggerContentUri[size];
955             }
956         };
957     }
958 
959     /** Builder class for constructing {@link JobInfo} objects. */
960     public static final class Builder {
961         private final int mJobId;
962         private final ComponentName mJobService;
963         private PersistableBundle mExtras = PersistableBundle.EMPTY;
964         private Bundle mTransientExtras = Bundle.EMPTY;
965         private ClipData mClipData;
966         private int mClipGrantFlags;
967         private int mPriority = PRIORITY_DEFAULT;
968         private int mFlags;
969         // Requirements.
970         private int mConstraintFlags;
971         private NetworkRequest mNetworkRequest;
972         private long mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN;
973         private long mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN;
974         private ArrayList<TriggerContentUri> mTriggerContentUris;
975         private long mTriggerContentUpdateDelay = -1;
976         private long mTriggerContentMaxDelay = -1;
977         private boolean mIsPersisted;
978         // One-off parameters.
979         private long mMinLatencyMillis;
980         private long mMaxExecutionDelayMillis;
981         // Periodic parameters.
982         private boolean mIsPeriodic;
983         private boolean mHasEarlyConstraint;
984         private boolean mHasLateConstraint;
985         private long mIntervalMillis;
986         private long mFlexMillis;
987         // Back-off parameters.
988         private long mInitialBackoffMillis = DEFAULT_INITIAL_BACKOFF_MILLIS;
989         private int mBackoffPolicy = DEFAULT_BACKOFF_POLICY;
990         /** Easy way to track whether the client has tried to set a back-off policy. */
991         private boolean mBackoffPolicySet = false;
992 
993         /**
994          * Initialize a new Builder to construct a {@link JobInfo}.
995          *
996          * @param jobId Application-provided id for this job. Subsequent calls to cancel, or
997          * jobs created with the same jobId, will update the pre-existing job with
998          * the same id.  This ID must be unique across all clients of the same uid
999          * (not just the same package).  You will want to make sure this is a stable
1000          * id across app updates, so probably not based on a resource ID.
1001          * @param jobService The endpoint that you implement that will receive the callback from the
1002          * JobScheduler.
1003          */
Builder(int jobId, @NonNull ComponentName jobService)1004         public Builder(int jobId, @NonNull ComponentName jobService) {
1005             mJobService = jobService;
1006             mJobId = jobId;
1007         }
1008 
1009         /** @hide */
1010         @UnsupportedAppUsage
setPriority(int priority)1011         public Builder setPriority(int priority) {
1012             mPriority = priority;
1013             return this;
1014         }
1015 
1016         /** @hide */
1017         @UnsupportedAppUsage
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          * Set the estimated size of network traffic that will be performed by
1162          * this job, in bytes.
1163          * <p>
1164          * Apps are encouraged to provide values that are as accurate as
1165          * possible, but when the exact size isn't available, an
1166          * order-of-magnitude estimate can be provided instead. Here are some
1167          * specific examples:
1168          * <ul>
1169          * <li>A job that is backing up a photo knows the exact size of that
1170          * photo, so it should provide that size as the estimate.
1171          * <li>A job that refreshes top news stories wouldn't know an exact
1172          * size, but if the size is expected to be consistently around 100KB, it
1173          * can provide that order-of-magnitude value as the estimate.
1174          * <li>A job that synchronizes email could end up using an extreme range
1175          * of data, from under 1KB when nothing has changed, to dozens of MB
1176          * when there are new emails with attachments. Jobs that cannot provide
1177          * reasonable estimates should use the sentinel value
1178          * {@link JobInfo#NETWORK_BYTES_UNKNOWN}.
1179          * </ul>
1180          * Note that the system may choose to delay jobs with large network
1181          * usage estimates when the device has a poor network connection, in
1182          * order to save battery.
1183          * <p>
1184          * The values provided here only reflect the traffic that will be
1185          * performed by the base job; if you're using {@link JobWorkItem} then
1186          * you also need to define the network traffic used by each work item
1187          * when constructing them.
1188          *
1189          * @param downloadBytes The estimated size of network traffic that will
1190          *            be downloaded by this job, in bytes.
1191          * @param uploadBytes The estimated size of network traffic that will be
1192          *            uploaded by this job, in bytes.
1193          * @see JobInfo#getEstimatedNetworkDownloadBytes()
1194          * @see JobInfo#getEstimatedNetworkUploadBytes()
1195          * @see JobWorkItem#JobWorkItem(android.content.Intent, long, long)
1196          */
setEstimatedNetworkBytes(@ytesLong long downloadBytes, @BytesLong long uploadBytes)1197         public Builder setEstimatedNetworkBytes(@BytesLong long downloadBytes,
1198                 @BytesLong long uploadBytes) {
1199             mNetworkDownloadBytes = downloadBytes;
1200             mNetworkUploadBytes = uploadBytes;
1201             return this;
1202         }
1203 
1204         /**
1205          * Specify that to run this job, the device must be charging (or be a
1206          * non-battery-powered device connected to permanent power, such as Android TV
1207          * devices). This defaults to {@code false}.
1208          *
1209          * <p class="note">For purposes of running jobs, a battery-powered device
1210          * "charging" is not quite the same as simply being connected to power.  If the
1211          * device is so busy that the battery is draining despite a power connection, jobs
1212          * with this constraint will <em>not</em> run.  This can happen during some
1213          * common use cases such as video chat, particularly if the device is plugged in
1214          * to USB rather than to wall power.
1215          *
1216          * @param requiresCharging Pass {@code true} to require that the device be
1217          *     charging in order to run the job.
1218          * @see JobInfo#isRequireCharging()
1219          */
setRequiresCharging(boolean requiresCharging)1220         public Builder setRequiresCharging(boolean requiresCharging) {
1221             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_CHARGING)
1222                     | (requiresCharging ? CONSTRAINT_FLAG_CHARGING : 0);
1223             return this;
1224         }
1225 
1226         /**
1227          * Specify that to run this job, the device's battery level must not be low.
1228          * This defaults to false.  If true, the job will only run when the battery level
1229          * is not low, which is generally the point where the user is given a "low battery"
1230          * warning.
1231          * @param batteryNotLow Whether or not the device's battery level must not be low.
1232          * @see JobInfo#isRequireBatteryNotLow()
1233          */
setRequiresBatteryNotLow(boolean batteryNotLow)1234         public Builder setRequiresBatteryNotLow(boolean batteryNotLow) {
1235             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_BATTERY_NOT_LOW)
1236                     | (batteryNotLow ? CONSTRAINT_FLAG_BATTERY_NOT_LOW : 0);
1237             return this;
1238         }
1239 
1240         /**
1241          * When set {@code true}, ensure that this job will not run if the device is in active use.
1242          * The default state is {@code false}: that is, the for the job to be runnable even when
1243          * someone is interacting with the device.
1244          *
1245          * <p>This state is a loose definition provided by the system. In general, it means that
1246          * the device is not currently being used interactively, and has not been in use for some
1247          * time. As such, it is a good time to perform resource heavy jobs. Bear in mind that
1248          * battery usage will still be attributed to your application, and surfaced to the user in
1249          * battery stats.</p>
1250          *
1251          * <p class="note">Despite the similar naming, this job constraint is <em>not</em>
1252          * related to the system's "device idle" or "doze" states.  This constraint only
1253          * determines whether a job is allowed to run while the device is directly in use.
1254          *
1255          * @param requiresDeviceIdle Pass {@code true} to prevent the job from running
1256          *     while the device is being used interactively.
1257          * @see JobInfo#isRequireDeviceIdle()
1258          */
setRequiresDeviceIdle(boolean requiresDeviceIdle)1259         public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) {
1260             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_DEVICE_IDLE)
1261                     | (requiresDeviceIdle ? CONSTRAINT_FLAG_DEVICE_IDLE : 0);
1262             return this;
1263         }
1264 
1265         /**
1266          * Specify that to run this job, the device's available storage must not be low.
1267          * This defaults to false.  If true, the job will only run when the device is not
1268          * in a low storage state, which is generally the point where the user is given a
1269          * "low storage" warning.
1270          * @param storageNotLow Whether or not the device's available storage must not be low.
1271          * @see JobInfo#isRequireStorageNotLow()
1272          */
setRequiresStorageNotLow(boolean storageNotLow)1273         public Builder setRequiresStorageNotLow(boolean storageNotLow) {
1274             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_STORAGE_NOT_LOW)
1275                     | (storageNotLow ? CONSTRAINT_FLAG_STORAGE_NOT_LOW : 0);
1276             return this;
1277         }
1278 
1279         /**
1280          * Add a new content: URI that will be monitored with a
1281          * {@link android.database.ContentObserver}, and will cause the job to execute if changed.
1282          * If you have any trigger content URIs associated with a job, it will not execute until
1283          * there has been a change report for one or more of them.
1284          *
1285          * <p>Note that trigger URIs can not be used in combination with
1286          * {@link #setPeriodic(long)} or {@link #setPersisted(boolean)}.  To continually monitor
1287          * for content changes, you need to schedule a new JobInfo observing the same URIs
1288          * before you finish execution of the JobService handling the most recent changes.
1289          * Following this pattern will ensure you do not lose any content changes: while your
1290          * job is running, the system will continue monitoring for content changes, and propagate
1291          * any it sees over to the next job you schedule.</p>
1292          *
1293          * <p>Because setting this property is not compatible with periodic or
1294          * persisted jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when
1295          * {@link android.app.job.JobInfo.Builder#build()} is called.</p>
1296          *
1297          * <p>The following example shows how this feature can be used to monitor for changes
1298          * in the photos on a device.</p>
1299          *
1300          * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/PhotosContentJob.java
1301          *      job}
1302          *
1303          * @param uri The content: URI to monitor.
1304          * @see JobInfo#getTriggerContentUris()
1305          */
addTriggerContentUri(@onNull TriggerContentUri uri)1306         public Builder addTriggerContentUri(@NonNull TriggerContentUri uri) {
1307             if (mTriggerContentUris == null) {
1308                 mTriggerContentUris = new ArrayList<>();
1309             }
1310             mTriggerContentUris.add(uri);
1311             return this;
1312         }
1313 
1314         /**
1315          * Set the delay (in milliseconds) from when a content change is detected until
1316          * the job is scheduled.  If there are more changes during that time, the delay
1317          * will be reset to start at the time of the most recent change.
1318          * @param durationMs Delay after most recent content change, in milliseconds.
1319          * @see JobInfo#getTriggerContentUpdateDelay()
1320          */
setTriggerContentUpdateDelay(long durationMs)1321         public Builder setTriggerContentUpdateDelay(long durationMs) {
1322             mTriggerContentUpdateDelay = durationMs;
1323             return this;
1324         }
1325 
1326         /**
1327          * Set the maximum total delay (in milliseconds) that is allowed from the first
1328          * time a content change is detected until the job is scheduled.
1329          * @param durationMs Delay after initial content change, in milliseconds.
1330          * @see JobInfo#getTriggerContentMaxDelay()
1331          */
setTriggerContentMaxDelay(long durationMs)1332         public Builder setTriggerContentMaxDelay(long durationMs) {
1333             mTriggerContentMaxDelay = durationMs;
1334             return this;
1335         }
1336 
1337         /**
1338          * Specify that this job should recur with the provided interval, not more than once per
1339          * period. You have no control over when within this interval this job will be executed,
1340          * only the guarantee that it will be executed at most once within this interval.
1341          * Setting this function on the builder with {@link #setMinimumLatency(long)} or
1342          * {@link #setOverrideDeadline(long)} will result in an error.
1343          * @param intervalMillis Millisecond interval for which this job will repeat.
1344          * @see JobInfo#getIntervalMillis()
1345          * @see JobInfo#getFlexMillis()
1346          */
setPeriodic(long intervalMillis)1347         public Builder setPeriodic(long intervalMillis) {
1348             return setPeriodic(intervalMillis, intervalMillis);
1349         }
1350 
1351         /**
1352          * Specify that this job should recur with the provided interval and flex. The job can
1353          * execute at any time in a window of flex length at the end of the period.
1354          * @param intervalMillis Millisecond interval for which this job will repeat. A minimum
1355          *                       value of {@link #getMinPeriodMillis()} is enforced.
1356          * @param flexMillis Millisecond flex for this job. Flex is clamped to be at least
1357          *                   {@link #getMinFlexMillis()} or 5 percent of the period, whichever is
1358          *                   higher.
1359          * @see JobInfo#getIntervalMillis()
1360          * @see JobInfo#getFlexMillis()
1361          */
setPeriodic(long intervalMillis, long flexMillis)1362         public Builder setPeriodic(long intervalMillis, long flexMillis) {
1363             final long minPeriod = getMinPeriodMillis();
1364             if (intervalMillis < minPeriod) {
1365                 Log.w(TAG, "Requested interval " + formatDuration(intervalMillis) + " for job "
1366                         + mJobId + " is too small; raising to " + formatDuration(minPeriod));
1367                 intervalMillis = minPeriod;
1368             }
1369 
1370             final long percentClamp = 5 * intervalMillis / 100;
1371             final long minFlex = Math.max(percentClamp, getMinFlexMillis());
1372             if (flexMillis < minFlex) {
1373                 Log.w(TAG, "Requested flex " + formatDuration(flexMillis) + " for job " + mJobId
1374                         + " is too small; raising to " + formatDuration(minFlex));
1375                 flexMillis = minFlex;
1376             }
1377 
1378             mIsPeriodic = true;
1379             mIntervalMillis = intervalMillis;
1380             mFlexMillis = flexMillis;
1381             mHasEarlyConstraint = mHasLateConstraint = true;
1382             return this;
1383         }
1384 
1385         /**
1386          * Specify that this job should be delayed by the provided amount of time.
1387          * Because it doesn't make sense setting this property on a periodic job, doing so will
1388          * throw an {@link java.lang.IllegalArgumentException} when
1389          * {@link android.app.job.JobInfo.Builder#build()} is called.
1390          * @param minLatencyMillis Milliseconds before which this job will not be considered for
1391          *                         execution.
1392          * @see JobInfo#getMinLatencyMillis()
1393          */
setMinimumLatency(long minLatencyMillis)1394         public Builder setMinimumLatency(long minLatencyMillis) {
1395             mMinLatencyMillis = minLatencyMillis;
1396             mHasEarlyConstraint = true;
1397             return this;
1398         }
1399 
1400         /**
1401          * Set deadline which is the maximum scheduling latency. The job will be run by this
1402          * deadline even if other requirements are not met. Because it doesn't make sense setting
1403          * this property on a periodic job, doing so will throw an
1404          * {@link java.lang.IllegalArgumentException} when
1405          * {@link android.app.job.JobInfo.Builder#build()} is called.
1406          * @see JobInfo#getMaxExecutionDelayMillis()
1407          */
setOverrideDeadline(long maxExecutionDelayMillis)1408         public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
1409             mMaxExecutionDelayMillis = maxExecutionDelayMillis;
1410             mHasLateConstraint = true;
1411             return this;
1412         }
1413 
1414         /**
1415          * Set up the back-off/retry policy.
1416          * This defaults to some respectable values: {30 seconds, Exponential}. We cap back-off at
1417          * 5hrs.
1418          * Note that trying to set a backoff criteria for a job with
1419          * {@link #setRequiresDeviceIdle(boolean)} will throw an exception when you call build().
1420          * This is because back-off typically does not make sense for these types of jobs. See
1421          * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)}
1422          * for more description of the return value for the case of a job executing while in idle
1423          * mode.
1424          * @param initialBackoffMillis Millisecond time interval to wait initially when job has
1425          *                             failed.
1426          * @see JobInfo#getInitialBackoffMillis()
1427          * @see JobInfo#getBackoffPolicy()
1428          */
setBackoffCriteria(long initialBackoffMillis, @BackoffPolicy int backoffPolicy)1429         public Builder setBackoffCriteria(long initialBackoffMillis,
1430                 @BackoffPolicy int backoffPolicy) {
1431             final long minBackoff = getMinBackoffMillis();
1432             if (initialBackoffMillis < minBackoff) {
1433                 Log.w(TAG, "Requested backoff " + formatDuration(initialBackoffMillis) + " for job "
1434                         + mJobId + " is too small; raising to " + formatDuration(minBackoff));
1435                 initialBackoffMillis = minBackoff;
1436             }
1437 
1438             mBackoffPolicySet = true;
1439             mInitialBackoffMillis = initialBackoffMillis;
1440             mBackoffPolicy = backoffPolicy;
1441             return this;
1442         }
1443 
1444         /**
1445          * Setting this to true indicates that this job is important while the scheduling app
1446          * is in the foreground or on the temporary whitelist for background restrictions.
1447          * This means that the system will relax doze restrictions on this job during this time.
1448          *
1449          * Apps should use this flag only for short jobs that are essential for the app to function
1450          * properly in the foreground.
1451          *
1452          * Note that once the scheduling app is no longer whitelisted from background restrictions
1453          * and in the background, or the job failed due to unsatisfied constraints,
1454          * this job should be expected to behave like other jobs without this flag.
1455          *
1456          * @param importantWhileForeground whether to relax doze restrictions for this job when the
1457          *                                 app is in the foreground. False by default.
1458          * @see JobInfo#isImportantWhileForeground()
1459          */
setImportantWhileForeground(boolean importantWhileForeground)1460         public Builder setImportantWhileForeground(boolean importantWhileForeground) {
1461             if (importantWhileForeground) {
1462                 mFlags |= FLAG_IMPORTANT_WHILE_FOREGROUND;
1463             } else {
1464                 mFlags &= (~FLAG_IMPORTANT_WHILE_FOREGROUND);
1465             }
1466             return this;
1467         }
1468 
1469         /**
1470          * Setting this to true indicates that this job is designed to prefetch
1471          * content that will make a material improvement to the experience of
1472          * the specific user of this device. For example, fetching top headlines
1473          * of interest to the current user.
1474          * <p>
1475          * The system may use this signal to relax the network constraints you
1476          * originally requested, such as allowing a
1477          * {@link JobInfo#NETWORK_TYPE_UNMETERED} job to run over a metered
1478          * network when there is a surplus of metered data available. The system
1479          * may also use this signal in combination with end user usage patterns
1480          * to ensure data is prefetched before the user launches your app.
1481          * @see JobInfo#isPrefetch()
1482          */
setPrefetch(boolean prefetch)1483         public Builder setPrefetch(boolean prefetch) {
1484             if (prefetch) {
1485                 mFlags |= FLAG_PREFETCH;
1486             } else {
1487                 mFlags &= (~FLAG_PREFETCH);
1488             }
1489             return this;
1490         }
1491 
1492         /**
1493          * Set whether or not to persist this job across device reboots.
1494          *
1495          * @param isPersisted True to indicate that the job will be written to
1496          *            disk and loaded at boot.
1497          * @see JobInfo#isPersisted()
1498          */
1499         @RequiresPermission(android.Manifest.permission.RECEIVE_BOOT_COMPLETED)
setPersisted(boolean isPersisted)1500         public Builder setPersisted(boolean isPersisted) {
1501             mIsPersisted = isPersisted;
1502             return this;
1503         }
1504 
1505         /**
1506          * @return The job object to hand to the JobScheduler. This object is immutable.
1507          */
build()1508         public JobInfo build() {
1509             // Check that network estimates require network type
1510             if ((mNetworkDownloadBytes > 0 || mNetworkUploadBytes > 0) && mNetworkRequest == null) {
1511                 throw new IllegalArgumentException(
1512                         "Can't provide estimated network usage without requiring a network");
1513             }
1514             // We can't serialize network specifiers
1515             if (mIsPersisted && mNetworkRequest != null
1516                     && mNetworkRequest.networkCapabilities.getNetworkSpecifier() != null) {
1517                 throw new IllegalArgumentException(
1518                         "Network specifiers aren't supported for persistent jobs");
1519             }
1520             // Check that a deadline was not set on a periodic job.
1521             if (mIsPeriodic) {
1522                 if (mMaxExecutionDelayMillis != 0L) {
1523                     throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " +
1524                             "periodic job.");
1525                 }
1526                 if (mMinLatencyMillis != 0L) {
1527                     throw new IllegalArgumentException("Can't call setMinimumLatency() on a " +
1528                             "periodic job");
1529                 }
1530                 if (mTriggerContentUris != null) {
1531                     throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " +
1532                             "periodic job");
1533                 }
1534             }
1535             if (mIsPersisted) {
1536                 if (mTriggerContentUris != null) {
1537                     throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " +
1538                             "persisted job");
1539                 }
1540                 if (!mTransientExtras.isEmpty()) {
1541                     throw new IllegalArgumentException("Can't call setTransientExtras() on a " +
1542                             "persisted job");
1543                 }
1544                 if (mClipData != null) {
1545                     throw new IllegalArgumentException("Can't call setClipData() on a " +
1546                             "persisted job");
1547                 }
1548             }
1549             if ((mFlags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0 && mHasEarlyConstraint) {
1550                 throw new IllegalArgumentException("An important while foreground job cannot "
1551                         + "have a time delay");
1552             }
1553             if (mBackoffPolicySet && (mConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) {
1554                 throw new IllegalArgumentException("An idle mode job will not respect any" +
1555                         " back-off policy, so calling setBackoffCriteria with" +
1556                         " setRequiresDeviceIdle is an error.");
1557             }
1558             return new JobInfo(this);
1559         }
1560 
1561         /**
1562          * @hide
1563          */
summarize()1564         public String summarize() {
1565             final String service = (mJobService != null)
1566                     ? mJobService.flattenToShortString()
1567                     : "null";
1568             return "JobInfo.Builder{job:" + mJobId + "/" + service + "}";
1569         }
1570     }
1571 
1572     /**
1573      * Convert a priority integer into a human readable string for debugging.
1574      * @hide
1575      */
getPriorityString(int priority)1576     public static String getPriorityString(int priority) {
1577         switch (priority) {
1578             case PRIORITY_DEFAULT:
1579                 return PRIORITY_DEFAULT + " [DEFAULT]";
1580             case PRIORITY_SYNC_EXPEDITED:
1581                 return PRIORITY_SYNC_EXPEDITED + " [SYNC_EXPEDITED]";
1582             case PRIORITY_SYNC_INITIALIZATION:
1583                 return PRIORITY_SYNC_INITIALIZATION + " [SYNC_INITIALIZATION]";
1584             case PRIORITY_BOUND_FOREGROUND_SERVICE:
1585                 return PRIORITY_BOUND_FOREGROUND_SERVICE + " [BFGS_APP]";
1586             case PRIORITY_FOREGROUND_SERVICE:
1587                 return PRIORITY_FOREGROUND_SERVICE + " [FGS_APP]";
1588             case PRIORITY_TOP_APP:
1589                 return PRIORITY_TOP_APP + " [TOP_APP]";
1590 
1591                 // PRIORITY_ADJ_* are adjustments and not used as real priorities.
1592                 // No need to convert to strings.
1593         }
1594         return priority + " [UNKNOWN]";
1595     }
1596 }
1597