JobParameters.java revision 33a412b52d49c15e2948f3392b7c619c4e3e308d
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.app.job.IJobCallback;
22import android.content.ClipData;
23import android.net.Network;
24import android.net.Uri;
25import android.os.Bundle;
26import android.os.IBinder;
27import android.os.Parcel;
28import android.os.Parcelable;
29import android.os.PersistableBundle;
30import android.os.RemoteException;
31
32/**
33 * Contains the parameters used to configure/identify your job. You do not create this object
34 * yourself, instead it is handed in to your application by the System.
35 */
36public class JobParameters implements Parcelable {
37
38    /** @hide */
39    public static final int REASON_CANCELED = JobProtoEnums.STOP_REASON_CANCELLED; // 0.
40    /** @hide */
41    public static final int REASON_CONSTRAINTS_NOT_SATISFIED =
42            JobProtoEnums.STOP_REASON_CONSTRAINTS_NOT_SATISFIED; //1.
43    /** @hide */
44    public static final int REASON_PREEMPT = JobProtoEnums.STOP_REASON_PREEMPT; // 2.
45    /** @hide */
46    public static final int REASON_TIMEOUT = JobProtoEnums.STOP_REASON_TIMEOUT; // 3.
47    /** @hide */
48    public static final int REASON_DEVICE_IDLE = JobProtoEnums.STOP_REASON_DEVICE_IDLE; // 4.
49
50    /** @hide */
51    public static String getReasonName(int reason) {
52        switch (reason) {
53            case REASON_CANCELED: return "canceled";
54            case REASON_CONSTRAINTS_NOT_SATISFIED: return "constraints";
55            case REASON_PREEMPT: return "preempt";
56            case REASON_TIMEOUT: return "timeout";
57            case REASON_DEVICE_IDLE: return "device_idle";
58            default: return "unknown:" + reason;
59        }
60    }
61
62    private final int jobId;
63    private final PersistableBundle extras;
64    private final Bundle transientExtras;
65    private final ClipData clipData;
66    private final int clipGrantFlags;
67    private final IBinder callback;
68    private final boolean overrideDeadlineExpired;
69    private final Uri[] mTriggeredContentUris;
70    private final String[] mTriggeredContentAuthorities;
71    private final Network network;
72
73    private int stopReason; // Default value of stopReason is REASON_CANCELED
74    private String debugStopReason; // Human readable stop reason for debugging.
75
76    /** @hide */
77    public JobParameters(IBinder callback, int jobId, PersistableBundle extras,
78            Bundle transientExtras, ClipData clipData, int clipGrantFlags,
79            boolean overrideDeadlineExpired, Uri[] triggeredContentUris,
80            String[] triggeredContentAuthorities, Network network) {
81        this.jobId = jobId;
82        this.extras = extras;
83        this.transientExtras = transientExtras;
84        this.clipData = clipData;
85        this.clipGrantFlags = clipGrantFlags;
86        this.callback = callback;
87        this.overrideDeadlineExpired = overrideDeadlineExpired;
88        this.mTriggeredContentUris = triggeredContentUris;
89        this.mTriggeredContentAuthorities = triggeredContentAuthorities;
90        this.network = network;
91    }
92
93    /**
94     * @return The unique id of this job, specified at creation time.
95     */
96    public int getJobId() {
97        return jobId;
98    }
99
100    /**
101     * Reason onStopJob() was called on this job.
102     * @hide
103     */
104    public int getStopReason() {
105        return stopReason;
106    }
107
108    /**
109     * Reason onStopJob() was called on this job.
110     * @hide
111     */
112    public String getDebugStopReason() {
113        return debugStopReason;
114    }
115
116    /**
117     * @return The extras you passed in when constructing this job with
118     * {@link android.app.job.JobInfo.Builder#setExtras(android.os.PersistableBundle)}. This will
119     * never be null. If you did not set any extras this will be an empty bundle.
120     */
121    public @NonNull PersistableBundle getExtras() {
122        return extras;
123    }
124
125    /**
126     * @return The transient extras you passed in when constructing this job with
127     * {@link android.app.job.JobInfo.Builder#setTransientExtras(android.os.Bundle)}. This will
128     * never be null. If you did not set any extras this will be an empty bundle.
129     */
130    public @NonNull Bundle getTransientExtras() {
131        return transientExtras;
132    }
133
134    /**
135     * @return The clip you passed in when constructing this job with
136     * {@link android.app.job.JobInfo.Builder#setClipData(ClipData, int)}. Will be null
137     * if it was not set.
138     */
139    public @Nullable ClipData getClipData() {
140        return clipData;
141    }
142
143    /**
144     * @return The clip grant flags you passed in when constructing this job with
145     * {@link android.app.job.JobInfo.Builder#setClipData(ClipData, int)}. Will be 0
146     * if it was not set.
147     */
148    public int getClipGrantFlags() {
149        return clipGrantFlags;
150    }
151
152    /**
153     * For jobs with {@link android.app.job.JobInfo.Builder#setOverrideDeadline(long)} set, this
154     * provides an easy way to tell whether the job is being executed due to the deadline
155     * expiring. Note: If the job is running because its deadline expired, it implies that its
156     * constraints will not be met.
157     */
158    public boolean isOverrideDeadlineExpired() {
159        return overrideDeadlineExpired;
160    }
161
162    /**
163     * For jobs with {@link android.app.job.JobInfo.Builder#addTriggerContentUri} set, this
164     * reports which URIs have triggered the job.  This will be null if either no URIs have
165     * triggered it (it went off due to a deadline or other reason), or the number of changed
166     * URIs is too large to report.  Whether or not the number of URIs is too large, you can
167     * always use {@link #getTriggeredContentAuthorities()} to determine whether the job was
168     * triggered due to any content changes and the authorities they are associated with.
169     */
170    public @Nullable Uri[] getTriggeredContentUris() {
171        return mTriggeredContentUris;
172    }
173
174    /**
175     * For jobs with {@link android.app.job.JobInfo.Builder#addTriggerContentUri} set, this
176     * reports which content authorities have triggered the job.  It will only be null if no
177     * authorities have triggered it -- that is, the job executed for some other reason, such
178     * as a deadline expiring.  If this is non-null, you can use {@link #getTriggeredContentUris()}
179     * to retrieve the details of which URIs changed (as long as that has not exceeded the maximum
180     * number it can reported).
181     */
182    public @Nullable String[] getTriggeredContentAuthorities() {
183        return mTriggeredContentAuthorities;
184    }
185
186    /**
187     * Return the network that should be used to perform any network requests
188     * for this job.
189     * <p>
190     * Devices may have multiple active network connections simultaneously, or
191     * they may not have a default network route at all. To correctly handle all
192     * situations like this, your job should always use the network returned by
193     * this method instead of implicitly using the default network route.
194     * <p>
195     * Note that the system may relax the constraints you originally requested,
196     * such as allowing a {@link JobInfo#NETWORK_TYPE_UNMETERED} job to run over
197     * a metered network when there is a surplus of metered data available.
198     *
199     * @return the network that should be used to perform any network requests
200     *         for this job, or {@code null} if this job didn't set any required
201     *         network type.
202     * @see JobInfo.Builder#setRequiredNetworkType(int)
203     */
204    public @Nullable Network getNetwork() {
205        return network;
206    }
207
208    /**
209     * Dequeue the next pending {@link JobWorkItem} from these JobParameters associated with their
210     * currently running job.  Calling this method when there is no more work available and all
211     * previously dequeued work has been completed will result in the system taking care of
212     * stopping the job for you --
213     * you should not call {@link JobService#jobFinished(JobParameters, boolean)} yourself
214     * (otherwise you risk losing an upcoming JobWorkItem that is being enqueued at the same time).
215     *
216     * <p>Once you are done with the {@link JobWorkItem} returned by this method, you must call
217     * {@link #completeWork(JobWorkItem)} with it to inform the system that you are done
218     * executing the work.  The job will not be finished until all dequeued work has been
219     * completed.  You do not, however, have to complete each returned work item before deqeueing
220     * the next one -- you can use {@link #dequeueWork()} multiple times before completing
221     * previous work if you want to process work in parallel, and you can complete the work
222     * in whatever order you want.</p>
223     *
224     * <p>If the job runs to the end of its available time period before all work has been
225     * completed, it will stop as normal.  You should return true from
226     * {@link JobService#onStopJob(JobParameters)} in order to have the job rescheduled, and by
227     * doing so any pending as well as remaining uncompleted work will be re-queued
228     * for the next time the job runs.</p>
229     *
230     * <p>This example shows how to construct a JobService that will serially dequeue and
231     * process work that is available for it:</p>
232     *
233     * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/JobWorkService.java
234     *      service}
235     *
236     * @return Returns a new {@link JobWorkItem} if there is one pending, otherwise null.
237     * If null is returned, the system will also stop the job if all work has also been completed.
238     * (This means that for correct operation, you must always call dequeueWork() after you have
239     * completed other work, to check either for more work or allow the system to stop the job.)
240     */
241    public @Nullable JobWorkItem dequeueWork() {
242        try {
243            return getCallback().dequeueWork(getJobId());
244        } catch (RemoteException e) {
245            throw e.rethrowFromSystemServer();
246        }
247    }
248
249    /**
250     * Report the completion of executing a {@link JobWorkItem} previously returned by
251     * {@link #dequeueWork()}.  This tells the system you are done with the
252     * work associated with that item, so it will not be returned again.  Note that if this
253     * is the last work in the queue, completing it here will <em>not</em> finish the overall
254     * job -- for that to happen, you still need to call {@link #dequeueWork()}
255     * again.
256     *
257     * <p>If you are enqueueing work into a job, you must call this method for each piece
258     * of work you process.  Do <em>not</em> call
259     * {@link JobService#jobFinished(JobParameters, boolean)}
260     * or else you can lose work in your queue.</p>
261     *
262     * @param work The work you have completed processing, as previously returned by
263     * {@link #dequeueWork()}
264     */
265    public void completeWork(@NonNull JobWorkItem work) {
266        try {
267            if (!getCallback().completeWork(getJobId(), work.getWorkId())) {
268                throw new IllegalArgumentException("Given work is not active: " + work);
269            }
270        } catch (RemoteException e) {
271            throw e.rethrowFromSystemServer();
272        }
273    }
274
275    /** @hide */
276    public IJobCallback getCallback() {
277        return IJobCallback.Stub.asInterface(callback);
278    }
279
280    private JobParameters(Parcel in) {
281        jobId = in.readInt();
282        extras = in.readPersistableBundle();
283        transientExtras = in.readBundle();
284        if (in.readInt() != 0) {
285            clipData = ClipData.CREATOR.createFromParcel(in);
286            clipGrantFlags = in.readInt();
287        } else {
288            clipData = null;
289            clipGrantFlags = 0;
290        }
291        callback = in.readStrongBinder();
292        overrideDeadlineExpired = in.readInt() == 1;
293        mTriggeredContentUris = in.createTypedArray(Uri.CREATOR);
294        mTriggeredContentAuthorities = in.createStringArray();
295        if (in.readInt() != 0) {
296            network = Network.CREATOR.createFromParcel(in);
297        } else {
298            network = null;
299        }
300        stopReason = in.readInt();
301        debugStopReason = in.readString();
302    }
303
304    /** @hide */
305    public void setStopReason(int reason, String debugStopReason) {
306        stopReason = reason;
307        this.debugStopReason = debugStopReason;
308    }
309
310    @Override
311    public int describeContents() {
312        return 0;
313    }
314
315    @Override
316    public void writeToParcel(Parcel dest, int flags) {
317        dest.writeInt(jobId);
318        dest.writePersistableBundle(extras);
319        dest.writeBundle(transientExtras);
320        if (clipData != null) {
321            dest.writeInt(1);
322            clipData.writeToParcel(dest, flags);
323            dest.writeInt(clipGrantFlags);
324        } else {
325            dest.writeInt(0);
326        }
327        dest.writeStrongBinder(callback);
328        dest.writeInt(overrideDeadlineExpired ? 1 : 0);
329        dest.writeTypedArray(mTriggeredContentUris, flags);
330        dest.writeStringArray(mTriggeredContentAuthorities);
331        if (network != null) {
332            dest.writeInt(1);
333            network.writeToParcel(dest, flags);
334        } else {
335            dest.writeInt(0);
336        }
337        dest.writeInt(stopReason);
338        dest.writeString(debugStopReason);
339    }
340
341    public static final Creator<JobParameters> CREATOR = new Creator<JobParameters>() {
342        @Override
343        public JobParameters createFromParcel(Parcel in) {
344            return new JobParameters(in);
345        }
346
347        @Override
348        public JobParameters[] newArray(int size) {
349            return new JobParameters[size];
350        }
351    };
352}
353