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 android.text.format.DateUtils.HOUR_IN_MILLIS; 20 21 import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX; 22 import static com.android.server.job.JobSchedulerService.EXEMPTED_INDEX; 23 import static com.android.server.job.JobSchedulerService.NEVER_INDEX; 24 import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; 25 import static com.android.server.job.JobSchedulerService.WORKING_INDEX; 26 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; 27 28 import android.annotation.ElapsedRealtimeLong; 29 import android.annotation.NonNull; 30 import android.annotation.Nullable; 31 import android.app.AppGlobals; 32 import android.app.job.JobInfo; 33 import android.app.job.JobParameters; 34 import android.app.job.JobScheduler; 35 import android.app.job.JobWorkItem; 36 import android.app.job.UserVisibleJobSummary; 37 import android.content.ClipData; 38 import android.content.ComponentName; 39 import android.net.Network; 40 import android.net.NetworkRequest; 41 import android.net.Uri; 42 import android.os.RemoteException; 43 import android.os.UserHandle; 44 import android.provider.MediaStore; 45 import android.text.format.DateFormat; 46 import android.util.ArrayMap; 47 import android.util.ArraySet; 48 import android.util.IndentingPrintWriter; 49 import android.util.Pair; 50 import android.util.Range; 51 import android.util.Slog; 52 import android.util.TimeUtils; 53 import android.util.proto.ProtoOutputStream; 54 55 import com.android.internal.annotations.GuardedBy; 56 import com.android.internal.annotations.VisibleForTesting; 57 import com.android.internal.util.ArrayUtils; 58 import com.android.internal.util.FrameworkStatsLog; 59 import com.android.modules.expresslog.Counter; 60 import com.android.server.LocalServices; 61 import com.android.server.job.GrantedUriPermissions; 62 import com.android.server.job.JobSchedulerInternal; 63 import com.android.server.job.JobSchedulerService; 64 import com.android.server.job.JobServerProtoEnums; 65 import com.android.server.job.JobStatusDumpProto; 66 import com.android.server.job.JobStatusShortInfoProto; 67 68 import dalvik.annotation.optimization.NeverCompile; 69 70 import java.io.PrintWriter; 71 import java.security.MessageDigest; 72 import java.util.ArrayList; 73 import java.util.Arrays; 74 import java.util.Collections; 75 import java.util.Objects; 76 import java.util.Random; 77 import java.util.function.Predicate; 78 79 /** 80 * Uniquely identifies a job internally. 81 * Created from the public {@link android.app.job.JobInfo} object when it lands on the scheduler. 82 * Contains current state of the requirements of the job, as well as a function to evaluate 83 * whether it's ready to run. 84 * This object is shared among the various controllers - hence why the different fields are atomic. 85 * This isn't strictly necessary because each controller is only interested in a specific field, 86 * and the receivers that are listening for global state change will all run on the main looper, 87 * but we don't enforce that so this is safer. 88 * 89 * Test: atest com.android.server.job.controllers.JobStatusTest 90 * @hide 91 */ 92 public final class JobStatus { 93 private static final String TAG = "JobScheduler.JobStatus"; 94 static final boolean DEBUG = JobSchedulerService.DEBUG; 95 96 private static MessageDigest sMessageDigest; 97 /** Cache of namespace to hash to reduce how often we need to generate the namespace hash. */ 98 @GuardedBy("sNamespaceHashCache") 99 private static final ArrayMap<String, String> sNamespaceHashCache = new ArrayMap<>(); 100 /** Maximum size of {@link #sNamespaceHashCache}. */ 101 private static final int MAX_NAMESPACE_CACHE_SIZE = 128; 102 103 private static final int NUM_CONSTRAINT_CHANGE_HISTORY = 10; 104 105 public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE; 106 public static final long NO_EARLIEST_RUNTIME = 0L; 107 108 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 109 public static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; // 1 < 0 110 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 111 public static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE; // 1 << 2 112 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 113 public static final int CONSTRAINT_BATTERY_NOT_LOW = 114 JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1 115 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 116 public static final int CONSTRAINT_STORAGE_NOT_LOW = 117 JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW; // 1 << 3 118 public static final int CONSTRAINT_TIMING_DELAY = 1 << 31; 119 public static final int CONSTRAINT_DEADLINE = 1 << 30; 120 public static final int CONSTRAINT_CONNECTIVITY = 1 << 28; 121 static final int CONSTRAINT_TARE_WEALTH = 1 << 27; // Implicit constraint 122 public static final int CONSTRAINT_CONTENT_TRIGGER = 1 << 26; 123 static final int CONSTRAINT_DEVICE_NOT_DOZING = 1 << 25; // Implicit constraint 124 static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24; // Implicit constraint 125 static final int CONSTRAINT_PREFETCH = 1 << 23; 126 static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1 << 22; // Implicit constraint 127 static final int CONSTRAINT_FLEXIBLE = 1 << 21; 128 129 private static final int IMPLICIT_CONSTRAINTS = 0 130 | CONSTRAINT_BACKGROUND_NOT_RESTRICTED 131 | CONSTRAINT_DEVICE_NOT_DOZING 132 | CONSTRAINT_TARE_WEALTH 133 | CONSTRAINT_WITHIN_QUOTA; 134 135 // The following set of dynamic constraints are for specific use cases (as explained in their 136 // relative naming and comments). Right now, they apply different constraints, which is fine, 137 // but if in the future, we have overlapping dynamic constraint sets, removing one constraint 138 // set may accidentally remove a constraint applied by another dynamic set. 139 // TODO: properly handle overlapping dynamic constraint sets 140 141 /** 142 * The additional set of dynamic constraints that must be met if the job's effective bucket is 143 * {@link JobSchedulerService#RESTRICTED_INDEX}. Connectivity can be ignored if the job doesn't 144 * need network. 145 */ 146 private static final int DYNAMIC_RESTRICTED_CONSTRAINTS = 147 CONSTRAINT_BATTERY_NOT_LOW 148 | CONSTRAINT_CHARGING 149 | CONSTRAINT_CONNECTIVITY 150 | CONSTRAINT_IDLE; 151 152 /** 153 * Keeps track of how many flexible constraints must be satisfied for the job to execute. 154 */ 155 private final int mNumRequiredFlexibleConstraints; 156 157 /** 158 * Number of required flexible constraints that have been dropped. 159 */ 160 private int mNumDroppedFlexibleConstraints; 161 162 /** If the job is going to be passed an unmetered network. */ 163 private boolean mHasAccessToUnmetered; 164 165 /** If the effective bucket has been downgraded once due to being buggy. */ 166 private boolean mIsDowngradedDueToBuggyApp; 167 168 /** 169 * The additional set of dynamic constraints that must be met if this is an expedited job that 170 * had a long enough run while the device was Dozing or in battery saver. 171 */ 172 private static final int DYNAMIC_EXPEDITED_DEFERRAL_CONSTRAINTS = 173 CONSTRAINT_DEVICE_NOT_DOZING | CONSTRAINT_BACKGROUND_NOT_RESTRICTED; 174 175 /** 176 * Standard media URIs that contain the media files that might be important to the user. 177 * @see #mHasMediaBackupExemption 178 */ 179 private static final Uri[] MEDIA_URIS_FOR_STANDBY_EXEMPTION = { 180 MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 181 MediaStore.Video.Media.EXTERNAL_CONTENT_URI, 182 }; 183 184 /** 185 * The constraints that we want to log to statsd. 186 * 187 * Constraints that can be inferred from other atoms have been excluded to avoid logging too 188 * much information and to reduce redundancy: 189 * 190 * * CONSTRAINT_CHARGING can be inferred with PluggedStateChanged (Atom #32) 191 * * CONSTRAINT_BATTERY_NOT_LOW can be inferred with BatteryLevelChanged (Atom #30) 192 * * CONSTRAINT_CONNECTIVITY can be partially inferred with ConnectivityStateChanged 193 * (Atom #98) and BatterySaverModeStateChanged (Atom #20). 194 * * CONSTRAINT_DEVICE_NOT_DOZING can be mostly inferred with DeviceIdleModeStateChanged 195 * (Atom #21) 196 * * CONSTRAINT_BACKGROUND_NOT_RESTRICTED can be inferred with BatterySaverModeStateChanged 197 * (Atom #20) 198 * * CONSTRAINT_STORAGE_NOT_LOW can be inferred with LowStorageStateChanged (Atom #130) 199 */ 200 private static final int STATSD_CONSTRAINTS_TO_LOG = CONSTRAINT_CONTENT_TRIGGER 201 | CONSTRAINT_DEADLINE 202 | CONSTRAINT_PREFETCH 203 | CONSTRAINT_TARE_WEALTH 204 | CONSTRAINT_TIMING_DELAY 205 | CONSTRAINT_WITHIN_QUOTA; 206 207 // TODO(b/129954980): ensure this doesn't spam statsd, especially at boot 208 private static final boolean STATS_LOG_ENABLED = false; 209 210 // No override. 211 public static final int OVERRIDE_NONE = 0; 212 // Override to improve sorting order. Does not affect constraint evaluation. 213 public static final int OVERRIDE_SORTING = 1; 214 // Soft override: ignore constraints like time that don't affect API availability 215 public static final int OVERRIDE_SOFT = 2; 216 // Full override: ignore all constraints including API-affecting like connectivity 217 public static final int OVERRIDE_FULL = 3; 218 219 /** If not specified, trigger update delay is 10 seconds. */ 220 public static final long DEFAULT_TRIGGER_UPDATE_DELAY = 10*1000; 221 222 /** The minimum possible update delay is 1/2 second. */ 223 public static final long MIN_TRIGGER_UPDATE_DELAY = 500; 224 225 /** If not specified, trigger maximum delay is 2 minutes. */ 226 public static final long DEFAULT_TRIGGER_MAX_DELAY = 2*60*1000; 227 228 /** The minimum possible update delay is 1 second. */ 229 public static final long MIN_TRIGGER_MAX_DELAY = 1000; 230 231 final JobInfo job; 232 /** 233 * Uid of the package requesting this job. This can differ from the "source" 234 * uid when the job was scheduled on the app's behalf, such as with the jobs 235 * that underly Sync Manager operation. 236 */ 237 final int callingUid; 238 final String batteryName; 239 240 /** 241 * Identity of the app in which the job is hosted. 242 */ 243 final String sourcePackageName; 244 final int sourceUserId; 245 final int sourceUid; 246 final String sourceTag; 247 @Nullable 248 private final String mNamespace; 249 @Nullable 250 private final String mNamespaceHash; 251 /** An ID that can be used to uniquely identify the job when logging statsd metrics. */ 252 private final long mLoggingJobId; 253 254 final String tag; 255 256 private GrantedUriPermissions uriPerms; 257 private boolean prepared; 258 259 static final boolean DEBUG_PREPARE = true; 260 private Throwable unpreparedPoint = null; 261 262 /** 263 * Earliest point in the future at which this job will be eligible to run. A value of 0 264 * indicates there is no delay constraint. See {@link #hasTimingDelayConstraint()}. 265 */ 266 private final long earliestRunTimeElapsedMillis; 267 /** 268 * Latest point in the future at which this job must be run. A value of {@link Long#MAX_VALUE} 269 * indicates there is no deadline constraint. See {@link #hasDeadlineConstraint()}. 270 */ 271 private final long latestRunTimeElapsedMillis; 272 273 /** 274 * Valid only for periodic jobs. The original latest point in the future at which this 275 * job was expected to run. 276 */ 277 private long mOriginalLatestRunTimeElapsedMillis; 278 279 /** 280 * How many times this job has failed to complete on its own 281 * (via {@link android.app.job.JobService#jobFinished(JobParameters, boolean)} or because of 282 * a timeout). 283 * This count doesn't include most times JobScheduler decided to stop the job 284 * (via {@link android.app.job.JobService#onStopJob(JobParameters)}. 285 */ 286 private final int numFailures; 287 288 /** 289 * The number of times JobScheduler has forced this job to stop due to reasons mostly outside 290 * of the app's control. 291 */ 292 private final int mNumSystemStops; 293 294 /** 295 * Which app standby bucket this job's app is in. Updated when the app is moved to a 296 * different bucket. 297 */ 298 private int standbyBucket; 299 300 /** 301 * Whether we've logged an error due to standby bucket mismatch with active uid state. 302 */ 303 private boolean mLoggedBucketMismatch; 304 305 /** 306 * Debugging: timestamp if we ever defer this job based on standby bucketing, this 307 * is when we did so. 308 */ 309 private long whenStandbyDeferred; 310 311 /** The first time this job was force batched. */ 312 private long mFirstForceBatchedTimeElapsed; 313 314 // Constraints. 315 final int requiredConstraints; 316 private final int mPreferredConstraints; 317 private final int mRequiredConstraintsOfInterest; 318 int satisfiedConstraints = 0; 319 private int mSatisfiedConstraintsOfInterest = 0; 320 /** 321 * Set of constraints that must be satisfied for the job if/because it's in the RESTRICTED 322 * bucket. 323 */ 324 private int mDynamicConstraints = 0; 325 326 /** 327 * Indicates whether the job is responsible for backing up media, so we can be lenient in 328 * applying standby throttling. 329 * 330 * Doesn't exempt jobs with a deadline constraint, as they can be started without any content or 331 * network changes, in which case this exemption does not make sense. 332 */ 333 private boolean mHasMediaBackupExemption; 334 private final boolean mHasExemptedMediaUrisOnly; 335 336 // Set to true if doze constraint was satisfied due to app being whitelisted. 337 boolean appHasDozeExemption; 338 339 // Set to true when the app is "active" per AppStateTracker 340 public boolean uidActive; 341 342 /** 343 * Flag for {@link #trackingControllers}: the battery controller is currently tracking this job. 344 */ 345 public static final int TRACKING_BATTERY = 1<<0; 346 /** 347 * Flag for {@link #trackingControllers}: the network connectivity controller is currently 348 * tracking this job. 349 */ 350 public static final int TRACKING_CONNECTIVITY = 1<<1; 351 /** 352 * Flag for {@link #trackingControllers}: the content observer controller is currently 353 * tracking this job. 354 */ 355 public static final int TRACKING_CONTENT = 1<<2; 356 /** 357 * Flag for {@link #trackingControllers}: the idle controller is currently tracking this job. 358 */ 359 public static final int TRACKING_IDLE = 1<<3; 360 /** 361 * Flag for {@link #trackingControllers}: the storage controller is currently tracking this job. 362 */ 363 public static final int TRACKING_STORAGE = 1<<4; 364 /** 365 * Flag for {@link #trackingControllers}: the time controller is currently tracking this job. 366 */ 367 public static final int TRACKING_TIME = 1<<5; 368 /** 369 * Flag for {@link #trackingControllers}: the quota controller is currently tracking this job. 370 */ 371 public static final int TRACKING_QUOTA = 1 << 6; 372 373 /** 374 * Flag for {@link #trackingControllers}: the flexibility controller is currently tracking this 375 * job. 376 */ 377 public static final int TRACKING_FLEXIBILITY = 1 << 7; 378 379 /** 380 * Bit mask of controllers that are currently tracking the job. 381 */ 382 private int trackingControllers; 383 384 /** 385 * Flag for {@link #mInternalFlags}: this job was scheduled when the app that owns the job 386 * service (not necessarily the caller) was in the foreground and the job has no time 387 * constraints, which makes it exempted from the battery saver job restriction. 388 * 389 * @hide 390 */ 391 public static final int INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION = 1 << 0; 392 /** 393 * Flag for {@link #mInternalFlags}: this job was stopped by the user for some reason 394 * and is thus considered demoted from whatever privileged state it had in the past. 395 */ 396 public static final int INTERNAL_FLAG_DEMOTED_BY_USER = 1 << 1; 397 /** 398 * Flag for {@link #mInternalFlags}: this job is demoted by the system 399 * from running as a user-initiated job. 400 */ 401 public static final int INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ = 1 << 2; 402 403 /** Minimum difference between start and end time to have flexible constraint */ 404 @VisibleForTesting 405 static final long MIN_WINDOW_FOR_FLEXIBILITY_MS = HOUR_IN_MILLIS; 406 /** 407 * Versatile, persistable flags for a job that's updated within the system server, 408 * as opposed to {@link JobInfo#flags} that's set by callers. 409 */ 410 private int mInternalFlags; 411 412 /** 413 * The cumulative amount of time this job has run for, including previous executions. 414 * This is reset for periodic jobs upon a successful job execution. 415 */ 416 private long mCumulativeExecutionTimeMs; 417 418 // These are filled in by controllers when preparing for execution. 419 public ArraySet<Uri> changedUris; 420 public ArraySet<String> changedAuthorities; 421 public Network network; 422 public String serviceProcessName; 423 424 /** The evaluated bias of the job when it started running. */ 425 public int lastEvaluatedBias; 426 427 /** 428 * Whether or not this particular JobStatus instance was treated as an EJ when it started 429 * running. This isn't copied over when a job is rescheduled. 430 */ 431 public boolean startedAsExpeditedJob = false; 432 /** 433 * Whether or not this particular JobStatus instance was treated as a user-initiated job 434 * when it started running. This isn't copied over when a job is rescheduled. 435 */ 436 public boolean startedAsUserInitiatedJob = false; 437 /** 438 * Whether this particular JobStatus instance started with the foreground flag 439 * (or more accurately, did <b>not</b> have the 440 * {@link android.content.Context#BIND_NOT_FOREGROUND} flag 441 * included in its binding flags when started). 442 */ 443 public boolean startedWithForegroundFlag = false; 444 445 public boolean startedWithImmediacyPrivilege = false; 446 447 // If non-null, this is work that has been enqueued for the job. 448 public ArrayList<JobWorkItem> pendingWork; 449 450 // If non-null, this is work that is currently being executed. 451 public ArrayList<JobWorkItem> executingWork; 452 453 public int nextPendingWorkId = 1; 454 455 // Used by shell commands 456 public int overrideState = JobStatus.OVERRIDE_NONE; 457 458 // When this job was enqueued, for ordering. (in elapsedRealtimeMillis) 459 @ElapsedRealtimeLong 460 public long enqueueTime; 461 462 // Metrics about queue latency. (in uptimeMillis) 463 public long madePending; 464 public long madeActive; 465 466 /** 467 * Last time a job finished successfully for a periodic job, in the currentTimeMillis time, 468 * for dumpsys. 469 */ 470 private long mLastSuccessfulRunTime; 471 472 /** 473 * Last time a job finished unsuccessfully, in the currentTimeMillis time, for dumpsys. 474 */ 475 private long mLastFailedRunTime; 476 477 /** Whether or not the app is background restricted by the user (FAS). */ 478 private boolean mIsUserBgRestricted; 479 480 /** 481 * Transient: when a job is inflated from disk before we have a reliable RTC clock time, 482 * we retain the canonical (delay, deadline) scheduling tuple read out of the persistent 483 * store in UTC so that we can fix up the job's scheduling criteria once we get a good 484 * wall-clock time. If we have to persist the job again before the clock has been updated, 485 * we record these times again rather than calculating based on the earliest/latest elapsed 486 * time base figures. 487 * 488 * 'first' is the earliest/delay time, and 'second' is the latest/deadline time. 489 */ 490 private Pair<Long, Long> mPersistedUtcTimes; 491 492 private int mConstraintChangeHistoryIndex = 0; 493 private final long[] mConstraintUpdatedTimesElapsed = new long[NUM_CONSTRAINT_CHANGE_HISTORY]; 494 private final int[] mConstraintStatusHistory = new int[NUM_CONSTRAINT_CHANGE_HISTORY]; 495 496 /** 497 * For use only by ContentObserverController: state it is maintaining about content URIs 498 * being observed. 499 */ 500 ContentObserverController.JobInstance contentObserverJobInstance; 501 502 private long mTotalNetworkDownloadBytes = JobInfo.NETWORK_BYTES_UNKNOWN; 503 private long mTotalNetworkUploadBytes = JobInfo.NETWORK_BYTES_UNKNOWN; 504 private long mMinimumNetworkChunkBytes = JobInfo.NETWORK_BYTES_UNKNOWN; 505 506 /** 507 * Whether or not this job is approved to be treated as expedited per quota policy. 508 */ 509 private boolean mExpeditedQuotaApproved; 510 /** 511 * Whether or not this job is approved to be treated as expedited per TARE policy. 512 */ 513 private boolean mExpeditedTareApproved; 514 515 /** 516 * Summary describing this job. Lazily created in {@link #getUserVisibleJobSummary()} 517 * since not every job will need it. 518 */ 519 private UserVisibleJobSummary mUserVisibleJobSummary; 520 521 /////// Booleans that track if a job is ready to run. They should be updated whenever dependent 522 /////// states change. 523 524 /** 525 * The deadline for the job has passed. This is only good for non-periodic jobs. A periodic job 526 * should only run if its constraints are satisfied. 527 * Computed as: NOT periodic AND has deadline constraint AND deadline constraint satisfied. 528 */ 529 private boolean mReadyDeadlineSatisfied; 530 531 /** 532 * The device isn't Dozing or this job is exempt from Dozing (eg. it will be in the foreground 533 * or will run as an expedited job). This implicit constraint must be satisfied. 534 */ 535 private boolean mReadyNotDozing; 536 537 /** 538 * The job is not restricted from running in the background (due to Battery Saver). This 539 * implicit constraint must be satisfied. 540 */ 541 private boolean mReadyNotRestrictedInBg; 542 543 /** The job is within its quota based on its standby bucket. */ 544 private boolean mReadyWithinQuota; 545 546 /** The job has enough credits to run based on TARE. */ 547 private boolean mReadyTareWealth; 548 549 /** The job's dynamic requirements have been satisfied. */ 550 private boolean mReadyDynamicSatisfied; 551 552 /** 553 * The job prefers an unmetered network if it has the connectivity constraint but is 554 * okay with any meteredness. 555 */ 556 private final boolean mPreferUnmetered; 557 558 /** The reason a job most recently went from ready to not ready. */ 559 private int mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED; 560 561 /** 562 * Core constructor for JobStatus instances. All other ctors funnel down to this one. 563 * 564 * @param job The actual requested parameters for the job 565 * @param callingUid Identity of the app that is scheduling the job. This may not be the 566 * app in which the job is implemented; such as with sync jobs. 567 * @param sourcePackageName The package name of the app in which the job will run. 568 * @param sourceUserId The user in which the job will run 569 * @param standbyBucket The standby bucket that the source package is currently assigned to, 570 * cached here for speed of handling during runnability evaluations (and updated when bucket 571 * assignments are changed) 572 * @param namespace The custom namespace the app put this job in. 573 * @param tag A string associated with the job for debugging/logging purposes. 574 * @param numFailures Count of how many times this job has requested a reschedule because 575 * its work was not yet finished. 576 * @param numSystemStops Count of how many times JobScheduler has forced this job to stop due to 577 * factors mostly out of the app's control. 578 * @param earliestRunTimeElapsedMillis Milestone: earliest point in time at which the job 579 * is to be considered runnable 580 * @param latestRunTimeElapsedMillis Milestone: point in time at which the job will be 581 * considered overdue 582 * @param lastSuccessfulRunTime When did we last run this job to completion? 583 * @param lastFailedRunTime When did we last run this job only to have it stop incomplete? 584 * @param internalFlags Non-API property flags about this job 585 */ JobStatus(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId, int standbyBucket, @Nullable String namespace, String tag, int numFailures, int numSystemStops, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, long lastSuccessfulRunTime, long lastFailedRunTime, long cumulativeExecutionTimeMs, int internalFlags, int dynamicConstraints)586 private JobStatus(JobInfo job, int callingUid, String sourcePackageName, 587 int sourceUserId, int standbyBucket, @Nullable String namespace, String tag, 588 int numFailures, int numSystemStops, 589 long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, 590 long lastSuccessfulRunTime, long lastFailedRunTime, long cumulativeExecutionTimeMs, 591 int internalFlags, 592 int dynamicConstraints) { 593 this.job = job; 594 this.callingUid = callingUid; 595 this.standbyBucket = standbyBucket; 596 mNamespace = namespace; 597 mNamespaceHash = generateNamespaceHash(namespace); 598 mLoggingJobId = generateLoggingId(namespace, job.getId()); 599 600 int tempSourceUid = -1; 601 if (sourceUserId != -1 && sourcePackageName != null) { 602 try { 603 tempSourceUid = AppGlobals.getPackageManager().getPackageUid(sourcePackageName, 0, 604 sourceUserId); 605 } catch (RemoteException ex) { 606 // Can't happen, PackageManager runs in the same process. 607 } 608 } 609 if (tempSourceUid == -1) { 610 this.sourceUid = callingUid; 611 this.sourceUserId = UserHandle.getUserId(callingUid); 612 this.sourcePackageName = job.getService().getPackageName(); 613 this.sourceTag = null; 614 } else { 615 this.sourceUid = tempSourceUid; 616 this.sourceUserId = sourceUserId; 617 this.sourcePackageName = sourcePackageName; 618 this.sourceTag = tag; 619 } 620 621 final String bnNamespace = namespace == null ? "" : "@" + namespace + "@"; 622 this.batteryName = this.sourceTag != null 623 ? bnNamespace + this.sourceTag + ":" + job.getService().getPackageName() 624 : bnNamespace + job.getService().flattenToShortString(); 625 this.tag = "*job*/" + this.batteryName + "#" + job.getId(); 626 627 this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis; 628 this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis; 629 this.mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsedMillis; 630 this.numFailures = numFailures; 631 mNumSystemStops = numSystemStops; 632 633 int requiredConstraints = job.getConstraintFlags(); 634 if (job.getRequiredNetwork() != null) { 635 requiredConstraints |= CONSTRAINT_CONNECTIVITY; 636 } 637 if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME) { 638 requiredConstraints |= CONSTRAINT_TIMING_DELAY; 639 } 640 if (latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) { 641 requiredConstraints |= CONSTRAINT_DEADLINE; 642 } 643 if (job.isPrefetch()) { 644 requiredConstraints |= CONSTRAINT_PREFETCH; 645 } 646 boolean exemptedMediaUrisOnly = false; 647 if (job.getTriggerContentUris() != null) { 648 requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER; 649 exemptedMediaUrisOnly = true; 650 for (JobInfo.TriggerContentUri uri : job.getTriggerContentUris()) { 651 if (!ArrayUtils.contains(MEDIA_URIS_FOR_STANDBY_EXEMPTION, uri.getUri())) { 652 exemptedMediaUrisOnly = false; 653 break; 654 } 655 } 656 } 657 mHasExemptedMediaUrisOnly = exemptedMediaUrisOnly; 658 659 mPreferredConstraints = job.getPreferredConstraintFlags(); 660 661 // Exposing a preferredNetworkRequest API requires that we make sure that the preferred 662 // NetworkRequest is a subset of the required NetworkRequest. We currently don't have the 663 // code to ensure that, so disable this part for now. 664 // TODO(236261941): look into enabling flexible network constraint requests 665 mPreferUnmetered = false; 666 // && job.getRequiredNetwork() != null 667 // && !job.getRequiredNetwork().hasCapability(NET_CAPABILITY_NOT_METERED); 668 669 final boolean satisfiesMinWindowException = 670 (latestRunTimeElapsedMillis - earliestRunTimeElapsedMillis) 671 >= MIN_WINDOW_FOR_FLEXIBILITY_MS; 672 673 // The first time a job is rescheduled it will not be subject to flexible constraints. 674 // Otherwise, every consecutive reschedule increases a jobs' flexibility deadline. 675 if (mPreferredConstraints != 0 && !isRequestedExpeditedJob() && !job.isUserInitiated() 676 && satisfiesMinWindowException 677 && (numFailures + numSystemStops) != 1) { 678 mNumRequiredFlexibleConstraints = Integer.bitCount(mPreferredConstraints); 679 requiredConstraints |= CONSTRAINT_FLEXIBLE; 680 } else { 681 mNumRequiredFlexibleConstraints = 0; 682 } 683 684 this.requiredConstraints = requiredConstraints; 685 mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST; 686 addDynamicConstraints(dynamicConstraints); 687 mReadyNotDozing = canRunInDoze(); 688 if (standbyBucket == RESTRICTED_INDEX) { 689 addDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS); 690 } else { 691 mReadyDynamicSatisfied = false; 692 } 693 694 mCumulativeExecutionTimeMs = cumulativeExecutionTimeMs; 695 696 mLastSuccessfulRunTime = lastSuccessfulRunTime; 697 mLastFailedRunTime = lastFailedRunTime; 698 699 mInternalFlags = internalFlags; 700 701 updateNetworkBytesLocked(); 702 703 if (job.getRequiredNetwork() != null) { 704 // Later, when we check if a given network satisfies the required 705 // network, we need to know the UID that is requesting it, so push 706 // our source UID into place. 707 final JobInfo.Builder builder = new JobInfo.Builder(job); 708 final NetworkRequest.Builder requestBuilder = 709 new NetworkRequest.Builder(job.getRequiredNetwork()); 710 requestBuilder.setUids( 711 Collections.singleton(new Range<Integer>(this.sourceUid, this.sourceUid))); 712 builder.setRequiredNetwork(requestBuilder.build()); 713 // Don't perform validation checks at this point since we've already passed the 714 // initial validation check. 715 job = builder.build(false, false); 716 } 717 718 updateMediaBackupExemptionStatus(); 719 } 720 721 /** Copy constructor: used specifically when cloning JobStatus objects for persistence, 722 * so we preserve RTC window bounds if the source object has them. */ JobStatus(JobStatus jobStatus)723 public JobStatus(JobStatus jobStatus) { 724 this(jobStatus.getJob(), jobStatus.getUid(), 725 jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(), 726 jobStatus.getStandbyBucket(), jobStatus.getNamespace(), 727 jobStatus.getSourceTag(), jobStatus.getNumFailures(), jobStatus.getNumSystemStops(), 728 jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed(), 729 jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime(), 730 jobStatus.getCumulativeExecutionTimeMs(), 731 jobStatus.getInternalFlags(), jobStatus.mDynamicConstraints); 732 mPersistedUtcTimes = jobStatus.mPersistedUtcTimes; 733 if (jobStatus.mPersistedUtcTimes != null) { 734 if (DEBUG) { 735 Slog.i(TAG, "Cloning job with persisted run times", new RuntimeException("here")); 736 } 737 } 738 if (jobStatus.executingWork != null && jobStatus.executingWork.size() > 0) { 739 executingWork = new ArrayList<>(jobStatus.executingWork); 740 } 741 if (jobStatus.pendingWork != null && jobStatus.pendingWork.size() > 0) { 742 pendingWork = new ArrayList<>(jobStatus.pendingWork); 743 } 744 } 745 746 /** 747 * Create a new JobStatus that was loaded from disk. We ignore the provided 748 * {@link android.app.job.JobInfo} time criteria because we can load a persisted periodic job 749 * from the {@link com.android.server.job.JobStore} and still want to respect its 750 * wallclock runtime rather than resetting it on every boot. 751 * We consider a freshly loaded job to no longer be in back-off, and the associated 752 * standby bucket is whatever the OS thinks it should be at this moment. 753 */ JobStatus(JobInfo job, int callingUid, String sourcePkgName, int sourceUserId, int standbyBucket, @Nullable String namespace, String sourceTag, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, long lastSuccessfulRunTime, long lastFailedRunTime, long cumulativeExecutionTimeMs, Pair<Long, Long> persistedExecutionTimesUTC, int innerFlags, int dynamicConstraints)754 public JobStatus(JobInfo job, int callingUid, String sourcePkgName, int sourceUserId, 755 int standbyBucket, @Nullable String namespace, String sourceTag, 756 long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, 757 long lastSuccessfulRunTime, long lastFailedRunTime, 758 long cumulativeExecutionTimeMs, 759 Pair<Long, Long> persistedExecutionTimesUTC, 760 int innerFlags, int dynamicConstraints) { 761 this(job, callingUid, sourcePkgName, sourceUserId, 762 standbyBucket, namespace, 763 sourceTag, /* numFailures */ 0, /* numSystemStops */ 0, 764 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 765 lastSuccessfulRunTime, lastFailedRunTime, cumulativeExecutionTimeMs, 766 innerFlags, dynamicConstraints); 767 768 // Only during initial inflation do we record the UTC-timebase execution bounds 769 // read from the persistent store. If we ever have to recreate the JobStatus on 770 // the fly, it means we're rescheduling the job; and this means that the calculated 771 // elapsed timebase bounds intrinsically become correct. 772 this.mPersistedUtcTimes = persistedExecutionTimesUTC; 773 if (persistedExecutionTimesUTC != null) { 774 if (DEBUG) { 775 Slog.i(TAG, "+ restored job with RTC times because of bad boot clock"); 776 } 777 } 778 } 779 780 /** Create a new job to be rescheduled with the provided parameters. */ JobStatus(JobStatus rescheduling, long newEarliestRuntimeElapsedMillis, long newLatestRuntimeElapsedMillis, int numFailures, int numSystemStops, long lastSuccessfulRunTime, long lastFailedRunTime, long cumulativeExecutionTimeMs)781 public JobStatus(JobStatus rescheduling, 782 long newEarliestRuntimeElapsedMillis, 783 long newLatestRuntimeElapsedMillis, int numFailures, int numSystemStops, 784 long lastSuccessfulRunTime, long lastFailedRunTime, 785 long cumulativeExecutionTimeMs) { 786 this(rescheduling.job, rescheduling.getUid(), 787 rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(), 788 rescheduling.getStandbyBucket(), rescheduling.getNamespace(), 789 rescheduling.getSourceTag(), numFailures, numSystemStops, 790 newEarliestRuntimeElapsedMillis, 791 newLatestRuntimeElapsedMillis, 792 lastSuccessfulRunTime, lastFailedRunTime, cumulativeExecutionTimeMs, 793 rescheduling.getInternalFlags(), 794 rescheduling.mDynamicConstraints); 795 } 796 797 /** 798 * Create a newly scheduled job. 799 * @param callingUid Uid of the package that scheduled this job. 800 * @param sourcePkg Package name of the app that will actually run the job. Null indicates 801 * that the calling package is the source. 802 * @param sourceUserId User id for whom this job is scheduled. -1 indicates this is same as the 803 * caller. 804 */ createFromJobInfo(JobInfo job, int callingUid, String sourcePkg, int sourceUserId, @Nullable String namespace, String tag)805 public static JobStatus createFromJobInfo(JobInfo job, int callingUid, String sourcePkg, 806 int sourceUserId, @Nullable String namespace, String tag) { 807 final long elapsedNow = sElapsedRealtimeClock.millis(); 808 final long earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis; 809 if (job.isPeriodic()) { 810 // Make sure period is in the interval [min_possible_period, max_possible_period]. 811 final long period = Math.max(JobInfo.getMinPeriodMillis(), 812 Math.min(JobSchedulerService.MAX_ALLOWED_PERIOD_MS, job.getIntervalMillis())); 813 latestRunTimeElapsedMillis = elapsedNow + period; 814 earliestRunTimeElapsedMillis = latestRunTimeElapsedMillis 815 // Make sure flex is in the interval [min_possible_flex, period]. 816 - Math.max(JobInfo.getMinFlexMillis(), Math.min(period, job.getFlexMillis())); 817 } else { 818 earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ? 819 elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME; 820 latestRunTimeElapsedMillis = job.hasLateConstraint() ? 821 elapsedNow + job.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME; 822 } 823 String jobPackage = (sourcePkg != null) ? sourcePkg : job.getService().getPackageName(); 824 825 int standbyBucket = JobSchedulerService.standbyBucketForPackage(jobPackage, 826 sourceUserId, elapsedNow); 827 return new JobStatus(job, callingUid, sourcePkg, sourceUserId, 828 standbyBucket, namespace, tag, /* numFailures */ 0, /* numSystemStops */ 0, 829 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 830 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */, 831 /* cumulativeExecutionTime */ 0, 832 /*innerFlags=*/ 0, /* dynamicConstraints */ 0); 833 } 834 generateLoggingId(@ullable String namespace, int jobId)835 private long generateLoggingId(@Nullable String namespace, int jobId) { 836 if (namespace == null) { 837 return jobId; 838 } 839 return ((long) namespace.hashCode()) << 31 | jobId; 840 } 841 842 @Nullable generateNamespaceHash(@ullable String namespace)843 private static String generateNamespaceHash(@Nullable String namespace) { 844 if (namespace == null) { 845 return null; 846 } 847 if (namespace.trim().isEmpty()) { 848 // Input is composed of all spaces (or nothing at all). 849 return namespace; 850 } 851 synchronized (sNamespaceHashCache) { 852 final int idx = sNamespaceHashCache.indexOfKey(namespace); 853 if (idx >= 0) { 854 return sNamespaceHashCache.valueAt(idx); 855 } 856 } 857 String hash = null; 858 try { 859 // .hashCode() can result in conflicts that would make distinguishing between 860 // namespaces hard and reduce the accuracy of certain metrics. Use SHA-256 861 // to generate the hash since the probability of collision is extremely low. 862 if (sMessageDigest == null) { 863 sMessageDigest = MessageDigest.getInstance("SHA-256"); 864 } 865 final byte[] digest = sMessageDigest.digest(namespace.getBytes()); 866 // Convert to hexadecimal representation 867 StringBuilder hexBuilder = new StringBuilder(digest.length); 868 for (byte byteChar : digest) { 869 hexBuilder.append(String.format("%02X", byteChar)); 870 } 871 hash = hexBuilder.toString(); 872 } catch (Exception e) { 873 Slog.wtf(TAG, "Couldn't hash input", e); 874 } 875 if (hash == null) { 876 // If we get to this point, something went wrong with the MessageDigest above. 877 // Don't return the raw input value (which would defeat the purpose of hashing). 878 return "failed_namespace_hash"; 879 } 880 hash = hash.intern(); 881 synchronized (sNamespaceHashCache) { 882 if (sNamespaceHashCache.size() >= MAX_NAMESPACE_CACHE_SIZE) { 883 // Drop a random mapping instead of dropping at a predefined index to avoid 884 // potentially always dropping the same mapping. 885 sNamespaceHashCache.removeAt((new Random()).nextInt(MAX_NAMESPACE_CACHE_SIZE)); 886 } 887 sNamespaceHashCache.put(namespace, hash); 888 } 889 return hash; 890 } 891 enqueueWorkLocked(JobWorkItem work)892 public void enqueueWorkLocked(JobWorkItem work) { 893 if (pendingWork == null) { 894 pendingWork = new ArrayList<>(); 895 } 896 work.setWorkId(nextPendingWorkId); 897 nextPendingWorkId++; 898 if (work.getIntent() != null 899 && GrantedUriPermissions.checkGrantFlags(work.getIntent().getFlags())) { 900 work.setGrants(GrantedUriPermissions.createFromIntent(work.getIntent(), sourceUid, 901 sourcePackageName, sourceUserId, toShortString())); 902 } 903 pendingWork.add(work); 904 updateNetworkBytesLocked(); 905 } 906 dequeueWorkLocked()907 public JobWorkItem dequeueWorkLocked() { 908 if (pendingWork != null && pendingWork.size() > 0) { 909 JobWorkItem work = pendingWork.remove(0); 910 if (work != null) { 911 if (executingWork == null) { 912 executingWork = new ArrayList<>(); 913 } 914 executingWork.add(work); 915 work.bumpDeliveryCount(); 916 } 917 return work; 918 } 919 return null; 920 } 921 922 /** Returns the number of {@link JobWorkItem JobWorkItems} attached to this job. */ getWorkCount()923 public int getWorkCount() { 924 final int pendingCount = pendingWork == null ? 0 : pendingWork.size(); 925 final int executingCount = executingWork == null ? 0 : executingWork.size(); 926 return pendingCount + executingCount; 927 } 928 hasWorkLocked()929 public boolean hasWorkLocked() { 930 return (pendingWork != null && pendingWork.size() > 0) || hasExecutingWorkLocked(); 931 } 932 hasExecutingWorkLocked()933 public boolean hasExecutingWorkLocked() { 934 return executingWork != null && executingWork.size() > 0; 935 } 936 ungrantWorkItem(JobWorkItem work)937 private static void ungrantWorkItem(JobWorkItem work) { 938 if (work.getGrants() != null) { 939 ((GrantedUriPermissions)work.getGrants()).revoke(); 940 } 941 } 942 943 /** 944 * Returns {@code true} if the JobWorkItem queue was updated, 945 * and {@code false} if nothing changed. 946 */ completeWorkLocked(int workId)947 public boolean completeWorkLocked(int workId) { 948 if (executingWork != null) { 949 final int N = executingWork.size(); 950 for (int i = 0; i < N; i++) { 951 JobWorkItem work = executingWork.get(i); 952 if (work.getWorkId() == workId) { 953 executingWork.remove(i); 954 ungrantWorkItem(work); 955 updateNetworkBytesLocked(); 956 return true; 957 } 958 } 959 } 960 return false; 961 } 962 ungrantWorkList(ArrayList<JobWorkItem> list)963 private static void ungrantWorkList(ArrayList<JobWorkItem> list) { 964 if (list != null) { 965 final int N = list.size(); 966 for (int i = 0; i < N; i++) { 967 ungrantWorkItem(list.get(i)); 968 } 969 } 970 } 971 stopTrackingJobLocked(JobStatus incomingJob)972 public void stopTrackingJobLocked(JobStatus incomingJob) { 973 if (incomingJob != null) { 974 // We are replacing with a new job -- transfer the work! We do any executing 975 // work first, since that was originally at the front of the pending work. 976 if (executingWork != null && executingWork.size() > 0) { 977 incomingJob.pendingWork = executingWork; 978 } 979 if (incomingJob.pendingWork == null) { 980 incomingJob.pendingWork = pendingWork; 981 } else if (pendingWork != null && pendingWork.size() > 0) { 982 incomingJob.pendingWork.addAll(pendingWork); 983 } 984 pendingWork = null; 985 executingWork = null; 986 incomingJob.nextPendingWorkId = nextPendingWorkId; 987 incomingJob.updateNetworkBytesLocked(); 988 } else { 989 // We are completely stopping the job... need to clean up work. 990 ungrantWorkList(pendingWork); 991 pendingWork = null; 992 ungrantWorkList(executingWork); 993 executingWork = null; 994 } 995 updateNetworkBytesLocked(); 996 } 997 prepareLocked()998 public void prepareLocked() { 999 if (prepared) { 1000 Slog.wtf(TAG, "Already prepared: " + this); 1001 return; 1002 } 1003 prepared = true; 1004 if (DEBUG_PREPARE) { 1005 unpreparedPoint = null; 1006 } 1007 final ClipData clip = job.getClipData(); 1008 if (clip != null) { 1009 uriPerms = GrantedUriPermissions.createFromClip(clip, sourceUid, sourcePackageName, 1010 sourceUserId, job.getClipGrantFlags(), toShortString()); 1011 } 1012 } 1013 unprepareLocked()1014 public void unprepareLocked() { 1015 if (!prepared) { 1016 Slog.wtf(TAG, "Hasn't been prepared: " + this); 1017 if (DEBUG_PREPARE && unpreparedPoint != null) { 1018 Slog.e(TAG, "Was already unprepared at ", unpreparedPoint); 1019 } 1020 return; 1021 } 1022 prepared = false; 1023 if (DEBUG_PREPARE) { 1024 unpreparedPoint = new Throwable().fillInStackTrace(); 1025 } 1026 if (uriPerms != null) { 1027 uriPerms.revoke(); 1028 uriPerms = null; 1029 } 1030 } 1031 isPreparedLocked()1032 public boolean isPreparedLocked() { 1033 return prepared; 1034 } 1035 getJob()1036 public JobInfo getJob() { 1037 return job; 1038 } 1039 getJobId()1040 public int getJobId() { 1041 return job.getId(); 1042 } 1043 1044 /** Returns an ID that can be used to uniquely identify the job when logging statsd metrics. */ getLoggingJobId()1045 public long getLoggingJobId() { 1046 return mLoggingJobId; 1047 } 1048 printUniqueId(PrintWriter pw)1049 public void printUniqueId(PrintWriter pw) { 1050 if (mNamespace != null) { 1051 pw.print(mNamespace); 1052 pw.print(":"); 1053 } else { 1054 pw.print("#"); 1055 } 1056 UserHandle.formatUid(pw, callingUid); 1057 pw.print("/"); 1058 pw.print(job.getId()); 1059 } 1060 1061 /** 1062 * Returns the number of times the job stopped previously for reasons that appeared to be within 1063 * the app's control. 1064 */ getNumFailures()1065 public int getNumFailures() { 1066 return numFailures; 1067 } 1068 1069 /** 1070 * Returns the number of times the system stopped a previous execution of this job for reasons 1071 * that were likely outside the app's control. 1072 */ getNumSystemStops()1073 public int getNumSystemStops() { 1074 return mNumSystemStops; 1075 } 1076 1077 /** Returns the total number of times we've attempted to run this job in the past. */ getNumPreviousAttempts()1078 public int getNumPreviousAttempts() { 1079 return numFailures + mNumSystemStops; 1080 } 1081 getServiceComponent()1082 public ComponentName getServiceComponent() { 1083 return job.getService(); 1084 } 1085 getSourcePackageName()1086 public String getSourcePackageName() { 1087 return sourcePackageName; 1088 } 1089 getSourceUid()1090 public int getSourceUid() { 1091 return sourceUid; 1092 } 1093 getSourceUserId()1094 public int getSourceUserId() { 1095 return sourceUserId; 1096 } 1097 getUserId()1098 public int getUserId() { 1099 return UserHandle.getUserId(callingUid); 1100 } 1101 shouldBlameSourceForTimeout()1102 private boolean shouldBlameSourceForTimeout() { 1103 // If the system scheduled the job on behalf of an app, assume the app is the one 1104 // doing the work and blame the app directly. This is the case with things like 1105 // syncs via SyncManager. 1106 // If the system didn't schedule the job on behalf of an app, then 1107 // blame the app doing the actual work. Proxied jobs are a little tricky. 1108 // Proxied jobs scheduled by built-in system apps like DownloadManager may be fine 1109 // and we could consider exempting those jobs. For example, in DownloadManager's 1110 // case, all it does is download files and the code is vetted. A timeout likely 1111 // means it's downloading a large file, which isn't an error. For now, DownloadManager 1112 // is an exempted app, so this shouldn't be an issue. 1113 // However, proxied jobs coming from other system apps (such as those that can 1114 // be updated separately from an OTA) may not be fine and we would want to apply 1115 // this policy to those jobs/apps. 1116 // TODO(284512488): consider exempting DownloadManager or other system apps 1117 return UserHandle.isCore(callingUid); 1118 } 1119 1120 /** 1121 * Returns the package name that should most likely be blamed for the job timing out. 1122 */ getTimeoutBlamePackageName()1123 public String getTimeoutBlamePackageName() { 1124 if (shouldBlameSourceForTimeout()) { 1125 return sourcePackageName; 1126 } 1127 return getServiceComponent().getPackageName(); 1128 } 1129 1130 /** 1131 * Returns the UID that should most likely be blamed for the job timing out. 1132 */ getTimeoutBlameUid()1133 public int getTimeoutBlameUid() { 1134 if (shouldBlameSourceForTimeout()) { 1135 return sourceUid; 1136 } 1137 return callingUid; 1138 } 1139 1140 /** 1141 * Returns the userId that should most likely be blamed for the job timing out. 1142 */ getTimeoutBlameUserId()1143 public int getTimeoutBlameUserId() { 1144 if (shouldBlameSourceForTimeout()) { 1145 return sourceUserId; 1146 } 1147 return UserHandle.getUserId(callingUid); 1148 } 1149 1150 /** 1151 * Returns an appropriate standby bucket for the job, taking into account any standby 1152 * exemptions. 1153 */ getEffectiveStandbyBucket()1154 public int getEffectiveStandbyBucket() { 1155 final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class); 1156 final boolean isBuggy = jsi.isAppConsideredBuggy( 1157 getUserId(), getServiceComponent().getPackageName(), 1158 getTimeoutBlameUserId(), getTimeoutBlamePackageName()); 1159 1160 final int actualBucket = getStandbyBucket(); 1161 if (actualBucket == EXEMPTED_INDEX) { 1162 // EXEMPTED apps always have their jobs exempted, even if they're buggy, because the 1163 // user has explicitly told the system to avoid restricting the app for power reasons. 1164 if (isBuggy) { 1165 final String pkg; 1166 if (getServiceComponent().getPackageName().equals(sourcePackageName)) { 1167 pkg = sourcePackageName; 1168 } else { 1169 pkg = getServiceComponent().getPackageName() + "/" + sourcePackageName; 1170 } 1171 Slog.w(TAG, "Exempted app " + pkg + " considered buggy"); 1172 } 1173 return actualBucket; 1174 } 1175 if (uidActive || getJob().isExemptedFromAppStandby()) { 1176 // Treat these cases as if they're in the ACTIVE bucket so that they get throttled 1177 // like other ACTIVE apps. 1178 return ACTIVE_INDEX; 1179 } 1180 1181 final int bucketWithMediaExemption; 1182 if (actualBucket != RESTRICTED_INDEX && actualBucket != NEVER_INDEX 1183 && mHasMediaBackupExemption) { 1184 // Treat it as if it's at most WORKING_INDEX (lower index grants higher quota) since 1185 // media backup jobs are important to the user, and the source package may not have 1186 // been used directly in a while. 1187 bucketWithMediaExemption = Math.min(WORKING_INDEX, actualBucket); 1188 } else { 1189 bucketWithMediaExemption = actualBucket; 1190 } 1191 1192 // If the app is considered buggy, but hasn't yet been put in the RESTRICTED bucket 1193 // (potentially because it's used frequently by the user), limit its effective bucket 1194 // so that it doesn't get to run as much as a normal ACTIVE app. 1195 if (isBuggy && bucketWithMediaExemption < WORKING_INDEX) { 1196 if (!mIsDowngradedDueToBuggyApp) { 1197 // Safety check to avoid logging multiple times for the same job. 1198 Counter.logIncrementWithUid( 1199 "job_scheduler.value_job_quota_reduced_due_to_buggy_uid", 1200 getTimeoutBlameUid()); 1201 mIsDowngradedDueToBuggyApp = true; 1202 } 1203 return WORKING_INDEX; 1204 } 1205 return bucketWithMediaExemption; 1206 } 1207 1208 /** Returns the real standby bucket of the job. */ getStandbyBucket()1209 public int getStandbyBucket() { 1210 return standbyBucket; 1211 } 1212 setStandbyBucket(int newBucket)1213 public void setStandbyBucket(int newBucket) { 1214 if (newBucket == RESTRICTED_INDEX) { 1215 // Adding to the bucket. 1216 addDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS); 1217 } else if (standbyBucket == RESTRICTED_INDEX) { 1218 // Removing from the RESTRICTED bucket. 1219 removeDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS); 1220 } 1221 1222 standbyBucket = newBucket; 1223 mLoggedBucketMismatch = false; 1224 } 1225 1226 /** 1227 * Log a bucket mismatch if this is the first time for this job. 1228 */ maybeLogBucketMismatch()1229 public void maybeLogBucketMismatch() { 1230 if (!mLoggedBucketMismatch) { 1231 Slog.wtf(TAG, 1232 "App " + getSourcePackageName() + " became active but still in NEVER bucket"); 1233 mLoggedBucketMismatch = true; 1234 } 1235 } 1236 1237 // Called only by the standby monitoring code getWhenStandbyDeferred()1238 public long getWhenStandbyDeferred() { 1239 return whenStandbyDeferred; 1240 } 1241 1242 // Called only by the standby monitoring code setWhenStandbyDeferred(long now)1243 public void setWhenStandbyDeferred(long now) { 1244 whenStandbyDeferred = now; 1245 } 1246 1247 /** 1248 * Returns the first time this job was force batched, in the elapsed realtime timebase. Will be 1249 * 0 if this job was never force batched. 1250 */ getFirstForceBatchedTimeElapsed()1251 public long getFirstForceBatchedTimeElapsed() { 1252 return mFirstForceBatchedTimeElapsed; 1253 } 1254 setFirstForceBatchedTimeElapsed(long now)1255 public void setFirstForceBatchedTimeElapsed(long now) { 1256 mFirstForceBatchedTimeElapsed = now; 1257 } 1258 1259 /** 1260 * Re-evaluates the media backup exemption status. 1261 * 1262 * @return true if the exemption status changed 1263 */ updateMediaBackupExemptionStatus()1264 public boolean updateMediaBackupExemptionStatus() { 1265 final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class); 1266 boolean hasMediaExemption = mHasExemptedMediaUrisOnly 1267 && !job.hasLateConstraint() 1268 && job.getRequiredNetwork() != null 1269 && getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT 1270 && sourcePackageName.equals(jsi.getCloudMediaProviderPackage(sourceUserId)); 1271 if (mHasMediaBackupExemption == hasMediaExemption) { 1272 return false; 1273 } 1274 mHasMediaBackupExemption = hasMediaExemption; 1275 return true; 1276 } 1277 1278 @Nullable getNamespace()1279 public String getNamespace() { 1280 return mNamespace; 1281 } 1282 1283 @Nullable getNamespaceHash()1284 public String getNamespaceHash() { 1285 return mNamespaceHash; 1286 } 1287 getSourceTag()1288 public String getSourceTag() { 1289 return sourceTag; 1290 } 1291 getUid()1292 public int getUid() { 1293 return callingUid; 1294 } 1295 getBatteryName()1296 public String getBatteryName() { 1297 return batteryName; 1298 } 1299 getTag()1300 public String getTag() { 1301 return tag; 1302 } 1303 getBias()1304 public int getBias() { 1305 return job.getBias(); 1306 } 1307 1308 /** 1309 * Returns the priority of the job, which may be adjusted due to various factors. 1310 * @see JobInfo.Builder#setPriority(int) 1311 */ 1312 @JobInfo.Priority getEffectivePriority()1313 public int getEffectivePriority() { 1314 final boolean isDemoted = 1315 (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_USER) != 0 1316 || (job.isUserInitiated() 1317 && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ) != 0); 1318 final int maxPriority; 1319 if (isDemoted) { 1320 // If the job was demoted for some reason, limit its priority to HIGH. 1321 maxPriority = JobInfo.PRIORITY_HIGH; 1322 } else { 1323 maxPriority = JobInfo.PRIORITY_MAX; 1324 } 1325 final int rawPriority = Math.min(maxPriority, job.getPriority()); 1326 if (numFailures < 2) { 1327 return rawPriority; 1328 } 1329 if (shouldTreatAsUserInitiatedJob()) { 1330 // Don't drop priority of UI jobs. 1331 return rawPriority; 1332 } 1333 // Slowly decay priority of jobs to prevent starvation of other jobs. 1334 if (isRequestedExpeditedJob()) { 1335 // EJs can't fall below HIGH priority. 1336 return JobInfo.PRIORITY_HIGH; 1337 } 1338 // Set a maximum priority based on the number of failures. 1339 final int dropPower = numFailures / 2; 1340 switch (dropPower) { 1341 case 1: return Math.min(JobInfo.PRIORITY_DEFAULT, rawPriority); 1342 case 2: return Math.min(JobInfo.PRIORITY_LOW, rawPriority); 1343 default: return JobInfo.PRIORITY_MIN; 1344 } 1345 } 1346 getFlags()1347 public int getFlags() { 1348 return job.getFlags(); 1349 } 1350 getInternalFlags()1351 public int getInternalFlags() { 1352 return mInternalFlags; 1353 } 1354 addInternalFlags(int flags)1355 public void addInternalFlags(int flags) { 1356 mInternalFlags |= flags; 1357 } 1358 removeInternalFlags(int flags)1359 public void removeInternalFlags(int flags) { 1360 mInternalFlags = mInternalFlags & ~flags; 1361 } 1362 getPreferredConstraintFlags()1363 int getPreferredConstraintFlags() { 1364 return mPreferredConstraints; 1365 } 1366 getSatisfiedConstraintFlags()1367 public int getSatisfiedConstraintFlags() { 1368 return satisfiedConstraints; 1369 } 1370 maybeAddForegroundExemption(Predicate<Integer> uidForegroundChecker)1371 public void maybeAddForegroundExemption(Predicate<Integer> uidForegroundChecker) { 1372 // Jobs with time constraints shouldn't be exempted. 1373 if (job.hasEarlyConstraint() || job.hasLateConstraint()) { 1374 return; 1375 } 1376 // Already exempted, skip the foreground check. 1377 if ((mInternalFlags & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0) { 1378 return; 1379 } 1380 if (uidForegroundChecker.test(getSourceUid())) { 1381 addInternalFlags(INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION); 1382 } 1383 } 1384 updateNetworkBytesLocked()1385 private void updateNetworkBytesLocked() { 1386 mTotalNetworkDownloadBytes = job.getEstimatedNetworkDownloadBytes(); 1387 if (mTotalNetworkDownloadBytes < 0) { 1388 // Legacy apps may have provided invalid negative values. Ignore invalid values. 1389 mTotalNetworkDownloadBytes = JobInfo.NETWORK_BYTES_UNKNOWN; 1390 } 1391 mTotalNetworkUploadBytes = job.getEstimatedNetworkUploadBytes(); 1392 if (mTotalNetworkUploadBytes < 0) { 1393 // Legacy apps may have provided invalid negative values. Ignore invalid values. 1394 mTotalNetworkUploadBytes = JobInfo.NETWORK_BYTES_UNKNOWN; 1395 } 1396 // Minimum network chunk bytes has had data validation since its introduction, so no 1397 // need to do validation again. 1398 mMinimumNetworkChunkBytes = job.getMinimumNetworkChunkBytes(); 1399 1400 if (pendingWork != null) { 1401 for (int i = 0; i < pendingWork.size(); i++) { 1402 long downloadBytes = pendingWork.get(i).getEstimatedNetworkDownloadBytes(); 1403 if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN && downloadBytes > 0) { 1404 // If any component of the job has unknown usage, we won't have a 1405 // complete picture of what data will be used. However, we use what we are given 1406 // to get us as close to the complete picture as possible. 1407 if (mTotalNetworkDownloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { 1408 mTotalNetworkDownloadBytes += downloadBytes; 1409 } else { 1410 mTotalNetworkDownloadBytes = downloadBytes; 1411 } 1412 } 1413 long uploadBytes = pendingWork.get(i).getEstimatedNetworkUploadBytes(); 1414 if (uploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN && uploadBytes > 0) { 1415 // If any component of the job has unknown usage, we won't have a 1416 // complete picture of what data will be used. However, we use what we are given 1417 // to get us as close to the complete picture as possible. 1418 if (mTotalNetworkUploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { 1419 mTotalNetworkUploadBytes += uploadBytes; 1420 } else { 1421 mTotalNetworkUploadBytes = uploadBytes; 1422 } 1423 } 1424 final long chunkBytes = pendingWork.get(i).getMinimumNetworkChunkBytes(); 1425 if (mMinimumNetworkChunkBytes == JobInfo.NETWORK_BYTES_UNKNOWN) { 1426 mMinimumNetworkChunkBytes = chunkBytes; 1427 } else if (chunkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { 1428 mMinimumNetworkChunkBytes = Math.min(mMinimumNetworkChunkBytes, chunkBytes); 1429 } 1430 } 1431 } 1432 } 1433 getEstimatedNetworkDownloadBytes()1434 public long getEstimatedNetworkDownloadBytes() { 1435 return mTotalNetworkDownloadBytes; 1436 } 1437 getEstimatedNetworkUploadBytes()1438 public long getEstimatedNetworkUploadBytes() { 1439 return mTotalNetworkUploadBytes; 1440 } 1441 getMinimumNetworkChunkBytes()1442 public long getMinimumNetworkChunkBytes() { 1443 return mMinimumNetworkChunkBytes; 1444 } 1445 1446 /** Does this job have any sort of networking constraint? */ hasConnectivityConstraint()1447 public boolean hasConnectivityConstraint() { 1448 // No need to check mDynamicConstraints since connectivity will only be in that list if 1449 // it's already in the requiredConstraints list. 1450 return (requiredConstraints&CONSTRAINT_CONNECTIVITY) != 0; 1451 } 1452 hasChargingConstraint()1453 public boolean hasChargingConstraint() { 1454 return hasConstraint(CONSTRAINT_CHARGING); 1455 } 1456 hasBatteryNotLowConstraint()1457 public boolean hasBatteryNotLowConstraint() { 1458 return hasConstraint(CONSTRAINT_BATTERY_NOT_LOW); 1459 } 1460 1461 /** Returns true if the job requires charging OR battery not low. */ hasPowerConstraint()1462 boolean hasPowerConstraint() { 1463 return hasConstraint(CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW); 1464 } 1465 hasStorageNotLowConstraint()1466 public boolean hasStorageNotLowConstraint() { 1467 return hasConstraint(CONSTRAINT_STORAGE_NOT_LOW); 1468 } 1469 hasTimingDelayConstraint()1470 public boolean hasTimingDelayConstraint() { 1471 return hasConstraint(CONSTRAINT_TIMING_DELAY); 1472 } 1473 hasDeadlineConstraint()1474 public boolean hasDeadlineConstraint() { 1475 return hasConstraint(CONSTRAINT_DEADLINE); 1476 } 1477 hasIdleConstraint()1478 public boolean hasIdleConstraint() { 1479 return hasConstraint(CONSTRAINT_IDLE); 1480 } 1481 hasContentTriggerConstraint()1482 public boolean hasContentTriggerConstraint() { 1483 // No need to check mDynamicConstraints since content trigger will only be in that list if 1484 // it's already in the requiredConstraints list. 1485 return (requiredConstraints&CONSTRAINT_CONTENT_TRIGGER) != 0; 1486 } 1487 1488 /** Returns true if the job has flexible job constraints enabled */ hasFlexibilityConstraint()1489 public boolean hasFlexibilityConstraint() { 1490 return (requiredConstraints & CONSTRAINT_FLEXIBLE) != 0; 1491 } 1492 1493 /** Returns the number of flexible job constraints required to be satisfied to execute */ getNumRequiredFlexibleConstraints()1494 public int getNumRequiredFlexibleConstraints() { 1495 return mNumRequiredFlexibleConstraints - mNumDroppedFlexibleConstraints; 1496 } 1497 1498 /** 1499 * Returns the number of required flexible job constraints that have been dropped with time. 1500 * The lower this number is the easier it is for the flexibility constraint to be satisfied. 1501 */ getNumDroppedFlexibleConstraints()1502 public int getNumDroppedFlexibleConstraints() { 1503 return mNumDroppedFlexibleConstraints; 1504 } 1505 1506 /** 1507 * Checks both {@link #requiredConstraints} and {@link #mDynamicConstraints} to see if this job 1508 * requires the specified constraint. 1509 */ hasConstraint(int constraint)1510 private boolean hasConstraint(int constraint) { 1511 return (requiredConstraints & constraint) != 0 || (mDynamicConstraints & constraint) != 0; 1512 } 1513 getTriggerContentUpdateDelay()1514 public long getTriggerContentUpdateDelay() { 1515 long time = job.getTriggerContentUpdateDelay(); 1516 if (time < 0) { 1517 return DEFAULT_TRIGGER_UPDATE_DELAY; 1518 } 1519 return Math.max(time, MIN_TRIGGER_UPDATE_DELAY); 1520 } 1521 getTriggerContentMaxDelay()1522 public long getTriggerContentMaxDelay() { 1523 long time = job.getTriggerContentMaxDelay(); 1524 if (time < 0) { 1525 return DEFAULT_TRIGGER_MAX_DELAY; 1526 } 1527 return Math.max(time, MIN_TRIGGER_MAX_DELAY); 1528 } 1529 isPersisted()1530 public boolean isPersisted() { 1531 return job.isPersisted(); 1532 } 1533 getCumulativeExecutionTimeMs()1534 public long getCumulativeExecutionTimeMs() { 1535 return mCumulativeExecutionTimeMs; 1536 } 1537 incrementCumulativeExecutionTime(long incrementMs)1538 public void incrementCumulativeExecutionTime(long incrementMs) { 1539 mCumulativeExecutionTimeMs += incrementMs; 1540 } 1541 getEarliestRunTime()1542 public long getEarliestRunTime() { 1543 return earliestRunTimeElapsedMillis; 1544 } 1545 getLatestRunTimeElapsed()1546 public long getLatestRunTimeElapsed() { 1547 return latestRunTimeElapsedMillis; 1548 } 1549 getOriginalLatestRunTimeElapsed()1550 public long getOriginalLatestRunTimeElapsed() { 1551 return mOriginalLatestRunTimeElapsedMillis; 1552 } 1553 setOriginalLatestRunTimeElapsed(long latestRunTimeElapsed)1554 public void setOriginalLatestRunTimeElapsed(long latestRunTimeElapsed) { 1555 mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsed; 1556 } 1557 1558 /** Sets the jobs access to an unmetered network. */ setHasAccessToUnmetered(boolean access)1559 void setHasAccessToUnmetered(boolean access) { 1560 mHasAccessToUnmetered = access; 1561 } 1562 getHasAccessToUnmetered()1563 boolean getHasAccessToUnmetered() { 1564 return mHasAccessToUnmetered; 1565 } 1566 getPreferUnmetered()1567 boolean getPreferUnmetered() { 1568 return mPreferUnmetered; 1569 } 1570 1571 @JobParameters.StopReason getStopReason()1572 public int getStopReason() { 1573 return mReasonReadyToUnready; 1574 } 1575 1576 /** 1577 * Return the fractional position of "now" within the "run time" window of 1578 * this job. 1579 * <p> 1580 * For example, if the earliest run time was 10 minutes ago, and the latest 1581 * run time is 30 minutes from now, this would return 0.25. 1582 * <p> 1583 * If the job has no window defined, returns 1. When only an earliest or 1584 * latest time is defined, it's treated as an infinitely small window at 1585 * that time. 1586 */ getFractionRunTime()1587 public float getFractionRunTime() { 1588 final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); 1589 if (earliestRunTimeElapsedMillis == NO_EARLIEST_RUNTIME 1590 && latestRunTimeElapsedMillis == NO_LATEST_RUNTIME) { 1591 return 1; 1592 } else if (earliestRunTimeElapsedMillis == NO_EARLIEST_RUNTIME) { 1593 return now >= latestRunTimeElapsedMillis ? 1 : 0; 1594 } else if (latestRunTimeElapsedMillis == NO_LATEST_RUNTIME) { 1595 return now >= earliestRunTimeElapsedMillis ? 1 : 0; 1596 } else { 1597 if (now <= earliestRunTimeElapsedMillis) { 1598 return 0; 1599 } else if (now >= latestRunTimeElapsedMillis) { 1600 return 1; 1601 } else { 1602 return (float) (now - earliestRunTimeElapsedMillis) 1603 / (float) (latestRunTimeElapsedMillis - earliestRunTimeElapsedMillis); 1604 } 1605 } 1606 } 1607 getPersistedUtcTimes()1608 public Pair<Long, Long> getPersistedUtcTimes() { 1609 return mPersistedUtcTimes; 1610 } 1611 clearPersistedUtcTimes()1612 public void clearPersistedUtcTimes() { 1613 mPersistedUtcTimes = null; 1614 } 1615 1616 /** @return true if the app has requested that this run as an expedited job. */ isRequestedExpeditedJob()1617 public boolean isRequestedExpeditedJob() { 1618 return (getFlags() & JobInfo.FLAG_EXPEDITED) != 0; 1619 } 1620 1621 /** 1622 * @return true if all expedited job requirements are satisfied and therefore this should be 1623 * treated as an expedited job. 1624 */ shouldTreatAsExpeditedJob()1625 public boolean shouldTreatAsExpeditedJob() { 1626 return mExpeditedQuotaApproved && mExpeditedTareApproved && isRequestedExpeditedJob(); 1627 } 1628 1629 /** 1630 * @return true if the job was scheduled as a user-initiated job and it hasn't been downgraded 1631 * for any reason. 1632 */ shouldTreatAsUserInitiatedJob()1633 public boolean shouldTreatAsUserInitiatedJob() { 1634 // isUserBgRestricted is intentionally excluded from this method. It should be fine to 1635 // treat the job as a UI job while the app is TOP, but just not in the background. 1636 // Instead of adding a proc state check here, the parts of JS that can make the distinction 1637 // and care about the distinction can do the check. 1638 return getJob().isUserInitiated() 1639 && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_USER) == 0 1640 && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ) == 0; 1641 } 1642 1643 /** 1644 * Return a summary that uniquely identifies the underlying job. 1645 */ 1646 @NonNull getUserVisibleJobSummary()1647 public UserVisibleJobSummary getUserVisibleJobSummary() { 1648 if (mUserVisibleJobSummary == null) { 1649 mUserVisibleJobSummary = new UserVisibleJobSummary( 1650 callingUid, getServiceComponent().getPackageName(), 1651 getSourceUserId(), getSourcePackageName(), 1652 getNamespace(), getJobId()); 1653 } 1654 return mUserVisibleJobSummary; 1655 } 1656 1657 /** 1658 * @return true if this is a job whose execution should be made visible to the user. 1659 */ isUserVisibleJob()1660 public boolean isUserVisibleJob() { 1661 return shouldTreatAsUserInitiatedJob() || startedAsUserInitiatedJob; 1662 } 1663 1664 /** 1665 * @return true if the job is exempted from Doze restrictions and therefore allowed to run 1666 * in Doze. 1667 */ canRunInDoze()1668 public boolean canRunInDoze() { 1669 return appHasDozeExemption 1670 || (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0 1671 || shouldTreatAsUserInitiatedJob() 1672 // EJs can't run in Doze if we explicitly require that the device is not Dozing. 1673 || ((shouldTreatAsExpeditedJob() || startedAsExpeditedJob) 1674 && (mDynamicConstraints & CONSTRAINT_DEVICE_NOT_DOZING) == 0); 1675 } 1676 canRunInBatterySaver()1677 boolean canRunInBatterySaver() { 1678 return (getInternalFlags() & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0 1679 || shouldTreatAsUserInitiatedJob() 1680 // EJs can't run in Battery Saver if we explicitly require that Battery Saver is off 1681 || ((shouldTreatAsExpeditedJob() || startedAsExpeditedJob) 1682 && (mDynamicConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) == 0); 1683 } 1684 1685 /** Returns whether or not the app is background restricted by the user (FAS). */ isUserBgRestricted()1686 public boolean isUserBgRestricted() { 1687 return mIsUserBgRestricted; 1688 } 1689 1690 /** @return true if the constraint was changed, false otherwise. */ setChargingConstraintSatisfied(final long nowElapsed, boolean state)1691 boolean setChargingConstraintSatisfied(final long nowElapsed, boolean state) { 1692 return setConstraintSatisfied(CONSTRAINT_CHARGING, nowElapsed, state); 1693 } 1694 1695 /** @return true if the constraint was changed, false otherwise. */ setBatteryNotLowConstraintSatisfied(final long nowElapsed, boolean state)1696 boolean setBatteryNotLowConstraintSatisfied(final long nowElapsed, boolean state) { 1697 return setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, nowElapsed, state); 1698 } 1699 1700 /** @return true if the constraint was changed, false otherwise. */ setStorageNotLowConstraintSatisfied(final long nowElapsed, boolean state)1701 boolean setStorageNotLowConstraintSatisfied(final long nowElapsed, boolean state) { 1702 return setConstraintSatisfied(CONSTRAINT_STORAGE_NOT_LOW, nowElapsed, state); 1703 } 1704 1705 /** @return true if the constraint was changed, false otherwise. */ setPrefetchConstraintSatisfied(final long nowElapsed, boolean state)1706 boolean setPrefetchConstraintSatisfied(final long nowElapsed, boolean state) { 1707 return setConstraintSatisfied(CONSTRAINT_PREFETCH, nowElapsed, state); 1708 } 1709 1710 /** @return true if the constraint was changed, false otherwise. */ setTimingDelayConstraintSatisfied(final long nowElapsed, boolean state)1711 boolean setTimingDelayConstraintSatisfied(final long nowElapsed, boolean state) { 1712 return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, nowElapsed, state); 1713 } 1714 1715 /** @return true if the constraint was changed, false otherwise. */ setDeadlineConstraintSatisfied(final long nowElapsed, boolean state)1716 boolean setDeadlineConstraintSatisfied(final long nowElapsed, boolean state) { 1717 if (setConstraintSatisfied(CONSTRAINT_DEADLINE, nowElapsed, state)) { 1718 // The constraint was changed. Update the ready flag. 1719 mReadyDeadlineSatisfied = !job.isPeriodic() && hasDeadlineConstraint() && state; 1720 return true; 1721 } 1722 return false; 1723 } 1724 1725 /** @return true if the constraint was changed, false otherwise. */ setIdleConstraintSatisfied(final long nowElapsed, boolean state)1726 boolean setIdleConstraintSatisfied(final long nowElapsed, boolean state) { 1727 return setConstraintSatisfied(CONSTRAINT_IDLE, nowElapsed, state); 1728 } 1729 1730 /** @return true if the constraint was changed, false otherwise. */ setConnectivityConstraintSatisfied(final long nowElapsed, boolean state)1731 boolean setConnectivityConstraintSatisfied(final long nowElapsed, boolean state) { 1732 return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, nowElapsed, state); 1733 } 1734 1735 /** @return true if the constraint was changed, false otherwise. */ setContentTriggerConstraintSatisfied(final long nowElapsed, boolean state)1736 boolean setContentTriggerConstraintSatisfied(final long nowElapsed, boolean state) { 1737 return setConstraintSatisfied(CONSTRAINT_CONTENT_TRIGGER, nowElapsed, state); 1738 } 1739 1740 /** @return true if the constraint was changed, false otherwise. */ setDeviceNotDozingConstraintSatisfied(final long nowElapsed, boolean state, boolean whitelisted)1741 boolean setDeviceNotDozingConstraintSatisfied(final long nowElapsed, 1742 boolean state, boolean whitelisted) { 1743 appHasDozeExemption = whitelisted; 1744 if (setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, nowElapsed, state)) { 1745 // The constraint was changed. Update the ready flag. 1746 mReadyNotDozing = state || canRunInDoze(); 1747 return true; 1748 } 1749 return false; 1750 } 1751 1752 /** @return true if the constraint was changed, false otherwise. */ setBackgroundNotRestrictedConstraintSatisfied(final long nowElapsed, boolean state, boolean isUserBgRestricted)1753 boolean setBackgroundNotRestrictedConstraintSatisfied(final long nowElapsed, boolean state, 1754 boolean isUserBgRestricted) { 1755 mIsUserBgRestricted = isUserBgRestricted; 1756 if (setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, nowElapsed, state)) { 1757 // The constraint was changed. Update the ready flag. 1758 mReadyNotRestrictedInBg = state; 1759 return true; 1760 } 1761 return false; 1762 } 1763 1764 /** @return true if the constraint was changed, false otherwise. */ setQuotaConstraintSatisfied(final long nowElapsed, boolean state)1765 boolean setQuotaConstraintSatisfied(final long nowElapsed, boolean state) { 1766 if (setConstraintSatisfied(CONSTRAINT_WITHIN_QUOTA, nowElapsed, state)) { 1767 // The constraint was changed. Update the ready flag. 1768 mReadyWithinQuota = state; 1769 return true; 1770 } 1771 return false; 1772 } 1773 1774 /** @return true if the constraint was changed, false otherwise. */ setTareWealthConstraintSatisfied(final long nowElapsed, boolean state)1775 boolean setTareWealthConstraintSatisfied(final long nowElapsed, boolean state) { 1776 if (setConstraintSatisfied(CONSTRAINT_TARE_WEALTH, nowElapsed, state)) { 1777 // The constraint was changed. Update the ready flag. 1778 mReadyTareWealth = state; 1779 return true; 1780 } 1781 return false; 1782 } 1783 1784 /** @return true if the constraint was changed, false otherwise. */ setFlexibilityConstraintSatisfied(final long nowElapsed, boolean state)1785 boolean setFlexibilityConstraintSatisfied(final long nowElapsed, boolean state) { 1786 return setConstraintSatisfied(CONSTRAINT_FLEXIBLE, nowElapsed, state); 1787 } 1788 1789 /** 1790 * Sets whether or not this job is approved to be treated as an expedited job based on quota 1791 * policy. 1792 * 1793 * @return true if the approval bit was changed, false otherwise. 1794 */ setExpeditedJobQuotaApproved(final long nowElapsed, boolean state)1795 boolean setExpeditedJobQuotaApproved(final long nowElapsed, boolean state) { 1796 if (mExpeditedQuotaApproved == state) { 1797 return false; 1798 } 1799 final boolean wasReady = !state && isReady(); 1800 mExpeditedQuotaApproved = state; 1801 updateExpeditedDependencies(); 1802 final boolean isReady = isReady(); 1803 if (wasReady && !isReady) { 1804 mReasonReadyToUnready = JobParameters.STOP_REASON_QUOTA; 1805 } else if (!wasReady && isReady) { 1806 mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED; 1807 } 1808 return true; 1809 } 1810 1811 /** 1812 * Sets whether or not this job is approved to be treated as an expedited job based on TARE 1813 * policy. 1814 * 1815 * @return true if the approval bit was changed, false otherwise. 1816 */ setExpeditedJobTareApproved(final long nowElapsed, boolean state)1817 boolean setExpeditedJobTareApproved(final long nowElapsed, boolean state) { 1818 if (mExpeditedTareApproved == state) { 1819 return false; 1820 } 1821 final boolean wasReady = !state && isReady(); 1822 mExpeditedTareApproved = state; 1823 updateExpeditedDependencies(); 1824 final boolean isReady = isReady(); 1825 if (wasReady && !isReady) { 1826 mReasonReadyToUnready = JobParameters.STOP_REASON_QUOTA; 1827 } else if (!wasReady && isReady) { 1828 mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED; 1829 } 1830 return true; 1831 } 1832 updateExpeditedDependencies()1833 private void updateExpeditedDependencies() { 1834 // DeviceIdleJobsController currently only tracks jobs with the WILL_BE_FOREGROUND flag. 1835 // Making it also track requested-expedited jobs would add unnecessary hops since the 1836 // controller would then defer to canRunInDoze. Avoid the hops and just update 1837 // mReadyNotDozing directly. 1838 mReadyNotDozing = isConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING) || canRunInDoze(); 1839 } 1840 1841 /** @return true if the state was changed, false otherwise. */ setUidActive(final boolean newActiveState)1842 boolean setUidActive(final boolean newActiveState) { 1843 if (newActiveState != uidActive) { 1844 uidActive = newActiveState; 1845 return true; 1846 } 1847 return false; /* unchanged */ 1848 } 1849 1850 /** @return true if the constraint was changed, false otherwise. */ setConstraintSatisfied(int constraint, final long nowElapsed, boolean state)1851 boolean setConstraintSatisfied(int constraint, final long nowElapsed, boolean state) { 1852 boolean old = (satisfiedConstraints&constraint) != 0; 1853 if (old == state) { 1854 return false; 1855 } 1856 if (DEBUG) { 1857 Slog.v(TAG, 1858 "Constraint " + constraint + " is " + (!state ? "NOT " : "") + "satisfied for " 1859 + toShortString()); 1860 } 1861 final boolean wasReady = !state && isReady(); 1862 satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0); 1863 mSatisfiedConstraintsOfInterest = satisfiedConstraints & CONSTRAINTS_OF_INTEREST; 1864 mReadyDynamicSatisfied = mDynamicConstraints != 0 1865 && mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints); 1866 if (STATS_LOG_ENABLED && (STATSD_CONSTRAINTS_TO_LOG & constraint) != 0) { 1867 FrameworkStatsLog.write_non_chained( 1868 FrameworkStatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED, 1869 sourceUid, null, getBatteryName(), getProtoConstraint(constraint), 1870 state ? FrameworkStatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__SATISFIED 1871 : FrameworkStatsLog 1872 .SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__UNSATISFIED); 1873 } 1874 1875 mConstraintUpdatedTimesElapsed[mConstraintChangeHistoryIndex] = nowElapsed; 1876 mConstraintStatusHistory[mConstraintChangeHistoryIndex] = satisfiedConstraints; 1877 mConstraintChangeHistoryIndex = 1878 (mConstraintChangeHistoryIndex + 1) % NUM_CONSTRAINT_CHANGE_HISTORY; 1879 1880 // Can't use isReady() directly since "cache booleans" haven't updated yet. 1881 final boolean isReady = readinessStatusWithConstraint(constraint, state); 1882 if (wasReady && !isReady) { 1883 mReasonReadyToUnready = constraintToStopReason(constraint); 1884 } else if (!wasReady && isReady) { 1885 mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED; 1886 } 1887 1888 return true; 1889 } 1890 1891 @JobParameters.StopReason constraintToStopReason(int constraint)1892 private int constraintToStopReason(int constraint) { 1893 switch (constraint) { 1894 case CONSTRAINT_BATTERY_NOT_LOW: 1895 if ((requiredConstraints & constraint) != 0) { 1896 // The developer requested this constraint, so it makes sense to return the 1897 // explicit constraint reason. 1898 return JobParameters.STOP_REASON_CONSTRAINT_BATTERY_NOT_LOW; 1899 } 1900 // Hard-coding right now since the current dynamic constraint sets don't overlap 1901 // TODO: return based on active dynamic constraint sets when they start overlapping 1902 return JobParameters.STOP_REASON_APP_STANDBY; 1903 case CONSTRAINT_CHARGING: 1904 if ((requiredConstraints & constraint) != 0) { 1905 // The developer requested this constraint, so it makes sense to return the 1906 // explicit constraint reason. 1907 return JobParameters.STOP_REASON_CONSTRAINT_CHARGING; 1908 } 1909 // Hard-coding right now since the current dynamic constraint sets don't overlap 1910 // TODO: return based on active dynamic constraint sets when they start overlapping 1911 return JobParameters.STOP_REASON_APP_STANDBY; 1912 case CONSTRAINT_CONNECTIVITY: 1913 return JobParameters.STOP_REASON_CONSTRAINT_CONNECTIVITY; 1914 case CONSTRAINT_IDLE: 1915 if ((requiredConstraints & constraint) != 0) { 1916 // The developer requested this constraint, so it makes sense to return the 1917 // explicit constraint reason. 1918 return JobParameters.STOP_REASON_CONSTRAINT_DEVICE_IDLE; 1919 } 1920 // Hard-coding right now since the current dynamic constraint sets don't overlap 1921 // TODO: return based on active dynamic constraint sets when they start overlapping 1922 return JobParameters.STOP_REASON_APP_STANDBY; 1923 case CONSTRAINT_STORAGE_NOT_LOW: 1924 return JobParameters.STOP_REASON_CONSTRAINT_STORAGE_NOT_LOW; 1925 1926 case CONSTRAINT_BACKGROUND_NOT_RESTRICTED: 1927 // The BACKGROUND_NOT_RESTRICTED constraint could be dissatisfied either because 1928 // the app is background restricted, or because we're restricting background work 1929 // in battery saver. Assume that background restriction is the reason apps that 1930 // are background restricted have their jobs stopped, and battery saver otherwise. 1931 // This has the benefit of being consistent for background restricted apps 1932 // (they'll always get BACKGROUND_RESTRICTION) as the reason, regardless of 1933 // battery saver state. 1934 if (mIsUserBgRestricted) { 1935 return JobParameters.STOP_REASON_BACKGROUND_RESTRICTION; 1936 } 1937 return JobParameters.STOP_REASON_DEVICE_STATE; 1938 case CONSTRAINT_DEVICE_NOT_DOZING: 1939 return JobParameters.STOP_REASON_DEVICE_STATE; 1940 1941 case CONSTRAINT_PREFETCH: 1942 return JobParameters.STOP_REASON_ESTIMATED_APP_LAUNCH_TIME_CHANGED; 1943 1944 case CONSTRAINT_TARE_WEALTH: 1945 case CONSTRAINT_WITHIN_QUOTA: 1946 return JobParameters.STOP_REASON_QUOTA; 1947 1948 // These should never be stop reasons since they can never go from true to false. 1949 case CONSTRAINT_CONTENT_TRIGGER: 1950 case CONSTRAINT_DEADLINE: 1951 case CONSTRAINT_TIMING_DELAY: 1952 default: 1953 Slog.wtf(TAG, "Unsupported constraint (" + constraint + ") --stop reason mapping"); 1954 return JobParameters.STOP_REASON_UNDEFINED; 1955 } 1956 } 1957 1958 /** 1959 * If {@link #isReady()} returns false, this will return a single reason why the job isn't 1960 * ready. If {@link #isReady()} returns true, this will return 1961 * {@link JobScheduler#PENDING_JOB_REASON_UNDEFINED}. 1962 */ 1963 @JobScheduler.PendingJobReason getPendingJobReason()1964 public int getPendingJobReason() { 1965 final int unsatisfiedConstraints = ~satisfiedConstraints 1966 & (requiredConstraints | mDynamicConstraints | IMPLICIT_CONSTRAINTS); 1967 if ((CONSTRAINT_BACKGROUND_NOT_RESTRICTED & unsatisfiedConstraints) != 0) { 1968 // The BACKGROUND_NOT_RESTRICTED constraint could be unsatisfied either because 1969 // the app is background restricted, or because we're restricting background work 1970 // in battery saver. Assume that background restriction is the reason apps that 1971 // jobs are not ready, and battery saver otherwise. 1972 // This has the benefit of being consistent for background restricted apps 1973 // (they'll always get BACKGROUND_RESTRICTION) as the reason, regardless of 1974 // battery saver state. 1975 if (mIsUserBgRestricted) { 1976 return JobScheduler.PENDING_JOB_REASON_BACKGROUND_RESTRICTION; 1977 } 1978 return JobScheduler.PENDING_JOB_REASON_DEVICE_STATE; 1979 } 1980 if ((CONSTRAINT_BATTERY_NOT_LOW & unsatisfiedConstraints) != 0) { 1981 if ((CONSTRAINT_BATTERY_NOT_LOW & requiredConstraints) != 0) { 1982 // The developer requested this constraint, so it makes sense to return the 1983 // explicit constraint reason. 1984 return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_BATTERY_NOT_LOW; 1985 } 1986 // Hard-coding right now since the current dynamic constraint sets don't overlap 1987 // TODO: return based on active dynamic constraint sets when they start overlapping 1988 return JobScheduler.PENDING_JOB_REASON_APP_STANDBY; 1989 } 1990 if ((CONSTRAINT_CHARGING & unsatisfiedConstraints) != 0) { 1991 if ((CONSTRAINT_CHARGING & requiredConstraints) != 0) { 1992 // The developer requested this constraint, so it makes sense to return the 1993 // explicit constraint reason. 1994 return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_CHARGING; 1995 } 1996 // Hard-coding right now since the current dynamic constraint sets don't overlap 1997 // TODO: return based on active dynamic constraint sets when they start overlapping 1998 return JobScheduler.PENDING_JOB_REASON_APP_STANDBY; 1999 } 2000 if ((CONSTRAINT_CONNECTIVITY & unsatisfiedConstraints) != 0) { 2001 return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_CONNECTIVITY; 2002 } 2003 if ((CONSTRAINT_CONTENT_TRIGGER & unsatisfiedConstraints) != 0) { 2004 return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_CONTENT_TRIGGER; 2005 } 2006 if ((CONSTRAINT_DEVICE_NOT_DOZING & unsatisfiedConstraints) != 0) { 2007 return JobScheduler.PENDING_JOB_REASON_DEVICE_STATE; 2008 } 2009 if ((CONSTRAINT_FLEXIBLE & unsatisfiedConstraints) != 0) { 2010 return JobScheduler.PENDING_JOB_REASON_JOB_SCHEDULER_OPTIMIZATION; 2011 } 2012 if ((CONSTRAINT_IDLE & unsatisfiedConstraints) != 0) { 2013 if ((CONSTRAINT_IDLE & requiredConstraints) != 0) { 2014 // The developer requested this constraint, so it makes sense to return the 2015 // explicit constraint reason. 2016 return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_DEVICE_IDLE; 2017 } 2018 // Hard-coding right now since the current dynamic constraint sets don't overlap 2019 // TODO: return based on active dynamic constraint sets when they start overlapping 2020 return JobScheduler.PENDING_JOB_REASON_APP_STANDBY; 2021 } 2022 if ((CONSTRAINT_PREFETCH & unsatisfiedConstraints) != 0) { 2023 return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_PREFETCH; 2024 } 2025 if ((CONSTRAINT_STORAGE_NOT_LOW & unsatisfiedConstraints) != 0) { 2026 return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_STORAGE_NOT_LOW; 2027 } 2028 if ((CONSTRAINT_TARE_WEALTH & unsatisfiedConstraints) != 0) { 2029 return JobScheduler.PENDING_JOB_REASON_QUOTA; 2030 } 2031 if ((CONSTRAINT_TIMING_DELAY & unsatisfiedConstraints) != 0) { 2032 return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_MINIMUM_LATENCY; 2033 } 2034 if ((CONSTRAINT_WITHIN_QUOTA & unsatisfiedConstraints) != 0) { 2035 return JobScheduler.PENDING_JOB_REASON_QUOTA; 2036 } 2037 2038 if (getEffectiveStandbyBucket() == NEVER_INDEX) { 2039 Slog.wtf(TAG, "App in NEVER bucket querying pending job reason"); 2040 // The user hasn't officially launched this app. 2041 return JobScheduler.PENDING_JOB_REASON_USER; 2042 } 2043 if (serviceProcessName != null) { 2044 return JobScheduler.PENDING_JOB_REASON_APP; 2045 } 2046 2047 if (!isReady()) { 2048 Slog.wtf(TAG, "Unknown reason job isn't ready"); 2049 } 2050 return JobScheduler.PENDING_JOB_REASON_UNDEFINED; 2051 } 2052 2053 /** @return whether or not the @param constraint is satisfied */ isConstraintSatisfied(int constraint)2054 public boolean isConstraintSatisfied(int constraint) { 2055 return (satisfiedConstraints&constraint) != 0; 2056 } 2057 isExpeditedQuotaApproved()2058 boolean isExpeditedQuotaApproved() { 2059 return mExpeditedQuotaApproved; 2060 } 2061 clearTrackingController(int which)2062 boolean clearTrackingController(int which) { 2063 if ((trackingControllers&which) != 0) { 2064 trackingControllers &= ~which; 2065 return true; 2066 } 2067 return false; 2068 } 2069 setTrackingController(int which)2070 void setTrackingController(int which) { 2071 trackingControllers |= which; 2072 } 2073 2074 /** Adjusts the number of required flexible constraints by the given number */ adjustNumRequiredFlexibleConstraints(int adjustment)2075 public void adjustNumRequiredFlexibleConstraints(int adjustment) { 2076 mNumDroppedFlexibleConstraints = Math.max(0, Math.min(mNumRequiredFlexibleConstraints, 2077 mNumDroppedFlexibleConstraints - adjustment)); 2078 } 2079 2080 /** 2081 * Add additional constraints to prevent this job from running when doze or battery saver are 2082 * active. 2083 */ disallowRunInBatterySaverAndDoze()2084 public void disallowRunInBatterySaverAndDoze() { 2085 addDynamicConstraints(DYNAMIC_EXPEDITED_DEFERRAL_CONSTRAINTS); 2086 } 2087 2088 /** 2089 * Indicates that this job cannot run without the specified constraints. This is evaluated 2090 * separately from the job's explicitly requested constraints and MUST be satisfied before 2091 * the job can run if the app doesn't have quota. 2092 */ 2093 @VisibleForTesting addDynamicConstraints(int constraints)2094 public void addDynamicConstraints(int constraints) { 2095 if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) { 2096 // Quota should never be used as a dynamic constraint. 2097 Slog.wtf(TAG, "Tried to set quota as a dynamic constraint"); 2098 constraints &= ~CONSTRAINT_WITHIN_QUOTA; 2099 } 2100 if ((constraints & CONSTRAINT_TARE_WEALTH) != 0) { 2101 // Quota should never be used as a dynamic constraint. 2102 Slog.wtf(TAG, "Tried to set TARE as a dynamic constraint"); 2103 constraints &= ~CONSTRAINT_TARE_WEALTH; 2104 } 2105 2106 // Connectivity and content trigger are special since they're only valid to add if the 2107 // job has requested network or specific content URIs. Adding these constraints to jobs 2108 // that don't need them doesn't make sense. 2109 if (!hasConnectivityConstraint()) { 2110 constraints &= ~CONSTRAINT_CONNECTIVITY; 2111 } 2112 if (!hasContentTriggerConstraint()) { 2113 constraints &= ~CONSTRAINT_CONTENT_TRIGGER; 2114 } 2115 2116 mDynamicConstraints |= constraints; 2117 mReadyDynamicSatisfied = mDynamicConstraints != 0 2118 && mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints); 2119 } 2120 2121 /** 2122 * Removes dynamic constraints from a job, meaning that the requirements are not required for 2123 * the job to run (if the job itself hasn't requested the constraint. This is separate from 2124 * the job's explicitly requested constraints and does not remove those requested constraints. 2125 * 2126 */ removeDynamicConstraints(int constraints)2127 private void removeDynamicConstraints(int constraints) { 2128 mDynamicConstraints &= ~constraints; 2129 mReadyDynamicSatisfied = mDynamicConstraints != 0 2130 && mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints); 2131 } 2132 getLastSuccessfulRunTime()2133 public long getLastSuccessfulRunTime() { 2134 return mLastSuccessfulRunTime; 2135 } 2136 getLastFailedRunTime()2137 public long getLastFailedRunTime() { 2138 return mLastFailedRunTime; 2139 } 2140 2141 /** 2142 * @return Whether or not this job is ready to run, based on its requirements. 2143 */ isReady()2144 public boolean isReady() { 2145 return isReady(mSatisfiedConstraintsOfInterest); 2146 } 2147 2148 /** 2149 * @return Whether or not this job would be ready to run if it had the specified constraint 2150 * granted, based on its requirements. 2151 */ wouldBeReadyWithConstraint(int constraint)2152 boolean wouldBeReadyWithConstraint(int constraint) { 2153 return readinessStatusWithConstraint(constraint, true); 2154 } 2155 2156 @VisibleForTesting readinessStatusWithConstraint(int constraint, boolean value)2157 boolean readinessStatusWithConstraint(int constraint, boolean value) { 2158 boolean oldValue = false; 2159 int satisfied = mSatisfiedConstraintsOfInterest; 2160 switch (constraint) { 2161 case CONSTRAINT_BACKGROUND_NOT_RESTRICTED: 2162 oldValue = mReadyNotRestrictedInBg; 2163 mReadyNotRestrictedInBg = value; 2164 break; 2165 case CONSTRAINT_DEADLINE: 2166 oldValue = mReadyDeadlineSatisfied; 2167 mReadyDeadlineSatisfied = value; 2168 break; 2169 case CONSTRAINT_DEVICE_NOT_DOZING: 2170 oldValue = mReadyNotDozing; 2171 mReadyNotDozing = value; 2172 break; 2173 case CONSTRAINT_TARE_WEALTH: 2174 oldValue = mReadyTareWealth; 2175 mReadyTareWealth = value; 2176 break; 2177 case CONSTRAINT_WITHIN_QUOTA: 2178 oldValue = mReadyWithinQuota; 2179 mReadyWithinQuota = value; 2180 break; 2181 default: 2182 if (value) { 2183 satisfied |= constraint; 2184 } else { 2185 satisfied &= ~constraint; 2186 } 2187 mReadyDynamicSatisfied = mDynamicConstraints != 0 2188 && mDynamicConstraints == (satisfied & mDynamicConstraints); 2189 2190 break; 2191 } 2192 2193 // The flexibility constraint relies on other constraints to be satisfied. 2194 // This function lacks the information to determine if flexibility will be satisfied. 2195 // But for the purposes of this function it is still useful to know the jobs' readiness 2196 // not including the flexibility constraint. If flexibility is the constraint in question 2197 // we can proceed as normal. 2198 if (constraint != CONSTRAINT_FLEXIBLE) { 2199 satisfied |= CONSTRAINT_FLEXIBLE; 2200 } 2201 2202 boolean toReturn = isReady(satisfied); 2203 2204 switch (constraint) { 2205 case CONSTRAINT_BACKGROUND_NOT_RESTRICTED: 2206 mReadyNotRestrictedInBg = oldValue; 2207 break; 2208 case CONSTRAINT_DEADLINE: 2209 mReadyDeadlineSatisfied = oldValue; 2210 break; 2211 case CONSTRAINT_DEVICE_NOT_DOZING: 2212 mReadyNotDozing = oldValue; 2213 break; 2214 case CONSTRAINT_TARE_WEALTH: 2215 mReadyTareWealth = oldValue; 2216 break; 2217 case CONSTRAINT_WITHIN_QUOTA: 2218 mReadyWithinQuota = oldValue; 2219 break; 2220 default: 2221 mReadyDynamicSatisfied = mDynamicConstraints != 0 2222 && mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints); 2223 break; 2224 } 2225 return toReturn; 2226 } 2227 isReady(int satisfiedConstraints)2228 private boolean isReady(int satisfiedConstraints) { 2229 // Quota and dynamic constraints trump all other constraints. 2230 // NEVER jobs are not supposed to run at all. Since we're using quota to allow parole 2231 // sessions (exempt from dynamic restrictions), we need the additional check to ensure 2232 // that NEVER jobs don't run. 2233 // TODO: cleanup quota and standby bucket management so we don't need the additional checks 2234 if (((!mReadyWithinQuota || !mReadyTareWealth) 2235 && !mReadyDynamicSatisfied && !shouldTreatAsExpeditedJob()) 2236 || getEffectiveStandbyBucket() == NEVER_INDEX) { 2237 return false; 2238 } 2239 // Deadline constraint trumps other constraints besides quota and dynamic (except for 2240 // periodic jobs where deadline is an implementation detail. A periodic job should only 2241 // run if its constraints are satisfied). 2242 // DeviceNotDozing implicit constraint must be satisfied 2243 // NotRestrictedInBackground implicit constraint must be satisfied 2244 return mReadyNotDozing && mReadyNotRestrictedInBg && (serviceProcessName != null) 2245 && (mReadyDeadlineSatisfied || isConstraintsSatisfied(satisfiedConstraints)); 2246 } 2247 2248 /** All constraints besides implicit and deadline. */ 2249 static final int CONSTRAINTS_OF_INTEREST = CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW 2250 | CONSTRAINT_STORAGE_NOT_LOW | CONSTRAINT_TIMING_DELAY | CONSTRAINT_CONNECTIVITY 2251 | CONSTRAINT_IDLE | CONSTRAINT_CONTENT_TRIGGER | CONSTRAINT_PREFETCH 2252 | CONSTRAINT_FLEXIBLE; 2253 2254 // Soft override covers all non-"functional" constraints 2255 static final int SOFT_OVERRIDE_CONSTRAINTS = 2256 CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW | CONSTRAINT_STORAGE_NOT_LOW 2257 | CONSTRAINT_TIMING_DELAY | CONSTRAINT_IDLE | CONSTRAINT_PREFETCH 2258 | CONSTRAINT_FLEXIBLE; 2259 2260 /** Returns true whenever all dynamically set constraints are satisfied. */ areDynamicConstraintsSatisfied()2261 public boolean areDynamicConstraintsSatisfied() { 2262 return mReadyDynamicSatisfied; 2263 } 2264 2265 /** 2266 * @return Whether the constraints set on this job are satisfied. 2267 */ isConstraintsSatisfied()2268 public boolean isConstraintsSatisfied() { 2269 return isConstraintsSatisfied(mSatisfiedConstraintsOfInterest); 2270 } 2271 isConstraintsSatisfied(int satisfiedConstraints)2272 private boolean isConstraintsSatisfied(int satisfiedConstraints) { 2273 if (overrideState == OVERRIDE_FULL) { 2274 // force override: the job is always runnable 2275 return true; 2276 } 2277 2278 int sat = satisfiedConstraints; 2279 if (overrideState == OVERRIDE_SOFT) { 2280 // override: pretend all 'soft' requirements are satisfied 2281 sat |= (requiredConstraints & SOFT_OVERRIDE_CONSTRAINTS); 2282 } 2283 2284 return (sat & mRequiredConstraintsOfInterest) == mRequiredConstraintsOfInterest; 2285 } 2286 2287 /** 2288 * Returns true if the given parameters match this job's unique identifier. 2289 */ matches(int uid, @Nullable String namespace, int jobId)2290 public boolean matches(int uid, @Nullable String namespace, int jobId) { 2291 return this.job.getId() == jobId && this.callingUid == uid 2292 && Objects.equals(mNamespace, namespace); 2293 } 2294 2295 @Override toString()2296 public String toString() { 2297 StringBuilder sb = new StringBuilder(128); 2298 sb.append("JobStatus{"); 2299 sb.append(Integer.toHexString(System.identityHashCode(this))); 2300 if (mNamespace != null) { 2301 sb.append(" "); 2302 sb.append(mNamespace); 2303 sb.append(":"); 2304 } else { 2305 sb.append(" #"); 2306 } 2307 UserHandle.formatUid(sb, callingUid); 2308 sb.append("/"); 2309 sb.append(job.getId()); 2310 sb.append(' '); 2311 sb.append(batteryName); 2312 sb.append(" u="); 2313 sb.append(getUserId()); 2314 sb.append(" s="); 2315 sb.append(getSourceUid()); 2316 if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME 2317 || latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) { 2318 long now = sElapsedRealtimeClock.millis(); 2319 sb.append(" TIME="); 2320 formatRunTime(sb, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, now); 2321 sb.append(":"); 2322 formatRunTime(sb, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, now); 2323 } 2324 if (job.getRequiredNetwork() != null) { 2325 sb.append(" NET"); 2326 } 2327 if (job.isRequireCharging()) { 2328 sb.append(" CHARGING"); 2329 } 2330 if (job.isRequireBatteryNotLow()) { 2331 sb.append(" BATNOTLOW"); 2332 } 2333 if (job.isRequireStorageNotLow()) { 2334 sb.append(" STORENOTLOW"); 2335 } 2336 if (job.isRequireDeviceIdle()) { 2337 sb.append(" IDLE"); 2338 } 2339 if (job.isPeriodic()) { 2340 sb.append(" PERIODIC"); 2341 } 2342 if (job.isPersisted()) { 2343 sb.append(" PERSISTED"); 2344 } 2345 if ((satisfiedConstraints&CONSTRAINT_DEVICE_NOT_DOZING) == 0) { 2346 sb.append(" WAIT:DEV_NOT_DOZING"); 2347 } 2348 if (job.getTriggerContentUris() != null) { 2349 sb.append(" URIS="); 2350 sb.append(Arrays.toString(job.getTriggerContentUris())); 2351 } 2352 if (numFailures != 0) { 2353 sb.append(" failures="); 2354 sb.append(numFailures); 2355 } 2356 if (mNumSystemStops != 0) { 2357 sb.append(" system stops="); 2358 sb.append(mNumSystemStops); 2359 } 2360 if (isReady()) { 2361 sb.append(" READY"); 2362 } else { 2363 sb.append(" satisfied:0x").append(Integer.toHexString(satisfiedConstraints)); 2364 final int requiredConstraints = mRequiredConstraintsOfInterest | IMPLICIT_CONSTRAINTS; 2365 sb.append(" unsatisfied:0x").append(Integer.toHexString( 2366 (satisfiedConstraints & requiredConstraints) ^ requiredConstraints)); 2367 } 2368 sb.append("}"); 2369 return sb.toString(); 2370 } 2371 formatRunTime(PrintWriter pw, long runtime, long defaultValue, long now)2372 private void formatRunTime(PrintWriter pw, long runtime, long defaultValue, long now) { 2373 if (runtime == defaultValue) { 2374 pw.print("none"); 2375 } else { 2376 TimeUtils.formatDuration(runtime - now, pw); 2377 } 2378 } 2379 formatRunTime(StringBuilder sb, long runtime, long defaultValue, long now)2380 private void formatRunTime(StringBuilder sb, long runtime, long defaultValue, long now) { 2381 if (runtime == defaultValue) { 2382 sb.append("none"); 2383 } else { 2384 TimeUtils.formatDuration(runtime - now, sb); 2385 } 2386 } 2387 2388 /** 2389 * Convenience function to identify a job uniquely without pulling all the data that 2390 * {@link #toString()} returns. 2391 */ toShortString()2392 public String toShortString() { 2393 StringBuilder sb = new StringBuilder(); 2394 sb.append(Integer.toHexString(System.identityHashCode(this))); 2395 if (mNamespace != null) { 2396 sb.append(" {").append(mNamespace).append("}"); 2397 } 2398 sb.append(" #"); 2399 UserHandle.formatUid(sb, callingUid); 2400 sb.append("/"); 2401 sb.append(job.getId()); 2402 sb.append(' '); 2403 sb.append(batteryName); 2404 return sb.toString(); 2405 } 2406 2407 /** 2408 * Convenience function to identify a job uniquely without pulling all the data that 2409 * {@link #toString()} returns. 2410 */ toShortStringExceptUniqueId()2411 public String toShortStringExceptUniqueId() { 2412 StringBuilder sb = new StringBuilder(); 2413 sb.append(Integer.toHexString(System.identityHashCode(this))); 2414 sb.append(' '); 2415 sb.append(batteryName); 2416 return sb.toString(); 2417 } 2418 2419 /** 2420 * Convenience function to dump data that identifies a job uniquely to proto. This is intended 2421 * to mimic {@link #toShortString}. 2422 */ writeToShortProto(ProtoOutputStream proto, long fieldId)2423 public void writeToShortProto(ProtoOutputStream proto, long fieldId) { 2424 final long token = proto.start(fieldId); 2425 2426 proto.write(JobStatusShortInfoProto.CALLING_UID, callingUid); 2427 proto.write(JobStatusShortInfoProto.JOB_ID, job.getId()); 2428 proto.write(JobStatusShortInfoProto.BATTERY_NAME, batteryName); 2429 2430 proto.end(token); 2431 } 2432 dumpConstraints(PrintWriter pw, int constraints)2433 static void dumpConstraints(PrintWriter pw, int constraints) { 2434 if ((constraints & CONSTRAINT_CHARGING) != 0) { 2435 pw.print(" CHARGING"); 2436 } 2437 if ((constraints & CONSTRAINT_BATTERY_NOT_LOW) != 0) { 2438 pw.print(" BATTERY_NOT_LOW"); 2439 } 2440 if ((constraints & CONSTRAINT_STORAGE_NOT_LOW) != 0) { 2441 pw.print(" STORAGE_NOT_LOW"); 2442 } 2443 if ((constraints & CONSTRAINT_TIMING_DELAY) != 0) { 2444 pw.print(" TIMING_DELAY"); 2445 } 2446 if ((constraints & CONSTRAINT_DEADLINE) != 0) { 2447 pw.print(" DEADLINE"); 2448 } 2449 if ((constraints & CONSTRAINT_IDLE) != 0) { 2450 pw.print(" IDLE"); 2451 } 2452 if ((constraints & CONSTRAINT_CONNECTIVITY) != 0) { 2453 pw.print(" CONNECTIVITY"); 2454 } 2455 if ((constraints & CONSTRAINT_FLEXIBLE) != 0) { 2456 pw.print(" FLEXIBILITY"); 2457 } 2458 if ((constraints & CONSTRAINT_CONTENT_TRIGGER) != 0) { 2459 pw.print(" CONTENT_TRIGGER"); 2460 } 2461 if ((constraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0) { 2462 pw.print(" DEVICE_NOT_DOZING"); 2463 } 2464 if ((constraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) { 2465 pw.print(" BACKGROUND_NOT_RESTRICTED"); 2466 } 2467 if ((constraints & CONSTRAINT_PREFETCH) != 0) { 2468 pw.print(" PREFETCH"); 2469 } 2470 if ((constraints & CONSTRAINT_TARE_WEALTH) != 0) { 2471 pw.print(" TARE_WEALTH"); 2472 } 2473 if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) { 2474 pw.print(" WITHIN_QUOTA"); 2475 } 2476 if (constraints != 0) { 2477 pw.print(" [0x"); 2478 pw.print(Integer.toHexString(constraints)); 2479 pw.print("]"); 2480 } 2481 } 2482 2483 /** Returns a {@link JobServerProtoEnums.Constraint} enum value for the given constraint. */ getProtoConstraint(int constraint)2484 static int getProtoConstraint(int constraint) { 2485 switch (constraint) { 2486 case CONSTRAINT_BACKGROUND_NOT_RESTRICTED: 2487 return JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED; 2488 case CONSTRAINT_BATTERY_NOT_LOW: 2489 return JobServerProtoEnums.CONSTRAINT_BATTERY_NOT_LOW; 2490 case CONSTRAINT_CHARGING: 2491 return JobServerProtoEnums.CONSTRAINT_CHARGING; 2492 case CONSTRAINT_CONNECTIVITY: 2493 return JobServerProtoEnums.CONSTRAINT_CONNECTIVITY; 2494 case CONSTRAINT_CONTENT_TRIGGER: 2495 return JobServerProtoEnums.CONSTRAINT_CONTENT_TRIGGER; 2496 case CONSTRAINT_DEADLINE: 2497 return JobServerProtoEnums.CONSTRAINT_DEADLINE; 2498 case CONSTRAINT_DEVICE_NOT_DOZING: 2499 return JobServerProtoEnums.CONSTRAINT_DEVICE_NOT_DOZING; 2500 case CONSTRAINT_FLEXIBLE: 2501 return JobServerProtoEnums.CONSTRAINT_FLEXIBILITY; 2502 case CONSTRAINT_IDLE: 2503 return JobServerProtoEnums.CONSTRAINT_IDLE; 2504 case CONSTRAINT_PREFETCH: 2505 return JobServerProtoEnums.CONSTRAINT_PREFETCH; 2506 case CONSTRAINT_STORAGE_NOT_LOW: 2507 return JobServerProtoEnums.CONSTRAINT_STORAGE_NOT_LOW; 2508 case CONSTRAINT_TARE_WEALTH: 2509 return JobServerProtoEnums.CONSTRAINT_TARE_WEALTH; 2510 case CONSTRAINT_TIMING_DELAY: 2511 return JobServerProtoEnums.CONSTRAINT_TIMING_DELAY; 2512 case CONSTRAINT_WITHIN_QUOTA: 2513 return JobServerProtoEnums.CONSTRAINT_WITHIN_QUOTA; 2514 default: 2515 return JobServerProtoEnums.CONSTRAINT_UNKNOWN; 2516 } 2517 } 2518 2519 /** Writes constraints to the given repeating proto field. */ dumpConstraints(ProtoOutputStream proto, long fieldId, int constraints)2520 void dumpConstraints(ProtoOutputStream proto, long fieldId, int constraints) { 2521 if ((constraints & CONSTRAINT_CHARGING) != 0) { 2522 proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CHARGING); 2523 } 2524 if ((constraints & CONSTRAINT_BATTERY_NOT_LOW) != 0) { 2525 proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_BATTERY_NOT_LOW); 2526 } 2527 if ((constraints & CONSTRAINT_STORAGE_NOT_LOW) != 0) { 2528 proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_STORAGE_NOT_LOW); 2529 } 2530 if ((constraints & CONSTRAINT_TIMING_DELAY) != 0) { 2531 proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_TIMING_DELAY); 2532 } 2533 if ((constraints & CONSTRAINT_DEADLINE) != 0) { 2534 proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_DEADLINE); 2535 } 2536 if ((constraints & CONSTRAINT_IDLE) != 0) { 2537 proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_IDLE); 2538 } 2539 if ((constraints & CONSTRAINT_CONNECTIVITY) != 0) { 2540 proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CONNECTIVITY); 2541 } 2542 if ((constraints & CONSTRAINT_CONTENT_TRIGGER) != 0) { 2543 proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CONTENT_TRIGGER); 2544 } 2545 if ((constraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0) { 2546 proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_DEVICE_NOT_DOZING); 2547 } 2548 if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) { 2549 proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_WITHIN_QUOTA); 2550 } 2551 if ((constraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) { 2552 proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED); 2553 } 2554 } 2555 dumpJobWorkItem(IndentingPrintWriter pw, JobWorkItem work, int index)2556 private void dumpJobWorkItem(IndentingPrintWriter pw, JobWorkItem work, int index) { 2557 pw.increaseIndent(); 2558 pw.print("#"); pw.print(index); pw.print(": #"); 2559 pw.print(work.getWorkId()); pw.print(" "); pw.print(work.getDeliveryCount()); 2560 pw.print("x "); pw.println(work.getIntent()); 2561 if (work.getGrants() != null) { 2562 pw.println("URI grants:"); 2563 pw.increaseIndent(); 2564 ((GrantedUriPermissions) work.getGrants()).dump(pw); 2565 pw.decreaseIndent(); 2566 } 2567 pw.decreaseIndent(); 2568 } 2569 dumpJobWorkItem(ProtoOutputStream proto, long fieldId, JobWorkItem work)2570 private void dumpJobWorkItem(ProtoOutputStream proto, long fieldId, JobWorkItem work) { 2571 final long token = proto.start(fieldId); 2572 2573 proto.write(JobStatusDumpProto.JobWorkItem.WORK_ID, work.getWorkId()); 2574 proto.write(JobStatusDumpProto.JobWorkItem.DELIVERY_COUNT, work.getDeliveryCount()); 2575 if (work.getIntent() != null) { 2576 work.getIntent().dumpDebug(proto, JobStatusDumpProto.JobWorkItem.INTENT); 2577 } 2578 Object grants = work.getGrants(); 2579 if (grants != null) { 2580 ((GrantedUriPermissions) grants).dump(proto, JobStatusDumpProto.JobWorkItem.URI_GRANTS); 2581 } 2582 2583 proto.end(token); 2584 } 2585 2586 /** 2587 * Returns a bucket name based on the normalized bucket indices, not the AppStandby constants. 2588 */ getBucketName()2589 String getBucketName() { 2590 return bucketName(standbyBucket); 2591 } 2592 2593 /** 2594 * Returns a bucket name based on the normalized bucket indices, not the AppStandby constants. 2595 */ bucketName(int standbyBucket)2596 static String bucketName(int standbyBucket) { 2597 switch (standbyBucket) { 2598 case 0: return "ACTIVE"; 2599 case 1: return "WORKING_SET"; 2600 case 2: return "FREQUENT"; 2601 case 3: return "RARE"; 2602 case 4: return "NEVER"; 2603 case 5: return "RESTRICTED"; 2604 case 6: return "EXEMPTED"; 2605 default: 2606 return "Unknown: " + standbyBucket; 2607 } 2608 } 2609 2610 // Dumpsys infrastructure 2611 @NeverCompile // Avoid size overhead of debugging code. dump(IndentingPrintWriter pw, boolean full, long nowElapsed)2612 public void dump(IndentingPrintWriter pw, boolean full, long nowElapsed) { 2613 UserHandle.formatUid(pw, callingUid); 2614 pw.print(" tag="); pw.println(tag); 2615 2616 pw.print("Source: uid="); UserHandle.formatUid(pw, getSourceUid()); 2617 pw.print(" user="); pw.print(getSourceUserId()); 2618 pw.print(" pkg="); pw.println(getSourcePackageName()); 2619 if (full) { 2620 pw.println("JobInfo:"); 2621 pw.increaseIndent(); 2622 2623 pw.print("Service: "); 2624 pw.println(job.getService().flattenToShortString()); 2625 if (job.isPeriodic()) { 2626 pw.print("PERIODIC: interval="); 2627 TimeUtils.formatDuration(job.getIntervalMillis(), pw); 2628 pw.print(" flex="); TimeUtils.formatDuration(job.getFlexMillis(), pw); 2629 pw.println(); 2630 } 2631 if (job.isPersisted()) { 2632 pw.println("PERSISTED"); 2633 } 2634 if (job.getBias() != 0) { 2635 pw.print("Bias: "); 2636 pw.println(JobInfo.getBiasString(job.getBias())); 2637 } 2638 pw.print("Priority: "); 2639 pw.print(JobInfo.getPriorityString(job.getPriority())); 2640 final int effectivePriority = getEffectivePriority(); 2641 if (effectivePriority != job.getPriority()) { 2642 pw.print(" effective="); 2643 pw.print(JobInfo.getPriorityString(effectivePriority)); 2644 } 2645 pw.println(); 2646 if (job.getFlags() != 0) { 2647 pw.print("Flags: "); 2648 pw.println(Integer.toHexString(job.getFlags())); 2649 } 2650 if (getInternalFlags() != 0) { 2651 pw.print("Internal flags: "); 2652 pw.print(Integer.toHexString(getInternalFlags())); 2653 2654 if ((getInternalFlags()&INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0) { 2655 pw.print(" HAS_FOREGROUND_EXEMPTION"); 2656 } 2657 pw.println(); 2658 } 2659 pw.print("Requires: charging="); 2660 pw.print(job.isRequireCharging()); pw.print(" batteryNotLow="); 2661 pw.print(job.isRequireBatteryNotLow()); pw.print(" deviceIdle="); 2662 pw.println(job.isRequireDeviceIdle()); 2663 if (job.getTriggerContentUris() != null) { 2664 pw.println("Trigger content URIs:"); 2665 pw.increaseIndent(); 2666 for (int i = 0; i < job.getTriggerContentUris().length; i++) { 2667 JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i]; 2668 pw.print(Integer.toHexString(trig.getFlags())); 2669 pw.print(' '); pw.println(trig.getUri()); 2670 } 2671 pw.decreaseIndent(); 2672 if (job.getTriggerContentUpdateDelay() >= 0) { 2673 pw.print("Trigger update delay: "); 2674 TimeUtils.formatDuration(job.getTriggerContentUpdateDelay(), pw); 2675 pw.println(); 2676 } 2677 if (job.getTriggerContentMaxDelay() >= 0) { 2678 pw.print("Trigger max delay: "); 2679 TimeUtils.formatDuration(job.getTriggerContentMaxDelay(), pw); 2680 pw.println(); 2681 } 2682 pw.print("Has media backup exemption", mHasMediaBackupExemption).println(); 2683 } 2684 if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) { 2685 pw.print("Extras: "); 2686 pw.println(job.getExtras().toShortString()); 2687 } 2688 if (job.getTransientExtras() != null && !job.getTransientExtras().isDefinitelyEmpty()) { 2689 pw.print("Transient extras: "); 2690 pw.println(job.getTransientExtras().toShortString()); 2691 } 2692 if (job.getClipData() != null) { 2693 pw.print("Clip data: "); 2694 StringBuilder b = new StringBuilder(128); 2695 b.append(job.getClipData()); 2696 pw.println(b); 2697 } 2698 if (uriPerms != null) { 2699 pw.println("Granted URI permissions:"); 2700 uriPerms.dump(pw); 2701 } 2702 if (job.getRequiredNetwork() != null) { 2703 pw.print("Network type: "); 2704 pw.println(job.getRequiredNetwork()); 2705 } 2706 if (mTotalNetworkDownloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { 2707 pw.print("Network download bytes: "); 2708 pw.println(mTotalNetworkDownloadBytes); 2709 } 2710 if (mTotalNetworkUploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { 2711 pw.print("Network upload bytes: "); 2712 pw.println(mTotalNetworkUploadBytes); 2713 } 2714 if (mMinimumNetworkChunkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { 2715 pw.print("Minimum network chunk bytes: "); 2716 pw.println(mMinimumNetworkChunkBytes); 2717 } 2718 if (job.getMinLatencyMillis() != 0) { 2719 pw.print("Minimum latency: "); 2720 TimeUtils.formatDuration(job.getMinLatencyMillis(), pw); 2721 pw.println(); 2722 } 2723 if (job.getMaxExecutionDelayMillis() != 0) { 2724 pw.print("Max execution delay: "); 2725 TimeUtils.formatDuration(job.getMaxExecutionDelayMillis(), pw); 2726 pw.println(); 2727 } 2728 pw.print("Backoff: policy="); pw.print(job.getBackoffPolicy()); 2729 pw.print(" initial="); TimeUtils.formatDuration(job.getInitialBackoffMillis(), pw); 2730 pw.println(); 2731 if (job.hasEarlyConstraint()) { 2732 pw.println("Has early constraint"); 2733 } 2734 if (job.hasLateConstraint()) { 2735 pw.println("Has late constraint"); 2736 } 2737 2738 pw.decreaseIndent(); 2739 } 2740 2741 pw.print("Required constraints:"); 2742 dumpConstraints(pw, requiredConstraints); 2743 pw.println(); 2744 pw.print("Preferred constraints:"); 2745 dumpConstraints(pw, mPreferredConstraints); 2746 pw.println(); 2747 pw.print("Dynamic constraints:"); 2748 dumpConstraints(pw, mDynamicConstraints); 2749 pw.println(); 2750 if (full) { 2751 pw.print("Satisfied constraints:"); 2752 dumpConstraints(pw, satisfiedConstraints); 2753 pw.println(); 2754 pw.print("Unsatisfied constraints:"); 2755 dumpConstraints(pw, 2756 ((requiredConstraints | CONSTRAINT_WITHIN_QUOTA | CONSTRAINT_TARE_WEALTH) 2757 & ~satisfiedConstraints)); 2758 pw.println(); 2759 if (hasFlexibilityConstraint()) { 2760 pw.print("Num Required Flexible constraints: "); 2761 pw.print(getNumRequiredFlexibleConstraints()); 2762 pw.println(); 2763 pw.print("Num Dropped Flexible constraints: "); 2764 pw.print(getNumDroppedFlexibleConstraints()); 2765 pw.println(); 2766 } 2767 2768 pw.println("Constraint history:"); 2769 pw.increaseIndent(); 2770 for (int h = 0; h < NUM_CONSTRAINT_CHANGE_HISTORY; ++h) { 2771 final int idx = (h + mConstraintChangeHistoryIndex) % NUM_CONSTRAINT_CHANGE_HISTORY; 2772 if (mConstraintUpdatedTimesElapsed[idx] == 0) { 2773 continue; 2774 } 2775 TimeUtils.formatDuration(mConstraintUpdatedTimesElapsed[idx], nowElapsed, pw); 2776 // dumpConstraints prepends with a space, so no need to add a space after the = 2777 pw.print(" ="); 2778 dumpConstraints(pw, mConstraintStatusHistory[idx]); 2779 pw.println(); 2780 } 2781 pw.decreaseIndent(); 2782 2783 if (appHasDozeExemption) { 2784 pw.println("Doze whitelisted: true"); 2785 } 2786 if (uidActive) { 2787 pw.println("Uid: active"); 2788 } 2789 if (job.isExemptedFromAppStandby()) { 2790 pw.println("Is exempted from app standby"); 2791 } 2792 } 2793 if (trackingControllers != 0) { 2794 pw.print("Tracking:"); 2795 if ((trackingControllers&TRACKING_BATTERY) != 0) pw.print(" BATTERY"); 2796 if ((trackingControllers&TRACKING_CONNECTIVITY) != 0) pw.print(" CONNECTIVITY"); 2797 if ((trackingControllers&TRACKING_CONTENT) != 0) pw.print(" CONTENT"); 2798 if ((trackingControllers&TRACKING_IDLE) != 0) pw.print(" IDLE"); 2799 if ((trackingControllers&TRACKING_STORAGE) != 0) pw.print(" STORAGE"); 2800 if ((trackingControllers&TRACKING_TIME) != 0) pw.print(" TIME"); 2801 if ((trackingControllers & TRACKING_QUOTA) != 0) pw.print(" QUOTA"); 2802 pw.println(); 2803 } 2804 2805 pw.println("Implicit constraints:"); 2806 pw.increaseIndent(); 2807 pw.print("readyNotDozing: "); 2808 pw.println(mReadyNotDozing); 2809 pw.print("readyNotRestrictedInBg: "); 2810 pw.println(mReadyNotRestrictedInBg); 2811 if (!job.isPeriodic() && hasDeadlineConstraint()) { 2812 pw.print("readyDeadlineSatisfied: "); 2813 pw.println(mReadyDeadlineSatisfied); 2814 } 2815 if (mDynamicConstraints != 0) { 2816 pw.print("readyDynamicSatisfied: "); 2817 pw.println(mReadyDynamicSatisfied); 2818 } 2819 pw.print("readyComponentEnabled: "); 2820 pw.println(serviceProcessName != null); 2821 if ((getFlags() & JobInfo.FLAG_EXPEDITED) != 0) { 2822 pw.print("expeditedQuotaApproved: "); 2823 pw.print(mExpeditedQuotaApproved); 2824 pw.print(" expeditedTareApproved: "); 2825 pw.print(mExpeditedTareApproved); 2826 pw.print(" (started as EJ: "); 2827 pw.print(startedAsExpeditedJob); 2828 pw.println(")"); 2829 } 2830 if ((getFlags() & JobInfo.FLAG_USER_INITIATED) != 0) { 2831 pw.print("userInitiatedApproved: "); 2832 pw.print(shouldTreatAsUserInitiatedJob()); 2833 pw.print(" (started as UIJ: "); 2834 pw.print(startedAsUserInitiatedJob); 2835 pw.println(")"); 2836 } 2837 pw.decreaseIndent(); 2838 2839 pw.print("Started with foreground flag: "); 2840 pw.println(startedWithForegroundFlag); 2841 if (mIsUserBgRestricted) { 2842 pw.println("User BG restricted"); 2843 } 2844 2845 if (changedAuthorities != null) { 2846 pw.println("Changed authorities:"); 2847 pw.increaseIndent(); 2848 for (int i=0; i<changedAuthorities.size(); i++) { 2849 pw.println(changedAuthorities.valueAt(i)); 2850 } 2851 pw.decreaseIndent(); 2852 } 2853 if (changedUris != null) { 2854 pw.println("Changed URIs:"); 2855 pw.increaseIndent(); 2856 for (int i = 0; i < changedUris.size(); i++) { 2857 pw.println(changedUris.valueAt(i)); 2858 } 2859 pw.decreaseIndent(); 2860 } 2861 if (network != null) { 2862 pw.print("Network: "); pw.println(network); 2863 } 2864 if (pendingWork != null && pendingWork.size() > 0) { 2865 pw.println("Pending work:"); 2866 for (int i = 0; i < pendingWork.size(); i++) { 2867 dumpJobWorkItem(pw, pendingWork.get(i), i); 2868 } 2869 } 2870 if (executingWork != null && executingWork.size() > 0) { 2871 pw.println("Executing work:"); 2872 for (int i = 0; i < executingWork.size(); i++) { 2873 dumpJobWorkItem(pw, executingWork.get(i), i); 2874 } 2875 } 2876 pw.print("Standby bucket: "); 2877 pw.println(getBucketName()); 2878 pw.increaseIndent(); 2879 if (whenStandbyDeferred != 0) { 2880 pw.print("Deferred since: "); 2881 TimeUtils.formatDuration(whenStandbyDeferred, nowElapsed, pw); 2882 pw.println(); 2883 } 2884 if (mFirstForceBatchedTimeElapsed != 0) { 2885 pw.print("Time since first force batch attempt: "); 2886 TimeUtils.formatDuration(mFirstForceBatchedTimeElapsed, nowElapsed, pw); 2887 pw.println(); 2888 } 2889 pw.decreaseIndent(); 2890 2891 pw.print("Enqueue time: "); 2892 TimeUtils.formatDuration(enqueueTime, nowElapsed, pw); 2893 pw.println(); 2894 pw.print("Run time: earliest="); 2895 formatRunTime(pw, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, nowElapsed); 2896 pw.print(", latest="); 2897 formatRunTime(pw, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, nowElapsed); 2898 pw.print(", original latest="); 2899 formatRunTime(pw, mOriginalLatestRunTimeElapsedMillis, NO_LATEST_RUNTIME, nowElapsed); 2900 pw.println(); 2901 if (mCumulativeExecutionTimeMs != 0) { 2902 pw.print("Cumulative execution time="); 2903 TimeUtils.formatDuration(mCumulativeExecutionTimeMs, pw); 2904 pw.println(); 2905 } 2906 if (numFailures != 0) { 2907 pw.print("Num failures: "); pw.println(numFailures); 2908 } 2909 if (mNumSystemStops != 0) { 2910 pw.print("Num system stops: "); pw.println(mNumSystemStops); 2911 } 2912 if (mLastSuccessfulRunTime != 0) { 2913 pw.print("Last successful run: "); 2914 pw.println(formatTime(mLastSuccessfulRunTime)); 2915 } 2916 if (mLastFailedRunTime != 0) { 2917 pw.print("Last failed run: "); 2918 pw.println(formatTime(mLastFailedRunTime)); 2919 } 2920 } 2921 formatTime(long time)2922 private static CharSequence formatTime(long time) { 2923 return DateFormat.format("yyyy-MM-dd HH:mm:ss", time); 2924 } 2925 dump(ProtoOutputStream proto, long fieldId, boolean full, long elapsedRealtimeMillis)2926 public void dump(ProtoOutputStream proto, long fieldId, boolean full, long elapsedRealtimeMillis) { 2927 final long token = proto.start(fieldId); 2928 2929 proto.write(JobStatusDumpProto.CALLING_UID, callingUid); 2930 proto.write(JobStatusDumpProto.TAG, tag); 2931 proto.write(JobStatusDumpProto.SOURCE_UID, getSourceUid()); 2932 proto.write(JobStatusDumpProto.SOURCE_USER_ID, getSourceUserId()); 2933 proto.write(JobStatusDumpProto.SOURCE_PACKAGE_NAME, getSourcePackageName()); 2934 2935 if (full) { 2936 final long jiToken = proto.start(JobStatusDumpProto.JOB_INFO); 2937 2938 job.getService().dumpDebug(proto, JobStatusDumpProto.JobInfo.SERVICE); 2939 2940 proto.write(JobStatusDumpProto.JobInfo.IS_PERIODIC, job.isPeriodic()); 2941 proto.write(JobStatusDumpProto.JobInfo.PERIOD_INTERVAL_MS, job.getIntervalMillis()); 2942 proto.write(JobStatusDumpProto.JobInfo.PERIOD_FLEX_MS, job.getFlexMillis()); 2943 2944 proto.write(JobStatusDumpProto.JobInfo.IS_PERSISTED, job.isPersisted()); 2945 proto.write(JobStatusDumpProto.JobInfo.PRIORITY, job.getBias()); 2946 proto.write(JobStatusDumpProto.JobInfo.FLAGS, job.getFlags()); 2947 proto.write(JobStatusDumpProto.INTERNAL_FLAGS, getInternalFlags()); 2948 // Foreground exemption can be determined from internal flags value. 2949 2950 proto.write(JobStatusDumpProto.JobInfo.REQUIRES_CHARGING, job.isRequireCharging()); 2951 proto.write(JobStatusDumpProto.JobInfo.REQUIRES_BATTERY_NOT_LOW, job.isRequireBatteryNotLow()); 2952 proto.write(JobStatusDumpProto.JobInfo.REQUIRES_DEVICE_IDLE, job.isRequireDeviceIdle()); 2953 2954 if (job.getTriggerContentUris() != null) { 2955 for (int i = 0; i < job.getTriggerContentUris().length; i++) { 2956 final long tcuToken = proto.start(JobStatusDumpProto.JobInfo.TRIGGER_CONTENT_URIS); 2957 JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i]; 2958 2959 proto.write(JobStatusDumpProto.JobInfo.TriggerContentUri.FLAGS, trig.getFlags()); 2960 Uri u = trig.getUri(); 2961 if (u != null) { 2962 proto.write(JobStatusDumpProto.JobInfo.TriggerContentUri.URI, u.toString()); 2963 } 2964 2965 proto.end(tcuToken); 2966 } 2967 if (job.getTriggerContentUpdateDelay() >= 0) { 2968 proto.write(JobStatusDumpProto.JobInfo.TRIGGER_CONTENT_UPDATE_DELAY_MS, 2969 job.getTriggerContentUpdateDelay()); 2970 } 2971 if (job.getTriggerContentMaxDelay() >= 0) { 2972 proto.write(JobStatusDumpProto.JobInfo.TRIGGER_CONTENT_MAX_DELAY_MS, 2973 job.getTriggerContentMaxDelay()); 2974 } 2975 } 2976 if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) { 2977 job.getExtras().dumpDebug(proto, JobStatusDumpProto.JobInfo.EXTRAS); 2978 } 2979 if (job.getTransientExtras() != null && !job.getTransientExtras().isDefinitelyEmpty()) { 2980 job.getTransientExtras().dumpDebug(proto, JobStatusDumpProto.JobInfo.TRANSIENT_EXTRAS); 2981 } 2982 if (job.getClipData() != null) { 2983 job.getClipData().dumpDebug(proto, JobStatusDumpProto.JobInfo.CLIP_DATA); 2984 } 2985 if (uriPerms != null) { 2986 uriPerms.dump(proto, JobStatusDumpProto.JobInfo.GRANTED_URI_PERMISSIONS); 2987 } 2988 if (mTotalNetworkDownloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { 2989 proto.write(JobStatusDumpProto.JobInfo.TOTAL_NETWORK_DOWNLOAD_BYTES, 2990 mTotalNetworkDownloadBytes); 2991 } 2992 if (mTotalNetworkUploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { 2993 proto.write(JobStatusDumpProto.JobInfo.TOTAL_NETWORK_UPLOAD_BYTES, 2994 mTotalNetworkUploadBytes); 2995 } 2996 proto.write(JobStatusDumpProto.JobInfo.MIN_LATENCY_MS, job.getMinLatencyMillis()); 2997 proto.write(JobStatusDumpProto.JobInfo.MAX_EXECUTION_DELAY_MS, job.getMaxExecutionDelayMillis()); 2998 2999 final long bpToken = proto.start(JobStatusDumpProto.JobInfo.BACKOFF_POLICY); 3000 proto.write(JobStatusDumpProto.JobInfo.Backoff.POLICY, job.getBackoffPolicy()); 3001 proto.write(JobStatusDumpProto.JobInfo.Backoff.INITIAL_BACKOFF_MS, 3002 job.getInitialBackoffMillis()); 3003 proto.end(bpToken); 3004 3005 proto.write(JobStatusDumpProto.JobInfo.HAS_EARLY_CONSTRAINT, job.hasEarlyConstraint()); 3006 proto.write(JobStatusDumpProto.JobInfo.HAS_LATE_CONSTRAINT, job.hasLateConstraint()); 3007 3008 proto.end(jiToken); 3009 } 3010 3011 dumpConstraints(proto, JobStatusDumpProto.REQUIRED_CONSTRAINTS, requiredConstraints); 3012 dumpConstraints(proto, JobStatusDumpProto.DYNAMIC_CONSTRAINTS, mDynamicConstraints); 3013 if (full) { 3014 dumpConstraints(proto, JobStatusDumpProto.SATISFIED_CONSTRAINTS, satisfiedConstraints); 3015 dumpConstraints(proto, JobStatusDumpProto.UNSATISFIED_CONSTRAINTS, 3016 ((requiredConstraints | CONSTRAINT_WITHIN_QUOTA) & ~satisfiedConstraints)); 3017 proto.write(JobStatusDumpProto.IS_DOZE_WHITELISTED, appHasDozeExemption); 3018 proto.write(JobStatusDumpProto.IS_UID_ACTIVE, uidActive); 3019 proto.write(JobStatusDumpProto.IS_EXEMPTED_FROM_APP_STANDBY, 3020 job.isExemptedFromAppStandby()); 3021 } 3022 3023 // Tracking controllers 3024 if ((trackingControllers&TRACKING_BATTERY) != 0) { 3025 proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS, 3026 JobStatusDumpProto.TRACKING_BATTERY); 3027 } 3028 if ((trackingControllers&TRACKING_CONNECTIVITY) != 0) { 3029 proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS, 3030 JobStatusDumpProto.TRACKING_CONNECTIVITY); 3031 } 3032 if ((trackingControllers&TRACKING_CONTENT) != 0) { 3033 proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS, 3034 JobStatusDumpProto.TRACKING_CONTENT); 3035 } 3036 if ((trackingControllers&TRACKING_IDLE) != 0) { 3037 proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS, 3038 JobStatusDumpProto.TRACKING_IDLE); 3039 } 3040 if ((trackingControllers&TRACKING_STORAGE) != 0) { 3041 proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS, 3042 JobStatusDumpProto.TRACKING_STORAGE); 3043 } 3044 if ((trackingControllers&TRACKING_TIME) != 0) { 3045 proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS, 3046 JobStatusDumpProto.TRACKING_TIME); 3047 } 3048 if ((trackingControllers & TRACKING_QUOTA) != 0) { 3049 proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS, 3050 JobStatusDumpProto.TRACKING_QUOTA); 3051 } 3052 3053 // Implicit constraints 3054 final long icToken = proto.start(JobStatusDumpProto.IMPLICIT_CONSTRAINTS); 3055 proto.write(JobStatusDumpProto.ImplicitConstraints.IS_NOT_DOZING, mReadyNotDozing); 3056 proto.write(JobStatusDumpProto.ImplicitConstraints.IS_NOT_RESTRICTED_IN_BG, 3057 mReadyNotRestrictedInBg); 3058 // mReadyDeadlineSatisfied isn't an implicit constraint...and can be determined from other 3059 // field values. 3060 proto.write(JobStatusDumpProto.ImplicitConstraints.IS_DYNAMIC_SATISFIED, 3061 mReadyDynamicSatisfied); 3062 proto.end(icToken); 3063 3064 if (changedAuthorities != null) { 3065 for (int k = 0; k < changedAuthorities.size(); k++) { 3066 proto.write(JobStatusDumpProto.CHANGED_AUTHORITIES, changedAuthorities.valueAt(k)); 3067 } 3068 } 3069 if (changedUris != null) { 3070 for (int i = 0; i < changedUris.size(); i++) { 3071 Uri u = changedUris.valueAt(i); 3072 proto.write(JobStatusDumpProto.CHANGED_URIS, u.toString()); 3073 } 3074 } 3075 3076 if (pendingWork != null) { 3077 for (int i = 0; i < pendingWork.size(); i++) { 3078 dumpJobWorkItem(proto, JobStatusDumpProto.PENDING_WORK, pendingWork.get(i)); 3079 } 3080 } 3081 if (executingWork != null) { 3082 for (int i = 0; i < executingWork.size(); i++) { 3083 dumpJobWorkItem(proto, JobStatusDumpProto.EXECUTING_WORK, executingWork.get(i)); 3084 } 3085 } 3086 3087 proto.write(JobStatusDumpProto.STANDBY_BUCKET, standbyBucket); 3088 proto.write(JobStatusDumpProto.ENQUEUE_DURATION_MS, elapsedRealtimeMillis - enqueueTime); 3089 proto.write(JobStatusDumpProto.TIME_SINCE_FIRST_DEFERRAL_MS, 3090 whenStandbyDeferred == 0 ? 0 : elapsedRealtimeMillis - whenStandbyDeferred); 3091 proto.write(JobStatusDumpProto.TIME_SINCE_FIRST_FORCE_BATCH_ATTEMPT_MS, 3092 mFirstForceBatchedTimeElapsed == 0 3093 ? 0 : elapsedRealtimeMillis - mFirstForceBatchedTimeElapsed); 3094 if (earliestRunTimeElapsedMillis == NO_EARLIEST_RUNTIME) { 3095 proto.write(JobStatusDumpProto.TIME_UNTIL_EARLIEST_RUNTIME_MS, 0); 3096 } else { 3097 proto.write(JobStatusDumpProto.TIME_UNTIL_EARLIEST_RUNTIME_MS, 3098 earliestRunTimeElapsedMillis - elapsedRealtimeMillis); 3099 } 3100 if (latestRunTimeElapsedMillis == NO_LATEST_RUNTIME) { 3101 proto.write(JobStatusDumpProto.TIME_UNTIL_LATEST_RUNTIME_MS, 0); 3102 } else { 3103 proto.write(JobStatusDumpProto.TIME_UNTIL_LATEST_RUNTIME_MS, 3104 latestRunTimeElapsedMillis - elapsedRealtimeMillis); 3105 } 3106 proto.write(JobStatusDumpProto.ORIGINAL_LATEST_RUNTIME_ELAPSED, 3107 mOriginalLatestRunTimeElapsedMillis); 3108 3109 proto.write(JobStatusDumpProto.NUM_FAILURES, numFailures + mNumSystemStops); 3110 proto.write(JobStatusDumpProto.LAST_SUCCESSFUL_RUN_TIME, mLastSuccessfulRunTime); 3111 proto.write(JobStatusDumpProto.LAST_FAILED_RUN_TIME, mLastFailedRunTime); 3112 3113 proto.end(token); 3114 } 3115 } 3116