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