• 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 com.android.server.job.controllers;
18 
19 import android.app.AppGlobals;
20 import android.app.job.JobInfo;
21 import android.content.ComponentName;
22 import android.net.Uri;
23 import android.os.PersistableBundle;
24 import android.os.RemoteException;
25 import android.os.SystemClock;
26 import android.os.UserHandle;
27 import android.text.format.DateUtils;
28 import android.util.ArraySet;
29 import android.util.TimeUtils;
30 
31 import java.io.PrintWriter;
32 
33 /**
34  * Uniquely identifies a job internally.
35  * Created from the public {@link android.app.job.JobInfo} object when it lands on the scheduler.
36  * Contains current state of the requirements of the job, as well as a function to evaluate
37  * whether it's ready to run.
38  * This object is shared among the various controllers - hence why the different fields are atomic.
39  * This isn't strictly necessary because each controller is only interested in a specific field,
40  * and the receivers that are listening for global state change will all run on the main looper,
41  * but we don't enforce that so this is safer.
42  * @hide
43  */
44 public final class JobStatus {
45     public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE;
46     public static final long NO_EARLIEST_RUNTIME = 0L;
47 
48     static final int CONSTRAINT_CHARGING = 1<<0;
49     static final int CONSTRAINT_TIMING_DELAY = 1<<1;
50     static final int CONSTRAINT_DEADLINE = 1<<2;
51     static final int CONSTRAINT_IDLE = 1<<3;
52     static final int CONSTRAINT_UNMETERED = 1<<4;
53     static final int CONSTRAINT_CONNECTIVITY = 1<<5;
54     static final int CONSTRAINT_APP_NOT_IDLE = 1<<6;
55     static final int CONSTRAINT_CONTENT_TRIGGER = 1<<7;
56     static final int CONSTRAINT_DEVICE_NOT_DOZING = 1<<8;
57     static final int CONSTRAINT_NOT_ROAMING = 1<<9;
58 
59     // Soft override: ignore constraints like time that don't affect API availability
60     public static final int OVERRIDE_SOFT = 1;
61     // Full override: ignore all constraints including API-affecting like connectivity
62     public static final int OVERRIDE_FULL = 2;
63 
64     /** If not specified, trigger update delay is 10 seconds. */
65     public static final long DEFAULT_TRIGGER_UPDATE_DELAY = 10*1000;
66 
67     /** The minimum possible update delay is 1/2 second. */
68     public static final long MIN_TRIGGER_UPDATE_DELAY = 500;
69 
70     /** If not specified, trigger maxumum delay is 2 minutes. */
71     public static final long DEFAULT_TRIGGER_MAX_DELAY = 2*60*1000;
72 
73     /** The minimum possible update delay is 1 second. */
74     public static final long MIN_TRIGGER_MAX_DELAY = 1000;
75 
76     final JobInfo job;
77     /** Uid of the package requesting this job. */
78     final int callingUid;
79     final String batteryName;
80 
81     final String sourcePackageName;
82     final int sourceUserId;
83     final int sourceUid;
84     final String sourceTag;
85 
86     final String tag;
87 
88     /**
89      * Earliest point in the future at which this job will be eligible to run. A value of 0
90      * indicates there is no delay constraint. See {@link #hasTimingDelayConstraint()}.
91      */
92     private final long earliestRunTimeElapsedMillis;
93     /**
94      * Latest point in the future at which this job must be run. A value of {@link Long#MAX_VALUE}
95      * indicates there is no deadline constraint. See {@link #hasDeadlineConstraint()}.
96      */
97     private final long latestRunTimeElapsedMillis;
98 
99     /** How many times this job has failed, used to compute back-off. */
100     private final int numFailures;
101 
102     // Constraints.
103     final int requiredConstraints;
104     int satisfiedConstraints = 0;
105 
106     // Set to true if doze constraint was satisfied due to app being whitelisted.
107     public boolean dozeWhitelisted;
108 
109     // These are filled in by controllers when preparing for execution.
110     public ArraySet<Uri> changedUris;
111     public ArraySet<String> changedAuthorities;
112 
113     public int lastEvaluatedPriority;
114 
115     // Used by shell commands
116     public int overrideState = 0;
117 
118     /**
119      * For use only by ContentObserverController: state it is maintaining about content URIs
120      * being observed.
121      */
122     ContentObserverController.JobInstance contentObserverJobInstance;
123 
124     /** Provide a handle to the service that this job will be run on. */
getServiceToken()125     public int getServiceToken() {
126         return callingUid;
127     }
128 
JobStatus(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId, String tag, int numFailures, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis)129     private JobStatus(JobInfo job, int callingUid, String sourcePackageName,
130             int sourceUserId, String tag, int numFailures, long earliestRunTimeElapsedMillis,
131             long latestRunTimeElapsedMillis) {
132         this.job = job;
133         this.callingUid = callingUid;
134 
135         int tempSourceUid = -1;
136         if (sourceUserId != -1 && sourcePackageName != null) {
137             try {
138                 tempSourceUid = AppGlobals.getPackageManager().getPackageUid(sourcePackageName, 0,
139                         sourceUserId);
140             } catch (RemoteException ex) {
141                 // Can't happen, PackageManager runs in the same process.
142             }
143         }
144         if (tempSourceUid == -1) {
145             this.sourceUid = callingUid;
146             this.sourceUserId = UserHandle.getUserId(callingUid);
147             this.sourcePackageName = job.getService().getPackageName();
148             this.sourceTag = null;
149         } else {
150             this.sourceUid = tempSourceUid;
151             this.sourceUserId = sourceUserId;
152             this.sourcePackageName = sourcePackageName;
153             this.sourceTag = tag;
154         }
155 
156         this.batteryName = this.sourceTag != null
157                 ? this.sourceTag + ":" + job.getService().getPackageName()
158                 : job.getService().flattenToShortString();
159         this.tag = "*job*/" + this.batteryName;
160 
161         this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis;
162         this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
163         this.numFailures = numFailures;
164 
165         int requiredConstraints = 0;
166         if (job.getNetworkType() == JobInfo.NETWORK_TYPE_ANY) {
167             requiredConstraints |= CONSTRAINT_CONNECTIVITY;
168         }
169         if (job.getNetworkType() == JobInfo.NETWORK_TYPE_UNMETERED) {
170             requiredConstraints |= CONSTRAINT_UNMETERED;
171         }
172         if (job.getNetworkType() == JobInfo.NETWORK_TYPE_NOT_ROAMING) {
173             requiredConstraints |= CONSTRAINT_NOT_ROAMING;
174         }
175         if (job.isRequireCharging()) {
176             requiredConstraints |= CONSTRAINT_CHARGING;
177         }
178         if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME) {
179             requiredConstraints |= CONSTRAINT_TIMING_DELAY;
180         }
181         if (latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) {
182             requiredConstraints |= CONSTRAINT_DEADLINE;
183         }
184         if (job.isRequireDeviceIdle()) {
185             requiredConstraints |= CONSTRAINT_IDLE;
186         }
187         if (job.getTriggerContentUris() != null) {
188             requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER;
189         }
190         this.requiredConstraints = requiredConstraints;
191     }
192 
193     /** Copy constructor. */
JobStatus(JobStatus jobStatus)194     public JobStatus(JobStatus jobStatus) {
195         this(jobStatus.getJob(), jobStatus.getUid(),
196                 jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(),
197                 jobStatus.getSourceTag(), jobStatus.getNumFailures(),
198                 jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed());
199     }
200 
201     /**
202      * Create a new JobStatus that was loaded from disk. We ignore the provided
203      * {@link android.app.job.JobInfo} time criteria because we can load a persisted periodic job
204      * from the {@link com.android.server.job.JobStore} and still want to respect its
205      * wallclock runtime rather than resetting it on every boot.
206      * We consider a freshly loaded job to no longer be in back-off.
207      */
JobStatus(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId, String sourceTag, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis)208     public JobStatus(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId,
209             String sourceTag, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
210         this(job, callingUid, sourcePackageName, sourceUserId, sourceTag, 0,
211                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis);
212     }
213 
214     /** Create a new job to be rescheduled with the provided parameters. */
JobStatus(JobStatus rescheduling, long newEarliestRuntimeElapsedMillis, long newLatestRuntimeElapsedMillis, int backoffAttempt)215     public JobStatus(JobStatus rescheduling, long newEarliestRuntimeElapsedMillis,
216                       long newLatestRuntimeElapsedMillis, int backoffAttempt) {
217         this(rescheduling.job, rescheduling.getUid(),
218                 rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(),
219                 rescheduling.getSourceTag(), backoffAttempt, newEarliestRuntimeElapsedMillis,
220                 newLatestRuntimeElapsedMillis);
221     }
222 
223     /**
224      * Create a newly scheduled job.
225      * @param callingUid Uid of the package that scheduled this job.
226      * @param sourcePackageName Package name on whose behalf this job is scheduled. Null indicates
227      *                          the calling package is the source.
228      * @param sourceUserId User id for whom this job is scheduled. -1 indicates this is same as the
229      */
createFromJobInfo(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId, String tag)230     public static JobStatus createFromJobInfo(JobInfo job, int callingUid, String sourcePackageName,
231             int sourceUserId, String tag) {
232         final long elapsedNow = SystemClock.elapsedRealtime();
233         final long earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis;
234         if (job.isPeriodic()) {
235             latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis();
236             earliestRunTimeElapsedMillis = latestRunTimeElapsedMillis - job.getFlexMillis();
237         } else {
238             earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ?
239                     elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME;
240             latestRunTimeElapsedMillis = job.hasLateConstraint() ?
241                     elapsedNow + job.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME;
242         }
243         return new JobStatus(job, callingUid, sourcePackageName, sourceUserId, tag, 0,
244                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis);
245     }
246 
getJob()247     public JobInfo getJob() {
248         return job;
249     }
250 
getJobId()251     public int getJobId() {
252         return job.getId();
253     }
254 
printUniqueId(PrintWriter pw)255     public void printUniqueId(PrintWriter pw) {
256         UserHandle.formatUid(pw, callingUid);
257         pw.print("/");
258         pw.print(job.getId());
259     }
260 
getNumFailures()261     public int getNumFailures() {
262         return numFailures;
263     }
264 
getServiceComponent()265     public ComponentName getServiceComponent() {
266         return job.getService();
267     }
268 
getSourcePackageName()269     public String getSourcePackageName() {
270         return sourcePackageName;
271     }
272 
getSourceUid()273     public int getSourceUid() {
274         return sourceUid;
275     }
276 
getSourceUserId()277     public int getSourceUserId() {
278         return sourceUserId;
279     }
280 
getUserId()281     public int getUserId() {
282         return UserHandle.getUserId(callingUid);
283     }
284 
getSourceTag()285     public String getSourceTag() {
286         return sourceTag;
287     }
288 
getUid()289     public int getUid() {
290         return callingUid;
291     }
292 
getBatteryName()293     public String getBatteryName() {
294         return batteryName;
295     }
296 
getTag()297     public String getTag() {
298         return tag;
299     }
300 
getExtras()301     public PersistableBundle getExtras() {
302         return job.getExtras();
303     }
304 
getPriority()305     public int getPriority() {
306         return job.getPriority();
307     }
308 
getFlags()309     public int getFlags() {
310         return job.getFlags();
311     }
312 
hasConnectivityConstraint()313     public boolean hasConnectivityConstraint() {
314         return (requiredConstraints&CONSTRAINT_CONNECTIVITY) != 0;
315     }
316 
hasUnmeteredConstraint()317     public boolean hasUnmeteredConstraint() {
318         return (requiredConstraints&CONSTRAINT_UNMETERED) != 0;
319     }
320 
hasNotRoamingConstraint()321     public boolean hasNotRoamingConstraint() {
322         return (requiredConstraints&CONSTRAINT_NOT_ROAMING) != 0;
323     }
324 
hasChargingConstraint()325     public boolean hasChargingConstraint() {
326         return (requiredConstraints&CONSTRAINT_CHARGING) != 0;
327     }
328 
hasTimingDelayConstraint()329     public boolean hasTimingDelayConstraint() {
330         return (requiredConstraints&CONSTRAINT_TIMING_DELAY) != 0;
331     }
332 
hasDeadlineConstraint()333     public boolean hasDeadlineConstraint() {
334         return (requiredConstraints&CONSTRAINT_DEADLINE) != 0;
335     }
336 
hasIdleConstraint()337     public boolean hasIdleConstraint() {
338         return (requiredConstraints&CONSTRAINT_IDLE) != 0;
339     }
340 
hasContentTriggerConstraint()341     public boolean hasContentTriggerConstraint() {
342         return (requiredConstraints&CONSTRAINT_CONTENT_TRIGGER) != 0;
343     }
344 
getTriggerContentUpdateDelay()345     public long getTriggerContentUpdateDelay() {
346         long time = job.getTriggerContentUpdateDelay();
347         if (time < 0) {
348             return DEFAULT_TRIGGER_UPDATE_DELAY;
349         }
350         return Math.max(time, MIN_TRIGGER_UPDATE_DELAY);
351     }
352 
getTriggerContentMaxDelay()353     public long getTriggerContentMaxDelay() {
354         long time = job.getTriggerContentMaxDelay();
355         if (time < 0) {
356             return DEFAULT_TRIGGER_MAX_DELAY;
357         }
358         return Math.max(time, MIN_TRIGGER_MAX_DELAY);
359     }
360 
isPersisted()361     public boolean isPersisted() {
362         return job.isPersisted();
363     }
364 
getEarliestRunTime()365     public long getEarliestRunTime() {
366         return earliestRunTimeElapsedMillis;
367     }
368 
getLatestRunTimeElapsed()369     public long getLatestRunTimeElapsed() {
370         return latestRunTimeElapsedMillis;
371     }
372 
setChargingConstraintSatisfied(boolean state)373     boolean setChargingConstraintSatisfied(boolean state) {
374         return setConstraintSatisfied(CONSTRAINT_CHARGING, state);
375     }
376 
setTimingDelayConstraintSatisfied(boolean state)377     boolean setTimingDelayConstraintSatisfied(boolean state) {
378         return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, state);
379     }
380 
setDeadlineConstraintSatisfied(boolean state)381     boolean setDeadlineConstraintSatisfied(boolean state) {
382         return setConstraintSatisfied(CONSTRAINT_DEADLINE, state);
383     }
384 
setIdleConstraintSatisfied(boolean state)385     boolean setIdleConstraintSatisfied(boolean state) {
386         return setConstraintSatisfied(CONSTRAINT_IDLE, state);
387     }
388 
setConnectivityConstraintSatisfied(boolean state)389     boolean setConnectivityConstraintSatisfied(boolean state) {
390         return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, state);
391     }
392 
setUnmeteredConstraintSatisfied(boolean state)393     boolean setUnmeteredConstraintSatisfied(boolean state) {
394         return setConstraintSatisfied(CONSTRAINT_UNMETERED, state);
395     }
396 
setNotRoamingConstraintSatisfied(boolean state)397     boolean setNotRoamingConstraintSatisfied(boolean state) {
398         return setConstraintSatisfied(CONSTRAINT_NOT_ROAMING, state);
399     }
400 
setAppNotIdleConstraintSatisfied(boolean state)401     boolean setAppNotIdleConstraintSatisfied(boolean state) {
402         return setConstraintSatisfied(CONSTRAINT_APP_NOT_IDLE, state);
403     }
404 
setContentTriggerConstraintSatisfied(boolean state)405     boolean setContentTriggerConstraintSatisfied(boolean state) {
406         return setConstraintSatisfied(CONSTRAINT_CONTENT_TRIGGER, state);
407     }
408 
setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted)409     boolean setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted) {
410         dozeWhitelisted = whitelisted;
411         return setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state);
412     }
413 
setConstraintSatisfied(int constraint, boolean state)414     boolean setConstraintSatisfied(int constraint, boolean state) {
415         boolean old = (satisfiedConstraints&constraint) != 0;
416         if (old == state) {
417             return false;
418         }
419         satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0);
420         return true;
421     }
422 
isConstraintSatisfied(int constraint)423     boolean isConstraintSatisfied(int constraint) {
424         return (satisfiedConstraints&constraint) != 0;
425     }
426 
shouldDump(int filterUid)427     public boolean shouldDump(int filterUid) {
428         return filterUid == -1 || UserHandle.getAppId(getUid()) == filterUid
429                 || UserHandle.getAppId(getSourceUid()) == filterUid;
430     }
431 
432     /**
433      * @return Whether or not this job is ready to run, based on its requirements. This is true if
434      * the constraints are satisfied <strong>or</strong> the deadline on the job has expired.
435      */
isReady()436     public boolean isReady() {
437         // Deadline constraint trumps other constraints (except for periodic jobs where deadline
438         // is an implementation detail. A periodic job should only run if its constraints are
439         // satisfied).
440         // AppNotIdle implicit constraint must be satisfied
441         // DeviceNotDozing implicit constraint must be satisfied
442         final boolean deadlineSatisfied = (!job.isPeriodic() && hasDeadlineConstraint()
443                 && (satisfiedConstraints & CONSTRAINT_DEADLINE) != 0);
444         final boolean notIdle = (satisfiedConstraints & CONSTRAINT_APP_NOT_IDLE) != 0;
445         final boolean notDozing = (satisfiedConstraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0
446                 || (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
447         return (isConstraintsSatisfied() || deadlineSatisfied) && notIdle && notDozing;
448     }
449 
450     static final int CONSTRAINTS_OF_INTEREST =
451             CONSTRAINT_CHARGING | CONSTRAINT_TIMING_DELAY |
452             CONSTRAINT_CONNECTIVITY | CONSTRAINT_UNMETERED | CONSTRAINT_NOT_ROAMING |
453             CONSTRAINT_IDLE | CONSTRAINT_CONTENT_TRIGGER;
454 
455     // Soft override covers all non-"functional" constraints
456     static final int SOFT_OVERRIDE_CONSTRAINTS =
457             CONSTRAINT_CHARGING | CONSTRAINT_TIMING_DELAY | CONSTRAINT_IDLE;
458 
459     /**
460      * @return Whether the constraints set on this job are satisfied.
461      */
isConstraintsSatisfied()462     public boolean isConstraintsSatisfied() {
463         if (overrideState == OVERRIDE_FULL) {
464             // force override: the job is always runnable
465             return true;
466         }
467 
468         final int req = requiredConstraints & CONSTRAINTS_OF_INTEREST;
469 
470         int sat = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
471         if (overrideState == OVERRIDE_SOFT) {
472             // override: pretend all 'soft' requirements are satisfied
473             sat |= (requiredConstraints & SOFT_OVERRIDE_CONSTRAINTS);
474         }
475 
476         return (sat & req) == req;
477     }
478 
matches(int uid, int jobId)479     public boolean matches(int uid, int jobId) {
480         return this.job.getId() == jobId && this.callingUid == uid;
481     }
482 
483     @Override
toString()484     public String toString() {
485         return String.valueOf(hashCode()).substring(0, 3) + ".."
486                 + ":[" + job.getService()
487                 + ",jId=" + job.getId()
488                 + ",u" + getUserId()
489                 + ",suid=" + getSourceUid()
490                 + ",R=(" + formatRunTime(earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME)
491                 + "," + formatRunTime(latestRunTimeElapsedMillis, NO_LATEST_RUNTIME) + ")"
492                 + ",N=" + job.getNetworkType() + ",C=" + job.isRequireCharging()
493                 + ",I=" + job.isRequireDeviceIdle()
494                 + ",U=" + (job.getTriggerContentUris() != null)
495                 + ",F=" + numFailures + ",P=" + job.isPersisted()
496                 + ",ANI=" + ((satisfiedConstraints&CONSTRAINT_APP_NOT_IDLE) != 0)
497                 + ",DND=" + ((satisfiedConstraints&CONSTRAINT_DEVICE_NOT_DOZING) != 0)
498                 + (isReady() ? "(READY)" : "")
499                 + "]";
500     }
501 
formatRunTime(long runtime, long defaultValue)502     private String formatRunTime(long runtime, long  defaultValue) {
503         if (runtime == defaultValue) {
504             return "none";
505         } else {
506             long elapsedNow = SystemClock.elapsedRealtime();
507             long nextRuntime = runtime - elapsedNow;
508             if (nextRuntime > 0) {
509                 return DateUtils.formatElapsedTime(nextRuntime / 1000);
510             } else {
511                 return "-" + DateUtils.formatElapsedTime(nextRuntime / -1000);
512             }
513         }
514     }
515 
516     /**
517      * Convenience function to identify a job uniquely without pulling all the data that
518      * {@link #toString()} returns.
519      */
toShortString()520     public String toShortString() {
521         StringBuilder sb = new StringBuilder();
522         sb.append(Integer.toHexString(System.identityHashCode(this)));
523         sb.append(" #");
524         UserHandle.formatUid(sb, callingUid);
525         sb.append("/");
526         sb.append(job.getId());
527         sb.append(' ');
528         sb.append(batteryName);
529         return sb.toString();
530     }
531 
532     /**
533      * Convenience function to identify a job uniquely without pulling all the data that
534      * {@link #toString()} returns.
535      */
toShortStringExceptUniqueId()536     public String toShortStringExceptUniqueId() {
537         StringBuilder sb = new StringBuilder();
538         sb.append(Integer.toHexString(System.identityHashCode(this)));
539         sb.append(' ');
540         sb.append(batteryName);
541         return sb.toString();
542     }
543 
dumpConstraints(PrintWriter pw, int constraints)544     void dumpConstraints(PrintWriter pw, int constraints) {
545         if ((constraints&CONSTRAINT_CHARGING) != 0) {
546             pw.print(" CHARGING");
547         }
548         if ((constraints&CONSTRAINT_TIMING_DELAY) != 0) {
549             pw.print(" TIMING_DELAY");
550         }
551         if ((constraints&CONSTRAINT_DEADLINE) != 0) {
552             pw.print(" DEADLINE");
553         }
554         if ((constraints&CONSTRAINT_IDLE) != 0) {
555             pw.print(" IDLE");
556         }
557         if ((constraints&CONSTRAINT_CONNECTIVITY) != 0) {
558             pw.print(" CONNECTIVITY");
559         }
560         if ((constraints&CONSTRAINT_UNMETERED) != 0) {
561             pw.print(" UNMETERED");
562         }
563         if ((constraints&CONSTRAINT_NOT_ROAMING) != 0) {
564             pw.print(" NOT_ROAMING");
565         }
566         if ((constraints&CONSTRAINT_APP_NOT_IDLE) != 0) {
567             pw.print(" APP_NOT_IDLE");
568         }
569         if ((constraints&CONSTRAINT_CONTENT_TRIGGER) != 0) {
570             pw.print(" CONTENT_TRIGGER");
571         }
572         if ((constraints&CONSTRAINT_DEVICE_NOT_DOZING) != 0) {
573             pw.print(" DEVICE_NOT_DOZING");
574         }
575     }
576 
577     // Dumpsys infrastructure
dump(PrintWriter pw, String prefix, boolean full)578     public void dump(PrintWriter pw, String prefix, boolean full) {
579         pw.print(prefix); UserHandle.formatUid(pw, callingUid);
580         pw.print(" tag="); pw.println(tag);
581         pw.print(prefix);
582         pw.print("Source: uid="); UserHandle.formatUid(pw, getSourceUid());
583         pw.print(" user="); pw.print(getSourceUserId());
584         pw.print(" pkg="); pw.println(getSourcePackageName());
585         if (full) {
586             pw.print(prefix); pw.println("JobInfo:"); pw.print(prefix);
587             pw.print("  Service: "); pw.println(job.getService().flattenToShortString());
588             if (job.isPeriodic()) {
589                 pw.print(prefix); pw.print("  PERIODIC: interval=");
590                 TimeUtils.formatDuration(job.getIntervalMillis(), pw);
591                 pw.print(" flex="); TimeUtils.formatDuration(job.getFlexMillis(), pw);
592                 pw.println();
593             }
594             if (job.isPersisted()) {
595                 pw.print(prefix); pw.println("  PERSISTED");
596             }
597             if (job.getPriority() != 0) {
598                 pw.print(prefix); pw.print("  Priority: "); pw.println(job.getPriority());
599             }
600             if (job.getFlags() != 0) {
601                 pw.print(prefix); pw.print("  Flags: ");
602                 pw.println(Integer.toHexString(job.getFlags()));
603             }
604             pw.print(prefix); pw.print("  Requires: charging=");
605             pw.print(job.isRequireCharging()); pw.print(" deviceIdle=");
606             pw.println(job.isRequireDeviceIdle());
607             if (job.getTriggerContentUris() != null) {
608                 pw.print(prefix); pw.println("  Trigger content URIs:");
609                 for (int i = 0; i < job.getTriggerContentUris().length; i++) {
610                     JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i];
611                     pw.print(prefix); pw.print("    ");
612                     pw.print(Integer.toHexString(trig.getFlags()));
613                     pw.print(' '); pw.println(trig.getUri());
614                 }
615                 if (job.getTriggerContentUpdateDelay() >= 0) {
616                     pw.print(prefix); pw.print("  Trigger update delay: ");
617                     TimeUtils.formatDuration(job.getTriggerContentUpdateDelay(), pw);
618                     pw.println();
619                 }
620                 if (job.getTriggerContentMaxDelay() >= 0) {
621                     pw.print(prefix); pw.print("  Trigger max delay: ");
622                     TimeUtils.formatDuration(job.getTriggerContentMaxDelay(), pw);
623                     pw.println();
624                 }
625             }
626             if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) {
627                 pw.print(prefix); pw.print("  Network type: "); pw.println(job.getNetworkType());
628             }
629             if (job.getMinLatencyMillis() != 0) {
630                 pw.print(prefix); pw.print("  Minimum latency: ");
631                 TimeUtils.formatDuration(job.getMinLatencyMillis(), pw);
632                 pw.println();
633             }
634             if (job.getMaxExecutionDelayMillis() != 0) {
635                 pw.print(prefix); pw.print("  Max execution delay: ");
636                 TimeUtils.formatDuration(job.getMaxExecutionDelayMillis(), pw);
637                 pw.println();
638             }
639             pw.print(prefix); pw.print("  Backoff: policy="); pw.print(job.getBackoffPolicy());
640             pw.print(" initial="); TimeUtils.formatDuration(job.getInitialBackoffMillis(), pw);
641             pw.println();
642             if (job.hasEarlyConstraint()) {
643                 pw.print(prefix); pw.println("  Has early constraint");
644             }
645             if (job.hasLateConstraint()) {
646                 pw.print(prefix); pw.println("  Has late constraint");
647             }
648         }
649         pw.print(prefix); pw.print("Required constraints:");
650         dumpConstraints(pw, requiredConstraints);
651         pw.println();
652         if (full) {
653             pw.print(prefix); pw.print("Satisfied constraints:");
654             dumpConstraints(pw, satisfiedConstraints);
655             pw.println();
656             pw.print(prefix); pw.print("Unsatisfied constraints:");
657             dumpConstraints(pw, (requiredConstraints & ~satisfiedConstraints));
658             pw.println();
659             if (dozeWhitelisted) {
660                 pw.print(prefix); pw.println("Doze whitelisted: true");
661             }
662         }
663         if (changedAuthorities != null) {
664             pw.print(prefix); pw.println("Changed authorities:");
665             for (int i=0; i<changedAuthorities.size(); i++) {
666                 pw.print(prefix); pw.print("  "); pw.println(changedAuthorities.valueAt(i));
667             }
668             if (changedUris != null) {
669                 pw.print(prefix); pw.println("Changed URIs:");
670                 for (int i=0; i<changedUris.size(); i++) {
671                     pw.print(prefix); pw.print("  "); pw.println(changedUris.valueAt(i));
672                 }
673             }
674         }
675         pw.print(prefix); pw.print("Earliest run time: ");
676         pw.println(formatRunTime(earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME));
677         pw.print(prefix); pw.print("Latest run time: ");
678         pw.println(formatRunTime(latestRunTimeElapsedMillis, NO_LATEST_RUNTIME));
679         if (numFailures != 0) {
680             pw.print(prefix); pw.print("Num failures: "); pw.println(numFailures);
681         }
682     }
683 }
684