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