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 static com.android.server.job.JobSchedulerService.ACTIVE_INDEX; 20 import static com.android.server.job.JobSchedulerService.EXEMPTED_INDEX; 21 import static com.android.server.job.JobSchedulerService.NEVER_INDEX; 22 import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; 23 import static com.android.server.job.JobSchedulerService.WORKING_INDEX; 24 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; 25 26 import android.annotation.ElapsedRealtimeLong; 27 import android.app.AppGlobals; 28 import android.app.job.JobInfo; 29 import android.app.job.JobParameters; 30 import android.app.job.JobWorkItem; 31 import android.content.ClipData; 32 import android.content.ComponentName; 33 import android.content.pm.ServiceInfo; 34 import android.net.Network; 35 import android.net.NetworkRequest; 36 import android.net.Uri; 37 import android.os.RemoteException; 38 import android.os.UserHandle; 39 import android.provider.MediaStore; 40 import android.text.format.DateFormat; 41 import android.util.ArraySet; 42 import android.util.IndentingPrintWriter; 43 import android.util.Pair; 44 import android.util.Range; 45 import android.util.Slog; 46 import android.util.TimeUtils; 47 import android.util.proto.ProtoOutputStream; 48 49 import com.android.internal.util.ArrayUtils; 50 import com.android.internal.util.FrameworkStatsLog; 51 import com.android.server.LocalServices; 52 import com.android.server.job.GrantedUriPermissions; 53 import com.android.server.job.JobSchedulerInternal; 54 import com.android.server.job.JobSchedulerService; 55 import com.android.server.job.JobServerProtoEnums; 56 import com.android.server.job.JobStatusDumpProto; 57 import com.android.server.job.JobStatusShortInfoProto; 58 59 import dalvik.annotation.optimization.NeverCompile; 60 61 import java.io.PrintWriter; 62 import java.util.ArrayList; 63 import java.util.Arrays; 64 import java.util.Collections; 65 import java.util.function.Predicate; 66 67 /** 68 * Uniquely identifies a job internally. 69 * Created from the public {@link android.app.job.JobInfo} object when it lands on the scheduler. 70 * Contains current state of the requirements of the job, as well as a function to evaluate 71 * whether it's ready to run. 72 * This object is shared among the various controllers - hence why the different fields are atomic. 73 * This isn't strictly necessary because each controller is only interested in a specific field, 74 * and the receivers that are listening for global state change will all run on the main looper, 75 * but we don't enforce that so this is safer. 76 * 77 * Test: atest com.android.server.job.controllers.JobStatusTest 78 * @hide 79 */ 80 public final class JobStatus { 81 private static final String TAG = "JobScheduler.JobStatus"; 82 static final boolean DEBUG = JobSchedulerService.DEBUG; 83 84 private static final int NUM_CONSTRAINT_CHANGE_HISTORY = 10; 85 86 public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE; 87 public static final long NO_EARLIEST_RUNTIME = 0L; 88 89 static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; // 1 < 0 90 static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE; // 1 << 2 91 static final int CONSTRAINT_BATTERY_NOT_LOW = JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1 92 static final int CONSTRAINT_STORAGE_NOT_LOW = JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW; // 1 << 3 93 static final int CONSTRAINT_TIMING_DELAY = 1<<31; 94 static final int CONSTRAINT_DEADLINE = 1<<30; 95 static final int CONSTRAINT_CONNECTIVITY = 1 << 28; 96 static final int CONSTRAINT_TARE_WEALTH = 1 << 27; // Implicit constraint 97 static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26; 98 static final int CONSTRAINT_DEVICE_NOT_DOZING = 1 << 25; // Implicit constraint 99 static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24; // Implicit constraint 100 static final int CONSTRAINT_PREFETCH = 1 << 23; 101 static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1 << 22; // Implicit constraint 102 103 // The following set of dynamic constraints are for specific use cases (as explained in their 104 // relative naming and comments). Right now, they apply different constraints, which is fine, 105 // but if in the future, we have overlapping dynamic constraint sets, removing one constraint 106 // set may accidentally remove a constraint applied by another dynamic set. 107 // TODO: properly handle overlapping dynamic constraint sets 108 109 /** 110 * The additional set of dynamic constraints that must be met if the job's effective bucket is 111 * {@link JobSchedulerService#RESTRICTED_INDEX}. Connectivity can be ignored if the job doesn't 112 * need network. 113 */ 114 private static final int DYNAMIC_RESTRICTED_CONSTRAINTS = 115 CONSTRAINT_BATTERY_NOT_LOW 116 | CONSTRAINT_CHARGING 117 | CONSTRAINT_CONNECTIVITY 118 | CONSTRAINT_IDLE; 119 120 /** 121 * The additional set of dynamic constraints that must be met if this is an expedited job that 122 * had a long enough run while the device was Dozing or in battery saver. 123 */ 124 private static final int DYNAMIC_EXPEDITED_DEFERRAL_CONSTRAINTS = 125 CONSTRAINT_DEVICE_NOT_DOZING | CONSTRAINT_BACKGROUND_NOT_RESTRICTED; 126 127 /** 128 * Standard media URIs that contain the media files that might be important to the user. 129 * @see #mHasMediaBackupExemption 130 */ 131 private static final Uri[] MEDIA_URIS_FOR_STANDBY_EXEMPTION = { 132 MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 133 MediaStore.Video.Media.EXTERNAL_CONTENT_URI, 134 }; 135 136 /** 137 * The constraints that we want to log to statsd. 138 * 139 * Constraints that can be inferred from other atoms have been excluded to avoid logging too 140 * much information and to reduce redundancy: 141 * 142 * * CONSTRAINT_CHARGING can be inferred with PluggedStateChanged (Atom #32) 143 * * CONSTRAINT_BATTERY_NOT_LOW can be inferred with BatteryLevelChanged (Atom #30) 144 * * CONSTRAINT_CONNECTIVITY can be partially inferred with ConnectivityStateChanged 145 * (Atom #98) and BatterySaverModeStateChanged (Atom #20). 146 * * CONSTRAINT_DEVICE_NOT_DOZING can be mostly inferred with DeviceIdleModeStateChanged 147 * (Atom #21) 148 * * CONSTRAINT_BACKGROUND_NOT_RESTRICTED can be inferred with BatterySaverModeStateChanged 149 * (Atom #20) 150 * * CONSTRAINT_STORAGE_NOT_LOW can be inferred with LowStorageStateChanged (Atom #130) 151 */ 152 private static final int STATSD_CONSTRAINTS_TO_LOG = CONSTRAINT_CONTENT_TRIGGER 153 | CONSTRAINT_DEADLINE 154 | CONSTRAINT_PREFETCH 155 | CONSTRAINT_TARE_WEALTH 156 | CONSTRAINT_TIMING_DELAY 157 | CONSTRAINT_WITHIN_QUOTA; 158 159 // TODO(b/129954980): ensure this doesn't spam statsd, especially at boot 160 private static final boolean STATS_LOG_ENABLED = false; 161 162 // No override. 163 public static final int OVERRIDE_NONE = 0; 164 // Override to improve sorting order. Does not affect constraint evaluation. 165 public static final int OVERRIDE_SORTING = 1; 166 // Soft override: ignore constraints like time that don't affect API availability 167 public static final int OVERRIDE_SOFT = 2; 168 // Full override: ignore all constraints including API-affecting like connectivity 169 public static final int OVERRIDE_FULL = 3; 170 171 /** If not specified, trigger update delay is 10 seconds. */ 172 public static final long DEFAULT_TRIGGER_UPDATE_DELAY = 10*1000; 173 174 /** The minimum possible update delay is 1/2 second. */ 175 public static final long MIN_TRIGGER_UPDATE_DELAY = 500; 176 177 /** If not specified, trigger maximum delay is 2 minutes. */ 178 public static final long DEFAULT_TRIGGER_MAX_DELAY = 2*60*1000; 179 180 /** The minimum possible update delay is 1 second. */ 181 public static final long MIN_TRIGGER_MAX_DELAY = 1000; 182 183 final JobInfo job; 184 /** 185 * Uid of the package requesting this job. This can differ from the "source" 186 * uid when the job was scheduled on the app's behalf, such as with the jobs 187 * that underly Sync Manager operation. 188 */ 189 final int callingUid; 190 final String batteryName; 191 192 /** 193 * Identity of the app in which the job is hosted. 194 */ 195 final String sourcePackageName; 196 final int sourceUserId; 197 final int sourceUid; 198 final String sourceTag; 199 200 final String tag; 201 202 private GrantedUriPermissions uriPerms; 203 private boolean prepared; 204 205 static final boolean DEBUG_PREPARE = true; 206 private Throwable unpreparedPoint = null; 207 208 /** 209 * Earliest point in the future at which this job will be eligible to run. A value of 0 210 * indicates there is no delay constraint. See {@link #hasTimingDelayConstraint()}. 211 */ 212 private final long earliestRunTimeElapsedMillis; 213 /** 214 * Latest point in the future at which this job must be run. A value of {@link Long#MAX_VALUE} 215 * indicates there is no deadline constraint. See {@link #hasDeadlineConstraint()}. 216 */ 217 private final long latestRunTimeElapsedMillis; 218 219 /** 220 * Valid only for periodic jobs. The original latest point in the future at which this 221 * job was expected to run. 222 */ 223 private long mOriginalLatestRunTimeElapsedMillis; 224 225 /** How many times this job has failed, used to compute back-off. */ 226 private final int numFailures; 227 228 /** 229 * Which app standby bucket this job's app is in. Updated when the app is moved to a 230 * different bucket. 231 */ 232 private int standbyBucket; 233 234 /** 235 * Whether we've logged an error due to standby bucket mismatch with active uid state. 236 */ 237 private boolean mLoggedBucketMismatch; 238 239 /** 240 * Debugging: timestamp if we ever defer this job based on standby bucketing, this 241 * is when we did so. 242 */ 243 private long whenStandbyDeferred; 244 245 /** The first time this job was force batched. */ 246 private long mFirstForceBatchedTimeElapsed; 247 248 // Constraints. 249 final int requiredConstraints; 250 private final int mRequiredConstraintsOfInterest; 251 int satisfiedConstraints = 0; 252 private int mSatisfiedConstraintsOfInterest = 0; 253 /** 254 * Set of constraints that must be satisfied for the job if/because it's in the RESTRICTED 255 * bucket. 256 */ 257 private int mDynamicConstraints = 0; 258 259 /** 260 * Indicates whether the job is responsible for backing up media, so we can be lenient in 261 * applying standby throttling. 262 * 263 * Doesn't exempt jobs with a deadline constraint, as they can be started without any content or 264 * network changes, in which case this exemption does not make sense. 265 */ 266 private boolean mHasMediaBackupExemption; 267 private final boolean mHasExemptedMediaUrisOnly; 268 269 // Set to true if doze constraint was satisfied due to app being whitelisted. 270 boolean appHasDozeExemption; 271 272 // Set to true when the app is "active" per AppStateTracker 273 public boolean uidActive; 274 275 /** 276 * Flag for {@link #trackingControllers}: the battery controller is currently tracking this job. 277 */ 278 public static final int TRACKING_BATTERY = 1<<0; 279 /** 280 * Flag for {@link #trackingControllers}: the network connectivity controller is currently 281 * tracking this job. 282 */ 283 public static final int TRACKING_CONNECTIVITY = 1<<1; 284 /** 285 * Flag for {@link #trackingControllers}: the content observer controller is currently 286 * tracking this job. 287 */ 288 public static final int TRACKING_CONTENT = 1<<2; 289 /** 290 * Flag for {@link #trackingControllers}: the idle controller is currently tracking this job. 291 */ 292 public static final int TRACKING_IDLE = 1<<3; 293 /** 294 * Flag for {@link #trackingControllers}: the storage controller is currently tracking this job. 295 */ 296 public static final int TRACKING_STORAGE = 1<<4; 297 /** 298 * Flag for {@link #trackingControllers}: the time controller is currently tracking this job. 299 */ 300 public static final int TRACKING_TIME = 1<<5; 301 /** 302 * Flag for {@link #trackingControllers}: the quota controller is currently tracking this job. 303 */ 304 public static final int TRACKING_QUOTA = 1 << 6; 305 306 /** 307 * Bit mask of controllers that are currently tracking the job. 308 */ 309 private int trackingControllers; 310 311 /** 312 * Flag for {@link #mInternalFlags}: this job was scheduled when the app that owns the job 313 * service (not necessarily the caller) was in the foreground and the job has no time 314 * constraints, which makes it exempted from the battery saver job restriction. 315 * 316 * @hide 317 */ 318 public static final int INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION = 1 << 0; 319 320 /** 321 * Versatile, persistable flags for a job that's updated within the system server, 322 * as opposed to {@link JobInfo#flags} that's set by callers. 323 */ 324 private int mInternalFlags; 325 326 // These are filled in by controllers when preparing for execution. 327 public ArraySet<Uri> changedUris; 328 public ArraySet<String> changedAuthorities; 329 public Network network; 330 public ServiceInfo serviceInfo; 331 332 /** The evaluated bias of the job when it started running. */ 333 public int lastEvaluatedBias; 334 335 /** 336 * Whether or not this particular JobStatus instance was treated as an EJ when it started 337 * running. This isn't copied over when a job is rescheduled. 338 */ 339 public boolean startedAsExpeditedJob = false; 340 341 // If non-null, this is work that has been enqueued for the job. 342 public ArrayList<JobWorkItem> pendingWork; 343 344 // If non-null, this is work that is currently being executed. 345 public ArrayList<JobWorkItem> executingWork; 346 347 public int nextPendingWorkId = 1; 348 349 // Used by shell commands 350 public int overrideState = JobStatus.OVERRIDE_NONE; 351 352 // When this job was enqueued, for ordering. (in elapsedRealtimeMillis) 353 @ElapsedRealtimeLong 354 public long enqueueTime; 355 356 // Metrics about queue latency. (in uptimeMillis) 357 public long madePending; 358 public long madeActive; 359 360 /** 361 * Last time a job finished successfully for a periodic job, in the currentTimeMillis time, 362 * for dumpsys. 363 */ 364 private long mLastSuccessfulRunTime; 365 366 /** 367 * Last time a job finished unsuccessfully, in the currentTimeMillis time, for dumpsys. 368 */ 369 private long mLastFailedRunTime; 370 371 /** Whether or not the app is background restricted by the user (FAS). */ 372 private boolean mIsUserBgRestricted; 373 374 /** 375 * Transient: when a job is inflated from disk before we have a reliable RTC clock time, 376 * we retain the canonical (delay, deadline) scheduling tuple read out of the persistent 377 * store in UTC so that we can fix up the job's scheduling criteria once we get a good 378 * wall-clock time. If we have to persist the job again before the clock has been updated, 379 * we record these times again rather than calculating based on the earliest/latest elapsed 380 * time base figures. 381 * 382 * 'first' is the earliest/delay time, and 'second' is the latest/deadline time. 383 */ 384 private Pair<Long, Long> mPersistedUtcTimes; 385 386 private int mConstraintChangeHistoryIndex = 0; 387 private final long[] mConstraintUpdatedTimesElapsed = new long[NUM_CONSTRAINT_CHANGE_HISTORY]; 388 private final int[] mConstraintStatusHistory = new int[NUM_CONSTRAINT_CHANGE_HISTORY]; 389 390 /** 391 * For use only by ContentObserverController: state it is maintaining about content URIs 392 * being observed. 393 */ 394 ContentObserverController.JobInstance contentObserverJobInstance; 395 396 private long mTotalNetworkDownloadBytes = JobInfo.NETWORK_BYTES_UNKNOWN; 397 private long mTotalNetworkUploadBytes = JobInfo.NETWORK_BYTES_UNKNOWN; 398 private long mMinimumNetworkChunkBytes = JobInfo.NETWORK_BYTES_UNKNOWN; 399 400 /** 401 * Whether or not this job is approved to be treated as expedited per quota policy. 402 */ 403 private boolean mExpeditedQuotaApproved; 404 /** 405 * Whether or not this job is approved to be treated as expedited per TARE policy. 406 */ 407 private boolean mExpeditedTareApproved; 408 409 /////// Booleans that track if a job is ready to run. They should be updated whenever dependent 410 /////// states change. 411 412 /** 413 * The deadline for the job has passed. This is only good for non-periodic jobs. A periodic job 414 * should only run if its constraints are satisfied. 415 * Computed as: NOT periodic AND has deadline constraint AND deadline constraint satisfied. 416 */ 417 private boolean mReadyDeadlineSatisfied; 418 419 /** 420 * The device isn't Dozing or this job is exempt from Dozing (eg. it will be in the foreground 421 * or will run as an expedited job). This implicit constraint must be satisfied. 422 */ 423 private boolean mReadyNotDozing; 424 425 /** 426 * The job is not restricted from running in the background (due to Battery Saver). This 427 * implicit constraint must be satisfied. 428 */ 429 private boolean mReadyNotRestrictedInBg; 430 431 /** The job is within its quota based on its standby bucket. */ 432 private boolean mReadyWithinQuota; 433 434 /** The job has enough credits to run based on TARE. */ 435 private boolean mReadyTareWealth; 436 437 /** The job's dynamic requirements have been satisfied. */ 438 private boolean mReadyDynamicSatisfied; 439 440 /** The reason a job most recently went from ready to not ready. */ 441 private int mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED; 442 443 /** 444 * Core constructor for JobStatus instances. All other ctors funnel down to this one. 445 * 446 * @param job The actual requested parameters for the job 447 * @param callingUid Identity of the app that is scheduling the job. This may not be the 448 * app in which the job is implemented; such as with sync jobs. 449 * @param sourcePackageName The package name of the app in which the job will run. 450 * @param sourceUserId The user in which the job will run 451 * @param standbyBucket The standby bucket that the source package is currently assigned to, 452 * cached here for speed of handling during runnability evaluations (and updated when bucket 453 * assignments are changed) 454 * @param tag A string associated with the job for debugging/logging purposes. 455 * @param numFailures Count of how many times this job has requested a reschedule because 456 * its work was not yet finished. 457 * @param earliestRunTimeElapsedMillis Milestone: earliest point in time at which the job 458 * is to be considered runnable 459 * @param latestRunTimeElapsedMillis Milestone: point in time at which the job will be 460 * considered overdue 461 * @param lastSuccessfulRunTime When did we last run this job to completion? 462 * @param lastFailedRunTime When did we last run this job only to have it stop incomplete? 463 * @param internalFlags Non-API property flags about this job 464 */ JobStatus(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId, int standbyBucket, String tag, int numFailures, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, long lastSuccessfulRunTime, long lastFailedRunTime, int internalFlags, int dynamicConstraints)465 private JobStatus(JobInfo job, int callingUid, String sourcePackageName, 466 int sourceUserId, int standbyBucket, String tag, int numFailures, 467 long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, 468 long lastSuccessfulRunTime, long lastFailedRunTime, int internalFlags, 469 int dynamicConstraints) { 470 this.job = job; 471 this.callingUid = callingUid; 472 this.standbyBucket = standbyBucket; 473 474 int tempSourceUid = -1; 475 if (sourceUserId != -1 && sourcePackageName != null) { 476 try { 477 tempSourceUid = AppGlobals.getPackageManager().getPackageUid(sourcePackageName, 0, 478 sourceUserId); 479 } catch (RemoteException ex) { 480 // Can't happen, PackageManager runs in the same process. 481 } 482 } 483 if (tempSourceUid == -1) { 484 this.sourceUid = callingUid; 485 this.sourceUserId = UserHandle.getUserId(callingUid); 486 this.sourcePackageName = job.getService().getPackageName(); 487 this.sourceTag = null; 488 } else { 489 this.sourceUid = tempSourceUid; 490 this.sourceUserId = sourceUserId; 491 this.sourcePackageName = sourcePackageName; 492 this.sourceTag = tag; 493 } 494 495 this.batteryName = this.sourceTag != null 496 ? this.sourceTag + ":" + job.getService().getPackageName() 497 : job.getService().flattenToShortString(); 498 this.tag = "*job*/" + this.batteryName + "#" + job.getId(); 499 500 this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis; 501 this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis; 502 this.mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsedMillis; 503 this.numFailures = numFailures; 504 505 int requiredConstraints = job.getConstraintFlags(); 506 if (job.getRequiredNetwork() != null) { 507 requiredConstraints |= CONSTRAINT_CONNECTIVITY; 508 } 509 if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME) { 510 requiredConstraints |= CONSTRAINT_TIMING_DELAY; 511 } 512 if (latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) { 513 requiredConstraints |= CONSTRAINT_DEADLINE; 514 } 515 if (job.isPrefetch()) { 516 requiredConstraints |= CONSTRAINT_PREFETCH; 517 } 518 boolean exemptedMediaUrisOnly = false; 519 if (job.getTriggerContentUris() != null) { 520 requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER; 521 exemptedMediaUrisOnly = true; 522 for (JobInfo.TriggerContentUri uri : job.getTriggerContentUris()) { 523 if (!ArrayUtils.contains(MEDIA_URIS_FOR_STANDBY_EXEMPTION, uri.getUri())) { 524 exemptedMediaUrisOnly = false; 525 break; 526 } 527 } 528 } 529 mHasExemptedMediaUrisOnly = exemptedMediaUrisOnly; 530 this.requiredConstraints = requiredConstraints; 531 mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST; 532 addDynamicConstraints(dynamicConstraints); 533 mReadyNotDozing = canRunInDoze(); 534 if (standbyBucket == RESTRICTED_INDEX) { 535 addDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS); 536 } else { 537 mReadyDynamicSatisfied = false; 538 } 539 540 mLastSuccessfulRunTime = lastSuccessfulRunTime; 541 mLastFailedRunTime = lastFailedRunTime; 542 543 mInternalFlags = internalFlags; 544 545 updateNetworkBytesLocked(); 546 547 if (job.getRequiredNetwork() != null) { 548 // Later, when we check if a given network satisfies the required 549 // network, we need to know the UID that is requesting it, so push 550 // our source UID into place. 551 final JobInfo.Builder builder = new JobInfo.Builder(job); 552 final NetworkRequest.Builder requestBuilder = 553 new NetworkRequest.Builder(job.getRequiredNetwork()); 554 requestBuilder.setUids( 555 Collections.singleton(new Range<Integer>(this.sourceUid, this.sourceUid))); 556 builder.setRequiredNetwork(requestBuilder.build()); 557 // Don't perform prefetch-deadline check at this point. We've already passed the 558 // initial validation check. 559 job = builder.build(false); 560 } 561 562 updateMediaBackupExemptionStatus(); 563 } 564 565 /** Copy constructor: used specifically when cloning JobStatus objects for persistence, 566 * so we preserve RTC window bounds if the source object has them. */ JobStatus(JobStatus jobStatus)567 public JobStatus(JobStatus jobStatus) { 568 this(jobStatus.getJob(), jobStatus.getUid(), 569 jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(), 570 jobStatus.getStandbyBucket(), 571 jobStatus.getSourceTag(), jobStatus.getNumFailures(), 572 jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed(), 573 jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime(), 574 jobStatus.getInternalFlags(), jobStatus.mDynamicConstraints); 575 mPersistedUtcTimes = jobStatus.mPersistedUtcTimes; 576 if (jobStatus.mPersistedUtcTimes != null) { 577 if (DEBUG) { 578 Slog.i(TAG, "Cloning job with persisted run times", new RuntimeException("here")); 579 } 580 } 581 } 582 583 /** 584 * Create a new JobStatus that was loaded from disk. We ignore the provided 585 * {@link android.app.job.JobInfo} time criteria because we can load a persisted periodic job 586 * from the {@link com.android.server.job.JobStore} and still want to respect its 587 * wallclock runtime rather than resetting it on every boot. 588 * We consider a freshly loaded job to no longer be in back-off, and the associated 589 * standby bucket is whatever the OS thinks it should be at this moment. 590 */ JobStatus(JobInfo job, int callingUid, String sourcePkgName, int sourceUserId, int standbyBucket, String sourceTag, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, long lastSuccessfulRunTime, long lastFailedRunTime, Pair<Long, Long> persistedExecutionTimesUTC, int innerFlags, int dynamicConstraints)591 public JobStatus(JobInfo job, int callingUid, String sourcePkgName, int sourceUserId, 592 int standbyBucket, String sourceTag, 593 long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, 594 long lastSuccessfulRunTime, long lastFailedRunTime, 595 Pair<Long, Long> persistedExecutionTimesUTC, 596 int innerFlags, int dynamicConstraints) { 597 this(job, callingUid, sourcePkgName, sourceUserId, 598 standbyBucket, 599 sourceTag, 0, 600 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 601 lastSuccessfulRunTime, lastFailedRunTime, innerFlags, dynamicConstraints); 602 603 // Only during initial inflation do we record the UTC-timebase execution bounds 604 // read from the persistent store. If we ever have to recreate the JobStatus on 605 // the fly, it means we're rescheduling the job; and this means that the calculated 606 // elapsed timebase bounds intrinsically become correct. 607 this.mPersistedUtcTimes = persistedExecutionTimesUTC; 608 if (persistedExecutionTimesUTC != null) { 609 if (DEBUG) { 610 Slog.i(TAG, "+ restored job with RTC times because of bad boot clock"); 611 } 612 } 613 } 614 615 /** Create a new job to be rescheduled with the provided parameters. */ JobStatus(JobStatus rescheduling, long newEarliestRuntimeElapsedMillis, long newLatestRuntimeElapsedMillis, int backoffAttempt, long lastSuccessfulRunTime, long lastFailedRunTime)616 public JobStatus(JobStatus rescheduling, 617 long newEarliestRuntimeElapsedMillis, 618 long newLatestRuntimeElapsedMillis, int backoffAttempt, 619 long lastSuccessfulRunTime, long lastFailedRunTime) { 620 this(rescheduling.job, rescheduling.getUid(), 621 rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(), 622 rescheduling.getStandbyBucket(), 623 rescheduling.getSourceTag(), backoffAttempt, newEarliestRuntimeElapsedMillis, 624 newLatestRuntimeElapsedMillis, 625 lastSuccessfulRunTime, lastFailedRunTime, rescheduling.getInternalFlags(), 626 rescheduling.mDynamicConstraints); 627 } 628 629 /** 630 * Create a newly scheduled job. 631 * @param callingUid Uid of the package that scheduled this job. 632 * @param sourcePkg Package name of the app that will actually run the job. Null indicates 633 * that the calling package is the source. 634 * @param sourceUserId User id for whom this job is scheduled. -1 indicates this is same as the 635 * caller. 636 */ createFromJobInfo(JobInfo job, int callingUid, String sourcePkg, int sourceUserId, String tag)637 public static JobStatus createFromJobInfo(JobInfo job, int callingUid, String sourcePkg, 638 int sourceUserId, String tag) { 639 final long elapsedNow = sElapsedRealtimeClock.millis(); 640 final long earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis; 641 if (job.isPeriodic()) { 642 // Make sure period is in the interval [min_possible_period, max_possible_period]. 643 final long period = Math.max(JobInfo.getMinPeriodMillis(), 644 Math.min(JobSchedulerService.MAX_ALLOWED_PERIOD_MS, job.getIntervalMillis())); 645 latestRunTimeElapsedMillis = elapsedNow + period; 646 earliestRunTimeElapsedMillis = latestRunTimeElapsedMillis 647 // Make sure flex is in the interval [min_possible_flex, period]. 648 - Math.max(JobInfo.getMinFlexMillis(), Math.min(period, job.getFlexMillis())); 649 } else { 650 earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ? 651 elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME; 652 latestRunTimeElapsedMillis = job.hasLateConstraint() ? 653 elapsedNow + job.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME; 654 } 655 String jobPackage = (sourcePkg != null) ? sourcePkg : job.getService().getPackageName(); 656 657 int standbyBucket = JobSchedulerService.standbyBucketForPackage(jobPackage, 658 sourceUserId, elapsedNow); 659 return new JobStatus(job, callingUid, sourcePkg, sourceUserId, 660 standbyBucket, tag, 0, 661 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 662 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */, 663 /*innerFlags=*/ 0, /* dynamicConstraints */ 0); 664 } 665 enqueueWorkLocked(JobWorkItem work)666 public void enqueueWorkLocked(JobWorkItem work) { 667 if (pendingWork == null) { 668 pendingWork = new ArrayList<>(); 669 } 670 work.setWorkId(nextPendingWorkId); 671 nextPendingWorkId++; 672 if (work.getIntent() != null 673 && GrantedUriPermissions.checkGrantFlags(work.getIntent().getFlags())) { 674 work.setGrants(GrantedUriPermissions.createFromIntent(work.getIntent(), sourceUid, 675 sourcePackageName, sourceUserId, toShortString())); 676 } 677 pendingWork.add(work); 678 updateNetworkBytesLocked(); 679 } 680 dequeueWorkLocked()681 public JobWorkItem dequeueWorkLocked() { 682 if (pendingWork != null && pendingWork.size() > 0) { 683 JobWorkItem work = pendingWork.remove(0); 684 if (work != null) { 685 if (executingWork == null) { 686 executingWork = new ArrayList<>(); 687 } 688 executingWork.add(work); 689 work.bumpDeliveryCount(); 690 } 691 updateNetworkBytesLocked(); 692 return work; 693 } 694 return null; 695 } 696 hasWorkLocked()697 public boolean hasWorkLocked() { 698 return (pendingWork != null && pendingWork.size() > 0) || hasExecutingWorkLocked(); 699 } 700 hasExecutingWorkLocked()701 public boolean hasExecutingWorkLocked() { 702 return executingWork != null && executingWork.size() > 0; 703 } 704 ungrantWorkItem(JobWorkItem work)705 private static void ungrantWorkItem(JobWorkItem work) { 706 if (work.getGrants() != null) { 707 ((GrantedUriPermissions)work.getGrants()).revoke(); 708 } 709 } 710 completeWorkLocked(int workId)711 public boolean completeWorkLocked(int workId) { 712 if (executingWork != null) { 713 final int N = executingWork.size(); 714 for (int i = 0; i < N; i++) { 715 JobWorkItem work = executingWork.get(i); 716 if (work.getWorkId() == workId) { 717 executingWork.remove(i); 718 ungrantWorkItem(work); 719 return true; 720 } 721 } 722 } 723 return false; 724 } 725 ungrantWorkList(ArrayList<JobWorkItem> list)726 private static void ungrantWorkList(ArrayList<JobWorkItem> list) { 727 if (list != null) { 728 final int N = list.size(); 729 for (int i = 0; i < N; i++) { 730 ungrantWorkItem(list.get(i)); 731 } 732 } 733 } 734 stopTrackingJobLocked(JobStatus incomingJob)735 public void stopTrackingJobLocked(JobStatus incomingJob) { 736 if (incomingJob != null) { 737 // We are replacing with a new job -- transfer the work! We do any executing 738 // work first, since that was originally at the front of the pending work. 739 if (executingWork != null && executingWork.size() > 0) { 740 incomingJob.pendingWork = executingWork; 741 } 742 if (incomingJob.pendingWork == null) { 743 incomingJob.pendingWork = pendingWork; 744 } else if (pendingWork != null && pendingWork.size() > 0) { 745 incomingJob.pendingWork.addAll(pendingWork); 746 } 747 pendingWork = null; 748 executingWork = null; 749 incomingJob.nextPendingWorkId = nextPendingWorkId; 750 incomingJob.updateNetworkBytesLocked(); 751 } else { 752 // We are completely stopping the job... need to clean up work. 753 ungrantWorkList(pendingWork); 754 pendingWork = null; 755 ungrantWorkList(executingWork); 756 executingWork = null; 757 } 758 updateNetworkBytesLocked(); 759 } 760 prepareLocked()761 public void prepareLocked() { 762 if (prepared) { 763 Slog.wtf(TAG, "Already prepared: " + this); 764 return; 765 } 766 prepared = true; 767 if (DEBUG_PREPARE) { 768 unpreparedPoint = null; 769 } 770 final ClipData clip = job.getClipData(); 771 if (clip != null) { 772 uriPerms = GrantedUriPermissions.createFromClip(clip, sourceUid, sourcePackageName, 773 sourceUserId, job.getClipGrantFlags(), toShortString()); 774 } 775 } 776 unprepareLocked()777 public void unprepareLocked() { 778 if (!prepared) { 779 Slog.wtf(TAG, "Hasn't been prepared: " + this); 780 if (DEBUG_PREPARE && unpreparedPoint != null) { 781 Slog.e(TAG, "Was already unprepared at ", unpreparedPoint); 782 } 783 return; 784 } 785 prepared = false; 786 if (DEBUG_PREPARE) { 787 unpreparedPoint = new Throwable().fillInStackTrace(); 788 } 789 if (uriPerms != null) { 790 uriPerms.revoke(); 791 uriPerms = null; 792 } 793 } 794 isPreparedLocked()795 public boolean isPreparedLocked() { 796 return prepared; 797 } 798 getJob()799 public JobInfo getJob() { 800 return job; 801 } 802 getJobId()803 public int getJobId() { 804 return job.getId(); 805 } 806 printUniqueId(PrintWriter pw)807 public void printUniqueId(PrintWriter pw) { 808 UserHandle.formatUid(pw, callingUid); 809 pw.print("/"); 810 pw.print(job.getId()); 811 } 812 getNumFailures()813 public int getNumFailures() { 814 return numFailures; 815 } 816 getServiceComponent()817 public ComponentName getServiceComponent() { 818 return job.getService(); 819 } 820 getSourcePackageName()821 public String getSourcePackageName() { 822 return sourcePackageName; 823 } 824 getSourceUid()825 public int getSourceUid() { 826 return sourceUid; 827 } 828 getSourceUserId()829 public int getSourceUserId() { 830 return sourceUserId; 831 } 832 getUserId()833 public int getUserId() { 834 return UserHandle.getUserId(callingUid); 835 } 836 837 /** 838 * Returns an appropriate standby bucket for the job, taking into account any standby 839 * exemptions. 840 */ getEffectiveStandbyBucket()841 public int getEffectiveStandbyBucket() { 842 final int actualBucket = getStandbyBucket(); 843 if (actualBucket == EXEMPTED_INDEX) { 844 return actualBucket; 845 } 846 if (uidActive || getJob().isExemptedFromAppStandby()) { 847 // Treat these cases as if they're in the ACTIVE bucket so that they get throttled 848 // like other ACTIVE apps. 849 return ACTIVE_INDEX; 850 } 851 if (actualBucket != RESTRICTED_INDEX && actualBucket != NEVER_INDEX 852 && mHasMediaBackupExemption) { 853 // Cap it at WORKING_INDEX as media back up jobs are important to the user, and the 854 // source package may not have been used directly in a while. 855 return Math.min(WORKING_INDEX, actualBucket); 856 } 857 return actualBucket; 858 } 859 860 /** Returns the real standby bucket of the job. */ getStandbyBucket()861 public int getStandbyBucket() { 862 return standbyBucket; 863 } 864 setStandbyBucket(int newBucket)865 public void setStandbyBucket(int newBucket) { 866 if (newBucket == RESTRICTED_INDEX) { 867 // Adding to the bucket. 868 addDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS); 869 } else if (standbyBucket == RESTRICTED_INDEX) { 870 // Removing from the RESTRICTED bucket. 871 removeDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS); 872 } 873 874 standbyBucket = newBucket; 875 mLoggedBucketMismatch = false; 876 } 877 878 /** 879 * Log a bucket mismatch if this is the first time for this job. 880 */ maybeLogBucketMismatch()881 public void maybeLogBucketMismatch() { 882 if (!mLoggedBucketMismatch) { 883 Slog.wtf(TAG, 884 "App " + getSourcePackageName() + " became active but still in NEVER bucket"); 885 mLoggedBucketMismatch = true; 886 } 887 } 888 889 // Called only by the standby monitoring code getWhenStandbyDeferred()890 public long getWhenStandbyDeferred() { 891 return whenStandbyDeferred; 892 } 893 894 // Called only by the standby monitoring code setWhenStandbyDeferred(long now)895 public void setWhenStandbyDeferred(long now) { 896 whenStandbyDeferred = now; 897 } 898 899 /** 900 * Returns the first time this job was force batched, in the elapsed realtime timebase. Will be 901 * 0 if this job was never force batched. 902 */ getFirstForceBatchedTimeElapsed()903 public long getFirstForceBatchedTimeElapsed() { 904 return mFirstForceBatchedTimeElapsed; 905 } 906 setFirstForceBatchedTimeElapsed(long now)907 public void setFirstForceBatchedTimeElapsed(long now) { 908 mFirstForceBatchedTimeElapsed = now; 909 } 910 911 /** 912 * Re-evaluates the media backup exemption status. 913 * 914 * @return true if the exemption status changed 915 */ updateMediaBackupExemptionStatus()916 public boolean updateMediaBackupExemptionStatus() { 917 final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class); 918 boolean hasMediaExemption = mHasExemptedMediaUrisOnly 919 && !job.hasLateConstraint() 920 && job.getRequiredNetwork() != null 921 && getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT 922 && sourcePackageName.equals(jsi.getCloudMediaProviderPackage(sourceUserId)); 923 if (mHasMediaBackupExemption == hasMediaExemption) { 924 return false; 925 } 926 mHasMediaBackupExemption = hasMediaExemption; 927 return true; 928 } 929 getSourceTag()930 public String getSourceTag() { 931 return sourceTag; 932 } 933 getUid()934 public int getUid() { 935 return callingUid; 936 } 937 getBatteryName()938 public String getBatteryName() { 939 return batteryName; 940 } 941 getTag()942 public String getTag() { 943 return tag; 944 } 945 getBias()946 public int getBias() { 947 return job.getBias(); 948 } 949 950 /** 951 * Returns the priority of the job, which may be adjusted due to various factors. 952 * @see JobInfo.Builder#setPriority(int) 953 */ 954 @JobInfo.Priority getEffectivePriority()955 public int getEffectivePriority() { 956 final int rawPriority = job.getPriority(); 957 if (numFailures < 2) { 958 return rawPriority; 959 } 960 // Slowly decay priority of jobs to prevent starvation of other jobs. 961 if (isRequestedExpeditedJob()) { 962 // EJs can't fall below HIGH priority. 963 return JobInfo.PRIORITY_HIGH; 964 } 965 // Set a maximum priority based on the number of failures. 966 final int dropPower = numFailures / 2; 967 switch (dropPower) { 968 case 1: return Math.min(JobInfo.PRIORITY_DEFAULT, rawPriority); 969 case 2: return Math.min(JobInfo.PRIORITY_LOW, rawPriority); 970 default: return JobInfo.PRIORITY_MIN; 971 } 972 } 973 getFlags()974 public int getFlags() { 975 return job.getFlags(); 976 } 977 getInternalFlags()978 public int getInternalFlags() { 979 return mInternalFlags; 980 } 981 addInternalFlags(int flags)982 public void addInternalFlags(int flags) { 983 mInternalFlags |= flags; 984 } 985 getSatisfiedConstraintFlags()986 public int getSatisfiedConstraintFlags() { 987 return satisfiedConstraints; 988 } 989 maybeAddForegroundExemption(Predicate<Integer> uidForegroundChecker)990 public void maybeAddForegroundExemption(Predicate<Integer> uidForegroundChecker) { 991 // Jobs with time constraints shouldn't be exempted. 992 if (job.hasEarlyConstraint() || job.hasLateConstraint()) { 993 return; 994 } 995 // Already exempted, skip the foreground check. 996 if ((mInternalFlags & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0) { 997 return; 998 } 999 if (uidForegroundChecker.test(getSourceUid())) { 1000 addInternalFlags(INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION); 1001 } 1002 } 1003 updateNetworkBytesLocked()1004 private void updateNetworkBytesLocked() { 1005 mTotalNetworkDownloadBytes = job.getEstimatedNetworkDownloadBytes(); 1006 mTotalNetworkUploadBytes = job.getEstimatedNetworkUploadBytes(); 1007 mMinimumNetworkChunkBytes = job.getMinimumNetworkChunkBytes(); 1008 1009 if (pendingWork != null) { 1010 for (int i = 0; i < pendingWork.size(); i++) { 1011 if (mTotalNetworkDownloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { 1012 // If any component of the job has unknown usage, we don't have a 1013 // complete picture of what data will be used, and we have to treat the 1014 // entire up/download as unknown. 1015 long downloadBytes = pendingWork.get(i).getEstimatedNetworkDownloadBytes(); 1016 if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { 1017 mTotalNetworkDownloadBytes += downloadBytes; 1018 } 1019 } 1020 if (mTotalNetworkUploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { 1021 // If any component of the job has unknown usage, we don't have a 1022 // complete picture of what data will be used, and we have to treat the 1023 // entire up/download as unknown. 1024 long uploadBytes = pendingWork.get(i).getEstimatedNetworkUploadBytes(); 1025 if (uploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { 1026 mTotalNetworkUploadBytes += uploadBytes; 1027 } 1028 } 1029 final long chunkBytes = pendingWork.get(i).getMinimumNetworkChunkBytes(); 1030 if (mMinimumNetworkChunkBytes == JobInfo.NETWORK_BYTES_UNKNOWN) { 1031 mMinimumNetworkChunkBytes = chunkBytes; 1032 } else if (chunkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { 1033 mMinimumNetworkChunkBytes = Math.min(mMinimumNetworkChunkBytes, chunkBytes); 1034 } 1035 } 1036 } 1037 } 1038 getEstimatedNetworkDownloadBytes()1039 public long getEstimatedNetworkDownloadBytes() { 1040 return mTotalNetworkDownloadBytes; 1041 } 1042 getEstimatedNetworkUploadBytes()1043 public long getEstimatedNetworkUploadBytes() { 1044 return mTotalNetworkUploadBytes; 1045 } 1046 getMinimumNetworkChunkBytes()1047 public long getMinimumNetworkChunkBytes() { 1048 return mMinimumNetworkChunkBytes; 1049 } 1050 1051 /** Does this job have any sort of networking constraint? */ hasConnectivityConstraint()1052 public boolean hasConnectivityConstraint() { 1053 // No need to check mDynamicConstraints since connectivity will only be in that list if 1054 // it's already in the requiredConstraints list. 1055 return (requiredConstraints&CONSTRAINT_CONNECTIVITY) != 0; 1056 } 1057 hasChargingConstraint()1058 public boolean hasChargingConstraint() { 1059 return hasConstraint(CONSTRAINT_CHARGING); 1060 } 1061 hasBatteryNotLowConstraint()1062 public boolean hasBatteryNotLowConstraint() { 1063 return hasConstraint(CONSTRAINT_BATTERY_NOT_LOW); 1064 } 1065 1066 /** Returns true if the job requires charging OR battery not low. */ hasPowerConstraint()1067 boolean hasPowerConstraint() { 1068 return hasConstraint(CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW); 1069 } 1070 hasStorageNotLowConstraint()1071 public boolean hasStorageNotLowConstraint() { 1072 return hasConstraint(CONSTRAINT_STORAGE_NOT_LOW); 1073 } 1074 hasTimingDelayConstraint()1075 public boolean hasTimingDelayConstraint() { 1076 return hasConstraint(CONSTRAINT_TIMING_DELAY); 1077 } 1078 hasDeadlineConstraint()1079 public boolean hasDeadlineConstraint() { 1080 return hasConstraint(CONSTRAINT_DEADLINE); 1081 } 1082 hasIdleConstraint()1083 public boolean hasIdleConstraint() { 1084 return hasConstraint(CONSTRAINT_IDLE); 1085 } 1086 hasContentTriggerConstraint()1087 public boolean hasContentTriggerConstraint() { 1088 // No need to check mDynamicConstraints since content trigger will only be in that list if 1089 // it's already in the requiredConstraints list. 1090 return (requiredConstraints&CONSTRAINT_CONTENT_TRIGGER) != 0; 1091 } 1092 1093 /** 1094 * Checks both {@link #requiredConstraints} and {@link #mDynamicConstraints} to see if this job 1095 * requires the specified constraint. 1096 */ hasConstraint(int constraint)1097 private boolean hasConstraint(int constraint) { 1098 return (requiredConstraints & constraint) != 0 || (mDynamicConstraints & constraint) != 0; 1099 } 1100 getTriggerContentUpdateDelay()1101 public long getTriggerContentUpdateDelay() { 1102 long time = job.getTriggerContentUpdateDelay(); 1103 if (time < 0) { 1104 return DEFAULT_TRIGGER_UPDATE_DELAY; 1105 } 1106 return Math.max(time, MIN_TRIGGER_UPDATE_DELAY); 1107 } 1108 getTriggerContentMaxDelay()1109 public long getTriggerContentMaxDelay() { 1110 long time = job.getTriggerContentMaxDelay(); 1111 if (time < 0) { 1112 return DEFAULT_TRIGGER_MAX_DELAY; 1113 } 1114 return Math.max(time, MIN_TRIGGER_MAX_DELAY); 1115 } 1116 isPersisted()1117 public boolean isPersisted() { 1118 return job.isPersisted(); 1119 } 1120 getEarliestRunTime()1121 public long getEarliestRunTime() { 1122 return earliestRunTimeElapsedMillis; 1123 } 1124 getLatestRunTimeElapsed()1125 public long getLatestRunTimeElapsed() { 1126 return latestRunTimeElapsedMillis; 1127 } 1128 getOriginalLatestRunTimeElapsed()1129 public long getOriginalLatestRunTimeElapsed() { 1130 return mOriginalLatestRunTimeElapsedMillis; 1131 } 1132 setOriginalLatestRunTimeElapsed(long latestRunTimeElapsed)1133 public void setOriginalLatestRunTimeElapsed(long latestRunTimeElapsed) { 1134 mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsed; 1135 } 1136 1137 @JobParameters.StopReason getStopReason()1138 public int getStopReason() { 1139 return mReasonReadyToUnready; 1140 } 1141 1142 /** 1143 * Return the fractional position of "now" within the "run time" window of 1144 * this job. 1145 * <p> 1146 * For example, if the earliest run time was 10 minutes ago, and the latest 1147 * run time is 30 minutes from now, this would return 0.25. 1148 * <p> 1149 * If the job has no window defined, returns 1. When only an earliest or 1150 * latest time is defined, it's treated as an infinitely small window at 1151 * that time. 1152 */ getFractionRunTime()1153 public float getFractionRunTime() { 1154 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 1155 if (earliestRunTimeElapsedMillis == NO_EARLIEST_RUNTIME 1156 && latestRunTimeElapsedMillis == NO_LATEST_RUNTIME) { 1157 return 1; 1158 } else if (earliestRunTimeElapsedMillis == NO_EARLIEST_RUNTIME) { 1159 return now >= latestRunTimeElapsedMillis ? 1 : 0; 1160 } else if (latestRunTimeElapsedMillis == NO_LATEST_RUNTIME) { 1161 return now >= earliestRunTimeElapsedMillis ? 1 : 0; 1162 } else { 1163 if (now <= earliestRunTimeElapsedMillis) { 1164 return 0; 1165 } else if (now >= latestRunTimeElapsedMillis) { 1166 return 1; 1167 } else { 1168 return (float) (now - earliestRunTimeElapsedMillis) 1169 / (float) (latestRunTimeElapsedMillis - earliestRunTimeElapsedMillis); 1170 } 1171 } 1172 } 1173 getPersistedUtcTimes()1174 public Pair<Long, Long> getPersistedUtcTimes() { 1175 return mPersistedUtcTimes; 1176 } 1177 clearPersistedUtcTimes()1178 public void clearPersistedUtcTimes() { 1179 mPersistedUtcTimes = null; 1180 } 1181 1182 /** @return true if the app has requested that this run as an expedited job. */ isRequestedExpeditedJob()1183 public boolean isRequestedExpeditedJob() { 1184 return (getFlags() & JobInfo.FLAG_EXPEDITED) != 0; 1185 } 1186 1187 /** 1188 * @return true if all expedited job requirements are satisfied and therefore this should be 1189 * treated as an expedited job. 1190 */ shouldTreatAsExpeditedJob()1191 public boolean shouldTreatAsExpeditedJob() { 1192 return mExpeditedQuotaApproved && mExpeditedTareApproved && isRequestedExpeditedJob(); 1193 } 1194 1195 /** 1196 * @return true if the job is exempted from Doze restrictions and therefore allowed to run 1197 * in Doze. 1198 */ canRunInDoze()1199 public boolean canRunInDoze() { 1200 return appHasDozeExemption 1201 || (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0 1202 || ((shouldTreatAsExpeditedJob() || startedAsExpeditedJob) 1203 && (mDynamicConstraints & CONSTRAINT_DEVICE_NOT_DOZING) == 0); 1204 } 1205 canRunInBatterySaver()1206 boolean canRunInBatterySaver() { 1207 return (getInternalFlags() & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0 1208 || ((shouldTreatAsExpeditedJob() || startedAsExpeditedJob) 1209 && (mDynamicConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) == 0); 1210 } 1211 1212 /** @return true if the constraint was changed, false otherwise. */ setChargingConstraintSatisfied(final long nowElapsed, boolean state)1213 boolean setChargingConstraintSatisfied(final long nowElapsed, boolean state) { 1214 return setConstraintSatisfied(CONSTRAINT_CHARGING, nowElapsed, state); 1215 } 1216 1217 /** @return true if the constraint was changed, false otherwise. */ setBatteryNotLowConstraintSatisfied(final long nowElapsed, boolean state)1218 boolean setBatteryNotLowConstraintSatisfied(final long nowElapsed, boolean state) { 1219 return setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, nowElapsed, state); 1220 } 1221 1222 /** @return true if the constraint was changed, false otherwise. */ setStorageNotLowConstraintSatisfied(final long nowElapsed, boolean state)1223 boolean setStorageNotLowConstraintSatisfied(final long nowElapsed, boolean state) { 1224 return setConstraintSatisfied(CONSTRAINT_STORAGE_NOT_LOW, nowElapsed, state); 1225 } 1226 1227 /** @return true if the constraint was changed, false otherwise. */ setPrefetchConstraintSatisfied(final long nowElapsed, boolean state)1228 boolean setPrefetchConstraintSatisfied(final long nowElapsed, boolean state) { 1229 return setConstraintSatisfied(CONSTRAINT_PREFETCH, nowElapsed, state); 1230 } 1231 1232 /** @return true if the constraint was changed, false otherwise. */ setTimingDelayConstraintSatisfied(final long nowElapsed, boolean state)1233 boolean setTimingDelayConstraintSatisfied(final long nowElapsed, boolean state) { 1234 return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, nowElapsed, state); 1235 } 1236 1237 /** @return true if the constraint was changed, false otherwise. */ setDeadlineConstraintSatisfied(final long nowElapsed, boolean state)1238 boolean setDeadlineConstraintSatisfied(final long nowElapsed, boolean state) { 1239 if (setConstraintSatisfied(CONSTRAINT_DEADLINE, nowElapsed, state)) { 1240 // The constraint was changed. Update the ready flag. 1241 mReadyDeadlineSatisfied = !job.isPeriodic() && hasDeadlineConstraint() && state; 1242 return true; 1243 } 1244 return false; 1245 } 1246 1247 /** @return true if the constraint was changed, false otherwise. */ setIdleConstraintSatisfied(final long nowElapsed, boolean state)1248 boolean setIdleConstraintSatisfied(final long nowElapsed, boolean state) { 1249 return setConstraintSatisfied(CONSTRAINT_IDLE, nowElapsed, state); 1250 } 1251 1252 /** @return true if the constraint was changed, false otherwise. */ setConnectivityConstraintSatisfied(final long nowElapsed, boolean state)1253 boolean setConnectivityConstraintSatisfied(final long nowElapsed, boolean state) { 1254 return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, nowElapsed, state); 1255 } 1256 1257 /** @return true if the constraint was changed, false otherwise. */ setContentTriggerConstraintSatisfied(final long nowElapsed, boolean state)1258 boolean setContentTriggerConstraintSatisfied(final long nowElapsed, boolean state) { 1259 return setConstraintSatisfied(CONSTRAINT_CONTENT_TRIGGER, nowElapsed, state); 1260 } 1261 1262 /** @return true if the constraint was changed, false otherwise. */ setDeviceNotDozingConstraintSatisfied(final long nowElapsed, boolean state, boolean whitelisted)1263 boolean setDeviceNotDozingConstraintSatisfied(final long nowElapsed, 1264 boolean state, boolean whitelisted) { 1265 appHasDozeExemption = whitelisted; 1266 if (setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, nowElapsed, state)) { 1267 // The constraint was changed. Update the ready flag. 1268 mReadyNotDozing = state || canRunInDoze(); 1269 return true; 1270 } 1271 return false; 1272 } 1273 1274 /** @return true if the constraint was changed, false otherwise. */ setBackgroundNotRestrictedConstraintSatisfied(final long nowElapsed, boolean state, boolean isUserBgRestricted)1275 boolean setBackgroundNotRestrictedConstraintSatisfied(final long nowElapsed, boolean state, 1276 boolean isUserBgRestricted) { 1277 mIsUserBgRestricted = isUserBgRestricted; 1278 if (setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, nowElapsed, state)) { 1279 // The constraint was changed. Update the ready flag. 1280 mReadyNotRestrictedInBg = state; 1281 return true; 1282 } 1283 return false; 1284 } 1285 1286 /** @return true if the constraint was changed, false otherwise. */ setQuotaConstraintSatisfied(final long nowElapsed, boolean state)1287 boolean setQuotaConstraintSatisfied(final long nowElapsed, boolean state) { 1288 if (setConstraintSatisfied(CONSTRAINT_WITHIN_QUOTA, nowElapsed, state)) { 1289 // The constraint was changed. Update the ready flag. 1290 mReadyWithinQuota = state; 1291 return true; 1292 } 1293 return false; 1294 } 1295 1296 /** @return true if the constraint was changed, false otherwise. */ setTareWealthConstraintSatisfied(final long nowElapsed, boolean state)1297 boolean setTareWealthConstraintSatisfied(final long nowElapsed, boolean state) { 1298 if (setConstraintSatisfied(CONSTRAINT_TARE_WEALTH, nowElapsed, state)) { 1299 // The constraint was changed. Update the ready flag. 1300 mReadyTareWealth = state; 1301 return true; 1302 } 1303 return false; 1304 } 1305 1306 /** 1307 * Sets whether or not this job is approved to be treated as an expedited job based on quota 1308 * policy. 1309 * 1310 * @return true if the approval bit was changed, false otherwise. 1311 */ setExpeditedJobQuotaApproved(final long nowElapsed, boolean state)1312 boolean setExpeditedJobQuotaApproved(final long nowElapsed, boolean state) { 1313 if (mExpeditedQuotaApproved == state) { 1314 return false; 1315 } 1316 final boolean wasReady = !state && isReady(); 1317 mExpeditedQuotaApproved = state; 1318 updateExpeditedDependencies(); 1319 final boolean isReady = isReady(); 1320 if (wasReady && !isReady) { 1321 mReasonReadyToUnready = JobParameters.STOP_REASON_QUOTA; 1322 } else if (!wasReady && isReady) { 1323 mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED; 1324 } 1325 return true; 1326 } 1327 1328 /** 1329 * Sets whether or not this job is approved to be treated as an expedited job based on TARE 1330 * policy. 1331 * 1332 * @return true if the approval bit was changed, false otherwise. 1333 */ setExpeditedJobTareApproved(final long nowElapsed, boolean state)1334 boolean setExpeditedJobTareApproved(final long nowElapsed, boolean state) { 1335 if (mExpeditedTareApproved == state) { 1336 return false; 1337 } 1338 final boolean wasReady = !state && isReady(); 1339 mExpeditedTareApproved = state; 1340 updateExpeditedDependencies(); 1341 final boolean isReady = isReady(); 1342 if (wasReady && !isReady) { 1343 mReasonReadyToUnready = JobParameters.STOP_REASON_QUOTA; 1344 } else if (!wasReady && isReady) { 1345 mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED; 1346 } 1347 return true; 1348 } 1349 updateExpeditedDependencies()1350 private void updateExpeditedDependencies() { 1351 // DeviceIdleJobsController currently only tracks jobs with the WILL_BE_FOREGROUND flag. 1352 // Making it also track requested-expedited jobs would add unnecessary hops since the 1353 // controller would then defer to canRunInDoze. Avoid the hops and just update 1354 // mReadyNotDozing directly. 1355 mReadyNotDozing = isConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING) || canRunInDoze(); 1356 } 1357 1358 /** @return true if the state was changed, false otherwise. */ setUidActive(final boolean newActiveState)1359 boolean setUidActive(final boolean newActiveState) { 1360 if (newActiveState != uidActive) { 1361 uidActive = newActiveState; 1362 return true; 1363 } 1364 return false; /* unchanged */ 1365 } 1366 1367 /** @return true if the constraint was changed, false otherwise. */ setConstraintSatisfied(int constraint, final long nowElapsed, boolean state)1368 boolean setConstraintSatisfied(int constraint, final long nowElapsed, boolean state) { 1369 boolean old = (satisfiedConstraints&constraint) != 0; 1370 if (old == state) { 1371 return false; 1372 } 1373 if (DEBUG) { 1374 Slog.v(TAG, 1375 "Constraint " + constraint + " is " + (!state ? "NOT " : "") + "satisfied for " 1376 + toShortString()); 1377 } 1378 final boolean wasReady = !state && isReady(); 1379 satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0); 1380 mSatisfiedConstraintsOfInterest = satisfiedConstraints & CONSTRAINTS_OF_INTEREST; 1381 mReadyDynamicSatisfied = mDynamicConstraints != 0 1382 && mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints); 1383 if (STATS_LOG_ENABLED && (STATSD_CONSTRAINTS_TO_LOG & constraint) != 0) { 1384 FrameworkStatsLog.write_non_chained( 1385 FrameworkStatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED, 1386 sourceUid, null, getBatteryName(), getProtoConstraint(constraint), 1387 state ? FrameworkStatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__SATISFIED 1388 : FrameworkStatsLog 1389 .SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__UNSATISFIED); 1390 } 1391 1392 mConstraintUpdatedTimesElapsed[mConstraintChangeHistoryIndex] = nowElapsed; 1393 mConstraintStatusHistory[mConstraintChangeHistoryIndex] = satisfiedConstraints; 1394 mConstraintChangeHistoryIndex = 1395 (mConstraintChangeHistoryIndex + 1) % NUM_CONSTRAINT_CHANGE_HISTORY; 1396 1397 // Can't use isReady() directly since "cache booleans" haven't updated yet. 1398 final boolean isReady = readinessStatusWithConstraint(constraint, state); 1399 if (wasReady && !isReady) { 1400 mReasonReadyToUnready = constraintToStopReason(constraint); 1401 } else if (!wasReady && isReady) { 1402 mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED; 1403 } 1404 1405 return true; 1406 } 1407 1408 @JobParameters.StopReason constraintToStopReason(int constraint)1409 private int constraintToStopReason(int constraint) { 1410 switch (constraint) { 1411 case CONSTRAINT_BATTERY_NOT_LOW: 1412 if ((requiredConstraints & constraint) != 0) { 1413 // The developer requested this constraint, so it makes sense to return the 1414 // explicit constraint reason. 1415 return JobParameters.STOP_REASON_CONSTRAINT_BATTERY_NOT_LOW; 1416 } 1417 // Hard-coding right now since the current dynamic constraint sets don't overlap 1418 // TODO: return based on active dynamic constraint sets when they start overlapping 1419 return JobParameters.STOP_REASON_APP_STANDBY; 1420 case CONSTRAINT_CHARGING: 1421 if ((requiredConstraints & constraint) != 0) { 1422 // The developer requested this constraint, so it makes sense to return the 1423 // explicit constraint reason. 1424 return JobParameters.STOP_REASON_CONSTRAINT_CHARGING; 1425 } 1426 // Hard-coding right now since the current dynamic constraint sets don't overlap 1427 // TODO: return based on active dynamic constraint sets when they start overlapping 1428 return JobParameters.STOP_REASON_APP_STANDBY; 1429 case CONSTRAINT_CONNECTIVITY: 1430 return JobParameters.STOP_REASON_CONSTRAINT_CONNECTIVITY; 1431 case CONSTRAINT_IDLE: 1432 if ((requiredConstraints & constraint) != 0) { 1433 // The developer requested this constraint, so it makes sense to return the 1434 // explicit constraint reason. 1435 return JobParameters.STOP_REASON_CONSTRAINT_DEVICE_IDLE; 1436 } 1437 // Hard-coding right now since the current dynamic constraint sets don't overlap 1438 // TODO: return based on active dynamic constraint sets when they start overlapping 1439 return JobParameters.STOP_REASON_APP_STANDBY; 1440 case CONSTRAINT_STORAGE_NOT_LOW: 1441 return JobParameters.STOP_REASON_CONSTRAINT_STORAGE_NOT_LOW; 1442 1443 case CONSTRAINT_BACKGROUND_NOT_RESTRICTED: 1444 // The BACKGROUND_NOT_RESTRICTED constraint could be dissatisfied either because 1445 // the app is background restricted, or because we're restricting background work 1446 // in battery saver. Assume that background restriction is the reason apps that 1447 // are background restricted have their jobs stopped, and battery saver otherwise. 1448 // This has the benefit of being consistent for background restricted apps 1449 // (they'll always get BACKGROUND_RESTRICTION) as the reason, regardless of 1450 // battery saver state. 1451 if (mIsUserBgRestricted) { 1452 return JobParameters.STOP_REASON_BACKGROUND_RESTRICTION; 1453 } 1454 return JobParameters.STOP_REASON_DEVICE_STATE; 1455 case CONSTRAINT_DEVICE_NOT_DOZING: 1456 return JobParameters.STOP_REASON_DEVICE_STATE; 1457 1458 case CONSTRAINT_PREFETCH: 1459 return JobParameters.STOP_REASON_ESTIMATED_APP_LAUNCH_TIME_CHANGED; 1460 1461 case CONSTRAINT_TARE_WEALTH: 1462 case CONSTRAINT_WITHIN_QUOTA: 1463 return JobParameters.STOP_REASON_QUOTA; 1464 1465 // These should never be stop reasons since they can never go from true to false. 1466 case CONSTRAINT_CONTENT_TRIGGER: 1467 case CONSTRAINT_DEADLINE: 1468 case CONSTRAINT_TIMING_DELAY: 1469 default: 1470 Slog.wtf(TAG, "Unsupported constraint (" + constraint + ") --stop reason mapping"); 1471 return JobParameters.STOP_REASON_UNDEFINED; 1472 } 1473 } 1474 isConstraintSatisfied(int constraint)1475 boolean isConstraintSatisfied(int constraint) { 1476 return (satisfiedConstraints&constraint) != 0; 1477 } 1478 isExpeditedQuotaApproved()1479 boolean isExpeditedQuotaApproved() { 1480 return mExpeditedQuotaApproved; 1481 } 1482 clearTrackingController(int which)1483 boolean clearTrackingController(int which) { 1484 if ((trackingControllers&which) != 0) { 1485 trackingControllers &= ~which; 1486 return true; 1487 } 1488 return false; 1489 } 1490 setTrackingController(int which)1491 void setTrackingController(int which) { 1492 trackingControllers |= which; 1493 } 1494 1495 /** 1496 * Add additional constraints to prevent this job from running when doze or battery saver are 1497 * active. 1498 */ disallowRunInBatterySaverAndDoze()1499 public void disallowRunInBatterySaverAndDoze() { 1500 addDynamicConstraints(DYNAMIC_EXPEDITED_DEFERRAL_CONSTRAINTS); 1501 } 1502 1503 /** 1504 * Indicates that this job cannot run without the specified constraints. This is evaluated 1505 * separately from the job's explicitly requested constraints and MUST be satisfied before 1506 * the job can run if the app doesn't have quota. 1507 */ addDynamicConstraints(int constraints)1508 private void addDynamicConstraints(int constraints) { 1509 if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) { 1510 // Quota should never be used as a dynamic constraint. 1511 Slog.wtf(TAG, "Tried to set quota as a dynamic constraint"); 1512 constraints &= ~CONSTRAINT_WITHIN_QUOTA; 1513 } 1514 if ((constraints & CONSTRAINT_TARE_WEALTH) != 0) { 1515 // Quota should never be used as a dynamic constraint. 1516 Slog.wtf(TAG, "Tried to set TARE as a dynamic constraint"); 1517 constraints &= ~CONSTRAINT_TARE_WEALTH; 1518 } 1519 1520 // Connectivity and content trigger are special since they're only valid to add if the 1521 // job has requested network or specific content URIs. Adding these constraints to jobs 1522 // that don't need them doesn't make sense. 1523 if (!hasConnectivityConstraint()) { 1524 constraints &= ~CONSTRAINT_CONNECTIVITY; 1525 } 1526 if (!hasContentTriggerConstraint()) { 1527 constraints &= ~CONSTRAINT_CONTENT_TRIGGER; 1528 } 1529 1530 mDynamicConstraints |= constraints; 1531 mReadyDynamicSatisfied = mDynamicConstraints != 0 1532 && mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints); 1533 } 1534 1535 /** 1536 * Removes dynamic constraints from a job, meaning that the requirements are not required for 1537 * the job to run (if the job itself hasn't requested the constraint. This is separate from 1538 * the job's explicitly requested constraints and does not remove those requested constraints. 1539 * 1540 */ removeDynamicConstraints(int constraints)1541 private void removeDynamicConstraints(int constraints) { 1542 mDynamicConstraints &= ~constraints; 1543 mReadyDynamicSatisfied = mDynamicConstraints != 0 1544 && mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints); 1545 } 1546 getLastSuccessfulRunTime()1547 public long getLastSuccessfulRunTime() { 1548 return mLastSuccessfulRunTime; 1549 } 1550 getLastFailedRunTime()1551 public long getLastFailedRunTime() { 1552 return mLastFailedRunTime; 1553 } 1554 1555 /** 1556 * @return Whether or not this job is ready to run, based on its requirements. 1557 */ isReady()1558 public boolean isReady() { 1559 return isReady(mSatisfiedConstraintsOfInterest); 1560 } 1561 1562 /** 1563 * @return Whether or not this job would be ready to run if it had the specified constraint 1564 * granted, based on its requirements. 1565 */ wouldBeReadyWithConstraint(int constraint)1566 boolean wouldBeReadyWithConstraint(int constraint) { 1567 return readinessStatusWithConstraint(constraint, true); 1568 } 1569 readinessStatusWithConstraint(int constraint, boolean value)1570 private boolean readinessStatusWithConstraint(int constraint, boolean value) { 1571 boolean oldValue = false; 1572 int satisfied = mSatisfiedConstraintsOfInterest; 1573 switch (constraint) { 1574 case CONSTRAINT_BACKGROUND_NOT_RESTRICTED: 1575 oldValue = mReadyNotRestrictedInBg; 1576 mReadyNotRestrictedInBg = value; 1577 break; 1578 case CONSTRAINT_DEADLINE: 1579 oldValue = mReadyDeadlineSatisfied; 1580 mReadyDeadlineSatisfied = value; 1581 break; 1582 case CONSTRAINT_DEVICE_NOT_DOZING: 1583 oldValue = mReadyNotDozing; 1584 mReadyNotDozing = value; 1585 break; 1586 case CONSTRAINT_TARE_WEALTH: 1587 oldValue = mReadyTareWealth; 1588 mReadyTareWealth = value; 1589 break; 1590 case CONSTRAINT_WITHIN_QUOTA: 1591 oldValue = mReadyWithinQuota; 1592 mReadyWithinQuota = value; 1593 break; 1594 default: 1595 if (value) { 1596 satisfied |= constraint; 1597 } else { 1598 satisfied &= ~constraint; 1599 } 1600 mReadyDynamicSatisfied = mDynamicConstraints != 0 1601 && mDynamicConstraints == (satisfied & mDynamicConstraints); 1602 1603 break; 1604 } 1605 1606 boolean toReturn = isReady(satisfied); 1607 1608 switch (constraint) { 1609 case CONSTRAINT_BACKGROUND_NOT_RESTRICTED: 1610 mReadyNotRestrictedInBg = oldValue; 1611 break; 1612 case CONSTRAINT_DEADLINE: 1613 mReadyDeadlineSatisfied = oldValue; 1614 break; 1615 case CONSTRAINT_DEVICE_NOT_DOZING: 1616 mReadyNotDozing = oldValue; 1617 break; 1618 case CONSTRAINT_TARE_WEALTH: 1619 mReadyTareWealth = oldValue; 1620 break; 1621 case CONSTRAINT_WITHIN_QUOTA: 1622 mReadyWithinQuota = oldValue; 1623 break; 1624 default: 1625 mReadyDynamicSatisfied = mDynamicConstraints != 0 1626 && mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints); 1627 break; 1628 } 1629 return toReturn; 1630 } 1631 isReady(int satisfiedConstraints)1632 private boolean isReady(int satisfiedConstraints) { 1633 // Quota and dynamic constraints trump all other constraints. 1634 // NEVER jobs are not supposed to run at all. Since we're using quota to allow parole 1635 // sessions (exempt from dynamic restrictions), we need the additional check to ensure 1636 // that NEVER jobs don't run. 1637 // TODO: cleanup quota and standby bucket management so we don't need the additional checks 1638 if (((!mReadyWithinQuota || !mReadyTareWealth) 1639 && !mReadyDynamicSatisfied && !shouldTreatAsExpeditedJob()) 1640 || getEffectiveStandbyBucket() == NEVER_INDEX) { 1641 return false; 1642 } 1643 // Deadline constraint trumps other constraints besides quota and dynamic (except for 1644 // periodic jobs where deadline is an implementation detail. A periodic job should only 1645 // run if its constraints are satisfied). 1646 // DeviceNotDozing implicit constraint must be satisfied 1647 // NotRestrictedInBackground implicit constraint must be satisfied 1648 return mReadyNotDozing && mReadyNotRestrictedInBg && (serviceInfo != null) 1649 && (mReadyDeadlineSatisfied || isConstraintsSatisfied(satisfiedConstraints)); 1650 } 1651 1652 /** All constraints besides implicit and deadline. */ 1653 static final int CONSTRAINTS_OF_INTEREST = CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW 1654 | CONSTRAINT_STORAGE_NOT_LOW | CONSTRAINT_TIMING_DELAY | CONSTRAINT_CONNECTIVITY 1655 | CONSTRAINT_IDLE | CONSTRAINT_CONTENT_TRIGGER | CONSTRAINT_PREFETCH; 1656 1657 // Soft override covers all non-"functional" constraints 1658 static final int SOFT_OVERRIDE_CONSTRAINTS = 1659 CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW | CONSTRAINT_STORAGE_NOT_LOW 1660 | CONSTRAINT_TIMING_DELAY | CONSTRAINT_IDLE | CONSTRAINT_PREFETCH; 1661 1662 /** Returns true whenever all dynamically set constraints are satisfied. */ areDynamicConstraintsSatisfied()1663 public boolean areDynamicConstraintsSatisfied() { 1664 return mReadyDynamicSatisfied; 1665 } 1666 1667 /** 1668 * @return Whether the constraints set on this job are satisfied. 1669 */ isConstraintsSatisfied()1670 public boolean isConstraintsSatisfied() { 1671 return isConstraintsSatisfied(mSatisfiedConstraintsOfInterest); 1672 } 1673 isConstraintsSatisfied(int satisfiedConstraints)1674 private boolean isConstraintsSatisfied(int satisfiedConstraints) { 1675 if (overrideState == OVERRIDE_FULL) { 1676 // force override: the job is always runnable 1677 return true; 1678 } 1679 1680 int sat = satisfiedConstraints; 1681 if (overrideState == OVERRIDE_SOFT) { 1682 // override: pretend all 'soft' requirements are satisfied 1683 sat |= (requiredConstraints & SOFT_OVERRIDE_CONSTRAINTS); 1684 } 1685 1686 return (sat & mRequiredConstraintsOfInterest) == mRequiredConstraintsOfInterest; 1687 } 1688 matches(int uid, int jobId)1689 public boolean matches(int uid, int jobId) { 1690 return this.job.getId() == jobId && this.callingUid == uid; 1691 } 1692 1693 @Override toString()1694 public String toString() { 1695 StringBuilder sb = new StringBuilder(128); 1696 sb.append("JobStatus{"); 1697 sb.append(Integer.toHexString(System.identityHashCode(this))); 1698 sb.append(" #"); 1699 UserHandle.formatUid(sb, callingUid); 1700 sb.append("/"); 1701 sb.append(job.getId()); 1702 sb.append(' '); 1703 sb.append(batteryName); 1704 sb.append(" u="); 1705 sb.append(getUserId()); 1706 sb.append(" s="); 1707 sb.append(getSourceUid()); 1708 if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME 1709 || latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) { 1710 long now = sElapsedRealtimeClock.millis(); 1711 sb.append(" TIME="); 1712 formatRunTime(sb, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, now); 1713 sb.append(":"); 1714 formatRunTime(sb, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, now); 1715 } 1716 if (job.getRequiredNetwork() != null) { 1717 sb.append(" NET"); 1718 } 1719 if (job.isRequireCharging()) { 1720 sb.append(" CHARGING"); 1721 } 1722 if (job.isRequireBatteryNotLow()) { 1723 sb.append(" BATNOTLOW"); 1724 } 1725 if (job.isRequireStorageNotLow()) { 1726 sb.append(" STORENOTLOW"); 1727 } 1728 if (job.isRequireDeviceIdle()) { 1729 sb.append(" IDLE"); 1730 } 1731 if (job.isPeriodic()) { 1732 sb.append(" PERIODIC"); 1733 } 1734 if (job.isPersisted()) { 1735 sb.append(" PERSISTED"); 1736 } 1737 if ((satisfiedConstraints&CONSTRAINT_DEVICE_NOT_DOZING) == 0) { 1738 sb.append(" WAIT:DEV_NOT_DOZING"); 1739 } 1740 if (job.getTriggerContentUris() != null) { 1741 sb.append(" URIS="); 1742 sb.append(Arrays.toString(job.getTriggerContentUris())); 1743 } 1744 if (numFailures != 0) { 1745 sb.append(" failures="); 1746 sb.append(numFailures); 1747 } 1748 if (isReady()) { 1749 sb.append(" READY"); 1750 } else { 1751 sb.append(" satisfied:0x").append(Integer.toHexString(satisfiedConstraints)); 1752 sb.append(" unsatisfied:0x").append(Integer.toHexString( 1753 (satisfiedConstraints & mRequiredConstraintsOfInterest) 1754 ^ mRequiredConstraintsOfInterest)); 1755 } 1756 sb.append("}"); 1757 return sb.toString(); 1758 } 1759 formatRunTime(PrintWriter pw, long runtime, long defaultValue, long now)1760 private void formatRunTime(PrintWriter pw, long runtime, long defaultValue, long now) { 1761 if (runtime == defaultValue) { 1762 pw.print("none"); 1763 } else { 1764 TimeUtils.formatDuration(runtime - now, pw); 1765 } 1766 } 1767 formatRunTime(StringBuilder sb, long runtime, long defaultValue, long now)1768 private void formatRunTime(StringBuilder sb, long runtime, long defaultValue, long now) { 1769 if (runtime == defaultValue) { 1770 sb.append("none"); 1771 } else { 1772 TimeUtils.formatDuration(runtime - now, sb); 1773 } 1774 } 1775 1776 /** 1777 * Convenience function to identify a job uniquely without pulling all the data that 1778 * {@link #toString()} returns. 1779 */ toShortString()1780 public String toShortString() { 1781 StringBuilder sb = new StringBuilder(); 1782 sb.append(Integer.toHexString(System.identityHashCode(this))); 1783 sb.append(" #"); 1784 UserHandle.formatUid(sb, callingUid); 1785 sb.append("/"); 1786 sb.append(job.getId()); 1787 sb.append(' '); 1788 sb.append(batteryName); 1789 return sb.toString(); 1790 } 1791 1792 /** 1793 * Convenience function to identify a job uniquely without pulling all the data that 1794 * {@link #toString()} returns. 1795 */ toShortStringExceptUniqueId()1796 public String toShortStringExceptUniqueId() { 1797 StringBuilder sb = new StringBuilder(); 1798 sb.append(Integer.toHexString(System.identityHashCode(this))); 1799 sb.append(' '); 1800 sb.append(batteryName); 1801 return sb.toString(); 1802 } 1803 1804 /** 1805 * Convenience function to dump data that identifies a job uniquely to proto. This is intended 1806 * to mimic {@link #toShortString}. 1807 */ writeToShortProto(ProtoOutputStream proto, long fieldId)1808 public void writeToShortProto(ProtoOutputStream proto, long fieldId) { 1809 final long token = proto.start(fieldId); 1810 1811 proto.write(JobStatusShortInfoProto.CALLING_UID, callingUid); 1812 proto.write(JobStatusShortInfoProto.JOB_ID, job.getId()); 1813 proto.write(JobStatusShortInfoProto.BATTERY_NAME, batteryName); 1814 1815 proto.end(token); 1816 } 1817 dumpConstraints(PrintWriter pw, int constraints)1818 void dumpConstraints(PrintWriter pw, int constraints) { 1819 if ((constraints&CONSTRAINT_CHARGING) != 0) { 1820 pw.print(" CHARGING"); 1821 } 1822 if ((constraints& CONSTRAINT_BATTERY_NOT_LOW) != 0) { 1823 pw.print(" BATTERY_NOT_LOW"); 1824 } 1825 if ((constraints& CONSTRAINT_STORAGE_NOT_LOW) != 0) { 1826 pw.print(" STORAGE_NOT_LOW"); 1827 } 1828 if ((constraints&CONSTRAINT_TIMING_DELAY) != 0) { 1829 pw.print(" TIMING_DELAY"); 1830 } 1831 if ((constraints&CONSTRAINT_DEADLINE) != 0) { 1832 pw.print(" DEADLINE"); 1833 } 1834 if ((constraints&CONSTRAINT_IDLE) != 0) { 1835 pw.print(" IDLE"); 1836 } 1837 if ((constraints&CONSTRAINT_CONNECTIVITY) != 0) { 1838 pw.print(" CONNECTIVITY"); 1839 } 1840 if ((constraints&CONSTRAINT_CONTENT_TRIGGER) != 0) { 1841 pw.print(" CONTENT_TRIGGER"); 1842 } 1843 if ((constraints&CONSTRAINT_DEVICE_NOT_DOZING) != 0) { 1844 pw.print(" DEVICE_NOT_DOZING"); 1845 } 1846 if ((constraints&CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) { 1847 pw.print(" BACKGROUND_NOT_RESTRICTED"); 1848 } 1849 if ((constraints & CONSTRAINT_PREFETCH) != 0) { 1850 pw.print(" PREFETCH"); 1851 } 1852 if ((constraints & CONSTRAINT_TARE_WEALTH) != 0) { 1853 pw.print(" TARE_WEALTH"); 1854 } 1855 if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) { 1856 pw.print(" WITHIN_QUOTA"); 1857 } 1858 if (constraints != 0) { 1859 pw.print(" [0x"); 1860 pw.print(Integer.toHexString(constraints)); 1861 pw.print("]"); 1862 } 1863 } 1864 1865 /** Returns a {@link JobServerProtoEnums.Constraint} enum value for the given constraint. */ getProtoConstraint(int constraint)1866 static int getProtoConstraint(int constraint) { 1867 switch (constraint) { 1868 case CONSTRAINT_BACKGROUND_NOT_RESTRICTED: 1869 return JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED; 1870 case CONSTRAINT_BATTERY_NOT_LOW: 1871 return JobServerProtoEnums.CONSTRAINT_BATTERY_NOT_LOW; 1872 case CONSTRAINT_CHARGING: 1873 return JobServerProtoEnums.CONSTRAINT_CHARGING; 1874 case CONSTRAINT_CONNECTIVITY: 1875 return JobServerProtoEnums.CONSTRAINT_CONNECTIVITY; 1876 case CONSTRAINT_CONTENT_TRIGGER: 1877 return JobServerProtoEnums.CONSTRAINT_CONTENT_TRIGGER; 1878 case CONSTRAINT_DEADLINE: 1879 return JobServerProtoEnums.CONSTRAINT_DEADLINE; 1880 case CONSTRAINT_DEVICE_NOT_DOZING: 1881 return JobServerProtoEnums.CONSTRAINT_DEVICE_NOT_DOZING; 1882 case CONSTRAINT_IDLE: 1883 return JobServerProtoEnums.CONSTRAINT_IDLE; 1884 case CONSTRAINT_PREFETCH: 1885 return JobServerProtoEnums.CONSTRAINT_PREFETCH; 1886 case CONSTRAINT_STORAGE_NOT_LOW: 1887 return JobServerProtoEnums.CONSTRAINT_STORAGE_NOT_LOW; 1888 case CONSTRAINT_TARE_WEALTH: 1889 return JobServerProtoEnums.CONSTRAINT_TARE_WEALTH; 1890 case CONSTRAINT_TIMING_DELAY: 1891 return JobServerProtoEnums.CONSTRAINT_TIMING_DELAY; 1892 case CONSTRAINT_WITHIN_QUOTA: 1893 return JobServerProtoEnums.CONSTRAINT_WITHIN_QUOTA; 1894 default: 1895 return JobServerProtoEnums.CONSTRAINT_UNKNOWN; 1896 } 1897 } 1898 1899 /** Writes constraints to the given repeating proto field. */ dumpConstraints(ProtoOutputStream proto, long fieldId, int constraints)1900 void dumpConstraints(ProtoOutputStream proto, long fieldId, int constraints) { 1901 if ((constraints & CONSTRAINT_CHARGING) != 0) { 1902 proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CHARGING); 1903 } 1904 if ((constraints & CONSTRAINT_BATTERY_NOT_LOW) != 0) { 1905 proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_BATTERY_NOT_LOW); 1906 } 1907 if ((constraints & CONSTRAINT_STORAGE_NOT_LOW) != 0) { 1908 proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_STORAGE_NOT_LOW); 1909 } 1910 if ((constraints & CONSTRAINT_TIMING_DELAY) != 0) { 1911 proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_TIMING_DELAY); 1912 } 1913 if ((constraints & CONSTRAINT_DEADLINE) != 0) { 1914 proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_DEADLINE); 1915 } 1916 if ((constraints & CONSTRAINT_IDLE) != 0) { 1917 proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_IDLE); 1918 } 1919 if ((constraints & CONSTRAINT_CONNECTIVITY) != 0) { 1920 proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CONNECTIVITY); 1921 } 1922 if ((constraints & CONSTRAINT_CONTENT_TRIGGER) != 0) { 1923 proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CONTENT_TRIGGER); 1924 } 1925 if ((constraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0) { 1926 proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_DEVICE_NOT_DOZING); 1927 } 1928 if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) { 1929 proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_WITHIN_QUOTA); 1930 } 1931 if ((constraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) { 1932 proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED); 1933 } 1934 } 1935 dumpJobWorkItem(IndentingPrintWriter pw, JobWorkItem work, int index)1936 private void dumpJobWorkItem(IndentingPrintWriter pw, JobWorkItem work, int index) { 1937 pw.increaseIndent(); 1938 pw.print("#"); pw.print(index); pw.print(": #"); 1939 pw.print(work.getWorkId()); pw.print(" "); pw.print(work.getDeliveryCount()); 1940 pw.print("x "); pw.println(work.getIntent()); 1941 if (work.getGrants() != null) { 1942 pw.println("URI grants:"); 1943 pw.increaseIndent(); 1944 ((GrantedUriPermissions) work.getGrants()).dump(pw); 1945 pw.decreaseIndent(); 1946 } 1947 pw.decreaseIndent(); 1948 } 1949 dumpJobWorkItem(ProtoOutputStream proto, long fieldId, JobWorkItem work)1950 private void dumpJobWorkItem(ProtoOutputStream proto, long fieldId, JobWorkItem work) { 1951 final long token = proto.start(fieldId); 1952 1953 proto.write(JobStatusDumpProto.JobWorkItem.WORK_ID, work.getWorkId()); 1954 proto.write(JobStatusDumpProto.JobWorkItem.DELIVERY_COUNT, work.getDeliveryCount()); 1955 if (work.getIntent() != null) { 1956 work.getIntent().dumpDebug(proto, JobStatusDumpProto.JobWorkItem.INTENT); 1957 } 1958 Object grants = work.getGrants(); 1959 if (grants != null) { 1960 ((GrantedUriPermissions) grants).dump(proto, JobStatusDumpProto.JobWorkItem.URI_GRANTS); 1961 } 1962 1963 proto.end(token); 1964 } 1965 1966 /** 1967 * Returns a bucket name based on the normalized bucket indices, not the AppStandby constants. 1968 */ getBucketName()1969 String getBucketName() { 1970 return bucketName(standbyBucket); 1971 } 1972 1973 /** 1974 * Returns a bucket name based on the normalized bucket indices, not the AppStandby constants. 1975 */ bucketName(int standbyBucket)1976 static String bucketName(int standbyBucket) { 1977 switch (standbyBucket) { 1978 case 0: return "ACTIVE"; 1979 case 1: return "WORKING_SET"; 1980 case 2: return "FREQUENT"; 1981 case 3: return "RARE"; 1982 case 4: return "NEVER"; 1983 case 5: return "RESTRICTED"; 1984 case 6: return "EXEMPTED"; 1985 default: 1986 return "Unknown: " + standbyBucket; 1987 } 1988 } 1989 1990 // Dumpsys infrastructure 1991 @NeverCompile // Avoid size overhead of debugging code. dump(IndentingPrintWriter pw, boolean full, long nowElapsed)1992 public void dump(IndentingPrintWriter pw, boolean full, long nowElapsed) { 1993 UserHandle.formatUid(pw, callingUid); 1994 pw.print(" tag="); pw.println(tag); 1995 1996 pw.print("Source: uid="); UserHandle.formatUid(pw, getSourceUid()); 1997 pw.print(" user="); pw.print(getSourceUserId()); 1998 pw.print(" pkg="); pw.println(getSourcePackageName()); 1999 if (full) { 2000 pw.println("JobInfo:"); 2001 pw.increaseIndent(); 2002 2003 pw.print("Service: "); 2004 pw.println(job.getService().flattenToShortString()); 2005 if (job.isPeriodic()) { 2006 pw.print("PERIODIC: interval="); 2007 TimeUtils.formatDuration(job.getIntervalMillis(), pw); 2008 pw.print(" flex="); TimeUtils.formatDuration(job.getFlexMillis(), pw); 2009 pw.println(); 2010 } 2011 if (job.isPersisted()) { 2012 pw.println("PERSISTED"); 2013 } 2014 if (job.getBias() != 0) { 2015 pw.print("Bias: "); 2016 pw.println(JobInfo.getBiasString(job.getBias())); 2017 } 2018 pw.print("Priority: "); 2019 pw.print(JobInfo.getPriorityString(job.getPriority())); 2020 final int effectivePriority = getEffectivePriority(); 2021 if (effectivePriority != job.getPriority()) { 2022 pw.print(" effective="); 2023 pw.print(JobInfo.getPriorityString(effectivePriority)); 2024 } 2025 pw.println(); 2026 if (job.getFlags() != 0) { 2027 pw.print("Flags: "); 2028 pw.println(Integer.toHexString(job.getFlags())); 2029 } 2030 if (getInternalFlags() != 0) { 2031 pw.print("Internal flags: "); 2032 pw.print(Integer.toHexString(getInternalFlags())); 2033 2034 if ((getInternalFlags()&INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0) { 2035 pw.print(" HAS_FOREGROUND_EXEMPTION"); 2036 } 2037 pw.println(); 2038 } 2039 pw.print("Requires: charging="); 2040 pw.print(job.isRequireCharging()); pw.print(" batteryNotLow="); 2041 pw.print(job.isRequireBatteryNotLow()); pw.print(" deviceIdle="); 2042 pw.println(job.isRequireDeviceIdle()); 2043 if (job.getTriggerContentUris() != null) { 2044 pw.println("Trigger content URIs:"); 2045 pw.increaseIndent(); 2046 for (int i = 0; i < job.getTriggerContentUris().length; i++) { 2047 JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i]; 2048 pw.print(Integer.toHexString(trig.getFlags())); 2049 pw.print(' '); pw.println(trig.getUri()); 2050 } 2051 pw.decreaseIndent(); 2052 if (job.getTriggerContentUpdateDelay() >= 0) { 2053 pw.print("Trigger update delay: "); 2054 TimeUtils.formatDuration(job.getTriggerContentUpdateDelay(), pw); 2055 pw.println(); 2056 } 2057 if (job.getTriggerContentMaxDelay() >= 0) { 2058 pw.print("Trigger max delay: "); 2059 TimeUtils.formatDuration(job.getTriggerContentMaxDelay(), pw); 2060 pw.println(); 2061 } 2062 pw.print("Has media backup exemption", mHasMediaBackupExemption).println(); 2063 } 2064 if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) { 2065 pw.print("Extras: "); 2066 pw.println(job.getExtras().toShortString()); 2067 } 2068 if (job.getTransientExtras() != null && !job.getTransientExtras().isDefinitelyEmpty()) { 2069 pw.print("Transient extras: "); 2070 pw.println(job.getTransientExtras().toShortString()); 2071 } 2072 if (job.getClipData() != null) { 2073 pw.print("Clip data: "); 2074 StringBuilder b = new StringBuilder(128); 2075 b.append(job.getClipData()); 2076 pw.println(b); 2077 } 2078 if (uriPerms != null) { 2079 pw.println("Granted URI permissions:"); 2080 uriPerms.dump(pw); 2081 } 2082 if (job.getRequiredNetwork() != null) { 2083 pw.print("Network type: "); 2084 pw.println(job.getRequiredNetwork()); 2085 } 2086 if (mTotalNetworkDownloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { 2087 pw.print("Network download bytes: "); 2088 pw.println(mTotalNetworkDownloadBytes); 2089 } 2090 if (mTotalNetworkUploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { 2091 pw.print("Network upload bytes: "); 2092 pw.println(mTotalNetworkUploadBytes); 2093 } 2094 if (mMinimumNetworkChunkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { 2095 pw.print("Minimum network chunk bytes: "); 2096 pw.println(mMinimumNetworkChunkBytes); 2097 } 2098 if (job.getMinLatencyMillis() != 0) { 2099 pw.print("Minimum latency: "); 2100 TimeUtils.formatDuration(job.getMinLatencyMillis(), pw); 2101 pw.println(); 2102 } 2103 if (job.getMaxExecutionDelayMillis() != 0) { 2104 pw.print("Max execution delay: "); 2105 TimeUtils.formatDuration(job.getMaxExecutionDelayMillis(), pw); 2106 pw.println(); 2107 } 2108 pw.print("Backoff: policy="); pw.print(job.getBackoffPolicy()); 2109 pw.print(" initial="); TimeUtils.formatDuration(job.getInitialBackoffMillis(), pw); 2110 pw.println(); 2111 if (job.hasEarlyConstraint()) { 2112 pw.println("Has early constraint"); 2113 } 2114 if (job.hasLateConstraint()) { 2115 pw.println("Has late constraint"); 2116 } 2117 2118 pw.decreaseIndent(); 2119 } 2120 2121 pw.print("Required constraints:"); 2122 dumpConstraints(pw, requiredConstraints); 2123 pw.println(); 2124 pw.print("Dynamic constraints:"); 2125 dumpConstraints(pw, mDynamicConstraints); 2126 pw.println(); 2127 if (full) { 2128 pw.print("Satisfied constraints:"); 2129 dumpConstraints(pw, satisfiedConstraints); 2130 pw.println(); 2131 pw.print("Unsatisfied constraints:"); 2132 dumpConstraints(pw, 2133 ((requiredConstraints | CONSTRAINT_WITHIN_QUOTA | CONSTRAINT_TARE_WEALTH) 2134 & ~satisfiedConstraints)); 2135 pw.println(); 2136 2137 pw.println("Constraint history:"); 2138 pw.increaseIndent(); 2139 for (int h = 0; h < NUM_CONSTRAINT_CHANGE_HISTORY; ++h) { 2140 final int idx = (h + mConstraintChangeHistoryIndex) % NUM_CONSTRAINT_CHANGE_HISTORY; 2141 if (mConstraintUpdatedTimesElapsed[idx] == 0) { 2142 continue; 2143 } 2144 TimeUtils.formatDuration(mConstraintUpdatedTimesElapsed[idx], nowElapsed, pw); 2145 // dumpConstraints prepends with a space, so no need to add a space after the = 2146 pw.print(" ="); 2147 dumpConstraints(pw, mConstraintStatusHistory[idx]); 2148 pw.println(); 2149 } 2150 pw.decreaseIndent(); 2151 2152 if (appHasDozeExemption) { 2153 pw.println("Doze whitelisted: true"); 2154 } 2155 if (uidActive) { 2156 pw.println("Uid: active"); 2157 } 2158 if (job.isExemptedFromAppStandby()) { 2159 pw.println("Is exempted from app standby"); 2160 } 2161 } 2162 if (trackingControllers != 0) { 2163 pw.print("Tracking:"); 2164 if ((trackingControllers&TRACKING_BATTERY) != 0) pw.print(" BATTERY"); 2165 if ((trackingControllers&TRACKING_CONNECTIVITY) != 0) pw.print(" CONNECTIVITY"); 2166 if ((trackingControllers&TRACKING_CONTENT) != 0) pw.print(" CONTENT"); 2167 if ((trackingControllers&TRACKING_IDLE) != 0) pw.print(" IDLE"); 2168 if ((trackingControllers&TRACKING_STORAGE) != 0) pw.print(" STORAGE"); 2169 if ((trackingControllers&TRACKING_TIME) != 0) pw.print(" TIME"); 2170 if ((trackingControllers & TRACKING_QUOTA) != 0) pw.print(" QUOTA"); 2171 pw.println(); 2172 } 2173 2174 pw.println("Implicit constraints:"); 2175 pw.increaseIndent(); 2176 pw.print("readyNotDozing: "); 2177 pw.println(mReadyNotDozing); 2178 pw.print("readyNotRestrictedInBg: "); 2179 pw.println(mReadyNotRestrictedInBg); 2180 if (!job.isPeriodic() && hasDeadlineConstraint()) { 2181 pw.print("readyDeadlineSatisfied: "); 2182 pw.println(mReadyDeadlineSatisfied); 2183 } 2184 if (mDynamicConstraints != 0) { 2185 pw.print("readyDynamicSatisfied: "); 2186 pw.println(mReadyDynamicSatisfied); 2187 } 2188 pw.print("readyComponentEnabled: "); 2189 pw.println(serviceInfo != null); 2190 if ((getFlags() & JobInfo.FLAG_EXPEDITED) != 0) { 2191 pw.print("expeditedQuotaApproved: "); 2192 pw.print(mExpeditedQuotaApproved); 2193 pw.print(" expeditedTareApproved: "); 2194 pw.print(mExpeditedTareApproved); 2195 pw.print(" (started as EJ: "); 2196 pw.print(startedAsExpeditedJob); 2197 pw.println(")"); 2198 } 2199 pw.decreaseIndent(); 2200 2201 if (changedAuthorities != null) { 2202 pw.println("Changed authorities:"); 2203 pw.increaseIndent(); 2204 for (int i=0; i<changedAuthorities.size(); i++) { 2205 pw.println(changedAuthorities.valueAt(i)); 2206 } 2207 pw.decreaseIndent(); 2208 } 2209 if (changedUris != null) { 2210 pw.println("Changed URIs:"); 2211 pw.increaseIndent(); 2212 for (int i = 0; i < changedUris.size(); i++) { 2213 pw.println(changedUris.valueAt(i)); 2214 } 2215 pw.decreaseIndent(); 2216 } 2217 if (network != null) { 2218 pw.print("Network: "); pw.println(network); 2219 } 2220 if (pendingWork != null && pendingWork.size() > 0) { 2221 pw.println("Pending work:"); 2222 for (int i = 0; i < pendingWork.size(); i++) { 2223 dumpJobWorkItem(pw, pendingWork.get(i), i); 2224 } 2225 } 2226 if (executingWork != null && executingWork.size() > 0) { 2227 pw.println("Executing work:"); 2228 for (int i = 0; i < executingWork.size(); i++) { 2229 dumpJobWorkItem(pw, executingWork.get(i), i); 2230 } 2231 } 2232 pw.print("Standby bucket: "); 2233 pw.println(getBucketName()); 2234 pw.increaseIndent(); 2235 if (whenStandbyDeferred != 0) { 2236 pw.print("Deferred since: "); 2237 TimeUtils.formatDuration(whenStandbyDeferred, nowElapsed, pw); 2238 pw.println(); 2239 } 2240 if (mFirstForceBatchedTimeElapsed != 0) { 2241 pw.print("Time since first force batch attempt: "); 2242 TimeUtils.formatDuration(mFirstForceBatchedTimeElapsed, nowElapsed, pw); 2243 pw.println(); 2244 } 2245 pw.decreaseIndent(); 2246 2247 pw.print("Enqueue time: "); 2248 TimeUtils.formatDuration(enqueueTime, nowElapsed, pw); 2249 pw.println(); 2250 pw.print("Run time: earliest="); 2251 formatRunTime(pw, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, nowElapsed); 2252 pw.print(", latest="); 2253 formatRunTime(pw, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, nowElapsed); 2254 pw.print(", original latest="); 2255 formatRunTime(pw, mOriginalLatestRunTimeElapsedMillis, NO_LATEST_RUNTIME, nowElapsed); 2256 pw.println(); 2257 if (numFailures != 0) { 2258 pw.print("Num failures: "); pw.println(numFailures); 2259 } 2260 if (mLastSuccessfulRunTime != 0) { 2261 pw.print("Last successful run: "); 2262 pw.println(formatTime(mLastSuccessfulRunTime)); 2263 } 2264 if (mLastFailedRunTime != 0) { 2265 pw.print("Last failed run: "); 2266 pw.println(formatTime(mLastFailedRunTime)); 2267 } 2268 } 2269 formatTime(long time)2270 private static CharSequence formatTime(long time) { 2271 return DateFormat.format("yyyy-MM-dd HH:mm:ss", time); 2272 } 2273 dump(ProtoOutputStream proto, long fieldId, boolean full, long elapsedRealtimeMillis)2274 public void dump(ProtoOutputStream proto, long fieldId, boolean full, long elapsedRealtimeMillis) { 2275 final long token = proto.start(fieldId); 2276 2277 proto.write(JobStatusDumpProto.CALLING_UID, callingUid); 2278 proto.write(JobStatusDumpProto.TAG, tag); 2279 proto.write(JobStatusDumpProto.SOURCE_UID, getSourceUid()); 2280 proto.write(JobStatusDumpProto.SOURCE_USER_ID, getSourceUserId()); 2281 proto.write(JobStatusDumpProto.SOURCE_PACKAGE_NAME, getSourcePackageName()); 2282 2283 if (full) { 2284 final long jiToken = proto.start(JobStatusDumpProto.JOB_INFO); 2285 2286 job.getService().dumpDebug(proto, JobStatusDumpProto.JobInfo.SERVICE); 2287 2288 proto.write(JobStatusDumpProto.JobInfo.IS_PERIODIC, job.isPeriodic()); 2289 proto.write(JobStatusDumpProto.JobInfo.PERIOD_INTERVAL_MS, job.getIntervalMillis()); 2290 proto.write(JobStatusDumpProto.JobInfo.PERIOD_FLEX_MS, job.getFlexMillis()); 2291 2292 proto.write(JobStatusDumpProto.JobInfo.IS_PERSISTED, job.isPersisted()); 2293 proto.write(JobStatusDumpProto.JobInfo.PRIORITY, job.getBias()); 2294 proto.write(JobStatusDumpProto.JobInfo.FLAGS, job.getFlags()); 2295 proto.write(JobStatusDumpProto.INTERNAL_FLAGS, getInternalFlags()); 2296 // Foreground exemption can be determined from internal flags value. 2297 2298 proto.write(JobStatusDumpProto.JobInfo.REQUIRES_CHARGING, job.isRequireCharging()); 2299 proto.write(JobStatusDumpProto.JobInfo.REQUIRES_BATTERY_NOT_LOW, job.isRequireBatteryNotLow()); 2300 proto.write(JobStatusDumpProto.JobInfo.REQUIRES_DEVICE_IDLE, job.isRequireDeviceIdle()); 2301 2302 if (job.getTriggerContentUris() != null) { 2303 for (int i = 0; i < job.getTriggerContentUris().length; i++) { 2304 final long tcuToken = proto.start(JobStatusDumpProto.JobInfo.TRIGGER_CONTENT_URIS); 2305 JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i]; 2306 2307 proto.write(JobStatusDumpProto.JobInfo.TriggerContentUri.FLAGS, trig.getFlags()); 2308 Uri u = trig.getUri(); 2309 if (u != null) { 2310 proto.write(JobStatusDumpProto.JobInfo.TriggerContentUri.URI, u.toString()); 2311 } 2312 2313 proto.end(tcuToken); 2314 } 2315 if (job.getTriggerContentUpdateDelay() >= 0) { 2316 proto.write(JobStatusDumpProto.JobInfo.TRIGGER_CONTENT_UPDATE_DELAY_MS, 2317 job.getTriggerContentUpdateDelay()); 2318 } 2319 if (job.getTriggerContentMaxDelay() >= 0) { 2320 proto.write(JobStatusDumpProto.JobInfo.TRIGGER_CONTENT_MAX_DELAY_MS, 2321 job.getTriggerContentMaxDelay()); 2322 } 2323 } 2324 if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) { 2325 job.getExtras().dumpDebug(proto, JobStatusDumpProto.JobInfo.EXTRAS); 2326 } 2327 if (job.getTransientExtras() != null && !job.getTransientExtras().isDefinitelyEmpty()) { 2328 job.getTransientExtras().dumpDebug(proto, JobStatusDumpProto.JobInfo.TRANSIENT_EXTRAS); 2329 } 2330 if (job.getClipData() != null) { 2331 job.getClipData().dumpDebug(proto, JobStatusDumpProto.JobInfo.CLIP_DATA); 2332 } 2333 if (uriPerms != null) { 2334 uriPerms.dump(proto, JobStatusDumpProto.JobInfo.GRANTED_URI_PERMISSIONS); 2335 } 2336 if (mTotalNetworkDownloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { 2337 proto.write(JobStatusDumpProto.JobInfo.TOTAL_NETWORK_DOWNLOAD_BYTES, 2338 mTotalNetworkDownloadBytes); 2339 } 2340 if (mTotalNetworkUploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { 2341 proto.write(JobStatusDumpProto.JobInfo.TOTAL_NETWORK_UPLOAD_BYTES, 2342 mTotalNetworkUploadBytes); 2343 } 2344 proto.write(JobStatusDumpProto.JobInfo.MIN_LATENCY_MS, job.getMinLatencyMillis()); 2345 proto.write(JobStatusDumpProto.JobInfo.MAX_EXECUTION_DELAY_MS, job.getMaxExecutionDelayMillis()); 2346 2347 final long bpToken = proto.start(JobStatusDumpProto.JobInfo.BACKOFF_POLICY); 2348 proto.write(JobStatusDumpProto.JobInfo.Backoff.POLICY, job.getBackoffPolicy()); 2349 proto.write(JobStatusDumpProto.JobInfo.Backoff.INITIAL_BACKOFF_MS, 2350 job.getInitialBackoffMillis()); 2351 proto.end(bpToken); 2352 2353 proto.write(JobStatusDumpProto.JobInfo.HAS_EARLY_CONSTRAINT, job.hasEarlyConstraint()); 2354 proto.write(JobStatusDumpProto.JobInfo.HAS_LATE_CONSTRAINT, job.hasLateConstraint()); 2355 2356 proto.end(jiToken); 2357 } 2358 2359 dumpConstraints(proto, JobStatusDumpProto.REQUIRED_CONSTRAINTS, requiredConstraints); 2360 dumpConstraints(proto, JobStatusDumpProto.DYNAMIC_CONSTRAINTS, mDynamicConstraints); 2361 if (full) { 2362 dumpConstraints(proto, JobStatusDumpProto.SATISFIED_CONSTRAINTS, satisfiedConstraints); 2363 dumpConstraints(proto, JobStatusDumpProto.UNSATISFIED_CONSTRAINTS, 2364 ((requiredConstraints | CONSTRAINT_WITHIN_QUOTA) & ~satisfiedConstraints)); 2365 proto.write(JobStatusDumpProto.IS_DOZE_WHITELISTED, appHasDozeExemption); 2366 proto.write(JobStatusDumpProto.IS_UID_ACTIVE, uidActive); 2367 proto.write(JobStatusDumpProto.IS_EXEMPTED_FROM_APP_STANDBY, 2368 job.isExemptedFromAppStandby()); 2369 } 2370 2371 // Tracking controllers 2372 if ((trackingControllers&TRACKING_BATTERY) != 0) { 2373 proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS, 2374 JobStatusDumpProto.TRACKING_BATTERY); 2375 } 2376 if ((trackingControllers&TRACKING_CONNECTIVITY) != 0) { 2377 proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS, 2378 JobStatusDumpProto.TRACKING_CONNECTIVITY); 2379 } 2380 if ((trackingControllers&TRACKING_CONTENT) != 0) { 2381 proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS, 2382 JobStatusDumpProto.TRACKING_CONTENT); 2383 } 2384 if ((trackingControllers&TRACKING_IDLE) != 0) { 2385 proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS, 2386 JobStatusDumpProto.TRACKING_IDLE); 2387 } 2388 if ((trackingControllers&TRACKING_STORAGE) != 0) { 2389 proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS, 2390 JobStatusDumpProto.TRACKING_STORAGE); 2391 } 2392 if ((trackingControllers&TRACKING_TIME) != 0) { 2393 proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS, 2394 JobStatusDumpProto.TRACKING_TIME); 2395 } 2396 if ((trackingControllers & TRACKING_QUOTA) != 0) { 2397 proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS, 2398 JobStatusDumpProto.TRACKING_QUOTA); 2399 } 2400 2401 // Implicit constraints 2402 final long icToken = proto.start(JobStatusDumpProto.IMPLICIT_CONSTRAINTS); 2403 proto.write(JobStatusDumpProto.ImplicitConstraints.IS_NOT_DOZING, mReadyNotDozing); 2404 proto.write(JobStatusDumpProto.ImplicitConstraints.IS_NOT_RESTRICTED_IN_BG, 2405 mReadyNotRestrictedInBg); 2406 // mReadyDeadlineSatisfied isn't an implicit constraint...and can be determined from other 2407 // field values. 2408 proto.write(JobStatusDumpProto.ImplicitConstraints.IS_DYNAMIC_SATISFIED, 2409 mReadyDynamicSatisfied); 2410 proto.end(icToken); 2411 2412 if (changedAuthorities != null) { 2413 for (int k = 0; k < changedAuthorities.size(); k++) { 2414 proto.write(JobStatusDumpProto.CHANGED_AUTHORITIES, changedAuthorities.valueAt(k)); 2415 } 2416 } 2417 if (changedUris != null) { 2418 for (int i = 0; i < changedUris.size(); i++) { 2419 Uri u = changedUris.valueAt(i); 2420 proto.write(JobStatusDumpProto.CHANGED_URIS, u.toString()); 2421 } 2422 } 2423 2424 if (pendingWork != null) { 2425 for (int i = 0; i < pendingWork.size(); i++) { 2426 dumpJobWorkItem(proto, JobStatusDumpProto.PENDING_WORK, pendingWork.get(i)); 2427 } 2428 } 2429 if (executingWork != null) { 2430 for (int i = 0; i < executingWork.size(); i++) { 2431 dumpJobWorkItem(proto, JobStatusDumpProto.EXECUTING_WORK, executingWork.get(i)); 2432 } 2433 } 2434 2435 proto.write(JobStatusDumpProto.STANDBY_BUCKET, standbyBucket); 2436 proto.write(JobStatusDumpProto.ENQUEUE_DURATION_MS, elapsedRealtimeMillis - enqueueTime); 2437 proto.write(JobStatusDumpProto.TIME_SINCE_FIRST_DEFERRAL_MS, 2438 whenStandbyDeferred == 0 ? 0 : elapsedRealtimeMillis - whenStandbyDeferred); 2439 proto.write(JobStatusDumpProto.TIME_SINCE_FIRST_FORCE_BATCH_ATTEMPT_MS, 2440 mFirstForceBatchedTimeElapsed == 0 2441 ? 0 : elapsedRealtimeMillis - mFirstForceBatchedTimeElapsed); 2442 if (earliestRunTimeElapsedMillis == NO_EARLIEST_RUNTIME) { 2443 proto.write(JobStatusDumpProto.TIME_UNTIL_EARLIEST_RUNTIME_MS, 0); 2444 } else { 2445 proto.write(JobStatusDumpProto.TIME_UNTIL_EARLIEST_RUNTIME_MS, 2446 earliestRunTimeElapsedMillis - elapsedRealtimeMillis); 2447 } 2448 if (latestRunTimeElapsedMillis == NO_LATEST_RUNTIME) { 2449 proto.write(JobStatusDumpProto.TIME_UNTIL_LATEST_RUNTIME_MS, 0); 2450 } else { 2451 proto.write(JobStatusDumpProto.TIME_UNTIL_LATEST_RUNTIME_MS, 2452 latestRunTimeElapsedMillis - elapsedRealtimeMillis); 2453 } 2454 proto.write(JobStatusDumpProto.ORIGINAL_LATEST_RUNTIME_ELAPSED, 2455 mOriginalLatestRunTimeElapsedMillis); 2456 2457 proto.write(JobStatusDumpProto.NUM_FAILURES, numFailures); 2458 proto.write(JobStatusDumpProto.LAST_SUCCESSFUL_RUN_TIME, mLastSuccessfulRunTime); 2459 proto.write(JobStatusDumpProto.LAST_FAILED_RUN_TIME, mLastFailedRunTime); 2460 2461 proto.end(token); 2462 } 2463 } 2464