JobInfo.java revision e3f617b23f202e11d4ee67d322609ee7b07b11bb
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 android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.content.ComponentName;
22import android.net.Uri;
23import android.os.Parcel;
24import android.os.Parcelable;
25import android.os.PersistableBundle;
26import android.util.Log;
27import static android.util.TimeUtils.formatForLogging;
28
29import java.util.ArrayList;
30
31/**
32 * Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the
33 * parameters required to schedule work against the calling application. These are constructed
34 * using the {@link JobInfo.Builder}.
35 * You must specify at least one sort of constraint on the JobInfo object that you are creating.
36 * The goal here is to provide the scheduler with high-level semantics about the work you want to
37 * accomplish. Doing otherwise with throw an exception in your app.
38 */
39public class JobInfo implements Parcelable {
40    private static String TAG = "JobInfo";
41    /** Default. */
42    public static final int NETWORK_TYPE_NONE = 0;
43    /** This job requires network connectivity. */
44    public static final int NETWORK_TYPE_ANY = 1;
45    /** This job requires network connectivity that is unmetered. */
46    public static final int NETWORK_TYPE_UNMETERED = 2;
47
48    /**
49     * Amount of backoff a job has initially by default, in milliseconds.
50     */
51    public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L;  // 30 seconds.
52
53    /**
54     * Maximum backoff we allow for a job, in milliseconds.
55     */
56    public static final long MAX_BACKOFF_DELAY_MILLIS = 5 * 60 * 60 * 1000;  // 5 hours.
57
58    /**
59     * Linearly back-off a failed job. See
60     * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)}
61     * retry_time(current_time, num_failures) =
62     *     current_time + initial_backoff_millis * num_failures, num_failures >= 1
63     */
64    public static final int BACKOFF_POLICY_LINEAR = 0;
65
66    /**
67     * Exponentially back-off a failed job. See
68     * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)}
69     *
70     * retry_time(current_time, num_failures) =
71     *     current_time + initial_backoff_millis * 2 ^ (num_failures - 1), num_failures >= 1
72     */
73    public static final int BACKOFF_POLICY_EXPONENTIAL = 1;
74
75    /* Minimum interval for a periodic job, in milliseconds. */
76    private static final long MIN_PERIOD_MILLIS = 15 * 60 * 1000L;   // 15 minutes
77
78    /* Minimum flex for a periodic job, in milliseconds. */
79    private static final long MIN_FLEX_MILLIS = 5 * 60 * 1000L; // 5 minutes
80
81    /**
82     * Query the minimum interval allowed for periodic scheduled jobs.  Attempting
83     * to declare a smaller period that this when scheduling a job will result in a
84     * job that is still periodic, but will run with this effective period.
85     *
86     * @return The minimum available interval for scheduling periodic jobs, in milliseconds.
87     */
88    public static final long getMinimumPeriod() {
89        return MIN_PERIOD_MILLIS;
90    }
91
92    /**
93     * Query the minimum flex time allowed for periodic scheduled jobs.  Attempting
94     * to declare a shorter flex time than this when scheduling such a job will
95     * result in this amount as the effective flex time for the job.
96     *
97     * @return The minimum available flex time for scheduling periodic jobs, in milliseconds.
98     */
99    public static final long getMinimumFlex() {
100        return MIN_FLEX_MILLIS;
101    }
102
103    /**
104     * Default type of backoff.
105     * @hide
106     */
107    public static final int DEFAULT_BACKOFF_POLICY = BACKOFF_POLICY_EXPONENTIAL;
108
109    /**
110     * Default of {@link #getPriority}.
111     * @hide
112     */
113    public static final int PRIORITY_DEFAULT = 0;
114
115    /**
116     * Value of {@link #getPriority} for expedited syncs.
117     * @hide
118     */
119    public static final int PRIORITY_SYNC_EXPEDITED = 10;
120
121    /**
122     * Value of {@link #getPriority} for first time initialization syncs.
123     * @hide
124     */
125    public static final int PRIORITY_SYNC_INITIALIZATION = 20;
126
127    /**
128     * Value of {@link #getPriority} for a foreground app (overrides the supplied
129     * JobInfo priority if it is smaller).
130     * @hide
131     */
132    public static final int PRIORITY_FOREGROUND_APP = 30;
133
134    /**
135     * Value of {@link #getPriority} for the current top app (overrides the supplied
136     * JobInfo priority if it is smaller).
137     * @hide
138     */
139    public static final int PRIORITY_TOP_APP = 40;
140
141    /**
142     * Adjustment of {@link #getPriority} if the app has often (50% or more of the time)
143     * been running jobs.
144     * @hide
145     */
146    public static final int PRIORITY_ADJ_OFTEN_RUNNING = -40;
147
148    /**
149     * Adjustment of {@link #getPriority} if the app has always (90% or more of the time)
150     * been running jobs.
151     * @hide
152     */
153    public static final int PRIORITY_ADJ_ALWAYS_RUNNING = -80;
154
155    private final int jobId;
156    private final PersistableBundle extras;
157    private final ComponentName service;
158    private final boolean requireCharging;
159    private final boolean requireDeviceIdle;
160    private final TriggerContentUri[] triggerContentUris;
161    private final boolean hasEarlyConstraint;
162    private final boolean hasLateConstraint;
163    private final int networkType;
164    private final long minLatencyMillis;
165    private final long maxExecutionDelayMillis;
166    private final boolean isPeriodic;
167    private final boolean isPersisted;
168    private final long intervalMillis;
169    private final long flexMillis;
170    private final long initialBackoffMillis;
171    private final int backoffPolicy;
172    private final int priority;
173
174    /**
175     * Unique job id associated with this class. This is assigned to your job by the scheduler.
176     */
177    public int getId() {
178        return jobId;
179    }
180
181    /**
182     * Bundle of extras which are returned to your application at execution time.
183     */
184    public PersistableBundle getExtras() {
185        return extras;
186    }
187
188    /**
189     * Name of the service endpoint that will be called back into by the JobScheduler.
190     */
191    public ComponentName getService() {
192        return service;
193    }
194
195    /** @hide */
196    public int getPriority() {
197        return priority;
198    }
199
200    /**
201     * Whether this job needs the device to be plugged in.
202     */
203    public boolean isRequireCharging() {
204        return requireCharging;
205    }
206
207    /**
208     * Whether this job needs the device to be in an Idle maintenance window.
209     */
210    public boolean isRequireDeviceIdle() {
211        return requireDeviceIdle;
212    }
213
214    /**
215     * Which content: URIs must change for the job to be scheduled.  Returns null
216     * if there are none required.
217     */
218    @Nullable
219    public TriggerContentUri[] getTriggerContentUris() {
220        return triggerContentUris;
221    }
222
223    /**
224     * One of {@link android.app.job.JobInfo#NETWORK_TYPE_ANY},
225     * {@link android.app.job.JobInfo#NETWORK_TYPE_NONE}, or
226     * {@link android.app.job.JobInfo#NETWORK_TYPE_UNMETERED}.
227     */
228    public int getNetworkType() {
229        return networkType;
230    }
231
232    /**
233     * Set for a job that does not recur periodically, to specify a delay after which the job
234     * will be eligible for execution. This value is not set if the job recurs periodically.
235     */
236    public long getMinLatencyMillis() {
237        return minLatencyMillis;
238    }
239
240    /**
241     * See {@link Builder#setOverrideDeadline(long)}. This value is not set if the job recurs
242     * periodically.
243     */
244    public long getMaxExecutionDelayMillis() {
245        return maxExecutionDelayMillis;
246    }
247
248    /**
249     * Track whether this job will repeat with a given period.
250     */
251    public boolean isPeriodic() {
252        return isPeriodic;
253    }
254
255    /**
256     * @return Whether or not this job should be persisted across device reboots.
257     */
258    public boolean isPersisted() {
259        return isPersisted;
260    }
261
262    /**
263     * Set to the interval between occurrences of this job. This value is <b>not</b> set if the
264     * job does not recur periodically.
265     */
266    public long getIntervalMillis() {
267        return intervalMillis >= getMinimumPeriod() ? intervalMillis : getMinimumPeriod();
268    }
269
270    /**
271     * Flex time for this job. Only valid if this is a periodic job.  The job can
272     * execute at any time in a window of flex length at the end of the period.
273     */
274    public long getFlexMillis() {
275        long interval = getIntervalMillis();
276        long percentClamp = 5 * interval / 100;
277        long clampedFlex = Math.max(flexMillis, Math.max(percentClamp, getMinimumFlex()));
278        return clampedFlex <= interval ? clampedFlex : interval;
279    }
280
281    /**
282     * The amount of time the JobScheduler will wait before rescheduling a failed job. This value
283     * will be increased depending on the backoff policy specified at job creation time. Defaults
284     * to 5 seconds.
285     */
286    public long getInitialBackoffMillis() {
287        return initialBackoffMillis;
288    }
289
290    /**
291     * One of either {@link android.app.job.JobInfo#BACKOFF_POLICY_EXPONENTIAL}, or
292     * {@link android.app.job.JobInfo#BACKOFF_POLICY_LINEAR}, depending on which criteria you set
293     * when creating this job.
294     */
295    public int getBackoffPolicy() {
296        return backoffPolicy;
297    }
298
299    /**
300     * User can specify an early constraint of 0L, which is valid, so we keep track of whether the
301     * function was called at all.
302     * @hide
303     */
304    public boolean hasEarlyConstraint() {
305        return hasEarlyConstraint;
306    }
307
308    /**
309     * User can specify a late constraint of 0L, which is valid, so we keep track of whether the
310     * function was called at all.
311     * @hide
312     */
313    public boolean hasLateConstraint() {
314        return hasLateConstraint;
315    }
316
317    private JobInfo(Parcel in) {
318        jobId = in.readInt();
319        extras = in.readPersistableBundle();
320        service = in.readParcelable(null);
321        requireCharging = in.readInt() == 1;
322        requireDeviceIdle = in.readInt() == 1;
323        triggerContentUris = in.createTypedArray(TriggerContentUri.CREATOR);
324        networkType = in.readInt();
325        minLatencyMillis = in.readLong();
326        maxExecutionDelayMillis = in.readLong();
327        isPeriodic = in.readInt() == 1;
328        isPersisted = in.readInt() == 1;
329        intervalMillis = in.readLong();
330        flexMillis = in.readLong();
331        initialBackoffMillis = in.readLong();
332        backoffPolicy = in.readInt();
333        hasEarlyConstraint = in.readInt() == 1;
334        hasLateConstraint = in.readInt() == 1;
335        priority = in.readInt();
336    }
337
338    private JobInfo(JobInfo.Builder b) {
339        jobId = b.mJobId;
340        extras = b.mExtras;
341        service = b.mJobService;
342        requireCharging = b.mRequiresCharging;
343        requireDeviceIdle = b.mRequiresDeviceIdle;
344        triggerContentUris = b.mTriggerContentUris != null
345                ? b.mTriggerContentUris.toArray(new TriggerContentUri[b.mTriggerContentUris.size()])
346                : null;
347        networkType = b.mNetworkType;
348        minLatencyMillis = b.mMinLatencyMillis;
349        maxExecutionDelayMillis = b.mMaxExecutionDelayMillis;
350        isPeriodic = b.mIsPeriodic;
351        isPersisted = b.mIsPersisted;
352        intervalMillis = b.mIntervalMillis;
353        flexMillis = b.mFlexMillis;
354        initialBackoffMillis = b.mInitialBackoffMillis;
355        backoffPolicy = b.mBackoffPolicy;
356        hasEarlyConstraint = b.mHasEarlyConstraint;
357        hasLateConstraint = b.mHasLateConstraint;
358        priority = b.mPriority;
359    }
360
361    @Override
362    public int describeContents() {
363        return 0;
364    }
365
366    @Override
367    public void writeToParcel(Parcel out, int flags) {
368        out.writeInt(jobId);
369        out.writePersistableBundle(extras);
370        out.writeParcelable(service, flags);
371        out.writeInt(requireCharging ? 1 : 0);
372        out.writeInt(requireDeviceIdle ? 1 : 0);
373        out.writeTypedArray(triggerContentUris, flags);
374        out.writeInt(networkType);
375        out.writeLong(minLatencyMillis);
376        out.writeLong(maxExecutionDelayMillis);
377        out.writeInt(isPeriodic ? 1 : 0);
378        out.writeInt(isPersisted ? 1 : 0);
379        out.writeLong(intervalMillis);
380        out.writeLong(flexMillis);
381        out.writeLong(initialBackoffMillis);
382        out.writeInt(backoffPolicy);
383        out.writeInt(hasEarlyConstraint ? 1 : 0);
384        out.writeInt(hasLateConstraint ? 1 : 0);
385        out.writeInt(priority);
386    }
387
388    public static final Creator<JobInfo> CREATOR = new Creator<JobInfo>() {
389        @Override
390        public JobInfo createFromParcel(Parcel in) {
391            return new JobInfo(in);
392        }
393
394        @Override
395        public JobInfo[] newArray(int size) {
396            return new JobInfo[size];
397        }
398    };
399
400    @Override
401    public String toString() {
402        return "(job:" + jobId + "/" + service.flattenToShortString() + ")";
403    }
404
405    /**
406     * Information about a content URI modification that a job would like to
407     * trigger on.
408     */
409    public static final class TriggerContentUri implements Parcelable {
410        private final Uri mUri;
411        private final int mFlags;
412
413        /**
414         * Flag for trigger: also trigger if any descendants of the given URI change.
415         * Corresponds to the <var>notifyForDescendants</var> of
416         * {@link android.content.ContentResolver#registerContentObserver}.
417         */
418        public static final int FLAG_NOTIFY_FOR_DESCENDANTS = 1<<0;
419
420        /**
421         * Create a new trigger description.
422         * @param uri The URI to observe.  Must be non-null.
423         * @param flags Optional flags for the observer, either 0 or
424         * {@link #FLAG_NOTIFY_FOR_DESCENDANTS}.
425         */
426        public TriggerContentUri(@NonNull Uri uri, int flags) {
427            mUri = uri;
428            mFlags = flags;
429        }
430
431        /**
432         * Return the Uri this trigger was created for.
433         */
434        public Uri getUri() {
435            return mUri;
436        }
437
438        /**
439         * Return the flags supplied for the trigger.
440         */
441        public int getFlags() {
442            return mFlags;
443        }
444
445        private TriggerContentUri(Parcel in) {
446            mUri = Uri.CREATOR.createFromParcel(in);
447            mFlags = in.readInt();
448        }
449
450        @Override
451        public int describeContents() {
452            return 0;
453        }
454
455        @Override
456        public void writeToParcel(Parcel out, int flags) {
457            mUri.writeToParcel(out, flags);
458            out.writeInt(mFlags);
459        }
460
461        public static final Creator<TriggerContentUri> CREATOR = new Creator<TriggerContentUri>() {
462            @Override
463            public TriggerContentUri createFromParcel(Parcel in) {
464                return new TriggerContentUri(in);
465            }
466
467            @Override
468            public TriggerContentUri[] newArray(int size) {
469                return new TriggerContentUri[size];
470            }
471        };
472    }
473
474    /** Builder class for constructing {@link JobInfo} objects. */
475    public static final class Builder {
476        private int mJobId;
477        private PersistableBundle mExtras = PersistableBundle.EMPTY;
478        private ComponentName mJobService;
479        private int mPriority = PRIORITY_DEFAULT;
480        // Requirements.
481        private boolean mRequiresCharging;
482        private boolean mRequiresDeviceIdle;
483        private int mNetworkType;
484        private ArrayList<TriggerContentUri> mTriggerContentUris;
485        private boolean mIsPersisted;
486        // One-off parameters.
487        private long mMinLatencyMillis;
488        private long mMaxExecutionDelayMillis;
489        // Periodic parameters.
490        private boolean mIsPeriodic;
491        private boolean mHasEarlyConstraint;
492        private boolean mHasLateConstraint;
493        private long mIntervalMillis;
494        private long mFlexMillis;
495        // Back-off parameters.
496        private long mInitialBackoffMillis = DEFAULT_INITIAL_BACKOFF_MILLIS;
497        private int mBackoffPolicy = DEFAULT_BACKOFF_POLICY;
498        /** Easy way to track whether the client has tried to set a back-off policy. */
499        private boolean mBackoffPolicySet = false;
500
501        /**
502         * @param jobId Application-provided id for this job. Subsequent calls to cancel, or
503         *               jobs created with the same jobId, will update the pre-existing job with
504         *               the same id.
505         * @param jobService The endpoint that you implement that will receive the callback from the
506         *            JobScheduler.
507         */
508        public Builder(int jobId, ComponentName jobService) {
509            mJobService = jobService;
510            mJobId = jobId;
511        }
512
513        /**
514         * @hide
515         */
516        public Builder setPriority(int priority) {
517            mPriority = priority;
518            return this;
519        }
520
521        /**
522         * Set optional extras. This is persisted, so we only allow primitive types.
523         * @param extras Bundle containing extras you want the scheduler to hold on to for you.
524         */
525        public Builder setExtras(PersistableBundle extras) {
526            mExtras = extras;
527            return this;
528        }
529
530        /**
531         * Set some description of the kind of network type your job needs to have.
532         * Not calling this function means the network is not necessary, as the default is
533         * {@link #NETWORK_TYPE_NONE}.
534         * Bear in mind that calling this function defines network as a strict requirement for your
535         * job. If the network requested is not available your job will never run. See
536         * {@link #setOverrideDeadline(long)} to change this behaviour.
537         */
538        public Builder setRequiredNetworkType(int networkType) {
539            mNetworkType = networkType;
540            return this;
541        }
542
543        /**
544         * Specify that to run this job, the device needs to be plugged in. This defaults to
545         * false.
546         * @param requiresCharging Whether or not the device is plugged in.
547         */
548        public Builder setRequiresCharging(boolean requiresCharging) {
549            mRequiresCharging = requiresCharging;
550            return this;
551        }
552
553        /**
554         * Specify that to run, the job needs the device to be in idle mode. This defaults to
555         * false.
556         * <p>Idle mode is a loose definition provided by the system, which means that the device
557         * is not in use, and has not been in use for some time. As such, it is a good time to
558         * perform resource heavy jobs. Bear in mind that battery usage will still be attributed
559         * to your application, and surfaced to the user in battery stats.</p>
560         * @param requiresDeviceIdle Whether or not the device need be within an idle maintenance
561         *                           window.
562         */
563        public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) {
564            mRequiresDeviceIdle = requiresDeviceIdle;
565            return this;
566        }
567
568        /**
569         * Add a new content: URI that will be monitored with a
570         * {@link android.database.ContentObserver}, and will cause the job to execute if changed.
571         * If you have any trigger content URIs associated with a job, it will not execute until
572         * there has been a change report for one or more of them.
573         * <p>Note that trigger URIs can not be used in combination with
574         * {@link #setPeriodic(long)} or {@link #setPersisted(boolean)}.  To continually monitor
575         * for content changes, you need to schedule a new JobInfo observing the same URIs
576         * before you finish execution of the JobService handling the most recent changes.</p>
577         * <p>Because because setting this property is not compatible with periodic or
578         * persisted jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when
579         * {@link android.app.job.JobInfo.Builder#build()} is called.</p>
580         * @param uri The content: URI to monitor.
581         */
582        public Builder addTriggerContentUri(@NonNull TriggerContentUri uri) {
583            if (mTriggerContentUris == null) {
584                mTriggerContentUris = new ArrayList<>();
585            }
586            mTriggerContentUris.add(uri);
587            return this;
588        }
589
590        /**
591         * Specify that this job should recur with the provided interval, not more than once per
592         * period. You have no control over when within this interval this job will be executed,
593         * only the guarantee that it will be executed at most once within this interval.
594         * Setting this function on the builder with {@link #setMinimumLatency(long)} or
595         * {@link #setOverrideDeadline(long)} will result in an error.
596         * @param intervalMillis Millisecond interval for which this job will repeat.
597         */
598        public Builder setPeriodic(long intervalMillis) {
599            return setPeriodic(intervalMillis, intervalMillis);
600        }
601
602        /**
603         * Specify that this job should recur with the provided interval and flex. The job can
604         * execute at any time in a window of flex length at the end of the period.
605         * @param intervalMillis Millisecond interval for which this job will repeat. A minimum
606         *                       value of {@link #getMinimumPeriod()} is enforced.
607         * @param flexMillis Millisecond flex for this job. Flex is clamped to be at least
608         *                   {@link #getMinimumFlex()} or 5 percent of the period, whichever is
609         *                   higher.
610         */
611        public Builder setPeriodic(long intervalMillis, long flexMillis) {
612            mIsPeriodic = true;
613            mIntervalMillis = intervalMillis;
614            mFlexMillis = flexMillis;
615            mHasEarlyConstraint = mHasLateConstraint = true;
616            return this;
617        }
618
619        /**
620         * Specify that this job should be delayed by the provided amount of time.
621         * Because it doesn't make sense setting this property on a periodic job, doing so will
622         * throw an {@link java.lang.IllegalArgumentException} when
623         * {@link android.app.job.JobInfo.Builder#build()} is called.
624         * @param minLatencyMillis Milliseconds before which this job will not be considered for
625         *                         execution.
626         */
627        public Builder setMinimumLatency(long minLatencyMillis) {
628            mMinLatencyMillis = minLatencyMillis;
629            mHasEarlyConstraint = true;
630            return this;
631        }
632
633        /**
634         * Set deadline which is the maximum scheduling latency. The job will be run by this
635         * deadline even if other requirements are not met. Because it doesn't make sense setting
636         * this property on a periodic job, doing so will throw an
637         * {@link java.lang.IllegalArgumentException} when
638         * {@link android.app.job.JobInfo.Builder#build()} is called.
639         */
640        public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
641            mMaxExecutionDelayMillis = maxExecutionDelayMillis;
642            mHasLateConstraint = true;
643            return this;
644        }
645
646        /**
647         * Set up the back-off/retry policy.
648         * This defaults to some respectable values: {30 seconds, Exponential}. We cap back-off at
649         * 5hrs.
650         * Note that trying to set a backoff criteria for a job with
651         * {@link #setRequiresDeviceIdle(boolean)} will throw an exception when you call build().
652         * This is because back-off typically does not make sense for these types of jobs. See
653         * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)}
654         * for more description of the return value for the case of a job executing while in idle
655         * mode.
656         * @param initialBackoffMillis Millisecond time interval to wait initially when job has
657         *                             failed.
658         * @param backoffPolicy is one of {@link #BACKOFF_POLICY_LINEAR} or
659         * {@link #BACKOFF_POLICY_EXPONENTIAL}
660         */
661        public Builder setBackoffCriteria(long initialBackoffMillis, int backoffPolicy) {
662            mBackoffPolicySet = true;
663            mInitialBackoffMillis = initialBackoffMillis;
664            mBackoffPolicy = backoffPolicy;
665            return this;
666        }
667
668        /**
669         * Set whether or not to persist this job across device reboots. This will only have an
670         * effect if your application holds the permission
671         * {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED}. Otherwise an exception will
672         * be thrown.
673         * @param isPersisted True to indicate that the job will be written to disk and loaded at
674         *                    boot.
675         */
676        public Builder setPersisted(boolean isPersisted) {
677            mIsPersisted = isPersisted;
678            return this;
679        }
680
681        /**
682         * @return The job object to hand to the JobScheduler. This object is immutable.
683         */
684        public JobInfo build() {
685            // Allow jobs with no constraints - What am I, a database?
686            if (!mHasEarlyConstraint && !mHasLateConstraint && !mRequiresCharging &&
687                    !mRequiresDeviceIdle && mNetworkType == NETWORK_TYPE_NONE &&
688                    mTriggerContentUris == null) {
689                throw new IllegalArgumentException("You're trying to build a job with no " +
690                        "constraints, this is not allowed.");
691            }
692            mExtras = new PersistableBundle(mExtras);  // Make our own copy.
693            // Check that a deadline was not set on a periodic job.
694            if (mIsPeriodic && (mMaxExecutionDelayMillis != 0L)) {
695                throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " +
696                        "periodic job.");
697            }
698            if (mIsPeriodic && (mMinLatencyMillis != 0L)) {
699                throw new IllegalArgumentException("Can't call setMinimumLatency() on a " +
700                        "periodic job");
701            }
702            if (mIsPeriodic && (mTriggerContentUris != null)) {
703                throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " +
704                        "periodic job");
705            }
706            if (mIsPersisted && (mTriggerContentUris != null)) {
707                throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " +
708                        "persisted job");
709            }
710            if (mBackoffPolicySet && mRequiresDeviceIdle) {
711                throw new IllegalArgumentException("An idle mode job will not respect any" +
712                        " back-off policy, so calling setBackoffCriteria with" +
713                        " setRequiresDeviceIdle is an error.");
714            }
715            JobInfo job = new JobInfo(this);
716            if (job.intervalMillis != job.getIntervalMillis()) {
717                Log.w(TAG, "Specified interval for " + mJobService.getPackageName() + " is "
718                        + formatForLogging(mIntervalMillis) + ". Clamped to " +
719                        formatForLogging(job.getIntervalMillis()));
720            }
721            if (job.flexMillis != job.getFlexMillis()) {
722                Log.w(TAG, "Specified interval for " + mJobService.getPackageName() + " is "
723                        + formatForLogging(mFlexMillis) + ". Clamped to " +
724                        formatForLogging(job.getFlexMillis()));
725            }
726            return job;
727        }
728    }
729
730}
731