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