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.IActivityManager; 21 import android.app.job.JobInfo; 22 import android.app.job.JobWorkItem; 23 import android.content.ClipData; 24 import android.content.ComponentName; 25 import android.net.Uri; 26 import android.os.RemoteException; 27 import android.os.SystemClock; 28 import android.os.UserHandle; 29 import android.text.format.Time; 30 import android.util.ArraySet; 31 import android.util.Pair; 32 import android.util.Slog; 33 import android.util.TimeUtils; 34 35 import com.android.server.job.GrantedUriPermissions; 36 import com.android.server.job.JobSchedulerService; 37 38 import java.io.PrintWriter; 39 import java.util.ArrayList; 40 import java.util.Arrays; 41 42 /** 43 * Uniquely identifies a job internally. 44 * Created from the public {@link android.app.job.JobInfo} object when it lands on the scheduler. 45 * Contains current state of the requirements of the job, as well as a function to evaluate 46 * whether it's ready to run. 47 * This object is shared among the various controllers - hence why the different fields are atomic. 48 * This isn't strictly necessary because each controller is only interested in a specific field, 49 * and the receivers that are listening for global state change will all run on the main looper, 50 * but we don't enforce that so this is safer. 51 * @hide 52 */ 53 public final class JobStatus { 54 static final String TAG = "JobSchedulerService"; 55 static final boolean DEBUG = JobSchedulerService.DEBUG; 56 57 public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE; 58 public static final long NO_EARLIEST_RUNTIME = 0L; 59 60 static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; 61 static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE; 62 static final int CONSTRAINT_BATTERY_NOT_LOW = JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; 63 static final int CONSTRAINT_STORAGE_NOT_LOW = JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW; 64 static final int CONSTRAINT_TIMING_DELAY = 1<<31; 65 static final int CONSTRAINT_DEADLINE = 1<<30; 66 static final int CONSTRAINT_UNMETERED = 1<<29; 67 static final int CONSTRAINT_CONNECTIVITY = 1<<28; 68 static final int CONSTRAINT_APP_NOT_IDLE = 1<<27; 69 static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26; 70 static final int CONSTRAINT_DEVICE_NOT_DOZING = 1<<25; 71 static final int CONSTRAINT_NOT_ROAMING = 1<<24; 72 static final int CONSTRAINT_METERED = 1<<23; 73 74 static final int CONNECTIVITY_MASK = 75 CONSTRAINT_UNMETERED | CONSTRAINT_CONNECTIVITY | 76 CONSTRAINT_NOT_ROAMING | CONSTRAINT_METERED; 77 78 // Soft override: ignore constraints like time that don't affect API availability 79 public static final int OVERRIDE_SOFT = 1; 80 // Full override: ignore all constraints including API-affecting like connectivity 81 public static final int OVERRIDE_FULL = 2; 82 83 /** If not specified, trigger update delay is 10 seconds. */ 84 public static final long DEFAULT_TRIGGER_UPDATE_DELAY = 10*1000; 85 86 /** The minimum possible update delay is 1/2 second. */ 87 public static final long MIN_TRIGGER_UPDATE_DELAY = 500; 88 89 /** If not specified, trigger maxumum delay is 2 minutes. */ 90 public static final long DEFAULT_TRIGGER_MAX_DELAY = 2*60*1000; 91 92 /** The minimum possible update delay is 1 second. */ 93 public static final long MIN_TRIGGER_MAX_DELAY = 1000; 94 95 final JobInfo job; 96 /** Uid of the package requesting this job. */ 97 final int callingUid; 98 final String batteryName; 99 100 final String sourcePackageName; 101 final int sourceUserId; 102 final int sourceUid; 103 final String sourceTag; 104 105 final String tag; 106 107 private GrantedUriPermissions uriPerms; 108 private boolean prepared; 109 110 static final boolean DEBUG_PREPARE = true; 111 private Throwable unpreparedPoint = null; 112 113 /** 114 * Earliest point in the future at which this job will be eligible to run. A value of 0 115 * indicates there is no delay constraint. See {@link #hasTimingDelayConstraint()}. 116 */ 117 private final long earliestRunTimeElapsedMillis; 118 /** 119 * Latest point in the future at which this job must be run. A value of {@link Long#MAX_VALUE} 120 * indicates there is no deadline constraint. See {@link #hasDeadlineConstraint()}. 121 */ 122 private final long latestRunTimeElapsedMillis; 123 124 /** How many times this job has failed, used to compute back-off. */ 125 private final int numFailures; 126 127 // Constraints. 128 final int requiredConstraints; 129 int satisfiedConstraints = 0; 130 131 // Set to true if doze constraint was satisfied due to app being whitelisted. 132 public boolean dozeWhitelisted; 133 134 /** 135 * Flag for {@link #trackingControllers}: the battery controller is currently tracking this job. 136 */ 137 public static final int TRACKING_BATTERY = 1<<0; 138 /** 139 * Flag for {@link #trackingControllers}: the network connectivity controller is currently 140 * tracking this job. 141 */ 142 public static final int TRACKING_CONNECTIVITY = 1<<1; 143 /** 144 * Flag for {@link #trackingControllers}: the content observer controller is currently 145 * tracking this job. 146 */ 147 public static final int TRACKING_CONTENT = 1<<2; 148 /** 149 * Flag for {@link #trackingControllers}: the idle controller is currently tracking this job. 150 */ 151 public static final int TRACKING_IDLE = 1<<3; 152 /** 153 * Flag for {@link #trackingControllers}: the storage controller is currently tracking this job. 154 */ 155 public static final int TRACKING_STORAGE = 1<<4; 156 /** 157 * Flag for {@link #trackingControllers}: the time controller is currently tracking this job. 158 */ 159 public static final int TRACKING_TIME = 1<<5; 160 161 /** 162 * Bit mask of controllers that are currently tracking the job. 163 */ 164 private int trackingControllers; 165 166 // These are filled in by controllers when preparing for execution. 167 public ArraySet<Uri> changedUris; 168 public ArraySet<String> changedAuthorities; 169 170 public int lastEvaluatedPriority; 171 172 // If non-null, this is work that has been enqueued for the job. 173 public ArrayList<JobWorkItem> pendingWork; 174 175 // If non-null, this is work that is currently being executed. 176 public ArrayList<JobWorkItem> executingWork; 177 178 public int nextPendingWorkId = 1; 179 180 // Used by shell commands 181 public int overrideState = 0; 182 183 // When this job was enqueued, for ordering. (in elapsedRealtimeMillis) 184 public long enqueueTime; 185 186 // Metrics about queue latency. (in uptimeMillis) 187 public long madePending; 188 public long madeActive; 189 190 /** 191 * Last time a job finished successfully for a periodic job, in the currentTimeMillis time, 192 * for dumpsys. 193 */ 194 private long mLastSuccessfulRunTime; 195 196 /** 197 * Last time a job finished unsuccessfully, in the currentTimeMillis time, for dumpsys. 198 */ 199 private long mLastFailedRunTime; 200 201 /** 202 * Transient: when a job is inflated from disk before we have a reliable RTC clock time, 203 * we retain the canonical (delay, deadline) scheduling tuple read out of the persistent 204 * store in UTC so that we can fix up the job's scheduling criteria once we get a good 205 * wall-clock time. If we have to persist the job again before the clock has been updated, 206 * we record these times again rather than calculating based on the earliest/latest elapsed 207 * time base figures. 208 * 209 * 'first' is the earliest/delay time, and 'second' is the latest/deadline time. 210 */ 211 private Pair<Long, Long> mPersistedUtcTimes; 212 213 /** 214 * For use only by ContentObserverController: state it is maintaining about content URIs 215 * being observed. 216 */ 217 ContentObserverController.JobInstance contentObserverJobInstance; 218 219 /** Provide a handle to the service that this job will be run on. */ 220 public int getServiceToken() { 221 return callingUid; 222 } 223 224 private JobStatus(JobInfo job, int callingUid, String sourcePackageName, 225 int sourceUserId, String tag, int numFailures, long earliestRunTimeElapsedMillis, 226 long latestRunTimeElapsedMillis, long lastSuccessfulRunTime, long lastFailedRunTime) { 227 this.job = job; 228 this.callingUid = callingUid; 229 230 int tempSourceUid = -1; 231 if (sourceUserId != -1 && sourcePackageName != null) { 232 try { 233 tempSourceUid = AppGlobals.getPackageManager().getPackageUid(sourcePackageName, 0, 234 sourceUserId); 235 } catch (RemoteException ex) { 236 // Can't happen, PackageManager runs in the same process. 237 } 238 } 239 if (tempSourceUid == -1) { 240 this.sourceUid = callingUid; 241 this.sourceUserId = UserHandle.getUserId(callingUid); 242 this.sourcePackageName = job.getService().getPackageName(); 243 this.sourceTag = null; 244 } else { 245 this.sourceUid = tempSourceUid; 246 this.sourceUserId = sourceUserId; 247 this.sourcePackageName = sourcePackageName; 248 this.sourceTag = tag; 249 } 250 251 this.batteryName = this.sourceTag != null 252 ? this.sourceTag + ":" + job.getService().getPackageName() 253 : job.getService().flattenToShortString(); 254 this.tag = "*job*/" + this.batteryName; 255 256 this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis; 257 this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis; 258 this.numFailures = numFailures; 259 260 int requiredConstraints = job.getConstraintFlags(); 261 262 switch (job.getNetworkType()) { 263 case JobInfo.NETWORK_TYPE_NONE: 264 // No constraint. 265 break; 266 case JobInfo.NETWORK_TYPE_ANY: 267 requiredConstraints |= CONSTRAINT_CONNECTIVITY; 268 break; 269 case JobInfo.NETWORK_TYPE_UNMETERED: 270 requiredConstraints |= CONSTRAINT_UNMETERED; 271 break; 272 case JobInfo.NETWORK_TYPE_NOT_ROAMING: 273 requiredConstraints |= CONSTRAINT_NOT_ROAMING; 274 break; 275 case JobInfo.NETWORK_TYPE_METERED: 276 requiredConstraints |= CONSTRAINT_METERED; 277 break; 278 default: 279 Slog.w(TAG, "Unrecognized networking constraint " + job.getNetworkType()); 280 break; 281 } 282 283 if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME) { 284 requiredConstraints |= CONSTRAINT_TIMING_DELAY; 285 } 286 if (latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) { 287 requiredConstraints |= CONSTRAINT_DEADLINE; 288 } 289 if (job.getTriggerContentUris() != null) { 290 requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER; 291 } 292 this.requiredConstraints = requiredConstraints; 293 294 mLastSuccessfulRunTime = lastSuccessfulRunTime; 295 mLastFailedRunTime = lastFailedRunTime; 296 } 297 298 /** Copy constructor: used specifically when cloning JobStatus objects for persistence, 299 * so we preserve RTC window bounds if the source object has them. */ 300 public JobStatus(JobStatus jobStatus) { 301 this(jobStatus.getJob(), jobStatus.getUid(), 302 jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(), 303 jobStatus.getSourceTag(), jobStatus.getNumFailures(), 304 jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed(), 305 jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime()); 306 mPersistedUtcTimes = jobStatus.mPersistedUtcTimes; 307 if (jobStatus.mPersistedUtcTimes != null) { 308 if (DEBUG) { 309 Slog.i(TAG, "Cloning job with persisted run times", new RuntimeException("here")); 310 } 311 } 312 } 313 314 /** 315 * Create a new JobStatus that was loaded from disk. We ignore the provided 316 * {@link android.app.job.JobInfo} time criteria because we can load a persisted periodic job 317 * from the {@link com.android.server.job.JobStore} and still want to respect its 318 * wallclock runtime rather than resetting it on every boot. 319 * We consider a freshly loaded job to no longer be in back-off. 320 */ 321 public JobStatus(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId, 322 String sourceTag, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, 323 long lastSuccessfulRunTime, long lastFailedRunTime, 324 Pair<Long, Long> persistedExecutionTimesUTC) { 325 this(job, callingUid, sourcePackageName, sourceUserId, sourceTag, 0, 326 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 327 lastSuccessfulRunTime, lastFailedRunTime); 328 329 // Only during initial inflation do we record the UTC-timebase execution bounds 330 // read from the persistent store. If we ever have to recreate the JobStatus on 331 // the fly, it means we're rescheduling the job; and this means that the calculated 332 // elapsed timebase bounds intrinsically become correct. 333 this.mPersistedUtcTimes = persistedExecutionTimesUTC; 334 if (persistedExecutionTimesUTC != null) { 335 if (DEBUG) { 336 Slog.i(TAG, "+ restored job with RTC times because of bad boot clock"); 337 } 338 } 339 } 340 341 /** Create a new job to be rescheduled with the provided parameters. */ 342 public JobStatus(JobStatus rescheduling, long newEarliestRuntimeElapsedMillis, 343 long newLatestRuntimeElapsedMillis, int backoffAttempt, 344 long lastSuccessfulRunTime, long lastFailedRunTime) { 345 this(rescheduling.job, rescheduling.getUid(), 346 rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(), 347 rescheduling.getSourceTag(), backoffAttempt, newEarliestRuntimeElapsedMillis, 348 newLatestRuntimeElapsedMillis, 349 lastSuccessfulRunTime, lastFailedRunTime); 350 } 351 352 /** 353 * Create a newly scheduled job. 354 * @param callingUid Uid of the package that scheduled this job. 355 * @param sourcePackageName Package name on whose behalf this job is scheduled. Null indicates 356 * the calling package is the source. 357 * @param sourceUserId User id for whom this job is scheduled. -1 indicates this is same as the 358 */ 359 public static JobStatus createFromJobInfo(JobInfo job, int callingUid, String sourcePackageName, 360 int sourceUserId, String tag) { 361 final long elapsedNow = SystemClock.elapsedRealtime(); 362 final long earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis; 363 if (job.isPeriodic()) { 364 latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis(); 365 earliestRunTimeElapsedMillis = latestRunTimeElapsedMillis - job.getFlexMillis(); 366 } else { 367 earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ? 368 elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME; 369 latestRunTimeElapsedMillis = job.hasLateConstraint() ? 370 elapsedNow + job.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME; 371 } 372 return new JobStatus(job, callingUid, sourcePackageName, sourceUserId, tag, 0, 373 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 374 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */); 375 } 376 377 public void enqueueWorkLocked(IActivityManager am, JobWorkItem work) { 378 if (pendingWork == null) { 379 pendingWork = new ArrayList<>(); 380 } 381 work.setWorkId(nextPendingWorkId); 382 nextPendingWorkId++; 383 if (work.getIntent() != null 384 && GrantedUriPermissions.checkGrantFlags(work.getIntent().getFlags())) { 385 work.setGrants(GrantedUriPermissions.createFromIntent(am, work.getIntent(), sourceUid, 386 sourcePackageName, sourceUserId, toShortString())); 387 } 388 pendingWork.add(work); 389 } 390 391 public JobWorkItem dequeueWorkLocked() { 392 if (pendingWork != null && pendingWork.size() > 0) { 393 JobWorkItem work = pendingWork.remove(0); 394 if (work != null) { 395 if (executingWork == null) { 396 executingWork = new ArrayList<>(); 397 } 398 executingWork.add(work); 399 work.bumpDeliveryCount(); 400 } 401 return work; 402 } 403 return null; 404 } 405 406 public boolean hasWorkLocked() { 407 return (pendingWork != null && pendingWork.size() > 0) || hasExecutingWorkLocked(); 408 } 409 410 public boolean hasExecutingWorkLocked() { 411 return executingWork != null && executingWork.size() > 0; 412 } 413 414 private static void ungrantWorkItem(IActivityManager am, JobWorkItem work) { 415 if (work.getGrants() != null) { 416 ((GrantedUriPermissions)work.getGrants()).revoke(am); 417 } 418 } 419 420 public boolean completeWorkLocked(IActivityManager am, int workId) { 421 if (executingWork != null) { 422 final int N = executingWork.size(); 423 for (int i = 0; i < N; i++) { 424 JobWorkItem work = executingWork.get(i); 425 if (work.getWorkId() == workId) { 426 executingWork.remove(i); 427 ungrantWorkItem(am, work); 428 return true; 429 } 430 } 431 } 432 return false; 433 } 434 435 private static void ungrantWorkList(IActivityManager am, ArrayList<JobWorkItem> list) { 436 if (list != null) { 437 final int N = list.size(); 438 for (int i = 0; i < N; i++) { 439 ungrantWorkItem(am, list.get(i)); 440 } 441 } 442 } 443 444 public void stopTrackingJobLocked(IActivityManager am, JobStatus incomingJob) { 445 if (incomingJob != null) { 446 // We are replacing with a new job -- transfer the work! We do any executing 447 // work first, since that was originally at the front of the pending work. 448 if (executingWork != null && executingWork.size() > 0) { 449 incomingJob.pendingWork = executingWork; 450 } 451 if (incomingJob.pendingWork == null) { 452 incomingJob.pendingWork = pendingWork; 453 } else if (pendingWork != null && pendingWork.size() > 0) { 454 incomingJob.pendingWork.addAll(pendingWork); 455 } 456 pendingWork = null; 457 executingWork = null; 458 incomingJob.nextPendingWorkId = nextPendingWorkId; 459 } else { 460 // We are completely stopping the job... need to clean up work. 461 ungrantWorkList(am, pendingWork); 462 pendingWork = null; 463 ungrantWorkList(am, executingWork); 464 executingWork = null; 465 } 466 } 467 468 public void prepareLocked(IActivityManager am) { 469 if (prepared) { 470 Slog.wtf(TAG, "Already prepared: " + this); 471 return; 472 } 473 prepared = true; 474 if (DEBUG_PREPARE) { 475 unpreparedPoint = null; 476 } 477 final ClipData clip = job.getClipData(); 478 if (clip != null) { 479 uriPerms = GrantedUriPermissions.createFromClip(am, clip, sourceUid, sourcePackageName, 480 sourceUserId, job.getClipGrantFlags(), toShortString()); 481 } 482 } 483 484 public void unprepareLocked(IActivityManager am) { 485 if (!prepared) { 486 Slog.wtf(TAG, "Hasn't been prepared: " + this); 487 if (DEBUG_PREPARE && unpreparedPoint != null) { 488 Slog.e(TAG, "Was already unprepared at ", unpreparedPoint); 489 } 490 return; 491 } 492 prepared = false; 493 if (DEBUG_PREPARE) { 494 unpreparedPoint = new Throwable().fillInStackTrace(); 495 } 496 if (uriPerms != null) { 497 uriPerms.revoke(am); 498 uriPerms = null; 499 } 500 } 501 502 public boolean isPreparedLocked() { 503 return prepared; 504 } 505 506 public JobInfo getJob() { 507 return job; 508 } 509 510 public int getJobId() { 511 return job.getId(); 512 } 513 514 public void printUniqueId(PrintWriter pw) { 515 UserHandle.formatUid(pw, callingUid); 516 pw.print("/"); 517 pw.print(job.getId()); 518 } 519 520 public int getNumFailures() { 521 return numFailures; 522 } 523 524 public ComponentName getServiceComponent() { 525 return job.getService(); 526 } 527 528 public String getSourcePackageName() { 529 return sourcePackageName; 530 } 531 532 public int getSourceUid() { 533 return sourceUid; 534 } 535 536 public int getSourceUserId() { 537 return sourceUserId; 538 } 539 540 public int getUserId() { 541 return UserHandle.getUserId(callingUid); 542 } 543 544 public String getSourceTag() { 545 return sourceTag; 546 } 547 548 public int getUid() { 549 return callingUid; 550 } 551 552 public String getBatteryName() { 553 return batteryName; 554 } 555 556 public String getTag() { 557 return tag; 558 } 559 560 public int getPriority() { 561 return job.getPriority(); 562 } 563 564 public int getFlags() { 565 return job.getFlags(); 566 } 567 568 /** Does this job have any sort of networking constraint? */ 569 public boolean hasConnectivityConstraint() { 570 return (requiredConstraints&CONNECTIVITY_MASK) != 0; 571 } 572 573 public boolean needsAnyConnectivity() { 574 return (requiredConstraints&CONSTRAINT_CONNECTIVITY) != 0; 575 } 576 577 public boolean needsUnmeteredConnectivity() { 578 return (requiredConstraints&CONSTRAINT_UNMETERED) != 0; 579 } 580 581 public boolean needsMeteredConnectivity() { 582 return (requiredConstraints&CONSTRAINT_METERED) != 0; 583 } 584 585 public boolean needsNonRoamingConnectivity() { 586 return (requiredConstraints&CONSTRAINT_NOT_ROAMING) != 0; 587 } 588 589 public boolean hasChargingConstraint() { 590 return (requiredConstraints&CONSTRAINT_CHARGING) != 0; 591 } 592 593 public boolean hasBatteryNotLowConstraint() { 594 return (requiredConstraints&CONSTRAINT_BATTERY_NOT_LOW) != 0; 595 } 596 597 public boolean hasPowerConstraint() { 598 return (requiredConstraints&(CONSTRAINT_CHARGING|CONSTRAINT_BATTERY_NOT_LOW)) != 0; 599 } 600 601 public boolean hasStorageNotLowConstraint() { 602 return (requiredConstraints&CONSTRAINT_STORAGE_NOT_LOW) != 0; 603 } 604 605 public boolean hasTimingDelayConstraint() { 606 return (requiredConstraints&CONSTRAINT_TIMING_DELAY) != 0; 607 } 608 609 public boolean hasDeadlineConstraint() { 610 return (requiredConstraints&CONSTRAINT_DEADLINE) != 0; 611 } 612 613 public boolean hasIdleConstraint() { 614 return (requiredConstraints&CONSTRAINT_IDLE) != 0; 615 } 616 617 public boolean hasContentTriggerConstraint() { 618 return (requiredConstraints&CONSTRAINT_CONTENT_TRIGGER) != 0; 619 } 620 621 public long getTriggerContentUpdateDelay() { 622 long time = job.getTriggerContentUpdateDelay(); 623 if (time < 0) { 624 return DEFAULT_TRIGGER_UPDATE_DELAY; 625 } 626 return Math.max(time, MIN_TRIGGER_UPDATE_DELAY); 627 } 628 629 public long getTriggerContentMaxDelay() { 630 long time = job.getTriggerContentMaxDelay(); 631 if (time < 0) { 632 return DEFAULT_TRIGGER_MAX_DELAY; 633 } 634 return Math.max(time, MIN_TRIGGER_MAX_DELAY); 635 } 636 637 public boolean isPersisted() { 638 return job.isPersisted(); 639 } 640 641 public long getEarliestRunTime() { 642 return earliestRunTimeElapsedMillis; 643 } 644 645 public long getLatestRunTimeElapsed() { 646 return latestRunTimeElapsedMillis; 647 } 648 649 public Pair<Long, Long> getPersistedUtcTimes() { 650 return mPersistedUtcTimes; 651 } 652 653 public void clearPersistedUtcTimes() { 654 mPersistedUtcTimes = null; 655 } 656 657 boolean setChargingConstraintSatisfied(boolean state) { 658 return setConstraintSatisfied(CONSTRAINT_CHARGING, state); 659 } 660 661 boolean setBatteryNotLowConstraintSatisfied(boolean state) { 662 return setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, state); 663 } 664 665 boolean setStorageNotLowConstraintSatisfied(boolean state) { 666 return setConstraintSatisfied(CONSTRAINT_STORAGE_NOT_LOW, state); 667 } 668 669 boolean setTimingDelayConstraintSatisfied(boolean state) { 670 return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, state); 671 } 672 673 boolean setDeadlineConstraintSatisfied(boolean state) { 674 return setConstraintSatisfied(CONSTRAINT_DEADLINE, state); 675 } 676 677 boolean setIdleConstraintSatisfied(boolean state) { 678 return setConstraintSatisfied(CONSTRAINT_IDLE, state); 679 } 680 681 boolean setConnectivityConstraintSatisfied(boolean state) { 682 return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, state); 683 } 684 685 boolean setUnmeteredConstraintSatisfied(boolean state) { 686 return setConstraintSatisfied(CONSTRAINT_UNMETERED, state); 687 } 688 689 boolean setMeteredConstraintSatisfied(boolean state) { 690 return setConstraintSatisfied(CONSTRAINT_METERED, state); 691 } 692 693 boolean setNotRoamingConstraintSatisfied(boolean state) { 694 return setConstraintSatisfied(CONSTRAINT_NOT_ROAMING, state); 695 } 696 697 boolean setAppNotIdleConstraintSatisfied(boolean state) { 698 return setConstraintSatisfied(CONSTRAINT_APP_NOT_IDLE, state); 699 } 700 701 boolean setContentTriggerConstraintSatisfied(boolean state) { 702 return setConstraintSatisfied(CONSTRAINT_CONTENT_TRIGGER, state); 703 } 704 705 boolean setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted) { 706 dozeWhitelisted = whitelisted; 707 return setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state); 708 } 709 710 boolean setConstraintSatisfied(int constraint, boolean state) { 711 boolean old = (satisfiedConstraints&constraint) != 0; 712 if (old == state) { 713 return false; 714 } 715 satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0); 716 return true; 717 } 718 719 boolean isConstraintSatisfied(int constraint) { 720 return (satisfiedConstraints&constraint) != 0; 721 } 722 723 boolean clearTrackingController(int which) { 724 if ((trackingControllers&which) != 0) { 725 trackingControllers &= ~which; 726 return true; 727 } 728 return false; 729 } 730 731 void setTrackingController(int which) { 732 trackingControllers |= which; 733 } 734 735 public long getLastSuccessfulRunTime() { 736 return mLastSuccessfulRunTime; 737 } 738 739 public long getLastFailedRunTime() { 740 return mLastFailedRunTime; 741 } 742 743 public boolean shouldDump(int filterUid) { 744 return filterUid == -1 || UserHandle.getAppId(getUid()) == filterUid 745 || UserHandle.getAppId(getSourceUid()) == filterUid; 746 } 747 748 /** 749 * @return Whether or not this job is ready to run, based on its requirements. This is true if 750 * the constraints are satisfied <strong>or</strong> the deadline on the job has expired. 751 * TODO: This function is called a *lot*. We should probably just have it check an 752 * already-computed boolean, which we updated whenever we see one of the states it depends 753 * on here change. 754 */ 755 public boolean isReady() { 756 // Deadline constraint trumps other constraints (except for periodic jobs where deadline 757 // is an implementation detail. A periodic job should only run if its constraints are 758 // satisfied). 759 // AppNotIdle implicit constraint must be satisfied 760 // DeviceNotDozing implicit constraint must be satisfied 761 final boolean deadlineSatisfied = (!job.isPeriodic() && hasDeadlineConstraint() 762 && (satisfiedConstraints & CONSTRAINT_DEADLINE) != 0); 763 final boolean notIdle = (satisfiedConstraints & CONSTRAINT_APP_NOT_IDLE) != 0; 764 final boolean notDozing = (satisfiedConstraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0 765 || (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0; 766 return (isConstraintsSatisfied() || deadlineSatisfied) && notIdle && notDozing; 767 } 768 769 static final int CONSTRAINTS_OF_INTEREST = 770 CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW | CONSTRAINT_STORAGE_NOT_LOW | 771 CONSTRAINT_TIMING_DELAY | 772 CONSTRAINT_CONNECTIVITY | CONSTRAINT_UNMETERED | 773 CONSTRAINT_NOT_ROAMING | CONSTRAINT_METERED | 774 CONSTRAINT_IDLE | CONSTRAINT_CONTENT_TRIGGER; 775 776 // Soft override covers all non-"functional" constraints 777 static final int SOFT_OVERRIDE_CONSTRAINTS = 778 CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW | CONSTRAINT_STORAGE_NOT_LOW 779 | CONSTRAINT_TIMING_DELAY | CONSTRAINT_IDLE; 780 781 /** 782 * @return Whether the constraints set on this job are satisfied. 783 */ 784 public boolean isConstraintsSatisfied() { 785 if (overrideState == OVERRIDE_FULL) { 786 // force override: the job is always runnable 787 return true; 788 } 789 790 final int req = requiredConstraints & CONSTRAINTS_OF_INTEREST; 791 792 int sat = satisfiedConstraints & CONSTRAINTS_OF_INTEREST; 793 if (overrideState == OVERRIDE_SOFT) { 794 // override: pretend all 'soft' requirements are satisfied 795 sat |= (requiredConstraints & SOFT_OVERRIDE_CONSTRAINTS); 796 } 797 798 return (sat & req) == req; 799 } 800 801 public boolean matches(int uid, int jobId) { 802 return this.job.getId() == jobId && this.callingUid == uid; 803 } 804 805 @Override 806 public String toString() { 807 StringBuilder sb = new StringBuilder(128); 808 sb.append("JobStatus{"); 809 sb.append(Integer.toHexString(System.identityHashCode(this))); 810 sb.append(" #"); 811 UserHandle.formatUid(sb, callingUid); 812 sb.append("/"); 813 sb.append(job.getId()); 814 sb.append(' '); 815 sb.append(batteryName); 816 sb.append(" u="); 817 sb.append(getUserId()); 818 sb.append(" s="); 819 sb.append(getSourceUid()); 820 if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME 821 || latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) { 822 long now = SystemClock.elapsedRealtime(); 823 sb.append(" TIME="); 824 formatRunTime(sb, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, now); 825 sb.append(":"); 826 formatRunTime(sb, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, now); 827 } 828 if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) { 829 sb.append(" NET="); 830 sb.append(job.getNetworkType()); 831 } 832 if (job.isRequireCharging()) { 833 sb.append(" CHARGING"); 834 } 835 if (job.isRequireBatteryNotLow()) { 836 sb.append(" BATNOTLOW"); 837 } 838 if (job.isRequireStorageNotLow()) { 839 sb.append(" STORENOTLOW"); 840 } 841 if (job.isRequireDeviceIdle()) { 842 sb.append(" IDLE"); 843 } 844 if (job.isPeriodic()) { 845 sb.append(" PERIODIC"); 846 } 847 if (job.isPersisted()) { 848 sb.append(" PERSISTED"); 849 } 850 if ((satisfiedConstraints&CONSTRAINT_APP_NOT_IDLE) == 0) { 851 sb.append(" WAIT:APP_NOT_IDLE"); 852 } 853 if ((satisfiedConstraints&CONSTRAINT_DEVICE_NOT_DOZING) == 0) { 854 sb.append(" WAIT:DEV_NOT_DOZING"); 855 } 856 if (job.getTriggerContentUris() != null) { 857 sb.append(" URIS="); 858 sb.append(Arrays.toString(job.getTriggerContentUris())); 859 } 860 if (numFailures != 0) { 861 sb.append(" failures="); 862 sb.append(numFailures); 863 } 864 if (isReady()) { 865 sb.append(" READY"); 866 } 867 sb.append("}"); 868 return sb.toString(); 869 } 870 871 private void formatRunTime(PrintWriter pw, long runtime, long defaultValue, long now) { 872 if (runtime == defaultValue) { 873 pw.print("none"); 874 } else { 875 TimeUtils.formatDuration(runtime - now, pw); 876 } 877 } 878 879 private void formatRunTime(StringBuilder sb, long runtime, long defaultValue, long now) { 880 if (runtime == defaultValue) { 881 sb.append("none"); 882 } else { 883 TimeUtils.formatDuration(runtime - now, sb); 884 } 885 } 886 887 /** 888 * Convenience function to identify a job uniquely without pulling all the data that 889 * {@link #toString()} returns. 890 */ 891 public String toShortString() { 892 StringBuilder sb = new StringBuilder(); 893 sb.append(Integer.toHexString(System.identityHashCode(this))); 894 sb.append(" #"); 895 UserHandle.formatUid(sb, callingUid); 896 sb.append("/"); 897 sb.append(job.getId()); 898 sb.append(' '); 899 sb.append(batteryName); 900 return sb.toString(); 901 } 902 903 /** 904 * Convenience function to identify a job uniquely without pulling all the data that 905 * {@link #toString()} returns. 906 */ 907 public String toShortStringExceptUniqueId() { 908 StringBuilder sb = new StringBuilder(); 909 sb.append(Integer.toHexString(System.identityHashCode(this))); 910 sb.append(' '); 911 sb.append(batteryName); 912 return sb.toString(); 913 } 914 915 void dumpConstraints(PrintWriter pw, int constraints) { 916 if ((constraints&CONSTRAINT_CHARGING) != 0) { 917 pw.print(" CHARGING"); 918 } 919 if ((constraints& CONSTRAINT_BATTERY_NOT_LOW) != 0) { 920 pw.print(" BATTERY_NOT_LOW"); 921 } 922 if ((constraints& CONSTRAINT_STORAGE_NOT_LOW) != 0) { 923 pw.print(" STORAGE_NOT_LOW"); 924 } 925 if ((constraints&CONSTRAINT_TIMING_DELAY) != 0) { 926 pw.print(" TIMING_DELAY"); 927 } 928 if ((constraints&CONSTRAINT_DEADLINE) != 0) { 929 pw.print(" DEADLINE"); 930 } 931 if ((constraints&CONSTRAINT_IDLE) != 0) { 932 pw.print(" IDLE"); 933 } 934 if ((constraints&CONSTRAINT_CONNECTIVITY) != 0) { 935 pw.print(" CONNECTIVITY"); 936 } 937 if ((constraints&CONSTRAINT_UNMETERED) != 0) { 938 pw.print(" UNMETERED"); 939 } 940 if ((constraints&CONSTRAINT_NOT_ROAMING) != 0) { 941 pw.print(" NOT_ROAMING"); 942 } 943 if ((constraints&CONSTRAINT_METERED) != 0) { 944 pw.print(" METERED"); 945 } 946 if ((constraints&CONSTRAINT_APP_NOT_IDLE) != 0) { 947 pw.print(" APP_NOT_IDLE"); 948 } 949 if ((constraints&CONSTRAINT_CONTENT_TRIGGER) != 0) { 950 pw.print(" CONTENT_TRIGGER"); 951 } 952 if ((constraints&CONSTRAINT_DEVICE_NOT_DOZING) != 0) { 953 pw.print(" DEVICE_NOT_DOZING"); 954 } 955 } 956 957 private void dumpJobWorkItem(PrintWriter pw, String prefix, JobWorkItem work, int index) { 958 pw.print(prefix); pw.print(" #"); pw.print(index); pw.print(": #"); 959 pw.print(work.getWorkId()); pw.print(" "); pw.print(work.getDeliveryCount()); 960 pw.print("x "); pw.println(work.getIntent()); 961 if (work.getGrants() != null) { 962 pw.print(prefix); pw.println(" URI grants:"); 963 ((GrantedUriPermissions)work.getGrants()).dump(pw, prefix + " "); 964 } 965 } 966 967 // Dumpsys infrastructure 968 public void dump(PrintWriter pw, String prefix, boolean full, long elapsedRealtimeMillis) { 969 pw.print(prefix); UserHandle.formatUid(pw, callingUid); 970 pw.print(" tag="); pw.println(tag); 971 pw.print(prefix); 972 pw.print("Source: uid="); UserHandle.formatUid(pw, getSourceUid()); 973 pw.print(" user="); pw.print(getSourceUserId()); 974 pw.print(" pkg="); pw.println(getSourcePackageName()); 975 if (full) { 976 pw.print(prefix); pw.println("JobInfo:"); 977 pw.print(prefix); pw.print(" Service: "); 978 pw.println(job.getService().flattenToShortString()); 979 if (job.isPeriodic()) { 980 pw.print(prefix); pw.print(" PERIODIC: interval="); 981 TimeUtils.formatDuration(job.getIntervalMillis(), pw); 982 pw.print(" flex="); TimeUtils.formatDuration(job.getFlexMillis(), pw); 983 pw.println(); 984 } 985 if (job.isPersisted()) { 986 pw.print(prefix); pw.println(" PERSISTED"); 987 } 988 if (job.getPriority() != 0) { 989 pw.print(prefix); pw.print(" Priority: "); pw.println(job.getPriority()); 990 } 991 if (job.getFlags() != 0) { 992 pw.print(prefix); pw.print(" Flags: "); 993 pw.println(Integer.toHexString(job.getFlags())); 994 } 995 pw.print(prefix); pw.print(" Requires: charging="); 996 pw.print(job.isRequireCharging()); pw.print(" batteryNotLow="); 997 pw.print(job.isRequireBatteryNotLow()); pw.print(" deviceIdle="); 998 pw.println(job.isRequireDeviceIdle()); 999 if (job.getTriggerContentUris() != null) { 1000 pw.print(prefix); pw.println(" Trigger content URIs:"); 1001 for (int i = 0; i < job.getTriggerContentUris().length; i++) { 1002 JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i]; 1003 pw.print(prefix); pw.print(" "); 1004 pw.print(Integer.toHexString(trig.getFlags())); 1005 pw.print(' '); pw.println(trig.getUri()); 1006 } 1007 if (job.getTriggerContentUpdateDelay() >= 0) { 1008 pw.print(prefix); pw.print(" Trigger update delay: "); 1009 TimeUtils.formatDuration(job.getTriggerContentUpdateDelay(), pw); 1010 pw.println(); 1011 } 1012 if (job.getTriggerContentMaxDelay() >= 0) { 1013 pw.print(prefix); pw.print(" Trigger max delay: "); 1014 TimeUtils.formatDuration(job.getTriggerContentMaxDelay(), pw); 1015 pw.println(); 1016 } 1017 } 1018 if (job.getExtras() != null && !job.getExtras().maybeIsEmpty()) { 1019 pw.print(prefix); pw.print(" Extras: "); 1020 pw.println(job.getExtras().toShortString()); 1021 } 1022 if (job.getTransientExtras() != null && !job.getTransientExtras().maybeIsEmpty()) { 1023 pw.print(prefix); pw.print(" Transient extras: "); 1024 pw.println(job.getTransientExtras().toShortString()); 1025 } 1026 if (job.getClipData() != null) { 1027 pw.print(prefix); pw.print(" Clip data: "); 1028 StringBuilder b = new StringBuilder(128); 1029 job.getClipData().toShortString(b); 1030 pw.println(b); 1031 } 1032 if (uriPerms != null) { 1033 pw.print(prefix); pw.println(" Granted URI permissions:"); 1034 uriPerms.dump(pw, prefix + " "); 1035 } 1036 if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) { 1037 pw.print(prefix); pw.print(" Network type: "); pw.println(job.getNetworkType()); 1038 } 1039 if (job.getMinLatencyMillis() != 0) { 1040 pw.print(prefix); pw.print(" Minimum latency: "); 1041 TimeUtils.formatDuration(job.getMinLatencyMillis(), pw); 1042 pw.println(); 1043 } 1044 if (job.getMaxExecutionDelayMillis() != 0) { 1045 pw.print(prefix); pw.print(" Max execution delay: "); 1046 TimeUtils.formatDuration(job.getMaxExecutionDelayMillis(), pw); 1047 pw.println(); 1048 } 1049 pw.print(prefix); pw.print(" Backoff: policy="); pw.print(job.getBackoffPolicy()); 1050 pw.print(" initial="); TimeUtils.formatDuration(job.getInitialBackoffMillis(), pw); 1051 pw.println(); 1052 if (job.hasEarlyConstraint()) { 1053 pw.print(prefix); pw.println(" Has early constraint"); 1054 } 1055 if (job.hasLateConstraint()) { 1056 pw.print(prefix); pw.println(" Has late constraint"); 1057 } 1058 } 1059 pw.print(prefix); pw.print("Required constraints:"); 1060 dumpConstraints(pw, requiredConstraints); 1061 pw.println(); 1062 if (full) { 1063 pw.print(prefix); pw.print("Satisfied constraints:"); 1064 dumpConstraints(pw, satisfiedConstraints); 1065 pw.println(); 1066 pw.print(prefix); pw.print("Unsatisfied constraints:"); 1067 dumpConstraints(pw, (requiredConstraints & ~satisfiedConstraints)); 1068 pw.println(); 1069 if (dozeWhitelisted) { 1070 pw.print(prefix); pw.println("Doze whitelisted: true"); 1071 } 1072 } 1073 if (trackingControllers != 0) { 1074 pw.print(prefix); pw.print("Tracking:"); 1075 if ((trackingControllers&TRACKING_BATTERY) != 0) pw.print(" BATTERY"); 1076 if ((trackingControllers&TRACKING_CONNECTIVITY) != 0) pw.print(" CONNECTIVITY"); 1077 if ((trackingControllers&TRACKING_CONTENT) != 0) pw.print(" CONTENT"); 1078 if ((trackingControllers&TRACKING_IDLE) != 0) pw.print(" IDLE"); 1079 if ((trackingControllers&TRACKING_STORAGE) != 0) pw.print(" STORAGE"); 1080 if ((trackingControllers&TRACKING_TIME) != 0) pw.print(" TIME"); 1081 pw.println(); 1082 } 1083 if (changedAuthorities != null) { 1084 pw.print(prefix); pw.println("Changed authorities:"); 1085 for (int i=0; i<changedAuthorities.size(); i++) { 1086 pw.print(prefix); pw.print(" "); pw.println(changedAuthorities.valueAt(i)); 1087 } 1088 if (changedUris != null) { 1089 pw.print(prefix); pw.println("Changed URIs:"); 1090 for (int i=0; i<changedUris.size(); i++) { 1091 pw.print(prefix); pw.print(" "); pw.println(changedUris.valueAt(i)); 1092 } 1093 } 1094 } 1095 if (pendingWork != null && pendingWork.size() > 0) { 1096 pw.print(prefix); pw.println("Pending work:"); 1097 for (int i = 0; i < pendingWork.size(); i++) { 1098 dumpJobWorkItem(pw, prefix, pendingWork.get(i), i); 1099 } 1100 } 1101 if (executingWork != null && executingWork.size() > 0) { 1102 pw.print(prefix); pw.println("Executing work:"); 1103 for (int i = 0; i < executingWork.size(); i++) { 1104 dumpJobWorkItem(pw, prefix, executingWork.get(i), i); 1105 } 1106 } 1107 pw.print(prefix); pw.print("Enqueue time: "); 1108 TimeUtils.formatDuration(enqueueTime, elapsedRealtimeMillis, pw); 1109 pw.println(); 1110 pw.print(prefix); pw.print("Run time: earliest="); 1111 formatRunTime(pw, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, elapsedRealtimeMillis); 1112 pw.print(", latest="); 1113 formatRunTime(pw, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, elapsedRealtimeMillis); 1114 pw.println(); 1115 if (numFailures != 0) { 1116 pw.print(prefix); pw.print("Num failures: "); pw.println(numFailures); 1117 } 1118 final Time t = new Time(); 1119 final String format = "%Y-%m-%d %H:%M:%S"; 1120 if (mLastSuccessfulRunTime != 0) { 1121 pw.print(prefix); pw.print("Last successful run: "); 1122 t.set(mLastSuccessfulRunTime); 1123 pw.println(t.format(format)); 1124 } 1125 if (mLastFailedRunTime != 0) { 1126 pw.print(prefix); pw.print("Last failed run: "); 1127 t.set(mLastFailedRunTime); 1128 pw.println(t.format(format)); 1129 } 1130 } 1131 } 1132