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. However,
442      * {@link android.app.job.JobInfo.Builder#setPeriodic(long) periodic jobs} will only ever
443      * run when their constraints are satisfied, therefore, the constraints will still be satisfied
444      * for a periodic job even if the deadline has expired.
445      */
isOverrideDeadlineExpired()446     public boolean isOverrideDeadlineExpired() {
447         return overrideDeadlineExpired;
448     }
449 
450     /**
451      * For jobs with {@link android.app.job.JobInfo.Builder#addTriggerContentUri} set, this
452      * reports which URIs have triggered the job.  This will be null if either no URIs have
453      * triggered it (it went off due to a deadline or other reason), or the number of changed
454      * URIs is too large to report.  Whether or not the number of URIs is too large, you can
455      * always use {@link #getTriggeredContentAuthorities()} to determine whether the job was
456      * triggered due to any content changes and the authorities they are associated with.
457      */
getTriggeredContentUris()458     public @Nullable Uri[] getTriggeredContentUris() {
459         return mTriggeredContentUris;
460     }
461 
462     /**
463      * For jobs with {@link android.app.job.JobInfo.Builder#addTriggerContentUri} set, this
464      * reports which content authorities have triggered the job.  It will only be null if no
465      * authorities have triggered it -- that is, the job executed for some other reason, such
466      * as a deadline expiring.  If this is non-null, you can use {@link #getTriggeredContentUris()}
467      * to retrieve the details of which URIs changed (as long as that has not exceeded the maximum
468      * number it can reported).
469      */
getTriggeredContentAuthorities()470     public @Nullable String[] getTriggeredContentAuthorities() {
471         return mTriggeredContentAuthorities;
472     }
473 
474     /**
475      * Return the network that should be used to perform any network requests
476      * for this job.
477      * <p>
478      * Devices may have multiple active network connections simultaneously, or
479      * they may not have a default network route at all. To correctly handle all
480      * situations like this, your job should always use the network returned by
481      * this method instead of implicitly using the default network route.
482      * <p>
483      * Note that the system may relax the constraints you originally requested,
484      * such as allowing a {@link JobInfo#NETWORK_TYPE_UNMETERED} job to run over
485      * a metered network when there is a surplus of metered data available.
486      *
487      * Starting in Android version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
488      * this will return {@code null} if the app does not hold the permissions specified in
489      * {@link JobInfo.Builder#setRequiredNetwork(NetworkRequest)}.
490      *
491      * @return the network that should be used to perform any network requests
492      *         for this job, or {@code null} if this job didn't set any required
493      *         network type or if the job executed when there was no available network to use.
494      * @see JobInfo.Builder#setRequiredNetworkType(int)
495      * @see JobInfo.Builder#setRequiredNetwork(NetworkRequest)
496      */
getNetwork()497     public @Nullable Network getNetwork() {
498         return mNetwork;
499     }
500 
501     /**
502      * Dequeue the next pending {@link JobWorkItem} from these JobParameters associated with their
503      * currently running job.  Calling this method when there is no more work available and all
504      * previously dequeued work has been completed will result in the system taking care of
505      * stopping the job for you --
506      * you should not call {@link JobService#jobFinished(JobParameters, boolean)} yourself
507      * (otherwise you risk losing an upcoming JobWorkItem that is being enqueued at the same time).
508      *
509      * <p>Once you are done with the {@link JobWorkItem} returned by this method, you must call
510      * {@link #completeWork(JobWorkItem)} with it to inform the system that you are done
511      * executing the work.  The job will not be finished until all dequeued work has been
512      * completed.  You do not, however, have to complete each returned work item before deqeueing
513      * the next one -- you can use {@link #dequeueWork()} multiple times before completing
514      * previous work if you want to process work in parallel, and you can complete the work
515      * in whatever order you want.</p>
516      *
517      * <p>If the job runs to the end of its available time period before all work has been
518      * completed, it will stop as normal.  You should return true from
519      * {@link JobService#onStopJob(JobParameters)} in order to have the job rescheduled, and by
520      * doing so any pending as well as remaining uncompleted work will be re-queued
521      * for the next time the job runs.</p>
522      *
523      * <p>This example shows how to construct a JobService that will serially dequeue and
524      * process work that is available for it:</p>
525      *
526      * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/JobWorkService.java
527      *      service}
528      *
529      * @return Returns a new {@link JobWorkItem} if there is one pending, otherwise null.
530      * If null is returned, the system will also stop the job if all work has also been completed.
531      * (This means that for correct operation, you must always call dequeueWork() after you have
532      * completed other work, to check either for more work or allow the system to stop the job.)
533      */
dequeueWork()534     public @Nullable JobWorkItem dequeueWork() {
535         try {
536             return getCallback().dequeueWork(getJobId());
537         } catch (RemoteException e) {
538             throw e.rethrowFromSystemServer();
539         }
540     }
541 
542     /**
543      * Report the completion of executing a {@link JobWorkItem} previously returned by
544      * {@link #dequeueWork()}.  This tells the system you are done with the
545      * work associated with that item, so it will not be returned again.  Note that if this
546      * is the last work in the queue, completing it here will <em>not</em> finish the overall
547      * job -- for that to happen, you still need to call {@link #dequeueWork()}
548      * again.
549      *
550      * <p>If you are enqueueing work into a job, you must call this method for each piece
551      * of work you process.  Do <em>not</em> call
552      * {@link JobService#jobFinished(JobParameters, boolean)}
553      * or else you can lose work in your queue.</p>
554      *
555      * @param work The work you have completed processing, as previously returned by
556      * {@link #dequeueWork()}
557      */
completeWork(@onNull JobWorkItem work)558     public void completeWork(@NonNull JobWorkItem work) {
559         try {
560             if (!getCallback().completeWork(getJobId(), work.getWorkId())) {
561                 throw new IllegalArgumentException("Given work is not active: " + work);
562             }
563         } catch (RemoteException e) {
564             throw e.rethrowFromSystemServer();
565         }
566     }
567 
568     /** @hide */
569     @UnsupportedAppUsage
getCallback()570     public IJobCallback getCallback() {
571         return IJobCallback.Stub.asInterface(callback);
572     }
573 
JobParameters(Parcel in)574     private JobParameters(Parcel in) {
575         jobId = in.readInt();
576         mJobNamespace = in.readString();
577         extras = in.readPersistableBundle();
578         transientExtras = in.readBundle();
579         if (in.readInt() != 0) {
580             clipData = ClipData.CREATOR.createFromParcel(in);
581             clipGrantFlags = in.readInt();
582         } else {
583             clipData = null;
584             clipGrantFlags = 0;
585         }
586         callback = in.readStrongBinder();
587         overrideDeadlineExpired = in.readInt() == 1;
588         mIsExpedited = in.readBoolean();
589         mIsUserInitiated = in.readBoolean();
590         mTriggeredContentUris = in.createTypedArray(Uri.CREATOR);
591         mTriggeredContentAuthorities = in.createStringArray();
592         if (in.readInt() != 0) {
593             mNetwork = Network.CREATOR.createFromParcel(in);
594         } else {
595             mNetwork = null;
596         }
597         mStopReason = in.readInt();
598         mInternalStopReason = in.readInt();
599         debugStopReason = in.readString();
600     }
601 
602     /** @hide */
setNetwork(@ullable Network network)603     public void setNetwork(@Nullable Network network) {
604         mNetwork = network;
605     }
606 
607     /** @hide */
setStopReason(@topReason int reason, int internalStopReason, String debugStopReason)608     public void setStopReason(@StopReason int reason, int internalStopReason,
609             String debugStopReason) {
610         mStopReason = reason;
611         mInternalStopReason = internalStopReason;
612         this.debugStopReason = debugStopReason;
613     }
614 
615     @Override
describeContents()616     public int describeContents() {
617         return 0;
618     }
619 
620     @Override
writeToParcel(Parcel dest, int flags)621     public void writeToParcel(Parcel dest, int flags) {
622         dest.writeInt(jobId);
623         dest.writeString(mJobNamespace);
624         dest.writePersistableBundle(extras);
625         dest.writeBundle(transientExtras);
626         if (clipData != null) {
627             dest.writeInt(1);
628             clipData.writeToParcel(dest, flags);
629             dest.writeInt(clipGrantFlags);
630         } else {
631             dest.writeInt(0);
632         }
633         dest.writeStrongBinder(callback);
634         dest.writeInt(overrideDeadlineExpired ? 1 : 0);
635         dest.writeBoolean(mIsExpedited);
636         dest.writeBoolean(mIsUserInitiated);
637         dest.writeTypedArray(mTriggeredContentUris, flags);
638         dest.writeStringArray(mTriggeredContentAuthorities);
639         if (mNetwork != null) {
640             dest.writeInt(1);
641             mNetwork.writeToParcel(dest, flags);
642         } else {
643             dest.writeInt(0);
644         }
645         dest.writeInt(mStopReason);
646         dest.writeInt(mInternalStopReason);
647         dest.writeString(debugStopReason);
648     }
649 
650     public static final @android.annotation.NonNull Creator<JobParameters> CREATOR = new Creator<JobParameters>() {
651         @Override
652         public JobParameters createFromParcel(Parcel in) {
653             return new JobParameters(in);
654         }
655 
656         @Override
657         public JobParameters[] newArray(int size) {
658             return new JobParameters[size];
659         }
660     };
661 }
662