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