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.Manifest.permission.INTERACT_ACROSS_USERS_FULL; 20 import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS; 21 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; 22 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; 23 import static android.text.format.DateUtils.HOUR_IN_MILLIS; 24 import static android.text.format.DateUtils.MINUTE_IN_MILLIS; 25 26 import android.Manifest; 27 import android.annotation.EnforcePermission; 28 import android.annotation.NonNull; 29 import android.annotation.Nullable; 30 import android.annotation.UserIdInt; 31 import android.app.Activity; 32 import android.app.ActivityManager; 33 import android.app.ActivityManagerInternal; 34 import android.app.AppGlobals; 35 import android.app.IUidObserver; 36 import android.app.UidObserver; 37 import android.app.compat.CompatChanges; 38 import android.app.job.IJobScheduler; 39 import android.app.job.IUserVisibleJobObserver; 40 import android.app.job.JobInfo; 41 import android.app.job.JobParameters; 42 import android.app.job.JobProtoEnums; 43 import android.app.job.JobScheduler; 44 import android.app.job.JobService; 45 import android.app.job.JobSnapshot; 46 import android.app.job.JobWorkItem; 47 import android.app.job.UserVisibleJobSummary; 48 import android.app.tare.EconomyManager; 49 import android.app.usage.UsageStatsManager; 50 import android.app.usage.UsageStatsManagerInternal; 51 import android.compat.annotation.ChangeId; 52 import android.compat.annotation.EnabledAfter; 53 import android.content.BroadcastReceiver; 54 import android.content.ComponentName; 55 import android.content.Context; 56 import android.content.Intent; 57 import android.content.IntentFilter; 58 import android.content.PermissionChecker; 59 import android.content.pm.ApplicationInfo; 60 import android.content.pm.IPackageManager; 61 import android.content.pm.PackageManager; 62 import android.content.pm.PackageManager.NameNotFoundException; 63 import android.content.pm.PackageManagerInternal; 64 import android.content.pm.ParceledListSlice; 65 import android.content.pm.ProviderInfo; 66 import android.content.pm.ServiceInfo; 67 import android.net.Network; 68 import android.net.Uri; 69 import android.os.BatteryManager; 70 import android.os.BatteryManagerInternal; 71 import android.os.BatteryStatsInternal; 72 import android.os.Binder; 73 import android.os.Build; 74 import android.os.Handler; 75 import android.os.LimitExceededException; 76 import android.os.Looper; 77 import android.os.Message; 78 import android.os.ParcelFileDescriptor; 79 import android.os.Process; 80 import android.os.RemoteCallbackList; 81 import android.os.RemoteException; 82 import android.os.SystemClock; 83 import android.os.UserHandle; 84 import android.os.WorkSource; 85 import android.os.storage.StorageManagerInternal; 86 import android.provider.DeviceConfig; 87 import android.provider.Settings; 88 import android.text.format.DateUtils; 89 import android.util.ArrayMap; 90 import android.util.ArraySet; 91 import android.util.IndentingPrintWriter; 92 import android.util.Log; 93 import android.util.Pair; 94 import android.util.Slog; 95 import android.util.SparseArray; 96 import android.util.SparseArrayMap; 97 import android.util.SparseBooleanArray; 98 import android.util.SparseIntArray; 99 import android.util.SparseSetArray; 100 import android.util.TimeUtils; 101 import android.util.proto.ProtoOutputStream; 102 103 import com.android.internal.annotations.GuardedBy; 104 import com.android.internal.annotations.VisibleForTesting; 105 import com.android.internal.os.SomeArgs; 106 import com.android.internal.util.ArrayUtils; 107 import com.android.internal.util.DumpUtils; 108 import com.android.internal.util.FrameworkStatsLog; 109 import com.android.modules.expresslog.Counter; 110 import com.android.modules.expresslog.Histogram; 111 import com.android.server.AppSchedulingModuleThread; 112 import com.android.server.AppStateTracker; 113 import com.android.server.AppStateTrackerImpl; 114 import com.android.server.DeviceIdleInternal; 115 import com.android.server.LocalServices; 116 import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob; 117 import com.android.server.job.controllers.BackgroundJobsController; 118 import com.android.server.job.controllers.BatteryController; 119 import com.android.server.job.controllers.ComponentController; 120 import com.android.server.job.controllers.ConnectivityController; 121 import com.android.server.job.controllers.ContentObserverController; 122 import com.android.server.job.controllers.DeviceIdleJobsController; 123 import com.android.server.job.controllers.FlexibilityController; 124 import com.android.server.job.controllers.IdleController; 125 import com.android.server.job.controllers.JobStatus; 126 import com.android.server.job.controllers.PrefetchController; 127 import com.android.server.job.controllers.QuotaController; 128 import com.android.server.job.controllers.RestrictingController; 129 import com.android.server.job.controllers.StateController; 130 import com.android.server.job.controllers.StorageController; 131 import com.android.server.job.controllers.TareController; 132 import com.android.server.job.controllers.TimeController; 133 import com.android.server.job.restrictions.JobRestriction; 134 import com.android.server.job.restrictions.ThermalStatusRestriction; 135 import com.android.server.pm.UserManagerInternal; 136 import com.android.server.tare.EconomyManagerInternal; 137 import com.android.server.tare.JobSchedulerEconomicPolicy; 138 import com.android.server.usage.AppStandbyInternal; 139 import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; 140 import com.android.server.utils.quota.Categorizer; 141 import com.android.server.utils.quota.Category; 142 import com.android.server.utils.quota.CountQuotaTracker; 143 144 import dalvik.annotation.optimization.NeverCompile; 145 146 import libcore.util.EmptyArray; 147 148 import java.io.FileDescriptor; 149 import java.io.PrintWriter; 150 import java.time.Clock; 151 import java.time.Instant; 152 import java.time.ZoneId; 153 import java.time.ZoneOffset; 154 import java.util.ArrayList; 155 import java.util.Arrays; 156 import java.util.Collections; 157 import java.util.Comparator; 158 import java.util.List; 159 import java.util.Map; 160 import java.util.Objects; 161 import java.util.concurrent.CountDownLatch; 162 import java.util.function.Consumer; 163 import java.util.function.Predicate; 164 165 /** 166 * Responsible for taking jobs representing work to be performed by a client app, and determining 167 * based on the criteria specified when that job should be run against the client application's 168 * endpoint. 169 * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing 170 * about constraints, or the state of active jobs. It receives callbacks from the various 171 * controllers and completed jobs and operates accordingly. 172 * 173 * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object. 174 * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}. 175 * 176 * @hide 177 */ 178 public class JobSchedulerService extends com.android.server.SystemService 179 implements StateChangedListener, JobCompletedListener { 180 public static final String TAG = "JobScheduler"; 181 public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 182 public static final boolean DEBUG_STANDBY = DEBUG || false; 183 184 /** The maximum number of jobs that we allow an app to schedule */ 185 private static final int MAX_JOBS_PER_APP = 150; 186 /** The number of the most recently completed jobs to keep track of for debugging purposes. */ 187 private static final int NUM_COMPLETED_JOB_HISTORY = 20; 188 189 /** 190 * Require the hosting job to specify a network constraint if the included 191 * {@link android.app.job.JobWorkItem} indicates network usage. 192 */ 193 @ChangeId 194 @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) 195 private static final long REQUIRE_NETWORK_CONSTRAINT_FOR_NETWORK_JOB_WORK_ITEMS = 241104082L; 196 197 /** 198 * Require the app to have the ACCESS_NETWORK_STATE permissions when scheduling 199 * a job with a connectivity constraint. 200 */ 201 @ChangeId 202 @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) 203 static final long REQUIRE_NETWORK_PERMISSIONS_FOR_CONNECTIVITY_JOBS = 271850009L; 204 205 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 206 public static Clock sSystemClock = Clock.systemUTC(); 207 208 private abstract static class MySimpleClock extends Clock { 209 private final ZoneId mZoneId; 210 MySimpleClock(ZoneId zoneId)211 MySimpleClock(ZoneId zoneId) { 212 this.mZoneId = zoneId; 213 } 214 215 @Override getZone()216 public ZoneId getZone() { 217 return mZoneId; 218 } 219 220 @Override withZone(ZoneId zone)221 public Clock withZone(ZoneId zone) { 222 return new MySimpleClock(zone) { 223 @Override 224 public long millis() { 225 return MySimpleClock.this.millis(); 226 } 227 }; 228 } 229 230 @Override millis()231 public abstract long millis(); 232 233 @Override instant()234 public Instant instant() { 235 return Instant.ofEpochMilli(millis()); 236 } 237 } 238 239 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 240 public static Clock sUptimeMillisClock = new MySimpleClock(ZoneOffset.UTC) { 241 @Override 242 public long millis() { 243 return SystemClock.uptimeMillis(); 244 } 245 }; 246 247 public static Clock sElapsedRealtimeClock = new MySimpleClock(ZoneOffset.UTC) { 248 @Override 249 public long millis() { 250 return SystemClock.elapsedRealtime(); 251 } 252 }; 253 254 /** Global local for all job scheduler state. */ 255 final Object mLock = new Object(); 256 /** Master list of jobs. */ 257 final JobStore mJobs; 258 private final CountDownLatch mJobStoreLoadedLatch; 259 /** Tracking the standby bucket state of each app */ 260 final StandbyTracker mStandbyTracker; 261 /** Tracking amount of time each package runs for. */ 262 final JobPackageTracker mJobPackageTracker = new JobPackageTracker(); 263 final JobConcurrencyManager mConcurrencyManager; 264 265 static final int MSG_CHECK_INDIVIDUAL_JOB = 0; 266 static final int MSG_CHECK_JOB = 1; 267 static final int MSG_STOP_JOB = 2; 268 static final int MSG_CHECK_JOB_GREEDY = 3; 269 static final int MSG_UID_STATE_CHANGED = 4; 270 static final int MSG_UID_GONE = 5; 271 static final int MSG_UID_ACTIVE = 6; 272 static final int MSG_UID_IDLE = 7; 273 static final int MSG_CHECK_CHANGED_JOB_LIST = 8; 274 static final int MSG_CHECK_MEDIA_EXEMPTION = 9; 275 static final int MSG_INFORM_OBSERVER_OF_ALL_USER_VISIBLE_JOBS = 10; 276 static final int MSG_INFORM_OBSERVERS_OF_USER_VISIBLE_JOB_CHANGE = 11; 277 278 /** List of controllers that will notify this service of updates to jobs. */ 279 final List<StateController> mControllers; 280 /** 281 * List of controllers that will apply to all jobs in the RESTRICTED bucket. This is a subset of 282 * {@link #mControllers}. 283 */ 284 private final List<RestrictingController> mRestrictiveControllers; 285 /** Need direct access to this for testing. */ 286 private final StorageController mStorageController; 287 /** Needed to get estimated transfer time. */ 288 private final ConnectivityController mConnectivityController; 289 /** Need directly for sending uid state changes */ 290 private final DeviceIdleJobsController mDeviceIdleJobsController; 291 /** Needed to get next estimated launch time. */ 292 private final PrefetchController mPrefetchController; 293 /** Needed to get remaining quota time. */ 294 private final QuotaController mQuotaController; 295 /** Needed to get max execution time and expedited-job allowance. */ 296 private final TareController mTareController; 297 /** 298 * List of restrictions. 299 * Note: do not add to or remove from this list at runtime except in the constructor, because we 300 * do not synchronize access to this list. 301 */ 302 private final List<JobRestriction> mJobRestrictions; 303 304 @GuardedBy("mLock") 305 private final BatteryStateTracker mBatteryStateTracker; 306 307 @GuardedBy("mLock") 308 private final SparseArray<String> mCloudMediaProviderPackages = new SparseArray<>(); 309 310 private final RemoteCallbackList<IUserVisibleJobObserver> mUserVisibleJobObservers = 311 new RemoteCallbackList<>(); 312 313 /** 314 * Cache of grant status of permissions, keyed by UID->PID->permission name. A missing value 315 * means the state has not been queried. 316 */ 317 @GuardedBy("mPermissionCache") 318 private final SparseArray<SparseArrayMap<String, Boolean>> mPermissionCache = 319 new SparseArray<>(); 320 321 private final CountQuotaTracker mQuotaTracker; 322 private static final String QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG = ".schedulePersisted()"; 323 private static final String QUOTA_TRACKER_SCHEDULE_LOGGED = 324 ".schedulePersisted out-of-quota logged"; 325 private static final String QUOTA_TRACKER_TIMEOUT_UIJ_TAG = "timeout-uij"; 326 private static final String QUOTA_TRACKER_TIMEOUT_EJ_TAG = "timeout-ej"; 327 private static final String QUOTA_TRACKER_TIMEOUT_REG_TAG = "timeout-reg"; 328 private static final String QUOTA_TRACKER_TIMEOUT_TOTAL_TAG = "timeout-total"; 329 private static final String QUOTA_TRACKER_ANR_TAG = "anr"; 330 private static final Category QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED = new Category( 331 ".schedulePersisted()"); 332 private static final Category QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED = new Category( 333 ".schedulePersisted out-of-quota logged"); 334 private static final Category QUOTA_TRACKER_CATEGORY_TIMEOUT_UIJ = 335 new Category(QUOTA_TRACKER_TIMEOUT_UIJ_TAG); 336 private static final Category QUOTA_TRACKER_CATEGORY_TIMEOUT_EJ = 337 new Category(QUOTA_TRACKER_TIMEOUT_EJ_TAG); 338 private static final Category QUOTA_TRACKER_CATEGORY_TIMEOUT_REG = 339 new Category(QUOTA_TRACKER_TIMEOUT_REG_TAG); 340 private static final Category QUOTA_TRACKER_CATEGORY_TIMEOUT_TOTAL = 341 new Category(QUOTA_TRACKER_TIMEOUT_TOTAL_TAG); 342 private static final Category QUOTA_TRACKER_CATEGORY_ANR = new Category(QUOTA_TRACKER_ANR_TAG); 343 private static final Category QUOTA_TRACKER_CATEGORY_DISABLED = new Category("disabled"); 344 345 /** 346 * Queue of pending jobs. The JobServiceContext class will receive jobs from this list 347 * when ready to execute them. 348 */ 349 private final PendingJobQueue mPendingJobQueue = new PendingJobQueue(); 350 351 int[] mStartedUsers = EmptyArray.INT; 352 353 final JobHandler mHandler; 354 final JobSchedulerStub mJobSchedulerStub; 355 356 PackageManagerInternal mLocalPM; 357 ActivityManagerInternal mActivityManagerInternal; 358 DeviceIdleInternal mLocalDeviceIdleController; 359 @VisibleForTesting 360 AppStateTrackerImpl mAppStateTracker; 361 final UsageStatsManagerInternal mUsageStats; 362 private final AppStandbyInternal mAppStandbyInternal; 363 364 /** 365 * Set to true once we are allowed to run third party apps. 366 */ 367 boolean mReadyToRock; 368 369 /** 370 * What we last reported to DeviceIdleController about whether we are active. 371 */ 372 boolean mReportedActive; 373 374 /** 375 * Track the most recently completed jobs (that had been executing and were stopped for any 376 * reason, including successful completion). 377 */ 378 private int mLastCompletedJobIndex = 0; 379 private final JobStatus[] mLastCompletedJobs = new JobStatus[NUM_COMPLETED_JOB_HISTORY]; 380 private final long[] mLastCompletedJobTimeElapsed = new long[NUM_COMPLETED_JOB_HISTORY]; 381 382 /** 383 * Track the most recently cancelled jobs (that had internal reason 384 * {@link JobParameters#INTERNAL_STOP_REASON_CANCELED}. 385 */ 386 private int mLastCancelledJobIndex = 0; 387 private final JobStatus[] mLastCancelledJobs = 388 new JobStatus[DEBUG ? NUM_COMPLETED_JOB_HISTORY : 0]; 389 private final long[] mLastCancelledJobTimeElapsed = 390 new long[DEBUG ? NUM_COMPLETED_JOB_HISTORY : 0]; 391 392 private static final Histogram sEnqueuedJwiHighWaterMarkLogger = new Histogram( 393 "job_scheduler.value_hist_w_uid_enqueued_work_items_high_water_mark", 394 new Histogram.ScaledRangeOptions(25, 0, 5, 1.4f)); 395 private static final Histogram sInitialJobEstimatedNetworkDownloadKBLogger = new Histogram( 396 "job_scheduler.value_hist_initial_job_estimated_network_download_kilobytes", 397 new Histogram.ScaledRangeOptions(50, 0, 32 /* 32 KB */, 1.31f)); 398 private static final Histogram sInitialJwiEstimatedNetworkDownloadKBLogger = new Histogram( 399 "job_scheduler.value_hist_initial_jwi_estimated_network_download_kilobytes", 400 new Histogram.ScaledRangeOptions(50, 0, 32 /* 32 KB */, 1.31f)); 401 private static final Histogram sInitialJobEstimatedNetworkUploadKBLogger = new Histogram( 402 "job_scheduler.value_hist_initial_job_estimated_network_upload_kilobytes", 403 new Histogram.ScaledRangeOptions(50, 0, 32 /* 32 KB */, 1.31f)); 404 private static final Histogram sInitialJwiEstimatedNetworkUploadKBLogger = new Histogram( 405 "job_scheduler.value_hist_initial_jwi_estimated_network_upload_kilobytes", 406 new Histogram.ScaledRangeOptions(50, 0, 32 /* 32 KB */, 1.31f)); 407 private static final Histogram sJobMinimumChunkKBLogger = new Histogram( 408 "job_scheduler.value_hist_w_uid_job_minimum_chunk_kilobytes", 409 new Histogram.ScaledRangeOptions(25, 0, 5 /* 5 KB */, 1.76f)); 410 private static final Histogram sJwiMinimumChunkKBLogger = new Histogram( 411 "job_scheduler.value_hist_w_uid_jwi_minimum_chunk_kilobytes", 412 new Histogram.ScaledRangeOptions(25, 0, 5 /* 5 KB */, 1.76f)); 413 414 /** 415 * A mapping of which uids are currently in the foreground to their effective bias. 416 */ 417 final SparseIntArray mUidBiasOverride = new SparseIntArray(); 418 /** 419 * A cached mapping of uids to their current capabilities. 420 */ 421 @GuardedBy("mLock") 422 private final SparseIntArray mUidCapabilities = new SparseIntArray(); 423 /** 424 * A cached mapping of uids to their proc states. 425 */ 426 @GuardedBy("mLock") 427 private final SparseIntArray mUidProcStates = new SparseIntArray(); 428 429 /** 430 * Which uids are currently performing backups, so we shouldn't allow their jobs to run. 431 */ 432 private final SparseBooleanArray mBackingUpUids = new SparseBooleanArray(); 433 434 /** 435 * Cache of debuggable app status. 436 */ 437 final ArrayMap<String, Boolean> mDebuggableApps = new ArrayMap<>(); 438 439 /** Cached mapping of UIDs (for all users) to a list of packages in the UID. */ 440 private final SparseSetArray<String> mUidToPackageCache = new SparseSetArray<>(); 441 442 /** List of jobs whose controller state has changed since the last time we evaluated the job. */ 443 @GuardedBy("mLock") 444 private final ArraySet<JobStatus> mChangedJobList = new ArraySet<>(); 445 446 /** 447 * Cached pending job reasons. Mapping from UID -> namespace -> job ID -> reason. 448 */ 449 @GuardedBy("mPendingJobReasonCache") // Use its own lock to avoid blocking JS processing 450 private final SparseArrayMap<String, SparseIntArray> mPendingJobReasonCache = 451 new SparseArrayMap<>(); 452 453 /** 454 * Named indices into standby bucket arrays, for clarity in referring to 455 * specific buckets' bookkeeping. 456 */ 457 public static final int ACTIVE_INDEX = 0; 458 public static final int WORKING_INDEX = 1; 459 public static final int FREQUENT_INDEX = 2; 460 public static final int RARE_INDEX = 3; 461 public static final int NEVER_INDEX = 4; 462 // Putting RESTRICTED_INDEX after NEVER_INDEX to make it easier for proto dumping 463 // (ScheduledJobStateChanged and JobStatusDumpProto). 464 public static final int RESTRICTED_INDEX = 5; 465 // Putting EXEMPTED_INDEX after RESTRICTED_INDEX to make it easier for proto dumping 466 // (ScheduledJobStateChanged and JobStatusDumpProto). 467 public static final int EXEMPTED_INDEX = 6; 468 469 private class ConstantsObserver implements DeviceConfig.OnPropertiesChangedListener, 470 EconomyManagerInternal.TareStateChangeListener { 471 public void start() { 472 DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_JOB_SCHEDULER, 473 AppSchedulingModuleThread.getExecutor(), this); 474 final EconomyManagerInternal economyManagerInternal = 475 LocalServices.getService(EconomyManagerInternal.class); 476 economyManagerInternal 477 .registerTareStateChangeListener(this, JobSchedulerEconomicPolicy.POLICY_JOB); 478 // Load all the constants. 479 synchronized (mLock) { 480 mConstants.updateTareSettingsLocked( 481 economyManagerInternal.getEnabledMode( 482 JobSchedulerEconomicPolicy.POLICY_JOB)); 483 } 484 onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER)); 485 } 486 487 @Override 488 public void onPropertiesChanged(DeviceConfig.Properties properties) { 489 boolean apiQuotaScheduleUpdated = false; 490 boolean concurrencyUpdated = false; 491 boolean persistenceUpdated = false; 492 boolean runtimeUpdated = false; 493 for (int controller = 0; controller < mControllers.size(); controller++) { 494 final StateController sc = mControllers.get(controller); 495 sc.prepareForUpdatedConstantsLocked(); 496 } 497 498 synchronized (mLock) { 499 for (String name : properties.getKeyset()) { 500 if (name == null) { 501 continue; 502 } 503 switch (name) { 504 case Constants.KEY_ENABLE_API_QUOTAS: 505 case Constants.KEY_ENABLE_EXECUTION_SAFEGUARDS_UDC: 506 case Constants.KEY_API_QUOTA_SCHEDULE_COUNT: 507 case Constants.KEY_API_QUOTA_SCHEDULE_WINDOW_MS: 508 case Constants.KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT: 509 case Constants.KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION: 510 case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT: 511 case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT: 512 case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT: 513 case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT: 514 case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS: 515 case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT: 516 case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS: 517 if (!apiQuotaScheduleUpdated) { 518 mConstants.updateApiQuotaConstantsLocked(); 519 updateQuotaTracker(); 520 apiQuotaScheduleUpdated = true; 521 } 522 break; 523 case Constants.KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT: 524 case Constants.KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS: 525 mConstants.updateBatchingConstantsLocked(); 526 break; 527 case Constants.KEY_HEAVY_USE_FACTOR: 528 case Constants.KEY_MODERATE_USE_FACTOR: 529 mConstants.updateUseFactorConstantsLocked(); 530 break; 531 case Constants.KEY_MIN_LINEAR_BACKOFF_TIME_MS: 532 case Constants.KEY_MIN_EXP_BACKOFF_TIME_MS: 533 case Constants.KEY_SYSTEM_STOP_TO_FAILURE_RATIO: 534 mConstants.updateBackoffConstantsLocked(); 535 break; 536 case Constants.KEY_CONN_CONGESTION_DELAY_FRAC: 537 case Constants.KEY_CONN_PREFETCH_RELAX_FRAC: 538 case Constants.KEY_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC: 539 case Constants.KEY_CONN_USE_CELL_SIGNAL_STRENGTH: 540 case Constants.KEY_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS: 541 mConstants.updateConnectivityConstantsLocked(); 542 break; 543 case Constants.KEY_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS: 544 mConstants.updatePrefetchConstantsLocked(); 545 break; 546 case Constants.KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS: 547 case Constants.KEY_RUNTIME_MIN_GUARANTEE_MS: 548 case Constants.KEY_RUNTIME_MIN_EJ_GUARANTEE_MS: 549 case Constants.KEY_RUNTIME_MIN_UI_GUARANTEE_MS: 550 case Constants.KEY_RUNTIME_UI_LIMIT_MS: 551 case Constants.KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR: 552 case Constants.KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS: 553 case Constants.KEY_RUNTIME_CUMULATIVE_UI_LIMIT_MS: 554 case Constants.KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS: 555 if (!runtimeUpdated) { 556 mConstants.updateRuntimeConstantsLocked(); 557 runtimeUpdated = true; 558 } 559 break; 560 case Constants.KEY_MAX_NUM_PERSISTED_JOB_WORK_ITEMS: 561 case Constants.KEY_PERSIST_IN_SPLIT_FILES: 562 if (!persistenceUpdated) { 563 mConstants.updatePersistingConstantsLocked(); 564 mJobs.setUseSplitFiles(mConstants.PERSIST_IN_SPLIT_FILES); 565 persistenceUpdated = true; 566 } 567 break; 568 default: 569 if (name.startsWith(JobConcurrencyManager.CONFIG_KEY_PREFIX_CONCURRENCY) 570 && !concurrencyUpdated) { 571 mConcurrencyManager.updateConfigLocked(); 572 concurrencyUpdated = true; 573 } else { 574 for (int ctrlr = 0; ctrlr < mControllers.size(); ctrlr++) { 575 final StateController sc = mControllers.get(ctrlr); 576 sc.processConstantLocked(properties, name); 577 } 578 } 579 break; 580 } 581 } 582 for (int controller = 0; controller < mControllers.size(); controller++) { 583 final StateController sc = mControllers.get(controller); 584 sc.onConstantsUpdatedLocked(); 585 } 586 } 587 } 588 589 @Override 590 public void onTareEnabledModeChanged(@EconomyManager.EnabledMode int enabledMode) { 591 if (mConstants.updateTareSettingsLocked(enabledMode)) { 592 for (int controller = 0; controller < mControllers.size(); controller++) { 593 final StateController sc = mControllers.get(controller); 594 sc.onConstantsUpdatedLocked(); 595 } 596 onControllerStateChanged(null); 597 } 598 } 599 } 600 601 @VisibleForTesting 602 void updateQuotaTracker() { 603 mQuotaTracker.setEnabled( 604 mConstants.ENABLE_API_QUOTAS || mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC); 605 mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED, 606 mConstants.API_QUOTA_SCHEDULE_COUNT, 607 mConstants.API_QUOTA_SCHEDULE_WINDOW_MS); 608 mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_TIMEOUT_UIJ, 609 mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT, 610 mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS); 611 mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_TIMEOUT_EJ, 612 mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT, 613 mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS); 614 mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_TIMEOUT_REG, 615 mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT, 616 mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS); 617 mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_TIMEOUT_TOTAL, 618 mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT, 619 mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS); 620 mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_ANR, 621 mConstants.EXECUTION_SAFEGUARDS_UDC_ANR_COUNT, 622 mConstants.EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS); 623 } 624 625 /** 626 * All times are in milliseconds. Any access to this class or its fields should be done while 627 * holding the JobSchedulerService.mLock lock. 628 */ 629 public static class Constants { 630 // Key names stored in the settings value. 631 private static final String KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT = 632 "min_ready_non_active_jobs_count"; 633 private static final String KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = 634 "max_non_active_job_batch_delay_ms"; 635 private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor"; 636 private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor"; 637 638 private static final String KEY_MIN_LINEAR_BACKOFF_TIME_MS = "min_linear_backoff_time_ms"; 639 private static final String KEY_MIN_EXP_BACKOFF_TIME_MS = "min_exp_backoff_time_ms"; 640 private static final String KEY_SYSTEM_STOP_TO_FAILURE_RATIO = 641 "system_stop_to_failure_ratio"; 642 private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac"; 643 private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac"; 644 private static final String KEY_CONN_USE_CELL_SIGNAL_STRENGTH = 645 "conn_use_cell_signal_strength"; 646 private static final String KEY_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS = 647 "conn_update_all_jobs_min_interval_ms"; 648 private static final String KEY_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC = 649 "conn_low_signal_strength_relax_frac"; 650 private static final String KEY_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS = 651 "prefetch_force_batch_relax_threshold_ms"; 652 // This has been enabled for 3+ full releases. We're unlikely to disable it. 653 // TODO(141645789): remove this flag 654 private static final String KEY_ENABLE_API_QUOTAS = "enable_api_quotas"; 655 private static final String KEY_API_QUOTA_SCHEDULE_COUNT = "aq_schedule_count"; 656 private static final String KEY_API_QUOTA_SCHEDULE_WINDOW_MS = "aq_schedule_window_ms"; 657 private static final String KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION = 658 "aq_schedule_throw_exception"; 659 private static final String KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = 660 "aq_schedule_return_failure"; 661 private static final String KEY_ENABLE_EXECUTION_SAFEGUARDS_UDC = 662 "enable_execution_safeguards_udc"; 663 private static final String KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 664 "es_u_timeout_uij_count"; 665 private static final String KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = 666 "es_u_timeout_ej_count"; 667 private static final String KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = 668 "es_u_timeout_reg_count"; 669 private static final String KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT = 670 "es_u_timeout_total_count"; 671 private static final String KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS = 672 "es_u_timeout_window_ms"; 673 private static final String KEY_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT = 674 "es_u_anr_count"; 675 private static final String KEY_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS = 676 "es_u_anr_window_ms"; 677 678 private static final String KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS = 679 "runtime_free_quota_max_limit_ms"; 680 private static final String KEY_RUNTIME_MIN_GUARANTEE_MS = "runtime_min_guarantee_ms"; 681 private static final String KEY_RUNTIME_MIN_EJ_GUARANTEE_MS = "runtime_min_ej_guarantee_ms"; 682 private static final String KEY_RUNTIME_MIN_UI_GUARANTEE_MS = "runtime_min_ui_guarantee_ms"; 683 private static final String KEY_RUNTIME_UI_LIMIT_MS = "runtime_ui_limit_ms"; 684 private static final String KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR = 685 "runtime_min_ui_data_transfer_guarantee_buffer_factor"; 686 private static final String KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS = 687 "runtime_min_ui_data_transfer_guarantee_ms"; 688 private static final String KEY_RUNTIME_CUMULATIVE_UI_LIMIT_MS = 689 "runtime_cumulative_ui_limit_ms"; 690 private static final String KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS = 691 "runtime_use_data_estimates_for_limits"; 692 693 private static final String KEY_PERSIST_IN_SPLIT_FILES = "persist_in_split_files"; 694 695 private static final String KEY_MAX_NUM_PERSISTED_JOB_WORK_ITEMS = 696 "max_num_persisted_job_work_items"; 697 698 private static final int DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT = 5; 699 private static final long DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = 31 * MINUTE_IN_MILLIS; 700 private static final float DEFAULT_HEAVY_USE_FACTOR = .9f; 701 private static final float DEFAULT_MODERATE_USE_FACTOR = .5f; 702 private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME_MS = JobInfo.MIN_BACKOFF_MILLIS; 703 private static final long DEFAULT_MIN_EXP_BACKOFF_TIME_MS = JobInfo.MIN_BACKOFF_MILLIS; 704 private static final int DEFAULT_SYSTEM_STOP_TO_FAILURE_RATIO = 3; 705 private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f; 706 private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f; 707 private static final boolean DEFAULT_CONN_USE_CELL_SIGNAL_STRENGTH = true; 708 private static final long DEFAULT_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS = MINUTE_IN_MILLIS; 709 private static final float DEFAULT_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC = 0.5f; 710 private static final long DEFAULT_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS = HOUR_IN_MILLIS; 711 private static final boolean DEFAULT_ENABLE_API_QUOTAS = true; 712 private static final int DEFAULT_API_QUOTA_SCHEDULE_COUNT = 250; 713 private static final long DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS = MINUTE_IN_MILLIS; 714 private static final boolean DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION = true; 715 private static final boolean DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false; 716 private static final boolean DEFAULT_ENABLE_EXECUTION_SAFEGUARDS_UDC = true; 717 private static final int DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 2; 718 // EJs have a shorter timeout, so set a higher limit for them to start with. 719 private static final int DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = 5; 720 private static final int DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = 3; 721 private static final int DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT = 10; 722 private static final long DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS = 723 24 * HOUR_IN_MILLIS; 724 private static final int DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT = 3; 725 private static final long DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS = 726 6 * HOUR_IN_MILLIS; 727 @VisibleForTesting 728 public static final long DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS = 30 * MINUTE_IN_MILLIS; 729 @VisibleForTesting 730 public static final long DEFAULT_RUNTIME_MIN_GUARANTEE_MS = 10 * MINUTE_IN_MILLIS; 731 @VisibleForTesting 732 public static final long DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS = 3 * MINUTE_IN_MILLIS; 733 public static final long DEFAULT_RUNTIME_MIN_UI_GUARANTEE_MS = 734 Math.max(6 * HOUR_IN_MILLIS, DEFAULT_RUNTIME_MIN_GUARANTEE_MS); 735 public static final long DEFAULT_RUNTIME_UI_LIMIT_MS = 736 Math.max(12 * HOUR_IN_MILLIS, DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS); 737 public static final float DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR = 738 1.35f; 739 public static final long DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS = 740 Math.max(10 * MINUTE_IN_MILLIS, DEFAULT_RUNTIME_MIN_UI_GUARANTEE_MS); 741 public static final long DEFAULT_RUNTIME_CUMULATIVE_UI_LIMIT_MS = 24 * HOUR_IN_MILLIS; 742 public static final boolean DEFAULT_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS = false; 743 static final boolean DEFAULT_PERSIST_IN_SPLIT_FILES = true; 744 static final int DEFAULT_MAX_NUM_PERSISTED_JOB_WORK_ITEMS = 100_000; 745 746 /** 747 * Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early. 748 */ 749 int MIN_READY_NON_ACTIVE_JOBS_COUNT = DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT; 750 751 /** 752 * Don't batch a non-ACTIVE job if it's been delayed due to force batching attempts for 753 * at least this amount of time. 754 */ 755 long MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS; 756 757 /** 758 * This is the job execution factor that is considered to be heavy use of the system. 759 */ 760 float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR; 761 /** 762 * This is the job execution factor that is considered to be moderate use of the system. 763 */ 764 float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR; 765 766 /** 767 * The minimum backoff time to allow for linear backoff. 768 */ 769 long MIN_LINEAR_BACKOFF_TIME_MS = DEFAULT_MIN_LINEAR_BACKOFF_TIME_MS; 770 /** 771 * The minimum backoff time to allow for exponential backoff. 772 */ 773 long MIN_EXP_BACKOFF_TIME_MS = DEFAULT_MIN_EXP_BACKOFF_TIME_MS; 774 /** 775 * The ratio to use to convert number of times a job was stopped by JobScheduler to an 776 * incremental failure in the backoff policy calculation. 777 */ 778 int SYSTEM_STOP_TO_FAILURE_RATIO = DEFAULT_SYSTEM_STOP_TO_FAILURE_RATIO; 779 780 /** 781 * The fraction of a job's running window that must pass before we 782 * consider running it when the network is congested. 783 */ 784 public float CONN_CONGESTION_DELAY_FRAC = DEFAULT_CONN_CONGESTION_DELAY_FRAC; 785 /** 786 * The fraction of a prefetch job's running window that must pass before 787 * we consider matching it against a metered network. 788 */ 789 public float CONN_PREFETCH_RELAX_FRAC = DEFAULT_CONN_PREFETCH_RELAX_FRAC; 790 /** 791 * Whether to use the cell signal strength to determine if a particular job is eligible to 792 * run. 793 */ 794 public boolean CONN_USE_CELL_SIGNAL_STRENGTH = DEFAULT_CONN_USE_CELL_SIGNAL_STRENGTH; 795 /** 796 * When throttling updating all tracked jobs, make sure not to update them more frequently 797 * than this value. 798 */ 799 public long CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS = 800 DEFAULT_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS; 801 /** 802 * The fraction of a job's running window that must pass before we consider running it on 803 * low signal strength networks. 804 */ 805 public float CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC = 806 DEFAULT_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC; 807 808 /** 809 * The amount of time within which we would consider the app to be launching relatively soon 810 * and will relax the force batching policy on prefetch jobs. If the app is not going to be 811 * launched within this amount of time from now, then we will force batch the prefetch job. 812 */ 813 public long PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS = 814 DEFAULT_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS; 815 816 /** 817 * Whether to enable quota limits on APIs. 818 */ 819 public boolean ENABLE_API_QUOTAS = DEFAULT_ENABLE_API_QUOTAS; 820 /** 821 * The maximum number of schedule() calls an app can make in a set amount of time. 822 */ 823 public int API_QUOTA_SCHEDULE_COUNT = DEFAULT_API_QUOTA_SCHEDULE_COUNT; 824 /** 825 * The time window that {@link #API_QUOTA_SCHEDULE_COUNT} should be evaluated over. 826 */ 827 public long API_QUOTA_SCHEDULE_WINDOW_MS = DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS; 828 /** 829 * Whether to throw an exception when an app hits its schedule quota limit. 830 */ 831 public boolean API_QUOTA_SCHEDULE_THROW_EXCEPTION = 832 DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION; 833 /** 834 * Whether or not to return a failure result when an app hits its schedule quota limit. 835 */ 836 public boolean API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = 837 DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT; 838 839 /** 840 * Whether to enable the execution safeguards added in UDC. 841 */ 842 public boolean ENABLE_EXECUTION_SAFEGUARDS_UDC = DEFAULT_ENABLE_EXECUTION_SAFEGUARDS_UDC; 843 /** 844 * The maximum number of times an app can have a user-iniated job time out before the system 845 * begins removing some of the app's privileges. 846 */ 847 public int EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 848 DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT; 849 /** 850 * The maximum number of times an app can have an expedited job time out before the system 851 * begins removing some of the app's privileges. 852 */ 853 public int EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = 854 DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT; 855 /** 856 * The maximum number of times an app can have a regular job time out before the system 857 * begins removing some of the app's privileges. 858 */ 859 public int EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = 860 DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT; 861 /** 862 * The maximum number of times an app can have jobs time out before the system 863 * attempts to restrict most of the app's privileges. 864 */ 865 public int EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT = 866 DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT; 867 /** 868 * The time window that {@link #EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT}, 869 * {@link #EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT}, 870 * {@link #EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT}, and 871 * {@link #EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT} should be evaluated over. 872 */ 873 public long EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS = 874 DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS; 875 876 /** 877 * The maximum number of times an app can ANR from JobScheduler's perspective before 878 * JobScheduler will attempt to restrict the app. 879 */ 880 public int EXECUTION_SAFEGUARDS_UDC_ANR_COUNT = DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT; 881 /** 882 * The time window that {@link #EXECUTION_SAFEGUARDS_UDC_ANR_COUNT} 883 * should be evaluated over. 884 */ 885 public long EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS = 886 DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS; 887 888 /** The maximum amount of time we will let a job run for when quota is "free". */ 889 public long RUNTIME_FREE_QUOTA_MAX_LIMIT_MS = DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; 890 891 /** 892 * The minimum amount of time we try to guarantee regular jobs will run for. 893 */ 894 public long RUNTIME_MIN_GUARANTEE_MS = DEFAULT_RUNTIME_MIN_GUARANTEE_MS; 895 896 /** 897 * The minimum amount of time we try to guarantee EJs will run for. 898 */ 899 public long RUNTIME_MIN_EJ_GUARANTEE_MS = DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS; 900 901 /** 902 * The minimum amount of time we try to guarantee normal user-initiated jobs will run for. 903 */ 904 public long RUNTIME_MIN_UI_GUARANTEE_MS = DEFAULT_RUNTIME_MIN_UI_GUARANTEE_MS; 905 906 /** 907 * The maximum amount of time we will let a user-initiated job run for. This will only 908 * apply if there are no other limits that apply to the specific user-initiated job. 909 */ 910 public long RUNTIME_UI_LIMIT_MS = DEFAULT_RUNTIME_UI_LIMIT_MS; 911 912 /** 913 * A factor to apply to estimated transfer durations for user-initiated data transfer jobs 914 * so that we give some extra time for unexpected situations. This will be at least 1 and 915 * so can just be multiplied with the original value to get the final value. 916 */ 917 public float RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR = 918 DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR; 919 920 /** 921 * The minimum amount of time we try to guarantee user-initiated data transfer jobs 922 * will run for. This is only considered when using data estimates to calculate 923 * execution limits. 924 */ 925 public long RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS = 926 DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS; 927 928 /** 929 * The maximum amount of cumulative time we will let a user-initiated job run for 930 * before downgrading it. 931 */ 932 public long RUNTIME_CUMULATIVE_UI_LIMIT_MS = DEFAULT_RUNTIME_CUMULATIVE_UI_LIMIT_MS; 933 934 /** 935 * Whether to use data estimates to determine execution limits for execution limits. 936 */ 937 public boolean RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS = 938 DEFAULT_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS; 939 940 /** 941 * Whether to persist jobs in split files (by UID). If false, all persisted jobs will be 942 * saved in a single file. 943 */ 944 public boolean PERSIST_IN_SPLIT_FILES = DEFAULT_PERSIST_IN_SPLIT_FILES; 945 946 /** 947 * The maximum number of {@link JobWorkItem JobWorkItems} that can be persisted per job. 948 */ 949 public int MAX_NUM_PERSISTED_JOB_WORK_ITEMS = DEFAULT_MAX_NUM_PERSISTED_JOB_WORK_ITEMS; 950 951 /** 952 * If true, use TARE policy for job limiting. If false, use quotas. 953 */ 954 public boolean USE_TARE_POLICY = EconomyManager.DEFAULT_ENABLE_POLICY_JOB_SCHEDULER 955 && EconomyManager.DEFAULT_ENABLE_TARE_MODE == EconomyManager.ENABLED_MODE_ON; 956 957 private void updateBatchingConstantsLocked() { 958 MIN_READY_NON_ACTIVE_JOBS_COUNT = DeviceConfig.getInt( 959 DeviceConfig.NAMESPACE_JOB_SCHEDULER, 960 KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT, 961 DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT); 962 MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = DeviceConfig.getLong( 963 DeviceConfig.NAMESPACE_JOB_SCHEDULER, 964 KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS, 965 DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS); 966 } 967 968 private void updateUseFactorConstantsLocked() { 969 HEAVY_USE_FACTOR = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_JOB_SCHEDULER, 970 KEY_HEAVY_USE_FACTOR, 971 DEFAULT_HEAVY_USE_FACTOR); 972 MODERATE_USE_FACTOR = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_JOB_SCHEDULER, 973 KEY_MODERATE_USE_FACTOR, 974 DEFAULT_MODERATE_USE_FACTOR); 975 } 976 977 private void updateBackoffConstantsLocked() { 978 MIN_LINEAR_BACKOFF_TIME_MS = DeviceConfig.getLong(DeviceConfig.NAMESPACE_JOB_SCHEDULER, 979 KEY_MIN_LINEAR_BACKOFF_TIME_MS, 980 DEFAULT_MIN_LINEAR_BACKOFF_TIME_MS); 981 MIN_EXP_BACKOFF_TIME_MS = DeviceConfig.getLong(DeviceConfig.NAMESPACE_JOB_SCHEDULER, 982 KEY_MIN_EXP_BACKOFF_TIME_MS, 983 DEFAULT_MIN_EXP_BACKOFF_TIME_MS); 984 SYSTEM_STOP_TO_FAILURE_RATIO = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, 985 KEY_SYSTEM_STOP_TO_FAILURE_RATIO, 986 DEFAULT_SYSTEM_STOP_TO_FAILURE_RATIO); 987 } 988 989 private void updateConnectivityConstantsLocked() { 990 CONN_CONGESTION_DELAY_FRAC = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_JOB_SCHEDULER, 991 KEY_CONN_CONGESTION_DELAY_FRAC, 992 DEFAULT_CONN_CONGESTION_DELAY_FRAC); 993 CONN_PREFETCH_RELAX_FRAC = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_JOB_SCHEDULER, 994 KEY_CONN_PREFETCH_RELAX_FRAC, 995 DEFAULT_CONN_PREFETCH_RELAX_FRAC); 996 CONN_USE_CELL_SIGNAL_STRENGTH = DeviceConfig.getBoolean( 997 DeviceConfig.NAMESPACE_JOB_SCHEDULER, 998 KEY_CONN_USE_CELL_SIGNAL_STRENGTH, 999 DEFAULT_CONN_USE_CELL_SIGNAL_STRENGTH); 1000 CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS = DeviceConfig.getLong( 1001 DeviceConfig.NAMESPACE_JOB_SCHEDULER, 1002 KEY_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS, 1003 DEFAULT_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS); 1004 CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC = DeviceConfig.getFloat( 1005 DeviceConfig.NAMESPACE_JOB_SCHEDULER, 1006 KEY_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC, 1007 DEFAULT_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC); 1008 } 1009 1010 private void updatePersistingConstantsLocked() { 1011 PERSIST_IN_SPLIT_FILES = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_JOB_SCHEDULER, 1012 KEY_PERSIST_IN_SPLIT_FILES, DEFAULT_PERSIST_IN_SPLIT_FILES); 1013 MAX_NUM_PERSISTED_JOB_WORK_ITEMS = DeviceConfig.getInt( 1014 DeviceConfig.NAMESPACE_JOB_SCHEDULER, 1015 KEY_MAX_NUM_PERSISTED_JOB_WORK_ITEMS, 1016 DEFAULT_MAX_NUM_PERSISTED_JOB_WORK_ITEMS); 1017 } 1018 1019 private void updatePrefetchConstantsLocked() { 1020 PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS = DeviceConfig.getLong( 1021 DeviceConfig.NAMESPACE_JOB_SCHEDULER, 1022 KEY_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS, 1023 DEFAULT_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS); 1024 } 1025 1026 private void updateApiQuotaConstantsLocked() { 1027 ENABLE_API_QUOTAS = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_JOB_SCHEDULER, 1028 KEY_ENABLE_API_QUOTAS, DEFAULT_ENABLE_API_QUOTAS); 1029 ENABLE_EXECUTION_SAFEGUARDS_UDC = DeviceConfig.getBoolean( 1030 DeviceConfig.NAMESPACE_JOB_SCHEDULER, 1031 KEY_ENABLE_EXECUTION_SAFEGUARDS_UDC, DEFAULT_ENABLE_EXECUTION_SAFEGUARDS_UDC); 1032 // Set a minimum value on the quota limit so it's not so low that it interferes with 1033 // legitimate use cases. 1034 API_QUOTA_SCHEDULE_COUNT = Math.max(250, 1035 DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, 1036 KEY_API_QUOTA_SCHEDULE_COUNT, DEFAULT_API_QUOTA_SCHEDULE_COUNT)); 1037 API_QUOTA_SCHEDULE_WINDOW_MS = DeviceConfig.getLong( 1038 DeviceConfig.NAMESPACE_JOB_SCHEDULER, 1039 KEY_API_QUOTA_SCHEDULE_WINDOW_MS, DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS); 1040 API_QUOTA_SCHEDULE_THROW_EXCEPTION = DeviceConfig.getBoolean( 1041 DeviceConfig.NAMESPACE_JOB_SCHEDULER, 1042 KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION, 1043 DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION); 1044 API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = DeviceConfig.getBoolean( 1045 DeviceConfig.NAMESPACE_JOB_SCHEDULER, 1046 KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT, 1047 DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT); 1048 1049 // Set a minimum value on the timeout limit so it's not so low that it interferes with 1050 // legitimate use cases. 1051 EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = Math.max(2, 1052 DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, 1053 KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT, 1054 DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT)); 1055 EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = Math.max(2, 1056 DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, 1057 KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT, 1058 DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT)); 1059 EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = Math.max(2, 1060 DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, 1061 KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT, 1062 DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT)); 1063 final int highestTimeoutCount = Math.max(EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT, 1064 Math.max(EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT, 1065 EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT)); 1066 EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT = Math.max(highestTimeoutCount, 1067 DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, 1068 KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT, 1069 DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT)); 1070 EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS = DeviceConfig.getLong( 1071 DeviceConfig.NAMESPACE_JOB_SCHEDULER, 1072 KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS, 1073 DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS); 1074 EXECUTION_SAFEGUARDS_UDC_ANR_COUNT = Math.max(1, 1075 DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, 1076 KEY_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT, 1077 DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT)); 1078 EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS = DeviceConfig.getLong( 1079 DeviceConfig.NAMESPACE_JOB_SCHEDULER, 1080 KEY_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS, 1081 DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS); 1082 } 1083 1084 private void updateRuntimeConstantsLocked() { 1085 DeviceConfig.Properties properties = DeviceConfig.getProperties( 1086 DeviceConfig.NAMESPACE_JOB_SCHEDULER, 1087 KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 1088 KEY_RUNTIME_MIN_GUARANTEE_MS, KEY_RUNTIME_MIN_EJ_GUARANTEE_MS, 1089 KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR, 1090 KEY_RUNTIME_MIN_UI_GUARANTEE_MS, 1091 KEY_RUNTIME_UI_LIMIT_MS, 1092 KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS, 1093 KEY_RUNTIME_CUMULATIVE_UI_LIMIT_MS, 1094 KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS); 1095 1096 // Make sure min runtime for regular jobs is at least 10 minutes. 1097 RUNTIME_MIN_GUARANTEE_MS = Math.max(10 * MINUTE_IN_MILLIS, 1098 properties.getLong( 1099 KEY_RUNTIME_MIN_GUARANTEE_MS, DEFAULT_RUNTIME_MIN_GUARANTEE_MS)); 1100 // Make sure min runtime for expedited jobs is at least one minute. 1101 RUNTIME_MIN_EJ_GUARANTEE_MS = Math.max(MINUTE_IN_MILLIS, 1102 properties.getLong( 1103 KEY_RUNTIME_MIN_EJ_GUARANTEE_MS, DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS)); 1104 RUNTIME_FREE_QUOTA_MAX_LIMIT_MS = Math.max(RUNTIME_MIN_GUARANTEE_MS, 1105 properties.getLong(KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 1106 DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS)); 1107 // Make sure min runtime is at least as long as regular jobs. 1108 RUNTIME_MIN_UI_GUARANTEE_MS = Math.max(RUNTIME_MIN_GUARANTEE_MS, 1109 properties.getLong( 1110 KEY_RUNTIME_MIN_UI_GUARANTEE_MS, DEFAULT_RUNTIME_MIN_UI_GUARANTEE_MS)); 1111 // Max limit should be at least the min guarantee AND the free quota. 1112 RUNTIME_UI_LIMIT_MS = Math.max(RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, 1113 Math.max(RUNTIME_MIN_UI_GUARANTEE_MS, 1114 properties.getLong( 1115 KEY_RUNTIME_UI_LIMIT_MS, DEFAULT_RUNTIME_UI_LIMIT_MS))); 1116 // The buffer factor should be at least 1 (so we don't decrease the time). 1117 RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR = Math.max(1, 1118 properties.getFloat( 1119 KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR, 1120 DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR 1121 )); 1122 // Make sure min runtime is at least as long as other user-initiated jobs. 1123 RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS = Math.max( 1124 RUNTIME_MIN_UI_GUARANTEE_MS, 1125 properties.getLong( 1126 KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS, 1127 DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS)); 1128 // The cumulative runtime limit should be at least the max execution limit. 1129 RUNTIME_CUMULATIVE_UI_LIMIT_MS = Math.max(RUNTIME_UI_LIMIT_MS, 1130 properties.getLong( 1131 KEY_RUNTIME_CUMULATIVE_UI_LIMIT_MS, 1132 DEFAULT_RUNTIME_CUMULATIVE_UI_LIMIT_MS)); 1133 1134 RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS = properties.getBoolean( 1135 KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS, 1136 DEFAULT_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS); 1137 } 1138 1139 private boolean updateTareSettingsLocked(@EconomyManager.EnabledMode int enabledMode) { 1140 boolean changed = false; 1141 final boolean useTare = enabledMode == EconomyManager.ENABLED_MODE_ON; 1142 if (USE_TARE_POLICY != useTare) { 1143 USE_TARE_POLICY = useTare; 1144 changed = true; 1145 } 1146 return changed; 1147 } 1148 1149 void dump(IndentingPrintWriter pw) { 1150 pw.println("Settings:"); 1151 pw.increaseIndent(); 1152 pw.print(KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT, 1153 MIN_READY_NON_ACTIVE_JOBS_COUNT).println(); 1154 pw.print(KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS, 1155 MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS).println(); 1156 pw.print(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println(); 1157 pw.print(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println(); 1158 1159 pw.print(KEY_MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS).println(); 1160 pw.print(KEY_MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS).println(); 1161 pw.print(KEY_SYSTEM_STOP_TO_FAILURE_RATIO, SYSTEM_STOP_TO_FAILURE_RATIO).println(); 1162 pw.print(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println(); 1163 pw.print(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println(); 1164 pw.print(KEY_CONN_USE_CELL_SIGNAL_STRENGTH, CONN_USE_CELL_SIGNAL_STRENGTH).println(); 1165 pw.print(KEY_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS, CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS) 1166 .println(); 1167 pw.print(KEY_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC, CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC) 1168 .println(); 1169 pw.print(KEY_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS, 1170 PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS).println(); 1171 1172 pw.print(KEY_ENABLE_API_QUOTAS, ENABLE_API_QUOTAS).println(); 1173 pw.print(KEY_API_QUOTA_SCHEDULE_COUNT, API_QUOTA_SCHEDULE_COUNT).println(); 1174 pw.print(KEY_API_QUOTA_SCHEDULE_WINDOW_MS, API_QUOTA_SCHEDULE_WINDOW_MS).println(); 1175 pw.print(KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION, 1176 API_QUOTA_SCHEDULE_THROW_EXCEPTION).println(); 1177 pw.print(KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT, 1178 API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT).println(); 1179 1180 pw.print(KEY_ENABLE_EXECUTION_SAFEGUARDS_UDC, ENABLE_EXECUTION_SAFEGUARDS_UDC) 1181 .println(); 1182 pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT, 1183 EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT).println(); 1184 pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT, 1185 EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT).println(); 1186 pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT, 1187 EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT).println(); 1188 pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT, 1189 EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT).println(); 1190 pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS, 1191 EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS).println(); 1192 pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT, 1193 EXECUTION_SAFEGUARDS_UDC_ANR_COUNT).println(); 1194 pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS, 1195 EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS).println(); 1196 1197 pw.print(KEY_RUNTIME_MIN_GUARANTEE_MS, RUNTIME_MIN_GUARANTEE_MS).println(); 1198 pw.print(KEY_RUNTIME_MIN_EJ_GUARANTEE_MS, RUNTIME_MIN_EJ_GUARANTEE_MS).println(); 1199 pw.print(KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) 1200 .println(); 1201 pw.print(KEY_RUNTIME_MIN_UI_GUARANTEE_MS, RUNTIME_MIN_UI_GUARANTEE_MS).println(); 1202 pw.print(KEY_RUNTIME_UI_LIMIT_MS, RUNTIME_UI_LIMIT_MS).println(); 1203 pw.print(KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR, 1204 RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR).println(); 1205 pw.print(KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS, 1206 RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS).println(); 1207 pw.print(KEY_RUNTIME_CUMULATIVE_UI_LIMIT_MS, RUNTIME_CUMULATIVE_UI_LIMIT_MS).println(); 1208 pw.print(KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS, 1209 RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS).println(); 1210 1211 pw.print(KEY_PERSIST_IN_SPLIT_FILES, PERSIST_IN_SPLIT_FILES).println(); 1212 pw.print(KEY_MAX_NUM_PERSISTED_JOB_WORK_ITEMS, MAX_NUM_PERSISTED_JOB_WORK_ITEMS) 1213 .println(); 1214 1215 pw.print(Settings.Global.ENABLE_TARE, USE_TARE_POLICY).println(); 1216 1217 pw.decreaseIndent(); 1218 } 1219 1220 void dump(ProtoOutputStream proto) { 1221 proto.write(ConstantsProto.MIN_READY_NON_ACTIVE_JOBS_COUNT, 1222 MIN_READY_NON_ACTIVE_JOBS_COUNT); 1223 proto.write(ConstantsProto.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS, 1224 MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS); 1225 proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR); 1226 proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR); 1227 1228 proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS); 1229 proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS); 1230 proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC); 1231 proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC); 1232 1233 proto.write(ConstantsProto.ENABLE_API_QUOTAS, ENABLE_API_QUOTAS); 1234 proto.write(ConstantsProto.API_QUOTA_SCHEDULE_COUNT, API_QUOTA_SCHEDULE_COUNT); 1235 proto.write(ConstantsProto.API_QUOTA_SCHEDULE_WINDOW_MS, API_QUOTA_SCHEDULE_WINDOW_MS); 1236 proto.write(ConstantsProto.API_QUOTA_SCHEDULE_THROW_EXCEPTION, 1237 API_QUOTA_SCHEDULE_THROW_EXCEPTION); 1238 proto.write(ConstantsProto.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT, 1239 API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT); 1240 } 1241 } 1242 1243 final Constants mConstants; 1244 final ConstantsObserver mConstantsObserver; 1245 1246 /** 1247 * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we 1248 * still clean up. On reinstall the package will have a new uid. 1249 */ 1250 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 1251 @Override 1252 public void onReceive(Context context, Intent intent) { 1253 final String action = intent.getAction(); 1254 if (DEBUG) { 1255 Slog.d(TAG, "Receieved: " + action); 1256 } 1257 final String pkgName = getPackageName(intent); 1258 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1); 1259 1260 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) { 1261 synchronized (mPermissionCache) { 1262 // Something changed. Better clear the cached permission set. 1263 mPermissionCache.remove(pkgUid); 1264 } 1265 // Purge the app's jobs if the whole package was just disabled. When this is 1266 // the case the component name will be a bare package name. 1267 if (pkgName != null && pkgUid != -1) { 1268 final String[] changedComponents = intent.getStringArrayExtra( 1269 Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST); 1270 if (changedComponents != null) { 1271 for (String component : changedComponents) { 1272 if (component.equals(pkgName)) { 1273 if (DEBUG) { 1274 Slog.d(TAG, "Package state change: " + pkgName); 1275 } 1276 try { 1277 final int userId = UserHandle.getUserId(pkgUid); 1278 IPackageManager pm = AppGlobals.getPackageManager(); 1279 final int state = 1280 pm.getApplicationEnabledSetting(pkgName, userId); 1281 if (state == COMPONENT_ENABLED_STATE_DISABLED 1282 || state == COMPONENT_ENABLED_STATE_DISABLED_USER) { 1283 if (DEBUG) { 1284 Slog.d(TAG, "Removing jobs for package " + pkgName 1285 + " in user " + userId); 1286 } 1287 synchronized (mLock) { 1288 // There's no guarantee that the process has been 1289 // stopped by the time we get here, but since this is 1290 // a user-initiated action, it should be fine to just 1291 // put USER instead of UNINSTALL or DISABLED. 1292 cancelJobsForPackageAndUidLocked(pkgName, pkgUid, 1293 /* includeSchedulingApp */ true, 1294 /* includeSourceApp */ true, 1295 JobParameters.STOP_REASON_USER, 1296 JobParameters.INTERNAL_STOP_REASON_UNINSTALL, 1297 "app disabled"); 1298 } 1299 } 1300 } catch (RemoteException | IllegalArgumentException e) { 1301 /* 1302 * IllegalArgumentException means that the package doesn't exist. 1303 * This arises when PACKAGE_CHANGED broadcast delivery has lagged 1304 * behind outright uninstall, so by the time we try to act it's gone. 1305 * We don't need to act on this PACKAGE_CHANGED when this happens; 1306 * we'll get a PACKAGE_REMOVED later and clean up then. 1307 * 1308 * RemoteException can't actually happen; the package manager is 1309 * running in this same process. 1310 */ 1311 } 1312 break; 1313 } 1314 } 1315 if (DEBUG) { 1316 Slog.d(TAG, "Something in " + pkgName 1317 + " changed. Reevaluating controller states."); 1318 } 1319 synchronized (mLock) { 1320 for (int c = mControllers.size() - 1; c >= 0; --c) { 1321 mControllers.get(c).reevaluateStateLocked(pkgUid); 1322 } 1323 } 1324 } 1325 } else { 1326 Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid); 1327 } 1328 } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { 1329 synchronized (mPermissionCache) { 1330 // Something changed. Better clear the cached permission set. 1331 mPermissionCache.remove(pkgUid); 1332 } 1333 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { 1334 synchronized (mLock) { 1335 mUidToPackageCache.remove(pkgUid); 1336 } 1337 } 1338 } else if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) { 1339 synchronized (mPermissionCache) { 1340 mPermissionCache.remove(pkgUid); 1341 } 1342 if (DEBUG) { 1343 Slog.d(TAG, "Removing jobs for " + pkgName + " (uid=" + pkgUid + ")"); 1344 } 1345 synchronized (mLock) { 1346 mUidToPackageCache.remove(pkgUid); 1347 // There's no guarantee that the process has been stopped by the time we 1348 // get here, but since this is generally a user-initiated action, it should 1349 // be fine to just put USER instead of UNINSTALL or DISABLED. 1350 cancelJobsForPackageAndUidLocked(pkgName, pkgUid, 1351 /* includeSchedulingApp */ true, /* includeSourceApp */ true, 1352 JobParameters.STOP_REASON_USER, 1353 JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "app uninstalled"); 1354 for (int c = 0; c < mControllers.size(); ++c) { 1355 mControllers.get(c).onAppRemovedLocked(pkgName, pkgUid); 1356 } 1357 mDebuggableApps.remove(pkgName); 1358 mConcurrencyManager.onAppRemovedLocked(pkgName, pkgUid); 1359 } 1360 } else if (Intent.ACTION_UID_REMOVED.equals(action)) { 1361 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { 1362 synchronized (mLock) { 1363 mUidBiasOverride.delete(pkgUid); 1364 mUidCapabilities.delete(pkgUid); 1365 mUidProcStates.delete(pkgUid); 1366 } 1367 } 1368 } else if (Intent.ACTION_USER_ADDED.equals(action)) { 1369 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); 1370 synchronized (mLock) { 1371 for (int c = 0; c < mControllers.size(); ++c) { 1372 mControllers.get(c).onUserAddedLocked(userId); 1373 } 1374 } 1375 } else if (Intent.ACTION_USER_REMOVED.equals(action)) { 1376 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); 1377 if (DEBUG) { 1378 Slog.d(TAG, "Removing jobs for user: " + userId); 1379 } 1380 synchronized (mLock) { 1381 mUidToPackageCache.clear(); 1382 cancelJobsForUserLocked(userId); 1383 for (int c = 0; c < mControllers.size(); ++c) { 1384 mControllers.get(c).onUserRemovedLocked(userId); 1385 } 1386 } 1387 mConcurrencyManager.onUserRemoved(userId); 1388 synchronized (mPermissionCache) { 1389 for (int u = mPermissionCache.size() - 1; u >= 0; --u) { 1390 final int uid = mPermissionCache.keyAt(u); 1391 if (userId == UserHandle.getUserId(uid)) { 1392 mPermissionCache.removeAt(u); 1393 } 1394 } 1395 } 1396 } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) { 1397 // Has this package scheduled any jobs, such that we will take action 1398 // if it were to be force-stopped? 1399 if (pkgUid != -1) { 1400 ArraySet<JobStatus> jobsForUid; 1401 synchronized (mLock) { 1402 jobsForUid = mJobs.getJobsByUid(pkgUid); 1403 } 1404 for (int i = jobsForUid.size() - 1; i >= 0; i--) { 1405 if (jobsForUid.valueAt(i).getSourcePackageName().equals(pkgName)) { 1406 if (DEBUG) { 1407 Slog.d(TAG, "Restart query: package " + pkgName + " at uid " 1408 + pkgUid + " has jobs"); 1409 } 1410 setResultCode(Activity.RESULT_OK); 1411 break; 1412 } 1413 } 1414 } 1415 } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) { 1416 // possible force-stop 1417 if (pkgUid != -1) { 1418 if (DEBUG) { 1419 Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid); 1420 } 1421 synchronized (mLock) { 1422 // Exclude jobs scheduled on behalf of this app for now because SyncManager 1423 // and other job proxy agents may not know to reschedule the job properly 1424 // after force stop. 1425 // TODO(209852664): determine how to best handle syncs & other proxied jobs 1426 cancelJobsForPackageAndUidLocked(pkgName, pkgUid, 1427 /* includeSchedulingApp */ true, /* includeSourceApp */ false, 1428 JobParameters.STOP_REASON_USER, 1429 JobParameters.INTERNAL_STOP_REASON_CANCELED, 1430 "app force stopped"); 1431 } 1432 } 1433 } 1434 } 1435 }; 1436 1437 private String getPackageName(Intent intent) { 1438 Uri uri = intent.getData(); 1439 String pkg = uri != null ? uri.getSchemeSpecificPart() : null; 1440 return pkg; 1441 } 1442 1443 final private IUidObserver mUidObserver = new UidObserver() { 1444 @Override public void onUidStateChanged(int uid, int procState, long procStateSeq, 1445 int capability) { 1446 final SomeArgs args = SomeArgs.obtain(); 1447 args.argi1 = uid; 1448 args.argi2 = procState; 1449 args.argi3 = capability; 1450 mHandler.obtainMessage(MSG_UID_STATE_CHANGED, args).sendToTarget(); 1451 } 1452 1453 @Override public void onUidGone(int uid, boolean disabled) { 1454 mHandler.obtainMessage(MSG_UID_GONE, uid, disabled ? 1 : 0).sendToTarget(); 1455 } 1456 1457 @Override public void onUidActive(int uid) { 1458 mHandler.obtainMessage(MSG_UID_ACTIVE, uid, 0).sendToTarget(); 1459 } 1460 1461 @Override public void onUidIdle(int uid, boolean disabled) { 1462 mHandler.obtainMessage(MSG_UID_IDLE, uid, disabled ? 1 : 0).sendToTarget(); 1463 } 1464 }; 1465 1466 public Context getTestableContext() { 1467 return getContext(); 1468 } 1469 1470 public Object getLock() { 1471 return mLock; 1472 } 1473 1474 public JobStore getJobStore() { 1475 return mJobs; 1476 } 1477 1478 public Constants getConstants() { 1479 return mConstants; 1480 } 1481 1482 @NonNull 1483 PendingJobQueue getPendingJobQueue() { 1484 return mPendingJobQueue; 1485 } 1486 1487 @NonNull 1488 public WorkSource deriveWorkSource(int sourceUid, @Nullable String sourcePackageName) { 1489 if (WorkSource.isChainedBatteryAttributionEnabled(getContext())) { 1490 WorkSource ws = new WorkSource(); 1491 ws.createWorkChain() 1492 .addNode(sourceUid, sourcePackageName) 1493 .addNode(Process.SYSTEM_UID, "JobScheduler"); 1494 return ws; 1495 } else { 1496 return sourcePackageName == null 1497 ? new WorkSource(sourceUid) : new WorkSource(sourceUid, sourcePackageName); 1498 } 1499 } 1500 1501 @Nullable 1502 @GuardedBy("mLock") 1503 public ArraySet<String> getPackagesForUidLocked(final int uid) { 1504 ArraySet<String> packages = mUidToPackageCache.get(uid); 1505 if (packages == null) { 1506 try { 1507 String[] pkgs = AppGlobals.getPackageManager() 1508 .getPackagesForUid(uid); 1509 if (pkgs != null) { 1510 for (String pkg : pkgs) { 1511 mUidToPackageCache.add(uid, pkg); 1512 } 1513 packages = mUidToPackageCache.get(uid); 1514 } 1515 } catch (RemoteException e) { 1516 // Shouldn't happen. 1517 } 1518 } 1519 return packages; 1520 } 1521 1522 @Override 1523 public void onUserStarting(@NonNull TargetUser user) { 1524 synchronized (mLock) { 1525 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, user.getUserIdentifier()); 1526 } 1527 } 1528 1529 /** Start jobs after user is available, delayed by a few seconds since non-urgent. */ 1530 @Override 1531 public void onUserCompletedEvent(@NonNull TargetUser user, UserCompletedEventType eventType) { 1532 if (eventType.includesOnUserStarting() || eventType.includesOnUserUnlocked()) { 1533 // onUserStarting: direct-boot-aware jobs can safely run 1534 // onUserUnlocked: direct-boot-UNaware jobs can safely run. 1535 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 1536 } 1537 } 1538 1539 @Override 1540 public void onUserStopping(@NonNull TargetUser user) { 1541 synchronized (mLock) { 1542 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, user.getUserIdentifier()); 1543 } 1544 } 1545 1546 /** 1547 * Return whether an UID is active or idle. 1548 */ 1549 private boolean isUidActive(int uid) { 1550 return mAppStateTracker.isUidActiveSynced(uid); 1551 } 1552 1553 private final Predicate<Integer> mIsUidActivePredicate = this::isUidActive; 1554 1555 public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName, 1556 int userId, @Nullable String namespace, String tag) { 1557 // Rate limit excessive schedule() calls. 1558 final String servicePkg = job.getService().getPackageName(); 1559 if (job.isPersisted() && (packageName == null || packageName.equals(servicePkg))) { 1560 // Only limit schedule calls for persisted jobs scheduled by the app itself. 1561 final String pkg = packageName == null ? servicePkg : packageName; 1562 if (!mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG)) { 1563 if (mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_LOGGED)) { 1564 // Don't log too frequently 1565 Slog.wtf(TAG, userId + "-" + pkg + " has called schedule() too many times"); 1566 mQuotaTracker.noteEvent(userId, pkg, QUOTA_TRACKER_SCHEDULE_LOGGED); 1567 } 1568 mAppStandbyInternal.restrictApp( 1569 pkg, userId, UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY); 1570 if (mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION) { 1571 final boolean isDebuggable; 1572 synchronized (mLock) { 1573 if (!mDebuggableApps.containsKey(packageName)) { 1574 try { 1575 final ApplicationInfo appInfo = AppGlobals.getPackageManager() 1576 .getApplicationInfo(pkg, 0, userId); 1577 if (appInfo != null) { 1578 mDebuggableApps.put(packageName, 1579 (appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0); 1580 } else { 1581 return JobScheduler.RESULT_FAILURE; 1582 } 1583 } catch (RemoteException e) { 1584 throw new RuntimeException(e); 1585 } 1586 } 1587 isDebuggable = mDebuggableApps.get(packageName); 1588 } 1589 if (isDebuggable) { 1590 // Only throw the exception for debuggable apps. 1591 throw new LimitExceededException( 1592 "schedule()/enqueue() called more than " 1593 + mQuotaTracker.getLimit( 1594 QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED) 1595 + " times in the past " 1596 + mQuotaTracker.getWindowSizeMs( 1597 QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED) 1598 + "ms. See the documentation for more information."); 1599 } 1600 } 1601 if (mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT) { 1602 return JobScheduler.RESULT_FAILURE; 1603 } 1604 } 1605 mQuotaTracker.noteEvent(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG); 1606 } 1607 1608 if (mActivityManagerInternal.isAppStartModeDisabled(uId, servicePkg)) { 1609 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString() 1610 + " -- package not allowed to start"); 1611 Counter.logIncrementWithUid( 1612 "job_scheduler.value_cntr_w_uid_schedule_failure_app_start_mode_disabled", 1613 uId); 1614 return JobScheduler.RESULT_FAILURE; 1615 } 1616 1617 if (job.getRequiredNetwork() != null) { 1618 sInitialJobEstimatedNetworkDownloadKBLogger.logSample( 1619 safelyScaleBytesToKBForHistogram( 1620 job.getEstimatedNetworkDownloadBytes())); 1621 sInitialJobEstimatedNetworkUploadKBLogger.logSample( 1622 safelyScaleBytesToKBForHistogram(job.getEstimatedNetworkUploadBytes())); 1623 sJobMinimumChunkKBLogger.logSampleWithUid(uId, 1624 safelyScaleBytesToKBForHistogram(job.getMinimumNetworkChunkBytes())); 1625 if (work != null) { 1626 sInitialJwiEstimatedNetworkDownloadKBLogger.logSample( 1627 safelyScaleBytesToKBForHistogram( 1628 work.getEstimatedNetworkDownloadBytes())); 1629 sInitialJwiEstimatedNetworkUploadKBLogger.logSample( 1630 safelyScaleBytesToKBForHistogram( 1631 work.getEstimatedNetworkUploadBytes())); 1632 sJwiMinimumChunkKBLogger.logSampleWithUid(uId, 1633 safelyScaleBytesToKBForHistogram( 1634 work.getMinimumNetworkChunkBytes())); 1635 } 1636 } 1637 1638 if (work != null) { 1639 Counter.logIncrementWithUid( 1640 "job_scheduler.value_cntr_w_uid_job_work_items_enqueued", uId); 1641 } 1642 1643 synchronized (mLock) { 1644 final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, namespace, job.getId()); 1645 1646 if (work != null && toCancel != null) { 1647 // Fast path: we are adding work to an existing job, and the JobInfo is not 1648 // changing. We can just directly enqueue this work in to the job. 1649 if (toCancel.getJob().equals(job)) { 1650 // On T and below, JobWorkItem count was unlimited but they could not be 1651 // persisted. Now in U and above, we allow persisting them. In both cases, 1652 // there is a danger of apps adding too many JobWorkItems and causing the 1653 // system to OOM since we keep everything in memory. The persisting danger 1654 // is greater because it could technically lead to a boot loop if the system 1655 // keeps trying to load all the JobWorkItems that led to the initial OOM. 1656 // Therefore, for now (partly for app compatibility), we tackle the latter 1657 // and limit the number of JobWorkItems that can be persisted. 1658 // Moving forward, we should look into two things: 1659 // 1. Limiting the number of unpersisted JobWorkItems 1660 // 2. Offloading some state to disk so we don't keep everything in memory 1661 // TODO(273758274): improve JobScheduler's resilience and memory management 1662 if (toCancel.getWorkCount() >= mConstants.MAX_NUM_PERSISTED_JOB_WORK_ITEMS 1663 && toCancel.isPersisted()) { 1664 Slog.w(TAG, "Too many JWIs for uid " + uId); 1665 throw new IllegalStateException("Apps may not persist more than " 1666 + mConstants.MAX_NUM_PERSISTED_JOB_WORK_ITEMS 1667 + " JobWorkItems per job"); 1668 } 1669 1670 toCancel.enqueueWorkLocked(work); 1671 if (toCancel.getJob().isUserInitiated()) { 1672 // The app is in a state to successfully schedule a UI job. Presumably, the 1673 // user has asked for this additional bit of work, so remove any demotion 1674 // flags. Only do this for UI jobs since they have strict scheduling 1675 // requirements; it's harder to assume other jobs were scheduled due to 1676 // user interaction/request. 1677 toCancel.removeInternalFlags( 1678 JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER 1679 | JobStatus.INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ); 1680 } 1681 mJobs.touchJob(toCancel); 1682 sEnqueuedJwiHighWaterMarkLogger.logSampleWithUid(uId, toCancel.getWorkCount()); 1683 1684 // If any of work item is enqueued when the source is in the foreground, 1685 // exempt the entire job. 1686 toCancel.maybeAddForegroundExemption(mIsUidActivePredicate); 1687 1688 return JobScheduler.RESULT_SUCCESS; 1689 } 1690 } 1691 1692 JobStatus jobStatus = 1693 JobStatus.createFromJobInfo(job, uId, packageName, userId, namespace, tag); 1694 1695 // Return failure early if expedited job quota used up. 1696 if (jobStatus.isRequestedExpeditedJob()) { 1697 if ((mConstants.USE_TARE_POLICY && !mTareController.canScheduleEJ(jobStatus)) 1698 || (!mConstants.USE_TARE_POLICY 1699 && !mQuotaController.isWithinEJQuotaLocked(jobStatus))) { 1700 Counter.logIncrementWithUid( 1701 "job_scheduler.value_cntr_w_uid_schedule_failure_ej_out_of_quota", 1702 uId); 1703 return JobScheduler.RESULT_FAILURE; 1704 } 1705 } 1706 1707 // Give exemption if the source is in the foreground just now. 1708 // Note if it's a sync job, this method is called on the handler so it's not exactly 1709 // the state when requestSync() was called, but that should be fine because of the 1710 // 1 minute foreground grace period. 1711 jobStatus.maybeAddForegroundExemption(mIsUidActivePredicate); 1712 1713 if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString()); 1714 // Jobs on behalf of others don't apply to the per-app job cap 1715 if (packageName == null) { 1716 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) { 1717 Slog.w(TAG, "Too many jobs for uid " + uId); 1718 Counter.logIncrementWithUid( 1719 "job_scheduler.value_cntr_w_uid_max_scheduling_limit_hit", uId); 1720 throw new IllegalStateException("Apps may not schedule more than " 1721 + MAX_JOBS_PER_APP + " distinct jobs"); 1722 } 1723 } 1724 1725 // This may throw a SecurityException. 1726 jobStatus.prepareLocked(); 1727 1728 if (toCancel != null) { 1729 // On T and below, JobWorkItem count was unlimited but they could not be 1730 // persisted. Now in U and above, we allow persisting them. In both cases, 1731 // there is a danger of apps adding too many JobWorkItems and causing the 1732 // system to OOM since we keep everything in memory. The persisting danger 1733 // is greater because it could technically lead to a boot loop if the system 1734 // keeps trying to load all the JobWorkItems that led to the initial OOM. 1735 // Therefore, for now (partly for app compatibility), we tackle the latter 1736 // and limit the number of JobWorkItems that can be persisted. 1737 // Moving forward, we should look into two things: 1738 // 1. Limiting the number of unpersisted JobWorkItems 1739 // 2. Offloading some state to disk so we don't keep everything in memory 1740 // TODO(273758274): improve JobScheduler's resilience and memory management 1741 if (work != null && toCancel.isPersisted() 1742 && toCancel.getWorkCount() >= mConstants.MAX_NUM_PERSISTED_JOB_WORK_ITEMS) { 1743 Slog.w(TAG, "Too many JWIs for uid " + uId); 1744 throw new IllegalStateException("Apps may not persist more than " 1745 + mConstants.MAX_NUM_PERSISTED_JOB_WORK_ITEMS 1746 + " JobWorkItems per job"); 1747 } 1748 1749 // Implicitly replaces the existing job record with the new instance 1750 cancelJobImplLocked(toCancel, jobStatus, JobParameters.STOP_REASON_CANCELLED_BY_APP, 1751 JobParameters.INTERNAL_STOP_REASON_CANCELED, "job rescheduled by app"); 1752 } else { 1753 startTrackingJobLocked(jobStatus, null); 1754 } 1755 1756 if (work != null) { 1757 // If work has been supplied, enqueue it into the new job. 1758 jobStatus.enqueueWorkLocked(work); 1759 sEnqueuedJwiHighWaterMarkLogger.logSampleWithUid(uId, jobStatus.getWorkCount()); 1760 } 1761 1762 FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED, 1763 uId, null, jobStatus.getBatteryName(), 1764 FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED, 1765 JobProtoEnums.INTERNAL_STOP_REASON_UNKNOWN, jobStatus.getStandbyBucket(), 1766 jobStatus.getLoggingJobId(), 1767 jobStatus.hasChargingConstraint(), 1768 jobStatus.hasBatteryNotLowConstraint(), 1769 jobStatus.hasStorageNotLowConstraint(), 1770 jobStatus.hasTimingDelayConstraint(), 1771 jobStatus.hasDeadlineConstraint(), 1772 jobStatus.hasIdleConstraint(), 1773 jobStatus.hasConnectivityConstraint(), 1774 jobStatus.hasContentTriggerConstraint(), 1775 jobStatus.isRequestedExpeditedJob(), 1776 /* isRunningAsExpeditedJob */ false, 1777 JobProtoEnums.STOP_REASON_UNDEFINED, 1778 jobStatus.getJob().isPrefetch(), 1779 jobStatus.getJob().getPriority(), 1780 jobStatus.getEffectivePriority(), 1781 jobStatus.getNumPreviousAttempts(), 1782 jobStatus.getJob().getMaxExecutionDelayMillis(), 1783 /* isDeadlineConstraintSatisfied */ false, 1784 /* isChargingSatisfied */ false, 1785 /* batteryNotLowSatisfied */ false, 1786 /* storageNotLowSatisfied */false, 1787 /* timingDelayConstraintSatisfied */ false, 1788 /* isDeviceIdleSatisfied */ false, 1789 /* hasConnectivityConstraintSatisfied */ false, 1790 /* hasContentTriggerConstraintSatisfied */ false, 1791 /* jobStartLatencyMs */ 0, 1792 jobStatus.getJob().isUserInitiated(), 1793 /* isRunningAsUserInitiatedJob */ false, 1794 jobStatus.getJob().isPeriodic(), 1795 jobStatus.getJob().getMinLatencyMillis(), 1796 jobStatus.getEstimatedNetworkDownloadBytes(), 1797 jobStatus.getEstimatedNetworkUploadBytes(), 1798 jobStatus.getWorkCount(), 1799 ActivityManager.processStateAmToProto(mUidProcStates.get(jobStatus.getUid())), 1800 jobStatus.getNamespaceHash()); 1801 1802 // If the job is immediately ready to run, then we can just immediately 1803 // put it in the pending list and try to schedule it. This is especially 1804 // important for jobs with a 0 deadline constraint, since they will happen a fair 1805 // amount, we want to handle them as quickly as possible, and semantically we want to 1806 // make sure we have started holding the wake lock for the job before returning to 1807 // the caller. 1808 // If the job is not yet ready to run, there is nothing more to do -- we are 1809 // now just waiting for one of its controllers to change state and schedule 1810 // the job appropriately. 1811 if (isReadyToBeExecutedLocked(jobStatus)) { 1812 // This is a new job, we can just immediately put it on the pending 1813 // list and try to run it. 1814 mJobPackageTracker.notePending(jobStatus); 1815 mPendingJobQueue.add(jobStatus); 1816 maybeRunPendingJobsLocked(); 1817 } 1818 } 1819 return JobScheduler.RESULT_SUCCESS; 1820 } 1821 1822 private ArrayMap<String, List<JobInfo>> getPendingJobs(int uid) { 1823 final ArrayMap<String, List<JobInfo>> outMap = new ArrayMap<>(); 1824 synchronized (mLock) { 1825 ArraySet<JobStatus> jobs = mJobs.getJobsByUid(uid); 1826 // Write out for loop to avoid creating an Iterator. 1827 for (int i = jobs.size() - 1; i >= 0; i--) { 1828 final JobStatus job = jobs.valueAt(i); 1829 List<JobInfo> outList = outMap.get(job.getNamespace()); 1830 if (outList == null) { 1831 outList = new ArrayList<>(); 1832 outMap.put(job.getNamespace(), outList); 1833 } 1834 1835 outList.add(job.getJob()); 1836 } 1837 return outMap; 1838 } 1839 } 1840 1841 private List<JobInfo> getPendingJobsInNamespace(int uid, @Nullable String namespace) { 1842 synchronized (mLock) { 1843 ArraySet<JobStatus> jobs = mJobs.getJobsByUid(uid); 1844 ArrayList<JobInfo> outList = new ArrayList<>(); 1845 // Write out for loop to avoid addAll() creating an Iterator. 1846 for (int i = jobs.size() - 1; i >= 0; i--) { 1847 final JobStatus job = jobs.valueAt(i); 1848 if (Objects.equals(namespace, job.getNamespace())) { 1849 outList.add(job.getJob()); 1850 } 1851 } 1852 return outList; 1853 } 1854 } 1855 1856 @JobScheduler.PendingJobReason 1857 private int getPendingJobReason(int uid, String namespace, int jobId) { 1858 int reason; 1859 // Some apps may attempt to query this frequently, so cache the reason under a separate lock 1860 // so that the rest of JS processing isn't negatively impacted. 1861 synchronized (mPendingJobReasonCache) { 1862 SparseIntArray jobIdToReason = mPendingJobReasonCache.get(uid, namespace); 1863 if (jobIdToReason != null) { 1864 reason = jobIdToReason.get(jobId, JobScheduler.PENDING_JOB_REASON_UNDEFINED); 1865 if (reason != JobScheduler.PENDING_JOB_REASON_UNDEFINED) { 1866 return reason; 1867 } 1868 } 1869 } 1870 synchronized (mLock) { 1871 reason = getPendingJobReasonLocked(uid, namespace, jobId); 1872 if (DEBUG) { 1873 Slog.v(TAG, "getPendingJobReason(" 1874 + uid + "," + namespace + "," + jobId + ")=" + reason); 1875 } 1876 } 1877 synchronized (mPendingJobReasonCache) { 1878 SparseIntArray jobIdToReason = mPendingJobReasonCache.get(uid, namespace); 1879 if (jobIdToReason == null) { 1880 jobIdToReason = new SparseIntArray(); 1881 mPendingJobReasonCache.add(uid, namespace, jobIdToReason); 1882 } 1883 jobIdToReason.put(jobId, reason); 1884 } 1885 return reason; 1886 } 1887 1888 @VisibleForTesting 1889 @JobScheduler.PendingJobReason 1890 int getPendingJobReason(JobStatus job) { 1891 return getPendingJobReason(job.getUid(), job.getNamespace(), job.getJobId()); 1892 } 1893 1894 @JobScheduler.PendingJobReason 1895 @GuardedBy("mLock") 1896 private int getPendingJobReasonLocked(int uid, String namespace, int jobId) { 1897 // Very similar code to isReadyToBeExecutedLocked. 1898 1899 JobStatus job = mJobs.getJobByUidAndJobId(uid, namespace, jobId); 1900 if (job == null) { 1901 // Job doesn't exist. 1902 return JobScheduler.PENDING_JOB_REASON_INVALID_JOB_ID; 1903 } 1904 1905 if (isCurrentlyRunningLocked(job)) { 1906 return JobScheduler.PENDING_JOB_REASON_EXECUTING; 1907 } 1908 1909 final boolean jobReady = job.isReady(); 1910 1911 if (DEBUG) { 1912 Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString() 1913 + " ready=" + jobReady); 1914 } 1915 1916 if (!jobReady) { 1917 return job.getPendingJobReason(); 1918 } 1919 1920 final boolean userStarted = areUsersStartedLocked(job); 1921 1922 if (DEBUG) { 1923 Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString() 1924 + " userStarted=" + userStarted); 1925 } 1926 if (!userStarted) { 1927 return JobScheduler.PENDING_JOB_REASON_USER; 1928 } 1929 1930 final boolean backingUp = mBackingUpUids.get(job.getSourceUid()); 1931 if (DEBUG) { 1932 Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString() 1933 + " backingUp=" + backingUp); 1934 } 1935 1936 if (backingUp) { 1937 // TODO: Should we make a special reason for this? 1938 return JobScheduler.PENDING_JOB_REASON_APP; 1939 } 1940 1941 JobRestriction restriction = checkIfRestricted(job); 1942 if (DEBUG) { 1943 Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString() 1944 + " restriction=" + restriction); 1945 } 1946 if (restriction != null) { 1947 return restriction.getPendingReason(); 1948 } 1949 1950 // The following can be a little more expensive (especially jobActive, since we need to 1951 // go through the array of all potentially active jobs), so we are doing them 1952 // later... but still before checking with the package manager! 1953 final boolean jobPending = mPendingJobQueue.contains(job); 1954 1955 1956 if (DEBUG) { 1957 Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString() 1958 + " pending=" + jobPending); 1959 } 1960 1961 if (jobPending) { 1962 // We haven't started the job for some reason. Presumably, there are too many jobs 1963 // running. 1964 return JobScheduler.PENDING_JOB_REASON_DEVICE_STATE; 1965 } 1966 1967 final boolean jobActive = mConcurrencyManager.isJobRunningLocked(job); 1968 1969 if (DEBUG) { 1970 Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString() 1971 + " active=" + jobActive); 1972 } 1973 if (jobActive) { 1974 return JobScheduler.PENDING_JOB_REASON_UNDEFINED; 1975 } 1976 1977 // Validate that the defined package+service is still present & viable. 1978 final boolean componentUsable = isComponentUsable(job); 1979 1980 if (DEBUG) { 1981 Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString() 1982 + " componentUsable=" + componentUsable); 1983 } 1984 if (!componentUsable) { 1985 return JobScheduler.PENDING_JOB_REASON_APP; 1986 } 1987 1988 return JobScheduler.PENDING_JOB_REASON_UNDEFINED; 1989 } 1990 1991 private JobInfo getPendingJob(int uid, @Nullable String namespace, int jobId) { 1992 synchronized (mLock) { 1993 ArraySet<JobStatus> jobs = mJobs.getJobsByUid(uid); 1994 for (int i = jobs.size() - 1; i >= 0; i--) { 1995 JobStatus job = jobs.valueAt(i); 1996 if (job.getJobId() == jobId && Objects.equals(namespace, job.getNamespace())) { 1997 return job.getJob(); 1998 } 1999 } 2000 return null; 2001 } 2002 } 2003 2004 @VisibleForTesting 2005 void notePendingUserRequestedAppStopInternal(@NonNull String packageName, int userId, 2006 @Nullable String debugReason) { 2007 final int packageUid = mLocalPM.getPackageUid(packageName, 0, userId); 2008 if (packageUid < 0) { 2009 Slog.wtf(TAG, "Asked to stop jobs of an unknown package"); 2010 return; 2011 } 2012 synchronized (mLock) { 2013 mConcurrencyManager.markJobsForUserStopLocked(userId, packageName, debugReason); 2014 final ArraySet<JobStatus> jobs = mJobs.getJobsByUid(packageUid); 2015 for (int i = jobs.size() - 1; i >= 0; i--) { 2016 final JobStatus job = jobs.valueAt(i); 2017 2018 // For now, demote all jobs of the app. However, if the app was only doing work 2019 // on behalf of another app and the user wanted just that work to stop, this 2020 // unfairly penalizes any other jobs that may be scheduled. 2021 // For example, if apps A & B ask app C to do something (thus A & B are "source" 2022 // and C is "calling"), but only A's work was under way and the user wanted 2023 // to stop only that work, B's jobs would be demoted as well. 2024 // TODO(255768978): make it possible to demote only the relevant subset of jobs 2025 job.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER); 2026 2027 // The app process will be killed soon. There's no point keeping its jobs in 2028 // the pending queue to try and start them. 2029 if (mPendingJobQueue.remove(job)) { 2030 synchronized (mPendingJobReasonCache) { 2031 SparseIntArray jobIdToReason = mPendingJobReasonCache.get( 2032 job.getUid(), job.getNamespace()); 2033 if (jobIdToReason == null) { 2034 jobIdToReason = new SparseIntArray(); 2035 mPendingJobReasonCache.add(job.getUid(), job.getNamespace(), 2036 jobIdToReason); 2037 } 2038 jobIdToReason.put(job.getJobId(), JobScheduler.PENDING_JOB_REASON_USER); 2039 } 2040 } 2041 } 2042 } 2043 } 2044 2045 private final Consumer<JobStatus> mCancelJobDueToUserRemovalConsumer = (toRemove) -> { 2046 // There's no guarantee that the process has been stopped by the time we get 2047 // here, but since this is a user-initiated action, it should be fine to just 2048 // put USER instead of UNINSTALL or DISABLED. 2049 cancelJobImplLocked(toRemove, null, JobParameters.STOP_REASON_USER, 2050 JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "user removed"); 2051 }; 2052 2053 private void cancelJobsForUserLocked(int userHandle) { 2054 mJobs.forEachJob( 2055 (job) -> job.getUserId() == userHandle || job.getSourceUserId() == userHandle, 2056 mCancelJobDueToUserRemovalConsumer); 2057 } 2058 2059 private void cancelJobsForNonExistentUsers() { 2060 UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class); 2061 synchronized (mLock) { 2062 mJobs.removeJobsOfUnlistedUsers(umi.getUserIds()); 2063 } 2064 synchronized (mPendingJobReasonCache) { 2065 mPendingJobReasonCache.clear(); 2066 } 2067 } 2068 2069 private void cancelJobsForPackageAndUidLocked(String pkgName, int uid, 2070 boolean includeSchedulingApp, boolean includeSourceApp, 2071 @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) { 2072 if (!includeSchedulingApp && !includeSourceApp) { 2073 Slog.wtfStack(TAG, 2074 "Didn't indicate whether to cancel jobs for scheduling and/or source app"); 2075 includeSourceApp = true; 2076 } 2077 if ("android".equals(pkgName)) { 2078 Slog.wtfStack(TAG, "Can't cancel all jobs for system package"); 2079 return; 2080 } 2081 final ArraySet<JobStatus> jobsForUid = new ArraySet<>(); 2082 if (includeSchedulingApp) { 2083 mJobs.getJobsByUid(uid, jobsForUid); 2084 } 2085 if (includeSourceApp) { 2086 mJobs.getJobsBySourceUid(uid, jobsForUid); 2087 } 2088 for (int i = jobsForUid.size() - 1; i >= 0; i--) { 2089 final JobStatus job = jobsForUid.valueAt(i); 2090 final boolean shouldCancel = 2091 (includeSchedulingApp 2092 && job.getServiceComponent().getPackageName().equals(pkgName)) 2093 || (includeSourceApp && job.getSourcePackageName().equals(pkgName)); 2094 if (shouldCancel) { 2095 cancelJobImplLocked(job, null, reason, internalReasonCode, debugReason); 2096 } 2097 } 2098 } 2099 2100 /** 2101 * Entry point from client to cancel all jobs scheduled for or from their uid. 2102 * This will remove the job from the master list, and cancel the job if it was staged for 2103 * execution or being executed. 2104 * 2105 * @param uid Uid to check against for removal of a job. 2106 * @param includeSourceApp Whether to include jobs scheduled for this UID by another UID. 2107 * If false, only jobs scheduled by this UID will be cancelled. 2108 */ 2109 public boolean cancelJobsForUid(int uid, boolean includeSourceApp, 2110 @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) { 2111 return cancelJobsForUid(uid, includeSourceApp, 2112 /* namespaceOnly */ false, /* namespace */ null, 2113 reason, internalReasonCode, debugReason); 2114 } 2115 2116 private boolean cancelJobsForUid(int uid, boolean includeSourceApp, 2117 boolean namespaceOnly, @Nullable String namespace, 2118 @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) { 2119 // Non-null system namespace means the cancelling is limited to the namespace 2120 // and won't cause issues for the system at large. 2121 if (uid == Process.SYSTEM_UID && (!namespaceOnly || namespace == null)) { 2122 Slog.wtfStack(TAG, "Can't cancel all jobs for system uid"); 2123 return false; 2124 } 2125 2126 boolean jobsCanceled = false; 2127 synchronized (mLock) { 2128 final ArraySet<JobStatus> jobsForUid = new ArraySet<>(); 2129 // Get jobs scheduled by the app. 2130 mJobs.getJobsByUid(uid, jobsForUid); 2131 if (includeSourceApp) { 2132 // Get jobs scheduled for the app by someone else. 2133 mJobs.getJobsBySourceUid(uid, jobsForUid); 2134 } 2135 for (int i = 0; i < jobsForUid.size(); i++) { 2136 JobStatus toRemove = jobsForUid.valueAt(i); 2137 if (!namespaceOnly || Objects.equals(namespace, toRemove.getNamespace())) { 2138 cancelJobImplLocked(toRemove, null, reason, internalReasonCode, debugReason); 2139 jobsCanceled = true; 2140 } 2141 } 2142 } 2143 return jobsCanceled; 2144 } 2145 2146 /** 2147 * Entry point from client to cancel the job corresponding to the jobId provided. 2148 * This will remove the job from the master list, and cancel the job if it was staged for 2149 * execution or being executed. 2150 * 2151 * @param uid Uid of the calling client. 2152 * @param jobId Id of the job, provided at schedule-time. 2153 */ 2154 private boolean cancelJob(int uid, String namespace, int jobId, int callingUid, 2155 @JobParameters.StopReason int reason) { 2156 JobStatus toCancel; 2157 synchronized (mLock) { 2158 toCancel = mJobs.getJobByUidAndJobId(uid, namespace, jobId); 2159 if (toCancel != null) { 2160 cancelJobImplLocked(toCancel, null, reason, 2161 JobParameters.INTERNAL_STOP_REASON_CANCELED, 2162 "cancel() called by app, callingUid=" + callingUid 2163 + " uid=" + uid + " jobId=" + jobId); 2164 } 2165 return (toCancel != null); 2166 } 2167 } 2168 2169 /** 2170 * Cancel the given job, stopping it if it's currently executing. If {@code incomingJob} 2171 * is null, the cancelled job is removed outright from the system. If 2172 * {@code incomingJob} is non-null, it replaces {@code cancelled} in the store of 2173 * currently scheduled jobs. 2174 */ 2175 @GuardedBy("mLock") 2176 private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, 2177 @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) { 2178 if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString()); 2179 cancelled.unprepareLocked(); 2180 stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */); 2181 // Remove from pending queue. 2182 if (mPendingJobQueue.remove(cancelled)) { 2183 mJobPackageTracker.noteNonpending(cancelled); 2184 } 2185 mChangedJobList.remove(cancelled); 2186 // Cancel if running. 2187 final boolean wasRunning = mConcurrencyManager.stopJobOnServiceContextLocked( 2188 cancelled, reason, internalReasonCode, debugReason); 2189 // If the job was running, the JobServiceContext should log with state FINISHED. 2190 if (!wasRunning) { 2191 FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED, 2192 cancelled.getSourceUid(), null, cancelled.getBatteryName(), 2193 FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__CANCELLED, 2194 internalReasonCode, cancelled.getStandbyBucket(), 2195 cancelled.getLoggingJobId(), 2196 cancelled.hasChargingConstraint(), 2197 cancelled.hasBatteryNotLowConstraint(), 2198 cancelled.hasStorageNotLowConstraint(), 2199 cancelled.hasTimingDelayConstraint(), 2200 cancelled.hasDeadlineConstraint(), 2201 cancelled.hasIdleConstraint(), 2202 cancelled.hasConnectivityConstraint(), 2203 cancelled.hasContentTriggerConstraint(), 2204 cancelled.isRequestedExpeditedJob(), 2205 /* isRunningAsExpeditedJob */ false, 2206 reason, 2207 cancelled.getJob().isPrefetch(), 2208 cancelled.getJob().getPriority(), 2209 cancelled.getEffectivePriority(), 2210 cancelled.getNumPreviousAttempts(), 2211 cancelled.getJob().getMaxExecutionDelayMillis(), 2212 cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE), 2213 cancelled.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_CHARGING), 2214 cancelled.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW), 2215 cancelled.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW), 2216 cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY), 2217 cancelled.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE), 2218 cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY), 2219 cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER), 2220 /* jobStartLatencyMs */ 0, 2221 cancelled.getJob().isUserInitiated(), 2222 /* isRunningAsUserInitiatedJob */ false, 2223 cancelled.getJob().isPeriodic(), 2224 cancelled.getJob().getMinLatencyMillis(), 2225 cancelled.getEstimatedNetworkDownloadBytes(), 2226 cancelled.getEstimatedNetworkUploadBytes(), 2227 cancelled.getWorkCount(), 2228 ActivityManager.processStateAmToProto(mUidProcStates.get(cancelled.getUid())), 2229 cancelled.getNamespaceHash()); 2230 } 2231 // If this is a replacement, bring in the new version of the job 2232 if (incomingJob != null) { 2233 if (DEBUG) Slog.i(TAG, "Tracking replacement job " + incomingJob.toShortString()); 2234 startTrackingJobLocked(incomingJob, cancelled); 2235 } 2236 reportActiveLocked(); 2237 if (mLastCancelledJobs.length > 0 2238 && internalReasonCode == JobParameters.INTERNAL_STOP_REASON_CANCELED) { 2239 mLastCancelledJobs[mLastCancelledJobIndex] = cancelled; 2240 mLastCancelledJobTimeElapsed[mLastCancelledJobIndex] = sElapsedRealtimeClock.millis(); 2241 mLastCancelledJobIndex = (mLastCancelledJobIndex + 1) % mLastCancelledJobs.length; 2242 } 2243 } 2244 2245 void updateUidState(int uid, int procState, int capabilities) { 2246 if (DEBUG) { 2247 Slog.d(TAG, "UID " + uid + " proc state changed to " 2248 + ActivityManager.procStateToString(procState) 2249 + " with capabilities=" + ActivityManager.getCapabilitiesSummary(capabilities)); 2250 } 2251 synchronized (mLock) { 2252 mUidProcStates.put(uid, procState); 2253 final int prevBias = mUidBiasOverride.get(uid, JobInfo.BIAS_DEFAULT); 2254 if (procState == ActivityManager.PROCESS_STATE_TOP) { 2255 // Only use this if we are exactly the top app. All others can live 2256 // with just the foreground bias. This means that persistent processes 2257 // can never have the top app bias... that is fine. 2258 mUidBiasOverride.put(uid, JobInfo.BIAS_TOP_APP); 2259 } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { 2260 mUidBiasOverride.put(uid, JobInfo.BIAS_FOREGROUND_SERVICE); 2261 } else if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { 2262 mUidBiasOverride.put(uid, JobInfo.BIAS_BOUND_FOREGROUND_SERVICE); 2263 } else { 2264 mUidBiasOverride.delete(uid); 2265 } 2266 if (capabilities == ActivityManager.PROCESS_CAPABILITY_NONE 2267 || procState == ActivityManager.PROCESS_STATE_NONEXISTENT) { 2268 mUidCapabilities.delete(uid); 2269 } else { 2270 mUidCapabilities.put(uid, capabilities); 2271 } 2272 final int newBias = mUidBiasOverride.get(uid, JobInfo.BIAS_DEFAULT); 2273 if (prevBias != newBias) { 2274 if (DEBUG) { 2275 Slog.d(TAG, "UID " + uid + " bias changed from " + prevBias + " to " + newBias); 2276 } 2277 for (int c = 0; c < mControllers.size(); ++c) { 2278 mControllers.get(c).onUidBiasChangedLocked(uid, prevBias, newBias); 2279 } 2280 mConcurrencyManager.onUidBiasChangedLocked(prevBias, newBias); 2281 } 2282 } 2283 } 2284 2285 /** Return the current bias of the given UID. */ 2286 public int getUidBias(int uid) { 2287 synchronized (mLock) { 2288 return mUidBiasOverride.get(uid, JobInfo.BIAS_DEFAULT); 2289 } 2290 } 2291 2292 /** 2293 * Return the current {@link ActivityManager#PROCESS_CAPABILITY_ALL capabilities} 2294 * of the given UID. 2295 */ 2296 public int getUidCapabilities(int uid) { 2297 synchronized (mLock) { 2298 return mUidCapabilities.get(uid, ActivityManager.PROCESS_CAPABILITY_NONE); 2299 } 2300 } 2301 2302 /** Return the current proc state of the given UID. */ 2303 public int getUidProcState(int uid) { 2304 synchronized (mLock) { 2305 return mUidProcStates.get(uid, ActivityManager.PROCESS_STATE_UNKNOWN); 2306 } 2307 } 2308 2309 @Override 2310 public void onDeviceIdleStateChanged(boolean deviceIdle) { 2311 synchronized (mLock) { 2312 if (DEBUG) { 2313 Slog.d(TAG, "Doze state changed: " + deviceIdle); 2314 } 2315 if (!deviceIdle) { 2316 // When coming out of idle, allow thing to start back up. 2317 if (mReadyToRock) { 2318 if (mLocalDeviceIdleController != null) { 2319 if (!mReportedActive) { 2320 mReportedActive = true; 2321 mLocalDeviceIdleController.setJobsActive(true); 2322 } 2323 } 2324 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 2325 } 2326 } 2327 } 2328 } 2329 2330 @Override 2331 public void onNetworkChanged(JobStatus jobStatus, Network newNetwork) { 2332 synchronized (mLock) { 2333 final JobServiceContext jsc = 2334 mConcurrencyManager.getRunningJobServiceContextLocked(jobStatus); 2335 if (jsc != null) { 2336 jsc.informOfNetworkChangeLocked(newNetwork); 2337 } 2338 } 2339 } 2340 2341 @Override 2342 public void onRestrictedBucketChanged(List<JobStatus> jobs) { 2343 final int len = jobs.size(); 2344 if (len == 0) { 2345 Slog.wtf(TAG, "onRestrictedBucketChanged called with no jobs"); 2346 return; 2347 } 2348 synchronized (mLock) { 2349 for (int i = 0; i < len; ++i) { 2350 JobStatus js = jobs.get(i); 2351 for (int j = mRestrictiveControllers.size() - 1; j >= 0; --j) { 2352 // Effective standby bucket can change after this in some situations so use 2353 // the real bucket so that the job is tracked by the controllers. 2354 if (js.getStandbyBucket() == RESTRICTED_INDEX) { 2355 mRestrictiveControllers.get(j).startTrackingRestrictedJobLocked(js); 2356 } else { 2357 mRestrictiveControllers.get(j).stopTrackingRestrictedJobLocked(js); 2358 } 2359 } 2360 } 2361 } 2362 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 2363 } 2364 2365 void reportActiveLocked() { 2366 // active is true if pending queue contains jobs OR some job is running. 2367 boolean active = mPendingJobQueue.size() > 0; 2368 if (!active) { 2369 final ArraySet<JobStatus> runningJobs = mConcurrencyManager.getRunningJobsLocked(); 2370 for (int i = runningJobs.size() - 1; i >= 0; --i) { 2371 final JobStatus job = runningJobs.valueAt(i); 2372 if (!job.canRunInDoze()) { 2373 // We will report active if we have a job running and it does not have an 2374 // exception that allows it to run in Doze. 2375 active = true; 2376 break; 2377 } 2378 } 2379 } 2380 2381 if (mReportedActive != active) { 2382 mReportedActive = active; 2383 if (mLocalDeviceIdleController != null) { 2384 mLocalDeviceIdleController.setJobsActive(active); 2385 } 2386 } 2387 } 2388 2389 void reportAppUsage(String packageName, int userId) { 2390 // This app just transitioned into interactive use or near equivalent, so we should 2391 // take a look at its job state for feedback purposes. 2392 } 2393 2394 /** 2395 * Initializes the system service. 2396 * <p> 2397 * Subclasses must define a single argument constructor that accepts the context 2398 * and passes it to super. 2399 * </p> 2400 * 2401 * @param context The system server context. 2402 */ 2403 public JobSchedulerService(Context context) { 2404 super(context); 2405 2406 mLocalPM = LocalServices.getService(PackageManagerInternal.class); 2407 mActivityManagerInternal = Objects.requireNonNull( 2408 LocalServices.getService(ActivityManagerInternal.class)); 2409 2410 mHandler = new JobHandler(AppSchedulingModuleThread.get().getLooper()); 2411 mConstants = new Constants(); 2412 mConstantsObserver = new ConstantsObserver(); 2413 mJobSchedulerStub = new JobSchedulerStub(); 2414 2415 mConcurrencyManager = new JobConcurrencyManager(this); 2416 2417 // Set up the app standby bucketing tracker 2418 mStandbyTracker = new StandbyTracker(); 2419 mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class); 2420 2421 final Categorizer quotaCategorizer = (userId, packageName, tag) -> { 2422 if (QUOTA_TRACKER_TIMEOUT_UIJ_TAG.equals(tag)) { 2423 return mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC 2424 ? QUOTA_TRACKER_CATEGORY_TIMEOUT_UIJ 2425 : QUOTA_TRACKER_CATEGORY_DISABLED; 2426 } 2427 if (QUOTA_TRACKER_TIMEOUT_EJ_TAG.equals(tag)) { 2428 return mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC 2429 ? QUOTA_TRACKER_CATEGORY_TIMEOUT_EJ 2430 : QUOTA_TRACKER_CATEGORY_DISABLED; 2431 } 2432 if (QUOTA_TRACKER_TIMEOUT_REG_TAG.equals(tag)) { 2433 return mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC 2434 ? QUOTA_TRACKER_CATEGORY_TIMEOUT_REG 2435 : QUOTA_TRACKER_CATEGORY_DISABLED; 2436 } 2437 if (QUOTA_TRACKER_TIMEOUT_TOTAL_TAG.equals(tag)) { 2438 return mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC 2439 ? QUOTA_TRACKER_CATEGORY_TIMEOUT_TOTAL 2440 : QUOTA_TRACKER_CATEGORY_DISABLED; 2441 } 2442 if (QUOTA_TRACKER_ANR_TAG.equals(tag)) { 2443 return mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC 2444 ? QUOTA_TRACKER_CATEGORY_ANR 2445 : QUOTA_TRACKER_CATEGORY_DISABLED; 2446 } 2447 if (QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG.equals(tag)) { 2448 return mConstants.ENABLE_API_QUOTAS 2449 ? QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED 2450 : QUOTA_TRACKER_CATEGORY_DISABLED; 2451 } 2452 if (QUOTA_TRACKER_SCHEDULE_LOGGED.equals(tag)) { 2453 return mConstants.ENABLE_API_QUOTAS 2454 ? QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED 2455 : QUOTA_TRACKER_CATEGORY_DISABLED; 2456 } 2457 Slog.wtf(TAG, "Unexpected category tag: " + tag); 2458 return QUOTA_TRACKER_CATEGORY_DISABLED; 2459 }; 2460 mQuotaTracker = new CountQuotaTracker(context, quotaCategorizer); 2461 updateQuotaTracker(); 2462 // Log at most once per minute. 2463 // Set outside updateQuotaTracker() since this is intentionally not configurable. 2464 mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED, 1, 60_000); 2465 mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_DISABLED, Integer.MAX_VALUE, 60_000); 2466 2467 mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class); 2468 mAppStandbyInternal.addListener(mStandbyTracker); 2469 2470 // The job store needs to call back 2471 publishLocalService(JobSchedulerInternal.class, new LocalService()); 2472 2473 // Initialize the job store and set up any persisted jobs 2474 mJobStoreLoadedLatch = new CountDownLatch(1); 2475 mJobs = JobStore.get(this); 2476 mJobs.initAsync(mJobStoreLoadedLatch); 2477 2478 mBatteryStateTracker = new BatteryStateTracker(); 2479 mBatteryStateTracker.startTracking(); 2480 2481 // Create the controllers. 2482 mControllers = new ArrayList<StateController>(); 2483 mPrefetchController = new PrefetchController(this); 2484 mControllers.add(mPrefetchController); 2485 final FlexibilityController flexibilityController = 2486 new FlexibilityController(this, mPrefetchController); 2487 mControllers.add(flexibilityController); 2488 mConnectivityController = 2489 new ConnectivityController(this, flexibilityController); 2490 mControllers.add(mConnectivityController); 2491 mControllers.add(new TimeController(this)); 2492 final IdleController idleController = new IdleController(this, flexibilityController); 2493 mControllers.add(idleController); 2494 final BatteryController batteryController = 2495 new BatteryController(this, flexibilityController); 2496 mControllers.add(batteryController); 2497 mStorageController = new StorageController(this); 2498 mControllers.add(mStorageController); 2499 final BackgroundJobsController backgroundJobsController = 2500 new BackgroundJobsController(this); 2501 mControllers.add(backgroundJobsController); 2502 mControllers.add(new ContentObserverController(this)); 2503 mDeviceIdleJobsController = new DeviceIdleJobsController(this); 2504 mControllers.add(mDeviceIdleJobsController); 2505 mQuotaController = 2506 new QuotaController(this, backgroundJobsController, mConnectivityController); 2507 mControllers.add(mQuotaController); 2508 mControllers.add(new ComponentController(this)); 2509 mTareController = 2510 new TareController(this, backgroundJobsController, mConnectivityController); 2511 mControllers.add(mTareController); 2512 2513 mRestrictiveControllers = new ArrayList<>(); 2514 mRestrictiveControllers.add(batteryController); 2515 mRestrictiveControllers.add(mConnectivityController); 2516 mRestrictiveControllers.add(idleController); 2517 2518 // Create restrictions 2519 mJobRestrictions = new ArrayList<>(); 2520 mJobRestrictions.add(new ThermalStatusRestriction(this)); 2521 2522 // If the job store determined that it can't yet reschedule persisted jobs, 2523 // we need to start watching the clock. 2524 if (!mJobs.jobTimesInflatedValid()) { 2525 Slog.w(TAG, "!!! RTC not yet good; tracking time updates for job scheduling"); 2526 context.registerReceiver(mTimeSetReceiver, new IntentFilter(Intent.ACTION_TIME_CHANGED)); 2527 } 2528 } 2529 2530 private final BroadcastReceiver mTimeSetReceiver = new BroadcastReceiver() { 2531 @Override 2532 public void onReceive(Context context, Intent intent) { 2533 if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) { 2534 // When we reach clock sanity, recalculate the temporal windows 2535 // of all affected jobs. 2536 if (mJobs.clockNowValidToInflate(sSystemClock.millis())) { 2537 Slog.i(TAG, "RTC now valid; recalculating persisted job windows"); 2538 2539 // We've done our job now, so stop watching the time. 2540 context.unregisterReceiver(this); 2541 2542 // And kick off the work to update the affected jobs, using a secondary 2543 // thread instead of chugging away here on the main looper thread. 2544 mJobs.runWorkAsync(mJobTimeUpdater); 2545 } 2546 } 2547 } 2548 }; 2549 2550 private final Runnable mJobTimeUpdater = () -> { 2551 Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND); 2552 2553 final ArrayList<JobStatus> toRemove = new ArrayList<>(); 2554 final ArrayList<JobStatus> toAdd = new ArrayList<>(); 2555 synchronized (mLock) { 2556 // Note: we intentionally both look up the existing affected jobs and replace them 2557 // with recalculated ones inside the same lock lifetime. 2558 getJobStore().getRtcCorrectedJobsLocked(toAdd, toRemove); 2559 2560 // Now, at each position [i], we have both the existing JobStatus 2561 // and the one that replaces it. 2562 final int N = toAdd.size(); 2563 for (int i = 0; i < N; i++) { 2564 final JobStatus oldJob = toRemove.get(i); 2565 final JobStatus newJob = toAdd.get(i); 2566 if (DEBUG) { 2567 Slog.v(TAG, " replacing " + oldJob + " with " + newJob); 2568 } 2569 cancelJobImplLocked(oldJob, newJob, JobParameters.STOP_REASON_SYSTEM_PROCESSING, 2570 JobParameters.INTERNAL_STOP_REASON_RTC_UPDATED, "deferred rtc calculation"); 2571 } 2572 } 2573 }; 2574 2575 @Override 2576 public void onStart() { 2577 publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub); 2578 } 2579 2580 @Override 2581 public void onBootPhase(int phase) { 2582 if (PHASE_LOCK_SETTINGS_READY == phase) { 2583 // This is the last phase before PHASE_SYSTEM_SERVICES_READY. We need to ensure that 2584 // persisted jobs are loaded before we can proceed to PHASE_SYSTEM_SERVICES_READY. 2585 try { 2586 mJobStoreLoadedLatch.await(); 2587 } catch (InterruptedException e) { 2588 Slog.e(TAG, "Couldn't wait on job store loading latch"); 2589 } 2590 } else if (PHASE_SYSTEM_SERVICES_READY == phase) { 2591 mConstantsObserver.start(); 2592 for (StateController controller : mControllers) { 2593 controller.onSystemServicesReady(); 2594 } 2595 2596 mAppStateTracker = (AppStateTrackerImpl) Objects.requireNonNull( 2597 LocalServices.getService(AppStateTracker.class)); 2598 2599 LocalServices.getService(StorageManagerInternal.class) 2600 .registerCloudProviderChangeListener(new CloudProviderChangeListener()); 2601 2602 // Register br for package removals and user removals. 2603 final IntentFilter filter = new IntentFilter(); 2604 filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED); 2605 filter.addAction(Intent.ACTION_PACKAGE_ADDED); 2606 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 2607 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 2608 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); 2609 filter.addDataScheme("package"); 2610 getContext().registerReceiverAsUser( 2611 mBroadcastReceiver, UserHandle.ALL, filter, null, null); 2612 final IntentFilter uidFilter = new IntentFilter(Intent.ACTION_UID_REMOVED); 2613 getContext().registerReceiverAsUser( 2614 mBroadcastReceiver, UserHandle.ALL, uidFilter, null, null); 2615 final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED); 2616 userFilter.addAction(Intent.ACTION_USER_ADDED); 2617 getContext().registerReceiverAsUser( 2618 mBroadcastReceiver, UserHandle.ALL, userFilter, null, null); 2619 try { 2620 ActivityManager.getService().registerUidObserver(mUidObserver, 2621 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE 2622 | ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_ACTIVE, 2623 ActivityManager.PROCESS_STATE_UNKNOWN, null); 2624 } catch (RemoteException e) { 2625 // ignored; both services live in system_server 2626 } 2627 2628 mConcurrencyManager.onSystemReady(); 2629 2630 // Remove any jobs that are not associated with any of the current users. 2631 cancelJobsForNonExistentUsers(); 2632 2633 for (int i = mJobRestrictions.size() - 1; i >= 0; i--) { 2634 mJobRestrictions.get(i).onSystemServicesReady(); 2635 } 2636 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { 2637 synchronized (mLock) { 2638 // Let's go! 2639 mReadyToRock = true; 2640 mLocalDeviceIdleController = 2641 LocalServices.getService(DeviceIdleInternal.class); 2642 mConcurrencyManager.onThirdPartyAppsCanStart(); 2643 // Attach jobs to their controllers. 2644 mJobs.forEachJob((job) -> { 2645 for (int controller = 0; controller < mControllers.size(); controller++) { 2646 final StateController sc = mControllers.get(controller); 2647 sc.maybeStartTrackingJobLocked(job, null); 2648 } 2649 }); 2650 // GO GO GO! 2651 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 2652 } 2653 } 2654 } 2655 2656 /** 2657 * Called when we have a job status object that we need to insert in our 2658 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know 2659 * about. 2660 */ 2661 private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { 2662 if (!jobStatus.isPreparedLocked()) { 2663 Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus); 2664 } 2665 jobStatus.enqueueTime = sElapsedRealtimeClock.millis(); 2666 final boolean update = lastJob != null; 2667 mJobs.add(jobStatus); 2668 // Clear potentially cached INVALID_JOB_ID reason. 2669 resetPendingJobReasonCache(jobStatus); 2670 if (mReadyToRock) { 2671 for (int i = 0; i < mControllers.size(); i++) { 2672 StateController controller = mControllers.get(i); 2673 if (update) { 2674 controller.maybeStopTrackingJobLocked(jobStatus, null); 2675 } 2676 controller.maybeStartTrackingJobLocked(jobStatus, lastJob); 2677 } 2678 } 2679 } 2680 2681 /** 2682 * Called when we want to remove a JobStatus object that we've finished executing. 2683 * 2684 * @return true if the job was removed. 2685 */ 2686 private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, 2687 boolean removeFromPersisted) { 2688 // Deal with any remaining work items in the old job. 2689 jobStatus.stopTrackingJobLocked(incomingJob); 2690 2691 synchronized (mPendingJobReasonCache) { 2692 SparseIntArray reasonCache = 2693 mPendingJobReasonCache.get(jobStatus.getUid(), jobStatus.getNamespace()); 2694 if (reasonCache != null) { 2695 reasonCache.delete(jobStatus.getJobId()); 2696 } 2697 } 2698 2699 // Remove from store as well as controllers. 2700 final boolean removed = mJobs.remove(jobStatus, removeFromPersisted); 2701 if (!removed) { 2702 // We never create JobStatus objects for the express purpose of removing them, and this 2703 // method is only ever called for jobs that were saved in the JobStore at some point, 2704 // so if we can't find it, something may be wrong. As of Android T, there is a 2705 // legitimate code path where removed is false --- when an actively running job is 2706 // cancelled (eg. via JobScheduler.cancel() or the app scheduling a new job with the 2707 // same job ID), we remove it from the JobStore and tell the JobServiceContext to stop 2708 // running the job. Once the job stops running, we then call this method again. 2709 // TODO: rework code so we don't intentionally call this method twice for the same job 2710 Slog.w(TAG, "Job didn't exist in JobStore: " + jobStatus.toShortString()); 2711 } 2712 if (mReadyToRock) { 2713 for (int i = 0; i < mControllers.size(); i++) { 2714 StateController controller = mControllers.get(i); 2715 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob); 2716 } 2717 } 2718 return removed; 2719 } 2720 2721 /** Remove the pending job reason for this job from the cache. */ 2722 void resetPendingJobReasonCache(@NonNull JobStatus jobStatus) { 2723 synchronized (mPendingJobReasonCache) { 2724 final SparseIntArray reasons = 2725 mPendingJobReasonCache.get(jobStatus.getUid(), jobStatus.getNamespace()); 2726 if (reasons != null) { 2727 reasons.delete(jobStatus.getJobId()); 2728 } 2729 } 2730 } 2731 2732 /** Return {@code true} if the specified job is currently executing. */ 2733 @GuardedBy("mLock") 2734 public boolean isCurrentlyRunningLocked(JobStatus job) { 2735 return mConcurrencyManager.isJobRunningLocked(job); 2736 } 2737 2738 /** @see JobConcurrencyManager#isJobInOvertimeLocked(JobStatus) */ 2739 @GuardedBy("mLock") 2740 public boolean isJobInOvertimeLocked(JobStatus job) { 2741 return mConcurrencyManager.isJobInOvertimeLocked(job); 2742 } 2743 2744 private void noteJobPending(JobStatus job) { 2745 mJobPackageTracker.notePending(job); 2746 } 2747 2748 void noteJobsPending(List<JobStatus> jobs) { 2749 for (int i = jobs.size() - 1; i >= 0; i--) { 2750 noteJobPending(jobs.get(i)); 2751 } 2752 } 2753 2754 private void noteJobNonPending(JobStatus job) { 2755 mJobPackageTracker.noteNonpending(job); 2756 } 2757 2758 private void clearPendingJobQueue() { 2759 JobStatus job; 2760 mPendingJobQueue.resetIterator(); 2761 while ((job = mPendingJobQueue.next()) != null) { 2762 noteJobNonPending(job); 2763 } 2764 mPendingJobQueue.clear(); 2765 } 2766 2767 /** 2768 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to 2769 * specify an override deadline on a failed job (the failed job will run even though it's not 2770 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any 2771 * ready job with {@link JobStatus#getNumPreviousAttempts()} > 0 will be executed. 2772 * 2773 * @param failureToReschedule Provided job status that we will reschedule. 2774 * @return A newly instantiated JobStatus with the same constraints as the last job except 2775 * with adjusted timing constraints, or {@code null} if the job shouldn't be rescheduled for 2776 * some policy reason. 2777 * @see #maybeQueueReadyJobsForExecutionLocked 2778 */ 2779 @Nullable 2780 @VisibleForTesting 2781 JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule, 2782 @JobParameters.StopReason int stopReason, int internalStopReason) { 2783 if (internalStopReason == JobParameters.INTERNAL_STOP_REASON_USER_UI_STOP 2784 && failureToReschedule.isUserVisibleJob()) { 2785 // If a user stops an app via Task Manager and the job was user-visible, then assume 2786 // the user wanted to stop that task and not let it run in the future. It's in the 2787 // app's best interests to provide action buttons in their notification to avoid this 2788 // scenario. 2789 Slog.i(TAG, 2790 "Dropping " + failureToReschedule.toShortString() + " because of user stop"); 2791 return null; 2792 } 2793 2794 final long elapsedNowMillis = sElapsedRealtimeClock.millis(); 2795 final JobInfo job = failureToReschedule.getJob(); 2796 2797 final long initialBackoffMillis = job.getInitialBackoffMillis(); 2798 int numFailures = failureToReschedule.getNumFailures(); 2799 int numSystemStops = failureToReschedule.getNumSystemStops(); 2800 // We should back off slowly if JobScheduler keeps stopping the job, 2801 // but back off immediately if the issue appeared to be the app's fault 2802 // or the user stopped the job somehow. 2803 if (internalStopReason == JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH 2804 || internalStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT 2805 || internalStopReason == JobParameters.INTERNAL_STOP_REASON_ANR 2806 || stopReason == JobParameters.STOP_REASON_USER) { 2807 numFailures++; 2808 } else { 2809 numSystemStops++; 2810 } 2811 final int backoffAttempts = 2812 numFailures + numSystemStops / mConstants.SYSTEM_STOP_TO_FAILURE_RATIO; 2813 final long earliestRuntimeMs; 2814 2815 if (backoffAttempts == 0) { 2816 earliestRuntimeMs = JobStatus.NO_EARLIEST_RUNTIME; 2817 } else { 2818 long delayMillis; 2819 switch (job.getBackoffPolicy()) { 2820 case JobInfo.BACKOFF_POLICY_LINEAR: { 2821 long backoff = initialBackoffMillis; 2822 if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME_MS) { 2823 backoff = mConstants.MIN_LINEAR_BACKOFF_TIME_MS; 2824 } 2825 delayMillis = backoff * backoffAttempts; 2826 } 2827 break; 2828 default: 2829 if (DEBUG) { 2830 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential."); 2831 } 2832 // Intentional fallthrough. 2833 case JobInfo.BACKOFF_POLICY_EXPONENTIAL: { 2834 long backoff = initialBackoffMillis; 2835 if (backoff < mConstants.MIN_EXP_BACKOFF_TIME_MS) { 2836 backoff = mConstants.MIN_EXP_BACKOFF_TIME_MS; 2837 } 2838 delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1); 2839 } 2840 break; 2841 } 2842 delayMillis = 2843 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS); 2844 earliestRuntimeMs = elapsedNowMillis + delayMillis; 2845 } 2846 JobStatus newJob = new JobStatus(failureToReschedule, 2847 earliestRuntimeMs, 2848 JobStatus.NO_LATEST_RUNTIME, numFailures, numSystemStops, 2849 failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis(), 2850 failureToReschedule.getCumulativeExecutionTimeMs()); 2851 if (stopReason == JobParameters.STOP_REASON_USER) { 2852 // Demote all jobs to regular for user stops so they don't keep privileges. 2853 newJob.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER); 2854 } 2855 if (newJob.getCumulativeExecutionTimeMs() >= mConstants.RUNTIME_CUMULATIVE_UI_LIMIT_MS 2856 && newJob.shouldTreatAsUserInitiatedJob()) { 2857 newJob.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ); 2858 } 2859 if (job.isPeriodic()) { 2860 newJob.setOriginalLatestRunTimeElapsed( 2861 failureToReschedule.getOriginalLatestRunTimeElapsed()); 2862 } 2863 for (int ic = 0; ic < mControllers.size(); ic++) { 2864 StateController controller = mControllers.get(ic); 2865 controller.rescheduleForFailureLocked(newJob, failureToReschedule); 2866 } 2867 return newJob; 2868 } 2869 2870 /** 2871 * Maximum time buffer in which JobScheduler will try to optimize periodic job scheduling. This 2872 * does not cause a job's period to be larger than requested (eg: if the requested period is 2873 * shorter than this buffer). This is used to put a limit on when JobScheduler will intervene 2874 * and try to optimize scheduling if the current job finished less than this amount of time to 2875 * the start of the next period 2876 */ 2877 private static final long PERIODIC_JOB_WINDOW_BUFFER = 30 * MINUTE_IN_MILLIS; 2878 2879 /** The maximum period a periodic job can have. Anything higher will be clamped down to this. */ 2880 public static final long MAX_ALLOWED_PERIOD_MS = 365 * 24 * 60 * 60 * 1000L; 2881 2882 /** 2883 * Called after a periodic has executed so we can reschedule it. We take the last execution 2884 * time of the job to be the time of completion (i.e. the time at which this function is 2885 * called). 2886 * <p>This could be inaccurate b/c the job can run for as long as 2887 * {@link Constants#DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS}, but 2888 * will lead to underscheduling at least, rather than if we had taken the last execution time 2889 * to be the start of the execution. 2890 * 2891 * @return A new job representing the execution criteria for this instantiation of the 2892 * recurring job. 2893 */ 2894 @VisibleForTesting 2895 JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) { 2896 final long elapsedNow = sElapsedRealtimeClock.millis(); 2897 final long newLatestRuntimeElapsed; 2898 // Make sure period is in the interval [min_possible_period, max_possible_period]. 2899 final long period = Math.max(JobInfo.getMinPeriodMillis(), 2900 Math.min(MAX_ALLOWED_PERIOD_MS, periodicToReschedule.getJob().getIntervalMillis())); 2901 // Make sure flex is in the interval [min_possible_flex, period]. 2902 final long flex = Math.max(JobInfo.getMinFlexMillis(), 2903 Math.min(period, periodicToReschedule.getJob().getFlexMillis())); 2904 long rescheduleBuffer = 0; 2905 2906 long olrte = periodicToReschedule.getOriginalLatestRunTimeElapsed(); 2907 if (olrte < 0 || olrte == JobStatus.NO_LATEST_RUNTIME) { 2908 Slog.wtf(TAG, "Invalid periodic job original latest run time: " + olrte); 2909 olrte = elapsedNow; 2910 } 2911 final long latestRunTimeElapsed = olrte; 2912 2913 final long diffMs = Math.abs(elapsedNow - latestRunTimeElapsed); 2914 if (elapsedNow > latestRunTimeElapsed) { 2915 // The job ran past its expected run window. Have it count towards the current window 2916 // and schedule a new job for the next window. 2917 if (DEBUG) { 2918 Slog.i(TAG, "Periodic job ran after its intended window by " + diffMs + " ms"); 2919 } 2920 long numSkippedWindows = (diffMs / period) + 1; // +1 to include original window 2921 // Determine how far into a single period the job ran, and determine if it's too close 2922 // to the start of the next period. If the difference between the start of the execution 2923 // window and the previous execution time inside of the period is less than the 2924 // threshold, then we say that the job ran too close to the next period. 2925 if (period != flex && (period - flex - (diffMs % period)) <= flex / 6) { 2926 if (DEBUG) { 2927 Slog.d(TAG, "Custom flex job ran too close to next window."); 2928 } 2929 // For custom flex periods, if the job was run too close to the next window, 2930 // skip the next window and schedule for the following one. 2931 numSkippedWindows += 1; 2932 } 2933 newLatestRuntimeElapsed = latestRunTimeElapsed + (period * numSkippedWindows); 2934 } else { 2935 newLatestRuntimeElapsed = latestRunTimeElapsed + period; 2936 if (diffMs < PERIODIC_JOB_WINDOW_BUFFER && diffMs < period / 6) { 2937 // Add a little buffer to the start of the next window so the job doesn't run 2938 // too soon after this completed one. 2939 rescheduleBuffer = Math.min(PERIODIC_JOB_WINDOW_BUFFER, period / 6 - diffMs); 2940 } 2941 } 2942 2943 if (newLatestRuntimeElapsed < elapsedNow) { 2944 Slog.wtf(TAG, "Rescheduling calculated latest runtime in the past: " 2945 + newLatestRuntimeElapsed); 2946 return new JobStatus(periodicToReschedule, 2947 elapsedNow + period - flex, elapsedNow + period, 2948 0 /* numFailures */, 0 /* numSystemStops */, 2949 sSystemClock.millis() /* lastSuccessfulRunTime */, 2950 periodicToReschedule.getLastFailedRunTime(), 2951 0 /* Reset cumulativeExecutionTime because of successful execution */); 2952 } 2953 2954 final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed 2955 - Math.min(flex, period - rescheduleBuffer); 2956 2957 if (DEBUG) { 2958 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" + 2959 newEarliestRunTimeElapsed / 1000 + ", " + newLatestRuntimeElapsed / 1000 2960 + "]s"); 2961 } 2962 return new JobStatus(periodicToReschedule, 2963 newEarliestRunTimeElapsed, newLatestRuntimeElapsed, 2964 0 /* numFailures */, 0 /* numSystemStops */, 2965 sSystemClock.millis() /* lastSuccessfulRunTime */, 2966 periodicToReschedule.getLastFailedRunTime(), 2967 0 /* Reset cumulativeExecutionTime because of successful execution */); 2968 } 2969 2970 @VisibleForTesting 2971 void maybeProcessBuggyJob(@NonNull JobStatus jobStatus, int debugStopReason) { 2972 boolean jobTimedOut = debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT; 2973 // If madeActive = 0, the job never actually started. 2974 if (!jobTimedOut && jobStatus.madeActive > 0) { 2975 final long executionDurationMs = sUptimeMillisClock.millis() - jobStatus.madeActive; 2976 // The debug reason may be different if we stopped the job for some other reason 2977 // (eg. constraints), so look at total execution time to be safe. 2978 if (jobStatus.startedAsUserInitiatedJob) { 2979 // TODO: factor in different min guarantees for different UI job types 2980 jobTimedOut = executionDurationMs >= mConstants.RUNTIME_MIN_UI_GUARANTEE_MS; 2981 } else if (jobStatus.startedAsExpeditedJob) { 2982 jobTimedOut = executionDurationMs >= mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS; 2983 } else { 2984 jobTimedOut = executionDurationMs >= mConstants.RUNTIME_MIN_GUARANTEE_MS; 2985 } 2986 } 2987 if (jobTimedOut) { 2988 final int userId = jobStatus.getTimeoutBlameUserId(); 2989 final String pkg = jobStatus.getTimeoutBlamePackageName(); 2990 mQuotaTracker.noteEvent(userId, pkg, 2991 jobStatus.startedAsUserInitiatedJob 2992 ? QUOTA_TRACKER_TIMEOUT_UIJ_TAG 2993 : (jobStatus.startedAsExpeditedJob 2994 ? QUOTA_TRACKER_TIMEOUT_EJ_TAG 2995 : QUOTA_TRACKER_TIMEOUT_REG_TAG)); 2996 if (!mQuotaTracker.noteEvent(userId, pkg, QUOTA_TRACKER_TIMEOUT_TOTAL_TAG)) { 2997 mAppStandbyInternal.restrictApp( 2998 pkg, userId, UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY); 2999 } 3000 } 3001 3002 if (debugStopReason == JobParameters.INTERNAL_STOP_REASON_ANR) { 3003 final int callingUserId = jobStatus.getUserId(); 3004 final String callingPkg = jobStatus.getServiceComponent().getPackageName(); 3005 if (!mQuotaTracker.noteEvent(callingUserId, callingPkg, QUOTA_TRACKER_ANR_TAG)) { 3006 mAppStandbyInternal.restrictApp(callingPkg, callingUserId, 3007 UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY); 3008 } 3009 } 3010 } 3011 3012 // JobCompletedListener implementations. 3013 3014 /** 3015 * A job just finished executing. We fetch the 3016 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on 3017 * whether we want to reschedule we re-add it to the controllers. 3018 * 3019 * @param jobStatus Completed job. 3020 * @param needsReschedule Whether the implementing class should reschedule this job. 3021 */ 3022 @Override 3023 public void onJobCompletedLocked(JobStatus jobStatus, @JobParameters.StopReason int stopReason, 3024 int debugStopReason, boolean needsReschedule) { 3025 if (DEBUG) { 3026 Slog.d(TAG, "Completed " + jobStatus + ", reason=" + debugStopReason 3027 + ", reschedule=" + needsReschedule); 3028 } 3029 3030 mLastCompletedJobs[mLastCompletedJobIndex] = jobStatus; 3031 mLastCompletedJobTimeElapsed[mLastCompletedJobIndex] = sElapsedRealtimeClock.millis(); 3032 mLastCompletedJobIndex = (mLastCompletedJobIndex + 1) % NUM_COMPLETED_JOB_HISTORY; 3033 3034 maybeProcessBuggyJob(jobStatus, debugStopReason); 3035 3036 if (debugStopReason == JobParameters.INTERNAL_STOP_REASON_UNINSTALL 3037 || debugStopReason == JobParameters.INTERNAL_STOP_REASON_DATA_CLEARED) { 3038 // The job should have already been cleared from the rest of the JS tracking. No need 3039 // to go through all that flow again. 3040 jobStatus.unprepareLocked(); 3041 reportActiveLocked(); 3042 return; 3043 } 3044 3045 // Intentionally not checking expedited job quota here. An app can't find out if it's run 3046 // out of quota when it asks JS to reschedule an expedited job. Instead, the rescheduled 3047 // EJ will just be demoted to a regular job if the app has no EJ quota left. 3048 3049 // If the job wants to be rescheduled, we first need to make the next upcoming 3050 // job so we can transfer any appropriate state over from the previous job when 3051 // we stop it. 3052 final JobStatus rescheduledJob = needsReschedule 3053 ? getRescheduleJobForFailureLocked(jobStatus, stopReason, debugStopReason) : null; 3054 if (rescheduledJob != null 3055 && !rescheduledJob.shouldTreatAsUserInitiatedJob() 3056 && (debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT 3057 || debugStopReason == JobParameters.INTERNAL_STOP_REASON_PREEMPT)) { 3058 rescheduledJob.disallowRunInBatterySaverAndDoze(); 3059 } 3060 3061 // Do not write back immediately if this is a periodic job. The job may get lost if system 3062 // shuts down before it is added back. 3063 if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) { 3064 if (DEBUG) { 3065 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?"); 3066 } 3067 JobStatus newJs = mJobs.getJobByUidAndJobId( 3068 jobStatus.getUid(), jobStatus.getNamespace(), jobStatus.getJobId()); 3069 if (newJs != null) { 3070 // This job was stopped because the app scheduled a new job with the same job ID. 3071 // Check if the new job is ready to run. 3072 mHandler.obtainMessage(MSG_CHECK_INDIVIDUAL_JOB, newJs).sendToTarget(); 3073 } 3074 return; 3075 } 3076 3077 if (rescheduledJob != null) { 3078 try { 3079 rescheduledJob.prepareLocked(); 3080 } catch (SecurityException e) { 3081 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob); 3082 } 3083 startTrackingJobLocked(rescheduledJob, jobStatus); 3084 } else if (jobStatus.getJob().isPeriodic()) { 3085 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus); 3086 try { 3087 rescheduledPeriodic.prepareLocked(); 3088 } catch (SecurityException e) { 3089 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic); 3090 } 3091 startTrackingJobLocked(rescheduledPeriodic, jobStatus); 3092 } 3093 jobStatus.unprepareLocked(); 3094 reportActiveLocked(); 3095 } 3096 3097 // StateChangedListener implementations. 3098 3099 /** 3100 * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} to run 3101 * through a list of jobs and start/stop any whose status has changed. 3102 */ 3103 @Override 3104 public void onControllerStateChanged(@Nullable ArraySet<JobStatus> changedJobs) { 3105 if (changedJobs == null) { 3106 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 3107 synchronized (mPendingJobReasonCache) { 3108 mPendingJobReasonCache.clear(); 3109 } 3110 } else if (changedJobs.size() > 0) { 3111 synchronized (mLock) { 3112 mChangedJobList.addAll(changedJobs); 3113 } 3114 mHandler.obtainMessage(MSG_CHECK_CHANGED_JOB_LIST).sendToTarget(); 3115 synchronized (mPendingJobReasonCache) { 3116 for (int i = changedJobs.size() - 1; i >= 0; --i) { 3117 final JobStatus job = changedJobs.valueAt(i); 3118 resetPendingJobReasonCache(job); 3119 } 3120 } 3121 } 3122 } 3123 3124 @Override 3125 public void onRestrictionStateChanged(@NonNull JobRestriction restriction, 3126 boolean stopOvertimeJobs) { 3127 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 3128 if (stopOvertimeJobs) { 3129 synchronized (mLock) { 3130 mConcurrencyManager.maybeStopOvertimeJobsLocked(restriction); 3131 } 3132 } 3133 } 3134 3135 @Override 3136 public void onRunJobNow(JobStatus jobStatus) { 3137 if (jobStatus == null) { 3138 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget(); 3139 } else { 3140 mHandler.obtainMessage(MSG_CHECK_INDIVIDUAL_JOB, jobStatus).sendToTarget(); 3141 } 3142 } 3143 3144 final private class JobHandler extends Handler { 3145 3146 public JobHandler(Looper looper) { 3147 super(looper); 3148 } 3149 3150 @Override 3151 public void handleMessage(Message message) { 3152 synchronized (mLock) { 3153 if (!mReadyToRock) { 3154 return; 3155 } 3156 switch (message.what) { 3157 case MSG_CHECK_INDIVIDUAL_JOB: { 3158 JobStatus js = (JobStatus) message.obj; 3159 if (js != null) { 3160 if (isReadyToBeExecutedLocked(js)) { 3161 mJobPackageTracker.notePending(js); 3162 mPendingJobQueue.add(js); 3163 } 3164 mChangedJobList.remove(js); 3165 } else { 3166 Slog.e(TAG, "Given null job to check individually"); 3167 } 3168 } break; 3169 case MSG_CHECK_JOB: 3170 if (DEBUG) { 3171 Slog.d(TAG, "MSG_CHECK_JOB"); 3172 } 3173 if (mReportedActive) { 3174 // if jobs are currently being run, queue all ready jobs for execution. 3175 queueReadyJobsForExecutionLocked(); 3176 } else { 3177 // Check the list of jobs and run some of them if we feel inclined. 3178 maybeQueueReadyJobsForExecutionLocked(); 3179 } 3180 break; 3181 case MSG_CHECK_JOB_GREEDY: 3182 if (DEBUG) { 3183 Slog.d(TAG, "MSG_CHECK_JOB_GREEDY"); 3184 } 3185 queueReadyJobsForExecutionLocked(); 3186 break; 3187 case MSG_CHECK_CHANGED_JOB_LIST: 3188 if (DEBUG) { 3189 Slog.d(TAG, "MSG_CHECK_CHANGED_JOB_LIST"); 3190 } 3191 checkChangedJobListLocked(); 3192 break; 3193 case MSG_STOP_JOB: 3194 cancelJobImplLocked((JobStatus) message.obj, null, message.arg1, 3195 JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED, 3196 "app no longer allowed to run"); 3197 break; 3198 3199 case MSG_UID_STATE_CHANGED: { 3200 final SomeArgs args = (SomeArgs) message.obj; 3201 final int uid = args.argi1; 3202 final int procState = args.argi2; 3203 final int capabilities = args.argi3; 3204 updateUidState(uid, procState, capabilities); 3205 args.recycle(); 3206 break; 3207 } 3208 case MSG_UID_GONE: { 3209 final int uid = message.arg1; 3210 final boolean disabled = message.arg2 != 0; 3211 updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY, 3212 ActivityManager.PROCESS_CAPABILITY_NONE); 3213 if (disabled) { 3214 cancelJobsForUid(uid, 3215 /* includeSourceApp */ true, 3216 JobParameters.STOP_REASON_BACKGROUND_RESTRICTION, 3217 JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED, 3218 "uid gone"); 3219 } 3220 synchronized (mLock) { 3221 mDeviceIdleJobsController.setUidActiveLocked(uid, false); 3222 } 3223 break; 3224 } 3225 case MSG_UID_ACTIVE: { 3226 final int uid = message.arg1; 3227 synchronized (mLock) { 3228 mDeviceIdleJobsController.setUidActiveLocked(uid, true); 3229 } 3230 break; 3231 } 3232 case MSG_UID_IDLE: { 3233 final int uid = message.arg1; 3234 final boolean disabled = message.arg2 != 0; 3235 if (disabled) { 3236 cancelJobsForUid(uid, 3237 /* includeSourceApp */ true, 3238 JobParameters.STOP_REASON_BACKGROUND_RESTRICTION, 3239 JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED, 3240 "app uid idle"); 3241 } 3242 synchronized (mLock) { 3243 mDeviceIdleJobsController.setUidActiveLocked(uid, false); 3244 } 3245 break; 3246 } 3247 3248 case MSG_CHECK_MEDIA_EXEMPTION: { 3249 final SomeArgs args = (SomeArgs) message.obj; 3250 synchronized (mLock) { 3251 updateMediaBackupExemptionLocked( 3252 args.argi1, (String) args.arg1, (String) args.arg2); 3253 } 3254 args.recycle(); 3255 break; 3256 } 3257 3258 case MSG_INFORM_OBSERVER_OF_ALL_USER_VISIBLE_JOBS: { 3259 final IUserVisibleJobObserver observer = 3260 (IUserVisibleJobObserver) message.obj; 3261 synchronized (mLock) { 3262 for (int i = mConcurrencyManager.mActiveServices.size() - 1; i >= 0; 3263 --i) { 3264 JobServiceContext context = 3265 mConcurrencyManager.mActiveServices.get(i); 3266 final JobStatus jobStatus = context.getRunningJobLocked(); 3267 if (jobStatus != null && jobStatus.isUserVisibleJob()) { 3268 try { 3269 observer.onUserVisibleJobStateChanged( 3270 jobStatus.getUserVisibleJobSummary(), 3271 /* isRunning */ true); 3272 } catch (RemoteException e) { 3273 // Will be unregistered automatically by 3274 // RemoteCallbackList's dead-object tracking, 3275 // so don't need to remove it here. 3276 break; 3277 } 3278 } 3279 } 3280 } 3281 break; 3282 } 3283 3284 case MSG_INFORM_OBSERVERS_OF_USER_VISIBLE_JOB_CHANGE: { 3285 final SomeArgs args = (SomeArgs) message.obj; 3286 final JobServiceContext context = (JobServiceContext) args.arg1; 3287 final JobStatus jobStatus = (JobStatus) args.arg2; 3288 final UserVisibleJobSummary summary = jobStatus.getUserVisibleJobSummary(); 3289 final boolean isRunning = args.argi1 == 1; 3290 for (int i = mUserVisibleJobObservers.beginBroadcast() - 1; i >= 0; --i) { 3291 try { 3292 mUserVisibleJobObservers.getBroadcastItem(i) 3293 .onUserVisibleJobStateChanged(summary, isRunning); 3294 } catch (RemoteException e) { 3295 // Will be unregistered automatically by RemoteCallbackList's 3296 // dead-object tracking, so nothing we need to do here. 3297 } 3298 } 3299 mUserVisibleJobObservers.finishBroadcast(); 3300 args.recycle(); 3301 break; 3302 } 3303 } 3304 maybeRunPendingJobsLocked(); 3305 } 3306 } 3307 } 3308 3309 /** 3310 * Check if a job is restricted by any of the declared {@link JobRestriction JobRestrictions}. 3311 * Note, that the jobs with {@link JobInfo#BIAS_FOREGROUND_SERVICE} bias or higher may not 3312 * be restricted, thus we won't even perform the check, but simply return null early. 3313 * 3314 * @param job to be checked 3315 * @return the first {@link JobRestriction} restricting the given job that has been found; null 3316 * - if passes all the restrictions or has {@link JobInfo#BIAS_FOREGROUND_SERVICE} bias 3317 * or higher. 3318 */ 3319 @GuardedBy("mLock") 3320 JobRestriction checkIfRestricted(JobStatus job) { 3321 if (evaluateJobBiasLocked(job) >= JobInfo.BIAS_FOREGROUND_SERVICE) { 3322 // Jobs with BIAS_FOREGROUND_SERVICE or higher should not be restricted 3323 return null; 3324 } 3325 for (int i = mJobRestrictions.size() - 1; i >= 0; i--) { 3326 final JobRestriction restriction = mJobRestrictions.get(i); 3327 if (restriction.isJobRestricted(job)) { 3328 return restriction; 3329 } 3330 } 3331 return null; 3332 } 3333 3334 @GuardedBy("mLock") 3335 private void stopNonReadyActiveJobsLocked() { 3336 mConcurrencyManager.stopNonReadyActiveJobsLocked(); 3337 } 3338 3339 /** 3340 * Run through list of jobs and execute all possible - at least one is expired so we do 3341 * as many as we can. 3342 */ 3343 @GuardedBy("mLock") 3344 private void queueReadyJobsForExecutionLocked() { 3345 // This method will check and capture all ready jobs, so we don't need to keep any messages 3346 // in the queue. 3347 mHandler.removeMessages(MSG_CHECK_JOB_GREEDY); 3348 mHandler.removeMessages(MSG_CHECK_INDIVIDUAL_JOB); 3349 // MSG_CHECK_JOB is a weaker form of _GREEDY. Since we're checking and queueing all ready 3350 // jobs, we don't need to keep any MSG_CHECK_JOB messages in the queue. 3351 mHandler.removeMessages(MSG_CHECK_JOB); 3352 // MSG_CHECK_CHANGED_JOB_LIST is a weaker form of _GREEDY. Since we're checking and queueing 3353 // all ready jobs, we don't need to keep any MSG_CHECK_CHANGED_JOB_LIST messages in the 3354 // queue. 3355 mHandler.removeMessages(MSG_CHECK_CHANGED_JOB_LIST); 3356 mChangedJobList.clear(); 3357 if (DEBUG) { 3358 Slog.d(TAG, "queuing all ready jobs for execution:"); 3359 } 3360 clearPendingJobQueue(); 3361 stopNonReadyActiveJobsLocked(); 3362 mJobs.forEachJob(mReadyQueueFunctor); 3363 mReadyQueueFunctor.postProcessLocked(); 3364 3365 if (DEBUG) { 3366 final int queuedJobs = mPendingJobQueue.size(); 3367 if (queuedJobs == 0) { 3368 Slog.d(TAG, "No jobs pending."); 3369 } else { 3370 Slog.d(TAG, queuedJobs + " jobs queued."); 3371 } 3372 } 3373 } 3374 3375 final class ReadyJobQueueFunctor implements Consumer<JobStatus> { 3376 final ArrayList<JobStatus> newReadyJobs = new ArrayList<>(); 3377 3378 @Override 3379 public void accept(JobStatus job) { 3380 if (isReadyToBeExecutedLocked(job)) { 3381 if (DEBUG) { 3382 Slog.d(TAG, " queued " + job.toShortString()); 3383 } 3384 newReadyJobs.add(job); 3385 } 3386 } 3387 3388 @GuardedBy("mLock") 3389 private void postProcessLocked() { 3390 noteJobsPending(newReadyJobs); 3391 mPendingJobQueue.addAll(newReadyJobs); 3392 3393 newReadyJobs.clear(); 3394 } 3395 } 3396 3397 private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor(); 3398 3399 /** 3400 * The state of at least one job has changed. Here is where we could enforce various 3401 * policies on when we want to execute jobs. 3402 */ 3403 final class MaybeReadyJobQueueFunctor implements Consumer<JobStatus> { 3404 int forceBatchedCount; 3405 int unbatchedCount; 3406 final List<JobStatus> runnableJobs = new ArrayList<>(); 3407 3408 public MaybeReadyJobQueueFunctor() { 3409 reset(); 3410 } 3411 3412 @Override 3413 public void accept(JobStatus job) { 3414 final boolean isRunning = isCurrentlyRunningLocked(job); 3415 if (isReadyToBeExecutedLocked(job, false)) { 3416 if (mActivityManagerInternal.isAppStartModeDisabled(job.getUid(), 3417 job.getJob().getService().getPackageName())) { 3418 Slog.w(TAG, "Aborting job " + job.getUid() + ":" 3419 + job.getJob().toString() + " -- package not allowed to start"); 3420 if (isRunning) { 3421 mHandler.obtainMessage(MSG_STOP_JOB, 3422 JobParameters.STOP_REASON_BACKGROUND_RESTRICTION, 0, job) 3423 .sendToTarget(); 3424 } else if (mPendingJobQueue.remove(job)) { 3425 noteJobNonPending(job); 3426 } 3427 return; 3428 } 3429 3430 final boolean shouldForceBatchJob; 3431 if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) { 3432 // Never batch expedited or user-initiated jobs, even for RESTRICTED apps. 3433 shouldForceBatchJob = false; 3434 } else if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX) { 3435 // Restricted jobs must always be batched 3436 shouldForceBatchJob = true; 3437 } else if (job.getJob().isPrefetch()) { 3438 // Only relax batching on prefetch jobs if we expect the app to be launched 3439 // relatively soon. PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS defines what 3440 // "relatively soon" means. 3441 final long relativelySoonCutoffTime = sSystemClock.millis() 3442 + mConstants.PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS; 3443 shouldForceBatchJob = 3444 mPrefetchController.getNextEstimatedLaunchTimeLocked(job) 3445 > relativelySoonCutoffTime; 3446 } else if (job.getNumPreviousAttempts() > 0) { 3447 shouldForceBatchJob = false; 3448 } else { 3449 final long nowElapsed = sElapsedRealtimeClock.millis(); 3450 final boolean batchDelayExpired = job.getFirstForceBatchedTimeElapsed() > 0 3451 && nowElapsed - job.getFirstForceBatchedTimeElapsed() 3452 >= mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS; 3453 shouldForceBatchJob = 3454 mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT > 1 3455 && job.getEffectiveStandbyBucket() != ACTIVE_INDEX 3456 && job.getEffectiveStandbyBucket() != EXEMPTED_INDEX 3457 && !batchDelayExpired; 3458 } 3459 3460 if (shouldForceBatchJob) { 3461 // Force batching non-ACTIVE jobs. Don't include them in the other counts. 3462 forceBatchedCount++; 3463 if (job.getFirstForceBatchedTimeElapsed() == 0) { 3464 job.setFirstForceBatchedTimeElapsed(sElapsedRealtimeClock.millis()); 3465 } 3466 } else { 3467 unbatchedCount++; 3468 } 3469 if (!isRunning) { 3470 runnableJobs.add(job); 3471 } 3472 } else { 3473 if (isRunning) { 3474 final int internalStopReason; 3475 final String debugReason; 3476 if (!job.isReady()) { 3477 if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX 3478 && job.getStopReason() == JobParameters.STOP_REASON_APP_STANDBY) { 3479 internalStopReason = 3480 JobParameters.INTERNAL_STOP_REASON_RESTRICTED_BUCKET; 3481 debugReason = "cancelled due to restricted bucket"; 3482 } else { 3483 internalStopReason = 3484 JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED; 3485 debugReason = "cancelled due to unsatisfied constraints"; 3486 } 3487 } else { 3488 final JobRestriction restriction = checkIfRestricted(job); 3489 if (restriction != null) { 3490 internalStopReason = restriction.getInternalReason(); 3491 debugReason = "restricted due to " 3492 + JobParameters.getInternalReasonCodeDescription( 3493 internalStopReason); 3494 } else { 3495 internalStopReason = JobParameters.INTERNAL_STOP_REASON_UNKNOWN; 3496 debugReason = "couldn't figure out why the job should stop running"; 3497 } 3498 } 3499 mConcurrencyManager.stopJobOnServiceContextLocked(job, job.getStopReason(), 3500 internalStopReason, debugReason); 3501 } else if (mPendingJobQueue.remove(job)) { 3502 noteJobNonPending(job); 3503 } 3504 } 3505 } 3506 3507 @GuardedBy("mLock") 3508 @VisibleForTesting 3509 void postProcessLocked() { 3510 if (unbatchedCount > 0 3511 || forceBatchedCount >= mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT) { 3512 if (DEBUG) { 3513 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs."); 3514 } 3515 noteJobsPending(runnableJobs); 3516 mPendingJobQueue.addAll(runnableJobs); 3517 } else { 3518 if (DEBUG) { 3519 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything."); 3520 } 3521 final int numRunnableJobs = runnableJobs.size(); 3522 if (numRunnableJobs > 0) { 3523 synchronized (mPendingJobReasonCache) { 3524 for (int i = 0; i < numRunnableJobs; ++i) { 3525 final JobStatus job = runnableJobs.get(i); 3526 SparseIntArray reasons = 3527 mPendingJobReasonCache.get(job.getUid(), job.getNamespace()); 3528 if (reasons == null) { 3529 reasons = new SparseIntArray(); 3530 mPendingJobReasonCache 3531 .add(job.getUid(), job.getNamespace(), reasons); 3532 } 3533 // We're force batching these jobs, so consider it an optimization 3534 // policy reason. 3535 reasons.put(job.getJobId(), 3536 JobScheduler.PENDING_JOB_REASON_JOB_SCHEDULER_OPTIMIZATION); 3537 } 3538 } 3539 } 3540 } 3541 3542 // Be ready for next time 3543 reset(); 3544 } 3545 3546 @VisibleForTesting 3547 void reset() { 3548 forceBatchedCount = 0; 3549 unbatchedCount = 0; 3550 runnableJobs.clear(); 3551 } 3552 } 3553 3554 private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor(); 3555 3556 @GuardedBy("mLock") 3557 private void maybeQueueReadyJobsForExecutionLocked() { 3558 mHandler.removeMessages(MSG_CHECK_JOB); 3559 // This method will evaluate all jobs, so we don't need to keep any messages for a subset 3560 // of jobs in the queue. 3561 mHandler.removeMessages(MSG_CHECK_CHANGED_JOB_LIST); 3562 mChangedJobList.clear(); 3563 if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs..."); 3564 3565 clearPendingJobQueue(); 3566 stopNonReadyActiveJobsLocked(); 3567 mJobs.forEachJob(mMaybeQueueFunctor); 3568 mMaybeQueueFunctor.postProcessLocked(); 3569 } 3570 3571 @GuardedBy("mLock") 3572 private void checkChangedJobListLocked() { 3573 mHandler.removeMessages(MSG_CHECK_CHANGED_JOB_LIST); 3574 if (DEBUG) { 3575 Slog.d(TAG, "Check changed jobs..."); 3576 } 3577 if (mChangedJobList.size() == 0) { 3578 return; 3579 } 3580 3581 mChangedJobList.forEach(mMaybeQueueFunctor); 3582 mMaybeQueueFunctor.postProcessLocked(); 3583 mChangedJobList.clear(); 3584 } 3585 3586 @GuardedBy("mLock") 3587 private void updateMediaBackupExemptionLocked(int userId, @Nullable String oldPkg, 3588 @Nullable String newPkg) { 3589 final Predicate<JobStatus> shouldProcessJob = 3590 (job) -> job.getSourceUserId() == userId 3591 && (job.getSourcePackageName().equals(oldPkg) 3592 || job.getSourcePackageName().equals(newPkg)); 3593 mJobs.forEachJob(shouldProcessJob, 3594 (job) -> { 3595 if (job.updateMediaBackupExemptionStatus()) { 3596 mChangedJobList.add(job); 3597 } 3598 }); 3599 mHandler.sendEmptyMessage(MSG_CHECK_CHANGED_JOB_LIST); 3600 } 3601 3602 /** Returns true if both the calling and source users for the job are started. */ 3603 @GuardedBy("mLock") 3604 public boolean areUsersStartedLocked(final JobStatus job) { 3605 boolean sourceStarted = ArrayUtils.contains(mStartedUsers, job.getSourceUserId()); 3606 if (job.getUserId() == job.getSourceUserId()) { 3607 return sourceStarted; 3608 } 3609 return sourceStarted && ArrayUtils.contains(mStartedUsers, job.getUserId()); 3610 } 3611 3612 /** 3613 * Criteria for moving a job into the pending queue: 3614 * - It's ready. 3615 * - It's not pending. 3616 * - It's not already running on a JSC. 3617 * - The user that requested the job is running. 3618 * - The job's standby bucket has come due to be runnable. 3619 * - The component is enabled and runnable. 3620 */ 3621 @VisibleForTesting 3622 @GuardedBy("mLock") 3623 boolean isReadyToBeExecutedLocked(JobStatus job) { 3624 return isReadyToBeExecutedLocked(job, true); 3625 } 3626 3627 @GuardedBy("mLock") 3628 boolean isReadyToBeExecutedLocked(JobStatus job, boolean rejectActive) { 3629 final boolean jobReady = job.isReady() || evaluateControllerStatesLocked(job); 3630 3631 if (DEBUG) { 3632 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString() 3633 + " ready=" + jobReady); 3634 } 3635 3636 // This is a condition that is very likely to be false (most jobs that are 3637 // scheduled are sitting there, not ready yet) and very cheap to check (just 3638 // a few conditions on data in JobStatus). 3639 if (!jobReady) { 3640 if (job.getSourcePackageName().equals("android.jobscheduler.cts.jobtestapp")) { 3641 Slog.v(TAG, " NOT READY: " + job); 3642 } 3643 return false; 3644 } 3645 3646 final boolean jobExists = mJobs.containsJob(job); 3647 final boolean userStarted = areUsersStartedLocked(job); 3648 final boolean backingUp = mBackingUpUids.get(job.getSourceUid()); 3649 3650 if (DEBUG) { 3651 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString() 3652 + " exists=" + jobExists + " userStarted=" + userStarted 3653 + " backingUp=" + backingUp); 3654 } 3655 3656 // These are also fairly cheap to check, though they typically will not 3657 // be conditions we fail. 3658 if (!jobExists || !userStarted || backingUp) { 3659 return false; 3660 } 3661 3662 if (checkIfRestricted(job) != null) { 3663 return false; 3664 } 3665 3666 final boolean jobPending = mPendingJobQueue.contains(job); 3667 final boolean jobActive = rejectActive && mConcurrencyManager.isJobRunningLocked(job); 3668 3669 if (DEBUG) { 3670 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString() 3671 + " pending=" + jobPending + " active=" + jobActive); 3672 } 3673 3674 // These can be a little more expensive (especially jobActive, since we need to 3675 // go through the array of all potentially active jobs), so we are doing them 3676 // later... but still before checking with the package manager! 3677 if (jobPending || jobActive) { 3678 return false; 3679 } 3680 3681 // Validate that the defined package+service is still present & viable. 3682 return isComponentUsable(job); 3683 } 3684 3685 private boolean isComponentUsable(@NonNull JobStatus job) { 3686 final String processName = job.serviceProcessName; 3687 3688 if (processName == null) { 3689 if (DEBUG) { 3690 Slog.v(TAG, "isComponentUsable: " + job.toShortString() 3691 + " component not present"); 3692 } 3693 return false; 3694 } 3695 3696 // Everything else checked out so far, so this is the final yes/no check 3697 final boolean appIsBad = mActivityManagerInternal.isAppBad(processName, job.getUid()); 3698 if (DEBUG && appIsBad) { 3699 Slog.i(TAG, "App is bad for " + job.toShortString() + " so not runnable"); 3700 } 3701 return !appIsBad; 3702 } 3703 3704 /** 3705 * Gets each controller to evaluate the job's state 3706 * and then returns the value of {@link JobStatus#isReady()}. 3707 */ 3708 @VisibleForTesting 3709 boolean evaluateControllerStatesLocked(final JobStatus job) { 3710 for (int c = mControllers.size() - 1; c >= 0; --c) { 3711 final StateController sc = mControllers.get(c); 3712 sc.evaluateStateLocked(job); 3713 } 3714 return job.isReady(); 3715 } 3716 3717 /** 3718 * Returns true if non-job constraint components are in place -- if job.isReady() returns true 3719 * and this method returns true, then the job is ready to be executed. 3720 */ 3721 public boolean areComponentsInPlaceLocked(JobStatus job) { 3722 // This code is very similar to the code in isReadyToBeExecutedLocked --- it uses the same 3723 // conditions. 3724 3725 final boolean jobExists = mJobs.containsJob(job); 3726 final boolean userStarted = areUsersStartedLocked(job); 3727 final boolean backingUp = mBackingUpUids.get(job.getSourceUid()); 3728 3729 if (DEBUG) { 3730 Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString() 3731 + " exists=" + jobExists + " userStarted=" + userStarted 3732 + " backingUp=" + backingUp); 3733 } 3734 3735 // These are also fairly cheap to check, though they typically will not 3736 // be conditions we fail. 3737 if (!jobExists || !userStarted || backingUp) { 3738 return false; 3739 } 3740 3741 final JobRestriction restriction = checkIfRestricted(job); 3742 if (restriction != null) { 3743 if (DEBUG) { 3744 Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString() 3745 + " restricted due to " + restriction.getInternalReason()); 3746 } 3747 return false; 3748 } 3749 3750 // Job pending/active doesn't affect the readiness of a job. 3751 3752 // The expensive check: validate that the defined package+service is 3753 // still present & viable. 3754 return isComponentUsable(job); 3755 } 3756 3757 /** Returns the minimum amount of time we should let this job run before timing out. */ 3758 public long getMinJobExecutionGuaranteeMs(JobStatus job) { 3759 synchronized (mLock) { 3760 if (job.shouldTreatAsUserInitiatedJob() 3761 && checkRunUserInitiatedJobsPermission( 3762 job.getSourceUid(), job.getSourcePackageName())) { 3763 // The calling package is the one doing the work, so use it in the 3764 // timeout quota checks. 3765 final boolean isWithinTimeoutQuota = mQuotaTracker.isWithinQuota( 3766 job.getTimeoutBlameUserId(), job.getTimeoutBlamePackageName(), 3767 QUOTA_TRACKER_TIMEOUT_UIJ_TAG); 3768 final long upperLimitMs = isWithinTimeoutQuota 3769 ? mConstants.RUNTIME_UI_LIMIT_MS 3770 : mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; 3771 if (job.getJob().getRequiredNetwork() != null) { 3772 // User-initiated data transfers. 3773 if (mConstants.RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS) { 3774 final long estimatedTransferTimeMs = 3775 mConnectivityController.getEstimatedTransferTimeMs(job); 3776 if (estimatedTransferTimeMs == ConnectivityController.UNKNOWN_TIME) { 3777 return Math.min(upperLimitMs, 3778 mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS); 3779 } 3780 // Try to give the job at least as much time as we think the transfer 3781 // will take, but cap it at the maximum limit. 3782 final long factoredTransferTimeMs = (long) (estimatedTransferTimeMs 3783 * mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR); 3784 return Math.min(upperLimitMs, 3785 Math.max(factoredTransferTimeMs, 3786 mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS)); 3787 } 3788 return Math.min(upperLimitMs, 3789 Math.max(mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, 3790 mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS)); 3791 } 3792 return Math.min(upperLimitMs, mConstants.RUNTIME_MIN_UI_GUARANTEE_MS); 3793 } else if (job.shouldTreatAsExpeditedJob()) { 3794 // Don't guarantee RESTRICTED jobs more than 5 minutes. 3795 return job.getEffectiveStandbyBucket() != RESTRICTED_INDEX 3796 ? mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS 3797 : Math.min(mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 5 * MINUTE_IN_MILLIS); 3798 } else { 3799 return mConstants.RUNTIME_MIN_GUARANTEE_MS; 3800 } 3801 } 3802 } 3803 3804 /** Returns the maximum amount of time this job could run for. */ 3805 public long getMaxJobExecutionTimeMs(JobStatus job) { 3806 synchronized (mLock) { 3807 if (job.shouldTreatAsUserInitiatedJob() 3808 && checkRunUserInitiatedJobsPermission( 3809 job.getSourceUid(), job.getSourcePackageName()) 3810 && mQuotaTracker.isWithinQuota(job.getTimeoutBlameUserId(), 3811 job.getTimeoutBlamePackageName(), 3812 QUOTA_TRACKER_TIMEOUT_UIJ_TAG)) { 3813 return mConstants.RUNTIME_UI_LIMIT_MS; 3814 } 3815 if (job.shouldTreatAsUserInitiatedJob()) { 3816 return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; 3817 } 3818 // Only let the app use the higher runtime if it hasn't repeatedly timed out. 3819 final String timeoutTag = job.shouldTreatAsExpeditedJob() 3820 ? QUOTA_TRACKER_TIMEOUT_EJ_TAG : QUOTA_TRACKER_TIMEOUT_REG_TAG; 3821 final long upperLimitMs = 3822 mQuotaTracker.isWithinQuota(job.getTimeoutBlameUserId(), 3823 job.getTimeoutBlamePackageName(), timeoutTag) 3824 ? mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS 3825 : mConstants.RUNTIME_MIN_GUARANTEE_MS; 3826 return Math.min(upperLimitMs, 3827 mConstants.USE_TARE_POLICY 3828 ? mTareController.getMaxJobExecutionTimeMsLocked(job) 3829 : mQuotaController.getMaxJobExecutionTimeMsLocked(job)); 3830 } 3831 } 3832 3833 /** 3834 * Reconcile jobs in the pending queue against available execution contexts. 3835 * A controller can force a job into the pending queue even if it's already running, but 3836 * here is where we decide whether to actually execute it. 3837 */ 3838 void maybeRunPendingJobsLocked() { 3839 if (DEBUG) { 3840 Slog.d(TAG, "pending queue: " + mPendingJobQueue.size() + " jobs."); 3841 } 3842 mConcurrencyManager.assignJobsToContextsLocked(); 3843 reportActiveLocked(); 3844 } 3845 3846 private int adjustJobBias(int curBias, JobStatus job) { 3847 if (curBias < JobInfo.BIAS_TOP_APP) { 3848 float factor = mJobPackageTracker.getLoadFactor(job); 3849 if (factor >= mConstants.HEAVY_USE_FACTOR) { 3850 curBias += JobInfo.BIAS_ADJ_ALWAYS_RUNNING; 3851 } else if (factor >= mConstants.MODERATE_USE_FACTOR) { 3852 curBias += JobInfo.BIAS_ADJ_OFTEN_RUNNING; 3853 } 3854 } 3855 return curBias; 3856 } 3857 3858 int evaluateJobBiasLocked(JobStatus job) { 3859 int bias = job.getBias(); 3860 if (bias >= JobInfo.BIAS_BOUND_FOREGROUND_SERVICE) { 3861 return adjustJobBias(bias, job); 3862 } 3863 int override = mUidBiasOverride.get(job.getSourceUid(), 0); 3864 if (override != 0) { 3865 return adjustJobBias(override, job); 3866 } 3867 return adjustJobBias(bias, job); 3868 } 3869 3870 void informObserversOfUserVisibleJobChange(JobServiceContext context, JobStatus jobStatus, 3871 boolean isRunning) { 3872 SomeArgs args = SomeArgs.obtain(); 3873 args.arg1 = context; 3874 args.arg2 = jobStatus; 3875 args.argi1 = isRunning ? 1 : 0; 3876 mHandler.obtainMessage(MSG_INFORM_OBSERVERS_OF_USER_VISIBLE_JOB_CHANGE, args) 3877 .sendToTarget(); 3878 } 3879 3880 private final class BatteryStateTracker extends BroadcastReceiver { 3881 /** 3882 * Track whether we're "charging", where charging means that we're ready to commit to 3883 * doing work. 3884 */ 3885 private boolean mCharging; 3886 /** Keep track of whether the battery is charged enough that we want to do work. */ 3887 private boolean mBatteryNotLow; 3888 /** Sequence number of last broadcast. */ 3889 private int mLastBatterySeq = -1; 3890 3891 private BroadcastReceiver mMonitor; 3892 3893 BatteryStateTracker() { 3894 } 3895 3896 public void startTracking() { 3897 IntentFilter filter = new IntentFilter(); 3898 3899 // Battery health. 3900 filter.addAction(Intent.ACTION_BATTERY_LOW); 3901 filter.addAction(Intent.ACTION_BATTERY_OKAY); 3902 // Charging/not charging. 3903 filter.addAction(BatteryManager.ACTION_CHARGING); 3904 filter.addAction(BatteryManager.ACTION_DISCHARGING); 3905 getTestableContext().registerReceiver(this, filter); 3906 3907 // Initialise tracker state. 3908 BatteryManagerInternal batteryManagerInternal = 3909 LocalServices.getService(BatteryManagerInternal.class); 3910 mBatteryNotLow = !batteryManagerInternal.getBatteryLevelLow(); 3911 mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY); 3912 } 3913 3914 public void setMonitorBatteryLocked(boolean enabled) { 3915 if (enabled) { 3916 if (mMonitor == null) { 3917 mMonitor = new BroadcastReceiver() { 3918 @Override 3919 public void onReceive(Context context, Intent intent) { 3920 onReceiveInternal(intent); 3921 } 3922 }; 3923 IntentFilter filter = new IntentFilter(); 3924 filter.addAction(Intent.ACTION_BATTERY_CHANGED); 3925 getTestableContext().registerReceiver(mMonitor, filter); 3926 } 3927 } else if (mMonitor != null) { 3928 getTestableContext().unregisterReceiver(mMonitor); 3929 mMonitor = null; 3930 } 3931 } 3932 3933 public boolean isCharging() { 3934 return mCharging; 3935 } 3936 3937 public boolean isBatteryNotLow() { 3938 return mBatteryNotLow; 3939 } 3940 3941 public boolean isMonitoring() { 3942 return mMonitor != null; 3943 } 3944 3945 public int getSeq() { 3946 return mLastBatterySeq; 3947 } 3948 3949 @Override 3950 public void onReceive(Context context, Intent intent) { 3951 onReceiveInternal(intent); 3952 } 3953 3954 @VisibleForTesting 3955 public void onReceiveInternal(Intent intent) { 3956 synchronized (mLock) { 3957 final String action = intent.getAction(); 3958 boolean changed = false; 3959 if (Intent.ACTION_BATTERY_LOW.equals(action)) { 3960 if (DEBUG) { 3961 Slog.d(TAG, "Battery life too low @ " + sElapsedRealtimeClock.millis()); 3962 } 3963 if (mBatteryNotLow) { 3964 mBatteryNotLow = false; 3965 changed = true; 3966 } 3967 } else if (Intent.ACTION_BATTERY_OKAY.equals(action)) { 3968 if (DEBUG) { 3969 Slog.d(TAG, "Battery high enough @ " + sElapsedRealtimeClock.millis()); 3970 } 3971 if (!mBatteryNotLow) { 3972 mBatteryNotLow = true; 3973 changed = true; 3974 } 3975 } else if (BatteryManager.ACTION_CHARGING.equals(action)) { 3976 if (DEBUG) { 3977 Slog.d(TAG, "Battery charging @ " + sElapsedRealtimeClock.millis()); 3978 } 3979 if (!mCharging) { 3980 mCharging = true; 3981 changed = true; 3982 } 3983 } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) { 3984 if (DEBUG) { 3985 Slog.d(TAG, "Battery discharging @ " + sElapsedRealtimeClock.millis()); 3986 } 3987 if (mCharging) { 3988 mCharging = false; 3989 changed = true; 3990 } 3991 } 3992 mLastBatterySeq = 3993 intent.getIntExtra(BatteryManager.EXTRA_SEQUENCE, mLastBatterySeq); 3994 if (changed) { 3995 for (int c = mControllers.size() - 1; c >= 0; --c) { 3996 mControllers.get(c).onBatteryStateChangedLocked(); 3997 } 3998 } 3999 } 4000 } 4001 } 4002 4003 final class LocalService implements JobSchedulerInternal { 4004 4005 @Override 4006 public List<JobInfo> getSystemScheduledOwnJobs(@Nullable String namespace) { 4007 synchronized (mLock) { 4008 final List<JobInfo> ownJobs = new ArrayList<>(); 4009 mJobs.forEachJob(Process.SYSTEM_UID, (job) -> { 4010 if (job.getSourceUid() == Process.SYSTEM_UID 4011 && Objects.equals(job.getNamespace(), namespace) 4012 && "android".equals(job.getSourcePackageName())) { 4013 ownJobs.add(job.getJob()); 4014 } 4015 }); 4016 return ownJobs; 4017 } 4018 } 4019 4020 @Override 4021 public void cancelJobsForUid(int uid, boolean includeProxiedJobs, 4022 @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) { 4023 JobSchedulerService.this.cancelJobsForUid(uid, 4024 includeProxiedJobs, reason, internalReasonCode, debugReason); 4025 } 4026 4027 @Override 4028 public void addBackingUpUid(int uid) { 4029 synchronized (mLock) { 4030 // No need to actually do anything here, since for a full backup the 4031 // activity manager will kill the process which will kill the job (and 4032 // cause it to restart, but now it can't run). 4033 mBackingUpUids.put(uid, true); 4034 } 4035 } 4036 4037 @Override 4038 public void removeBackingUpUid(int uid) { 4039 synchronized (mLock) { 4040 mBackingUpUids.delete(uid); 4041 // If there are any jobs for this uid, we need to rebuild the pending list 4042 // in case they are now ready to run. 4043 if (mJobs.countJobsForUid(uid) > 0) { 4044 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 4045 } 4046 } 4047 } 4048 4049 @Override 4050 public void clearAllBackingUpUids() { 4051 synchronized (mLock) { 4052 if (mBackingUpUids.size() > 0) { 4053 mBackingUpUids.clear(); 4054 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 4055 } 4056 } 4057 } 4058 4059 @Override 4060 public String getCloudMediaProviderPackage(int userId) { 4061 return mCloudMediaProviderPackages.get(userId); 4062 } 4063 4064 @Override 4065 public void reportAppUsage(String packageName, int userId) { 4066 JobSchedulerService.this.reportAppUsage(packageName, userId); 4067 } 4068 4069 @Override 4070 public boolean isAppConsideredBuggy(int callingUserId, @NonNull String callingPackageName, 4071 int timeoutBlameUserId, @NonNull String timeoutBlamePackageName) { 4072 return !mQuotaTracker.isWithinQuota(callingUserId, callingPackageName, 4073 QUOTA_TRACKER_ANR_TAG) 4074 || !mQuotaTracker.isWithinQuota(callingUserId, callingPackageName, 4075 QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG) 4076 || !mQuotaTracker.isWithinQuota(timeoutBlameUserId, timeoutBlamePackageName, 4077 QUOTA_TRACKER_TIMEOUT_TOTAL_TAG); 4078 } 4079 4080 @Override 4081 public boolean isNotificationAssociatedWithAnyUserInitiatedJobs(int notificationId, 4082 int userId, @NonNull String packageName) { 4083 if (packageName == null) { 4084 return false; 4085 } 4086 return mConcurrencyManager.isNotificationAssociatedWithAnyUserInitiatedJobs( 4087 notificationId, userId, packageName); 4088 } 4089 4090 @Override 4091 public boolean isNotificationChannelAssociatedWithAnyUserInitiatedJobs( 4092 @NonNull String notificationChannel, int userId, @NonNull String packageName) { 4093 if (packageName == null || notificationChannel == null) { 4094 return false; 4095 } 4096 return mConcurrencyManager.isNotificationChannelAssociatedWithAnyUserInitiatedJobs( 4097 notificationChannel, userId, packageName); 4098 } 4099 4100 @Override 4101 public JobStorePersistStats getPersistStats() { 4102 synchronized (mLock) { 4103 return new JobStorePersistStats(mJobs.getPersistStats()); 4104 } 4105 } 4106 } 4107 4108 /** 4109 * Tracking of app assignments to standby buckets 4110 */ 4111 final class StandbyTracker extends AppIdleStateChangeListener { 4112 4113 // AppIdleStateChangeListener interface for live updates 4114 4115 @Override 4116 public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId, 4117 boolean idle, int bucket, int reason) { 4118 // QuotaController handles this now. 4119 } 4120 4121 @Override 4122 public void onUserInteractionStarted(String packageName, int userId) { 4123 final int uid = mLocalPM.getPackageUid(packageName, 4124 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); 4125 if (uid < 0) { 4126 // Quietly ignore; the case is already logged elsewhere 4127 return; 4128 } 4129 4130 long sinceLast = mUsageStats.getTimeSinceLastJobRun(packageName, userId); 4131 if (sinceLast > 2 * DateUtils.DAY_IN_MILLIS) { 4132 // Too long ago, not worth logging 4133 sinceLast = 0L; 4134 } 4135 final DeferredJobCounter counter = new DeferredJobCounter(); 4136 synchronized (mLock) { 4137 mJobs.forEachJobForSourceUid(uid, counter); 4138 } 4139 if (counter.numDeferred() > 0 || sinceLast > 0) { 4140 BatteryStatsInternal mBatteryStatsInternal = LocalServices.getService 4141 (BatteryStatsInternal.class); 4142 mBatteryStatsInternal.noteJobsDeferred(uid, counter.numDeferred(), sinceLast); 4143 FrameworkStatsLog.write_non_chained( 4144 FrameworkStatsLog.DEFERRED_JOB_STATS_REPORTED, uid, null, 4145 counter.numDeferred(), sinceLast); 4146 } 4147 } 4148 } 4149 4150 static class DeferredJobCounter implements Consumer<JobStatus> { 4151 private int mDeferred = 0; 4152 4153 public int numDeferred() { 4154 return mDeferred; 4155 } 4156 4157 @Override 4158 public void accept(JobStatus job) { 4159 if (job.getWhenStandbyDeferred() > 0) { 4160 mDeferred++; 4161 } 4162 } 4163 } 4164 4165 public static int standbyBucketToBucketIndex(int bucket) { 4166 // Normalize AppStandby constants to indices into our bookkeeping 4167 if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) { 4168 return NEVER_INDEX; 4169 } else if (bucket > UsageStatsManager.STANDBY_BUCKET_RARE) { 4170 return RESTRICTED_INDEX; 4171 } else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) { 4172 return RARE_INDEX; 4173 } else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) { 4174 return FREQUENT_INDEX; 4175 } else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) { 4176 return WORKING_INDEX; 4177 } else if (bucket > UsageStatsManager.STANDBY_BUCKET_EXEMPTED) { 4178 return ACTIVE_INDEX; 4179 } else { 4180 return EXEMPTED_INDEX; 4181 } 4182 } 4183 4184 // Static to support external callers 4185 public static int standbyBucketForPackage(String packageName, int userId, long elapsedNow) { 4186 UsageStatsManagerInternal usageStats = LocalServices.getService( 4187 UsageStatsManagerInternal.class); 4188 int bucket = usageStats != null 4189 ? usageStats.getAppStandbyBucket(packageName, userId, elapsedNow) 4190 : 0; 4191 4192 bucket = standbyBucketToBucketIndex(bucket); 4193 4194 if (DEBUG_STANDBY) { 4195 Slog.v(TAG, packageName + "/" + userId + " standby bucket index: " + bucket); 4196 } 4197 return bucket; 4198 } 4199 4200 static int safelyScaleBytesToKBForHistogram(long bytes) { 4201 long kilobytes = bytes / 1000; 4202 // Anything over Integer.MAX_VALUE or under Integer.MIN_VALUE isn't expected and will 4203 // be put into the overflow buckets. 4204 if (kilobytes > Integer.MAX_VALUE) { 4205 return Integer.MAX_VALUE; 4206 } else if (kilobytes < Integer.MIN_VALUE) { 4207 return Integer.MIN_VALUE; 4208 } 4209 return (int) kilobytes; 4210 } 4211 4212 private class CloudProviderChangeListener implements 4213 StorageManagerInternal.CloudProviderChangeListener { 4214 4215 @Override 4216 public void onCloudProviderChanged(int userId, @Nullable String authority) { 4217 final PackageManager pm = getContext() 4218 .createContextAsUser(UserHandle.of(userId), 0) 4219 .getPackageManager(); 4220 final ProviderInfo pi = pm.resolveContentProvider( 4221 authority, PackageManager.ComponentInfoFlags.of(0)); 4222 final String newPkg = (pi == null) ? null : pi.packageName; 4223 synchronized (mLock) { 4224 final String oldPkg = mCloudMediaProviderPackages.get(userId); 4225 if (!Objects.equals(oldPkg, newPkg)) { 4226 if (DEBUG) { 4227 Slog.d(TAG, "Cloud provider of user " + userId + " changed from " + oldPkg 4228 + " to " + newPkg); 4229 } 4230 mCloudMediaProviderPackages.put(userId, newPkg); 4231 SomeArgs args = SomeArgs.obtain(); 4232 args.argi1 = userId; 4233 args.arg1 = oldPkg; 4234 args.arg2 = newPkg; 4235 mHandler.obtainMessage(MSG_CHECK_MEDIA_EXEMPTION, args).sendToTarget(); 4236 } 4237 } 4238 } 4239 } 4240 4241 /** 4242 * Returns whether the app has the permission granted. 4243 * This currently only works for normal permissions and <b>DOES NOT</b> work for runtime 4244 * permissions. 4245 * TODO: handle runtime permissions 4246 */ 4247 private boolean hasPermission(int uid, int pid, @NonNull String permission) { 4248 synchronized (mPermissionCache) { 4249 SparseArrayMap<String, Boolean> pidPermissions = mPermissionCache.get(uid); 4250 if (pidPermissions == null) { 4251 pidPermissions = new SparseArrayMap<>(); 4252 mPermissionCache.put(uid, pidPermissions); 4253 } 4254 final Boolean cached = pidPermissions.get(pid, permission); 4255 if (cached != null) { 4256 return cached; 4257 } 4258 4259 final int result = getContext().checkPermission(permission, pid, uid); 4260 final boolean permissionGranted = (result == PackageManager.PERMISSION_GRANTED); 4261 pidPermissions.add(pid, permission, permissionGranted); 4262 return permissionGranted; 4263 } 4264 } 4265 4266 /** 4267 * Binder stub trampoline implementation 4268 */ 4269 final class JobSchedulerStub extends IJobScheduler.Stub { 4270 // Enforce that only the app itself (or shared uid participant) can schedule a 4271 // job that runs one of the app's services, as well as verifying that the 4272 // named service properly requires the BIND_JOB_SERVICE permission 4273 // TODO(141645789): merge enforceValidJobRequest() with validateJob() 4274 private void enforceValidJobRequest(int uid, int pid, JobInfo job) { 4275 final PackageManager pm = getContext() 4276 .createContextAsUser(UserHandle.getUserHandleForUid(uid), 0) 4277 .getPackageManager(); 4278 final ComponentName service = job.getService(); 4279 try { 4280 ServiceInfo si = pm.getServiceInfo(service, 4281 PackageManager.MATCH_DIRECT_BOOT_AWARE 4282 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); 4283 if (si == null) { 4284 throw new IllegalArgumentException("No such service " + service); 4285 } 4286 if (si.applicationInfo.uid != uid) { 4287 throw new IllegalArgumentException("uid " + uid + 4288 " cannot schedule job in " + service.getPackageName()); 4289 } 4290 if (!JobService.PERMISSION_BIND.equals(si.permission)) { 4291 throw new IllegalArgumentException("Scheduled service " + service 4292 + " does not require android.permission.BIND_JOB_SERVICE permission"); 4293 } 4294 } catch (NameNotFoundException e) { 4295 throw new IllegalArgumentException( 4296 "Tried to schedule job for non-existent component: " + service); 4297 } 4298 // If we get this far we're good to go; all we need to do now is check 4299 // whether the app is allowed to persist its scheduled work. 4300 if (job.isPersisted() && !canPersistJobs(pid, uid)) { 4301 throw new IllegalArgumentException("Requested job cannot be persisted without" 4302 + " holding android.permission.RECEIVE_BOOT_COMPLETED permission"); 4303 } 4304 if (job.getRequiredNetwork() != null 4305 && CompatChanges.isChangeEnabled( 4306 REQUIRE_NETWORK_PERMISSIONS_FOR_CONNECTIVITY_JOBS, uid)) { 4307 if (!hasPermission(uid, pid, Manifest.permission.ACCESS_NETWORK_STATE)) { 4308 throw new SecurityException(Manifest.permission.ACCESS_NETWORK_STATE 4309 + " required for jobs with a connectivity constraint"); 4310 } 4311 } 4312 } 4313 4314 private boolean canPersistJobs(int pid, int uid) { 4315 // Persisting jobs is tantamount to running at boot, so we permit 4316 // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED 4317 // permission 4318 return hasPermission(uid, pid, Manifest.permission.RECEIVE_BOOT_COMPLETED); 4319 } 4320 4321 private int validateJob(@NonNull JobInfo job, int callingUid, int callingPid, 4322 int sourceUserId, 4323 @Nullable String sourcePkgName, @Nullable JobWorkItem jobWorkItem) { 4324 final boolean rejectNegativeNetworkEstimates = CompatChanges.isChangeEnabled( 4325 JobInfo.REJECT_NEGATIVE_NETWORK_ESTIMATES, callingUid); 4326 job.enforceValidity( 4327 CompatChanges.isChangeEnabled( 4328 JobInfo.DISALLOW_DEADLINES_FOR_PREFETCH_JOBS, callingUid), 4329 rejectNegativeNetworkEstimates); 4330 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) { 4331 getContext().enforceCallingOrSelfPermission( 4332 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG); 4333 } 4334 if ((job.getFlags() & JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY) != 0) { 4335 if (callingUid != Process.SYSTEM_UID) { 4336 throw new SecurityException("Job has invalid flags"); 4337 } 4338 if (job.isPeriodic()) { 4339 Slog.wtf(TAG, "Periodic jobs mustn't have" 4340 + " FLAG_EXEMPT_FROM_APP_STANDBY. Job=" + job); 4341 } 4342 } 4343 if (job.isUserInitiated()) { 4344 int sourceUid = -1; 4345 if (sourceUserId != -1 && sourcePkgName != null) { 4346 try { 4347 sourceUid = AppGlobals.getPackageManager().getPackageUid( 4348 sourcePkgName, 0, sourceUserId); 4349 } catch (RemoteException ex) { 4350 // Can't happen, PackageManager runs in the same process. 4351 } 4352 } 4353 // We aim to check the permission of both the source and calling app so that apps 4354 // don't attempt to bypass the permission by using other apps to do the work. 4355 boolean isInStateToScheduleUiJobSource = false; 4356 final String callingPkgName = job.getService().getPackageName(); 4357 if (sourceUid != -1) { 4358 // Check the permission of the source app. 4359 final int sourceResult = 4360 validateRunUserInitiatedJobsPermission(sourceUid, sourcePkgName); 4361 if (sourceResult != JobScheduler.RESULT_SUCCESS) { 4362 return sourceResult; 4363 } 4364 final int sourcePid = 4365 callingUid == sourceUid && callingPkgName.equals(sourcePkgName) 4366 ? callingPid : -1; 4367 isInStateToScheduleUiJobSource = isInStateToScheduleUserInitiatedJobs( 4368 sourceUid, sourcePid, sourcePkgName); 4369 } 4370 boolean isInStateToScheduleUiJobCalling = false; 4371 if (callingUid != sourceUid || !callingPkgName.equals(sourcePkgName)) { 4372 // Source app is different from calling app. Make sure the calling app also has 4373 // the permission. 4374 final int callingResult = 4375 validateRunUserInitiatedJobsPermission(callingUid, callingPkgName); 4376 if (callingResult != JobScheduler.RESULT_SUCCESS) { 4377 return callingResult; 4378 } 4379 // Avoid rechecking the state if the source app is able to schedule the job. 4380 if (!isInStateToScheduleUiJobSource) { 4381 isInStateToScheduleUiJobCalling = isInStateToScheduleUserInitiatedJobs( 4382 callingUid, callingPid, callingPkgName); 4383 } 4384 } 4385 4386 if (!isInStateToScheduleUiJobSource && !isInStateToScheduleUiJobCalling) { 4387 Slog.e(TAG, "Uid(s) " + sourceUid + "/" + callingUid 4388 + " not in a state to schedule user-initiated jobs"); 4389 Counter.logIncrementWithUid( 4390 "job_scheduler.value_cntr_w_uid_schedule_failure_uij_invalid_state", 4391 callingUid); 4392 return JobScheduler.RESULT_FAILURE; 4393 } 4394 } 4395 if (jobWorkItem != null) { 4396 jobWorkItem.enforceValidity(rejectNegativeNetworkEstimates); 4397 if (jobWorkItem.getEstimatedNetworkDownloadBytes() != JobInfo.NETWORK_BYTES_UNKNOWN 4398 || jobWorkItem.getEstimatedNetworkUploadBytes() 4399 != JobInfo.NETWORK_BYTES_UNKNOWN 4400 || jobWorkItem.getMinimumNetworkChunkBytes() 4401 != JobInfo.NETWORK_BYTES_UNKNOWN) { 4402 if (job.getRequiredNetwork() == null) { 4403 final String errorMsg = "JobWorkItem implies network usage" 4404 + " but job doesn't specify a network constraint"; 4405 if (CompatChanges.isChangeEnabled( 4406 REQUIRE_NETWORK_CONSTRAINT_FOR_NETWORK_JOB_WORK_ITEMS, 4407 callingUid)) { 4408 throw new IllegalArgumentException(errorMsg); 4409 } else { 4410 Slog.e(TAG, errorMsg); 4411 } 4412 } 4413 } 4414 if (job.isPersisted()) { 4415 // Intent.saveToXml() doesn't persist everything, so just reject all 4416 // JobWorkItems with Intents to be safe/predictable. 4417 if (jobWorkItem.getIntent() != null) { 4418 throw new IllegalArgumentException( 4419 "Cannot persist JobWorkItems with Intents"); 4420 } 4421 } 4422 } 4423 return JobScheduler.RESULT_SUCCESS; 4424 } 4425 4426 /** Returns a sanitized namespace if valid, or throws an exception if not. */ 4427 private String validateNamespace(@Nullable String namespace) { 4428 namespace = JobScheduler.sanitizeNamespace(namespace); 4429 if (namespace != null) { 4430 if (namespace.isEmpty()) { 4431 throw new IllegalArgumentException("namespace cannot be empty"); 4432 } 4433 if (namespace.length() > 1000) { 4434 throw new IllegalArgumentException( 4435 "namespace cannot be more than 1000 characters"); 4436 } 4437 namespace = namespace.intern(); 4438 } 4439 return namespace; 4440 } 4441 4442 private int validateRunUserInitiatedJobsPermission(int uid, String packageName) { 4443 final int state = getRunUserInitiatedJobsPermissionState(uid, packageName); 4444 if (state == PermissionChecker.PERMISSION_HARD_DENIED) { 4445 Counter.logIncrementWithUid( 4446 "job_scheduler.value_cntr_w_uid_schedule_failure_uij_no_permission", uid); 4447 throw new SecurityException(android.Manifest.permission.RUN_USER_INITIATED_JOBS 4448 + " required to schedule user-initiated jobs."); 4449 } 4450 if (state == PermissionChecker.PERMISSION_SOFT_DENIED) { 4451 Counter.logIncrementWithUid( 4452 "job_scheduler.value_cntr_w_uid_schedule_failure_uij_no_permission", uid); 4453 return JobScheduler.RESULT_FAILURE; 4454 } 4455 return JobScheduler.RESULT_SUCCESS; 4456 } 4457 4458 private boolean isInStateToScheduleUserInitiatedJobs(int uid, int pid, String pkgName) { 4459 final int procState = mActivityManagerInternal.getUidProcessState(uid); 4460 if (DEBUG) { 4461 Slog.d(TAG, "Uid " + uid + " proc state=" 4462 + ActivityManager.procStateToString(procState)); 4463 } 4464 if (procState == ActivityManager.PROCESS_STATE_TOP) { 4465 return true; 4466 } 4467 final boolean canScheduleUiJobsInBg = 4468 mActivityManagerInternal.canScheduleUserInitiatedJobs(uid, pid, pkgName); 4469 if (DEBUG) { 4470 Slog.d(TAG, "Uid " + uid 4471 + " AM.canScheduleUserInitiatedJobs= " + canScheduleUiJobsInBg); 4472 } 4473 return canScheduleUiJobsInBg; 4474 } 4475 4476 // IJobScheduler implementation 4477 @Override 4478 public int schedule(String namespace, JobInfo job) throws RemoteException { 4479 if (DEBUG) { 4480 Slog.d(TAG, "Scheduling job: " + job.toString()); 4481 } 4482 final int pid = Binder.getCallingPid(); 4483 final int uid = Binder.getCallingUid(); 4484 final int userId = UserHandle.getUserId(uid); 4485 4486 enforceValidJobRequest(uid, pid, job); 4487 4488 final int result = validateJob(job, uid, pid, -1, null, null); 4489 if (result != JobScheduler.RESULT_SUCCESS) { 4490 return result; 4491 } 4492 4493 namespace = validateNamespace(namespace); 4494 4495 final long ident = Binder.clearCallingIdentity(); 4496 try { 4497 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, userId, 4498 namespace, null); 4499 } finally { 4500 Binder.restoreCallingIdentity(ident); 4501 } 4502 } 4503 4504 // IJobScheduler implementation 4505 @Override 4506 public int enqueue(String namespace, JobInfo job, JobWorkItem work) throws RemoteException { 4507 if (DEBUG) { 4508 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work); 4509 } 4510 final int uid = Binder.getCallingUid(); 4511 final int pid = Binder.getCallingPid(); 4512 final int userId = UserHandle.getUserId(uid); 4513 4514 enforceValidJobRequest(uid, pid, job); 4515 if (work == null) { 4516 throw new NullPointerException("work is null"); 4517 } 4518 4519 final int result = validateJob(job, uid, pid, -1, null, work); 4520 if (result != JobScheduler.RESULT_SUCCESS) { 4521 return result; 4522 } 4523 4524 namespace = validateNamespace(namespace); 4525 4526 final long ident = Binder.clearCallingIdentity(); 4527 try { 4528 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, userId, 4529 namespace, null); 4530 } finally { 4531 Binder.restoreCallingIdentity(ident); 4532 } 4533 } 4534 4535 @Override 4536 public int scheduleAsPackage(String namespace, JobInfo job, String packageName, int userId, 4537 String tag) throws RemoteException { 4538 final int callerUid = Binder.getCallingUid(); 4539 final int callerPid = Binder.getCallingPid(); 4540 if (DEBUG) { 4541 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString() 4542 + " on behalf of " + packageName + "/"); 4543 } 4544 4545 if (packageName == null) { 4546 throw new NullPointerException("Must specify a package for scheduleAsPackage()"); 4547 } 4548 4549 int mayScheduleForOthers = getContext().checkCallingOrSelfPermission( 4550 android.Manifest.permission.UPDATE_DEVICE_STATS); 4551 if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) { 4552 throw new SecurityException("Caller uid " + callerUid 4553 + " not permitted to schedule jobs for other apps"); 4554 } 4555 4556 enforceValidJobRequest(callerUid, callerPid, job); 4557 4558 int result = validateJob(job, callerUid, callerPid, userId, packageName, null); 4559 if (result != JobScheduler.RESULT_SUCCESS) { 4560 return result; 4561 } 4562 4563 namespace = validateNamespace(namespace); 4564 4565 final long ident = Binder.clearCallingIdentity(); 4566 try { 4567 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid, 4568 packageName, userId, namespace, tag); 4569 } finally { 4570 Binder.restoreCallingIdentity(ident); 4571 } 4572 } 4573 4574 @Override 4575 public Map<String, ParceledListSlice<JobInfo>> getAllPendingJobs() throws RemoteException { 4576 final int uid = Binder.getCallingUid(); 4577 4578 final long ident = Binder.clearCallingIdentity(); 4579 try { 4580 final ArrayMap<String, List<JobInfo>> jobs = 4581 JobSchedulerService.this.getPendingJobs(uid); 4582 final ArrayMap<String, ParceledListSlice<JobInfo>> outMap = new ArrayMap<>(); 4583 for (int i = 0; i < jobs.size(); ++i) { 4584 outMap.put(jobs.keyAt(i), new ParceledListSlice<>(jobs.valueAt(i))); 4585 } 4586 return outMap; 4587 } finally { 4588 Binder.restoreCallingIdentity(ident); 4589 } 4590 } 4591 4592 @Override 4593 public ParceledListSlice<JobInfo> getAllPendingJobsInNamespace(String namespace) 4594 throws RemoteException { 4595 final int uid = Binder.getCallingUid(); 4596 4597 final long ident = Binder.clearCallingIdentity(); 4598 try { 4599 return new ParceledListSlice<>( 4600 JobSchedulerService.this.getPendingJobsInNamespace(uid, 4601 validateNamespace(namespace))); 4602 } finally { 4603 Binder.restoreCallingIdentity(ident); 4604 } 4605 } 4606 4607 @Override 4608 public JobInfo getPendingJob(String namespace, int jobId) throws RemoteException { 4609 final int uid = Binder.getCallingUid(); 4610 4611 final long ident = Binder.clearCallingIdentity(); 4612 try { 4613 return JobSchedulerService.this.getPendingJob( 4614 uid, validateNamespace(namespace), jobId); 4615 } finally { 4616 Binder.restoreCallingIdentity(ident); 4617 } 4618 } 4619 4620 @Override 4621 public int getPendingJobReason(String namespace, int jobId) throws RemoteException { 4622 final int uid = Binder.getCallingUid(); 4623 4624 final long ident = Binder.clearCallingIdentity(); 4625 try { 4626 return JobSchedulerService.this.getPendingJobReason( 4627 uid, validateNamespace(namespace), jobId); 4628 } finally { 4629 Binder.restoreCallingIdentity(ident); 4630 } 4631 } 4632 4633 @Override 4634 public void cancelAll() throws RemoteException { 4635 final int uid = Binder.getCallingUid(); 4636 final long ident = Binder.clearCallingIdentity(); 4637 try { 4638 JobSchedulerService.this.cancelJobsForUid(uid, 4639 // Documentation says only jobs scheduled BY the app will be cancelled 4640 /* includeSourceApp */ false, 4641 JobParameters.STOP_REASON_CANCELLED_BY_APP, 4642 JobParameters.INTERNAL_STOP_REASON_CANCELED, 4643 "cancelAll() called by app, callingUid=" + uid); 4644 } finally { 4645 Binder.restoreCallingIdentity(ident); 4646 } 4647 } 4648 4649 @Override 4650 public void cancelAllInNamespace(String namespace) throws RemoteException { 4651 final int uid = Binder.getCallingUid(); 4652 final long ident = Binder.clearCallingIdentity(); 4653 try { 4654 JobSchedulerService.this.cancelJobsForUid(uid, 4655 // Documentation says only jobs scheduled BY the app will be cancelled 4656 /* includeSourceApp */ false, 4657 /* namespaceOnly */ true, validateNamespace(namespace), 4658 JobParameters.STOP_REASON_CANCELLED_BY_APP, 4659 JobParameters.INTERNAL_STOP_REASON_CANCELED, 4660 "cancelAllInNamespace() called by app, callingUid=" + uid); 4661 } finally { 4662 Binder.restoreCallingIdentity(ident); 4663 } 4664 } 4665 4666 @Override 4667 public void cancel(String namespace, int jobId) throws RemoteException { 4668 final int uid = Binder.getCallingUid(); 4669 4670 final long ident = Binder.clearCallingIdentity(); 4671 try { 4672 JobSchedulerService.this.cancelJob(uid, validateNamespace(namespace), jobId, uid, 4673 JobParameters.STOP_REASON_CANCELLED_BY_APP); 4674 } finally { 4675 Binder.restoreCallingIdentity(ident); 4676 } 4677 } 4678 4679 public boolean canRunUserInitiatedJobs(@NonNull String packageName) { 4680 final int callingUid = Binder.getCallingUid(); 4681 final int userId = UserHandle.getUserId(callingUid); 4682 final int packageUid = mLocalPM.getPackageUid(packageName, 0, userId); 4683 if (callingUid != packageUid) { 4684 throw new SecurityException("Uid " + callingUid 4685 + " cannot query canRunUserInitiatedJobs for package " + packageName); 4686 } 4687 4688 return checkRunUserInitiatedJobsPermission(packageUid, packageName); 4689 } 4690 4691 public boolean hasRunUserInitiatedJobsPermission(@NonNull String packageName, 4692 @UserIdInt int userId) { 4693 final int uid = mLocalPM.getPackageUid(packageName, 0, userId); 4694 final int callingUid = Binder.getCallingUid(); 4695 if (callingUid != uid && !UserHandle.isCore(callingUid)) { 4696 throw new SecurityException("Uid " + callingUid 4697 + " cannot query hasRunUserInitiatedJobsPermission for package " 4698 + packageName); 4699 } 4700 4701 return checkRunUserInitiatedJobsPermission(uid, packageName); 4702 } 4703 4704 /** 4705 * "dumpsys" infrastructure 4706 */ 4707 @Override 4708 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 4709 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return; 4710 4711 int filterUid = -1; 4712 boolean proto = false; 4713 if (!ArrayUtils.isEmpty(args)) { 4714 int opti = 0; 4715 while (opti < args.length) { 4716 String arg = args[opti]; 4717 if ("-h".equals(arg)) { 4718 dumpHelp(pw); 4719 return; 4720 } else if ("-a".equals(arg)) { 4721 // Ignore, we always dump all. 4722 } else if ("--proto".equals(arg)) { 4723 proto = true; 4724 } else if (arg.length() > 0 && arg.charAt(0) == '-') { 4725 pw.println("Unknown option: " + arg); 4726 return; 4727 } else { 4728 break; 4729 } 4730 opti++; 4731 } 4732 if (opti < args.length) { 4733 String pkg = args[opti]; 4734 try { 4735 filterUid = getContext().getPackageManager().getPackageUid(pkg, 4736 PackageManager.MATCH_ANY_USER); 4737 } catch (NameNotFoundException ignored) { 4738 pw.println("Invalid package: " + pkg); 4739 return; 4740 } 4741 } 4742 } 4743 4744 final long identityToken = Binder.clearCallingIdentity(); 4745 try { 4746 if (proto) { 4747 JobSchedulerService.this.dumpInternalProto(fd, filterUid); 4748 } else { 4749 JobSchedulerService.this.dumpInternal(new IndentingPrintWriter(pw, " "), 4750 filterUid); 4751 } 4752 } finally { 4753 Binder.restoreCallingIdentity(identityToken); 4754 } 4755 } 4756 4757 @Override 4758 public int handleShellCommand(@NonNull ParcelFileDescriptor in, 4759 @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, 4760 @NonNull String[] args) { 4761 return (new JobSchedulerShellCommand(JobSchedulerService.this)).exec( 4762 this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), 4763 args); 4764 } 4765 4766 /** 4767 * <b>For internal system user only!</b> 4768 * Returns a list of all currently-executing jobs. 4769 */ 4770 @Override 4771 public List<JobInfo> getStartedJobs() { 4772 final int uid = Binder.getCallingUid(); 4773 if (uid != Process.SYSTEM_UID) { 4774 throw new SecurityException("getStartedJobs() is system internal use only."); 4775 } 4776 4777 final ArrayList<JobInfo> runningJobs; 4778 4779 synchronized (mLock) { 4780 final ArraySet<JobStatus> runningJobStatuses = 4781 mConcurrencyManager.getRunningJobsLocked(); 4782 runningJobs = new ArrayList<>(runningJobStatuses.size()); 4783 for (int i = runningJobStatuses.size() - 1; i >= 0; --i) { 4784 final JobStatus job = runningJobStatuses.valueAt(i); 4785 if (job != null) { 4786 runningJobs.add(job.getJob()); 4787 } 4788 } 4789 } 4790 4791 return runningJobs; 4792 } 4793 4794 /** 4795 * <b>For internal system user only!</b> 4796 * Returns a snapshot of the state of all jobs known to the system. 4797 * 4798 * <p class="note">This is a slow operation, so it should be called sparingly. 4799 */ 4800 @Override 4801 public ParceledListSlice<JobSnapshot> getAllJobSnapshots() { 4802 final int uid = Binder.getCallingUid(); 4803 if (uid != Process.SYSTEM_UID) { 4804 throw new SecurityException("getAllJobSnapshots() is system internal use only."); 4805 } 4806 synchronized (mLock) { 4807 final ArrayList<JobSnapshot> snapshots = new ArrayList<>(mJobs.size()); 4808 mJobs.forEachJob((job) -> snapshots.add( 4809 new JobSnapshot(job.getJob(), job.getSatisfiedConstraintFlags(), 4810 isReadyToBeExecutedLocked(job)))); 4811 return new ParceledListSlice<>(snapshots); 4812 } 4813 } 4814 4815 @Override 4816 @EnforcePermission(allOf = {MANAGE_ACTIVITY_TASKS, INTERACT_ACROSS_USERS_FULL}) 4817 public void registerUserVisibleJobObserver(@NonNull IUserVisibleJobObserver observer) { 4818 super.registerUserVisibleJobObserver_enforcePermission(); 4819 if (observer == null) { 4820 throw new NullPointerException("observer"); 4821 } 4822 mUserVisibleJobObservers.register(observer); 4823 mHandler.obtainMessage(MSG_INFORM_OBSERVER_OF_ALL_USER_VISIBLE_JOBS, observer) 4824 .sendToTarget(); 4825 } 4826 4827 @Override 4828 @EnforcePermission(allOf = {MANAGE_ACTIVITY_TASKS, INTERACT_ACROSS_USERS_FULL}) 4829 public void unregisterUserVisibleJobObserver(@NonNull IUserVisibleJobObserver observer) { 4830 super.unregisterUserVisibleJobObserver_enforcePermission(); 4831 if (observer == null) { 4832 throw new NullPointerException("observer"); 4833 } 4834 mUserVisibleJobObservers.unregister(observer); 4835 } 4836 4837 @Override 4838 @EnforcePermission(allOf = {MANAGE_ACTIVITY_TASKS, INTERACT_ACROSS_USERS_FULL}) 4839 public void notePendingUserRequestedAppStop(@NonNull String packageName, int userId, 4840 @Nullable String debugReason) { 4841 super.notePendingUserRequestedAppStop_enforcePermission(); 4842 if (packageName == null) { 4843 throw new NullPointerException("packageName"); 4844 } 4845 notePendingUserRequestedAppStopInternal(packageName, userId, debugReason); 4846 } 4847 } 4848 4849 // Shell command infrastructure: run the given job immediately 4850 int executeRunCommand(String pkgName, int userId, @Nullable String namespace, 4851 int jobId, boolean satisfied, boolean force) { 4852 Slog.d(TAG, "executeRunCommand(): " + pkgName + "/" + namespace + "/" + userId 4853 + " " + jobId + " s=" + satisfied + " f=" + force); 4854 4855 try { 4856 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, 4857 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM); 4858 if (uid < 0) { 4859 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE; 4860 } 4861 4862 synchronized (mLock) { 4863 final JobStatus js = mJobs.getJobByUidAndJobId(uid, namespace, jobId); 4864 if (js == null) { 4865 return JobSchedulerShellCommand.CMD_ERR_NO_JOB; 4866 } 4867 4868 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL 4869 : (satisfied ? JobStatus.OVERRIDE_SORTING : JobStatus.OVERRIDE_SOFT); 4870 4871 // Re-evaluate constraints after the override is set in case one of the overridden 4872 // constraints was preventing another constraint from thinking it needed to update. 4873 for (int c = mControllers.size() - 1; c >= 0; --c) { 4874 mControllers.get(c).reevaluateStateLocked(uid); 4875 } 4876 4877 if (!js.isConstraintsSatisfied()) { 4878 js.overrideState = JobStatus.OVERRIDE_NONE; 4879 return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS; 4880 } 4881 4882 queueReadyJobsForExecutionLocked(); 4883 maybeRunPendingJobsLocked(); 4884 } 4885 } catch (RemoteException e) { 4886 // can't happen 4887 } 4888 return 0; 4889 } 4890 4891 // Shell command infrastructure: immediately timeout currently executing jobs 4892 int executeStopCommand(PrintWriter pw, String pkgName, int userId, 4893 @Nullable String namespace, boolean hasJobId, int jobId, 4894 int stopReason, int internalStopReason) { 4895 if (DEBUG) { 4896 Slog.v(TAG, "executeStopJobCommand(): " + pkgName + "/" + userId + " " + jobId 4897 + ": " + stopReason + "(" 4898 + JobParameters.getInternalReasonCodeDescription(internalStopReason) + ")"); 4899 } 4900 4901 synchronized (mLock) { 4902 final boolean foundSome = mConcurrencyManager.executeStopCommandLocked(pw, 4903 pkgName, userId, namespace, hasJobId, jobId, stopReason, internalStopReason); 4904 if (!foundSome) { 4905 pw.println("No matching executing jobs found."); 4906 } 4907 } 4908 return 0; 4909 } 4910 4911 // Shell command infrastructure: cancel a scheduled job 4912 int executeCancelCommand(PrintWriter pw, String pkgName, int userId, @Nullable String namespace, 4913 boolean hasJobId, int jobId) { 4914 if (DEBUG) { 4915 Slog.v(TAG, "executeCancelCommand(): " + pkgName + "/" + userId + " " + jobId); 4916 } 4917 4918 int pkgUid = -1; 4919 try { 4920 IPackageManager pm = AppGlobals.getPackageManager(); 4921 pkgUid = pm.getPackageUid(pkgName, 0, userId); 4922 } catch (RemoteException e) { /* can't happen */ } 4923 4924 if (pkgUid < 0) { 4925 pw.println("Package " + pkgName + " not found."); 4926 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE; 4927 } 4928 4929 if (!hasJobId) { 4930 pw.println("Canceling all jobs for " + pkgName + " in user " + userId); 4931 if (!cancelJobsForUid(pkgUid, 4932 /* includeSourceApp */ false, 4933 JobParameters.STOP_REASON_USER, 4934 JobParameters.INTERNAL_STOP_REASON_CANCELED, 4935 "cancel shell command for package")) { 4936 pw.println("No matching jobs found."); 4937 } 4938 } else { 4939 pw.println("Canceling job " + pkgName + "/#" + jobId + " in user " + userId); 4940 if (!cancelJob(pkgUid, namespace, jobId, 4941 Process.SHELL_UID, JobParameters.STOP_REASON_USER)) { 4942 pw.println("No matching job found."); 4943 } 4944 } 4945 4946 return 0; 4947 } 4948 4949 void setMonitorBattery(boolean enabled) { 4950 synchronized (mLock) { 4951 mBatteryStateTracker.setMonitorBatteryLocked(enabled); 4952 } 4953 } 4954 4955 int getBatterySeq() { 4956 synchronized (mLock) { 4957 return mBatteryStateTracker.getSeq(); 4958 } 4959 } 4960 4961 /** Return {@code true} if the device is currently charging. */ 4962 public boolean isBatteryCharging() { 4963 synchronized (mLock) { 4964 return mBatteryStateTracker.isCharging(); 4965 } 4966 } 4967 4968 /** Return {@code true} if the battery is not low. */ 4969 public boolean isBatteryNotLow() { 4970 synchronized (mLock) { 4971 return mBatteryStateTracker.isBatteryNotLow(); 4972 } 4973 } 4974 4975 int getStorageSeq() { 4976 synchronized (mLock) { 4977 return mStorageController.getTracker().getSeq(); 4978 } 4979 } 4980 4981 boolean getStorageNotLow() { 4982 synchronized (mLock) { 4983 return mStorageController.getTracker().isStorageNotLow(); 4984 } 4985 } 4986 4987 int getEstimatedNetworkBytes(PrintWriter pw, String pkgName, int userId, String namespace, 4988 int jobId, int byteOption) { 4989 try { 4990 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, 4991 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM); 4992 if (uid < 0) { 4993 pw.print("unknown("); 4994 pw.print(pkgName); 4995 pw.println(")"); 4996 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE; 4997 } 4998 4999 synchronized (mLock) { 5000 final JobStatus js = mJobs.getJobByUidAndJobId(uid, namespace, jobId); 5001 if (DEBUG) { 5002 Slog.d(TAG, "get-estimated-network-bytes " + uid + "/" 5003 + namespace + "/" + jobId + ": " + js); 5004 } 5005 if (js == null) { 5006 pw.print("unknown("); UserHandle.formatUid(pw, uid); 5007 pw.print("/jid"); pw.print(jobId); pw.println(")"); 5008 return JobSchedulerShellCommand.CMD_ERR_NO_JOB; 5009 } 5010 5011 final long downloadBytes; 5012 final long uploadBytes; 5013 final Pair<Long, Long> bytes = mConcurrencyManager.getEstimatedNetworkBytesLocked( 5014 pkgName, uid, namespace, jobId); 5015 if (bytes == null) { 5016 downloadBytes = js.getEstimatedNetworkDownloadBytes(); 5017 uploadBytes = js.getEstimatedNetworkUploadBytes(); 5018 } else { 5019 downloadBytes = bytes.first; 5020 uploadBytes = bytes.second; 5021 } 5022 if (byteOption == JobSchedulerShellCommand.BYTE_OPTION_DOWNLOAD) { 5023 pw.println(downloadBytes); 5024 } else { 5025 pw.println(uploadBytes); 5026 } 5027 pw.println(); 5028 } 5029 } catch (RemoteException e) { 5030 // can't happen 5031 } 5032 return 0; 5033 } 5034 5035 int getTransferredNetworkBytes(PrintWriter pw, String pkgName, int userId, String namespace, 5036 int jobId, int byteOption) { 5037 try { 5038 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, 5039 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM); 5040 if (uid < 0) { 5041 pw.print("unknown("); 5042 pw.print(pkgName); 5043 pw.println(")"); 5044 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE; 5045 } 5046 5047 synchronized (mLock) { 5048 final JobStatus js = mJobs.getJobByUidAndJobId(uid, namespace, jobId); 5049 if (DEBUG) { 5050 Slog.d(TAG, "get-transferred-network-bytes " + uid 5051 + namespace + "/" + "/" + jobId + ": " + js); 5052 } 5053 if (js == null) { 5054 pw.print("unknown("); UserHandle.formatUid(pw, uid); 5055 pw.print("/jid"); pw.print(jobId); pw.println(")"); 5056 return JobSchedulerShellCommand.CMD_ERR_NO_JOB; 5057 } 5058 5059 final long downloadBytes; 5060 final long uploadBytes; 5061 final Pair<Long, Long> bytes = mConcurrencyManager.getTransferredNetworkBytesLocked( 5062 pkgName, uid, namespace, jobId); 5063 if (bytes == null) { 5064 downloadBytes = 0; 5065 uploadBytes = 0; 5066 } else { 5067 downloadBytes = bytes.first; 5068 uploadBytes = bytes.second; 5069 } 5070 if (byteOption == JobSchedulerShellCommand.BYTE_OPTION_DOWNLOAD) { 5071 pw.println(downloadBytes); 5072 } else { 5073 pw.println(uploadBytes); 5074 } 5075 pw.println(); 5076 } 5077 } catch (RemoteException e) { 5078 // can't happen 5079 } 5080 return 0; 5081 } 5082 5083 /** Returns true if both the appop and permission are granted. */ 5084 private boolean checkRunUserInitiatedJobsPermission(int packageUid, String packageName) { 5085 return getRunUserInitiatedJobsPermissionState(packageUid, packageName) 5086 == PermissionChecker.PERMISSION_GRANTED; 5087 } 5088 5089 private int getRunUserInitiatedJobsPermissionState(int packageUid, String packageName) { 5090 return PermissionChecker.checkPermissionForPreflight(getTestableContext(), 5091 android.Manifest.permission.RUN_USER_INITIATED_JOBS, PermissionChecker.PID_UNKNOWN, 5092 packageUid, packageName); 5093 } 5094 5095 @VisibleForTesting 5096 protected ConnectivityController getConnectivityController() { 5097 return mConnectivityController; 5098 } 5099 5100 @VisibleForTesting 5101 protected QuotaController getQuotaController() { 5102 return mQuotaController; 5103 } 5104 5105 @VisibleForTesting 5106 protected TareController getTareController() { 5107 return mTareController; 5108 } 5109 5110 // Shell command infrastructure 5111 int getJobState(PrintWriter pw, String pkgName, int userId, @Nullable String namespace, 5112 int jobId) { 5113 try { 5114 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, 5115 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM); 5116 if (uid < 0) { 5117 pw.print("unknown("); 5118 pw.print(pkgName); 5119 pw.println(")"); 5120 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE; 5121 } 5122 5123 synchronized (mLock) { 5124 final JobStatus js = mJobs.getJobByUidAndJobId(uid, namespace, jobId); 5125 if (DEBUG) { 5126 Slog.d(TAG, 5127 "get-job-state " + namespace + "/" + uid + "/" + jobId + ": " + js); 5128 } 5129 if (js == null) { 5130 pw.print("unknown("); 5131 UserHandle.formatUid(pw, uid); 5132 pw.print("/jid"); 5133 pw.print(jobId); 5134 pw.println(")"); 5135 return JobSchedulerShellCommand.CMD_ERR_NO_JOB; 5136 } 5137 5138 boolean printed = false; 5139 if (mPendingJobQueue.contains(js)) { 5140 pw.print("pending"); 5141 printed = true; 5142 } 5143 if (mConcurrencyManager.isJobRunningLocked(js)) { 5144 if (printed) { 5145 pw.print(" "); 5146 } 5147 printed = true; 5148 pw.println("active"); 5149 } 5150 if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) { 5151 if (printed) { 5152 pw.print(" "); 5153 } 5154 printed = true; 5155 pw.println("user-stopped"); 5156 } 5157 if (!ArrayUtils.contains(mStartedUsers, js.getSourceUserId())) { 5158 if (printed) { 5159 pw.print(" "); 5160 } 5161 printed = true; 5162 pw.println("source-user-stopped"); 5163 } 5164 if (mBackingUpUids.get(js.getSourceUid())) { 5165 if (printed) { 5166 pw.print(" "); 5167 } 5168 printed = true; 5169 pw.println("backing-up"); 5170 } 5171 boolean componentPresent = false; 5172 try { 5173 componentPresent = (AppGlobals.getPackageManager().getServiceInfo( 5174 js.getServiceComponent(), 5175 PackageManager.MATCH_DIRECT_BOOT_AUTO, 5176 js.getUserId()) != null); 5177 } catch (RemoteException e) { 5178 } 5179 if (!componentPresent) { 5180 if (printed) { 5181 pw.print(" "); 5182 } 5183 printed = true; 5184 pw.println("no-component"); 5185 } 5186 if (js.isReady()) { 5187 if (printed) { 5188 pw.print(" "); 5189 } 5190 printed = true; 5191 pw.println("ready"); 5192 } 5193 if (!printed) { 5194 pw.print("waiting"); 5195 } 5196 pw.println(); 5197 } 5198 } catch (RemoteException e) { 5199 // can't happen 5200 } 5201 return 0; 5202 } 5203 5204 void resetExecutionQuota(@NonNull String pkgName, int userId) { 5205 synchronized (mLock) { 5206 mQuotaController.clearAppStatsLocked(userId, pkgName); 5207 } 5208 } 5209 5210 void resetScheduleQuota() { 5211 mQuotaTracker.clear(); 5212 } 5213 5214 void triggerDockState(boolean idleState) { 5215 final Intent dockIntent; 5216 if (idleState) { 5217 dockIntent = new Intent(Intent.ACTION_DOCK_IDLE); 5218 } else { 5219 dockIntent = new Intent(Intent.ACTION_DOCK_ACTIVE); 5220 } 5221 dockIntent.setPackage("android"); 5222 dockIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); 5223 getContext().sendBroadcastAsUser(dockIntent, UserHandle.ALL); 5224 } 5225 5226 static void dumpHelp(PrintWriter pw) { 5227 pw.println("Job Scheduler (jobscheduler) dump options:"); 5228 pw.println(" [-h] [package] ..."); 5229 pw.println(" -h: print this help"); 5230 pw.println(" [package] is an optional package name to limit the output to."); 5231 } 5232 5233 /** Sort jobs by caller UID, then by Job ID. */ 5234 private static void sortJobs(List<JobStatus> jobs) { 5235 Collections.sort(jobs, new Comparator<JobStatus>() { 5236 @Override 5237 public int compare(JobStatus o1, JobStatus o2) { 5238 int uid1 = o1.getUid(); 5239 int uid2 = o2.getUid(); 5240 int id1 = o1.getJobId(); 5241 int id2 = o2.getJobId(); 5242 if (uid1 != uid2) { 5243 return uid1 < uid2 ? -1 : 1; 5244 } 5245 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0); 5246 } 5247 }); 5248 } 5249 5250 @NeverCompile // Avoid size overhead of debugging code. 5251 void dumpInternal(final IndentingPrintWriter pw, int filterUid) { 5252 final int filterAppId = UserHandle.getAppId(filterUid); 5253 final long now = sSystemClock.millis(); 5254 final long nowElapsed = sElapsedRealtimeClock.millis(); 5255 final long nowUptime = sUptimeMillisClock.millis(); 5256 5257 final Predicate<JobStatus> predicate = (js) -> { 5258 return filterAppId == -1 || UserHandle.getAppId(js.getUid()) == filterAppId 5259 || UserHandle.getAppId(js.getSourceUid()) == filterAppId; 5260 }; 5261 synchronized (mLock) { 5262 mConstants.dump(pw); 5263 for (StateController controller : mControllers) { 5264 pw.increaseIndent(); 5265 controller.dumpConstants(pw); 5266 pw.decreaseIndent(); 5267 } 5268 pw.println(); 5269 5270 for (int i = mJobRestrictions.size() - 1; i >= 0; i--) { 5271 mJobRestrictions.get(i).dumpConstants(pw); 5272 } 5273 pw.println(); 5274 5275 mQuotaTracker.dump(pw); 5276 pw.println(); 5277 5278 pw.print("Battery charging: "); 5279 pw.println(mBatteryStateTracker.isCharging()); 5280 pw.print("Battery not low: "); 5281 pw.println(mBatteryStateTracker.isBatteryNotLow()); 5282 if (mBatteryStateTracker.isMonitoring()) { 5283 pw.print("MONITORING: seq="); 5284 pw.println(mBatteryStateTracker.getSeq()); 5285 } 5286 pw.println(); 5287 5288 pw.println("Started users: " + Arrays.toString(mStartedUsers)); 5289 pw.println(); 5290 5291 pw.print("Media Cloud Providers: "); 5292 pw.println(mCloudMediaProviderPackages); 5293 pw.println(); 5294 5295 pw.print("Registered "); 5296 pw.print(mJobs.size()); 5297 pw.println(" jobs:"); 5298 pw.increaseIndent(); 5299 boolean jobPrinted = false; 5300 if (mJobs.size() > 0) { 5301 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs(); 5302 sortJobs(jobs); 5303 for (JobStatus job : jobs) { 5304 // Skip printing details if the caller requested a filter 5305 if (!predicate.test(job)) { 5306 continue; 5307 } 5308 jobPrinted = true; 5309 5310 pw.print("JOB "); 5311 job.printUniqueId(pw); 5312 pw.print(": "); 5313 pw.println(job.toShortStringExceptUniqueId()); 5314 5315 pw.increaseIndent(); 5316 job.dump(pw, true, nowElapsed); 5317 5318 pw.print("Restricted due to:"); 5319 final boolean isRestricted = checkIfRestricted(job) != null; 5320 if (isRestricted) { 5321 for (int i = mJobRestrictions.size() - 1; i >= 0; i--) { 5322 final JobRestriction restriction = mJobRestrictions.get(i); 5323 if (restriction.isJobRestricted(job)) { 5324 final int reason = restriction.getInternalReason(); 5325 pw.print(" "); 5326 pw.print(JobParameters.getInternalReasonCodeDescription(reason)); 5327 } 5328 } 5329 } else { 5330 pw.print(" none"); 5331 } 5332 pw.println("."); 5333 5334 pw.print("Ready: "); 5335 pw.print(isReadyToBeExecutedLocked(job)); 5336 pw.print(" (job="); 5337 pw.print(job.isReady()); 5338 pw.print(" user="); 5339 pw.print(areUsersStartedLocked(job)); 5340 pw.print(" !restricted="); 5341 pw.print(!isRestricted); 5342 pw.print(" !pending="); 5343 pw.print(!mPendingJobQueue.contains(job)); 5344 pw.print(" !active="); 5345 pw.print(!mConcurrencyManager.isJobRunningLocked(job)); 5346 pw.print(" !backingup="); 5347 pw.print(!(mBackingUpUids.get(job.getSourceUid()))); 5348 pw.print(" comp="); 5349 pw.print(isComponentUsable(job)); 5350 pw.println(")"); 5351 5352 pw.decreaseIndent(); 5353 } 5354 } 5355 if (!jobPrinted) { 5356 pw.println("None."); 5357 } 5358 pw.decreaseIndent(); 5359 5360 for (int i = 0; i < mControllers.size(); i++) { 5361 pw.println(); 5362 pw.println(mControllers.get(i).getClass().getSimpleName() + ":"); 5363 pw.increaseIndent(); 5364 mControllers.get(i).dumpControllerStateLocked(pw, predicate); 5365 pw.decreaseIndent(); 5366 } 5367 5368 boolean procStatePrinted = false; 5369 for (int i = 0; i < mUidProcStates.size(); i++) { 5370 int uid = mUidProcStates.keyAt(i); 5371 if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) { 5372 if (!procStatePrinted) { 5373 procStatePrinted = true; 5374 pw.println(); 5375 pw.println("Uid proc states:"); 5376 pw.increaseIndent(); 5377 } 5378 pw.print(UserHandle.formatUid(uid)); 5379 pw.print(": "); 5380 pw.println(ActivityManager.procStateToString(mUidProcStates.valueAt(i))); 5381 } 5382 } 5383 if (procStatePrinted) { 5384 pw.decreaseIndent(); 5385 } 5386 5387 boolean overridePrinted = false; 5388 for (int i = 0; i < mUidBiasOverride.size(); i++) { 5389 int uid = mUidBiasOverride.keyAt(i); 5390 if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) { 5391 if (!overridePrinted) { 5392 overridePrinted = true; 5393 pw.println(); 5394 pw.println("Uid bias overrides:"); 5395 pw.increaseIndent(); 5396 } 5397 pw.print(UserHandle.formatUid(uid)); 5398 pw.print(": "); pw.println(mUidBiasOverride.valueAt(i)); 5399 } 5400 } 5401 if (overridePrinted) { 5402 pw.decreaseIndent(); 5403 } 5404 5405 boolean capabilitiesPrinted = false; 5406 for (int i = 0; i < mUidCapabilities.size(); i++) { 5407 int uid = mUidCapabilities.keyAt(i); 5408 if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) { 5409 if (!capabilitiesPrinted) { 5410 capabilitiesPrinted = true; 5411 pw.println(); 5412 pw.println("Uid capabilities:"); 5413 pw.increaseIndent(); 5414 } 5415 pw.print(UserHandle.formatUid(uid)); 5416 pw.print(": "); 5417 pw.println(ActivityManager.getCapabilitiesSummary(mUidCapabilities.valueAt(i))); 5418 } 5419 } 5420 if (capabilitiesPrinted) { 5421 pw.decreaseIndent(); 5422 } 5423 5424 boolean uidMapPrinted = false; 5425 for (int i = 0; i < mUidToPackageCache.size(); ++i) { 5426 final int uid = mUidToPackageCache.keyAt(i); 5427 if (filterUid != -1 && filterUid != uid) { 5428 continue; 5429 } 5430 if (!uidMapPrinted) { 5431 uidMapPrinted = true; 5432 pw.println(); 5433 pw.println("Cached UID->package map:"); 5434 pw.increaseIndent(); 5435 } 5436 pw.print(uid); 5437 pw.print(": "); 5438 pw.println(mUidToPackageCache.get(uid)); 5439 } 5440 if (uidMapPrinted) { 5441 pw.decreaseIndent(); 5442 } 5443 5444 boolean backingPrinted = false; 5445 for (int i = 0; i < mBackingUpUids.size(); i++) { 5446 int uid = mBackingUpUids.keyAt(i); 5447 if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) { 5448 if (!backingPrinted) { 5449 pw.println(); 5450 pw.println("Backing up uids:"); 5451 pw.increaseIndent(); 5452 backingPrinted = true; 5453 } else { 5454 pw.print(", "); 5455 } 5456 pw.print(UserHandle.formatUid(uid)); 5457 } 5458 } 5459 if (backingPrinted) { 5460 pw.decreaseIndent(); 5461 pw.println(); 5462 } 5463 5464 pw.println(); 5465 mJobPackageTracker.dump(pw, filterAppId); 5466 pw.println(); 5467 if (mJobPackageTracker.dumpHistory(pw, filterAppId)) { 5468 pw.println(); 5469 } 5470 5471 boolean pendingPrinted = false; 5472 pw.println("Pending queue:"); 5473 pw.increaseIndent(); 5474 JobStatus job; 5475 int pendingIdx = 0; 5476 mPendingJobQueue.resetIterator(); 5477 while ((job = mPendingJobQueue.next()) != null) { 5478 pendingIdx++; 5479 if (!predicate.test(job)) { 5480 continue; 5481 } 5482 if (!pendingPrinted) { 5483 pendingPrinted = true; 5484 } 5485 5486 pw.print("Pending #"); pw.print(pendingIdx); pw.print(": "); 5487 pw.println(job.toShortString()); 5488 5489 pw.increaseIndent(); 5490 job.dump(pw, false, nowElapsed); 5491 int bias = evaluateJobBiasLocked(job); 5492 pw.print("Evaluated bias: "); 5493 pw.println(JobInfo.getBiasString(bias)); 5494 5495 pw.print("Tag: "); pw.println(job.getTag()); 5496 pw.print("Enq: "); 5497 TimeUtils.formatDuration(job.madePending - nowUptime, pw); 5498 pw.decreaseIndent(); 5499 pw.println(); 5500 } 5501 if (!pendingPrinted) { 5502 pw.println("None"); 5503 } 5504 pw.decreaseIndent(); 5505 5506 pw.println(); 5507 mConcurrencyManager.dumpContextInfoLocked(pw, predicate, nowElapsed, nowUptime); 5508 5509 pw.println(); 5510 boolean recentPrinted = false; 5511 pw.println("Recently completed jobs:"); 5512 pw.increaseIndent(); 5513 for (int r = 1; r <= NUM_COMPLETED_JOB_HISTORY; ++r) { 5514 // Print most recent first 5515 final int idx = (mLastCompletedJobIndex + NUM_COMPLETED_JOB_HISTORY - r) 5516 % NUM_COMPLETED_JOB_HISTORY; 5517 job = mLastCompletedJobs[idx]; 5518 if (job != null) { 5519 if (!predicate.test(job)) { 5520 continue; 5521 } 5522 recentPrinted = true; 5523 TimeUtils.formatDuration(mLastCompletedJobTimeElapsed[idx], nowElapsed, pw); 5524 pw.println(); 5525 // Double indent for readability 5526 pw.increaseIndent(); 5527 pw.increaseIndent(); 5528 pw.println(job.toShortString()); 5529 job.dump(pw, true, nowElapsed); 5530 pw.decreaseIndent(); 5531 pw.decreaseIndent(); 5532 } 5533 } 5534 if (!recentPrinted) { 5535 pw.println("None"); 5536 } 5537 pw.decreaseIndent(); 5538 pw.println(); 5539 5540 boolean recentCancellationsPrinted = false; 5541 for (int r = 1; r <= mLastCancelledJobs.length; ++r) { 5542 // Print most recent first 5543 final int idx = (mLastCancelledJobIndex + mLastCancelledJobs.length - r) 5544 % mLastCancelledJobs.length; 5545 job = mLastCancelledJobs[idx]; 5546 if (job != null) { 5547 if (!predicate.test(job)) { 5548 continue; 5549 } 5550 if (!recentCancellationsPrinted) { 5551 pw.println(); 5552 pw.println("Recently cancelled jobs:"); 5553 pw.increaseIndent(); 5554 recentCancellationsPrinted = true; 5555 } 5556 TimeUtils.formatDuration(mLastCancelledJobTimeElapsed[idx], nowElapsed, pw); 5557 pw.println(); 5558 // Double indent for readability 5559 pw.increaseIndent(); 5560 pw.increaseIndent(); 5561 pw.println(job.toShortString()); 5562 job.dump(pw, true, nowElapsed); 5563 pw.decreaseIndent(); 5564 pw.decreaseIndent(); 5565 } 5566 } 5567 if (!recentCancellationsPrinted) { 5568 pw.decreaseIndent(); 5569 pw.println(); 5570 } 5571 5572 if (filterUid == -1) { 5573 pw.println(); 5574 pw.print("mReadyToRock="); pw.println(mReadyToRock); 5575 pw.print("mReportedActive="); pw.println(mReportedActive); 5576 } 5577 pw.println(); 5578 5579 mConcurrencyManager.dumpLocked(pw, now, nowElapsed); 5580 5581 pw.println(); 5582 pw.print("PersistStats: "); 5583 pw.println(mJobs.getPersistStats()); 5584 } 5585 pw.println(); 5586 } 5587 5588 void dumpInternalProto(final FileDescriptor fd, int filterUid) { 5589 ProtoOutputStream proto = new ProtoOutputStream(fd); 5590 final int filterAppId = UserHandle.getAppId(filterUid); 5591 final long now = sSystemClock.millis(); 5592 final long nowElapsed = sElapsedRealtimeClock.millis(); 5593 final long nowUptime = sUptimeMillisClock.millis(); 5594 final Predicate<JobStatus> predicate = (js) -> { 5595 return filterAppId == -1 || UserHandle.getAppId(js.getUid()) == filterAppId 5596 || UserHandle.getAppId(js.getSourceUid()) == filterAppId; 5597 }; 5598 5599 synchronized (mLock) { 5600 final long settingsToken = proto.start(JobSchedulerServiceDumpProto.SETTINGS); 5601 mConstants.dump(proto); 5602 for (StateController controller : mControllers) { 5603 controller.dumpConstants(proto); 5604 } 5605 proto.end(settingsToken); 5606 5607 for (int i = mJobRestrictions.size() - 1; i >= 0; i--) { 5608 mJobRestrictions.get(i).dumpConstants(proto); 5609 } 5610 5611 for (int u : mStartedUsers) { 5612 proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u); 5613 } 5614 5615 mQuotaTracker.dump(proto, JobSchedulerServiceDumpProto.QUOTA_TRACKER); 5616 5617 if (mJobs.size() > 0) { 5618 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs(); 5619 sortJobs(jobs); 5620 for (JobStatus job : jobs) { 5621 final long rjToken = proto.start(JobSchedulerServiceDumpProto.REGISTERED_JOBS); 5622 job.writeToShortProto(proto, JobSchedulerServiceDumpProto.RegisteredJob.INFO); 5623 5624 // Skip printing details if the caller requested a filter 5625 if (!predicate.test(job)) { 5626 continue; 5627 } 5628 5629 job.dump(proto, 5630 JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed); 5631 5632 proto.write( 5633 JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY_TO_BE_EXECUTED, 5634 isReadyToBeExecutedLocked(job)); 5635 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY, 5636 job.isReady()); 5637 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.ARE_USERS_STARTED, 5638 areUsersStartedLocked(job)); 5639 proto.write( 5640 JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_RESTRICTED, 5641 checkIfRestricted(job) != null); 5642 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_PENDING, 5643 mPendingJobQueue.contains(job)); 5644 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE, 5645 mConcurrencyManager.isJobRunningLocked(job)); 5646 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_UID_BACKING_UP, 5647 mBackingUpUids.get(job.getSourceUid())); 5648 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_USABLE, 5649 isComponentUsable(job)); 5650 5651 for (JobRestriction restriction : mJobRestrictions) { 5652 final long restrictionsToken = proto.start( 5653 JobSchedulerServiceDumpProto.RegisteredJob.RESTRICTIONS); 5654 proto.write(JobSchedulerServiceDumpProto.JobRestriction.REASON, 5655 restriction.getInternalReason()); 5656 proto.write(JobSchedulerServiceDumpProto.JobRestriction.IS_RESTRICTING, 5657 restriction.isJobRestricted(job)); 5658 proto.end(restrictionsToken); 5659 } 5660 5661 proto.end(rjToken); 5662 } 5663 } 5664 for (StateController controller : mControllers) { 5665 controller.dumpControllerStateLocked( 5666 proto, JobSchedulerServiceDumpProto.CONTROLLERS, predicate); 5667 } 5668 for (int i = 0; i < mUidBiasOverride.size(); i++) { 5669 int uid = mUidBiasOverride.keyAt(i); 5670 if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) { 5671 long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES); 5672 proto.write(JobSchedulerServiceDumpProto.PriorityOverride.UID, uid); 5673 proto.write(JobSchedulerServiceDumpProto.PriorityOverride.OVERRIDE_VALUE, 5674 mUidBiasOverride.valueAt(i)); 5675 proto.end(pToken); 5676 } 5677 } 5678 for (int i = 0; i < mBackingUpUids.size(); i++) { 5679 int uid = mBackingUpUids.keyAt(i); 5680 if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) { 5681 proto.write(JobSchedulerServiceDumpProto.BACKING_UP_UIDS, uid); 5682 } 5683 } 5684 5685 mJobPackageTracker.dump(proto, JobSchedulerServiceDumpProto.PACKAGE_TRACKER, 5686 filterAppId); 5687 mJobPackageTracker.dumpHistory(proto, JobSchedulerServiceDumpProto.HISTORY, 5688 filterAppId); 5689 5690 JobStatus job; 5691 mPendingJobQueue.resetIterator(); 5692 while ((job = mPendingJobQueue.next()) != null) { 5693 final long pjToken = proto.start(JobSchedulerServiceDumpProto.PENDING_JOBS); 5694 5695 job.writeToShortProto(proto, PendingJob.INFO); 5696 job.dump(proto, PendingJob.DUMP, false, nowElapsed); 5697 proto.write(PendingJob.EVALUATED_PRIORITY, evaluateJobBiasLocked(job)); 5698 proto.write(PendingJob.PENDING_DURATION_MS, nowUptime - job.madePending); 5699 5700 proto.end(pjToken); 5701 } 5702 if (filterUid == -1) { 5703 proto.write(JobSchedulerServiceDumpProto.IS_READY_TO_ROCK, mReadyToRock); 5704 proto.write(JobSchedulerServiceDumpProto.REPORTED_ACTIVE, mReportedActive); 5705 } 5706 mConcurrencyManager.dumpProtoLocked(proto, 5707 JobSchedulerServiceDumpProto.CONCURRENCY_MANAGER, now, nowElapsed); 5708 5709 mJobs.getPersistStats().dumpDebug(proto, JobSchedulerServiceDumpProto.PERSIST_STATS); 5710 } 5711 5712 proto.flush(); 5713 } 5714 } 5715