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