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