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