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.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.app.ActivityManager; 23 import android.app.usage.UsageStatsManager; 24 import android.compat.annotation.UnsupportedAppUsage; 25 import android.content.ClipData; 26 import android.content.pm.PackageManager; 27 import android.net.Network; 28 import android.net.NetworkRequest; 29 import android.net.Uri; 30 import android.os.Bundle; 31 import android.os.IBinder; 32 import android.os.Parcel; 33 import android.os.Parcelable; 34 import android.os.PersistableBundle; 35 import android.os.RemoteException; 36 37 import java.lang.annotation.Retention; 38 import java.lang.annotation.RetentionPolicy; 39 40 /** 41 * Contains the parameters used to configure/identify your job. You do not create this object 42 * yourself, instead it is handed in to your application by the System. 43 */ 44 public class JobParameters implements Parcelable { 45 46 /** @hide */ 47 public static final int INTERNAL_STOP_REASON_UNKNOWN = -1; 48 49 /** @hide */ 50 public static final int INTERNAL_STOP_REASON_CANCELED = 51 JobProtoEnums.INTERNAL_STOP_REASON_CANCELLED; // 0. 52 /** @hide */ 53 public static final int INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED = 54 JobProtoEnums.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED; // 1. 55 /** @hide */ 56 public static final int INTERNAL_STOP_REASON_PREEMPT = 57 JobProtoEnums.INTERNAL_STOP_REASON_PREEMPT; // 2. 58 /** 59 * The job ran for at least its minimum execution limit. 60 * @hide 61 */ 62 public static final int INTERNAL_STOP_REASON_TIMEOUT = 63 JobProtoEnums.INTERNAL_STOP_REASON_TIMEOUT; // 3. 64 /** @hide */ 65 public static final int INTERNAL_STOP_REASON_DEVICE_IDLE = 66 JobProtoEnums.INTERNAL_STOP_REASON_DEVICE_IDLE; // 4. 67 /** @hide */ 68 public static final int INTERNAL_STOP_REASON_DEVICE_THERMAL = 69 JobProtoEnums.INTERNAL_STOP_REASON_DEVICE_THERMAL; // 5. 70 /** 71 * The job is in the {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} 72 * bucket. 73 * 74 * @hide 75 */ 76 public static final int INTERNAL_STOP_REASON_RESTRICTED_BUCKET = 77 JobProtoEnums.INTERNAL_STOP_REASON_RESTRICTED_BUCKET; // 6. 78 /** 79 * The app was uninstalled. 80 * @hide 81 */ 82 public static final int INTERNAL_STOP_REASON_UNINSTALL = 83 JobProtoEnums.INTERNAL_STOP_REASON_UNINSTALL; // 7. 84 /** 85 * The app's data was cleared. 86 * @hide 87 */ 88 public static final int INTERNAL_STOP_REASON_DATA_CLEARED = 89 JobProtoEnums.INTERNAL_STOP_REASON_DATA_CLEARED; // 8. 90 /** 91 * @hide 92 */ 93 public static final int INTERNAL_STOP_REASON_RTC_UPDATED = 94 JobProtoEnums.INTERNAL_STOP_REASON_RTC_UPDATED; // 9. 95 /** 96 * The app called jobFinished() on its own. 97 * @hide 98 */ 99 public static final int INTERNAL_STOP_REASON_SUCCESSFUL_FINISH = 100 JobProtoEnums.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH; // 10. 101 102 /** 103 * All the stop reason codes. This should be regarded as an immutable array at runtime. 104 * 105 * Note the order of these values will affect "dumpsys batterystats", and we do not want to 106 * change the order of existing fields, so adding new fields is okay but do not remove or 107 * change existing fields. When deprecating a field, just replace that with "-1" in this array. 108 * 109 * @hide 110 */ 111 public static final int[] JOB_STOP_REASON_CODES = { 112 INTERNAL_STOP_REASON_UNKNOWN, 113 INTERNAL_STOP_REASON_CANCELED, 114 INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED, 115 INTERNAL_STOP_REASON_PREEMPT, 116 INTERNAL_STOP_REASON_TIMEOUT, 117 INTERNAL_STOP_REASON_DEVICE_IDLE, 118 INTERNAL_STOP_REASON_DEVICE_THERMAL, 119 INTERNAL_STOP_REASON_RESTRICTED_BUCKET, 120 INTERNAL_STOP_REASON_UNINSTALL, 121 INTERNAL_STOP_REASON_DATA_CLEARED, 122 INTERNAL_STOP_REASON_RTC_UPDATED, 123 INTERNAL_STOP_REASON_SUCCESSFUL_FINISH, 124 }; 125 126 /** 127 * @hide 128 */ 129 // TODO(142420609): make it @SystemApi for mainline 130 @NonNull getInternalReasonCodeDescription(int reasonCode)131 public static String getInternalReasonCodeDescription(int reasonCode) { 132 switch (reasonCode) { 133 case INTERNAL_STOP_REASON_CANCELED: return "canceled"; 134 case INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED: return "constraints"; 135 case INTERNAL_STOP_REASON_PREEMPT: return "preempt"; 136 case INTERNAL_STOP_REASON_TIMEOUT: return "timeout"; 137 case INTERNAL_STOP_REASON_DEVICE_IDLE: return "device_idle"; 138 case INTERNAL_STOP_REASON_DEVICE_THERMAL: return "thermal"; 139 case INTERNAL_STOP_REASON_RESTRICTED_BUCKET: return "restricted_bucket"; 140 case INTERNAL_STOP_REASON_UNINSTALL: return "uninstall"; 141 case INTERNAL_STOP_REASON_DATA_CLEARED: return "data_cleared"; 142 case INTERNAL_STOP_REASON_RTC_UPDATED: return "rtc_updated"; 143 case INTERNAL_STOP_REASON_SUCCESSFUL_FINISH: return "successful_finish"; 144 default: return "unknown:" + reasonCode; 145 } 146 } 147 148 /** @hide */ 149 @NonNull getJobStopReasonCodes()150 public static int[] getJobStopReasonCodes() { 151 return JOB_STOP_REASON_CODES; 152 } 153 154 /** 155 * There is no reason the job is stopped. This is the value returned from the JobParameters 156 * object passed to {@link JobService#onStartJob(JobParameters)}. 157 */ 158 public static final int STOP_REASON_UNDEFINED = 0; 159 /** 160 * The job was cancelled directly by the app, either by calling 161 * {@link JobScheduler#cancel(int)}, {@link JobScheduler#cancelAll()}, or by scheduling a 162 * new job with the same job ID. 163 */ 164 public static final int STOP_REASON_CANCELLED_BY_APP = 1; 165 /** The job was stopped to run a higher priority job of the app. */ 166 public static final int STOP_REASON_PREEMPT = 2; 167 /** 168 * The job used up its maximum execution time and timed out. Each individual job has a maximum 169 * execution time limit, regardless of how much total quota the app has. See the note on 170 * {@link JobScheduler} and {@link JobInfo} for the execution time limits. 171 */ 172 public static final int STOP_REASON_TIMEOUT = 3; 173 /** 174 * The device state (eg. Doze, battery saver, memory usage, etc) requires JobScheduler stop this 175 * job. 176 */ 177 public static final int STOP_REASON_DEVICE_STATE = 4; 178 /** 179 * The requested battery-not-low constraint is no longer satisfied. 180 * 181 * @see JobInfo.Builder#setRequiresBatteryNotLow(boolean) 182 */ 183 public static final int STOP_REASON_CONSTRAINT_BATTERY_NOT_LOW = 5; 184 /** 185 * The requested charging constraint is no longer satisfied. 186 * 187 * @see JobInfo.Builder#setRequiresCharging(boolean) 188 */ 189 public static final int STOP_REASON_CONSTRAINT_CHARGING = 6; 190 /** 191 * The requested connectivity constraint is no longer satisfied. 192 * 193 * @see JobInfo.Builder#setRequiredNetwork(NetworkRequest) 194 * @see JobInfo.Builder#setRequiredNetworkType(int) 195 */ 196 public static final int STOP_REASON_CONSTRAINT_CONNECTIVITY = 7; 197 /** 198 * The requested idle constraint is no longer satisfied. 199 * 200 * @see JobInfo.Builder#setRequiresDeviceIdle(boolean) 201 */ 202 public static final int STOP_REASON_CONSTRAINT_DEVICE_IDLE = 8; 203 /** 204 * The requested storage-not-low constraint is no longer satisfied. 205 * 206 * @see JobInfo.Builder#setRequiresStorageNotLow(boolean) 207 */ 208 public static final int STOP_REASON_CONSTRAINT_STORAGE_NOT_LOW = 9; 209 /** 210 * The app has consumed all of its current quota. Each app is assigned a quota of how much 211 * it can run jobs within a certain time frame. The quota is informed, in part, by app standby 212 * buckets. Once an app has used up all of its quota, it won't be able to start jobs until 213 * quota is replenished, is changed, or is temporarily not applied. 214 * 215 * @see UsageStatsManager#getAppStandbyBucket() 216 */ 217 public static final int STOP_REASON_QUOTA = 10; 218 /** 219 * The app is restricted from running in the background. 220 * 221 * @see ActivityManager#isBackgroundRestricted() 222 * @see PackageManager#isInstantApp() 223 */ 224 public static final int STOP_REASON_BACKGROUND_RESTRICTION = 11; 225 /** 226 * The current standby bucket requires that the job stop now. 227 * 228 * @see UsageStatsManager#STANDBY_BUCKET_RESTRICTED 229 */ 230 public static final int STOP_REASON_APP_STANDBY = 12; 231 /** 232 * The user stopped the job. This can happen either through force-stop, adb shell commands, 233 * or uninstalling. 234 */ 235 public static final int STOP_REASON_USER = 13; 236 /** The system is doing some processing that requires stopping this job. */ 237 public static final int STOP_REASON_SYSTEM_PROCESSING = 14; 238 /** 239 * The system's estimate of when the app will be launched changed significantly enough to 240 * decide this job shouldn't be running right now. This will mostly apply to prefetch jobs. 241 * 242 * @see JobInfo#isPrefetch() 243 * @see JobInfo.Builder#setPrefetch(boolean) 244 */ 245 public static final int STOP_REASON_ESTIMATED_APP_LAUNCH_TIME_CHANGED = 15; 246 247 /** @hide */ 248 @IntDef(prefix = {"STOP_REASON_"}, value = { 249 STOP_REASON_UNDEFINED, 250 STOP_REASON_CANCELLED_BY_APP, 251 STOP_REASON_PREEMPT, 252 STOP_REASON_TIMEOUT, 253 STOP_REASON_DEVICE_STATE, 254 STOP_REASON_CONSTRAINT_BATTERY_NOT_LOW, 255 STOP_REASON_CONSTRAINT_CHARGING, 256 STOP_REASON_CONSTRAINT_CONNECTIVITY, 257 STOP_REASON_CONSTRAINT_DEVICE_IDLE, 258 STOP_REASON_CONSTRAINT_STORAGE_NOT_LOW, 259 STOP_REASON_QUOTA, 260 STOP_REASON_BACKGROUND_RESTRICTION, 261 STOP_REASON_APP_STANDBY, 262 STOP_REASON_USER, 263 STOP_REASON_SYSTEM_PROCESSING, 264 STOP_REASON_ESTIMATED_APP_LAUNCH_TIME_CHANGED, 265 }) 266 @Retention(RetentionPolicy.SOURCE) 267 public @interface StopReason { 268 } 269 270 @UnsupportedAppUsage 271 private final int jobId; 272 private final PersistableBundle extras; 273 private final Bundle transientExtras; 274 private final ClipData clipData; 275 private final int clipGrantFlags; 276 @UnsupportedAppUsage 277 private final IBinder callback; 278 private final boolean overrideDeadlineExpired; 279 private final boolean mIsExpedited; 280 private final Uri[] mTriggeredContentUris; 281 private final String[] mTriggeredContentAuthorities; 282 private final Network network; 283 284 private int mStopReason = STOP_REASON_UNDEFINED; 285 private int mInternalStopReason = INTERNAL_STOP_REASON_UNKNOWN; 286 private String debugStopReason; // Human readable stop reason for debugging. 287 288 /** @hide */ JobParameters(IBinder callback, int jobId, PersistableBundle extras, Bundle transientExtras, ClipData clipData, int clipGrantFlags, boolean overrideDeadlineExpired, boolean isExpedited, Uri[] triggeredContentUris, String[] triggeredContentAuthorities, Network network)289 public JobParameters(IBinder callback, int jobId, PersistableBundle extras, 290 Bundle transientExtras, ClipData clipData, int clipGrantFlags, 291 boolean overrideDeadlineExpired, boolean isExpedited, Uri[] triggeredContentUris, 292 String[] triggeredContentAuthorities, Network network) { 293 this.jobId = jobId; 294 this.extras = extras; 295 this.transientExtras = transientExtras; 296 this.clipData = clipData; 297 this.clipGrantFlags = clipGrantFlags; 298 this.callback = callback; 299 this.overrideDeadlineExpired = overrideDeadlineExpired; 300 this.mIsExpedited = isExpedited; 301 this.mTriggeredContentUris = triggeredContentUris; 302 this.mTriggeredContentAuthorities = triggeredContentAuthorities; 303 this.network = network; 304 } 305 306 /** 307 * @return The unique id of this job, specified at creation time. 308 */ getJobId()309 public int getJobId() { 310 return jobId; 311 } 312 313 /** 314 * @return The reason {@link JobService#onStopJob(JobParameters)} was called on this job. Will 315 * be {@link #STOP_REASON_UNDEFINED} if {@link JobService#onStopJob(JobParameters)} has not 316 * yet been called. 317 */ 318 @StopReason getStopReason()319 public int getStopReason() { 320 return mStopReason; 321 } 322 323 /** @hide */ getInternalStopReasonCode()324 public int getInternalStopReasonCode() { 325 return mInternalStopReason; 326 } 327 328 /** 329 * Reason onStopJob() was called on this job. 330 * 331 * @hide 332 */ getDebugStopReason()333 public String getDebugStopReason() { 334 return debugStopReason; 335 } 336 337 /** 338 * @return The extras you passed in when constructing this job with 339 * {@link android.app.job.JobInfo.Builder#setExtras(android.os.PersistableBundle)}. This will 340 * never be null. If you did not set any extras this will be an empty bundle. 341 */ getExtras()342 public @NonNull PersistableBundle getExtras() { 343 return extras; 344 } 345 346 /** 347 * @return The transient extras you passed in when constructing this job with 348 * {@link android.app.job.JobInfo.Builder#setTransientExtras(android.os.Bundle)}. This will 349 * never be null. If you did not set any extras this will be an empty bundle. 350 */ getTransientExtras()351 public @NonNull Bundle getTransientExtras() { 352 return transientExtras; 353 } 354 355 /** 356 * @return The clip you passed in when constructing this job with 357 * {@link android.app.job.JobInfo.Builder#setClipData(ClipData, int)}. Will be null 358 * if it was not set. 359 */ getClipData()360 public @Nullable ClipData getClipData() { 361 return clipData; 362 } 363 364 /** 365 * @return The clip grant flags you passed in when constructing this job with 366 * {@link android.app.job.JobInfo.Builder#setClipData(ClipData, int)}. Will be 0 367 * if it was not set. 368 */ getClipGrantFlags()369 public int getClipGrantFlags() { 370 return clipGrantFlags; 371 } 372 373 /** 374 * @return Whether this job is running as an expedited job or not. A job is guaranteed to have 375 * all expedited job guarantees for the duration of the job execution if this returns 376 * {@code true}. This will return {@code false} if the job that wasn't requested to run as a 377 * expedited job, or if it was requested to run as an expedited job but the app didn't have 378 * any remaining expedited job quota at the time of execution. 379 * 380 * @see JobInfo.Builder#setExpedited(boolean) 381 */ isExpeditedJob()382 public boolean isExpeditedJob() { 383 return mIsExpedited; 384 } 385 386 /** 387 * For jobs with {@link android.app.job.JobInfo.Builder#setOverrideDeadline(long)} set, this 388 * provides an easy way to tell whether the job is being executed due to the deadline 389 * expiring. Note: If the job is running because its deadline expired, it implies that its 390 * constraints will not be met. 391 */ isOverrideDeadlineExpired()392 public boolean isOverrideDeadlineExpired() { 393 return overrideDeadlineExpired; 394 } 395 396 /** 397 * For jobs with {@link android.app.job.JobInfo.Builder#addTriggerContentUri} set, this 398 * reports which URIs have triggered the job. This will be null if either no URIs have 399 * triggered it (it went off due to a deadline or other reason), or the number of changed 400 * URIs is too large to report. Whether or not the number of URIs is too large, you can 401 * always use {@link #getTriggeredContentAuthorities()} to determine whether the job was 402 * triggered due to any content changes and the authorities they are associated with. 403 */ getTriggeredContentUris()404 public @Nullable Uri[] getTriggeredContentUris() { 405 return mTriggeredContentUris; 406 } 407 408 /** 409 * For jobs with {@link android.app.job.JobInfo.Builder#addTriggerContentUri} set, this 410 * reports which content authorities have triggered the job. It will only be null if no 411 * authorities have triggered it -- that is, the job executed for some other reason, such 412 * as a deadline expiring. If this is non-null, you can use {@link #getTriggeredContentUris()} 413 * to retrieve the details of which URIs changed (as long as that has not exceeded the maximum 414 * number it can reported). 415 */ getTriggeredContentAuthorities()416 public @Nullable String[] getTriggeredContentAuthorities() { 417 return mTriggeredContentAuthorities; 418 } 419 420 /** 421 * Return the network that should be used to perform any network requests 422 * for this job. 423 * <p> 424 * Devices may have multiple active network connections simultaneously, or 425 * they may not have a default network route at all. To correctly handle all 426 * situations like this, your job should always use the network returned by 427 * this method instead of implicitly using the default network route. 428 * <p> 429 * Note that the system may relax the constraints you originally requested, 430 * such as allowing a {@link JobInfo#NETWORK_TYPE_UNMETERED} job to run over 431 * a metered network when there is a surplus of metered data available. 432 * 433 * @return the network that should be used to perform any network requests 434 * for this job, or {@code null} if this job didn't set any required 435 * network type or if the job executed when there was no available network to use. 436 * @see JobInfo.Builder#setRequiredNetworkType(int) 437 * @see JobInfo.Builder#setRequiredNetwork(NetworkRequest) 438 */ getNetwork()439 public @Nullable Network getNetwork() { 440 return network; 441 } 442 443 /** 444 * Dequeue the next pending {@link JobWorkItem} from these JobParameters associated with their 445 * currently running job. Calling this method when there is no more work available and all 446 * previously dequeued work has been completed will result in the system taking care of 447 * stopping the job for you -- 448 * you should not call {@link JobService#jobFinished(JobParameters, boolean)} yourself 449 * (otherwise you risk losing an upcoming JobWorkItem that is being enqueued at the same time). 450 * 451 * <p>Once you are done with the {@link JobWorkItem} returned by this method, you must call 452 * {@link #completeWork(JobWorkItem)} with it to inform the system that you are done 453 * executing the work. The job will not be finished until all dequeued work has been 454 * completed. You do not, however, have to complete each returned work item before deqeueing 455 * the next one -- you can use {@link #dequeueWork()} multiple times before completing 456 * previous work if you want to process work in parallel, and you can complete the work 457 * in whatever order you want.</p> 458 * 459 * <p>If the job runs to the end of its available time period before all work has been 460 * completed, it will stop as normal. You should return true from 461 * {@link JobService#onStopJob(JobParameters)} in order to have the job rescheduled, and by 462 * doing so any pending as well as remaining uncompleted work will be re-queued 463 * for the next time the job runs.</p> 464 * 465 * <p>This example shows how to construct a JobService that will serially dequeue and 466 * process work that is available for it:</p> 467 * 468 * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/JobWorkService.java 469 * service} 470 * 471 * @return Returns a new {@link JobWorkItem} if there is one pending, otherwise null. 472 * If null is returned, the system will also stop the job if all work has also been completed. 473 * (This means that for correct operation, you must always call dequeueWork() after you have 474 * completed other work, to check either for more work or allow the system to stop the job.) 475 */ dequeueWork()476 public @Nullable JobWorkItem dequeueWork() { 477 try { 478 return getCallback().dequeueWork(getJobId()); 479 } catch (RemoteException e) { 480 throw e.rethrowFromSystemServer(); 481 } 482 } 483 484 /** 485 * Report the completion of executing a {@link JobWorkItem} previously returned by 486 * {@link #dequeueWork()}. This tells the system you are done with the 487 * work associated with that item, so it will not be returned again. Note that if this 488 * is the last work in the queue, completing it here will <em>not</em> finish the overall 489 * job -- for that to happen, you still need to call {@link #dequeueWork()} 490 * again. 491 * 492 * <p>If you are enqueueing work into a job, you must call this method for each piece 493 * of work you process. Do <em>not</em> call 494 * {@link JobService#jobFinished(JobParameters, boolean)} 495 * or else you can lose work in your queue.</p> 496 * 497 * @param work The work you have completed processing, as previously returned by 498 * {@link #dequeueWork()} 499 */ completeWork(@onNull JobWorkItem work)500 public void completeWork(@NonNull JobWorkItem work) { 501 try { 502 if (!getCallback().completeWork(getJobId(), work.getWorkId())) { 503 throw new IllegalArgumentException("Given work is not active: " + work); 504 } 505 } catch (RemoteException e) { 506 throw e.rethrowFromSystemServer(); 507 } 508 } 509 510 /** @hide */ 511 @UnsupportedAppUsage getCallback()512 public IJobCallback getCallback() { 513 return IJobCallback.Stub.asInterface(callback); 514 } 515 JobParameters(Parcel in)516 private JobParameters(Parcel in) { 517 jobId = in.readInt(); 518 extras = in.readPersistableBundle(); 519 transientExtras = in.readBundle(); 520 if (in.readInt() != 0) { 521 clipData = ClipData.CREATOR.createFromParcel(in); 522 clipGrantFlags = in.readInt(); 523 } else { 524 clipData = null; 525 clipGrantFlags = 0; 526 } 527 callback = in.readStrongBinder(); 528 overrideDeadlineExpired = in.readInt() == 1; 529 mIsExpedited = in.readBoolean(); 530 mTriggeredContentUris = in.createTypedArray(Uri.CREATOR); 531 mTriggeredContentAuthorities = in.createStringArray(); 532 if (in.readInt() != 0) { 533 network = Network.CREATOR.createFromParcel(in); 534 } else { 535 network = null; 536 } 537 mStopReason = in.readInt(); 538 mInternalStopReason = in.readInt(); 539 debugStopReason = in.readString(); 540 } 541 542 /** @hide */ setStopReason(@topReason int reason, int internalStopReason, String debugStopReason)543 public void setStopReason(@StopReason int reason, int internalStopReason, 544 String debugStopReason) { 545 mStopReason = reason; 546 mInternalStopReason = internalStopReason; 547 this.debugStopReason = debugStopReason; 548 } 549 550 @Override describeContents()551 public int describeContents() { 552 return 0; 553 } 554 555 @Override writeToParcel(Parcel dest, int flags)556 public void writeToParcel(Parcel dest, int flags) { 557 dest.writeInt(jobId); 558 dest.writePersistableBundle(extras); 559 dest.writeBundle(transientExtras); 560 if (clipData != null) { 561 dest.writeInt(1); 562 clipData.writeToParcel(dest, flags); 563 dest.writeInt(clipGrantFlags); 564 } else { 565 dest.writeInt(0); 566 } 567 dest.writeStrongBinder(callback); 568 dest.writeInt(overrideDeadlineExpired ? 1 : 0); 569 dest.writeBoolean(mIsExpedited); 570 dest.writeTypedArray(mTriggeredContentUris, flags); 571 dest.writeStringArray(mTriggeredContentAuthorities); 572 if (network != null) { 573 dest.writeInt(1); 574 network.writeToParcel(dest, flags); 575 } else { 576 dest.writeInt(0); 577 } 578 dest.writeInt(mStopReason); 579 dest.writeInt(mInternalStopReason); 580 dest.writeString(debugStopReason); 581 } 582 583 public static final @android.annotation.NonNull Creator<JobParameters> CREATOR = new Creator<JobParameters>() { 584 @Override 585 public JobParameters createFromParcel(Parcel in) { 586 return new JobParameters(in); 587 } 588 589 @Override 590 public JobParameters[] newArray(int size) { 591 return new JobParameters[size]; 592 } 593 }; 594 } 595