Home | History | Annotate | Download | only in controllers
      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. */
    125     public int getServiceToken() {
    126         return callingUid;
    127     }
    128 
    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. */
    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      */
    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. */
    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      */
    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 
    247     public JobInfo getJob() {
    248         return job;
    249     }
    250 
    251     public int getJobId() {
    252         return job.getId();
    253     }
    254 
    255     public void printUniqueId(PrintWriter pw) {
    256         UserHandle.formatUid(pw, callingUid);
    257         pw.print("/");
    258         pw.print(job.getId());
    259     }
    260 
    261     public int getNumFailures() {
    262         return numFailures;
    263     }
    264 
    265     public ComponentName getServiceComponent() {
    266         return job.getService();
    267     }
    268 
    269     public String getSourcePackageName() {
    270         return sourcePackageName;
    271     }
    272 
    273     public int getSourceUid() {
    274         return sourceUid;
    275     }
    276 
    277     public int getSourceUserId() {
    278         return sourceUserId;
    279     }
    280 
    281     public int getUserId() {
    282         return UserHandle.getUserId(callingUid);
    283     }
    284 
    285     public String getSourceTag() {
    286         return sourceTag;
    287     }
    288 
    289     public int getUid() {
    290         return callingUid;
    291     }
    292 
    293     public String getBatteryName() {
    294         return batteryName;
    295     }
    296 
    297     public String getTag() {
    298         return tag;
    299     }
    300 
    301     public PersistableBundle getExtras() {
    302         return job.getExtras();
    303     }
    304 
    305     public int getPriority() {
    306         return job.getPriority();
    307     }
    308 
    309     public int getFlags() {
    310         return job.getFlags();
    311     }
    312 
    313     public boolean hasConnectivityConstraint() {
    314         return (requiredConstraints&CONSTRAINT_CONNECTIVITY) != 0;
    315     }
    316 
    317     public boolean hasUnmeteredConstraint() {
    318         return (requiredConstraints&CONSTRAINT_UNMETERED) != 0;
    319     }
    320 
    321     public boolean hasNotRoamingConstraint() {
    322         return (requiredConstraints&CONSTRAINT_NOT_ROAMING) != 0;
    323     }
    324 
    325     public boolean hasChargingConstraint() {
    326         return (requiredConstraints&CONSTRAINT_CHARGING) != 0;
    327     }
    328 
    329     public boolean hasTimingDelayConstraint() {
    330         return (requiredConstraints&CONSTRAINT_TIMING_DELAY) != 0;
    331     }
    332 
    333     public boolean hasDeadlineConstraint() {
    334         return (requiredConstraints&CONSTRAINT_DEADLINE) != 0;
    335     }
    336 
    337     public boolean hasIdleConstraint() {
    338         return (requiredConstraints&CONSTRAINT_IDLE) != 0;
    339     }
    340 
    341     public boolean hasContentTriggerConstraint() {
    342         return (requiredConstraints&CONSTRAINT_CONTENT_TRIGGER) != 0;
    343     }
    344 
    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 
    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 
    361     public boolean isPersisted() {
    362         return job.isPersisted();
    363     }
    364 
    365     public long getEarliestRunTime() {
    366         return earliestRunTimeElapsedMillis;
    367     }
    368 
    369     public long getLatestRunTimeElapsed() {
    370         return latestRunTimeElapsedMillis;
    371     }
    372 
    373     boolean setChargingConstraintSatisfied(boolean state) {
    374         return setConstraintSatisfied(CONSTRAINT_CHARGING, state);
    375     }
    376 
    377     boolean setTimingDelayConstraintSatisfied(boolean state) {
    378         return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, state);
    379     }
    380 
    381     boolean setDeadlineConstraintSatisfied(boolean state) {
    382         return setConstraintSatisfied(CONSTRAINT_DEADLINE, state);
    383     }
    384 
    385     boolean setIdleConstraintSatisfied(boolean state) {
    386         return setConstraintSatisfied(CONSTRAINT_IDLE, state);
    387     }
    388 
    389     boolean setConnectivityConstraintSatisfied(boolean state) {
    390         return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, state);
    391     }
    392 
    393     boolean setUnmeteredConstraintSatisfied(boolean state) {
    394         return setConstraintSatisfied(CONSTRAINT_UNMETERED, state);
    395     }
    396 
    397     boolean setNotRoamingConstraintSatisfied(boolean state) {
    398         return setConstraintSatisfied(CONSTRAINT_NOT_ROAMING, state);
    399     }
    400 
    401     boolean setAppNotIdleConstraintSatisfied(boolean state) {
    402         return setConstraintSatisfied(CONSTRAINT_APP_NOT_IDLE, state);
    403     }
    404 
    405     boolean setContentTriggerConstraintSatisfied(boolean state) {
    406         return setConstraintSatisfied(CONSTRAINT_CONTENT_TRIGGER, state);
    407     }
    408 
    409     boolean setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted) {
    410         dozeWhitelisted = whitelisted;
    411         return setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state);
    412     }
    413 
    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 
    423     boolean isConstraintSatisfied(int constraint) {
    424         return (satisfiedConstraints&constraint) != 0;
    425     }
    426 
    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      */
    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      */
    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 
    479     public boolean matches(int uid, int jobId) {
    480         return this.job.getId() == jobId && this.callingUid == uid;
    481     }
    482 
    483     @Override
    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 
    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      */
    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      */
    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 
    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
    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