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 17package com.android.server.job.controllers; 18 19import android.app.AppGlobals; 20import android.app.job.JobInfo; 21import android.content.ComponentName; 22import android.net.Uri; 23import android.os.PersistableBundle; 24import android.os.RemoteException; 25import android.os.SystemClock; 26import android.os.UserHandle; 27import android.text.format.DateUtils; 28import android.util.ArraySet; 29import android.util.TimeUtils; 30 31import 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 */ 44public 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 + ",R=(" + formatRunTime(earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME) 490 + "," + formatRunTime(latestRunTimeElapsedMillis, NO_LATEST_RUNTIME) + ")" 491 + ",N=" + job.getNetworkType() + ",C=" + job.isRequireCharging() 492 + ",I=" + job.isRequireDeviceIdle() 493 + ",U=" + (job.getTriggerContentUris() != null) 494 + ",F=" + numFailures + ",P=" + job.isPersisted() 495 + ",ANI=" + ((satisfiedConstraints&CONSTRAINT_APP_NOT_IDLE) != 0) 496 + ",DND=" + ((satisfiedConstraints&CONSTRAINT_DEVICE_NOT_DOZING) != 0) 497 + (isReady() ? "(READY)" : "") 498 + "]"; 499 } 500 501 private String formatRunTime(long runtime, long defaultValue) { 502 if (runtime == defaultValue) { 503 return "none"; 504 } else { 505 long elapsedNow = SystemClock.elapsedRealtime(); 506 long nextRuntime = runtime - elapsedNow; 507 if (nextRuntime > 0) { 508 return DateUtils.formatElapsedTime(nextRuntime / 1000); 509 } else { 510 return "-" + DateUtils.formatElapsedTime(nextRuntime / -1000); 511 } 512 } 513 } 514 515 /** 516 * Convenience function to identify a job uniquely without pulling all the data that 517 * {@link #toString()} returns. 518 */ 519 public String toShortString() { 520 StringBuilder sb = new StringBuilder(); 521 sb.append(Integer.toHexString(System.identityHashCode(this))); 522 sb.append(" #"); 523 UserHandle.formatUid(sb, callingUid); 524 sb.append("/"); 525 sb.append(job.getId()); 526 sb.append(' '); 527 sb.append(batteryName); 528 return sb.toString(); 529 } 530 531 /** 532 * Convenience function to identify a job uniquely without pulling all the data that 533 * {@link #toString()} returns. 534 */ 535 public String toShortStringExceptUniqueId() { 536 StringBuilder sb = new StringBuilder(); 537 sb.append(Integer.toHexString(System.identityHashCode(this))); 538 sb.append(' '); 539 sb.append(batteryName); 540 return sb.toString(); 541 } 542 543 void dumpConstraints(PrintWriter pw, int constraints) { 544 if ((constraints&CONSTRAINT_CHARGING) != 0) { 545 pw.print(" CHARGING"); 546 } 547 if ((constraints&CONSTRAINT_TIMING_DELAY) != 0) { 548 pw.print(" TIMING_DELAY"); 549 } 550 if ((constraints&CONSTRAINT_DEADLINE) != 0) { 551 pw.print(" DEADLINE"); 552 } 553 if ((constraints&CONSTRAINT_IDLE) != 0) { 554 pw.print(" IDLE"); 555 } 556 if ((constraints&CONSTRAINT_CONNECTIVITY) != 0) { 557 pw.print(" CONNECTIVITY"); 558 } 559 if ((constraints&CONSTRAINT_UNMETERED) != 0) { 560 pw.print(" UNMETERED"); 561 } 562 if ((constraints&CONSTRAINT_NOT_ROAMING) != 0) { 563 pw.print(" NOT_ROAMING"); 564 } 565 if ((constraints&CONSTRAINT_APP_NOT_IDLE) != 0) { 566 pw.print(" APP_NOT_IDLE"); 567 } 568 if ((constraints&CONSTRAINT_CONTENT_TRIGGER) != 0) { 569 pw.print(" CONTENT_TRIGGER"); 570 } 571 if ((constraints&CONSTRAINT_DEVICE_NOT_DOZING) != 0) { 572 pw.print(" DEVICE_NOT_DOZING"); 573 } 574 } 575 576 // Dumpsys infrastructure 577 public void dump(PrintWriter pw, String prefix, boolean full) { 578 pw.print(prefix); UserHandle.formatUid(pw, callingUid); 579 pw.print(" tag="); pw.println(tag); 580 pw.print(prefix); 581 pw.print("Source: uid="); UserHandle.formatUid(pw, getSourceUid()); 582 pw.print(" user="); pw.print(getSourceUserId()); 583 pw.print(" pkg="); pw.println(getSourcePackageName()); 584 if (full) { 585 pw.print(prefix); pw.println("JobInfo:"); pw.print(prefix); 586 pw.print(" Service: "); pw.println(job.getService().flattenToShortString()); 587 if (job.isPeriodic()) { 588 pw.print(prefix); pw.print(" PERIODIC: interval="); 589 TimeUtils.formatDuration(job.getIntervalMillis(), pw); 590 pw.print(" flex="); TimeUtils.formatDuration(job.getFlexMillis(), pw); 591 pw.println(); 592 } 593 if (job.isPersisted()) { 594 pw.print(prefix); pw.println(" PERSISTED"); 595 } 596 if (job.getPriority() != 0) { 597 pw.print(prefix); pw.print(" Priority: "); pw.println(job.getPriority()); 598 } 599 if (job.getFlags() != 0) { 600 pw.print(prefix); pw.print(" Flags: "); 601 pw.println(Integer.toHexString(job.getFlags())); 602 } 603 pw.print(prefix); pw.print(" Requires: charging="); 604 pw.print(job.isRequireCharging()); pw.print(" deviceIdle="); 605 pw.println(job.isRequireDeviceIdle()); 606 if (job.getTriggerContentUris() != null) { 607 pw.print(prefix); pw.println(" Trigger content URIs:"); 608 for (int i = 0; i < job.getTriggerContentUris().length; i++) { 609 JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i]; 610 pw.print(prefix); pw.print(" "); 611 pw.print(Integer.toHexString(trig.getFlags())); 612 pw.print(' '); pw.println(trig.getUri()); 613 } 614 if (job.getTriggerContentUpdateDelay() >= 0) { 615 pw.print(prefix); pw.print(" Trigger update delay: "); 616 TimeUtils.formatDuration(job.getTriggerContentUpdateDelay(), pw); 617 pw.println(); 618 } 619 if (job.getTriggerContentMaxDelay() >= 0) { 620 pw.print(prefix); pw.print(" Trigger max delay: "); 621 TimeUtils.formatDuration(job.getTriggerContentMaxDelay(), pw); 622 pw.println(); 623 } 624 } 625 if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) { 626 pw.print(prefix); pw.print(" Network type: "); pw.println(job.getNetworkType()); 627 } 628 if (job.getMinLatencyMillis() != 0) { 629 pw.print(prefix); pw.print(" Minimum latency: "); 630 TimeUtils.formatDuration(job.getMinLatencyMillis(), pw); 631 pw.println(); 632 } 633 if (job.getMaxExecutionDelayMillis() != 0) { 634 pw.print(prefix); pw.print(" Max execution delay: "); 635 TimeUtils.formatDuration(job.getMaxExecutionDelayMillis(), pw); 636 pw.println(); 637 } 638 pw.print(prefix); pw.print(" Backoff: policy="); pw.print(job.getBackoffPolicy()); 639 pw.print(" initial="); TimeUtils.formatDuration(job.getInitialBackoffMillis(), pw); 640 pw.println(); 641 if (job.hasEarlyConstraint()) { 642 pw.print(prefix); pw.println(" Has early constraint"); 643 } 644 if (job.hasLateConstraint()) { 645 pw.print(prefix); pw.println(" Has late constraint"); 646 } 647 } 648 pw.print(prefix); pw.print("Required constraints:"); 649 dumpConstraints(pw, requiredConstraints); 650 pw.println(); 651 if (full) { 652 pw.print(prefix); pw.print("Satisfied constraints:"); 653 dumpConstraints(pw, satisfiedConstraints); 654 pw.println(); 655 pw.print(prefix); pw.print("Unsatisfied constraints:"); 656 dumpConstraints(pw, (requiredConstraints & ~satisfiedConstraints)); 657 pw.println(); 658 if (dozeWhitelisted) { 659 pw.print(prefix); pw.println("Doze whitelisted: true"); 660 } 661 } 662 if (changedAuthorities != null) { 663 pw.print(prefix); pw.println("Changed authorities:"); 664 for (int i=0; i<changedAuthorities.size(); i++) { 665 pw.print(prefix); pw.print(" "); pw.println(changedAuthorities.valueAt(i)); 666 } 667 if (changedUris != null) { 668 pw.print(prefix); pw.println("Changed URIs:"); 669 for (int i=0; i<changedUris.size(); i++) { 670 pw.print(prefix); pw.print(" "); pw.println(changedUris.valueAt(i)); 671 } 672 } 673 } 674 pw.print(prefix); pw.print("Earliest run time: "); 675 pw.println(formatRunTime(earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME)); 676 pw.print(prefix); pw.print("Latest run time: "); 677 pw.println(formatRunTime(latestRunTimeElapsedMillis, NO_LATEST_RUNTIME)); 678 if (numFailures != 0) { 679 pw.print(prefix); pw.print("Num failures: "); pw.println(numFailures); 680 } 681 } 682} 683