1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package android.app.job;
18 
19 import android.content.ComponentName;
20 import android.os.Parcel;
21 import android.os.Parcelable;
22 import 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  */
32 public 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      */
getId()92     public int getId() {
93         return jobId;
94     }
95 
96     /**
97      * Bundle of extras which are returned to your application at execution time.
98      */
getExtras()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      */
getService()106     public ComponentName getService() {
107         return service;
108     }
109 
110     /**
111      * Whether this job needs the device to be plugged in.
112      */
isRequireCharging()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      */
isRequireDeviceIdle()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      */
getNetworkType()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      */
getMinLatencyMillis()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      */
getMaxExecutionDelayMillis()145     public long getMaxExecutionDelayMillis() {
146         return maxExecutionDelayMillis;
147     }
148 
149     /**
150      * Track whether this job will repeat with a given period.
151      */
isPeriodic()152     public boolean isPeriodic() {
153         return isPeriodic;
154     }
155 
156     /**
157      * @return Whether or not this job should be persisted across device reboots.
158      */
isPersisted()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      */
getIntervalMillis()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      */
getInitialBackoffMillis()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      */
getBackoffPolicy()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      */
hasEarlyConstraint()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      */
hasLateConstraint()203     public boolean hasLateConstraint() {
204         return hasLateConstraint;
205     }
206 
JobInfo(Parcel in)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 
JobInfo(JobInfo.Builder b)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
describeContents()244     public int describeContents() {
245         return 0;
246     }
247 
248     @Override
writeToParcel(Parcel out, int flags)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
toString()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          */
Builder(int jobId, ComponentName jobService)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          */
setExtras(PersistableBundle extras)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          */
setRequiredNetworkType(int networkType)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          */
setRequiresCharging(boolean requiresCharging)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          */
setRequiresDeviceIdle(boolean requiresDeviceIdle)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          */
setPeriodic(long intervalMillis)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          */
setMinimumLatency(long minLatencyMillis)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          */
setOverrideDeadline(long maxExecutionDelayMillis)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          */
setBackoffCriteria(long initialBackoffMillis, int backoffPolicy)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          */
setPersisted(boolean isPersisted)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          */
build()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