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