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