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 static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
20 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
21 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
22 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
23 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
24 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
25 import static android.util.TimeUtils.formatDuration;
26 
27 import android.annotation.BytesLong;
28 import android.annotation.IntDef;
29 import android.annotation.NonNull;
30 import android.annotation.Nullable;
31 import android.annotation.RequiresPermission;
32 import android.compat.annotation.UnsupportedAppUsage;
33 import android.content.ClipData;
34 import android.content.ComponentName;
35 import android.net.NetworkRequest;
36 import android.net.NetworkSpecifier;
37 import android.net.Uri;
38 import android.os.BaseBundle;
39 import android.os.Build;
40 import android.os.Bundle;
41 import android.os.Parcel;
42 import android.os.Parcelable;
43 import android.os.PersistableBundle;
44 import android.util.Log;
45 
46 import java.lang.annotation.Retention;
47 import java.lang.annotation.RetentionPolicy;
48 import java.util.ArrayList;
49 import java.util.Arrays;
50 import java.util.Objects;
51 
52 /**
53  * Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the
54  * parameters required to schedule work against the calling application. These are constructed
55  * using the {@link JobInfo.Builder}.
56  * You must specify at least one sort of constraint on the JobInfo object that you are creating.
57  * The goal here is to provide the scheduler with high-level semantics about the work you want to
58  * accomplish. Doing otherwise with throw an exception in your app.
59  */
60 public class JobInfo implements Parcelable {
61     private static String TAG = "JobInfo";
62 
63     /** @hide */
64     @IntDef(prefix = { "NETWORK_TYPE_" }, value = {
65             NETWORK_TYPE_NONE,
66             NETWORK_TYPE_ANY,
67             NETWORK_TYPE_UNMETERED,
68             NETWORK_TYPE_NOT_ROAMING,
69             NETWORK_TYPE_CELLULAR,
70     })
71     @Retention(RetentionPolicy.SOURCE)
72     public @interface NetworkType {}
73 
74     /** Default. */
75     public static final int NETWORK_TYPE_NONE = 0;
76     /** This job requires network connectivity. */
77     public static final int NETWORK_TYPE_ANY = 1;
78     /** This job requires network connectivity that is unmetered. */
79     public static final int NETWORK_TYPE_UNMETERED = 2;
80     /** This job requires network connectivity that is not roaming. */
81     public static final int NETWORK_TYPE_NOT_ROAMING = 3;
82     /** This job requires network connectivity that is a cellular network. */
83     public static final int NETWORK_TYPE_CELLULAR = 4;
84 
85     /**
86      * This job requires metered connectivity such as most cellular data
87      * networks.
88      *
89      * @deprecated Cellular networks may be unmetered, or Wi-Fi networks may be
90      *             metered, so this isn't a good way of selecting a specific
91      *             transport. Instead, use {@link #NETWORK_TYPE_CELLULAR} or
92      *             {@link android.net.NetworkRequest.Builder#addTransportType(int)}
93      *             if your job requires a specific network transport.
94      */
95     @Deprecated
96     public static final int NETWORK_TYPE_METERED = NETWORK_TYPE_CELLULAR;
97 
98     /** Sentinel value indicating that bytes are unknown. */
99     public static final int NETWORK_BYTES_UNKNOWN = -1;
100 
101     /**
102      * Amount of backoff a job has initially by default, in milliseconds.
103      */
104     public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L;  // 30 seconds.
105 
106     /**
107      * Maximum backoff we allow for a job, in milliseconds.
108      */
109     public static final long MAX_BACKOFF_DELAY_MILLIS = 5 * 60 * 60 * 1000;  // 5 hours.
110 
111     /** @hide */
112     @IntDef(prefix = { "BACKOFF_POLICY_" }, value = {
113             BACKOFF_POLICY_LINEAR,
114             BACKOFF_POLICY_EXPONENTIAL,
115     })
116     @Retention(RetentionPolicy.SOURCE)
117     public @interface BackoffPolicy {}
118 
119     /**
120      * Linearly back-off a failed job. See
121      * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)}
122      * retry_time(current_time, num_failures) =
123      *     current_time + initial_backoff_millis * num_failures, num_failures >= 1
124      */
125     public static final int BACKOFF_POLICY_LINEAR = 0;
126 
127     /**
128      * Exponentially back-off a failed job. See
129      * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)}
130      *
131      * retry_time(current_time, num_failures) =
132      *     current_time + initial_backoff_millis * 2 ^ (num_failures - 1), num_failures >= 1
133      */
134     public static final int BACKOFF_POLICY_EXPONENTIAL = 1;
135 
136     /* Minimum interval for a periodic job, in milliseconds. */
137     private static final long MIN_PERIOD_MILLIS = 15 * 60 * 1000L;   // 15 minutes
138 
139     /* Minimum flex for a periodic job, in milliseconds. */
140     private static final long MIN_FLEX_MILLIS = 5 * 60 * 1000L; // 5 minutes
141 
142     /**
143      * Minimum backoff interval for a job, in milliseconds
144      * @hide
145      */
146     public static final long MIN_BACKOFF_MILLIS = 10 * 1000L;      // 10 seconds
147 
148     /**
149      * Query the minimum interval allowed for periodic scheduled jobs.  Attempting
150      * to declare a smaller period that this when scheduling a job will result in a
151      * job that is still periodic, but will run with this effective period.
152      *
153      * @return The minimum available interval for scheduling periodic jobs, in milliseconds.
154      */
getMinPeriodMillis()155     public static final long getMinPeriodMillis() {
156         return MIN_PERIOD_MILLIS;
157     }
158 
159     /**
160      * Query the minimum flex time allowed for periodic scheduled jobs.  Attempting
161      * to declare a shorter flex time than this when scheduling such a job will
162      * result in this amount as the effective flex time for the job.
163      *
164      * @return The minimum available flex time for scheduling periodic jobs, in milliseconds.
165      */
getMinFlexMillis()166     public static final long getMinFlexMillis() {
167         return MIN_FLEX_MILLIS;
168     }
169 
170     /**
171      * Query the minimum automatic-reschedule backoff interval permitted for jobs.
172      * @hide
173      */
getMinBackoffMillis()174     public static final long getMinBackoffMillis() {
175         return MIN_BACKOFF_MILLIS;
176     }
177 
178     /**
179      * Default type of backoff.
180      * @hide
181      */
182     public static final int DEFAULT_BACKOFF_POLICY = BACKOFF_POLICY_EXPONENTIAL;
183 
184     /**
185      * Default of {@link #getPriority}.
186      * @hide
187      */
188     public static final int PRIORITY_DEFAULT = 0;
189 
190     /**
191      * Value of {@link #getPriority} for expedited syncs.
192      * @hide
193      */
194     public static final int PRIORITY_SYNC_EXPEDITED = 10;
195 
196     /**
197      * Value of {@link #getPriority} for first time initialization syncs.
198      * @hide
199      */
200     public static final int PRIORITY_SYNC_INITIALIZATION = 20;
201 
202     /**
203      * Value of {@link #getPriority} for a BFGS app (overrides the supplied
204      * JobInfo priority if it is smaller).
205      * @hide
206      */
207     public static final int PRIORITY_BOUND_FOREGROUND_SERVICE = 30;
208 
209     /** @hide For backward compatibility. */
210     @UnsupportedAppUsage
211     public static final int PRIORITY_FOREGROUND_APP = PRIORITY_BOUND_FOREGROUND_SERVICE;
212 
213     /**
214      * Value of {@link #getPriority} for a FG service app (overrides the supplied
215      * JobInfo priority if it is smaller).
216      * @hide
217      */
218     @UnsupportedAppUsage
219     public static final int PRIORITY_FOREGROUND_SERVICE = 35;
220 
221     /**
222      * Value of {@link #getPriority} for the current top app (overrides the supplied
223      * JobInfo priority if it is smaller).
224      * @hide
225      */
226     public static final int PRIORITY_TOP_APP = 40;
227 
228     /**
229      * Adjustment of {@link #getPriority} if the app has often (50% or more of the time)
230      * been running jobs.
231      * @hide
232      */
233     public static final int PRIORITY_ADJ_OFTEN_RUNNING = -40;
234 
235     /**
236      * Adjustment of {@link #getPriority} if the app has always (90% or more of the time)
237      * been running jobs.
238      * @hide
239      */
240     public static final int PRIORITY_ADJ_ALWAYS_RUNNING = -80;
241 
242     /**
243      * Indicates that the implementation of this job will be using
244      * {@link JobService#startForeground(int, android.app.Notification)} to run
245      * in the foreground.
246      * <p>
247      * When set, the internal scheduling of this job will ignore any background
248      * network restrictions for the requesting app. Note that this flag alone
249      * doesn't actually place your {@link JobService} in the foreground; you
250      * still need to post the notification yourself.
251      * <p>
252      * To use this flag, the caller must hold the
253      * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL} permission.
254      *
255      * @hide
256      */
257     @UnsupportedAppUsage
258     public static final int FLAG_WILL_BE_FOREGROUND = 1 << 0;
259 
260     /**
261      * Allows this job to run despite doze restrictions as long as the app is in the foreground
262      * or on the temporary whitelist
263      * @hide
264      */
265     public static final int FLAG_IMPORTANT_WHILE_FOREGROUND = 1 << 1;
266 
267     /**
268      * @hide
269      */
270     public static final int FLAG_PREFETCH = 1 << 2;
271 
272     /**
273      * This job needs to be exempted from the app standby throttling. Only the system (UID 1000)
274      * can set it. Jobs with a time constrant must not have it.
275      *
276      * @hide
277      */
278     public static final int FLAG_EXEMPT_FROM_APP_STANDBY = 1 << 3;
279 
280     /**
281      * @hide
282      */
283     public static final int CONSTRAINT_FLAG_CHARGING = 1 << 0;
284 
285     /**
286      * @hide
287      */
288     public static final int CONSTRAINT_FLAG_BATTERY_NOT_LOW = 1 << 1;
289 
290     /**
291      * @hide
292      */
293     public static final int CONSTRAINT_FLAG_DEVICE_IDLE = 1 << 2;
294 
295     /**
296      * @hide
297      */
298     public static final int CONSTRAINT_FLAG_STORAGE_NOT_LOW = 1 << 3;
299 
300     @UnsupportedAppUsage
301     private final int jobId;
302     private final PersistableBundle extras;
303     private final Bundle transientExtras;
304     private final ClipData clipData;
305     private final int clipGrantFlags;
306     @UnsupportedAppUsage
307     private final ComponentName service;
308     private final int constraintFlags;
309     private final TriggerContentUri[] triggerContentUris;
310     private final long triggerContentUpdateDelay;
311     private final long triggerContentMaxDelay;
312     private final boolean hasEarlyConstraint;
313     private final boolean hasLateConstraint;
314     private final NetworkRequest networkRequest;
315     private final long networkDownloadBytes;
316     private final long networkUploadBytes;
317     private final long minLatencyMillis;
318     private final long maxExecutionDelayMillis;
319     private final boolean isPeriodic;
320     private final boolean isPersisted;
321     private final long intervalMillis;
322     private final long flexMillis;
323     private final long initialBackoffMillis;
324     private final int backoffPolicy;
325     private final int priority;
326     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
327     private final int flags;
328 
329     /**
330      * Unique job id associated with this application (uid).  This is the same job ID
331      * you supplied in the {@link Builder} constructor.
332      */
getId()333     public int getId() {
334         return jobId;
335     }
336 
337     /**
338      * @see JobInfo.Builder#setExtras(PersistableBundle)
339      */
getExtras()340     public @NonNull PersistableBundle getExtras() {
341         return extras;
342     }
343 
344     /**
345      * @see JobInfo.Builder#setTransientExtras(Bundle)
346      */
getTransientExtras()347     public @NonNull Bundle getTransientExtras() {
348         return transientExtras;
349     }
350 
351     /**
352      * @see JobInfo.Builder#setClipData(ClipData, int)
353      */
getClipData()354     public @Nullable ClipData getClipData() {
355         return clipData;
356     }
357 
358     /**
359      * @see JobInfo.Builder#setClipData(ClipData, int)
360      */
getClipGrantFlags()361     public int getClipGrantFlags() {
362         return clipGrantFlags;
363     }
364 
365     /**
366      * Name of the service endpoint that will be called back into by the JobScheduler.
367      */
getService()368     public @NonNull ComponentName getService() {
369         return service;
370     }
371 
372     /** @hide */
getPriority()373     public int getPriority() {
374         return priority;
375     }
376 
377     /** @hide */
getFlags()378     public int getFlags() {
379         return flags;
380     }
381 
382     /** @hide */
isExemptedFromAppStandby()383     public boolean isExemptedFromAppStandby() {
384         return ((flags & FLAG_EXEMPT_FROM_APP_STANDBY) != 0) && !isPeriodic();
385     }
386 
387     /**
388      * @see JobInfo.Builder#setRequiresCharging(boolean)
389      */
isRequireCharging()390     public boolean isRequireCharging() {
391         return (constraintFlags & CONSTRAINT_FLAG_CHARGING) != 0;
392     }
393 
394     /**
395      * @see JobInfo.Builder#setRequiresBatteryNotLow(boolean)
396      */
isRequireBatteryNotLow()397     public boolean isRequireBatteryNotLow() {
398         return (constraintFlags & CONSTRAINT_FLAG_BATTERY_NOT_LOW) != 0;
399     }
400 
401     /**
402      * @see JobInfo.Builder#setRequiresDeviceIdle(boolean)
403      */
isRequireDeviceIdle()404     public boolean isRequireDeviceIdle() {
405         return (constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0;
406     }
407 
408     /**
409      * @see JobInfo.Builder#setRequiresStorageNotLow(boolean)
410      */
isRequireStorageNotLow()411     public boolean isRequireStorageNotLow() {
412         return (constraintFlags & CONSTRAINT_FLAG_STORAGE_NOT_LOW) != 0;
413     }
414 
415     /**
416      * @hide
417      */
getConstraintFlags()418     public int getConstraintFlags() {
419         return constraintFlags;
420     }
421 
422     /**
423      * Which content: URIs must change for the job to be scheduled.  Returns null
424      * if there are none required.
425      * @see JobInfo.Builder#addTriggerContentUri(TriggerContentUri)
426      */
getTriggerContentUris()427     public @Nullable TriggerContentUri[] getTriggerContentUris() {
428         return triggerContentUris;
429     }
430 
431     /**
432      * When triggering on content URI changes, this is the delay from when a change
433      * is detected until the job is scheduled.
434      * @see JobInfo.Builder#setTriggerContentUpdateDelay(long)
435      */
getTriggerContentUpdateDelay()436     public long getTriggerContentUpdateDelay() {
437         return triggerContentUpdateDelay;
438     }
439 
440     /**
441      * When triggering on content URI changes, this is the maximum delay we will
442      * use before scheduling the job.
443      * @see JobInfo.Builder#setTriggerContentMaxDelay(long)
444      */
getTriggerContentMaxDelay()445     public long getTriggerContentMaxDelay() {
446         return triggerContentMaxDelay;
447     }
448 
449     /**
450      * Return the basic description of the kind of network this job requires.
451      *
452      * @deprecated This method attempts to map {@link #getRequiredNetwork()}
453      *             into the set of simple constants, which results in a loss of
454      *             fidelity. Callers should move to using
455      *             {@link #getRequiredNetwork()} directly.
456      * @see Builder#setRequiredNetworkType(int)
457      */
458     @Deprecated
getNetworkType()459     public @NetworkType int getNetworkType() {
460         if (networkRequest == null) {
461             return NETWORK_TYPE_NONE;
462         } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
463             return NETWORK_TYPE_UNMETERED;
464         } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING)) {
465             return NETWORK_TYPE_NOT_ROAMING;
466         } else if (networkRequest.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
467             return NETWORK_TYPE_CELLULAR;
468         } else {
469             return NETWORK_TYPE_ANY;
470         }
471     }
472 
473     /**
474      * Return the detailed description of the kind of network this job requires,
475      * or {@code null} if no specific kind of network is required.
476      *
477      * @see Builder#setRequiredNetwork(NetworkRequest)
478      */
getRequiredNetwork()479     public @Nullable NetworkRequest getRequiredNetwork() {
480         return networkRequest;
481     }
482 
483     /**
484      * @deprecated replaced by {@link #getEstimatedNetworkDownloadBytes()} and
485      *             {@link #getEstimatedNetworkUploadBytes()}.
486      * @removed
487      */
488     @Deprecated
getEstimatedNetworkBytes()489     public @BytesLong long getEstimatedNetworkBytes() {
490         if (networkDownloadBytes == NETWORK_BYTES_UNKNOWN
491                 && networkUploadBytes == NETWORK_BYTES_UNKNOWN) {
492             return NETWORK_BYTES_UNKNOWN;
493         } else if (networkDownloadBytes == NETWORK_BYTES_UNKNOWN) {
494             return networkUploadBytes;
495         } else if (networkUploadBytes == NETWORK_BYTES_UNKNOWN) {
496             return networkDownloadBytes;
497         } else {
498             return networkDownloadBytes + networkUploadBytes;
499         }
500     }
501 
502     /**
503      * Return the estimated size of download traffic that will be performed by
504      * this job, in bytes.
505      *
506      * @return Estimated size of download traffic, or
507      *         {@link #NETWORK_BYTES_UNKNOWN} when unknown.
508      * @see Builder#setEstimatedNetworkBytes(long, long)
509      */
getEstimatedNetworkDownloadBytes()510     public @BytesLong long getEstimatedNetworkDownloadBytes() {
511         return networkDownloadBytes;
512     }
513 
514     /**
515      * Return the estimated size of upload traffic that will be performed by
516      * this job, in bytes.
517      *
518      * @return Estimated size of upload traffic, or
519      *         {@link #NETWORK_BYTES_UNKNOWN} when unknown.
520      * @see Builder#setEstimatedNetworkBytes(long, long)
521      */
getEstimatedNetworkUploadBytes()522     public @BytesLong long getEstimatedNetworkUploadBytes() {
523         return networkUploadBytes;
524     }
525 
526     /**
527      * Set for a job that does not recur periodically, to specify a delay after which the job
528      * will be eligible for execution. This value is not set if the job recurs periodically.
529      * @see JobInfo.Builder#setMinimumLatency(long)
530      */
getMinLatencyMillis()531     public long getMinLatencyMillis() {
532         return minLatencyMillis;
533     }
534 
535     /**
536      * @see JobInfo.Builder#setOverrideDeadline(long)
537      */
getMaxExecutionDelayMillis()538     public long getMaxExecutionDelayMillis() {
539         return maxExecutionDelayMillis;
540     }
541 
542     /**
543      * Track whether this job will repeat with a given period.
544      * @see JobInfo.Builder#setPeriodic(long)
545      * @see JobInfo.Builder#setPeriodic(long, long)
546      */
isPeriodic()547     public boolean isPeriodic() {
548         return isPeriodic;
549     }
550 
551     /**
552      * @see JobInfo.Builder#setPersisted(boolean)
553      */
isPersisted()554     public boolean isPersisted() {
555         return isPersisted;
556     }
557 
558     /**
559      * Set to the interval between occurrences of this job. This value is <b>not</b> set if the
560      * job does not recur periodically.
561      * @see JobInfo.Builder#setPeriodic(long)
562      * @see JobInfo.Builder#setPeriodic(long, long)
563      */
getIntervalMillis()564     public long getIntervalMillis() {
565         return intervalMillis;
566     }
567 
568     /**
569      * Flex time for this job. Only valid if this is a periodic job.  The job can
570      * execute at any time in a window of flex length at the end of the period.
571      * @see JobInfo.Builder#setPeriodic(long)
572      * @see JobInfo.Builder#setPeriodic(long, long)
573      */
getFlexMillis()574     public long getFlexMillis() {
575         return flexMillis;
576     }
577 
578     /**
579      * The amount of time the JobScheduler will wait before rescheduling a failed job. This value
580      * will be increased depending on the backoff policy specified at job creation time. Defaults
581      * to 30 seconds, minimum is currently 10 seconds.
582      * @see JobInfo.Builder#setBackoffCriteria(long, int)
583      */
getInitialBackoffMillis()584     public long getInitialBackoffMillis() {
585         return initialBackoffMillis;
586     }
587 
588     /**
589      * Return the backoff policy of this job.
590      * @see JobInfo.Builder#setBackoffCriteria(long, int)
591      */
getBackoffPolicy()592     public @BackoffPolicy int getBackoffPolicy() {
593         return backoffPolicy;
594     }
595 
596     /**
597      * @see JobInfo.Builder#setImportantWhileForeground(boolean)
598      */
isImportantWhileForeground()599     public boolean isImportantWhileForeground() {
600         return (flags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0;
601     }
602 
603     /**
604      * @see JobInfo.Builder#setPrefetch(boolean)
605      */
isPrefetch()606     public boolean isPrefetch() {
607         return (flags & FLAG_PREFETCH) != 0;
608     }
609 
610     /**
611      * User can specify an early constraint of 0L, which is valid, so we keep track of whether the
612      * function was called at all.
613      * @hide
614      */
hasEarlyConstraint()615     public boolean hasEarlyConstraint() {
616         return hasEarlyConstraint;
617     }
618 
619     /**
620      * User can specify a late constraint of 0L, which is valid, so we keep track of whether the
621      * function was called at all.
622      * @hide
623      */
hasLateConstraint()624     public boolean hasLateConstraint() {
625         return hasLateConstraint;
626     }
627 
628     @Override
equals(Object o)629     public boolean equals(Object o) {
630         if (!(o instanceof JobInfo)) {
631             return false;
632         }
633         JobInfo j = (JobInfo) o;
634         if (jobId != j.jobId) {
635             return false;
636         }
637         // XXX won't be correct if one is parcelled and the other not.
638         if (!BaseBundle.kindofEquals(extras, j.extras)) {
639             return false;
640         }
641         // XXX won't be correct if one is parcelled and the other not.
642         if (!BaseBundle.kindofEquals(transientExtras, j.transientExtras)) {
643             return false;
644         }
645         // XXX for now we consider two different clip data objects to be different,
646         // regardless of whether their contents are the same.
647         if (clipData != j.clipData) {
648             return false;
649         }
650         if (clipGrantFlags != j.clipGrantFlags) {
651             return false;
652         }
653         if (!Objects.equals(service, j.service)) {
654             return false;
655         }
656         if (constraintFlags != j.constraintFlags) {
657             return false;
658         }
659         if (!Arrays.equals(triggerContentUris, j.triggerContentUris)) {
660             return false;
661         }
662         if (triggerContentUpdateDelay != j.triggerContentUpdateDelay) {
663             return false;
664         }
665         if (triggerContentMaxDelay != j.triggerContentMaxDelay) {
666             return false;
667         }
668         if (hasEarlyConstraint != j.hasEarlyConstraint) {
669             return false;
670         }
671         if (hasLateConstraint != j.hasLateConstraint) {
672             return false;
673         }
674         if (!Objects.equals(networkRequest, j.networkRequest)) {
675             return false;
676         }
677         if (networkDownloadBytes != j.networkDownloadBytes) {
678             return false;
679         }
680         if (networkUploadBytes != j.networkUploadBytes) {
681             return false;
682         }
683         if (minLatencyMillis != j.minLatencyMillis) {
684             return false;
685         }
686         if (maxExecutionDelayMillis != j.maxExecutionDelayMillis) {
687             return false;
688         }
689         if (isPeriodic != j.isPeriodic) {
690             return false;
691         }
692         if (isPersisted != j.isPersisted) {
693             return false;
694         }
695         if (intervalMillis != j.intervalMillis) {
696             return false;
697         }
698         if (flexMillis != j.flexMillis) {
699             return false;
700         }
701         if (initialBackoffMillis != j.initialBackoffMillis) {
702             return false;
703         }
704         if (backoffPolicy != j.backoffPolicy) {
705             return false;
706         }
707         if (priority != j.priority) {
708             return false;
709         }
710         if (flags != j.flags) {
711             return false;
712         }
713         return true;
714     }
715 
716     @Override
hashCode()717     public int hashCode() {
718         int hashCode = jobId;
719         if (extras != null) {
720             hashCode = 31 * hashCode + extras.hashCode();
721         }
722         if (transientExtras != null) {
723             hashCode = 31 * hashCode + transientExtras.hashCode();
724         }
725         if (clipData != null) {
726             hashCode = 31 * hashCode + clipData.hashCode();
727         }
728         hashCode = 31*hashCode + clipGrantFlags;
729         if (service != null) {
730             hashCode = 31 * hashCode + service.hashCode();
731         }
732         hashCode = 31 * hashCode + constraintFlags;
733         if (triggerContentUris != null) {
734             hashCode = 31 * hashCode + Arrays.hashCode(triggerContentUris);
735         }
736         hashCode = 31 * hashCode + Long.hashCode(triggerContentUpdateDelay);
737         hashCode = 31 * hashCode + Long.hashCode(triggerContentMaxDelay);
738         hashCode = 31 * hashCode + Boolean.hashCode(hasEarlyConstraint);
739         hashCode = 31 * hashCode + Boolean.hashCode(hasLateConstraint);
740         if (networkRequest != null) {
741             hashCode = 31 * hashCode + networkRequest.hashCode();
742         }
743         hashCode = 31 * hashCode + Long.hashCode(networkDownloadBytes);
744         hashCode = 31 * hashCode + Long.hashCode(networkUploadBytes);
745         hashCode = 31 * hashCode + Long.hashCode(minLatencyMillis);
746         hashCode = 31 * hashCode + Long.hashCode(maxExecutionDelayMillis);
747         hashCode = 31 * hashCode + Boolean.hashCode(isPeriodic);
748         hashCode = 31 * hashCode + Boolean.hashCode(isPersisted);
749         hashCode = 31 * hashCode + Long.hashCode(intervalMillis);
750         hashCode = 31 * hashCode + Long.hashCode(flexMillis);
751         hashCode = 31 * hashCode + Long.hashCode(initialBackoffMillis);
752         hashCode = 31 * hashCode + backoffPolicy;
753         hashCode = 31 * hashCode + priority;
754         hashCode = 31 * hashCode + flags;
755         return hashCode;
756     }
757 
JobInfo(Parcel in)758     private JobInfo(Parcel in) {
759         jobId = in.readInt();
760         extras = in.readPersistableBundle();
761         transientExtras = in.readBundle();
762         if (in.readInt() != 0) {
763             clipData = ClipData.CREATOR.createFromParcel(in);
764             clipGrantFlags = in.readInt();
765         } else {
766             clipData = null;
767             clipGrantFlags = 0;
768         }
769         service = in.readParcelable(null);
770         constraintFlags = in.readInt();
771         triggerContentUris = in.createTypedArray(TriggerContentUri.CREATOR);
772         triggerContentUpdateDelay = in.readLong();
773         triggerContentMaxDelay = in.readLong();
774         if (in.readInt() != 0) {
775             networkRequest = NetworkRequest.CREATOR.createFromParcel(in);
776         } else {
777             networkRequest = null;
778         }
779         networkDownloadBytes = in.readLong();
780         networkUploadBytes = in.readLong();
781         minLatencyMillis = in.readLong();
782         maxExecutionDelayMillis = in.readLong();
783         isPeriodic = in.readInt() == 1;
784         isPersisted = in.readInt() == 1;
785         intervalMillis = in.readLong();
786         flexMillis = in.readLong();
787         initialBackoffMillis = in.readLong();
788         backoffPolicy = in.readInt();
789         hasEarlyConstraint = in.readInt() == 1;
790         hasLateConstraint = in.readInt() == 1;
791         priority = in.readInt();
792         flags = in.readInt();
793     }
794 
JobInfo(JobInfo.Builder b)795     private JobInfo(JobInfo.Builder b) {
796         jobId = b.mJobId;
797         extras = b.mExtras.deepCopy();
798         transientExtras = b.mTransientExtras.deepCopy();
799         clipData = b.mClipData;
800         clipGrantFlags = b.mClipGrantFlags;
801         service = b.mJobService;
802         constraintFlags = b.mConstraintFlags;
803         triggerContentUris = b.mTriggerContentUris != null
804                 ? b.mTriggerContentUris.toArray(new TriggerContentUri[b.mTriggerContentUris.size()])
805                 : null;
806         triggerContentUpdateDelay = b.mTriggerContentUpdateDelay;
807         triggerContentMaxDelay = b.mTriggerContentMaxDelay;
808         networkRequest = b.mNetworkRequest;
809         networkDownloadBytes = b.mNetworkDownloadBytes;
810         networkUploadBytes = b.mNetworkUploadBytes;
811         minLatencyMillis = b.mMinLatencyMillis;
812         maxExecutionDelayMillis = b.mMaxExecutionDelayMillis;
813         isPeriodic = b.mIsPeriodic;
814         isPersisted = b.mIsPersisted;
815         intervalMillis = b.mIntervalMillis;
816         flexMillis = b.mFlexMillis;
817         initialBackoffMillis = b.mInitialBackoffMillis;
818         backoffPolicy = b.mBackoffPolicy;
819         hasEarlyConstraint = b.mHasEarlyConstraint;
820         hasLateConstraint = b.mHasLateConstraint;
821         priority = b.mPriority;
822         flags = b.mFlags;
823     }
824 
825     @Override
describeContents()826     public int describeContents() {
827         return 0;
828     }
829 
830     @Override
writeToParcel(Parcel out, int flags)831     public void writeToParcel(Parcel out, int flags) {
832         out.writeInt(jobId);
833         out.writePersistableBundle(extras);
834         out.writeBundle(transientExtras);
835         if (clipData != null) {
836             out.writeInt(1);
837             clipData.writeToParcel(out, flags);
838             out.writeInt(clipGrantFlags);
839         } else {
840             out.writeInt(0);
841         }
842         out.writeParcelable(service, flags);
843         out.writeInt(constraintFlags);
844         out.writeTypedArray(triggerContentUris, flags);
845         out.writeLong(triggerContentUpdateDelay);
846         out.writeLong(triggerContentMaxDelay);
847         if (networkRequest != null) {
848             out.writeInt(1);
849             networkRequest.writeToParcel(out, flags);
850         } else {
851             out.writeInt(0);
852         }
853         out.writeLong(networkDownloadBytes);
854         out.writeLong(networkUploadBytes);
855         out.writeLong(minLatencyMillis);
856         out.writeLong(maxExecutionDelayMillis);
857         out.writeInt(isPeriodic ? 1 : 0);
858         out.writeInt(isPersisted ? 1 : 0);
859         out.writeLong(intervalMillis);
860         out.writeLong(flexMillis);
861         out.writeLong(initialBackoffMillis);
862         out.writeInt(backoffPolicy);
863         out.writeInt(hasEarlyConstraint ? 1 : 0);
864         out.writeInt(hasLateConstraint ? 1 : 0);
865         out.writeInt(priority);
866         out.writeInt(this.flags);
867     }
868 
869     public static final @android.annotation.NonNull Creator<JobInfo> CREATOR = new Creator<JobInfo>() {
870         @Override
871         public JobInfo createFromParcel(Parcel in) {
872             return new JobInfo(in);
873         }
874 
875         @Override
876         public JobInfo[] newArray(int size) {
877             return new JobInfo[size];
878         }
879     };
880 
881     @Override
toString()882     public String toString() {
883         return "(job:" + jobId + "/" + service.flattenToShortString() + ")";
884     }
885 
886     /**
887      * Information about a content URI modification that a job would like to
888      * trigger on.
889      */
890     public static final class TriggerContentUri implements Parcelable {
891         private final Uri mUri;
892         private final int mFlags;
893 
894         /** @hide */
895         @Retention(RetentionPolicy.SOURCE)
896         @IntDef(flag = true, prefix = { "FLAG_" }, value = {
897                 FLAG_NOTIFY_FOR_DESCENDANTS,
898         })
899         public @interface Flags { }
900 
901         /**
902          * Flag for trigger: also trigger if any descendants of the given URI change.
903          * Corresponds to the <var>notifyForDescendants</var> of
904          * {@link android.content.ContentResolver#registerContentObserver}.
905          */
906         public static final int FLAG_NOTIFY_FOR_DESCENDANTS = 1<<0;
907 
908         /**
909          * Create a new trigger description.
910          * @param uri The URI to observe.  Must be non-null.
911          * @param flags Flags for the observer.
912          */
TriggerContentUri(@onNull Uri uri, @Flags int flags)913         public TriggerContentUri(@NonNull Uri uri, @Flags int flags) {
914             mUri = uri;
915             mFlags = flags;
916         }
917 
918         /**
919          * Return the Uri this trigger was created for.
920          */
getUri()921         public Uri getUri() {
922             return mUri;
923         }
924 
925         /**
926          * Return the flags supplied for the trigger.
927          */
getFlags()928         public @Flags int getFlags() {
929             return mFlags;
930         }
931 
932         @Override
equals(Object o)933         public boolean equals(Object o) {
934             if (!(o instanceof TriggerContentUri)) {
935                 return false;
936             }
937             TriggerContentUri t = (TriggerContentUri) o;
938             return Objects.equals(t.mUri, mUri) && t.mFlags == mFlags;
939         }
940 
941         @Override
hashCode()942         public int hashCode() {
943             return (mUri == null ? 0 : mUri.hashCode()) ^ mFlags;
944         }
945 
TriggerContentUri(Parcel in)946         private TriggerContentUri(Parcel in) {
947             mUri = Uri.CREATOR.createFromParcel(in);
948             mFlags = in.readInt();
949         }
950 
951         @Override
describeContents()952         public int describeContents() {
953             return 0;
954         }
955 
956         @Override
writeToParcel(Parcel out, int flags)957         public void writeToParcel(Parcel out, int flags) {
958             mUri.writeToParcel(out, flags);
959             out.writeInt(mFlags);
960         }
961 
962         public static final @android.annotation.NonNull Creator<TriggerContentUri> CREATOR = new Creator<TriggerContentUri>() {
963             @Override
964             public TriggerContentUri createFromParcel(Parcel in) {
965                 return new TriggerContentUri(in);
966             }
967 
968             @Override
969             public TriggerContentUri[] newArray(int size) {
970                 return new TriggerContentUri[size];
971             }
972         };
973     }
974 
975     /** Builder class for constructing {@link JobInfo} objects. */
976     public static final class Builder {
977         private final int mJobId;
978         private final ComponentName mJobService;
979         private PersistableBundle mExtras = PersistableBundle.EMPTY;
980         private Bundle mTransientExtras = Bundle.EMPTY;
981         private ClipData mClipData;
982         private int mClipGrantFlags;
983         private int mPriority = PRIORITY_DEFAULT;
984         private int mFlags;
985         // Requirements.
986         private int mConstraintFlags;
987         private NetworkRequest mNetworkRequest;
988         private long mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN;
989         private long mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN;
990         private ArrayList<TriggerContentUri> mTriggerContentUris;
991         private long mTriggerContentUpdateDelay = -1;
992         private long mTriggerContentMaxDelay = -1;
993         private boolean mIsPersisted;
994         // One-off parameters.
995         private long mMinLatencyMillis;
996         private long mMaxExecutionDelayMillis;
997         // Periodic parameters.
998         private boolean mIsPeriodic;
999         private boolean mHasEarlyConstraint;
1000         private boolean mHasLateConstraint;
1001         private long mIntervalMillis;
1002         private long mFlexMillis;
1003         // Back-off parameters.
1004         private long mInitialBackoffMillis = DEFAULT_INITIAL_BACKOFF_MILLIS;
1005         private int mBackoffPolicy = DEFAULT_BACKOFF_POLICY;
1006         /** Easy way to track whether the client has tried to set a back-off policy. */
1007         private boolean mBackoffPolicySet = false;
1008 
1009         /**
1010          * Initialize a new Builder to construct a {@link JobInfo}.
1011          *
1012          * @param jobId Application-provided id for this job. Subsequent calls to cancel, or
1013          * jobs created with the same jobId, will update the pre-existing job with
1014          * the same id.  This ID must be unique across all clients of the same uid
1015          * (not just the same package).  You will want to make sure this is a stable
1016          * id across app updates, so probably not based on a resource ID.
1017          * @param jobService The endpoint that you implement that will receive the callback from the
1018          * JobScheduler.
1019          */
Builder(int jobId, @NonNull ComponentName jobService)1020         public Builder(int jobId, @NonNull ComponentName jobService) {
1021             mJobService = jobService;
1022             mJobId = jobId;
1023         }
1024 
1025         /** @hide */
1026         @UnsupportedAppUsage
setPriority(int priority)1027         public Builder setPriority(int priority) {
1028             mPriority = priority;
1029             return this;
1030         }
1031 
1032         /** @hide */
1033         @UnsupportedAppUsage
setFlags(int flags)1034         public Builder setFlags(int flags) {
1035             mFlags = flags;
1036             return this;
1037         }
1038 
1039         /**
1040          * Set optional extras. This is persisted, so we only allow primitive types.
1041          * @param extras Bundle containing extras you want the scheduler to hold on to for you.
1042          * @see JobInfo#getExtras()
1043          */
setExtras(@onNull PersistableBundle extras)1044         public Builder setExtras(@NonNull PersistableBundle extras) {
1045             mExtras = extras;
1046             return this;
1047         }
1048 
1049         /**
1050          * Set optional transient extras.
1051          *
1052          * <p>Because setting this property is not compatible with persisted
1053          * jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when
1054          * {@link android.app.job.JobInfo.Builder#build()} is called.</p>
1055          *
1056          * @param extras Bundle containing extras you want the scheduler to hold on to for you.
1057          * @see JobInfo#getTransientExtras()
1058          */
setTransientExtras(@onNull Bundle extras)1059         public Builder setTransientExtras(@NonNull Bundle extras) {
1060             mTransientExtras = extras;
1061             return this;
1062         }
1063 
1064         /**
1065          * Set a {@link ClipData} associated with this Job.
1066          *
1067          * <p>The main purpose of providing a ClipData is to allow granting of
1068          * URI permissions for data associated with the clip.  The exact kind
1069          * of permission grant to perform is specified through <var>grantFlags</var>.
1070          *
1071          * <p>If the ClipData contains items that are Intents, any
1072          * grant flags in those Intents will be ignored.  Only flags provided as an argument
1073          * to this method are respected, and will be applied to all Uri or
1074          * Intent items in the clip (or sub-items of the clip).
1075          *
1076          * <p>Because setting this property is not compatible with persisted
1077          * jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when
1078          * {@link android.app.job.JobInfo.Builder#build()} is called.</p>
1079          *
1080          * @param clip The new clip to set.  May be null to clear the current clip.
1081          * @param grantFlags The desired permissions to grant for any URIs.  This should be
1082          * a combination of {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION},
1083          * {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, and
1084          * {@link android.content.Intent#FLAG_GRANT_PREFIX_URI_PERMISSION}.
1085          * @see JobInfo#getClipData()
1086          * @see JobInfo#getClipGrantFlags()
1087          */
setClipData(@ullable ClipData clip, int grantFlags)1088         public Builder setClipData(@Nullable ClipData clip, int grantFlags) {
1089             mClipData = clip;
1090             mClipGrantFlags = grantFlags;
1091             return this;
1092         }
1093 
1094         /**
1095          * Set basic description of the kind of network your job requires. If
1096          * you need more precise control over network capabilities, see
1097          * {@link #setRequiredNetwork(NetworkRequest)}.
1098          * <p>
1099          * If your job doesn't need a network connection, you don't need to call
1100          * this method, as the default value is {@link #NETWORK_TYPE_NONE}.
1101          * <p>
1102          * Calling this method defines network as a strict requirement for your
1103          * job. If the network requested is not available your job will never
1104          * run. See {@link #setOverrideDeadline(long)} to change this behavior.
1105          * Calling this method will override any requirements previously defined
1106          * by {@link #setRequiredNetwork(NetworkRequest)}; you typically only
1107          * want to call one of these methods.
1108          * <p class="note">
1109          * When your job executes in
1110          * {@link JobService#onStartJob(JobParameters)}, be sure to use the
1111          * specific network returned by {@link JobParameters#getNetwork()},
1112          * otherwise you'll use the default network which may not meet this
1113          * constraint.
1114          *
1115          * @see #setRequiredNetwork(NetworkRequest)
1116          * @see JobInfo#getNetworkType()
1117          * @see JobParameters#getNetwork()
1118          */
setRequiredNetworkType(@etworkType int networkType)1119         public Builder setRequiredNetworkType(@NetworkType int networkType) {
1120             if (networkType == NETWORK_TYPE_NONE) {
1121                 return setRequiredNetwork(null);
1122             } else {
1123                 final NetworkRequest.Builder builder = new NetworkRequest.Builder();
1124 
1125                 // All types require validated Internet
1126                 builder.addCapability(NET_CAPABILITY_INTERNET);
1127                 builder.addCapability(NET_CAPABILITY_VALIDATED);
1128                 builder.removeCapability(NET_CAPABILITY_NOT_VPN);
1129 
1130                 if (networkType == NETWORK_TYPE_ANY) {
1131                     // No other capabilities
1132                 } else if (networkType == NETWORK_TYPE_UNMETERED) {
1133                     builder.addCapability(NET_CAPABILITY_NOT_METERED);
1134                 } else if (networkType == NETWORK_TYPE_NOT_ROAMING) {
1135                     builder.addCapability(NET_CAPABILITY_NOT_ROAMING);
1136                 } else if (networkType == NETWORK_TYPE_CELLULAR) {
1137                     builder.addTransportType(TRANSPORT_CELLULAR);
1138                 }
1139 
1140                 return setRequiredNetwork(builder.build());
1141             }
1142         }
1143 
1144         /**
1145          * Set detailed description of the kind of network your job requires.
1146          * <p>
1147          * If your job doesn't need a network connection, you don't need to call
1148          * this method, as the default is {@code null}.
1149          * <p>
1150          * Calling this method defines network as a strict requirement for your
1151          * job. If the network requested is not available your job will never
1152          * run. See {@link #setOverrideDeadline(long)} to change this behavior.
1153          * Calling this method will override any requirements previously defined
1154          * by {@link #setRequiredNetworkType(int)}; you typically only want to
1155          * call one of these methods.
1156          * <p class="note">
1157          * When your job executes in
1158          * {@link JobService#onStartJob(JobParameters)}, be sure to use the
1159          * specific network returned by {@link JobParameters#getNetwork()},
1160          * otherwise you'll use the default network which may not meet this
1161          * constraint.
1162          *
1163          * @param networkRequest The detailed description of the kind of network
1164          *            this job requires, or {@code null} if no specific kind of
1165          *            network is required. Defining a {@link NetworkSpecifier}
1166          *            is only supported for jobs that aren't persisted.
1167          * @see #setRequiredNetworkType(int)
1168          * @see JobInfo#getRequiredNetwork()
1169          * @see JobParameters#getNetwork()
1170          */
setRequiredNetwork(@ullable NetworkRequest networkRequest)1171         public Builder setRequiredNetwork(@Nullable NetworkRequest networkRequest) {
1172             mNetworkRequest = networkRequest;
1173             return this;
1174         }
1175 
1176         /**
1177          * @deprecated replaced by
1178          *             {@link #setEstimatedNetworkBytes(long, long)}.
1179          * @removed
1180          */
1181         @Deprecated
setEstimatedNetworkBytes(@ytesLong long networkBytes)1182         public Builder setEstimatedNetworkBytes(@BytesLong long networkBytes) {
1183             return setEstimatedNetworkBytes(networkBytes, NETWORK_BYTES_UNKNOWN);
1184         }
1185 
1186         /**
1187          * Set the estimated size of network traffic that will be performed by
1188          * this job, in bytes.
1189          * <p>
1190          * Apps are encouraged to provide values that are as accurate as
1191          * possible, but when the exact size isn't available, an
1192          * order-of-magnitude estimate can be provided instead. Here are some
1193          * specific examples:
1194          * <ul>
1195          * <li>A job that is backing up a photo knows the exact size of that
1196          * photo, so it should provide that size as the estimate.
1197          * <li>A job that refreshes top news stories wouldn't know an exact
1198          * size, but if the size is expected to be consistently around 100KB, it
1199          * can provide that order-of-magnitude value as the estimate.
1200          * <li>A job that synchronizes email could end up using an extreme range
1201          * of data, from under 1KB when nothing has changed, to dozens of MB
1202          * when there are new emails with attachments. Jobs that cannot provide
1203          * reasonable estimates should use the sentinel value
1204          * {@link JobInfo#NETWORK_BYTES_UNKNOWN}.
1205          * </ul>
1206          * Note that the system may choose to delay jobs with large network
1207          * usage estimates when the device has a poor network connection, in
1208          * order to save battery.
1209          * <p>
1210          * The values provided here only reflect the traffic that will be
1211          * performed by the base job; if you're using {@link JobWorkItem} then
1212          * you also need to define the network traffic used by each work item
1213          * when constructing them.
1214          *
1215          * @param downloadBytes The estimated size of network traffic that will
1216          *            be downloaded by this job, in bytes.
1217          * @param uploadBytes The estimated size of network traffic that will be
1218          *            uploaded by this job, in bytes.
1219          * @see JobInfo#getEstimatedNetworkDownloadBytes()
1220          * @see JobInfo#getEstimatedNetworkUploadBytes()
1221          * @see JobWorkItem#JobWorkItem(android.content.Intent, long, long)
1222          */
setEstimatedNetworkBytes(@ytesLong long downloadBytes, @BytesLong long uploadBytes)1223         public Builder setEstimatedNetworkBytes(@BytesLong long downloadBytes,
1224                 @BytesLong long uploadBytes) {
1225             mNetworkDownloadBytes = downloadBytes;
1226             mNetworkUploadBytes = uploadBytes;
1227             return this;
1228         }
1229 
1230         /**
1231          * Specify that to run this job, the device must be charging (or be a
1232          * non-battery-powered device connected to permanent power, such as Android TV
1233          * devices). This defaults to {@code false}.
1234          *
1235          * <p class="note">For purposes of running jobs, a battery-powered device
1236          * "charging" is not quite the same as simply being connected to power.  If the
1237          * device is so busy that the battery is draining despite a power connection, jobs
1238          * with this constraint will <em>not</em> run.  This can happen during some
1239          * common use cases such as video chat, particularly if the device is plugged in
1240          * to USB rather than to wall power.
1241          *
1242          * @param requiresCharging Pass {@code true} to require that the device be
1243          *     charging in order to run the job.
1244          * @see JobInfo#isRequireCharging()
1245          */
setRequiresCharging(boolean requiresCharging)1246         public Builder setRequiresCharging(boolean requiresCharging) {
1247             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_CHARGING)
1248                     | (requiresCharging ? CONSTRAINT_FLAG_CHARGING : 0);
1249             return this;
1250         }
1251 
1252         /**
1253          * Specify that to run this job, the device's battery level must not be low.
1254          * This defaults to false.  If true, the job will only run when the battery level
1255          * is not low, which is generally the point where the user is given a "low battery"
1256          * warning.
1257          * @param batteryNotLow Whether or not the device's battery level must not be low.
1258          * @see JobInfo#isRequireBatteryNotLow()
1259          */
setRequiresBatteryNotLow(boolean batteryNotLow)1260         public Builder setRequiresBatteryNotLow(boolean batteryNotLow) {
1261             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_BATTERY_NOT_LOW)
1262                     | (batteryNotLow ? CONSTRAINT_FLAG_BATTERY_NOT_LOW : 0);
1263             return this;
1264         }
1265 
1266         /**
1267          * When set {@code true}, ensure that this job will not run if the device is in active use.
1268          * The default state is {@code false}: that is, the for the job to be runnable even when
1269          * someone is interacting with the device.
1270          *
1271          * <p>This state is a loose definition provided by the system. In general, it means that
1272          * the device is not currently being used interactively, and has not been in use for some
1273          * time. As such, it is a good time to perform resource heavy jobs. Bear in mind that
1274          * battery usage will still be attributed to your application, and surfaced to the user in
1275          * battery stats.</p>
1276          *
1277          * <p class="note">Despite the similar naming, this job constraint is <em>not</em>
1278          * related to the system's "device idle" or "doze" states.  This constraint only
1279          * determines whether a job is allowed to run while the device is directly in use.
1280          *
1281          * @param requiresDeviceIdle Pass {@code true} to prevent the job from running
1282          *     while the device is being used interactively.
1283          * @see JobInfo#isRequireDeviceIdle()
1284          */
setRequiresDeviceIdle(boolean requiresDeviceIdle)1285         public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) {
1286             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_DEVICE_IDLE)
1287                     | (requiresDeviceIdle ? CONSTRAINT_FLAG_DEVICE_IDLE : 0);
1288             return this;
1289         }
1290 
1291         /**
1292          * Specify that to run this job, the device's available storage must not be low.
1293          * This defaults to false.  If true, the job will only run when the device is not
1294          * in a low storage state, which is generally the point where the user is given a
1295          * "low storage" warning.
1296          * @param storageNotLow Whether or not the device's available storage must not be low.
1297          * @see JobInfo#isRequireStorageNotLow()
1298          */
setRequiresStorageNotLow(boolean storageNotLow)1299         public Builder setRequiresStorageNotLow(boolean storageNotLow) {
1300             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_STORAGE_NOT_LOW)
1301                     | (storageNotLow ? CONSTRAINT_FLAG_STORAGE_NOT_LOW : 0);
1302             return this;
1303         }
1304 
1305         /**
1306          * Add a new content: URI that will be monitored with a
1307          * {@link android.database.ContentObserver}, and will cause the job to execute if changed.
1308          * If you have any trigger content URIs associated with a job, it will not execute until
1309          * there has been a change report for one or more of them.
1310          *
1311          * <p>Note that trigger URIs can not be used in combination with
1312          * {@link #setPeriodic(long)} or {@link #setPersisted(boolean)}.  To continually monitor
1313          * for content changes, you need to schedule a new JobInfo observing the same URIs
1314          * before you finish execution of the JobService handling the most recent changes.
1315          * Following this pattern will ensure you do not lost any content changes: while your
1316          * job is running, the system will continue monitoring for content changes, and propagate
1317          * any it sees over to the next job you schedule.</p>
1318          *
1319          * <p>Because setting this property is not compatible with periodic or
1320          * persisted jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when
1321          * {@link android.app.job.JobInfo.Builder#build()} is called.</p>
1322          *
1323          * <p>The following example shows how this feature can be used to monitor for changes
1324          * in the photos on a device.</p>
1325          *
1326          * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/PhotosContentJob.java
1327          *      job}
1328          *
1329          * @param uri The content: URI to monitor.
1330          * @see JobInfo#getTriggerContentUris()
1331          */
addTriggerContentUri(@onNull TriggerContentUri uri)1332         public Builder addTriggerContentUri(@NonNull TriggerContentUri uri) {
1333             if (mTriggerContentUris == null) {
1334                 mTriggerContentUris = new ArrayList<>();
1335             }
1336             mTriggerContentUris.add(uri);
1337             return this;
1338         }
1339 
1340         /**
1341          * Set the delay (in milliseconds) from when a content change is detected until
1342          * the job is scheduled.  If there are more changes during that time, the delay
1343          * will be reset to start at the time of the most recent change.
1344          * @param durationMs Delay after most recent content change, in milliseconds.
1345          * @see JobInfo#getTriggerContentUpdateDelay()
1346          */
setTriggerContentUpdateDelay(long durationMs)1347         public Builder setTriggerContentUpdateDelay(long durationMs) {
1348             mTriggerContentUpdateDelay = durationMs;
1349             return this;
1350         }
1351 
1352         /**
1353          * Set the maximum total delay (in milliseconds) that is allowed from the first
1354          * time a content change is detected until the job is scheduled.
1355          * @param durationMs Delay after initial content change, in milliseconds.
1356          * @see JobInfo#getTriggerContentMaxDelay()
1357          */
setTriggerContentMaxDelay(long durationMs)1358         public Builder setTriggerContentMaxDelay(long durationMs) {
1359             mTriggerContentMaxDelay = durationMs;
1360             return this;
1361         }
1362 
1363         /**
1364          * Specify that this job should recur with the provided interval, not more than once per
1365          * period. You have no control over when within this interval this job will be executed,
1366          * only the guarantee that it will be executed at most once within this interval.
1367          * Setting this function on the builder with {@link #setMinimumLatency(long)} or
1368          * {@link #setOverrideDeadline(long)} will result in an error.
1369          * @param intervalMillis Millisecond interval for which this job will repeat.
1370          * @see JobInfo#getIntervalMillis()
1371          * @see JobInfo#getFlexMillis()
1372          */
setPeriodic(long intervalMillis)1373         public Builder setPeriodic(long intervalMillis) {
1374             return setPeriodic(intervalMillis, intervalMillis);
1375         }
1376 
1377         /**
1378          * Specify that this job should recur with the provided interval and flex. The job can
1379          * execute at any time in a window of flex length at the end of the period.
1380          * @param intervalMillis Millisecond interval for which this job will repeat. A minimum
1381          *                       value of {@link #getMinPeriodMillis()} is enforced.
1382          * @param flexMillis Millisecond flex for this job. Flex is clamped to be at least
1383          *                   {@link #getMinFlexMillis()} or 5 percent of the period, whichever is
1384          *                   higher.
1385          * @see JobInfo#getIntervalMillis()
1386          * @see JobInfo#getFlexMillis()
1387          */
setPeriodic(long intervalMillis, long flexMillis)1388         public Builder setPeriodic(long intervalMillis, long flexMillis) {
1389             final long minPeriod = getMinPeriodMillis();
1390             if (intervalMillis < minPeriod) {
1391                 Log.w(TAG, "Requested interval " + formatDuration(intervalMillis) + " for job "
1392                         + mJobId + " is too small; raising to " + formatDuration(minPeriod));
1393                 intervalMillis = minPeriod;
1394             }
1395 
1396             final long percentClamp = 5 * intervalMillis / 100;
1397             final long minFlex = Math.max(percentClamp, getMinFlexMillis());
1398             if (flexMillis < minFlex) {
1399                 Log.w(TAG, "Requested flex " + formatDuration(flexMillis) + " for job " + mJobId
1400                         + " is too small; raising to " + formatDuration(minFlex));
1401                 flexMillis = minFlex;
1402             }
1403 
1404             mIsPeriodic = true;
1405             mIntervalMillis = intervalMillis;
1406             mFlexMillis = flexMillis;
1407             mHasEarlyConstraint = mHasLateConstraint = true;
1408             return this;
1409         }
1410 
1411         /**
1412          * Specify that this job should be delayed by the provided amount of time.
1413          * Because it doesn't make sense setting this property on a periodic job, doing so will
1414          * throw an {@link java.lang.IllegalArgumentException} when
1415          * {@link android.app.job.JobInfo.Builder#build()} is called.
1416          * @param minLatencyMillis Milliseconds before which this job will not be considered for
1417          *                         execution.
1418          * @see JobInfo#getMinLatencyMillis()
1419          */
setMinimumLatency(long minLatencyMillis)1420         public Builder setMinimumLatency(long minLatencyMillis) {
1421             mMinLatencyMillis = minLatencyMillis;
1422             mHasEarlyConstraint = true;
1423             return this;
1424         }
1425 
1426         /**
1427          * Set deadline which is the maximum scheduling latency. The job will be run by this
1428          * deadline even if other requirements are not met. Because it doesn't make sense setting
1429          * this property on a periodic job, doing so will throw an
1430          * {@link java.lang.IllegalArgumentException} when
1431          * {@link android.app.job.JobInfo.Builder#build()} is called.
1432          * @see JobInfo#getMaxExecutionDelayMillis()
1433          */
setOverrideDeadline(long maxExecutionDelayMillis)1434         public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
1435             mMaxExecutionDelayMillis = maxExecutionDelayMillis;
1436             mHasLateConstraint = true;
1437             return this;
1438         }
1439 
1440         /**
1441          * Set up the back-off/retry policy.
1442          * This defaults to some respectable values: {30 seconds, Exponential}. We cap back-off at
1443          * 5hrs.
1444          * Note that trying to set a backoff criteria for a job with
1445          * {@link #setRequiresDeviceIdle(boolean)} will throw an exception when you call build().
1446          * This is because back-off typically does not make sense for these types of jobs. See
1447          * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)}
1448          * for more description of the return value for the case of a job executing while in idle
1449          * mode.
1450          * @param initialBackoffMillis Millisecond time interval to wait initially when job has
1451          *                             failed.
1452          * @see JobInfo#getInitialBackoffMillis()
1453          * @see JobInfo#getBackoffPolicy()
1454          */
setBackoffCriteria(long initialBackoffMillis, @BackoffPolicy int backoffPolicy)1455         public Builder setBackoffCriteria(long initialBackoffMillis,
1456                 @BackoffPolicy int backoffPolicy) {
1457             final long minBackoff = getMinBackoffMillis();
1458             if (initialBackoffMillis < minBackoff) {
1459                 Log.w(TAG, "Requested backoff " + formatDuration(initialBackoffMillis) + " for job "
1460                         + mJobId + " is too small; raising to " + formatDuration(minBackoff));
1461                 initialBackoffMillis = minBackoff;
1462             }
1463 
1464             mBackoffPolicySet = true;
1465             mInitialBackoffMillis = initialBackoffMillis;
1466             mBackoffPolicy = backoffPolicy;
1467             return this;
1468         }
1469 
1470         /**
1471          * Setting this to true indicates that this job is important while the scheduling app
1472          * is in the foreground or on the temporary whitelist for background restrictions.
1473          * This means that the system will relax doze restrictions on this job during this time.
1474          *
1475          * Apps should use this flag only for short jobs that are essential for the app to function
1476          * properly in the foreground.
1477          *
1478          * Note that once the scheduling app is no longer whitelisted from background restrictions
1479          * and in the background, or the job failed due to unsatisfied constraints,
1480          * this job should be expected to behave like other jobs without this flag.
1481          *
1482          * @param importantWhileForeground whether to relax doze restrictions for this job when the
1483          *                                 app is in the foreground. False by default.
1484          * @see JobInfo#isImportantWhileForeground()
1485          */
setImportantWhileForeground(boolean importantWhileForeground)1486         public Builder setImportantWhileForeground(boolean importantWhileForeground) {
1487             if (importantWhileForeground) {
1488                 mFlags |= FLAG_IMPORTANT_WHILE_FOREGROUND;
1489             } else {
1490                 mFlags &= (~FLAG_IMPORTANT_WHILE_FOREGROUND);
1491             }
1492             return this;
1493         }
1494 
1495         /**
1496          * @removed
1497          * @deprecated replaced with {@link #setPrefetch(boolean)}
1498          */
1499         @Deprecated
setIsPrefetch(boolean isPrefetch)1500         public Builder setIsPrefetch(boolean isPrefetch) {
1501             return setPrefetch(isPrefetch);
1502         }
1503 
1504         /**
1505          * Setting this to true indicates that this job is designed to prefetch
1506          * content that will make a material improvement to the experience of
1507          * the specific user of this device. For example, fetching top headlines
1508          * of interest to the current user.
1509          * <p>
1510          * The system may use this signal to relax the network constraints you
1511          * originally requested, such as allowing a
1512          * {@link JobInfo#NETWORK_TYPE_UNMETERED} job to run over a metered
1513          * network when there is a surplus of metered data available. The system
1514          * may also use this signal in combination with end user usage patterns
1515          * to ensure data is prefetched before the user launches your app.
1516          * @see JobInfo#isPrefetch()
1517          */
setPrefetch(boolean prefetch)1518         public Builder setPrefetch(boolean prefetch) {
1519             if (prefetch) {
1520                 mFlags |= FLAG_PREFETCH;
1521             } else {
1522                 mFlags &= (~FLAG_PREFETCH);
1523             }
1524             return this;
1525         }
1526 
1527         /**
1528          * Set whether or not to persist this job across device reboots.
1529          *
1530          * @param isPersisted True to indicate that the job will be written to
1531          *            disk and loaded at boot.
1532          * @see JobInfo#isPersisted()
1533          */
1534         @RequiresPermission(android.Manifest.permission.RECEIVE_BOOT_COMPLETED)
setPersisted(boolean isPersisted)1535         public Builder setPersisted(boolean isPersisted) {
1536             mIsPersisted = isPersisted;
1537             return this;
1538         }
1539 
1540         /**
1541          * @return The job object to hand to the JobScheduler. This object is immutable.
1542          */
build()1543         public JobInfo build() {
1544             // Check that network estimates require network type
1545             if ((mNetworkDownloadBytes > 0 || mNetworkUploadBytes > 0) && mNetworkRequest == null) {
1546                 throw new IllegalArgumentException(
1547                         "Can't provide estimated network usage without requiring a network");
1548             }
1549             // We can't serialize network specifiers
1550             if (mIsPersisted && mNetworkRequest != null
1551                     && mNetworkRequest.networkCapabilities.getNetworkSpecifier() != null) {
1552                 throw new IllegalArgumentException(
1553                         "Network specifiers aren't supported for persistent jobs");
1554             }
1555             // Check that a deadline was not set on a periodic job.
1556             if (mIsPeriodic) {
1557                 if (mMaxExecutionDelayMillis != 0L) {
1558                     throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " +
1559                             "periodic job.");
1560                 }
1561                 if (mMinLatencyMillis != 0L) {
1562                     throw new IllegalArgumentException("Can't call setMinimumLatency() on a " +
1563                             "periodic job");
1564                 }
1565                 if (mTriggerContentUris != null) {
1566                     throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " +
1567                             "periodic job");
1568                 }
1569             }
1570             if (mIsPersisted) {
1571                 if (mTriggerContentUris != null) {
1572                     throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " +
1573                             "persisted job");
1574                 }
1575                 if (!mTransientExtras.isEmpty()) {
1576                     throw new IllegalArgumentException("Can't call setTransientExtras() on a " +
1577                             "persisted job");
1578                 }
1579                 if (mClipData != null) {
1580                     throw new IllegalArgumentException("Can't call setClipData() on a " +
1581                             "persisted job");
1582                 }
1583             }
1584             if ((mFlags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0 && mHasEarlyConstraint) {
1585                 throw new IllegalArgumentException("An important while foreground job cannot "
1586                         + "have a time delay");
1587             }
1588             if (mBackoffPolicySet && (mConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) {
1589                 throw new IllegalArgumentException("An idle mode job will not respect any" +
1590                         " back-off policy, so calling setBackoffCriteria with" +
1591                         " setRequiresDeviceIdle is an error.");
1592             }
1593             return new JobInfo(this);
1594         }
1595 
1596         /**
1597          * @hide
1598          */
summarize()1599         public String summarize() {
1600             final String service = (mJobService != null)
1601                     ? mJobService.flattenToShortString()
1602                     : "null";
1603             return "JobInfo.Builder{job:" + mJobId + "/" + service + "}";
1604         }
1605     }
1606 
1607     /**
1608      * Convert a priority integer into a human readable string for debugging.
1609      * @hide
1610      */
getPriorityString(int priority)1611     public static String getPriorityString(int priority) {
1612         switch (priority) {
1613             case PRIORITY_DEFAULT:
1614                 return PRIORITY_DEFAULT + " [DEFAULT]";
1615             case PRIORITY_SYNC_EXPEDITED:
1616                 return PRIORITY_SYNC_EXPEDITED + " [SYNC_EXPEDITED]";
1617             case PRIORITY_SYNC_INITIALIZATION:
1618                 return PRIORITY_SYNC_INITIALIZATION + " [SYNC_INITIALIZATION]";
1619             case PRIORITY_BOUND_FOREGROUND_SERVICE:
1620                 return PRIORITY_BOUND_FOREGROUND_SERVICE + " [BFGS_APP]";
1621             case PRIORITY_FOREGROUND_SERVICE:
1622                 return PRIORITY_FOREGROUND_SERVICE + " [FGS_APP]";
1623             case PRIORITY_TOP_APP:
1624                 return PRIORITY_TOP_APP + " [TOP_APP]";
1625 
1626                 // PRIORITY_ADJ_* are adjustments and not used as real priorities.
1627                 // No need to convert to strings.
1628         }
1629         return priority + " [UNKNOWN]";
1630     }
1631 }
1632