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