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