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