JobInfo.java revision d1c06753d045ad10e00e7aba53ee2adba0712ccc
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.content.ComponentName;
20import android.os.Parcel;
21import android.os.Parcelable;
22import android.os.PersistableBundle;
23
24/**
25 * Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the
26 * parameters required to schedule work against the calling application. These are constructed
27 * using the {@link JobInfo.Builder}.
28 * You must specify at least one sort of constraint on the JobInfo object that you are creating.
29 * The goal here is to provide the scheduler with high-level semantics about the work you want to
30 * accomplish. Doing otherwise with throw an exception in your app.
31 */
32public class JobInfo implements Parcelable {
33    /** Default. */
34    public static final int NETWORK_TYPE_NONE = 0;
35    /** This job requires network connectivity. */
36    public static final int NETWORK_TYPE_ANY = 1;
37    /** This job requires network connectivity that is unmetered. */
38    public static final int NETWORK_TYPE_UNMETERED = 2;
39
40    /**
41     * Amount of backoff a job has initially by default, in milliseconds.
42     */
43    public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L;  // 30 seconds.
44
45    /**
46     * Maximum backoff we allow for a job, in milliseconds.
47     */
48    public static final long MAX_BACKOFF_DELAY_MILLIS = 5 * 60 * 60 * 1000;  // 5 hours.
49
50    /**
51     * Linearly back-off a failed job. See
52     * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)}
53     * retry_time(current_time, num_failures) =
54     *     current_time + initial_backoff_millis * num_failures, num_failures >= 1
55     */
56    public static final int BACKOFF_POLICY_LINEAR = 0;
57
58    /**
59     * Exponentially back-off a failed job. See
60     * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)}
61     *
62     * retry_time(current_time, num_failures) =
63     *     current_time + initial_backoff_millis * 2 ^ (num_failures - 1), num_failures >= 1
64     */
65    public static final int BACKOFF_POLICY_EXPONENTIAL = 1;
66
67    /**
68     * Default type of backoff.
69     * @hide
70     */
71    public static final int DEFAULT_BACKOFF_POLICY = BACKOFF_POLICY_EXPONENTIAL;
72
73    private final int jobId;
74    private final PersistableBundle extras;
75    private final ComponentName service;
76    private final boolean requireCharging;
77    private final boolean requireDeviceIdle;
78    private final boolean hasEarlyConstraint;
79    private final boolean hasLateConstraint;
80    private final int networkType;
81    private final long minLatencyMillis;
82    private final long maxExecutionDelayMillis;
83    private final boolean isPeriodic;
84    private final boolean isPersisted;
85    private final long intervalMillis;
86    private final long initialBackoffMillis;
87    private final int backoffPolicy;
88
89    /**
90     * Unique job id associated with this class. This is assigned to your job by the scheduler.
91     */
92    public int getId() {
93        return jobId;
94    }
95
96    /**
97     * Bundle of extras which are returned to your application at execution time.
98     */
99    public PersistableBundle getExtras() {
100        return extras;
101    }
102
103    /**
104     * Name of the service endpoint that will be called back into by the JobScheduler.
105     */
106    public ComponentName getService() {
107        return service;
108    }
109
110    /**
111     * Whether this job needs the device to be plugged in.
112     */
113    public boolean isRequireCharging() {
114        return requireCharging;
115    }
116
117    /**
118     * Whether this job needs the device to be in an Idle maintenance window.
119     */
120    public boolean isRequireDeviceIdle() {
121        return requireDeviceIdle;
122    }
123
124    /**
125     * One of {@link android.app.job.JobInfo#NETWORK_TYPE_ANY},
126     * {@link android.app.job.JobInfo#NETWORK_TYPE_NONE}, or
127     * {@link android.app.job.JobInfo#NETWORK_TYPE_UNMETERED}.
128     */
129    public int getNetworkType() {
130        return networkType;
131    }
132
133    /**
134     * Set for a job that does not recur periodically, to specify a delay after which the job
135     * will be eligible for execution. This value is not set if the job recurs periodically.
136     */
137    public long getMinLatencyMillis() {
138        return minLatencyMillis;
139    }
140
141    /**
142     * See {@link Builder#setOverrideDeadline(long)}. This value is not set if the job recurs
143     * periodically.
144     */
145    public long getMaxExecutionDelayMillis() {
146        return maxExecutionDelayMillis;
147    }
148
149    /**
150     * Track whether this job will repeat with a given period.
151     */
152    public boolean isPeriodic() {
153        return isPeriodic;
154    }
155
156    /**
157     * @return Whether or not this job should be persisted across device reboots.
158     */
159    public boolean isPersisted() {
160        return isPersisted;
161    }
162
163    /**
164     * Set to the interval between occurrences of this job. This value is <b>not</b> set if the
165     * job does not recur periodically.
166     */
167    public long getIntervalMillis() {
168        return intervalMillis;
169    }
170
171    /**
172     * The amount of time the JobScheduler will wait before rescheduling a failed job. This value
173     * will be increased depending on the backoff policy specified at job creation time. Defaults
174     * to 5 seconds.
175     */
176    public long getInitialBackoffMillis() {
177        return initialBackoffMillis;
178    }
179
180    /**
181     * One of either {@link android.app.job.JobInfo#BACKOFF_POLICY_EXPONENTIAL}, or
182     * {@link android.app.job.JobInfo#BACKOFF_POLICY_LINEAR}, depending on which criteria you set
183     * when creating this job.
184     */
185    public int getBackoffPolicy() {
186        return backoffPolicy;
187    }
188
189    /**
190     * User can specify an early constraint of 0L, which is valid, so we keep track of whether the
191     * function was called at all.
192     * @hide
193     */
194    public boolean hasEarlyConstraint() {
195        return hasEarlyConstraint;
196    }
197
198    /**
199     * User can specify a late constraint of 0L, which is valid, so we keep track of whether the
200     * function was called at all.
201     * @hide
202     */
203    public boolean hasLateConstraint() {
204        return hasLateConstraint;
205    }
206
207    private JobInfo(Parcel in) {
208        jobId = in.readInt();
209        extras = in.readPersistableBundle();
210        service = in.readParcelable(null);
211        requireCharging = in.readInt() == 1;
212        requireDeviceIdle = in.readInt() == 1;
213        networkType = in.readInt();
214        minLatencyMillis = in.readLong();
215        maxExecutionDelayMillis = in.readLong();
216        isPeriodic = in.readInt() == 1;
217        isPersisted = in.readInt() == 1;
218        intervalMillis = in.readLong();
219        initialBackoffMillis = in.readLong();
220        backoffPolicy = in.readInt();
221        hasEarlyConstraint = in.readInt() == 1;
222        hasLateConstraint = in.readInt() == 1;
223    }
224
225    private JobInfo(JobInfo.Builder b) {
226        jobId = b.mJobId;
227        extras = b.mExtras;
228        service = b.mJobService;
229        requireCharging = b.mRequiresCharging;
230        requireDeviceIdle = b.mRequiresDeviceIdle;
231        networkType = b.mNetworkType;
232        minLatencyMillis = b.mMinLatencyMillis;
233        maxExecutionDelayMillis = b.mMaxExecutionDelayMillis;
234        isPeriodic = b.mIsPeriodic;
235        isPersisted = b.mIsPersisted;
236        intervalMillis = b.mIntervalMillis;
237        initialBackoffMillis = b.mInitialBackoffMillis;
238        backoffPolicy = b.mBackoffPolicy;
239        hasEarlyConstraint = b.mHasEarlyConstraint;
240        hasLateConstraint = b.mHasLateConstraint;
241    }
242
243    @Override
244    public int describeContents() {
245        return 0;
246    }
247
248    @Override
249    public void writeToParcel(Parcel out, int flags) {
250        out.writeInt(jobId);
251        out.writePersistableBundle(extras);
252        out.writeParcelable(service, flags);
253        out.writeInt(requireCharging ? 1 : 0);
254        out.writeInt(requireDeviceIdle ? 1 : 0);
255        out.writeInt(networkType);
256        out.writeLong(minLatencyMillis);
257        out.writeLong(maxExecutionDelayMillis);
258        out.writeInt(isPeriodic ? 1 : 0);
259        out.writeInt(isPersisted ? 1 : 0);
260        out.writeLong(intervalMillis);
261        out.writeLong(initialBackoffMillis);
262        out.writeInt(backoffPolicy);
263        out.writeInt(hasEarlyConstraint ? 1 : 0);
264        out.writeInt(hasLateConstraint ? 1 : 0);
265    }
266
267    public static final Creator<JobInfo> CREATOR = new Creator<JobInfo>() {
268        @Override
269        public JobInfo createFromParcel(Parcel in) {
270            return new JobInfo(in);
271        }
272
273        @Override
274        public JobInfo[] newArray(int size) {
275            return new JobInfo[size];
276        }
277    };
278
279    @Override
280    public String toString() {
281        return "(job:" + jobId + "/" + service.flattenToShortString() + ")";
282    }
283
284    /** Builder class for constructing {@link JobInfo} objects. */
285    public static final class Builder {
286        private int mJobId;
287        private PersistableBundle mExtras = PersistableBundle.EMPTY;
288        private ComponentName mJobService;
289        // Requirements.
290        private boolean mRequiresCharging;
291        private boolean mRequiresDeviceIdle;
292        private int mNetworkType;
293        private boolean mIsPersisted;
294        // One-off parameters.
295        private long mMinLatencyMillis;
296        private long mMaxExecutionDelayMillis;
297        // Periodic parameters.
298        private boolean mIsPeriodic;
299        private boolean mHasEarlyConstraint;
300        private boolean mHasLateConstraint;
301        private long mIntervalMillis;
302        // Back-off parameters.
303        private long mInitialBackoffMillis = DEFAULT_INITIAL_BACKOFF_MILLIS;
304        private int mBackoffPolicy = DEFAULT_BACKOFF_POLICY;
305        /** Easy way to track whether the client has tried to set a back-off policy. */
306        private boolean mBackoffPolicySet = false;
307
308        /**
309         * @param jobId Application-provided id for this job. Subsequent calls to cancel, or
310         *               jobs created with the same jobId, will update the pre-existing job with
311         *               the same id.
312         * @param jobService The endpoint that you implement that will receive the callback from the
313         *            JobScheduler.
314         */
315        public Builder(int jobId, ComponentName jobService) {
316            mJobService = jobService;
317            mJobId = jobId;
318        }
319
320        /**
321         * Set optional extras. This is persisted, so we only allow primitive types.
322         * @param extras Bundle containing extras you want the scheduler to hold on to for you.
323         */
324        public Builder setExtras(PersistableBundle extras) {
325            mExtras = extras;
326            return this;
327        }
328
329        /**
330         * Set some description of the kind of network type your job needs to have.
331         * Not calling this function means the network is not necessary, as the default is
332         * {@link #NETWORK_TYPE_NONE}.
333         * Bear in mind that calling this function defines network as a strict requirement for your
334         * job. If the network requested is not available your job will never run. See
335         * {@link #setOverrideDeadline(long)} to change this behaviour.
336         */
337        public Builder setRequiredNetworkType(int networkType) {
338            mNetworkType = networkType;
339            return this;
340        }
341
342        /**
343         * Specify that to run this job, the device needs to be plugged in. This defaults to
344         * false.
345         * @param requiresCharging Whether or not the device is plugged in.
346         */
347        public Builder setRequiresCharging(boolean requiresCharging) {
348            mRequiresCharging = requiresCharging;
349            return this;
350        }
351
352        /**
353         * Specify that to run, the job needs the device to be in idle mode. This defaults to
354         * false.
355         * <p>Idle mode is a loose definition provided by the system, which means that the device
356         * is not in use, and has not been in use for some time. As such, it is a good time to
357         * perform resource heavy jobs. Bear in mind that battery usage will still be attributed
358         * to your application, and surfaced to the user in battery stats.</p>
359         * @param requiresDeviceIdle Whether or not the device need be within an idle maintenance
360         *                           window.
361         */
362        public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) {
363            mRequiresDeviceIdle = requiresDeviceIdle;
364            return this;
365        }
366
367        /**
368         * Specify that this job should recur with the provided interval, not more than once per
369         * period. You have no control over when within this interval this job will be executed,
370         * only the guarantee that it will be executed at most once within this interval.
371         * Setting this function on the builder with {@link #setMinimumLatency(long)} or
372         * {@link #setOverrideDeadline(long)} will result in an error.
373         * @param intervalMillis Millisecond interval for which this job will repeat.
374         */
375        public Builder setPeriodic(long intervalMillis) {
376            mIsPeriodic = true;
377            mIntervalMillis = intervalMillis;
378            mHasEarlyConstraint = mHasLateConstraint = true;
379            return this;
380        }
381
382        /**
383         * Specify that this job should be delayed by the provided amount of time.
384         * Because it doesn't make sense setting this property on a periodic job, doing so will
385         * throw an {@link java.lang.IllegalArgumentException} when
386         * {@link android.app.job.JobInfo.Builder#build()} is called.
387         * @param minLatencyMillis Milliseconds before which this job will not be considered for
388         *                         execution.
389         */
390        public Builder setMinimumLatency(long minLatencyMillis) {
391            mMinLatencyMillis = minLatencyMillis;
392            mHasEarlyConstraint = true;
393            return this;
394        }
395
396        /**
397         * Set deadline which is the maximum scheduling latency. The job will be run by this
398         * deadline even if other requirements are not met. Because it doesn't make sense setting
399         * this property on a periodic job, doing so will throw an
400         * {@link java.lang.IllegalArgumentException} when
401         * {@link android.app.job.JobInfo.Builder#build()} is called.
402         */
403        public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
404            mMaxExecutionDelayMillis = maxExecutionDelayMillis;
405            mHasLateConstraint = true;
406            return this;
407        }
408
409        /**
410         * Set up the back-off/retry policy.
411         * This defaults to some respectable values: {30 seconds, Exponential}. We cap back-off at
412         * 5hrs.
413         * Note that trying to set a backoff criteria for a job with
414         * {@link #setRequiresDeviceIdle(boolean)} will throw an exception when you call build().
415         * This is because back-off typically does not make sense for these types of jobs. See
416         * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)}
417         * for more description of the return value for the case of a job executing while in idle
418         * mode.
419         * @param initialBackoffMillis Millisecond time interval to wait initially when job has
420         *                             failed.
421         * @param backoffPolicy is one of {@link #BACKOFF_POLICY_LINEAR} or
422         * {@link #BACKOFF_POLICY_EXPONENTIAL}
423         */
424        public Builder setBackoffCriteria(long initialBackoffMillis, int backoffPolicy) {
425            mBackoffPolicySet = true;
426            mInitialBackoffMillis = initialBackoffMillis;
427            mBackoffPolicy = backoffPolicy;
428            return this;
429        }
430
431        /**
432         * Set whether or not to persist this job across device reboots. This will only have an
433         * effect if your application holds the permission
434         * {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED}. Otherwise an exception will
435         * be thrown.
436         * @param isPersisted True to indicate that the job will be written to disk and loaded at
437         *                    boot.
438         */
439        public Builder setPersisted(boolean isPersisted) {
440            mIsPersisted = isPersisted;
441            return this;
442        }
443
444        /**
445         * @return The job object to hand to the JobScheduler. This object is immutable.
446         */
447        public JobInfo build() {
448            // Allow jobs with no constraints - What am I, a database?
449            if (!mHasEarlyConstraint && !mHasLateConstraint && !mRequiresCharging &&
450                    !mRequiresDeviceIdle && mNetworkType == NETWORK_TYPE_NONE) {
451                throw new IllegalArgumentException("You're trying to build a job with no " +
452                        "constraints, this is not allowed.");
453            }
454            mExtras = new PersistableBundle(mExtras);  // Make our own copy.
455            // Check that a deadline was not set on a periodic job.
456            if (mIsPeriodic && (mMaxExecutionDelayMillis != 0L)) {
457                throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " +
458                        "periodic job.");
459            }
460            if (mIsPeriodic && (mMinLatencyMillis != 0L)) {
461                throw new IllegalArgumentException("Can't call setMinimumLatency() on a " +
462                        "periodic job");
463            }
464            if (mBackoffPolicySet && mRequiresDeviceIdle) {
465                throw new IllegalArgumentException("An idle mode job will not respect any" +
466                        " back-off policy, so calling setBackoffCriteria with" +
467                        " setRequiresDeviceIdle is an error.");
468            }
469            return new JobInfo(this);
470        }
471    }
472
473}
474