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