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