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; 18 19 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; 20 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; 21 import static android.text.format.DateUtils.MINUTE_IN_MILLIS; 22 23 import android.annotation.NonNull; 24 import android.annotation.UserIdInt; 25 import android.app.Activity; 26 import android.app.ActivityManager; 27 import android.app.ActivityManagerInternal; 28 import android.app.AppGlobals; 29 import android.app.IUidObserver; 30 import android.app.job.IJobScheduler; 31 import android.app.job.JobInfo; 32 import android.app.job.JobParameters; 33 import android.app.job.JobProtoEnums; 34 import android.app.job.JobScheduler; 35 import android.app.job.JobService; 36 import android.app.job.JobSnapshot; 37 import android.app.job.JobWorkItem; 38 import android.app.usage.UsageStatsManager; 39 import android.app.usage.UsageStatsManagerInternal; 40 import android.content.BroadcastReceiver; 41 import android.content.ComponentName; 42 import android.content.ContentResolver; 43 import android.content.Context; 44 import android.content.Intent; 45 import android.content.IntentFilter; 46 import android.content.pm.ApplicationInfo; 47 import android.content.pm.IPackageManager; 48 import android.content.pm.PackageManager; 49 import android.content.pm.PackageManager.NameNotFoundException; 50 import android.content.pm.PackageManagerInternal; 51 import android.content.pm.ParceledListSlice; 52 import android.content.pm.ServiceInfo; 53 import android.database.ContentObserver; 54 import android.net.Uri; 55 import android.os.BatteryStats; 56 import android.os.BatteryStatsInternal; 57 import android.os.Binder; 58 import android.os.Handler; 59 import android.os.LimitExceededException; 60 import android.os.Looper; 61 import android.os.Message; 62 import android.os.ParcelFileDescriptor; 63 import android.os.Process; 64 import android.os.RemoteException; 65 import android.os.ServiceManager; 66 import android.os.SystemClock; 67 import android.os.UserHandle; 68 import android.os.UserManagerInternal; 69 import android.os.WorkSource; 70 import android.provider.Settings; 71 import android.text.format.DateUtils; 72 import android.util.ArrayMap; 73 import android.util.KeyValueListParser; 74 import android.util.Log; 75 import android.util.Slog; 76 import android.util.SparseArray; 77 import android.util.SparseIntArray; 78 import android.util.TimeUtils; 79 import android.util.proto.ProtoOutputStream; 80 81 import com.android.internal.R; 82 import com.android.internal.annotations.VisibleForTesting; 83 import com.android.internal.app.IBatteryStats; 84 import com.android.internal.util.ArrayUtils; 85 import com.android.internal.util.DumpUtils; 86 import com.android.internal.util.FrameworkStatsLog; 87 import com.android.internal.util.IndentingPrintWriter; 88 import com.android.server.AppStateTracker; 89 import com.android.server.DeviceIdleInternal; 90 import com.android.server.FgThread; 91 import com.android.server.LocalServices; 92 import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob; 93 import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob; 94 import com.android.server.job.controllers.BackgroundJobsController; 95 import com.android.server.job.controllers.BatteryController; 96 import com.android.server.job.controllers.ConnectivityController; 97 import com.android.server.job.controllers.ContentObserverController; 98 import com.android.server.job.controllers.DeviceIdleJobsController; 99 import com.android.server.job.controllers.IdleController; 100 import com.android.server.job.controllers.JobStatus; 101 import com.android.server.job.controllers.QuotaController; 102 import com.android.server.job.controllers.RestrictingController; 103 import com.android.server.job.controllers.StateController; 104 import com.android.server.job.controllers.StorageController; 105 import com.android.server.job.controllers.TimeController; 106 import com.android.server.job.restrictions.JobRestriction; 107 import com.android.server.job.restrictions.ThermalStatusRestriction; 108 import com.android.server.usage.AppStandbyInternal; 109 import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; 110 import com.android.server.utils.quota.Categorizer; 111 import com.android.server.utils.quota.Category; 112 import com.android.server.utils.quota.CountQuotaTracker; 113 114 import libcore.util.EmptyArray; 115 116 import java.io.FileDescriptor; 117 import java.io.PrintWriter; 118 import java.time.Clock; 119 import java.time.Instant; 120 import java.time.ZoneId; 121 import java.time.ZoneOffset; 122 import java.util.ArrayList; 123 import java.util.Arrays; 124 import java.util.Collections; 125 import java.util.Comparator; 126 import java.util.List; 127 import java.util.Objects; 128 import java.util.function.Consumer; 129 import java.util.function.Predicate; 130 131 /** 132 * Responsible for taking jobs representing work to be performed by a client app, and determining 133 * based on the criteria specified when that job should be run against the client application's 134 * endpoint. 135 * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing 136 * about constraints, or the state of active jobs. It receives callbacks from the various 137 * controllers and completed jobs and operates accordingly. 138 * 139 * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object. 140 * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}. 141 * @hide 142 */ 143 public class JobSchedulerService extends com.android.server.SystemService 144 implements StateChangedListener, JobCompletedListener { 145 public static final String TAG = "JobScheduler"; 146 public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 147 public static final boolean DEBUG_STANDBY = DEBUG || false; 148 149 /** The maximum number of concurrent jobs we run at one time. */ 150 static final int MAX_JOB_CONTEXTS_COUNT = 16; 151 /** Enforce a per-app limit on scheduled jobs? */ 152 private static final boolean ENFORCE_MAX_JOBS = true; 153 /** The maximum number of jobs that we allow an unprivileged app to schedule */ 154 private static final int MAX_JOBS_PER_APP = 100; 155 156 @VisibleForTesting 157 public static Clock sSystemClock = Clock.systemUTC(); 158 159 private abstract static class MySimpleClock extends Clock { 160 private final ZoneId mZoneId; 161 MySimpleClock(ZoneId zoneId)162 MySimpleClock(ZoneId zoneId) { 163 this.mZoneId = zoneId; 164 } 165 166 @Override getZone()167 public ZoneId getZone() { 168 return mZoneId; 169 } 170 171 @Override withZone(ZoneId zone)172 public Clock withZone(ZoneId zone) { 173 return new MySimpleClock(zone) { 174 @Override 175 public long millis() { 176 return MySimpleClock.this.millis(); 177 } 178 }; 179 } 180 181 @Override millis()182 public abstract long millis(); 183 184 @Override instant()185 public Instant instant() { 186 return Instant.ofEpochMilli(millis()); 187 } 188 } 189 190 @VisibleForTesting 191 public static Clock sUptimeMillisClock = new MySimpleClock(ZoneOffset.UTC) { 192 @Override 193 public long millis() { 194 return SystemClock.uptimeMillis(); 195 } 196 }; 197 198 @VisibleForTesting 199 public static Clock sElapsedRealtimeClock = new MySimpleClock(ZoneOffset.UTC) { 200 @Override 201 public long millis() { 202 return SystemClock.elapsedRealtime(); 203 } 204 }; 205 206 /** Global local for all job scheduler state. */ 207 final Object mLock = new Object(); 208 /** Master list of jobs. */ 209 final JobStore mJobs; 210 /** Tracking the standby bucket state of each app */ 211 final StandbyTracker mStandbyTracker; 212 /** Tracking amount of time each package runs for. */ 213 final JobPackageTracker mJobPackageTracker = new JobPackageTracker(); 214 final JobConcurrencyManager mConcurrencyManager; 215 216 static final int MSG_JOB_EXPIRED = 0; 217 static final int MSG_CHECK_JOB = 1; 218 static final int MSG_STOP_JOB = 2; 219 static final int MSG_CHECK_JOB_GREEDY = 3; 220 static final int MSG_UID_STATE_CHANGED = 4; 221 static final int MSG_UID_GONE = 5; 222 static final int MSG_UID_ACTIVE = 6; 223 static final int MSG_UID_IDLE = 7; 224 225 /** 226 * Track Services that have currently active or pending jobs. The index is provided by 227 * {@link JobStatus#getServiceToken()} 228 */ 229 final List<JobServiceContext> mActiveServices = new ArrayList<>(); 230 231 /** List of controllers that will notify this service of updates to jobs. */ 232 final List<StateController> mControllers; 233 /** 234 * List of controllers that will apply to all jobs in the RESTRICTED bucket. This is a subset of 235 * {@link #mControllers}. 236 */ 237 private final List<RestrictingController> mRestrictiveControllers; 238 /** Need direct access to this for testing. */ 239 private final BatteryController mBatteryController; 240 /** Need direct access to this for testing. */ 241 private final StorageController mStorageController; 242 /** Need directly for sending uid state changes */ 243 private final DeviceIdleJobsController mDeviceIdleJobsController; 244 /** Needed to get remaining quota time. */ 245 private final QuotaController mQuotaController; 246 /** 247 * List of restrictions. 248 * Note: do not add to or remove from this list at runtime except in the constructor, because we 249 * do not synchronize access to this list. 250 */ 251 private final List<JobRestriction> mJobRestrictions; 252 253 @NonNull 254 private final String mSystemGalleryPackage; 255 256 private final CountQuotaTracker mQuotaTracker; 257 private static final String QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG = ".schedulePersisted()"; 258 private static final String QUOTA_TRACKER_SCHEDULE_LOGGED = 259 ".schedulePersisted out-of-quota logged"; 260 private static final Category QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED = new Category( 261 ".schedulePersisted()"); 262 private static final Category QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED = new Category( 263 ".schedulePersisted out-of-quota logged"); 264 private static final Categorizer QUOTA_CATEGORIZER = (userId, packageName, tag) -> { 265 if (QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG.equals(tag)) { 266 return QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED; 267 } 268 return QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED; 269 }; 270 271 /** 272 * Queue of pending jobs. The JobServiceContext class will receive jobs from this list 273 * when ready to execute them. 274 */ 275 final ArrayList<JobStatus> mPendingJobs = new ArrayList<>(); 276 277 int[] mStartedUsers = EmptyArray.INT; 278 279 final JobHandler mHandler; 280 final JobSchedulerStub mJobSchedulerStub; 281 282 PackageManagerInternal mLocalPM; 283 ActivityManagerInternal mActivityManagerInternal; 284 IBatteryStats mBatteryStats; 285 DeviceIdleInternal mLocalDeviceIdleController; 286 @VisibleForTesting 287 AppStateTracker mAppStateTracker; 288 final UsageStatsManagerInternal mUsageStats; 289 private final AppStandbyInternal mAppStandbyInternal; 290 291 /** 292 * Set to true once we are allowed to run third party apps. 293 */ 294 boolean mReadyToRock; 295 296 /** 297 * What we last reported to DeviceIdleController about whether we are active. 298 */ 299 boolean mReportedActive; 300 301 /** 302 * A mapping of which uids are currently in the foreground to their effective priority. 303 */ 304 final SparseIntArray mUidPriorityOverride = new SparseIntArray(); 305 306 /** 307 * Which uids are currently performing backups, so we shouldn't allow their jobs to run. 308 */ 309 final SparseIntArray mBackingUpUids = new SparseIntArray(); 310 311 /** 312 * Cache of debuggable app status. 313 */ 314 final ArrayMap<String, Boolean> mDebuggableApps = new ArrayMap<>(); 315 316 /** 317 * Named indices into standby bucket arrays, for clarity in referring to 318 * specific buckets' bookkeeping. 319 */ 320 public static final int ACTIVE_INDEX = 0; 321 public static final int WORKING_INDEX = 1; 322 public static final int FREQUENT_INDEX = 2; 323 public static final int RARE_INDEX = 3; 324 public static final int NEVER_INDEX = 4; 325 // Putting RESTRICTED_INDEX after NEVER_INDEX to make it easier for proto dumping 326 // (ScheduledJobStateChanged and JobStatusDumpProto). 327 public static final int RESTRICTED_INDEX = 5; 328 329 // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked -- 330 331 private class ConstantsObserver extends ContentObserver { 332 private ContentResolver mResolver; 333 334 public ConstantsObserver(Handler handler) { 335 super(handler); 336 } 337 338 public void start(ContentResolver resolver) { 339 mResolver = resolver; 340 mResolver.registerContentObserver(Settings.Global.getUriFor( 341 Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this); 342 updateConstants(); 343 } 344 345 @Override 346 public void onChange(boolean selfChange, Uri uri) { 347 updateConstants(); 348 } 349 350 private void updateConstants() { 351 synchronized (mLock) { 352 try { 353 mConstants.updateConstantsLocked(Settings.Global.getString(mResolver, 354 Settings.Global.JOB_SCHEDULER_CONSTANTS)); 355 for (int controller = 0; controller < mControllers.size(); controller++) { 356 final StateController sc = mControllers.get(controller); 357 sc.onConstantsUpdatedLocked(); 358 } 359 updateQuotaTracker(); 360 } catch (IllegalArgumentException e) { 361 // Failed to parse the settings string, log this and move on 362 // with defaults. 363 Slog.e(TAG, "Bad jobscheduler settings", e); 364 } 365 } 366 } 367 } 368 369 @VisibleForTesting 370 void updateQuotaTracker() { 371 mQuotaTracker.setEnabled(mConstants.ENABLE_API_QUOTAS); 372 mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED, 373 mConstants.API_QUOTA_SCHEDULE_COUNT, 374 mConstants.API_QUOTA_SCHEDULE_WINDOW_MS); 375 } 376 377 static class MaxJobCounts { 378 private final KeyValueListParser.IntValue mTotal; 379 private final KeyValueListParser.IntValue mMaxBg; 380 private final KeyValueListParser.IntValue mMinBg; 381 382 MaxJobCounts(int totalDefault, String totalKey, 383 int maxBgDefault, String maxBgKey, int minBgDefault, String minBgKey) { 384 mTotal = new KeyValueListParser.IntValue(totalKey, totalDefault); 385 mMaxBg = new KeyValueListParser.IntValue(maxBgKey, maxBgDefault); 386 mMinBg = new KeyValueListParser.IntValue(minBgKey, minBgDefault); 387 } 388 389 public void parse(KeyValueListParser parser) { 390 mTotal.parse(parser); 391 mMaxBg.parse(parser); 392 mMinBg.parse(parser); 393 394 if (mTotal.getValue() < 1) { 395 mTotal.setValue(1); 396 } else if (mTotal.getValue() > MAX_JOB_CONTEXTS_COUNT) { 397 mTotal.setValue(MAX_JOB_CONTEXTS_COUNT); 398 } 399 400 if (mMaxBg.getValue() < 1) { 401 mMaxBg.setValue(1); 402 } else if (mMaxBg.getValue() > mTotal.getValue()) { 403 mMaxBg.setValue(mTotal.getValue()); 404 } 405 if (mMinBg.getValue() < 0) { 406 mMinBg.setValue(0); 407 } else { 408 if (mMinBg.getValue() > mMaxBg.getValue()) { 409 mMinBg.setValue(mMaxBg.getValue()); 410 } 411 if (mMinBg.getValue() >= mTotal.getValue()) { 412 mMinBg.setValue(mTotal.getValue() - 1); 413 } 414 } 415 } 416 417 /** Total number of jobs to run simultaneously. */ 418 public int getMaxTotal() { 419 return mTotal.getValue(); 420 } 421 422 /** Max number of BG (== owned by non-TOP apps) jobs to run simultaneously. */ 423 public int getMaxBg() { 424 return mMaxBg.getValue(); 425 } 426 427 /** 428 * We try to run at least this many BG (== owned by non-TOP apps) jobs, when there are any 429 * pending, rather than always running the TOTAL number of FG jobs. 430 */ 431 public int getMinBg() { 432 return mMinBg.getValue(); 433 } 434 435 public void dump(PrintWriter pw, String prefix) { 436 mTotal.dump(pw, prefix); 437 mMaxBg.dump(pw, prefix); 438 mMinBg.dump(pw, prefix); 439 } 440 441 public void dumpProto(ProtoOutputStream proto, long fieldId) { 442 final long token = proto.start(fieldId); 443 mTotal.dumpProto(proto, MaxJobCountsProto.TOTAL_JOBS); 444 mMaxBg.dumpProto(proto, MaxJobCountsProto.MAX_BG); 445 mMinBg.dumpProto(proto, MaxJobCountsProto.MIN_BG); 446 proto.end(token); 447 } 448 } 449 450 /** {@link MaxJobCounts} for each memory trim level. */ 451 static class MaxJobCountsPerMemoryTrimLevel { 452 public final MaxJobCounts normal; 453 public final MaxJobCounts moderate; 454 public final MaxJobCounts low; 455 public final MaxJobCounts critical; 456 457 MaxJobCountsPerMemoryTrimLevel( 458 MaxJobCounts normal, 459 MaxJobCounts moderate, MaxJobCounts low, 460 MaxJobCounts critical) { 461 this.normal = normal; 462 this.moderate = moderate; 463 this.low = low; 464 this.critical = critical; 465 } 466 467 public void dumpProto(ProtoOutputStream proto, long fieldId) { 468 final long token = proto.start(fieldId); 469 normal.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.NORMAL); 470 moderate.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.MODERATE); 471 low.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.LOW); 472 critical.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.CRITICAL); 473 proto.end(token); 474 } 475 } 476 477 /** 478 * All times are in milliseconds. These constants are kept synchronized with the system 479 * global Settings. Any access to this class or its fields should be done while 480 * holding the JobSchedulerService.mLock lock. 481 */ 482 public static class Constants { 483 // Key names stored in the settings value. 484 // TODO(124466289): remove deprecated flags when we migrate to DeviceConfig 485 private static final String DEPRECATED_KEY_MIN_IDLE_COUNT = "min_idle_count"; 486 private static final String DEPRECATED_KEY_MIN_CHARGING_COUNT = "min_charging_count"; 487 private static final String DEPRECATED_KEY_MIN_BATTERY_NOT_LOW_COUNT = 488 "min_battery_not_low_count"; 489 private static final String DEPRECATED_KEY_MIN_STORAGE_NOT_LOW_COUNT = 490 "min_storage_not_low_count"; 491 private static final String DEPRECATED_KEY_MIN_CONNECTIVITY_COUNT = 492 "min_connectivity_count"; 493 private static final String DEPRECATED_KEY_MIN_CONTENT_COUNT = "min_content_count"; 494 private static final String DEPRECATED_KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count"; 495 private static final String KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT = 496 "min_ready_non_active_jobs_count"; 497 private static final String KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = 498 "max_non_active_job_batch_delay_ms"; 499 private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor"; 500 private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor"; 501 502 // The following values used to be used on P and below. Do not reuse them. 503 private static final String DEPRECATED_KEY_FG_JOB_COUNT = "fg_job_count"; 504 private static final String DEPRECATED_KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count"; 505 private static final String DEPRECATED_KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count"; 506 private static final String DEPRECATED_KEY_BG_LOW_JOB_COUNT = "bg_low_job_count"; 507 private static final String DEPRECATED_KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count"; 508 509 private static final String DEPRECATED_KEY_MAX_STANDARD_RESCHEDULE_COUNT 510 = "max_standard_reschedule_count"; 511 private static final String DEPRECATED_KEY_MAX_WORK_RESCHEDULE_COUNT = 512 "max_work_reschedule_count"; 513 private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time"; 514 private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time"; 515 private static final String DEPRECATED_KEY_STANDBY_HEARTBEAT_TIME = 516 "standby_heartbeat_time"; 517 private static final String DEPRECATED_KEY_STANDBY_WORKING_BEATS = "standby_working_beats"; 518 private static final String DEPRECATED_KEY_STANDBY_FREQUENT_BEATS = 519 "standby_frequent_beats"; 520 private static final String DEPRECATED_KEY_STANDBY_RARE_BEATS = "standby_rare_beats"; 521 private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac"; 522 private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac"; 523 private static final String DEPRECATED_KEY_USE_HEARTBEATS = "use_heartbeats"; 524 private static final String KEY_ENABLE_API_QUOTAS = "enable_api_quotas"; 525 private static final String KEY_API_QUOTA_SCHEDULE_COUNT = "aq_schedule_count"; 526 private static final String KEY_API_QUOTA_SCHEDULE_WINDOW_MS = "aq_schedule_window_ms"; 527 private static final String KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION = 528 "aq_schedule_throw_exception"; 529 private static final String KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = 530 "aq_schedule_return_failure"; 531 532 private static final int DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT = 5; 533 private static final long DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = 31 * MINUTE_IN_MILLIS; 534 private static final float DEFAULT_HEAVY_USE_FACTOR = .9f; 535 private static final float DEFAULT_MODERATE_USE_FACTOR = .5f; 536 private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS; 537 private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS; 538 private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f; 539 private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f; 540 private static final boolean DEFAULT_ENABLE_API_QUOTAS = true; 541 private static final int DEFAULT_API_QUOTA_SCHEDULE_COUNT = 250; 542 private static final long DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS = MINUTE_IN_MILLIS; 543 private static final boolean DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION = true; 544 private static final boolean DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false; 545 546 /** 547 * Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early. 548 */ 549 int MIN_READY_NON_ACTIVE_JOBS_COUNT = DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT; 550 551 /** 552 * Don't batch a non-ACTIVE job if it's been delayed due to force batching attempts for 553 * at least this amount of time. 554 */ 555 long MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS; 556 557 /** 558 * This is the job execution factor that is considered to be heavy use of the system. 559 */ 560 float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR; 561 /** 562 * This is the job execution factor that is considered to be moderate use of the system. 563 */ 564 float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR; 565 566 // Max job counts for screen on / off, for each memory trim level. 567 final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_ON = 568 new MaxJobCountsPerMemoryTrimLevel( 569 new MaxJobCounts( 570 8, "max_job_total_on_normal", 571 6, "max_job_max_bg_on_normal", 572 2, "max_job_min_bg_on_normal"), 573 new MaxJobCounts( 574 8, "max_job_total_on_moderate", 575 4, "max_job_max_bg_on_moderate", 576 2, "max_job_min_bg_on_moderate"), 577 new MaxJobCounts( 578 5, "max_job_total_on_low", 579 1, "max_job_max_bg_on_low", 580 1, "max_job_min_bg_on_low"), 581 new MaxJobCounts( 582 5, "max_job_total_on_critical", 583 1, "max_job_max_bg_on_critical", 584 1, "max_job_min_bg_on_critical")); 585 586 final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_OFF = 587 new MaxJobCountsPerMemoryTrimLevel( 588 new MaxJobCounts( 589 10, "max_job_total_off_normal", 590 6, "max_job_max_bg_off_normal", 591 2, "max_job_min_bg_off_normal"), 592 new MaxJobCounts( 593 10, "max_job_total_off_moderate", 594 4, "max_job_max_bg_off_moderate", 595 2, "max_job_min_bg_off_moderate"), 596 new MaxJobCounts( 597 5, "max_job_total_off_low", 598 1, "max_job_max_bg_off_low", 599 1, "max_job_min_bg_off_low"), 600 new MaxJobCounts( 601 5, "max_job_total_off_critical", 602 1, "max_job_max_bg_off_critical", 603 1, "max_job_min_bg_off_critical")); 604 605 606 /** Wait for this long after screen off before increasing the job concurrency. */ 607 final KeyValueListParser.IntValue SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS = 608 new KeyValueListParser.IntValue( 609 "screen_off_job_concurrency_increase_delay_ms", 30_000); 610 611 /** 612 * The minimum backoff time to allow for linear backoff. 613 */ 614 long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME; 615 /** 616 * The minimum backoff time to allow for exponential backoff. 617 */ 618 long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME; 619 620 /** 621 * The fraction of a job's running window that must pass before we 622 * consider running it when the network is congested. 623 */ 624 public float CONN_CONGESTION_DELAY_FRAC = DEFAULT_CONN_CONGESTION_DELAY_FRAC; 625 /** 626 * The fraction of a prefetch job's running window that must pass before 627 * we consider matching it against a metered network. 628 */ 629 public float CONN_PREFETCH_RELAX_FRAC = DEFAULT_CONN_PREFETCH_RELAX_FRAC; 630 631 /** 632 * Whether to enable quota limits on APIs. 633 */ 634 public boolean ENABLE_API_QUOTAS = DEFAULT_ENABLE_API_QUOTAS; 635 /** 636 * The maximum number of schedule() calls an app can make in a set amount of time. 637 */ 638 public int API_QUOTA_SCHEDULE_COUNT = DEFAULT_API_QUOTA_SCHEDULE_COUNT; 639 /** 640 * The time window that {@link #API_QUOTA_SCHEDULE_COUNT} should be evaluated over. 641 */ 642 public long API_QUOTA_SCHEDULE_WINDOW_MS = DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS; 643 /** 644 * Whether to throw an exception when an app hits its schedule quota limit. 645 */ 646 public boolean API_QUOTA_SCHEDULE_THROW_EXCEPTION = 647 DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION; 648 /** 649 * Whether or not to return a failure result when an app hits its schedule quota limit. 650 */ 651 public boolean API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = 652 DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT; 653 654 private final KeyValueListParser mParser = new KeyValueListParser(','); 655 656 void updateConstantsLocked(String value) { 657 try { 658 mParser.setString(value); 659 } catch (Exception e) { 660 // Failed to parse the settings string, log this and move on 661 // with defaults. 662 Slog.e(TAG, "Bad jobscheduler settings", e); 663 } 664 665 MIN_READY_NON_ACTIVE_JOBS_COUNT = mParser.getInt( 666 KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT, 667 DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT); 668 MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = mParser.getLong( 669 KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS, 670 DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS); 671 HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR, 672 DEFAULT_HEAVY_USE_FACTOR); 673 MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR, 674 DEFAULT_MODERATE_USE_FACTOR); 675 676 MAX_JOB_COUNTS_SCREEN_ON.normal.parse(mParser); 677 MAX_JOB_COUNTS_SCREEN_ON.moderate.parse(mParser); 678 MAX_JOB_COUNTS_SCREEN_ON.low.parse(mParser); 679 MAX_JOB_COUNTS_SCREEN_ON.critical.parse(mParser); 680 681 MAX_JOB_COUNTS_SCREEN_OFF.normal.parse(mParser); 682 MAX_JOB_COUNTS_SCREEN_OFF.moderate.parse(mParser); 683 MAX_JOB_COUNTS_SCREEN_OFF.low.parse(mParser); 684 MAX_JOB_COUNTS_SCREEN_OFF.critical.parse(mParser); 685 686 SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.parse(mParser); 687 688 MIN_LINEAR_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_LINEAR_BACKOFF_TIME, 689 DEFAULT_MIN_LINEAR_BACKOFF_TIME); 690 MIN_EXP_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_EXP_BACKOFF_TIME, 691 DEFAULT_MIN_EXP_BACKOFF_TIME); 692 CONN_CONGESTION_DELAY_FRAC = mParser.getFloat(KEY_CONN_CONGESTION_DELAY_FRAC, 693 DEFAULT_CONN_CONGESTION_DELAY_FRAC); 694 CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC, 695 DEFAULT_CONN_PREFETCH_RELAX_FRAC); 696 697 ENABLE_API_QUOTAS = mParser.getBoolean(KEY_ENABLE_API_QUOTAS, 698 DEFAULT_ENABLE_API_QUOTAS); 699 // Set a minimum value on the quota limit so it's not so low that it interferes with 700 // legitimate use cases. 701 API_QUOTA_SCHEDULE_COUNT = Math.max(250, 702 mParser.getInt(KEY_API_QUOTA_SCHEDULE_COUNT, DEFAULT_API_QUOTA_SCHEDULE_COUNT)); 703 API_QUOTA_SCHEDULE_WINDOW_MS = mParser.getDurationMillis( 704 KEY_API_QUOTA_SCHEDULE_WINDOW_MS, DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS); 705 API_QUOTA_SCHEDULE_THROW_EXCEPTION = mParser.getBoolean( 706 KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION, 707 DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION); 708 API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = mParser.getBoolean( 709 KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT, 710 DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT); 711 } 712 713 void dump(IndentingPrintWriter pw) { 714 pw.println("Settings:"); 715 pw.increaseIndent(); 716 pw.printPair(KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT, 717 MIN_READY_NON_ACTIVE_JOBS_COUNT).println(); 718 pw.printPair(KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS, 719 MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS).println(); 720 pw.printPair(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println(); 721 pw.printPair(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println(); 722 723 MAX_JOB_COUNTS_SCREEN_ON.normal.dump(pw, ""); 724 MAX_JOB_COUNTS_SCREEN_ON.moderate.dump(pw, ""); 725 MAX_JOB_COUNTS_SCREEN_ON.low.dump(pw, ""); 726 MAX_JOB_COUNTS_SCREEN_ON.critical.dump(pw, ""); 727 728 MAX_JOB_COUNTS_SCREEN_OFF.normal.dump(pw, ""); 729 MAX_JOB_COUNTS_SCREEN_OFF.moderate.dump(pw, ""); 730 MAX_JOB_COUNTS_SCREEN_OFF.low.dump(pw, ""); 731 MAX_JOB_COUNTS_SCREEN_OFF.critical.dump(pw, ""); 732 733 SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.dump(pw, ""); 734 735 pw.printPair(KEY_MIN_LINEAR_BACKOFF_TIME, MIN_LINEAR_BACKOFF_TIME).println(); 736 pw.printPair(KEY_MIN_EXP_BACKOFF_TIME, MIN_EXP_BACKOFF_TIME).println(); 737 pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println(); 738 pw.printPair(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println(); 739 740 pw.printPair(KEY_ENABLE_API_QUOTAS, ENABLE_API_QUOTAS).println(); 741 pw.printPair(KEY_API_QUOTA_SCHEDULE_COUNT, API_QUOTA_SCHEDULE_COUNT).println(); 742 pw.printPair(KEY_API_QUOTA_SCHEDULE_WINDOW_MS, API_QUOTA_SCHEDULE_WINDOW_MS).println(); 743 pw.printPair(KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION, 744 API_QUOTA_SCHEDULE_THROW_EXCEPTION).println(); 745 pw.printPair(KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT, 746 API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT).println(); 747 748 pw.decreaseIndent(); 749 } 750 751 void dump(ProtoOutputStream proto) { 752 proto.write(ConstantsProto.MIN_READY_NON_ACTIVE_JOBS_COUNT, 753 MIN_READY_NON_ACTIVE_JOBS_COUNT); 754 proto.write(ConstantsProto.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS, 755 MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS); 756 proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR); 757 proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR); 758 759 MAX_JOB_COUNTS_SCREEN_ON.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_ON); 760 MAX_JOB_COUNTS_SCREEN_OFF.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_OFF); 761 762 SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.dumpProto(proto, 763 ConstantsProto.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS); 764 765 proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME); 766 proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME); 767 proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC); 768 proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC); 769 770 proto.write(ConstantsProto.ENABLE_API_QUOTAS, ENABLE_API_QUOTAS); 771 proto.write(ConstantsProto.API_QUOTA_SCHEDULE_COUNT, API_QUOTA_SCHEDULE_COUNT); 772 proto.write(ConstantsProto.API_QUOTA_SCHEDULE_WINDOW_MS, API_QUOTA_SCHEDULE_WINDOW_MS); 773 proto.write(ConstantsProto.API_QUOTA_SCHEDULE_THROW_EXCEPTION, 774 API_QUOTA_SCHEDULE_THROW_EXCEPTION); 775 proto.write(ConstantsProto.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT, 776 API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT); 777 } 778 } 779 780 final Constants mConstants; 781 final ConstantsObserver mConstantsObserver; 782 783 private static final Comparator<JobStatus> sPendingJobComparator = (o1, o2) -> { 784 // Jobs with an override state set (via adb) should be put first as tests/developers 785 // expect the jobs to run immediately. 786 if (o1.overrideState != o2.overrideState) { 787 // Higher override state (OVERRIDE_FULL) should be before lower state (OVERRIDE_SOFT) 788 return o2.overrideState - o1.overrideState; 789 } 790 if (o1.enqueueTime < o2.enqueueTime) { 791 return -1; 792 } 793 return o1.enqueueTime > o2.enqueueTime ? 1 : 0; 794 }; 795 796 static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) { 797 int where = Collections.binarySearch(array, newItem, comparator); 798 if (where < 0) { 799 where = ~where; 800 } 801 array.add(where, newItem); 802 } 803 804 /** 805 * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we 806 * still clean up. On reinstall the package will have a new uid. 807 */ 808 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 809 @Override 810 public void onReceive(Context context, Intent intent) { 811 final String action = intent.getAction(); 812 if (DEBUG) { 813 Slog.d(TAG, "Receieved: " + action); 814 } 815 final String pkgName = getPackageName(intent); 816 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1); 817 818 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) { 819 // Purge the app's jobs if the whole package was just disabled. When this is 820 // the case the component name will be a bare package name. 821 if (pkgName != null && pkgUid != -1) { 822 final String[] changedComponents = intent.getStringArrayExtra( 823 Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST); 824 if (changedComponents != null) { 825 for (String component : changedComponents) { 826 if (component.equals(pkgName)) { 827 if (DEBUG) { 828 Slog.d(TAG, "Package state change: " + pkgName); 829 } 830 try { 831 final int userId = UserHandle.getUserId(pkgUid); 832 IPackageManager pm = AppGlobals.getPackageManager(); 833 final int state = pm.getApplicationEnabledSetting(pkgName, userId); 834 if (state == COMPONENT_ENABLED_STATE_DISABLED 835 || state == COMPONENT_ENABLED_STATE_DISABLED_USER) { 836 if (DEBUG) { 837 Slog.d(TAG, "Removing jobs for package " + pkgName 838 + " in user " + userId); 839 } 840 cancelJobsForPackageAndUid(pkgName, pkgUid, 841 "app disabled"); 842 } 843 } catch (RemoteException|IllegalArgumentException e) { 844 /* 845 * IllegalArgumentException means that the package doesn't exist. 846 * This arises when PACKAGE_CHANGED broadcast delivery has lagged 847 * behind outright uninstall, so by the time we try to act it's gone. 848 * We don't need to act on this PACKAGE_CHANGED when this happens; 849 * we'll get a PACKAGE_REMOVED later and clean up then. 850 * 851 * RemoteException can't actually happen; the package manager is 852 * running in this same process. 853 */ 854 } 855 break; 856 } 857 } 858 if (DEBUG) { 859 Slog.d(TAG, "Something in " + pkgName 860 + " changed. Reevaluating controller states."); 861 } 862 synchronized (mLock) { 863 for (int c = mControllers.size() - 1; c >= 0; --c) { 864 mControllers.get(c).reevaluateStateLocked(pkgUid); 865 } 866 } 867 } 868 } else { 869 Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid); 870 } 871 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { 872 // If this is an outright uninstall rather than the first half of an 873 // app update sequence, cancel the jobs associated with the app. 874 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { 875 int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1); 876 if (DEBUG) { 877 Slog.d(TAG, "Removing jobs for uid: " + uidRemoved); 878 } 879 cancelJobsForPackageAndUid(pkgName, uidRemoved, "app uninstalled"); 880 synchronized (mLock) { 881 for (int c = 0; c < mControllers.size(); ++c) { 882 mControllers.get(c).onAppRemovedLocked(pkgName, pkgUid); 883 } 884 mDebuggableApps.remove(pkgName); 885 } 886 } 887 } else if (Intent.ACTION_USER_REMOVED.equals(action)) { 888 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); 889 if (DEBUG) { 890 Slog.d(TAG, "Removing jobs for user: " + userId); 891 } 892 cancelJobsForUser(userId); 893 synchronized (mLock) { 894 for (int c = 0; c < mControllers.size(); ++c) { 895 mControllers.get(c).onUserRemovedLocked(userId); 896 } 897 } 898 } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) { 899 // Has this package scheduled any jobs, such that we will take action 900 // if it were to be force-stopped? 901 if (pkgUid != -1) { 902 List<JobStatus> jobsForUid; 903 synchronized (mLock) { 904 jobsForUid = mJobs.getJobsByUid(pkgUid); 905 } 906 for (int i = jobsForUid.size() - 1; i >= 0; i--) { 907 if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) { 908 if (DEBUG) { 909 Slog.d(TAG, "Restart query: package " + pkgName + " at uid " 910 + pkgUid + " has jobs"); 911 } 912 setResultCode(Activity.RESULT_OK); 913 break; 914 } 915 } 916 } 917 } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) { 918 // possible force-stop 919 if (pkgUid != -1) { 920 if (DEBUG) { 921 Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid); 922 } 923 cancelJobsForPackageAndUid(pkgName, pkgUid, "app force stopped"); 924 } 925 } 926 } 927 }; 928 929 private String getPackageName(Intent intent) { 930 Uri uri = intent.getData(); 931 String pkg = uri != null ? uri.getSchemeSpecificPart() : null; 932 return pkg; 933 } 934 935 final private IUidObserver mUidObserver = new IUidObserver.Stub() { 936 @Override public void onUidStateChanged(int uid, int procState, long procStateSeq, 937 int capability) { 938 mHandler.obtainMessage(MSG_UID_STATE_CHANGED, uid, procState).sendToTarget(); 939 } 940 941 @Override public void onUidGone(int uid, boolean disabled) { 942 mHandler.obtainMessage(MSG_UID_GONE, uid, disabled ? 1 : 0).sendToTarget(); 943 } 944 945 @Override public void onUidActive(int uid) throws RemoteException { 946 mHandler.obtainMessage(MSG_UID_ACTIVE, uid, 0).sendToTarget(); 947 } 948 949 @Override public void onUidIdle(int uid, boolean disabled) { 950 mHandler.obtainMessage(MSG_UID_IDLE, uid, disabled ? 1 : 0).sendToTarget(); 951 } 952 953 @Override public void onUidCachedChanged(int uid, boolean cached) { 954 } 955 }; 956 957 public Context getTestableContext() { 958 return getContext(); 959 } 960 961 public Object getLock() { 962 return mLock; 963 } 964 965 public JobStore getJobStore() { 966 return mJobs; 967 } 968 969 public Constants getConstants() { 970 return mConstants; 971 } 972 973 public boolean isChainedAttributionEnabled() { 974 return WorkSource.isChainedBatteryAttributionEnabled(getContext()); 975 } 976 977 @Override 978 public void onStartUser(int userHandle) { 979 synchronized (mLock) { 980 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle); 981 } 982 // Let's kick any outstanding jobs for this user. 983 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 984 } 985 986 @Override 987 public void onUnlockUser(int userHandle) { 988 // Let's kick any outstanding jobs for this user. 989 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 990 } 991 992 @Override 993 public void onStopUser(int userHandle) { 994 synchronized (mLock) { 995 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle); 996 } 997 } 998 999 /** 1000 * Return whether an UID is active or idle. 1001 */ 1002 private boolean isUidActive(int uid) { 1003 return mAppStateTracker.isUidActiveSynced(uid); 1004 } 1005 1006 private final Predicate<Integer> mIsUidActivePredicate = this::isUidActive; 1007 1008 public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName, 1009 int userId, String tag) { 1010 final String servicePkg = job.getService().getPackageName(); 1011 if (job.isPersisted() && (packageName == null || packageName.equals(servicePkg))) { 1012 // Only limit schedule calls for persisted jobs scheduled by the app itself. 1013 final String pkg = 1014 packageName == null ? job.getService().getPackageName() : packageName; 1015 if (!mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG)) { 1016 if (mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_LOGGED)) { 1017 // Don't log too frequently 1018 Slog.wtf(TAG, userId + "-" + pkg + " has called schedule() too many times"); 1019 mQuotaTracker.noteEvent(userId, pkg, QUOTA_TRACKER_SCHEDULE_LOGGED); 1020 } 1021 mAppStandbyInternal.restrictApp( 1022 pkg, userId, UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY); 1023 if (mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION) { 1024 final boolean isDebuggable; 1025 synchronized (mLock) { 1026 if (!mDebuggableApps.containsKey(packageName)) { 1027 try { 1028 final ApplicationInfo appInfo = AppGlobals.getPackageManager() 1029 .getApplicationInfo(pkg, 0, userId); 1030 if (appInfo != null) { 1031 mDebuggableApps.put(packageName, 1032 (appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0); 1033 } else { 1034 return JobScheduler.RESULT_FAILURE; 1035 } 1036 } catch (RemoteException e) { 1037 throw new RuntimeException(e); 1038 } 1039 } 1040 isDebuggable = mDebuggableApps.get(packageName); 1041 } 1042 if (isDebuggable) { 1043 // Only throw the exception for debuggable apps. 1044 throw new LimitExceededException( 1045 "schedule()/enqueue() called more than " 1046 + mQuotaTracker.getLimit( 1047 QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED) 1048 + " times in the past " 1049 + mQuotaTracker.getWindowSizeMs( 1050 QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED) 1051 + "ms. See the documentation for more information."); 1052 } 1053 } 1054 if (mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT) { 1055 return JobScheduler.RESULT_FAILURE; 1056 } 1057 } 1058 mQuotaTracker.noteEvent(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG); 1059 } 1060 1061 try { 1062 if (ActivityManager.getService().isAppStartModeDisabled(uId, 1063 job.getService().getPackageName())) { 1064 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString() 1065 + " -- package not allowed to start"); 1066 return JobScheduler.RESULT_FAILURE; 1067 } 1068 } catch (RemoteException e) { 1069 } 1070 1071 synchronized (mLock) { 1072 final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId()); 1073 1074 if (work != null && toCancel != null) { 1075 // Fast path: we are adding work to an existing job, and the JobInfo is not 1076 // changing. We can just directly enqueue this work in to the job. 1077 if (toCancel.getJob().equals(job)) { 1078 1079 toCancel.enqueueWorkLocked(work); 1080 1081 // If any of work item is enqueued when the source is in the foreground, 1082 // exempt the entire job. 1083 toCancel.maybeAddForegroundExemption(mIsUidActivePredicate); 1084 1085 return JobScheduler.RESULT_SUCCESS; 1086 } 1087 } 1088 1089 JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag); 1090 1091 // Give exemption if the source is in the foreground just now. 1092 // Note if it's a sync job, this method is called on the handler so it's not exactly 1093 // the state when requestSync() was called, but that should be fine because of the 1094 // 1 minute foreground grace period. 1095 jobStatus.maybeAddForegroundExemption(mIsUidActivePredicate); 1096 1097 if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString()); 1098 // Jobs on behalf of others don't apply to the per-app job cap 1099 if (ENFORCE_MAX_JOBS && packageName == null) { 1100 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) { 1101 Slog.w(TAG, "Too many jobs for uid " + uId); 1102 throw new IllegalStateException("Apps may not schedule more than " 1103 + MAX_JOBS_PER_APP + " distinct jobs"); 1104 } 1105 } 1106 1107 // This may throw a SecurityException. 1108 jobStatus.prepareLocked(); 1109 1110 if (toCancel != null) { 1111 // Implicitly replaces the existing job record with the new instance 1112 cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app"); 1113 } else { 1114 startTrackingJobLocked(jobStatus, null); 1115 } 1116 1117 if (work != null) { 1118 // If work has been supplied, enqueue it into the new job. 1119 jobStatus.enqueueWorkLocked(work); 1120 } 1121 1122 FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED, 1123 uId, null, jobStatus.getBatteryName(), 1124 FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED, 1125 JobProtoEnums.STOP_REASON_CANCELLED, jobStatus.getStandbyBucket(), 1126 jobStatus.getJobId(), 1127 jobStatus.hasChargingConstraint(), 1128 jobStatus.hasBatteryNotLowConstraint(), 1129 jobStatus.hasStorageNotLowConstraint(), 1130 jobStatus.hasTimingDelayConstraint(), 1131 jobStatus.hasDeadlineConstraint(), 1132 jobStatus.hasIdleConstraint(), 1133 jobStatus.hasConnectivityConstraint(), 1134 jobStatus.hasContentTriggerConstraint()); 1135 1136 // If the job is immediately ready to run, then we can just immediately 1137 // put it in the pending list and try to schedule it. This is especially 1138 // important for jobs with a 0 deadline constraint, since they will happen a fair 1139 // amount, we want to handle them as quickly as possible, and semantically we want to 1140 // make sure we have started holding the wake lock for the job before returning to 1141 // the caller. 1142 // If the job is not yet ready to run, there is nothing more to do -- we are 1143 // now just waiting for one of its controllers to change state and schedule 1144 // the job appropriately. 1145 if (isReadyToBeExecutedLocked(jobStatus)) { 1146 // This is a new job, we can just immediately put it on the pending 1147 // list and try to run it. 1148 mJobPackageTracker.notePending(jobStatus); 1149 addOrderedItem(mPendingJobs, jobStatus, sPendingJobComparator); 1150 maybeRunPendingJobsLocked(); 1151 } else { 1152 evaluateControllerStatesLocked(jobStatus); 1153 } 1154 } 1155 return JobScheduler.RESULT_SUCCESS; 1156 } 1157 1158 public List<JobInfo> getPendingJobs(int uid) { 1159 synchronized (mLock) { 1160 List<JobStatus> jobs = mJobs.getJobsByUid(uid); 1161 ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size()); 1162 for (int i = jobs.size() - 1; i >= 0; i--) { 1163 JobStatus job = jobs.get(i); 1164 outList.add(job.getJob()); 1165 } 1166 return outList; 1167 } 1168 } 1169 1170 public JobInfo getPendingJob(int uid, int jobId) { 1171 synchronized (mLock) { 1172 List<JobStatus> jobs = mJobs.getJobsByUid(uid); 1173 for (int i = jobs.size() - 1; i >= 0; i--) { 1174 JobStatus job = jobs.get(i); 1175 if (job.getJobId() == jobId) { 1176 return job.getJob(); 1177 } 1178 } 1179 return null; 1180 } 1181 } 1182 1183 void cancelJobsForUser(int userHandle) { 1184 synchronized (mLock) { 1185 final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle); 1186 for (int i=0; i<jobsForUser.size(); i++) { 1187 JobStatus toRemove = jobsForUser.get(i); 1188 cancelJobImplLocked(toRemove, null, "user removed"); 1189 } 1190 } 1191 } 1192 1193 private void cancelJobsForNonExistentUsers() { 1194 UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class); 1195 synchronized (mLock) { 1196 mJobs.removeJobsOfNonUsers(umi.getUserIds()); 1197 } 1198 } 1199 1200 void cancelJobsForPackageAndUid(String pkgName, int uid, String reason) { 1201 if ("android".equals(pkgName)) { 1202 Slog.wtfStack(TAG, "Can't cancel all jobs for system package"); 1203 return; 1204 } 1205 synchronized (mLock) { 1206 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid); 1207 for (int i = jobsForUid.size() - 1; i >= 0; i--) { 1208 final JobStatus job = jobsForUid.get(i); 1209 if (job.getSourcePackageName().equals(pkgName)) { 1210 cancelJobImplLocked(job, null, reason); 1211 } 1212 } 1213 } 1214 } 1215 1216 /** 1217 * Entry point from client to cancel all jobs originating from their uid. 1218 * This will remove the job from the master list, and cancel the job if it was staged for 1219 * execution or being executed. 1220 * @param uid Uid to check against for removal of a job. 1221 * 1222 */ 1223 public boolean cancelJobsForUid(int uid, String reason) { 1224 if (uid == Process.SYSTEM_UID) { 1225 Slog.wtfStack(TAG, "Can't cancel all jobs for system uid"); 1226 return false; 1227 } 1228 1229 boolean jobsCanceled = false; 1230 synchronized (mLock) { 1231 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid); 1232 for (int i=0; i<jobsForUid.size(); i++) { 1233 JobStatus toRemove = jobsForUid.get(i); 1234 cancelJobImplLocked(toRemove, null, reason); 1235 jobsCanceled = true; 1236 } 1237 } 1238 return jobsCanceled; 1239 } 1240 1241 /** 1242 * Entry point from client to cancel the job corresponding to the jobId provided. 1243 * This will remove the job from the master list, and cancel the job if it was staged for 1244 * execution or being executed. 1245 * @param uid Uid of the calling client. 1246 * @param jobId Id of the job, provided at schedule-time. 1247 */ 1248 public boolean cancelJob(int uid, int jobId, int callingUid) { 1249 JobStatus toCancel; 1250 synchronized (mLock) { 1251 toCancel = mJobs.getJobByUidAndJobId(uid, jobId); 1252 if (toCancel != null) { 1253 cancelJobImplLocked(toCancel, null, 1254 "cancel() called by app, callingUid=" + callingUid 1255 + " uid=" + uid + " jobId=" + jobId); 1256 } 1257 return (toCancel != null); 1258 } 1259 } 1260 1261 /** 1262 * Cancel the given job, stopping it if it's currently executing. If {@code incomingJob} 1263 * is null, the cancelled job is removed outright from the system. If 1264 * {@code incomingJob} is non-null, it replaces {@code cancelled} in the store of 1265 * currently scheduled jobs. 1266 */ 1267 private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) { 1268 if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString()); 1269 cancelled.unprepareLocked(); 1270 stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */); 1271 // Remove from pending queue. 1272 if (mPendingJobs.remove(cancelled)) { 1273 mJobPackageTracker.noteNonpending(cancelled); 1274 } 1275 // Cancel if running. 1276 stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason); 1277 // If this is a replacement, bring in the new version of the job 1278 if (incomingJob != null) { 1279 if (DEBUG) Slog.i(TAG, "Tracking replacement job " + incomingJob.toShortString()); 1280 startTrackingJobLocked(incomingJob, cancelled); 1281 } 1282 reportActiveLocked(); 1283 } 1284 1285 void updateUidState(int uid, int procState) { 1286 synchronized (mLock) { 1287 if (procState == ActivityManager.PROCESS_STATE_TOP) { 1288 // Only use this if we are exactly the top app. All others can live 1289 // with just the foreground priority. This means that persistent processes 1290 // can never be the top app priority... that is fine. 1291 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP); 1292 } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { 1293 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_SERVICE); 1294 } else if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { 1295 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_BOUND_FOREGROUND_SERVICE); 1296 } else { 1297 mUidPriorityOverride.delete(uid); 1298 } 1299 } 1300 } 1301 1302 @Override 1303 public void onDeviceIdleStateChanged(boolean deviceIdle) { 1304 synchronized (mLock) { 1305 if (DEBUG) { 1306 Slog.d(TAG, "Doze state changed: " + deviceIdle); 1307 } 1308 if (deviceIdle) { 1309 // When becoming idle, make sure no jobs are actively running, 1310 // except those using the idle exemption flag. 1311 for (int i=0; i<mActiveServices.size(); i++) { 1312 JobServiceContext jsc = mActiveServices.get(i); 1313 final JobStatus executing = jsc.getRunningJobLocked(); 1314 if (executing != null 1315 && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) { 1316 jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE, 1317 "cancelled due to doze"); 1318 } 1319 } 1320 } else { 1321 // When coming out of idle, allow thing to start back up. 1322 if (mReadyToRock) { 1323 if (mLocalDeviceIdleController != null) { 1324 if (!mReportedActive) { 1325 mReportedActive = true; 1326 mLocalDeviceIdleController.setJobsActive(true); 1327 } 1328 } 1329 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 1330 } 1331 } 1332 } 1333 } 1334 1335 @Override 1336 public void onRestrictedBucketChanged(List<JobStatus> jobs) { 1337 final int len = jobs.size(); 1338 if (len == 0) { 1339 Slog.wtf(TAG, "onRestrictedBucketChanged called with no jobs"); 1340 return; 1341 } 1342 synchronized (mLock) { 1343 for (int i = 0; i < len; ++i) { 1344 JobStatus js = jobs.get(i); 1345 for (int j = mRestrictiveControllers.size() - 1; j >= 0; --j) { 1346 // Effective standby bucket can change after this in some situations so use 1347 // the real bucket so that the job is tracked by the controllers. 1348 if (js.getStandbyBucket() == RESTRICTED_INDEX) { 1349 mRestrictiveControllers.get(j).startTrackingRestrictedJobLocked(js); 1350 } else { 1351 mRestrictiveControllers.get(j).stopTrackingRestrictedJobLocked(js); 1352 } 1353 } 1354 } 1355 } 1356 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 1357 } 1358 1359 void reportActiveLocked() { 1360 // active is true if pending queue contains jobs OR some job is running. 1361 boolean active = mPendingJobs.size() > 0; 1362 if (mPendingJobs.size() <= 0) { 1363 for (int i=0; i<mActiveServices.size(); i++) { 1364 final JobServiceContext jsc = mActiveServices.get(i); 1365 final JobStatus job = jsc.getRunningJobLocked(); 1366 if (job != null 1367 && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0 1368 && !job.dozeWhitelisted 1369 && !job.uidActive) { 1370 // We will report active if we have a job running and it is not an exception 1371 // due to being in the foreground or whitelisted. 1372 active = true; 1373 break; 1374 } 1375 } 1376 } 1377 1378 if (mReportedActive != active) { 1379 mReportedActive = active; 1380 if (mLocalDeviceIdleController != null) { 1381 mLocalDeviceIdleController.setJobsActive(active); 1382 } 1383 } 1384 } 1385 1386 void reportAppUsage(String packageName, int userId) { 1387 // This app just transitioned into interactive use or near equivalent, so we should 1388 // take a look at its job state for feedback purposes. 1389 } 1390 1391 /** 1392 * Initializes the system service. 1393 * <p> 1394 * Subclasses must define a single argument constructor that accepts the context 1395 * and passes it to super. 1396 * </p> 1397 * 1398 * @param context The system server context. 1399 */ 1400 public JobSchedulerService(Context context) { 1401 super(context); 1402 1403 mLocalPM = LocalServices.getService(PackageManagerInternal.class); 1404 mActivityManagerInternal = Objects.requireNonNull( 1405 LocalServices.getService(ActivityManagerInternal.class)); 1406 1407 mHandler = new JobHandler(context.getMainLooper()); 1408 mConstants = new Constants(); 1409 mConstantsObserver = new ConstantsObserver(mHandler); 1410 mJobSchedulerStub = new JobSchedulerStub(); 1411 1412 mConcurrencyManager = new JobConcurrencyManager(this); 1413 1414 // Set up the app standby bucketing tracker 1415 mStandbyTracker = new StandbyTracker(); 1416 mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class); 1417 mQuotaTracker = new CountQuotaTracker(context, QUOTA_CATEGORIZER); 1418 mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED, 1419 mConstants.API_QUOTA_SCHEDULE_COUNT, 1420 mConstants.API_QUOTA_SCHEDULE_WINDOW_MS); 1421 // Log at most once per minute. 1422 mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED, 1, 60_000); 1423 1424 mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class); 1425 mAppStandbyInternal.addListener(mStandbyTracker); 1426 1427 // The job store needs to call back 1428 publishLocalService(JobSchedulerInternal.class, new LocalService()); 1429 1430 // Initialize the job store and set up any persisted jobs 1431 mJobs = JobStore.initAndGet(this); 1432 1433 // Create the controllers. 1434 mControllers = new ArrayList<StateController>(); 1435 final ConnectivityController connectivityController = new ConnectivityController(this); 1436 mControllers.add(connectivityController); 1437 mControllers.add(new TimeController(this)); 1438 final IdleController idleController = new IdleController(this); 1439 mControllers.add(idleController); 1440 mBatteryController = new BatteryController(this); 1441 mControllers.add(mBatteryController); 1442 mStorageController = new StorageController(this); 1443 mControllers.add(mStorageController); 1444 mControllers.add(new BackgroundJobsController(this)); 1445 mControllers.add(new ContentObserverController(this)); 1446 mDeviceIdleJobsController = new DeviceIdleJobsController(this); 1447 mControllers.add(mDeviceIdleJobsController); 1448 mQuotaController = new QuotaController(this); 1449 mControllers.add(mQuotaController); 1450 1451 mRestrictiveControllers = new ArrayList<>(); 1452 mRestrictiveControllers.add(mBatteryController); 1453 mRestrictiveControllers.add(connectivityController); 1454 mRestrictiveControllers.add(idleController); 1455 1456 // Create restrictions 1457 mJobRestrictions = new ArrayList<>(); 1458 mJobRestrictions.add(new ThermalStatusRestriction(this)); 1459 1460 mSystemGalleryPackage = Objects.requireNonNull( 1461 context.getString(R.string.config_systemGallery)); 1462 1463 // If the job store determined that it can't yet reschedule persisted jobs, 1464 // we need to start watching the clock. 1465 if (!mJobs.jobTimesInflatedValid()) { 1466 Slog.w(TAG, "!!! RTC not yet good; tracking time updates for job scheduling"); 1467 context.registerReceiver(mTimeSetReceiver, new IntentFilter(Intent.ACTION_TIME_CHANGED)); 1468 } 1469 } 1470 1471 private final BroadcastReceiver mTimeSetReceiver = new BroadcastReceiver() { 1472 @Override 1473 public void onReceive(Context context, Intent intent) { 1474 if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) { 1475 // When we reach clock sanity, recalculate the temporal windows 1476 // of all affected jobs. 1477 if (mJobs.clockNowValidToInflate(sSystemClock.millis())) { 1478 Slog.i(TAG, "RTC now valid; recalculating persisted job windows"); 1479 1480 // We've done our job now, so stop watching the time. 1481 context.unregisterReceiver(this); 1482 1483 // And kick off the work to update the affected jobs, using a secondary 1484 // thread instead of chugging away here on the main looper thread. 1485 FgThread.getHandler().post(mJobTimeUpdater); 1486 } 1487 } 1488 } 1489 }; 1490 1491 private final Runnable mJobTimeUpdater = () -> { 1492 final ArrayList<JobStatus> toRemove = new ArrayList<>(); 1493 final ArrayList<JobStatus> toAdd = new ArrayList<>(); 1494 synchronized (mLock) { 1495 // Note: we intentionally both look up the existing affected jobs and replace them 1496 // with recalculated ones inside the same lock lifetime. 1497 getJobStore().getRtcCorrectedJobsLocked(toAdd, toRemove); 1498 1499 // Now, at each position [i], we have both the existing JobStatus 1500 // and the one that replaces it. 1501 final int N = toAdd.size(); 1502 for (int i = 0; i < N; i++) { 1503 final JobStatus oldJob = toRemove.get(i); 1504 final JobStatus newJob = toAdd.get(i); 1505 if (DEBUG) { 1506 Slog.v(TAG, " replacing " + oldJob + " with " + newJob); 1507 } 1508 cancelJobImplLocked(oldJob, newJob, "deferred rtc calculation"); 1509 } 1510 } 1511 }; 1512 1513 @Override 1514 public void onStart() { 1515 publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub); 1516 } 1517 1518 @Override 1519 public void onBootPhase(int phase) { 1520 if (PHASE_SYSTEM_SERVICES_READY == phase) { 1521 mConstantsObserver.start(getContext().getContentResolver()); 1522 for (StateController controller : mControllers) { 1523 controller.onSystemServicesReady(); 1524 } 1525 1526 mAppStateTracker = Objects.requireNonNull( 1527 LocalServices.getService(AppStateTracker.class)); 1528 1529 // Register br for package removals and user removals. 1530 final IntentFilter filter = new IntentFilter(); 1531 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 1532 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 1533 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 1534 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); 1535 filter.addDataScheme("package"); 1536 getContext().registerReceiverAsUser( 1537 mBroadcastReceiver, UserHandle.ALL, filter, null, null); 1538 final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED); 1539 getContext().registerReceiverAsUser( 1540 mBroadcastReceiver, UserHandle.ALL, userFilter, null, null); 1541 try { 1542 ActivityManager.getService().registerUidObserver(mUidObserver, 1543 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE 1544 | ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_ACTIVE, 1545 ActivityManager.PROCESS_STATE_UNKNOWN, null); 1546 } catch (RemoteException e) { 1547 // ignored; both services live in system_server 1548 } 1549 1550 mConcurrencyManager.onSystemReady(); 1551 1552 // Remove any jobs that are not associated with any of the current users. 1553 cancelJobsForNonExistentUsers(); 1554 1555 for (int i = mJobRestrictions.size() - 1; i >= 0; i--) { 1556 mJobRestrictions.get(i).onSystemServicesReady(); 1557 } 1558 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { 1559 synchronized (mLock) { 1560 // Let's go! 1561 mReadyToRock = true; 1562 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService( 1563 BatteryStats.SERVICE_NAME)); 1564 mLocalDeviceIdleController = 1565 LocalServices.getService(DeviceIdleInternal.class); 1566 // Create the "runners". 1567 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) { 1568 mActiveServices.add( 1569 new JobServiceContext(this, mBatteryStats, mJobPackageTracker, 1570 getContext().getMainLooper())); 1571 } 1572 // Attach jobs to their controllers. 1573 mJobs.forEachJob((job) -> { 1574 for (int controller = 0; controller < mControllers.size(); controller++) { 1575 final StateController sc = mControllers.get(controller); 1576 sc.maybeStartTrackingJobLocked(job, null); 1577 } 1578 }); 1579 // GO GO GO! 1580 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 1581 } 1582 } 1583 } 1584 1585 /** 1586 * Called when we have a job status object that we need to insert in our 1587 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know 1588 * about. 1589 */ 1590 private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { 1591 if (!jobStatus.isPreparedLocked()) { 1592 Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus); 1593 } 1594 jobStatus.enqueueTime = sElapsedRealtimeClock.millis(); 1595 final boolean update = mJobs.add(jobStatus); 1596 if (mReadyToRock) { 1597 for (int i = 0; i < mControllers.size(); i++) { 1598 StateController controller = mControllers.get(i); 1599 if (update) { 1600 controller.maybeStopTrackingJobLocked(jobStatus, null, true); 1601 } 1602 controller.maybeStartTrackingJobLocked(jobStatus, lastJob); 1603 } 1604 } 1605 } 1606 1607 /** 1608 * Called when we want to remove a JobStatus object that we've finished executing. 1609 * @return true if the job was removed. 1610 */ 1611 private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, 1612 boolean removeFromPersisted) { 1613 // Deal with any remaining work items in the old job. 1614 jobStatus.stopTrackingJobLocked(incomingJob); 1615 1616 // Remove from store as well as controllers. 1617 final boolean removed = mJobs.remove(jobStatus, removeFromPersisted); 1618 if (removed && mReadyToRock) { 1619 for (int i=0; i<mControllers.size(); i++) { 1620 StateController controller = mControllers.get(i); 1621 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false); 1622 } 1623 } 1624 return removed; 1625 } 1626 1627 private boolean stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason) { 1628 for (int i=0; i<mActiveServices.size(); i++) { 1629 JobServiceContext jsc = mActiveServices.get(i); 1630 final JobStatus executing = jsc.getRunningJobLocked(); 1631 if (executing != null && executing.matches(job.getUid(), job.getJobId())) { 1632 jsc.cancelExecutingJobLocked(reason, debugReason); 1633 return true; 1634 } 1635 } 1636 return false; 1637 } 1638 1639 /** 1640 * @param job JobStatus we are querying against. 1641 * @return Whether or not the job represented by the status object is currently being run or 1642 * is pending. 1643 */ 1644 private boolean isCurrentlyActiveLocked(JobStatus job) { 1645 for (int i=0; i<mActiveServices.size(); i++) { 1646 JobServiceContext serviceContext = mActiveServices.get(i); 1647 final JobStatus running = serviceContext.getRunningJobLocked(); 1648 if (running != null && running.matches(job.getUid(), job.getJobId())) { 1649 return true; 1650 } 1651 } 1652 return false; 1653 } 1654 1655 void noteJobsPending(List<JobStatus> jobs) { 1656 for (int i = jobs.size() - 1; i >= 0; i--) { 1657 JobStatus job = jobs.get(i); 1658 mJobPackageTracker.notePending(job); 1659 } 1660 } 1661 1662 void noteJobsNonpending(List<JobStatus> jobs) { 1663 for (int i = jobs.size() - 1; i >= 0; i--) { 1664 JobStatus job = jobs.get(i); 1665 mJobPackageTracker.noteNonpending(job); 1666 } 1667 } 1668 1669 /** 1670 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to 1671 * specify an override deadline on a failed job (the failed job will run even though it's not 1672 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any 1673 * ready job with {@link JobStatus#getNumFailures()} > 0 will be executed. 1674 * 1675 * @param failureToReschedule Provided job status that we will reschedule. 1676 * @return A newly instantiated JobStatus with the same constraints as the last job except 1677 * with adjusted timing constraints. 1678 * 1679 * @see #maybeQueueReadyJobsForExecutionLocked 1680 */ 1681 @VisibleForTesting 1682 JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) { 1683 final long elapsedNowMillis = sElapsedRealtimeClock.millis(); 1684 final JobInfo job = failureToReschedule.getJob(); 1685 1686 final long initialBackoffMillis = job.getInitialBackoffMillis(); 1687 final int backoffAttempts = failureToReschedule.getNumFailures() + 1; 1688 long delayMillis; 1689 1690 switch (job.getBackoffPolicy()) { 1691 case JobInfo.BACKOFF_POLICY_LINEAR: { 1692 long backoff = initialBackoffMillis; 1693 if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME) { 1694 backoff = mConstants.MIN_LINEAR_BACKOFF_TIME; 1695 } 1696 delayMillis = backoff * backoffAttempts; 1697 } break; 1698 default: 1699 if (DEBUG) { 1700 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential."); 1701 } 1702 case JobInfo.BACKOFF_POLICY_EXPONENTIAL: { 1703 long backoff = initialBackoffMillis; 1704 if (backoff < mConstants.MIN_EXP_BACKOFF_TIME) { 1705 backoff = mConstants.MIN_EXP_BACKOFF_TIME; 1706 } 1707 delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1); 1708 } break; 1709 } 1710 delayMillis = 1711 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS); 1712 JobStatus newJob = new JobStatus(failureToReschedule, 1713 elapsedNowMillis + delayMillis, 1714 JobStatus.NO_LATEST_RUNTIME, backoffAttempts, 1715 failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis()); 1716 if (job.isPeriodic()) { 1717 newJob.setOriginalLatestRunTimeElapsed( 1718 failureToReschedule.getOriginalLatestRunTimeElapsed()); 1719 } 1720 for (int ic=0; ic<mControllers.size(); ic++) { 1721 StateController controller = mControllers.get(ic); 1722 controller.rescheduleForFailureLocked(newJob, failureToReschedule); 1723 } 1724 return newJob; 1725 } 1726 1727 /** 1728 * Maximum time buffer in which JobScheduler will try to optimize periodic job scheduling. This 1729 * does not cause a job's period to be larger than requested (eg: if the requested period is 1730 * shorter than this buffer). This is used to put a limit on when JobScheduler will intervene 1731 * and try to optimize scheduling if the current job finished less than this amount of time to 1732 * the start of the next period 1733 */ 1734 private static final long PERIODIC_JOB_WINDOW_BUFFER = 30 * MINUTE_IN_MILLIS; 1735 1736 /** The maximum period a periodic job can have. Anything higher will be clamped down to this. */ 1737 public static final long MAX_ALLOWED_PERIOD_MS = 365 * 24 * 60 * 60 * 1000L; 1738 1739 /** 1740 * Called after a periodic has executed so we can reschedule it. We take the last execution 1741 * time of the job to be the time of completion (i.e. the time at which this function is 1742 * called). 1743 * <p>This could be inaccurate b/c the job can run for as long as 1744 * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead 1745 * to underscheduling at least, rather than if we had taken the last execution time to be the 1746 * start of the execution. 1747 * 1748 * @return A new job representing the execution criteria for this instantiation of the 1749 * recurring job. 1750 */ 1751 @VisibleForTesting 1752 JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) { 1753 final long elapsedNow = sElapsedRealtimeClock.millis(); 1754 final long newLatestRuntimeElapsed; 1755 // Make sure period is in the interval [min_possible_period, max_possible_period]. 1756 final long period = Math.max(JobInfo.getMinPeriodMillis(), 1757 Math.min(MAX_ALLOWED_PERIOD_MS, periodicToReschedule.getJob().getIntervalMillis())); 1758 // Make sure flex is in the interval [min_possible_flex, period]. 1759 final long flex = Math.max(JobInfo.getMinFlexMillis(), 1760 Math.min(period, periodicToReschedule.getJob().getFlexMillis())); 1761 long rescheduleBuffer = 0; 1762 1763 long olrte = periodicToReschedule.getOriginalLatestRunTimeElapsed(); 1764 if (olrte < 0 || olrte == JobStatus.NO_LATEST_RUNTIME) { 1765 Slog.wtf(TAG, "Invalid periodic job original latest run time: " + olrte); 1766 olrte = elapsedNow; 1767 } 1768 final long latestRunTimeElapsed = olrte; 1769 1770 final long diffMs = Math.abs(elapsedNow - latestRunTimeElapsed); 1771 if (elapsedNow > latestRunTimeElapsed) { 1772 // The job ran past its expected run window. Have it count towards the current window 1773 // and schedule a new job for the next window. 1774 if (DEBUG) { 1775 Slog.i(TAG, "Periodic job ran after its intended window."); 1776 } 1777 long numSkippedWindows = (diffMs / period) + 1; // +1 to include original window 1778 if (period != flex && diffMs > Math.min(PERIODIC_JOB_WINDOW_BUFFER, 1779 (period - flex) / 2)) { 1780 if (DEBUG) { 1781 Slog.d(TAG, "Custom flex job ran too close to next window."); 1782 } 1783 // For custom flex periods, if the job was run too close to the next window, 1784 // skip the next window and schedule for the following one. 1785 numSkippedWindows += 1; 1786 } 1787 newLatestRuntimeElapsed = latestRunTimeElapsed + (period * numSkippedWindows); 1788 } else { 1789 newLatestRuntimeElapsed = latestRunTimeElapsed + period; 1790 if (diffMs < PERIODIC_JOB_WINDOW_BUFFER && diffMs < period / 6) { 1791 // Add a little buffer to the start of the next window so the job doesn't run 1792 // too soon after this completed one. 1793 rescheduleBuffer = Math.min(PERIODIC_JOB_WINDOW_BUFFER, period / 6 - diffMs); 1794 } 1795 } 1796 1797 if (newLatestRuntimeElapsed < elapsedNow) { 1798 Slog.wtf(TAG, "Rescheduling calculated latest runtime in the past: " 1799 + newLatestRuntimeElapsed); 1800 return new JobStatus(periodicToReschedule, 1801 elapsedNow + period - flex, elapsedNow + period, 1802 0 /* backoffAttempt */, 1803 sSystemClock.millis() /* lastSuccessfulRunTime */, 1804 periodicToReschedule.getLastFailedRunTime()); 1805 } 1806 1807 final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed 1808 - Math.min(flex, period - rescheduleBuffer); 1809 1810 if (DEBUG) { 1811 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" + 1812 newEarliestRunTimeElapsed / 1000 + ", " + newLatestRuntimeElapsed / 1000 1813 + "]s"); 1814 } 1815 return new JobStatus(periodicToReschedule, 1816 newEarliestRunTimeElapsed, newLatestRuntimeElapsed, 1817 0 /* backoffAttempt */, 1818 sSystemClock.millis() /* lastSuccessfulRunTime */, 1819 periodicToReschedule.getLastFailedRunTime()); 1820 } 1821 1822 // JobCompletedListener implementations. 1823 1824 /** 1825 * A job just finished executing. We fetch the 1826 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on 1827 * whether we want to reschedule we re-add it to the controllers. 1828 * @param jobStatus Completed job. 1829 * @param needsReschedule Whether the implementing class should reschedule this job. 1830 */ 1831 @Override 1832 public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) { 1833 if (DEBUG) { 1834 Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule); 1835 } 1836 1837 // If the job wants to be rescheduled, we first need to make the next upcoming 1838 // job so we can transfer any appropriate state over from the previous job when 1839 // we stop it. 1840 final JobStatus rescheduledJob = needsReschedule 1841 ? getRescheduleJobForFailureLocked(jobStatus) : null; 1842 1843 // Do not write back immediately if this is a periodic job. The job may get lost if system 1844 // shuts down before it is added back. 1845 if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) { 1846 if (DEBUG) { 1847 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?"); 1848 } 1849 // We still want to check for jobs to execute, because this job may have 1850 // scheduled a new job under the same job id, and now we can run it. 1851 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget(); 1852 return; 1853 } 1854 1855 if (rescheduledJob != null) { 1856 try { 1857 rescheduledJob.prepareLocked(); 1858 } catch (SecurityException e) { 1859 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob); 1860 } 1861 startTrackingJobLocked(rescheduledJob, jobStatus); 1862 } else if (jobStatus.getJob().isPeriodic()) { 1863 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus); 1864 try { 1865 rescheduledPeriodic.prepareLocked(); 1866 } catch (SecurityException e) { 1867 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic); 1868 } 1869 startTrackingJobLocked(rescheduledPeriodic, jobStatus); 1870 } 1871 jobStatus.unprepareLocked(); 1872 reportActiveLocked(); 1873 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget(); 1874 } 1875 1876 // StateChangedListener implementations. 1877 1878 /** 1879 * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that 1880 * some controller's state has changed, so as to run through the list of jobs and start/stop 1881 * any that are eligible. 1882 */ 1883 @Override 1884 public void onControllerStateChanged() { 1885 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 1886 } 1887 1888 @Override 1889 public void onRunJobNow(JobStatus jobStatus) { 1890 mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget(); 1891 } 1892 1893 final private class JobHandler extends Handler { 1894 1895 public JobHandler(Looper looper) { 1896 super(looper); 1897 } 1898 1899 @Override 1900 public void handleMessage(Message message) { 1901 synchronized (mLock) { 1902 if (!mReadyToRock) { 1903 return; 1904 } 1905 switch (message.what) { 1906 case MSG_JOB_EXPIRED: { 1907 JobStatus runNow = (JobStatus) message.obj; 1908 // runNow can be null, which is a controller's way of indicating that its 1909 // state is such that all ready jobs should be run immediately. 1910 if (runNow != null && isReadyToBeExecutedLocked(runNow)) { 1911 mJobPackageTracker.notePending(runNow); 1912 addOrderedItem(mPendingJobs, runNow, sPendingJobComparator); 1913 } else { 1914 queueReadyJobsForExecutionLocked(); 1915 } 1916 } break; 1917 case MSG_CHECK_JOB: 1918 if (DEBUG) { 1919 Slog.d(TAG, "MSG_CHECK_JOB"); 1920 } 1921 removeMessages(MSG_CHECK_JOB); 1922 if (mReportedActive) { 1923 // if jobs are currently being run, queue all ready jobs for execution. 1924 queueReadyJobsForExecutionLocked(); 1925 } else { 1926 // Check the list of jobs and run some of them if we feel inclined. 1927 maybeQueueReadyJobsForExecutionLocked(); 1928 } 1929 break; 1930 case MSG_CHECK_JOB_GREEDY: 1931 if (DEBUG) { 1932 Slog.d(TAG, "MSG_CHECK_JOB_GREEDY"); 1933 } 1934 queueReadyJobsForExecutionLocked(); 1935 break; 1936 case MSG_STOP_JOB: 1937 cancelJobImplLocked((JobStatus) message.obj, null, 1938 "app no longer allowed to run"); 1939 break; 1940 1941 case MSG_UID_STATE_CHANGED: { 1942 final int uid = message.arg1; 1943 final int procState = message.arg2; 1944 updateUidState(uid, procState); 1945 break; 1946 } 1947 case MSG_UID_GONE: { 1948 final int uid = message.arg1; 1949 final boolean disabled = message.arg2 != 0; 1950 updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); 1951 if (disabled) { 1952 cancelJobsForUid(uid, "uid gone"); 1953 } 1954 synchronized (mLock) { 1955 mDeviceIdleJobsController.setUidActiveLocked(uid, false); 1956 } 1957 break; 1958 } 1959 case MSG_UID_ACTIVE: { 1960 final int uid = message.arg1; 1961 synchronized (mLock) { 1962 mDeviceIdleJobsController.setUidActiveLocked(uid, true); 1963 } 1964 break; 1965 } 1966 case MSG_UID_IDLE: { 1967 final int uid = message.arg1; 1968 final boolean disabled = message.arg2 != 0; 1969 if (disabled) { 1970 cancelJobsForUid(uid, "app uid idle"); 1971 } 1972 synchronized (mLock) { 1973 mDeviceIdleJobsController.setUidActiveLocked(uid, false); 1974 } 1975 break; 1976 } 1977 1978 } 1979 maybeRunPendingJobsLocked(); 1980 // Don't remove JOB_EXPIRED in case one came along while processing the queue. 1981 } 1982 } 1983 } 1984 1985 /** 1986 * Check if a job is restricted by any of the declared {@link JobRestriction}s. 1987 * Note, that the jobs with {@link JobInfo#PRIORITY_FOREGROUND_APP} priority or higher may not 1988 * be restricted, thus we won't even perform the check, but simply return null early. 1989 * 1990 * @param job to be checked 1991 * @return the first {@link JobRestriction} restricting the given job that has been found; null 1992 * - if passes all the restrictions or has priority {@link JobInfo#PRIORITY_FOREGROUND_APP} 1993 * or higher. 1994 */ 1995 private JobRestriction checkIfRestricted(JobStatus job) { 1996 if (evaluateJobPriorityLocked(job) >= JobInfo.PRIORITY_FOREGROUND_APP) { 1997 // Jobs with PRIORITY_FOREGROUND_APP or higher should not be restricted 1998 return null; 1999 } 2000 for (int i = mJobRestrictions.size() - 1; i >= 0; i--) { 2001 final JobRestriction restriction = mJobRestrictions.get(i); 2002 if (restriction.isJobRestricted(job)) { 2003 return restriction; 2004 } 2005 } 2006 return null; 2007 } 2008 2009 private void stopNonReadyActiveJobsLocked() { 2010 for (int i=0; i<mActiveServices.size(); i++) { 2011 JobServiceContext serviceContext = mActiveServices.get(i); 2012 final JobStatus running = serviceContext.getRunningJobLocked(); 2013 if (running == null) { 2014 continue; 2015 } 2016 if (!running.isReady()) { 2017 // If a restricted job doesn't have dynamic constraints satisfied, assume that's 2018 // the reason the job is being stopped, instead of because of other constraints 2019 // not being satisfied. 2020 if (running.getEffectiveStandbyBucket() == RESTRICTED_INDEX 2021 && !running.areDynamicConstraintsSatisfied()) { 2022 serviceContext.cancelExecutingJobLocked( 2023 JobParameters.REASON_RESTRICTED_BUCKET, 2024 "cancelled due to restricted bucket"); 2025 } else { 2026 serviceContext.cancelExecutingJobLocked( 2027 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED, 2028 "cancelled due to unsatisfied constraints"); 2029 } 2030 } else { 2031 final JobRestriction restriction = checkIfRestricted(running); 2032 if (restriction != null) { 2033 final int reason = restriction.getReason(); 2034 serviceContext.cancelExecutingJobLocked(reason, 2035 "restricted due to " + JobParameters.getReasonCodeDescription(reason)); 2036 } 2037 } 2038 } 2039 } 2040 2041 /** 2042 * Run through list of jobs and execute all possible - at least one is expired so we do 2043 * as many as we can. 2044 */ 2045 private void queueReadyJobsForExecutionLocked() { 2046 if (DEBUG) { 2047 Slog.d(TAG, "queuing all ready jobs for execution:"); 2048 } 2049 noteJobsNonpending(mPendingJobs); 2050 mPendingJobs.clear(); 2051 stopNonReadyActiveJobsLocked(); 2052 mJobs.forEachJob(mReadyQueueFunctor); 2053 mReadyQueueFunctor.postProcess(); 2054 2055 if (DEBUG) { 2056 final int queuedJobs = mPendingJobs.size(); 2057 if (queuedJobs == 0) { 2058 Slog.d(TAG, "No jobs pending."); 2059 } else { 2060 Slog.d(TAG, queuedJobs + " jobs queued."); 2061 } 2062 } 2063 } 2064 2065 final class ReadyJobQueueFunctor implements Consumer<JobStatus> { 2066 final ArrayList<JobStatus> newReadyJobs = new ArrayList<>(); 2067 2068 @Override 2069 public void accept(JobStatus job) { 2070 if (isReadyToBeExecutedLocked(job)) { 2071 if (DEBUG) { 2072 Slog.d(TAG, " queued " + job.toShortString()); 2073 } 2074 newReadyJobs.add(job); 2075 } else { 2076 evaluateControllerStatesLocked(job); 2077 } 2078 } 2079 2080 public void postProcess() { 2081 noteJobsPending(newReadyJobs); 2082 mPendingJobs.addAll(newReadyJobs); 2083 if (mPendingJobs.size() > 1) { 2084 mPendingJobs.sort(sPendingJobComparator); 2085 } 2086 2087 newReadyJobs.clear(); 2088 } 2089 } 2090 private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor(); 2091 2092 /** 2093 * The state of at least one job has changed. Here is where we could enforce various 2094 * policies on when we want to execute jobs. 2095 */ 2096 final class MaybeReadyJobQueueFunctor implements Consumer<JobStatus> { 2097 int forceBatchedCount; 2098 int unbatchedCount; 2099 final List<JobStatus> runnableJobs = new ArrayList<>(); 2100 2101 public MaybeReadyJobQueueFunctor() { 2102 reset(); 2103 } 2104 2105 // Functor method invoked for each job via JobStore.forEachJob() 2106 @Override 2107 public void accept(JobStatus job) { 2108 if (isReadyToBeExecutedLocked(job)) { 2109 try { 2110 if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(), 2111 job.getJob().getService().getPackageName())) { 2112 Slog.w(TAG, "Aborting job " + job.getUid() + ":" 2113 + job.getJob().toString() + " -- package not allowed to start"); 2114 mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget(); 2115 return; 2116 } 2117 } catch (RemoteException e) { 2118 } 2119 2120 final boolean shouldForceBatchJob; 2121 // Restricted jobs must always be batched 2122 if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX) { 2123 shouldForceBatchJob = true; 2124 } else if (job.getNumFailures() > 0) { 2125 shouldForceBatchJob = false; 2126 } else { 2127 final long nowElapsed = sElapsedRealtimeClock.millis(); 2128 final boolean batchDelayExpired = job.getFirstForceBatchedTimeElapsed() > 0 2129 && nowElapsed - job.getFirstForceBatchedTimeElapsed() 2130 >= mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS; 2131 shouldForceBatchJob = 2132 mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT > 1 2133 && job.getEffectiveStandbyBucket() != ACTIVE_INDEX 2134 && !batchDelayExpired; 2135 } 2136 2137 if (shouldForceBatchJob) { 2138 // Force batching non-ACTIVE jobs. Don't include them in the other counts. 2139 forceBatchedCount++; 2140 if (job.getFirstForceBatchedTimeElapsed() == 0) { 2141 job.setFirstForceBatchedTimeElapsed(sElapsedRealtimeClock.millis()); 2142 } 2143 } else { 2144 unbatchedCount++; 2145 } 2146 runnableJobs.add(job); 2147 } else { 2148 evaluateControllerStatesLocked(job); 2149 } 2150 } 2151 2152 public void postProcess() { 2153 if (unbatchedCount > 0 2154 || forceBatchedCount >= mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT) { 2155 if (DEBUG) { 2156 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs."); 2157 } 2158 noteJobsPending(runnableJobs); 2159 mPendingJobs.addAll(runnableJobs); 2160 if (mPendingJobs.size() > 1) { 2161 mPendingJobs.sort(sPendingJobComparator); 2162 } 2163 } else { 2164 if (DEBUG) { 2165 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything."); 2166 } 2167 } 2168 2169 // Be ready for next time 2170 reset(); 2171 } 2172 2173 @VisibleForTesting 2174 void reset() { 2175 forceBatchedCount = 0; 2176 unbatchedCount = 0; 2177 runnableJobs.clear(); 2178 } 2179 } 2180 private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor(); 2181 2182 private void maybeQueueReadyJobsForExecutionLocked() { 2183 if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs..."); 2184 2185 noteJobsNonpending(mPendingJobs); 2186 mPendingJobs.clear(); 2187 stopNonReadyActiveJobsLocked(); 2188 mJobs.forEachJob(mMaybeQueueFunctor); 2189 mMaybeQueueFunctor.postProcess(); 2190 } 2191 2192 /** Returns true if both the calling and source users for the job are started. */ 2193 private boolean areUsersStartedLocked(final JobStatus job) { 2194 boolean sourceStarted = ArrayUtils.contains(mStartedUsers, job.getSourceUserId()); 2195 if (job.getUserId() == job.getSourceUserId()) { 2196 return sourceStarted; 2197 } 2198 return sourceStarted && ArrayUtils.contains(mStartedUsers, job.getUserId()); 2199 } 2200 2201 /** 2202 * Criteria for moving a job into the pending queue: 2203 * - It's ready. 2204 * - It's not pending. 2205 * - It's not already running on a JSC. 2206 * - The user that requested the job is running. 2207 * - The job's standby bucket has come due to be runnable. 2208 * - The component is enabled and runnable. 2209 */ 2210 @VisibleForTesting 2211 boolean isReadyToBeExecutedLocked(JobStatus job) { 2212 final boolean jobReady = job.isReady(); 2213 2214 if (DEBUG) { 2215 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString() 2216 + " ready=" + jobReady); 2217 } 2218 2219 // This is a condition that is very likely to be false (most jobs that are 2220 // scheduled are sitting there, not ready yet) and very cheap to check (just 2221 // a few conditions on data in JobStatus). 2222 if (!jobReady) { 2223 if (job.getSourcePackageName().equals("android.jobscheduler.cts.jobtestapp")) { 2224 Slog.v(TAG, " NOT READY: " + job); 2225 } 2226 return false; 2227 } 2228 2229 final boolean jobExists = mJobs.containsJob(job); 2230 final boolean userStarted = areUsersStartedLocked(job); 2231 final boolean backingUp = mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0; 2232 2233 if (DEBUG) { 2234 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString() 2235 + " exists=" + jobExists + " userStarted=" + userStarted 2236 + " backingUp=" + backingUp); 2237 } 2238 2239 // These are also fairly cheap to check, though they typically will not 2240 // be conditions we fail. 2241 if (!jobExists || !userStarted || backingUp) { 2242 return false; 2243 } 2244 2245 if (checkIfRestricted(job) != null) { 2246 return false; 2247 } 2248 2249 final boolean jobPending = mPendingJobs.contains(job); 2250 final boolean jobActive = isCurrentlyActiveLocked(job); 2251 2252 if (DEBUG) { 2253 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString() 2254 + " pending=" + jobPending + " active=" + jobActive); 2255 } 2256 2257 // These can be a little more expensive (especially jobActive, since we need to 2258 // go through the array of all potentially active jobs), so we are doing them 2259 // later... but still before checking with the package manager! 2260 if (jobPending || jobActive) { 2261 return false; 2262 } 2263 2264 // The expensive check: validate that the defined package+service is 2265 // still present & viable. 2266 return isComponentUsable(job); 2267 } 2268 2269 private boolean isComponentUsable(@NonNull JobStatus job) { 2270 final ServiceInfo service; 2271 try { 2272 // TODO: cache result until we're notified that something in the package changed. 2273 service = AppGlobals.getPackageManager().getServiceInfo( 2274 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING, 2275 job.getUserId()); 2276 } catch (RemoteException e) { 2277 throw new RuntimeException(e); 2278 } 2279 2280 if (service == null) { 2281 if (DEBUG) { 2282 Slog.v(TAG, "isComponentUsable: " + job.toShortString() 2283 + " component not present"); 2284 } 2285 return false; 2286 } 2287 2288 // Everything else checked out so far, so this is the final yes/no check 2289 final boolean appIsBad = mActivityManagerInternal.isAppBad(service.applicationInfo); 2290 if (DEBUG && appIsBad) { 2291 Slog.i(TAG, "App is bad for " + job.toShortString() + " so not runnable"); 2292 } 2293 return !appIsBad; 2294 } 2295 2296 @VisibleForTesting 2297 void evaluateControllerStatesLocked(final JobStatus job) { 2298 for (int c = mControllers.size() - 1; c >= 0; --c) { 2299 final StateController sc = mControllers.get(c); 2300 sc.evaluateStateLocked(job); 2301 } 2302 } 2303 2304 /** 2305 * Returns true if non-job constraint components are in place -- if job.isReady() returns true 2306 * and this method returns true, then the job is ready to be executed. 2307 */ 2308 public boolean areComponentsInPlaceLocked(JobStatus job) { 2309 // This code is very similar to the code in isReadyToBeExecutedLocked --- it uses the same 2310 // conditions. 2311 2312 final boolean jobExists = mJobs.containsJob(job); 2313 final boolean userStarted = areUsersStartedLocked(job); 2314 final boolean backingUp = mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0; 2315 2316 if (DEBUG) { 2317 Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString() 2318 + " exists=" + jobExists + " userStarted=" + userStarted 2319 + " backingUp=" + backingUp); 2320 } 2321 2322 // These are also fairly cheap to check, though they typically will not 2323 // be conditions we fail. 2324 if (!jobExists || !userStarted || backingUp) { 2325 return false; 2326 } 2327 2328 if (checkIfRestricted(job) != null) { 2329 return false; 2330 } 2331 2332 // Job pending/active doesn't affect the readiness of a job. 2333 2334 // The expensive check: validate that the defined package+service is 2335 // still present & viable. 2336 return isComponentUsable(job); 2337 } 2338 2339 /** Returns the maximum amount of time this job could run for. */ 2340 public long getMaxJobExecutionTimeMs(JobStatus job) { 2341 synchronized (mLock) { 2342 return Math.min(mQuotaController.getMaxJobExecutionTimeMsLocked(job), 2343 JobServiceContext.EXECUTING_TIMESLICE_MILLIS); 2344 } 2345 } 2346 2347 /** 2348 * Reconcile jobs in the pending queue against available execution contexts. 2349 * A controller can force a job into the pending queue even if it's already running, but 2350 * here is where we decide whether to actually execute it. 2351 */ 2352 void maybeRunPendingJobsLocked() { 2353 if (DEBUG) { 2354 Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs."); 2355 } 2356 mConcurrencyManager.assignJobsToContextsLocked(); 2357 reportActiveLocked(); 2358 } 2359 2360 private int adjustJobPriority(int curPriority, JobStatus job) { 2361 if (curPriority < JobInfo.PRIORITY_TOP_APP) { 2362 float factor = mJobPackageTracker.getLoadFactor(job); 2363 if (factor >= mConstants.HEAVY_USE_FACTOR) { 2364 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING; 2365 } else if (factor >= mConstants.MODERATE_USE_FACTOR) { 2366 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING; 2367 } 2368 } 2369 return curPriority; 2370 } 2371 2372 int evaluateJobPriorityLocked(JobStatus job) { 2373 int priority = job.getPriority(); 2374 if (priority >= JobInfo.PRIORITY_BOUND_FOREGROUND_SERVICE) { 2375 return adjustJobPriority(priority, job); 2376 } 2377 int override = mUidPriorityOverride.get(job.getSourceUid(), 0); 2378 if (override != 0) { 2379 return adjustJobPriority(override, job); 2380 } 2381 return adjustJobPriority(priority, job); 2382 } 2383 2384 final class LocalService implements JobSchedulerInternal { 2385 2386 /** 2387 * Returns a list of all pending jobs. A running job is not considered pending. Periodic 2388 * jobs are always considered pending. 2389 */ 2390 @Override 2391 public List<JobInfo> getSystemScheduledPendingJobs() { 2392 synchronized (mLock) { 2393 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>(); 2394 mJobs.forEachJob(Process.SYSTEM_UID, (job) -> { 2395 if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) { 2396 pendingJobs.add(job.getJob()); 2397 } 2398 }); 2399 return pendingJobs; 2400 } 2401 } 2402 2403 @Override 2404 public void cancelJobsForUid(int uid, String reason) { 2405 JobSchedulerService.this.cancelJobsForUid(uid, reason); 2406 } 2407 2408 @Override 2409 public void addBackingUpUid(int uid) { 2410 synchronized (mLock) { 2411 // No need to actually do anything here, since for a full backup the 2412 // activity manager will kill the process which will kill the job (and 2413 // cause it to restart, but now it can't run). 2414 mBackingUpUids.put(uid, uid); 2415 } 2416 } 2417 2418 @Override 2419 public void removeBackingUpUid(int uid) { 2420 synchronized (mLock) { 2421 mBackingUpUids.delete(uid); 2422 // If there are any jobs for this uid, we need to rebuild the pending list 2423 // in case they are now ready to run. 2424 if (mJobs.countJobsForUid(uid) > 0) { 2425 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 2426 } 2427 } 2428 } 2429 2430 @Override 2431 public void clearAllBackingUpUids() { 2432 synchronized (mLock) { 2433 if (mBackingUpUids.size() > 0) { 2434 mBackingUpUids.clear(); 2435 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 2436 } 2437 } 2438 } 2439 2440 @Override 2441 public String getMediaBackupPackage() { 2442 return mSystemGalleryPackage; 2443 } 2444 2445 @Override 2446 public void reportAppUsage(String packageName, int userId) { 2447 JobSchedulerService.this.reportAppUsage(packageName, userId); 2448 } 2449 2450 @Override 2451 public JobStorePersistStats getPersistStats() { 2452 synchronized (mLock) { 2453 return new JobStorePersistStats(mJobs.getPersistStats()); 2454 } 2455 } 2456 } 2457 2458 /** 2459 * Tracking of app assignments to standby buckets 2460 */ 2461 final class StandbyTracker extends AppIdleStateChangeListener { 2462 2463 // AppIdleStateChangeListener interface for live updates 2464 2465 @Override 2466 public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId, 2467 boolean idle, int bucket, int reason) { 2468 // QuotaController handles this now. 2469 } 2470 2471 @Override 2472 public void onUserInteractionStarted(String packageName, int userId) { 2473 final int uid = mLocalPM.getPackageUid(packageName, 2474 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); 2475 if (uid < 0) { 2476 // Quietly ignore; the case is already logged elsewhere 2477 return; 2478 } 2479 2480 long sinceLast = mUsageStats.getTimeSinceLastJobRun(packageName, userId); 2481 if (sinceLast > 2 * DateUtils.DAY_IN_MILLIS) { 2482 // Too long ago, not worth logging 2483 sinceLast = 0L; 2484 } 2485 final DeferredJobCounter counter = new DeferredJobCounter(); 2486 synchronized (mLock) { 2487 mJobs.forEachJobForSourceUid(uid, counter); 2488 } 2489 if (counter.numDeferred() > 0 || sinceLast > 0) { 2490 BatteryStatsInternal mBatteryStatsInternal = LocalServices.getService 2491 (BatteryStatsInternal.class); 2492 mBatteryStatsInternal.noteJobsDeferred(uid, counter.numDeferred(), sinceLast); 2493 FrameworkStatsLog.write_non_chained( 2494 FrameworkStatsLog.DEFERRED_JOB_STATS_REPORTED, uid, null, 2495 counter.numDeferred(), sinceLast); 2496 } 2497 } 2498 } 2499 2500 static class DeferredJobCounter implements Consumer<JobStatus> { 2501 private int mDeferred = 0; 2502 2503 public int numDeferred() { 2504 return mDeferred; 2505 } 2506 2507 @Override 2508 public void accept(JobStatus job) { 2509 if (job.getWhenStandbyDeferred() > 0) { 2510 mDeferred++; 2511 } 2512 } 2513 } 2514 2515 public static int standbyBucketToBucketIndex(int bucket) { 2516 // Normalize AppStandby constants to indices into our bookkeeping 2517 if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) { 2518 return NEVER_INDEX; 2519 } else if (bucket > UsageStatsManager.STANDBY_BUCKET_RARE) { 2520 return RESTRICTED_INDEX; 2521 } else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) { 2522 return RARE_INDEX; 2523 } else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) { 2524 return FREQUENT_INDEX; 2525 } else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) { 2526 return WORKING_INDEX; 2527 } else { 2528 return ACTIVE_INDEX; 2529 } 2530 } 2531 2532 // Static to support external callers 2533 public static int standbyBucketForPackage(String packageName, int userId, long elapsedNow) { 2534 UsageStatsManagerInternal usageStats = LocalServices.getService( 2535 UsageStatsManagerInternal.class); 2536 int bucket = usageStats != null 2537 ? usageStats.getAppStandbyBucket(packageName, userId, elapsedNow) 2538 : 0; 2539 2540 bucket = standbyBucketToBucketIndex(bucket); 2541 2542 if (DEBUG_STANDBY) { 2543 Slog.v(TAG, packageName + "/" + userId + " standby bucket index: " + bucket); 2544 } 2545 return bucket; 2546 } 2547 2548 /** 2549 * Binder stub trampoline implementation 2550 */ 2551 final class JobSchedulerStub extends IJobScheduler.Stub { 2552 /** Cache determination of whether a given app can persist jobs 2553 * key is uid of the calling app; value is undetermined/true/false 2554 */ 2555 private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>(); 2556 2557 // Enforce that only the app itself (or shared uid participant) can schedule a 2558 // job that runs one of the app's services, as well as verifying that the 2559 // named service properly requires the BIND_JOB_SERVICE permission 2560 private void enforceValidJobRequest(int uid, JobInfo job) { 2561 final IPackageManager pm = AppGlobals.getPackageManager(); 2562 final ComponentName service = job.getService(); 2563 try { 2564 ServiceInfo si = pm.getServiceInfo(service, 2565 PackageManager.MATCH_DIRECT_BOOT_AWARE 2566 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 2567 UserHandle.getUserId(uid)); 2568 if (si == null) { 2569 throw new IllegalArgumentException("No such service " + service); 2570 } 2571 if (si.applicationInfo.uid != uid) { 2572 throw new IllegalArgumentException("uid " + uid + 2573 " cannot schedule job in " + service.getPackageName()); 2574 } 2575 if (!JobService.PERMISSION_BIND.equals(si.permission)) { 2576 throw new IllegalArgumentException("Scheduled service " + service 2577 + " does not require android.permission.BIND_JOB_SERVICE permission"); 2578 } 2579 } catch (RemoteException e) { 2580 // Can't happen; the Package Manager is in this same process 2581 } 2582 } 2583 2584 private boolean canPersistJobs(int pid, int uid) { 2585 // If we get this far we're good to go; all we need to do now is check 2586 // whether the app is allowed to persist its scheduled work. 2587 final boolean canPersist; 2588 synchronized (mPersistCache) { 2589 Boolean cached = mPersistCache.get(uid); 2590 if (cached != null) { 2591 canPersist = cached.booleanValue(); 2592 } else { 2593 // Persisting jobs is tantamount to running at boot, so we permit 2594 // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED 2595 // permission 2596 int result = getContext().checkPermission( 2597 android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid); 2598 canPersist = (result == PackageManager.PERMISSION_GRANTED); 2599 mPersistCache.put(uid, canPersist); 2600 } 2601 } 2602 return canPersist; 2603 } 2604 2605 private void validateJobFlags(JobInfo job, int callingUid) { 2606 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) { 2607 getContext().enforceCallingOrSelfPermission( 2608 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG); 2609 } 2610 if ((job.getFlags() & JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY) != 0) { 2611 if (callingUid != Process.SYSTEM_UID) { 2612 throw new SecurityException("Job has invalid flags"); 2613 } 2614 if (job.isPeriodic()) { 2615 Slog.wtf(TAG, "Periodic jobs mustn't have" 2616 + " FLAG_EXEMPT_FROM_APP_STANDBY. Job=" + job); 2617 } 2618 } 2619 } 2620 2621 // IJobScheduler implementation 2622 @Override 2623 public int schedule(JobInfo job) throws RemoteException { 2624 if (DEBUG) { 2625 Slog.d(TAG, "Scheduling job: " + job.toString()); 2626 } 2627 final int pid = Binder.getCallingPid(); 2628 final int uid = Binder.getCallingUid(); 2629 final int userId = UserHandle.getUserId(uid); 2630 2631 enforceValidJobRequest(uid, job); 2632 if (job.isPersisted()) { 2633 if (!canPersistJobs(pid, uid)) { 2634 throw new IllegalArgumentException("Error: requested job be persisted without" 2635 + " holding RECEIVE_BOOT_COMPLETED permission."); 2636 } 2637 } 2638 2639 validateJobFlags(job, uid); 2640 2641 long ident = Binder.clearCallingIdentity(); 2642 try { 2643 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, userId, 2644 null); 2645 } finally { 2646 Binder.restoreCallingIdentity(ident); 2647 } 2648 } 2649 2650 // IJobScheduler implementation 2651 @Override 2652 public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException { 2653 if (DEBUG) { 2654 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work); 2655 } 2656 final int uid = Binder.getCallingUid(); 2657 final int userId = UserHandle.getUserId(uid); 2658 2659 enforceValidJobRequest(uid, job); 2660 if (job.isPersisted()) { 2661 throw new IllegalArgumentException("Can't enqueue work for persisted jobs"); 2662 } 2663 if (work == null) { 2664 throw new NullPointerException("work is null"); 2665 } 2666 2667 validateJobFlags(job, uid); 2668 2669 long ident = Binder.clearCallingIdentity(); 2670 try { 2671 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, userId, 2672 null); 2673 } finally { 2674 Binder.restoreCallingIdentity(ident); 2675 } 2676 } 2677 2678 @Override 2679 public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag) 2680 throws RemoteException { 2681 final int callerUid = Binder.getCallingUid(); 2682 if (DEBUG) { 2683 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString() 2684 + " on behalf of " + packageName + "/"); 2685 } 2686 2687 if (packageName == null) { 2688 throw new NullPointerException("Must specify a package for scheduleAsPackage()"); 2689 } 2690 2691 int mayScheduleForOthers = getContext().checkCallingOrSelfPermission( 2692 android.Manifest.permission.UPDATE_DEVICE_STATS); 2693 if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) { 2694 throw new SecurityException("Caller uid " + callerUid 2695 + " not permitted to schedule jobs for other apps"); 2696 } 2697 2698 validateJobFlags(job, callerUid); 2699 2700 long ident = Binder.clearCallingIdentity(); 2701 try { 2702 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid, 2703 packageName, userId, tag); 2704 } finally { 2705 Binder.restoreCallingIdentity(ident); 2706 } 2707 } 2708 2709 @Override 2710 public ParceledListSlice<JobInfo> getAllPendingJobs() throws RemoteException { 2711 final int uid = Binder.getCallingUid(); 2712 2713 long ident = Binder.clearCallingIdentity(); 2714 try { 2715 return new ParceledListSlice<>(JobSchedulerService.this.getPendingJobs(uid)); 2716 } finally { 2717 Binder.restoreCallingIdentity(ident); 2718 } 2719 } 2720 2721 @Override 2722 public JobInfo getPendingJob(int jobId) throws RemoteException { 2723 final int uid = Binder.getCallingUid(); 2724 2725 long ident = Binder.clearCallingIdentity(); 2726 try { 2727 return JobSchedulerService.this.getPendingJob(uid, jobId); 2728 } finally { 2729 Binder.restoreCallingIdentity(ident); 2730 } 2731 } 2732 2733 @Override 2734 public void cancelAll() throws RemoteException { 2735 final int uid = Binder.getCallingUid(); 2736 long ident = Binder.clearCallingIdentity(); 2737 try { 2738 JobSchedulerService.this.cancelJobsForUid(uid, 2739 "cancelAll() called by app, callingUid=" + uid); 2740 } finally { 2741 Binder.restoreCallingIdentity(ident); 2742 } 2743 } 2744 2745 @Override 2746 public void cancel(int jobId) throws RemoteException { 2747 final int uid = Binder.getCallingUid(); 2748 2749 long ident = Binder.clearCallingIdentity(); 2750 try { 2751 JobSchedulerService.this.cancelJob(uid, jobId, uid); 2752 } finally { 2753 Binder.restoreCallingIdentity(ident); 2754 } 2755 } 2756 2757 /** 2758 * "dumpsys" infrastructure 2759 */ 2760 @Override 2761 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2762 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return; 2763 2764 int filterUid = -1; 2765 boolean proto = false; 2766 if (!ArrayUtils.isEmpty(args)) { 2767 int opti = 0; 2768 while (opti < args.length) { 2769 String arg = args[opti]; 2770 if ("-h".equals(arg)) { 2771 dumpHelp(pw); 2772 return; 2773 } else if ("-a".equals(arg)) { 2774 // Ignore, we always dump all. 2775 } else if ("--proto".equals(arg)) { 2776 proto = true; 2777 } else if (arg.length() > 0 && arg.charAt(0) == '-') { 2778 pw.println("Unknown option: " + arg); 2779 return; 2780 } else { 2781 break; 2782 } 2783 opti++; 2784 } 2785 if (opti < args.length) { 2786 String pkg = args[opti]; 2787 try { 2788 filterUid = getContext().getPackageManager().getPackageUid(pkg, 2789 PackageManager.MATCH_ANY_USER); 2790 } catch (NameNotFoundException ignored) { 2791 pw.println("Invalid package: " + pkg); 2792 return; 2793 } 2794 } 2795 } 2796 2797 final long identityToken = Binder.clearCallingIdentity(); 2798 try { 2799 if (proto) { 2800 JobSchedulerService.this.dumpInternalProto(fd, filterUid); 2801 } else { 2802 JobSchedulerService.this.dumpInternal(new IndentingPrintWriter(pw, " "), 2803 filterUid); 2804 } 2805 } finally { 2806 Binder.restoreCallingIdentity(identityToken); 2807 } 2808 } 2809 2810 @Override 2811 public int handleShellCommand(@NonNull ParcelFileDescriptor in, 2812 @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, 2813 @NonNull String[] args) { 2814 return (new JobSchedulerShellCommand(JobSchedulerService.this)).exec( 2815 this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), 2816 args); 2817 } 2818 2819 /** 2820 * <b>For internal system user only!</b> 2821 * Returns a list of all currently-executing jobs. 2822 */ 2823 @Override 2824 public List<JobInfo> getStartedJobs() { 2825 final int uid = Binder.getCallingUid(); 2826 if (uid != Process.SYSTEM_UID) { 2827 throw new SecurityException( 2828 "getStartedJobs() is system internal use only."); 2829 } 2830 2831 final ArrayList<JobInfo> runningJobs; 2832 2833 synchronized (mLock) { 2834 runningJobs = new ArrayList<>(mActiveServices.size()); 2835 for (JobServiceContext jsc : mActiveServices) { 2836 final JobStatus job = jsc.getRunningJobLocked(); 2837 if (job != null) { 2838 runningJobs.add(job.getJob()); 2839 } 2840 } 2841 } 2842 2843 return runningJobs; 2844 } 2845 2846 /** 2847 * <b>For internal system user only!</b> 2848 * Returns a snapshot of the state of all jobs known to the system. 2849 * 2850 * <p class="note">This is a slow operation, so it should be called sparingly. 2851 */ 2852 @Override 2853 public ParceledListSlice<JobSnapshot> getAllJobSnapshots() { 2854 final int uid = Binder.getCallingUid(); 2855 if (uid != Process.SYSTEM_UID) { 2856 throw new SecurityException( 2857 "getAllJobSnapshots() is system internal use only."); 2858 } 2859 synchronized (mLock) { 2860 final ArrayList<JobSnapshot> snapshots = new ArrayList<>(mJobs.size()); 2861 mJobs.forEachJob((job) -> snapshots.add( 2862 new JobSnapshot(job.getJob(), job.getSatisfiedConstraintFlags(), 2863 isReadyToBeExecutedLocked(job)))); 2864 return new ParceledListSlice<>(snapshots); 2865 } 2866 } 2867 } 2868 2869 // Shell command infrastructure: run the given job immediately 2870 int executeRunCommand(String pkgName, int userId, int jobId, boolean satisfied, boolean force) { 2871 Slog.d(TAG, "executeRunCommand(): " + pkgName + "/" + userId 2872 + " " + jobId + " s=" + satisfied + " f=" + force); 2873 2874 try { 2875 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, 2876 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM); 2877 if (uid < 0) { 2878 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE; 2879 } 2880 2881 synchronized (mLock) { 2882 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId); 2883 if (js == null) { 2884 return JobSchedulerShellCommand.CMD_ERR_NO_JOB; 2885 } 2886 2887 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL 2888 : (satisfied ? JobStatus.OVERRIDE_SORTING : JobStatus.OVERRIDE_SOFT); 2889 2890 // Re-evaluate constraints after the override is set in case one of the overridden 2891 // constraints was preventing another constraint from thinking it needed to update. 2892 for (int c = mControllers.size() - 1; c >= 0; --c) { 2893 mControllers.get(c).reevaluateStateLocked(uid); 2894 } 2895 2896 if (!js.isConstraintsSatisfied()) { 2897 js.overrideState = JobStatus.OVERRIDE_NONE; 2898 return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS; 2899 } 2900 2901 queueReadyJobsForExecutionLocked(); 2902 maybeRunPendingJobsLocked(); 2903 } 2904 } catch (RemoteException e) { 2905 // can't happen 2906 } 2907 return 0; 2908 } 2909 2910 // Shell command infrastructure: immediately timeout currently executing jobs 2911 int executeTimeoutCommand(PrintWriter pw, String pkgName, int userId, 2912 boolean hasJobId, int jobId) { 2913 if (DEBUG) { 2914 Slog.v(TAG, "executeTimeoutCommand(): " + pkgName + "/" + userId + " " + jobId); 2915 } 2916 2917 synchronized (mLock) { 2918 boolean foundSome = false; 2919 for (int i=0; i<mActiveServices.size(); i++) { 2920 final JobServiceContext jc = mActiveServices.get(i); 2921 final JobStatus js = jc.getRunningJobLocked(); 2922 if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) { 2923 foundSome = true; 2924 pw.print("Timing out: "); 2925 js.printUniqueId(pw); 2926 pw.print(" "); 2927 pw.println(js.getServiceComponent().flattenToShortString()); 2928 } 2929 } 2930 if (!foundSome) { 2931 pw.println("No matching executing jobs found."); 2932 } 2933 } 2934 return 0; 2935 } 2936 2937 // Shell command infrastructure: cancel a scheduled job 2938 int executeCancelCommand(PrintWriter pw, String pkgName, int userId, 2939 boolean hasJobId, int jobId) { 2940 if (DEBUG) { 2941 Slog.v(TAG, "executeCancelCommand(): " + pkgName + "/" + userId + " " + jobId); 2942 } 2943 2944 int pkgUid = -1; 2945 try { 2946 IPackageManager pm = AppGlobals.getPackageManager(); 2947 pkgUid = pm.getPackageUid(pkgName, 0, userId); 2948 } catch (RemoteException e) { /* can't happen */ } 2949 2950 if (pkgUid < 0) { 2951 pw.println("Package " + pkgName + " not found."); 2952 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE; 2953 } 2954 2955 if (!hasJobId) { 2956 pw.println("Canceling all jobs for " + pkgName + " in user " + userId); 2957 if (!cancelJobsForUid(pkgUid, "cancel shell command for package")) { 2958 pw.println("No matching jobs found."); 2959 } 2960 } else { 2961 pw.println("Canceling job " + pkgName + "/#" + jobId + " in user " + userId); 2962 if (!cancelJob(pkgUid, jobId, Process.SHELL_UID)) { 2963 pw.println("No matching job found."); 2964 } 2965 } 2966 2967 return 0; 2968 } 2969 2970 void setMonitorBattery(boolean enabled) { 2971 synchronized (mLock) { 2972 if (mBatteryController != null) { 2973 mBatteryController.getTracker().setMonitorBatteryLocked(enabled); 2974 } 2975 } 2976 } 2977 2978 int getBatterySeq() { 2979 synchronized (mLock) { 2980 return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1; 2981 } 2982 } 2983 2984 boolean getBatteryCharging() { 2985 synchronized (mLock) { 2986 return mBatteryController != null 2987 ? mBatteryController.getTracker().isOnStablePower() : false; 2988 } 2989 } 2990 2991 boolean getBatteryNotLow() { 2992 synchronized (mLock) { 2993 return mBatteryController != null 2994 ? mBatteryController.getTracker().isBatteryNotLow() : false; 2995 } 2996 } 2997 2998 int getStorageSeq() { 2999 synchronized (mLock) { 3000 return mStorageController != null ? mStorageController.getTracker().getSeq() : -1; 3001 } 3002 } 3003 3004 boolean getStorageNotLow() { 3005 synchronized (mLock) { 3006 return mStorageController != null 3007 ? mStorageController.getTracker().isStorageNotLow() : false; 3008 } 3009 } 3010 3011 // Shell command infrastructure 3012 int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) { 3013 try { 3014 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, 3015 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM); 3016 if (uid < 0) { 3017 pw.print("unknown("); pw.print(pkgName); pw.println(")"); 3018 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE; 3019 } 3020 3021 synchronized (mLock) { 3022 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId); 3023 if (DEBUG) Slog.d(TAG, "get-job-state " + uid + "/" + jobId + ": " + js); 3024 if (js == null) { 3025 pw.print("unknown("); UserHandle.formatUid(pw, uid); 3026 pw.print("/jid"); pw.print(jobId); pw.println(")"); 3027 return JobSchedulerShellCommand.CMD_ERR_NO_JOB; 3028 } 3029 3030 boolean printed = false; 3031 if (mPendingJobs.contains(js)) { 3032 pw.print("pending"); 3033 printed = true; 3034 } 3035 if (isCurrentlyActiveLocked(js)) { 3036 if (printed) { 3037 pw.print(" "); 3038 } 3039 printed = true; 3040 pw.println("active"); 3041 } 3042 if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) { 3043 if (printed) { 3044 pw.print(" "); 3045 } 3046 printed = true; 3047 pw.println("user-stopped"); 3048 } 3049 if (!ArrayUtils.contains(mStartedUsers, js.getSourceUserId())) { 3050 if (printed) { 3051 pw.print(" "); 3052 } 3053 printed = true; 3054 pw.println("source-user-stopped"); 3055 } 3056 if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) { 3057 if (printed) { 3058 pw.print(" "); 3059 } 3060 printed = true; 3061 pw.println("backing-up"); 3062 } 3063 boolean componentPresent = false; 3064 try { 3065 componentPresent = (AppGlobals.getPackageManager().getServiceInfo( 3066 js.getServiceComponent(), 3067 PackageManager.MATCH_DEBUG_TRIAGED_MISSING, 3068 js.getUserId()) != null); 3069 } catch (RemoteException e) { 3070 } 3071 if (!componentPresent) { 3072 if (printed) { 3073 pw.print(" "); 3074 } 3075 printed = true; 3076 pw.println("no-component"); 3077 } 3078 if (js.isReady()) { 3079 if (printed) { 3080 pw.print(" "); 3081 } 3082 printed = true; 3083 pw.println("ready"); 3084 } 3085 if (!printed) { 3086 pw.print("waiting"); 3087 } 3088 pw.println(); 3089 } 3090 } catch (RemoteException e) { 3091 // can't happen 3092 } 3093 return 0; 3094 } 3095 3096 void resetExecutionQuota(@NonNull String pkgName, int userId) { 3097 mQuotaController.clearAppStats(userId, pkgName); 3098 } 3099 3100 void resetScheduleQuota() { 3101 mQuotaTracker.clear(); 3102 } 3103 3104 void triggerDockState(boolean idleState) { 3105 final Intent dockIntent; 3106 if (idleState) { 3107 dockIntent = new Intent(Intent.ACTION_DOCK_IDLE); 3108 } else { 3109 dockIntent = new Intent(Intent.ACTION_DOCK_ACTIVE); 3110 } 3111 dockIntent.setPackage("android"); 3112 dockIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); 3113 getContext().sendBroadcastAsUser(dockIntent, UserHandle.ALL); 3114 } 3115 3116 static void dumpHelp(PrintWriter pw) { 3117 pw.println("Job Scheduler (jobscheduler) dump options:"); 3118 pw.println(" [-h] [package] ..."); 3119 pw.println(" -h: print this help"); 3120 pw.println(" [package] is an optional package name to limit the output to."); 3121 } 3122 3123 /** Sort jobs by caller UID, then by Job ID. */ 3124 private static void sortJobs(List<JobStatus> jobs) { 3125 Collections.sort(jobs, new Comparator<JobStatus>() { 3126 @Override 3127 public int compare(JobStatus o1, JobStatus o2) { 3128 int uid1 = o1.getUid(); 3129 int uid2 = o2.getUid(); 3130 int id1 = o1.getJobId(); 3131 int id2 = o2.getJobId(); 3132 if (uid1 != uid2) { 3133 return uid1 < uid2 ? -1 : 1; 3134 } 3135 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0); 3136 } 3137 }); 3138 } 3139 3140 void dumpInternal(final IndentingPrintWriter pw, int filterUid) { 3141 final int filterUidFinal = UserHandle.getAppId(filterUid); 3142 final long now = sSystemClock.millis(); 3143 final long nowElapsed = sElapsedRealtimeClock.millis(); 3144 final long nowUptime = sUptimeMillisClock.millis(); 3145 3146 final Predicate<JobStatus> predicate = (js) -> { 3147 return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal 3148 || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal; 3149 }; 3150 synchronized (mLock) { 3151 mConstants.dump(pw); 3152 for (StateController controller : mControllers) { 3153 pw.increaseIndent(); 3154 controller.dumpConstants(pw); 3155 pw.decreaseIndent(); 3156 } 3157 pw.println(); 3158 3159 for (int i = mJobRestrictions.size() - 1; i >= 0; i--) { 3160 pw.print(" "); 3161 mJobRestrictions.get(i).dumpConstants(pw); 3162 pw.println(); 3163 } 3164 pw.println(); 3165 3166 mQuotaTracker.dump(pw); 3167 pw.println(); 3168 3169 pw.println("Started users: " + Arrays.toString(mStartedUsers)); 3170 pw.print("Registered "); 3171 pw.print(mJobs.size()); 3172 pw.println(" jobs:"); 3173 if (mJobs.size() > 0) { 3174 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs(); 3175 sortJobs(jobs); 3176 for (JobStatus job : jobs) { 3177 pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": "); 3178 pw.println(job.toShortStringExceptUniqueId()); 3179 3180 // Skip printing details if the caller requested a filter 3181 if (!predicate.test(job)) { 3182 continue; 3183 } 3184 3185 job.dump(pw, " ", true, nowElapsed); 3186 3187 3188 pw.print(" Restricted due to:"); 3189 final boolean isRestricted = checkIfRestricted(job) != null; 3190 if (isRestricted) { 3191 for (int i = mJobRestrictions.size() - 1; i >= 0; i--) { 3192 final JobRestriction restriction = mJobRestrictions.get(i); 3193 if (restriction.isJobRestricted(job)) { 3194 final int reason = restriction.getReason(); 3195 pw.print(" " + JobParameters.getReasonCodeDescription(reason)); 3196 } 3197 } 3198 } else { 3199 pw.print(" none"); 3200 } 3201 pw.println("."); 3202 3203 pw.print(" Ready: "); 3204 pw.print(isReadyToBeExecutedLocked(job)); 3205 pw.print(" (job="); 3206 pw.print(job.isReady()); 3207 pw.print(" user="); 3208 pw.print(areUsersStartedLocked(job)); 3209 pw.print(" !restricted="); 3210 pw.print(!isRestricted); 3211 pw.print(" !pending="); 3212 pw.print(!mPendingJobs.contains(job)); 3213 pw.print(" !active="); 3214 pw.print(!isCurrentlyActiveLocked(job)); 3215 pw.print(" !backingup="); 3216 pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0)); 3217 pw.print(" comp="); 3218 pw.print(isComponentUsable(job)); 3219 pw.println(")"); 3220 } 3221 } else { 3222 pw.println(" None."); 3223 } 3224 for (int i=0; i<mControllers.size(); i++) { 3225 pw.println(); 3226 pw.println(mControllers.get(i).getClass().getSimpleName() + ":"); 3227 pw.increaseIndent(); 3228 mControllers.get(i).dumpControllerStateLocked(pw, predicate); 3229 pw.decreaseIndent(); 3230 } 3231 pw.println(); 3232 pw.println("Uid priority overrides:"); 3233 for (int i=0; i< mUidPriorityOverride.size(); i++) { 3234 int uid = mUidPriorityOverride.keyAt(i); 3235 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) { 3236 pw.print(" "); pw.print(UserHandle.formatUid(uid)); 3237 pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i)); 3238 } 3239 } 3240 if (mBackingUpUids.size() > 0) { 3241 pw.println(); 3242 pw.println("Backing up uids:"); 3243 boolean first = true; 3244 for (int i = 0; i < mBackingUpUids.size(); i++) { 3245 int uid = mBackingUpUids.keyAt(i); 3246 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) { 3247 if (first) { 3248 pw.print(" "); 3249 first = false; 3250 } else { 3251 pw.print(", "); 3252 } 3253 pw.print(UserHandle.formatUid(uid)); 3254 } 3255 } 3256 pw.println(); 3257 } 3258 pw.println(); 3259 mJobPackageTracker.dump(pw, "", filterUidFinal); 3260 pw.println(); 3261 if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) { 3262 pw.println(); 3263 } 3264 pw.println("Pending queue:"); 3265 for (int i=0; i<mPendingJobs.size(); i++) { 3266 JobStatus job = mPendingJobs.get(i); 3267 pw.print(" Pending #"); pw.print(i); pw.print(": "); 3268 pw.println(job.toShortString()); 3269 job.dump(pw, " ", false, nowElapsed); 3270 int priority = evaluateJobPriorityLocked(job); 3271 pw.print(" Evaluated priority: "); 3272 pw.println(JobInfo.getPriorityString(priority)); 3273 3274 pw.print(" Tag: "); pw.println(job.getTag()); 3275 pw.print(" Enq: "); 3276 TimeUtils.formatDuration(job.madePending - nowUptime, pw); 3277 pw.println(); 3278 } 3279 pw.println(); 3280 pw.println("Active jobs:"); 3281 for (int i=0; i<mActiveServices.size(); i++) { 3282 JobServiceContext jsc = mActiveServices.get(i); 3283 pw.print(" Slot #"); pw.print(i); pw.print(": "); 3284 final JobStatus job = jsc.getRunningJobLocked(); 3285 if (job == null) { 3286 if (jsc.mStoppedReason != null) { 3287 pw.print("inactive since "); 3288 TimeUtils.formatDuration(jsc.mStoppedTime, nowElapsed, pw); 3289 pw.print(", stopped because: "); 3290 pw.println(jsc.mStoppedReason); 3291 } else { 3292 pw.println("inactive"); 3293 } 3294 continue; 3295 } else { 3296 pw.println(job.toShortString()); 3297 pw.print(" Running for: "); 3298 TimeUtils.formatDuration(nowElapsed - jsc.getExecutionStartTimeElapsed(), pw); 3299 pw.print(", timeout at: "); 3300 TimeUtils.formatDuration(jsc.getTimeoutElapsed() - nowElapsed, pw); 3301 pw.println(); 3302 job.dump(pw, " ", false, nowElapsed); 3303 int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked()); 3304 pw.print(" Evaluated priority: "); 3305 pw.println(JobInfo.getPriorityString(priority)); 3306 3307 pw.print(" Active at "); 3308 TimeUtils.formatDuration(job.madeActive - nowUptime, pw); 3309 pw.print(", pending for "); 3310 TimeUtils.formatDuration(job.madeActive - job.madePending, pw); 3311 pw.println(); 3312 } 3313 } 3314 if (filterUid == -1) { 3315 pw.println(); 3316 pw.print("mReadyToRock="); pw.println(mReadyToRock); 3317 pw.print("mReportedActive="); pw.println(mReportedActive); 3318 } 3319 pw.println(); 3320 3321 mConcurrencyManager.dumpLocked(pw, now, nowElapsed); 3322 3323 pw.println(); 3324 pw.print("PersistStats: "); 3325 pw.println(mJobs.getPersistStats()); 3326 } 3327 pw.println(); 3328 } 3329 3330 void dumpInternalProto(final FileDescriptor fd, int filterUid) { 3331 ProtoOutputStream proto = new ProtoOutputStream(fd); 3332 final int filterUidFinal = UserHandle.getAppId(filterUid); 3333 final long now = sSystemClock.millis(); 3334 final long nowElapsed = sElapsedRealtimeClock.millis(); 3335 final long nowUptime = sUptimeMillisClock.millis(); 3336 final Predicate<JobStatus> predicate = (js) -> { 3337 return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal 3338 || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal; 3339 }; 3340 3341 synchronized (mLock) { 3342 final long settingsToken = proto.start(JobSchedulerServiceDumpProto.SETTINGS); 3343 mConstants.dump(proto); 3344 for (StateController controller : mControllers) { 3345 controller.dumpConstants(proto); 3346 } 3347 proto.end(settingsToken); 3348 3349 for (int i = mJobRestrictions.size() - 1; i >= 0; i--) { 3350 mJobRestrictions.get(i).dumpConstants(proto); 3351 } 3352 3353 for (int u : mStartedUsers) { 3354 proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u); 3355 } 3356 3357 mQuotaTracker.dump(proto, JobSchedulerServiceDumpProto.QUOTA_TRACKER); 3358 3359 if (mJobs.size() > 0) { 3360 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs(); 3361 sortJobs(jobs); 3362 for (JobStatus job : jobs) { 3363 final long rjToken = proto.start(JobSchedulerServiceDumpProto.REGISTERED_JOBS); 3364 job.writeToShortProto(proto, JobSchedulerServiceDumpProto.RegisteredJob.INFO); 3365 3366 // Skip printing details if the caller requested a filter 3367 if (!predicate.test(job)) { 3368 continue; 3369 } 3370 3371 job.dump(proto, JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed); 3372 3373 proto.write( 3374 JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY_TO_BE_EXECUTED, 3375 isReadyToBeExecutedLocked(job)); 3376 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY, 3377 job.isReady()); 3378 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.ARE_USERS_STARTED, 3379 areUsersStartedLocked(job)); 3380 proto.write( 3381 JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_RESTRICTED, 3382 checkIfRestricted(job) != null); 3383 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_PENDING, 3384 mPendingJobs.contains(job)); 3385 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE, 3386 isCurrentlyActiveLocked(job)); 3387 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_UID_BACKING_UP, 3388 mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0); 3389 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_USABLE, 3390 isComponentUsable(job)); 3391 3392 for (JobRestriction restriction : mJobRestrictions) { 3393 final long restrictionsToken = proto.start( 3394 JobSchedulerServiceDumpProto.RegisteredJob.RESTRICTIONS); 3395 proto.write(JobSchedulerServiceDumpProto.JobRestriction.REASON, 3396 restriction.getReason()); 3397 proto.write(JobSchedulerServiceDumpProto.JobRestriction.IS_RESTRICTING, 3398 restriction.isJobRestricted(job)); 3399 proto.end(restrictionsToken); 3400 } 3401 3402 proto.end(rjToken); 3403 } 3404 } 3405 for (StateController controller : mControllers) { 3406 controller.dumpControllerStateLocked( 3407 proto, JobSchedulerServiceDumpProto.CONTROLLERS, predicate); 3408 } 3409 for (int i=0; i< mUidPriorityOverride.size(); i++) { 3410 int uid = mUidPriorityOverride.keyAt(i); 3411 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) { 3412 long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES); 3413 proto.write(JobSchedulerServiceDumpProto.PriorityOverride.UID, uid); 3414 proto.write(JobSchedulerServiceDumpProto.PriorityOverride.OVERRIDE_VALUE, 3415 mUidPriorityOverride.valueAt(i)); 3416 proto.end(pToken); 3417 } 3418 } 3419 for (int i = 0; i < mBackingUpUids.size(); i++) { 3420 int uid = mBackingUpUids.keyAt(i); 3421 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) { 3422 proto.write(JobSchedulerServiceDumpProto.BACKING_UP_UIDS, uid); 3423 } 3424 } 3425 3426 mJobPackageTracker.dump(proto, JobSchedulerServiceDumpProto.PACKAGE_TRACKER, 3427 filterUidFinal); 3428 mJobPackageTracker.dumpHistory(proto, JobSchedulerServiceDumpProto.HISTORY, 3429 filterUidFinal); 3430 3431 for (JobStatus job : mPendingJobs) { 3432 final long pjToken = proto.start(JobSchedulerServiceDumpProto.PENDING_JOBS); 3433 3434 job.writeToShortProto(proto, PendingJob.INFO); 3435 job.dump(proto, PendingJob.DUMP, false, nowElapsed); 3436 proto.write(PendingJob.EVALUATED_PRIORITY, evaluateJobPriorityLocked(job)); 3437 proto.write(PendingJob.PENDING_DURATION_MS, nowUptime - job.madePending); 3438 3439 proto.end(pjToken); 3440 } 3441 for (JobServiceContext jsc : mActiveServices) { 3442 final long ajToken = proto.start(JobSchedulerServiceDumpProto.ACTIVE_JOBS); 3443 final JobStatus job = jsc.getRunningJobLocked(); 3444 3445 if (job == null) { 3446 final long ijToken = proto.start(ActiveJob.INACTIVE); 3447 3448 proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS, 3449 nowElapsed - jsc.mStoppedTime); 3450 if (jsc.mStoppedReason != null) { 3451 proto.write(ActiveJob.InactiveJob.STOPPED_REASON, 3452 jsc.mStoppedReason); 3453 } 3454 3455 proto.end(ijToken); 3456 } else { 3457 final long rjToken = proto.start(ActiveJob.RUNNING); 3458 3459 job.writeToShortProto(proto, ActiveJob.RunningJob.INFO); 3460 3461 proto.write(ActiveJob.RunningJob.RUNNING_DURATION_MS, 3462 nowElapsed - jsc.getExecutionStartTimeElapsed()); 3463 proto.write(ActiveJob.RunningJob.TIME_UNTIL_TIMEOUT_MS, 3464 jsc.getTimeoutElapsed() - nowElapsed); 3465 3466 job.dump(proto, ActiveJob.RunningJob.DUMP, false, nowElapsed); 3467 3468 proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY, 3469 evaluateJobPriorityLocked(jsc.getRunningJobLocked())); 3470 3471 proto.write(ActiveJob.RunningJob.TIME_SINCE_MADE_ACTIVE_MS, 3472 nowUptime - job.madeActive); 3473 proto.write(ActiveJob.RunningJob.PENDING_DURATION_MS, 3474 job.madeActive - job.madePending); 3475 3476 proto.end(rjToken); 3477 } 3478 proto.end(ajToken); 3479 } 3480 if (filterUid == -1) { 3481 proto.write(JobSchedulerServiceDumpProto.IS_READY_TO_ROCK, mReadyToRock); 3482 proto.write(JobSchedulerServiceDumpProto.REPORTED_ACTIVE, mReportedActive); 3483 } 3484 mConcurrencyManager.dumpProtoLocked(proto, 3485 JobSchedulerServiceDumpProto.CONCURRENCY_MANAGER, now, nowElapsed); 3486 3487 mJobs.getPersistStats().dumpDebug(proto, JobSchedulerServiceDumpProto.PERSIST_STATS); 3488 } 3489 3490 proto.flush(); 3491 } 3492 } 3493