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