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 java.io.FileDescriptor; 23 import java.io.PrintWriter; 24 import java.util.ArrayList; 25 import java.util.Arrays; 26 import java.util.Collections; 27 import java.util.Comparator; 28 import java.util.Iterator; 29 import java.util.List; 30 31 import android.app.Activity; 32 import android.app.ActivityManager; 33 import android.app.AppGlobals; 34 import android.app.IUidObserver; 35 import android.app.job.JobInfo; 36 import android.app.job.JobParameters; 37 import android.app.job.JobScheduler; 38 import android.app.job.JobService; 39 import android.app.job.IJobScheduler; 40 import android.app.job.JobWorkItem; 41 import android.content.BroadcastReceiver; 42 import android.content.ComponentName; 43 import android.content.ContentResolver; 44 import android.content.Context; 45 import android.content.Intent; 46 import android.content.IntentFilter; 47 import android.content.pm.IPackageManager; 48 import android.content.pm.PackageManager; 49 import android.content.pm.ServiceInfo; 50 import android.content.pm.PackageManager.NameNotFoundException; 51 import android.database.ContentObserver; 52 import android.net.Uri; 53 import android.os.BatteryStats; 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.PowerManager; 60 import android.os.RemoteException; 61 import android.os.ResultReceiver; 62 import android.os.ServiceManager; 63 import android.os.ShellCallback; 64 import android.os.SystemClock; 65 import android.os.UserHandle; 66 import android.os.UserManagerInternal; 67 import android.provider.Settings; 68 import android.util.KeyValueListParser; 69 import android.util.Slog; 70 import android.util.SparseArray; 71 import android.util.SparseIntArray; 72 import android.util.TimeUtils; 73 74 import com.android.internal.app.IBatteryStats; 75 import com.android.internal.app.procstats.ProcessStats; 76 import com.android.internal.util.ArrayUtils; 77 import com.android.internal.util.DumpUtils; 78 import com.android.server.DeviceIdleController; 79 import com.android.server.FgThread; 80 import com.android.server.LocalServices; 81 import com.android.server.job.JobStore.JobStatusFunctor; 82 import com.android.server.job.controllers.AppIdleController; 83 import com.android.server.job.controllers.BatteryController; 84 import com.android.server.job.controllers.ConnectivityController; 85 import com.android.server.job.controllers.ContentObserverController; 86 import com.android.server.job.controllers.DeviceIdleJobsController; 87 import com.android.server.job.controllers.IdleController; 88 import com.android.server.job.controllers.JobStatus; 89 import com.android.server.job.controllers.StateController; 90 import com.android.server.job.controllers.StorageController; 91 import com.android.server.job.controllers.TimeController; 92 93 import libcore.util.EmptyArray; 94 95 /** 96 * Responsible for taking jobs representing work to be performed by a client app, and determining 97 * based on the criteria specified when that job should be run against the client application's 98 * endpoint. 99 * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing 100 * about constraints, or the state of active jobs. It receives callbacks from the various 101 * controllers and completed jobs and operates accordingly. 102 * 103 * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object. 104 * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}. 105 * @hide 106 */ 107 public final class JobSchedulerService extends com.android.server.SystemService 108 implements StateChangedListener, JobCompletedListener { 109 static final String TAG = "JobSchedulerService"; 110 public static final boolean DEBUG = false; 111 112 /** The maximum number of concurrent jobs we run at one time. */ 113 private static final int MAX_JOB_CONTEXTS_COUNT = 16; 114 /** Enforce a per-app limit on scheduled jobs? */ 115 private static final boolean ENFORCE_MAX_JOBS = true; 116 /** The maximum number of jobs that we allow an unprivileged app to schedule */ 117 private static final int MAX_JOBS_PER_APP = 100; 118 119 120 /** Global local for all job scheduler state. */ 121 final Object mLock = new Object(); 122 /** Master list of jobs. */ 123 final JobStore mJobs; 124 /** Tracking amount of time each package runs for. */ 125 final JobPackageTracker mJobPackageTracker = new JobPackageTracker(); 126 127 static final int MSG_JOB_EXPIRED = 0; 128 static final int MSG_CHECK_JOB = 1; 129 static final int MSG_STOP_JOB = 2; 130 static final int MSG_CHECK_JOB_GREEDY = 3; 131 132 /** 133 * Track Services that have currently active or pending jobs. The index is provided by 134 * {@link JobStatus#getServiceToken()} 135 */ 136 final List<JobServiceContext> mActiveServices = new ArrayList<>(); 137 /** List of controllers that will notify this service of updates to jobs. */ 138 List<StateController> mControllers; 139 /** Need direct access to this for testing. */ 140 BatteryController mBatteryController; 141 /** Need direct access to this for testing. */ 142 StorageController mStorageController; 143 /** 144 * Queue of pending jobs. The JobServiceContext class will receive jobs from this list 145 * when ready to execute them. 146 */ 147 final ArrayList<JobStatus> mPendingJobs = new ArrayList<>(); 148 149 int[] mStartedUsers = EmptyArray.INT; 150 151 final JobHandler mHandler; 152 final JobSchedulerStub mJobSchedulerStub; 153 154 IBatteryStats mBatteryStats; 155 PowerManager mPowerManager; 156 DeviceIdleController.LocalService mLocalDeviceIdleController; 157 158 /** 159 * Set to true once we are allowed to run third party apps. 160 */ 161 boolean mReadyToRock; 162 163 /** 164 * What we last reported to DeviceIdleController about whether we are active. 165 */ 166 boolean mReportedActive; 167 168 /** 169 * Current limit on the number of concurrent JobServiceContext entries we want to 170 * keep actively running a job. 171 */ 172 int mMaxActiveJobs = 1; 173 174 /** 175 * Which uids are currently in the foreground. 176 */ 177 final SparseIntArray mUidPriorityOverride = new SparseIntArray(); 178 179 /** 180 * Which uids are currently performing backups, so we shouldn't allow their jobs to run. 181 */ 182 final SparseIntArray mBackingUpUids = new SparseIntArray(); 183 184 // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked -- 185 186 /** 187 * This array essentially stores the state of mActiveServices array. 188 * The ith index stores the job present on the ith JobServiceContext. 189 * We manipulate this array until we arrive at what jobs should be running on 190 * what JobServiceContext. 191 */ 192 JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT]; 193 /** 194 * Indicates whether we need to act on this jobContext id 195 */ 196 boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT]; 197 /** 198 * The uid whose jobs we would like to assign to a context. 199 */ 200 int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT]; 201 202 /** 203 * All times are in milliseconds. These constants are kept synchronized with the system 204 * global Settings. Any access to this class or its fields should be done while 205 * holding the JobSchedulerService.mLock lock. 206 */ 207 private final class Constants extends ContentObserver { 208 // Key names stored in the settings value. 209 private static final String KEY_MIN_IDLE_COUNT = "min_idle_count"; 210 private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count"; 211 private static final String KEY_MIN_BATTERY_NOT_LOW_COUNT = "min_battery_not_low_count"; 212 private static final String KEY_MIN_STORAGE_NOT_LOW_COUNT = "min_storage_not_low_count"; 213 private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count"; 214 private static final String KEY_MIN_CONTENT_COUNT = "min_content_count"; 215 private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count"; 216 private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor"; 217 private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor"; 218 private static final String KEY_FG_JOB_COUNT = "fg_job_count"; 219 private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count"; 220 private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count"; 221 private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count"; 222 private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count"; 223 private static final String KEY_MAX_STANDARD_RESCHEDULE_COUNT 224 = "max_standard_reschedule_count"; 225 private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count"; 226 private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time"; 227 private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time"; 228 229 private static final int DEFAULT_MIN_IDLE_COUNT = 1; 230 private static final int DEFAULT_MIN_CHARGING_COUNT = 1; 231 private static final int DEFAULT_MIN_BATTERY_NOT_LOW_COUNT = 1; 232 private static final int DEFAULT_MIN_STORAGE_NOT_LOW_COUNT = 1; 233 private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1; 234 private static final int DEFAULT_MIN_CONTENT_COUNT = 1; 235 private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1; 236 private static final float DEFAULT_HEAVY_USE_FACTOR = .9f; 237 private static final float DEFAULT_MODERATE_USE_FACTOR = .5f; 238 private static final int DEFAULT_FG_JOB_COUNT = 4; 239 private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6; 240 private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4; 241 private static final int DEFAULT_BG_LOW_JOB_COUNT = 1; 242 private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1; 243 private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE; 244 private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE; 245 private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS; 246 private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS; 247 248 /** 249 * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things 250 * early. 251 */ 252 int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT; 253 /** 254 * Minimum # of charging jobs that must be ready in order to force the JMS to schedule 255 * things early. 256 */ 257 int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT; 258 /** 259 * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to 260 * schedule things early. 261 */ 262 int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT; 263 /** 264 * Minimum # of "storage not low" jobs that must be ready in order to force the JMS to 265 * schedule things early. 266 */ 267 int MIN_STORAGE_NOT_LOW_COUNT = DEFAULT_MIN_STORAGE_NOT_LOW_COUNT; 268 /** 269 * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule 270 * things early. 1 == Run connectivity jobs as soon as ready. 271 */ 272 int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT; 273 /** 274 * Minimum # of content trigger jobs that must be ready in order to force the JMS to 275 * schedule things early. 276 */ 277 int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT; 278 /** 279 * Minimum # of jobs (with no particular constraints) for which the JMS will be happy 280 * running some work early. This (and thus the other min counts) is now set to 1, to 281 * prevent any batching at this level. Since we now do batching through doze, that is 282 * a much better mechanism. 283 */ 284 int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT; 285 /** 286 * This is the job execution factor that is considered to be heavy use of the system. 287 */ 288 float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR; 289 /** 290 * This is the job execution factor that is considered to be moderate use of the system. 291 */ 292 float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR; 293 /** 294 * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app. 295 */ 296 int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT; 297 /** 298 * The maximum number of background jobs we allow when the system is in a normal 299 * memory state. 300 */ 301 int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT; 302 /** 303 * The maximum number of background jobs we allow when the system is in a moderate 304 * memory state. 305 */ 306 int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT; 307 /** 308 * The maximum number of background jobs we allow when the system is in a low 309 * memory state. 310 */ 311 int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT; 312 /** 313 * The maximum number of background jobs we allow when the system is in a critical 314 * memory state. 315 */ 316 int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT; 317 /** 318 * The maximum number of times we allow a job to have itself rescheduled before 319 * giving up on it, for standard jobs. 320 */ 321 int MAX_STANDARD_RESCHEDULE_COUNT = DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT; 322 /** 323 * The maximum number of times we allow a job to have itself rescheduled before 324 * giving up on it, for jobs that are executing work. 325 */ 326 int MAX_WORK_RESCHEDULE_COUNT = DEFAULT_MAX_WORK_RESCHEDULE_COUNT; 327 /** 328 * The minimum backoff time to allow for linear backoff. 329 */ 330 long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME; 331 /** 332 * The minimum backoff time to allow for exponential backoff. 333 */ 334 long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME; 335 336 private ContentResolver mResolver; 337 private final KeyValueListParser mParser = new KeyValueListParser(','); 338 339 public Constants(Handler handler) { 340 super(handler); 341 } 342 343 public void start(ContentResolver resolver) { 344 mResolver = resolver; 345 mResolver.registerContentObserver(Settings.Global.getUriFor( 346 Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this); 347 updateConstants(); 348 } 349 350 @Override 351 public void onChange(boolean selfChange, Uri uri) { 352 updateConstants(); 353 } 354 355 private void updateConstants() { 356 synchronized (mLock) { 357 try { 358 mParser.setString(Settings.Global.getString(mResolver, 359 Settings.Global.JOB_SCHEDULER_CONSTANTS)); 360 } catch (IllegalArgumentException e) { 361 // Failed to parse the settings string, log this and move on 362 // with defaults. 363 Slog.e(TAG, "Bad jobscheduler settings", e); 364 } 365 366 MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT, 367 DEFAULT_MIN_IDLE_COUNT); 368 MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT, 369 DEFAULT_MIN_CHARGING_COUNT); 370 MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT, 371 DEFAULT_MIN_BATTERY_NOT_LOW_COUNT); 372 MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT, 373 DEFAULT_MIN_STORAGE_NOT_LOW_COUNT); 374 MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT, 375 DEFAULT_MIN_CONNECTIVITY_COUNT); 376 MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT, 377 DEFAULT_MIN_CONTENT_COUNT); 378 MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT, 379 DEFAULT_MIN_READY_JOBS_COUNT); 380 HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR, 381 DEFAULT_HEAVY_USE_FACTOR); 382 MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR, 383 DEFAULT_MODERATE_USE_FACTOR); 384 FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT, 385 DEFAULT_FG_JOB_COUNT); 386 BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT, 387 DEFAULT_BG_NORMAL_JOB_COUNT); 388 if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) { 389 BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT; 390 } 391 BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT, 392 DEFAULT_BG_MODERATE_JOB_COUNT); 393 if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) { 394 BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT; 395 } 396 BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT, 397 DEFAULT_BG_LOW_JOB_COUNT); 398 if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) { 399 BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT; 400 } 401 BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT, 402 DEFAULT_BG_CRITICAL_JOB_COUNT); 403 if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) { 404 BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT; 405 } 406 MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT, 407 DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT); 408 MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT, 409 DEFAULT_MAX_WORK_RESCHEDULE_COUNT); 410 MIN_LINEAR_BACKOFF_TIME = mParser.getLong(KEY_MIN_LINEAR_BACKOFF_TIME, 411 DEFAULT_MIN_LINEAR_BACKOFF_TIME); 412 MIN_EXP_BACKOFF_TIME = mParser.getLong(KEY_MIN_EXP_BACKOFF_TIME, 413 DEFAULT_MIN_EXP_BACKOFF_TIME); 414 } 415 } 416 417 void dump(PrintWriter pw) { 418 pw.println(" Settings:"); 419 420 pw.print(" "); pw.print(KEY_MIN_IDLE_COUNT); pw.print("="); 421 pw.print(MIN_IDLE_COUNT); pw.println(); 422 423 pw.print(" "); pw.print(KEY_MIN_CHARGING_COUNT); pw.print("="); 424 pw.print(MIN_CHARGING_COUNT); pw.println(); 425 426 pw.print(" "); pw.print(KEY_MIN_BATTERY_NOT_LOW_COUNT); pw.print("="); 427 pw.print(MIN_BATTERY_NOT_LOW_COUNT); pw.println(); 428 429 pw.print(" "); pw.print(KEY_MIN_STORAGE_NOT_LOW_COUNT); pw.print("="); 430 pw.print(MIN_STORAGE_NOT_LOW_COUNT); pw.println(); 431 432 pw.print(" "); pw.print(KEY_MIN_CONNECTIVITY_COUNT); pw.print("="); 433 pw.print(MIN_CONNECTIVITY_COUNT); pw.println(); 434 435 pw.print(" "); pw.print(KEY_MIN_CONTENT_COUNT); pw.print("="); 436 pw.print(MIN_CONTENT_COUNT); pw.println(); 437 438 pw.print(" "); pw.print(KEY_MIN_READY_JOBS_COUNT); pw.print("="); 439 pw.print(MIN_READY_JOBS_COUNT); pw.println(); 440 441 pw.print(" "); pw.print(KEY_HEAVY_USE_FACTOR); pw.print("="); 442 pw.print(HEAVY_USE_FACTOR); pw.println(); 443 444 pw.print(" "); pw.print(KEY_MODERATE_USE_FACTOR); pw.print("="); 445 pw.print(MODERATE_USE_FACTOR); pw.println(); 446 447 pw.print(" "); pw.print(KEY_FG_JOB_COUNT); pw.print("="); 448 pw.print(FG_JOB_COUNT); pw.println(); 449 450 pw.print(" "); pw.print(KEY_BG_NORMAL_JOB_COUNT); pw.print("="); 451 pw.print(BG_NORMAL_JOB_COUNT); pw.println(); 452 453 pw.print(" "); pw.print(KEY_BG_MODERATE_JOB_COUNT); pw.print("="); 454 pw.print(BG_MODERATE_JOB_COUNT); pw.println(); 455 456 pw.print(" "); pw.print(KEY_BG_LOW_JOB_COUNT); pw.print("="); 457 pw.print(BG_LOW_JOB_COUNT); pw.println(); 458 459 pw.print(" "); pw.print(KEY_BG_CRITICAL_JOB_COUNT); pw.print("="); 460 pw.print(BG_CRITICAL_JOB_COUNT); pw.println(); 461 462 pw.print(" "); pw.print(KEY_MAX_STANDARD_RESCHEDULE_COUNT); pw.print("="); 463 pw.print(MAX_STANDARD_RESCHEDULE_COUNT); pw.println(); 464 465 pw.print(" "); pw.print(KEY_MAX_WORK_RESCHEDULE_COUNT); pw.print("="); 466 pw.print(MAX_WORK_RESCHEDULE_COUNT); pw.println(); 467 468 pw.print(" "); pw.print(KEY_MIN_LINEAR_BACKOFF_TIME); pw.print("="); 469 pw.print(MIN_LINEAR_BACKOFF_TIME); pw.println(); 470 471 pw.print(" "); pw.print(KEY_MIN_EXP_BACKOFF_TIME); pw.print("="); 472 pw.print(MIN_EXP_BACKOFF_TIME); pw.println(); 473 } 474 } 475 476 final Constants mConstants; 477 478 static final Comparator<JobStatus> mEnqueueTimeComparator = (o1, o2) -> { 479 if (o1.enqueueTime < o2.enqueueTime) { 480 return -1; 481 } 482 return o1.enqueueTime > o2.enqueueTime ? 1 : 0; 483 }; 484 485 static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) { 486 int where = Collections.binarySearch(array, newItem, comparator); 487 if (where < 0) { 488 where = ~where; 489 } 490 array.add(where, newItem); 491 } 492 493 /** 494 * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we 495 * still clean up. On reinstall the package will have a new uid. 496 */ 497 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 498 @Override 499 public void onReceive(Context context, Intent intent) { 500 final String action = intent.getAction(); 501 if (DEBUG) { 502 Slog.d(TAG, "Receieved: " + action); 503 } 504 final String pkgName = getPackageName(intent); 505 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1); 506 507 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) { 508 // Purge the app's jobs if the whole package was just disabled. When this is 509 // the case the component name will be a bare package name. 510 if (pkgName != null && pkgUid != -1) { 511 final String[] changedComponents = intent.getStringArrayExtra( 512 Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST); 513 if (changedComponents != null) { 514 for (String component : changedComponents) { 515 if (component.equals(pkgName)) { 516 if (DEBUG) { 517 Slog.d(TAG, "Package state change: " + pkgName); 518 } 519 try { 520 final int userId = UserHandle.getUserId(pkgUid); 521 IPackageManager pm = AppGlobals.getPackageManager(); 522 final int state = pm.getApplicationEnabledSetting(pkgName, userId); 523 if (state == COMPONENT_ENABLED_STATE_DISABLED 524 || state == COMPONENT_ENABLED_STATE_DISABLED_USER) { 525 if (DEBUG) { 526 Slog.d(TAG, "Removing jobs for package " + pkgName 527 + " in user " + userId); 528 } 529 cancelJobsForPackageAndUid(pkgName, pkgUid, 530 "app disabled"); 531 } 532 } catch (RemoteException|IllegalArgumentException e) { 533 /* 534 * IllegalArgumentException means that the package doesn't exist. 535 * This arises when PACKAGE_CHANGED broadcast delivery has lagged 536 * behind outright uninstall, so by the time we try to act it's gone. 537 * We don't need to act on this PACKAGE_CHANGED when this happens; 538 * we'll get a PACKAGE_REMOVED later and clean up then. 539 * 540 * RemoteException can't actually happen; the package manager is 541 * running in this same process. 542 */ 543 } 544 break; 545 } 546 } 547 } 548 } else { 549 Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid); 550 } 551 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { 552 // If this is an outright uninstall rather than the first half of an 553 // app update sequence, cancel the jobs associated with the app. 554 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { 555 int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1); 556 if (DEBUG) { 557 Slog.d(TAG, "Removing jobs for uid: " + uidRemoved); 558 } 559 cancelJobsForPackageAndUid(pkgName, uidRemoved, "app uninstalled"); 560 } 561 } else if (Intent.ACTION_USER_REMOVED.equals(action)) { 562 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); 563 if (DEBUG) { 564 Slog.d(TAG, "Removing jobs for user: " + userId); 565 } 566 cancelJobsForUser(userId); 567 } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) { 568 // Has this package scheduled any jobs, such that we will take action 569 // if it were to be force-stopped? 570 if (pkgUid != -1) { 571 List<JobStatus> jobsForUid; 572 synchronized (mLock) { 573 jobsForUid = mJobs.getJobsByUid(pkgUid); 574 } 575 for (int i = jobsForUid.size() - 1; i >= 0; i--) { 576 if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) { 577 if (DEBUG) { 578 Slog.d(TAG, "Restart query: package " + pkgName + " at uid " 579 + pkgUid + " has jobs"); 580 } 581 setResultCode(Activity.RESULT_OK); 582 break; 583 } 584 } 585 } 586 } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) { 587 // possible force-stop 588 if (pkgUid != -1) { 589 if (DEBUG) { 590 Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid); 591 } 592 cancelJobsForPackageAndUid(pkgName, pkgUid, "app force stopped"); 593 } 594 } 595 } 596 }; 597 598 private String getPackageName(Intent intent) { 599 Uri uri = intent.getData(); 600 String pkg = uri != null ? uri.getSchemeSpecificPart() : null; 601 return pkg; 602 } 603 604 final private IUidObserver mUidObserver = new IUidObserver.Stub() { 605 @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) { 606 updateUidState(uid, procState); 607 } 608 609 @Override public void onUidGone(int uid, boolean disabled) { 610 updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); 611 if (disabled) { 612 cancelJobsForUid(uid, "uid gone"); 613 } 614 } 615 616 @Override public void onUidActive(int uid) throws RemoteException { 617 } 618 619 @Override public void onUidIdle(int uid, boolean disabled) { 620 if (disabled) { 621 cancelJobsForUid(uid, "app uid idle"); 622 } 623 } 624 625 @Override public void onUidCachedChanged(int uid, boolean cached) { 626 } 627 }; 628 629 public Object getLock() { 630 return mLock; 631 } 632 633 public JobStore getJobStore() { 634 return mJobs; 635 } 636 637 @Override 638 public void onStartUser(int userHandle) { 639 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle); 640 // Let's kick any outstanding jobs for this user. 641 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 642 } 643 644 @Override 645 public void onUnlockUser(int userHandle) { 646 // Let's kick any outstanding jobs for this user. 647 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 648 } 649 650 @Override 651 public void onStopUser(int userHandle) { 652 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle); 653 } 654 655 public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName, 656 int userId, String tag) { 657 try { 658 if (ActivityManager.getService().isAppStartModeDisabled(uId, 659 job.getService().getPackageName())) { 660 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString() 661 + " -- package not allowed to start"); 662 return JobScheduler.RESULT_FAILURE; 663 } 664 } catch (RemoteException e) { 665 } 666 synchronized (mLock) { 667 final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId()); 668 669 if (work != null && toCancel != null) { 670 // Fast path: we are adding work to an existing job, and the JobInfo is not 671 // changing. We can just directly enqueue this work in to the job. 672 if (toCancel.getJob().equals(job)) { 673 toCancel.enqueueWorkLocked(ActivityManager.getService(), work); 674 return JobScheduler.RESULT_SUCCESS; 675 } 676 } 677 678 JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag); 679 if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString()); 680 // Jobs on behalf of others don't apply to the per-app job cap 681 if (ENFORCE_MAX_JOBS && packageName == null) { 682 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) { 683 Slog.w(TAG, "Too many jobs for uid " + uId); 684 throw new IllegalStateException("Apps may not schedule more than " 685 + MAX_JOBS_PER_APP + " distinct jobs"); 686 } 687 } 688 689 // This may throw a SecurityException. 690 jobStatus.prepareLocked(ActivityManager.getService()); 691 692 if (toCancel != null) { 693 cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app"); 694 } 695 if (work != null) { 696 // If work has been supplied, enqueue it into the new job. 697 jobStatus.enqueueWorkLocked(ActivityManager.getService(), work); 698 } 699 startTrackingJobLocked(jobStatus, toCancel); 700 701 // If the job is immediately ready to run, then we can just immediately 702 // put it in the pending list and try to schedule it. This is especially 703 // important for jobs with a 0 deadline constraint, since they will happen a fair 704 // amount, we want to handle them as quickly as possible, and semantically we want to 705 // make sure we have started holding the wake lock for the job before returning to 706 // the caller. 707 // If the job is not yet ready to run, there is nothing more to do -- we are 708 // now just waiting for one of its controllers to change state and schedule 709 // the job appropriately. 710 if (isReadyToBeExecutedLocked(jobStatus)) { 711 // This is a new job, we can just immediately put it on the pending 712 // list and try to run it. 713 mJobPackageTracker.notePending(jobStatus); 714 addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator); 715 maybeRunPendingJobsLocked(); 716 } 717 } 718 return JobScheduler.RESULT_SUCCESS; 719 } 720 721 public List<JobInfo> getPendingJobs(int uid) { 722 synchronized (mLock) { 723 List<JobStatus> jobs = mJobs.getJobsByUid(uid); 724 ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size()); 725 for (int i = jobs.size() - 1; i >= 0; i--) { 726 JobStatus job = jobs.get(i); 727 outList.add(job.getJob()); 728 } 729 return outList; 730 } 731 } 732 733 public JobInfo getPendingJob(int uid, int jobId) { 734 synchronized (mLock) { 735 List<JobStatus> jobs = mJobs.getJobsByUid(uid); 736 for (int i = jobs.size() - 1; i >= 0; i--) { 737 JobStatus job = jobs.get(i); 738 if (job.getJobId() == jobId) { 739 return job.getJob(); 740 } 741 } 742 return null; 743 } 744 } 745 746 void cancelJobsForUser(int userHandle) { 747 synchronized (mLock) { 748 final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle); 749 for (int i=0; i<jobsForUser.size(); i++) { 750 JobStatus toRemove = jobsForUser.get(i); 751 cancelJobImplLocked(toRemove, null, "user removed"); 752 } 753 } 754 } 755 756 private void cancelJobsForNonExistentUsers() { 757 UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class); 758 synchronized (mLock) { 759 mJobs.removeJobsOfNonUsers(umi.getUserIds()); 760 } 761 } 762 763 void cancelJobsForPackageAndUid(String pkgName, int uid, String reason) { 764 if ("android".equals(pkgName)) { 765 Slog.wtfStack(TAG, "Can't cancel all jobs for system package"); 766 return; 767 } 768 synchronized (mLock) { 769 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid); 770 for (int i = jobsForUid.size() - 1; i >= 0; i--) { 771 final JobStatus job = jobsForUid.get(i); 772 if (job.getSourcePackageName().equals(pkgName)) { 773 cancelJobImplLocked(job, null, reason); 774 } 775 } 776 } 777 } 778 779 /** 780 * Entry point from client to cancel all jobs originating from their uid. 781 * This will remove the job from the master list, and cancel the job if it was staged for 782 * execution or being executed. 783 * @param uid Uid to check against for removal of a job. 784 * 785 */ 786 public void cancelJobsForUid(int uid, String reason) { 787 if (uid == Process.SYSTEM_UID) { 788 Slog.wtfStack(TAG, "Can't cancel all jobs for system uid"); 789 return; 790 } 791 synchronized (mLock) { 792 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid); 793 for (int i=0; i<jobsForUid.size(); i++) { 794 JobStatus toRemove = jobsForUid.get(i); 795 cancelJobImplLocked(toRemove, null, reason); 796 } 797 } 798 } 799 800 /** 801 * Entry point from client to cancel the job corresponding to the jobId provided. 802 * This will remove the job from the master list, and cancel the job if it was staged for 803 * execution or being executed. 804 * @param uid Uid of the calling client. 805 * @param jobId Id of the job, provided at schedule-time. 806 */ 807 public void cancelJob(int uid, int jobId) { 808 JobStatus toCancel; 809 synchronized (mLock) { 810 toCancel = mJobs.getJobByUidAndJobId(uid, jobId); 811 if (toCancel != null) { 812 cancelJobImplLocked(toCancel, null, "cancel() called by app"); 813 } 814 } 815 } 816 817 private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) { 818 if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString()); 819 cancelled.unprepareLocked(ActivityManager.getService()); 820 stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */); 821 // Remove from pending queue. 822 if (mPendingJobs.remove(cancelled)) { 823 mJobPackageTracker.noteNonpending(cancelled); 824 } 825 // Cancel if running. 826 stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason); 827 reportActiveLocked(); 828 } 829 830 void updateUidState(int uid, int procState) { 831 synchronized (mLock) { 832 if (procState == ActivityManager.PROCESS_STATE_TOP) { 833 // Only use this if we are exactly the top app. All others can live 834 // with just the foreground priority. This means that persistent processes 835 // can never be the top app priority... that is fine. 836 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP); 837 } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { 838 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP); 839 } else { 840 mUidPriorityOverride.delete(uid); 841 } 842 } 843 } 844 845 @Override 846 public void onDeviceIdleStateChanged(boolean deviceIdle) { 847 synchronized (mLock) { 848 if (deviceIdle) { 849 // When becoming idle, make sure no jobs are actively running, 850 // except those using the idle exemption flag. 851 for (int i=0; i<mActiveServices.size(); i++) { 852 JobServiceContext jsc = mActiveServices.get(i); 853 final JobStatus executing = jsc.getRunningJobLocked(); 854 if (executing != null 855 && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) { 856 jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE, 857 "cancelled due to doze"); 858 } 859 } 860 } else { 861 // When coming out of idle, allow thing to start back up. 862 if (mReadyToRock) { 863 if (mLocalDeviceIdleController != null) { 864 if (!mReportedActive) { 865 mReportedActive = true; 866 mLocalDeviceIdleController.setJobsActive(true); 867 } 868 } 869 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 870 } 871 } 872 } 873 } 874 875 void reportActiveLocked() { 876 // active is true if pending queue contains jobs OR some job is running. 877 boolean active = mPendingJobs.size() > 0; 878 if (mPendingJobs.size() <= 0) { 879 for (int i=0; i<mActiveServices.size(); i++) { 880 final JobServiceContext jsc = mActiveServices.get(i); 881 final JobStatus job = jsc.getRunningJobLocked(); 882 if (job != null 883 && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0 884 && !job.dozeWhitelisted) { 885 // We will report active if we have a job running and it is not an exception 886 // due to being in the foreground or whitelisted. 887 active = true; 888 break; 889 } 890 } 891 } 892 893 if (mReportedActive != active) { 894 mReportedActive = active; 895 if (mLocalDeviceIdleController != null) { 896 mLocalDeviceIdleController.setJobsActive(active); 897 } 898 } 899 } 900 901 /** 902 * Initializes the system service. 903 * <p> 904 * Subclasses must define a single argument constructor that accepts the context 905 * and passes it to super. 906 * </p> 907 * 908 * @param context The system server context. 909 */ 910 public JobSchedulerService(Context context) { 911 super(context); 912 mHandler = new JobHandler(context.getMainLooper()); 913 mConstants = new Constants(mHandler); 914 mJobSchedulerStub = new JobSchedulerStub(); 915 mJobs = JobStore.initAndGet(this); 916 917 // Create the controllers. 918 mControllers = new ArrayList<StateController>(); 919 mControllers.add(ConnectivityController.get(this)); 920 mControllers.add(TimeController.get(this)); 921 mControllers.add(IdleController.get(this)); 922 mBatteryController = BatteryController.get(this); 923 mControllers.add(mBatteryController); 924 mStorageController = StorageController.get(this); 925 mControllers.add(mStorageController); 926 mControllers.add(AppIdleController.get(this)); 927 mControllers.add(ContentObserverController.get(this)); 928 mControllers.add(DeviceIdleJobsController.get(this)); 929 930 // If the job store determined that it can't yet reschedule persisted jobs, 931 // we need to start watching the clock. 932 if (!mJobs.jobTimesInflatedValid()) { 933 Slog.w(TAG, "!!! RTC not yet good; tracking time updates for job scheduling"); 934 context.registerReceiver(mTimeSetReceiver, new IntentFilter(Intent.ACTION_TIME_CHANGED)); 935 } 936 } 937 938 private final BroadcastReceiver mTimeSetReceiver = new BroadcastReceiver() { 939 @Override 940 public void onReceive(Context context, Intent intent) { 941 if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) { 942 // When we reach clock sanity, recalculate the temporal windows 943 // of all affected jobs. 944 if (mJobs.clockNowValidToInflate(System.currentTimeMillis())) { 945 Slog.i(TAG, "RTC now valid; recalculating persisted job windows"); 946 947 // We've done our job now, so stop watching the time. 948 context.unregisterReceiver(this); 949 950 // And kick off the work to update the affected jobs, using a secondary 951 // thread instead of chugging away here on the main looper thread. 952 FgThread.getHandler().post(mJobTimeUpdater); 953 } 954 } 955 } 956 }; 957 958 private final Runnable mJobTimeUpdater = () -> { 959 final ArrayList<JobStatus> toRemove = new ArrayList<>(); 960 final ArrayList<JobStatus> toAdd = new ArrayList<>(); 961 synchronized (mLock) { 962 // Note: we intentionally both look up the existing affected jobs and replace them 963 // with recalculated ones inside the same lock lifetime. 964 getJobStore().getRtcCorrectedJobsLocked(toAdd, toRemove); 965 966 // Now, at each position [i], we have both the existing JobStatus 967 // and the one that replaces it. 968 final int N = toAdd.size(); 969 for (int i = 0; i < N; i++) { 970 final JobStatus oldJob = toRemove.get(i); 971 final JobStatus newJob = toAdd.get(i); 972 if (DEBUG) { 973 Slog.v(TAG, " replacing " + oldJob + " with " + newJob); 974 } 975 cancelJobImplLocked(oldJob, newJob, "deferred rtc calculation"); 976 } 977 } 978 }; 979 980 @Override 981 public void onStart() { 982 publishLocalService(JobSchedulerInternal.class, new LocalService()); 983 publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub); 984 } 985 986 @Override 987 public void onBootPhase(int phase) { 988 if (PHASE_SYSTEM_SERVICES_READY == phase) { 989 mConstants.start(getContext().getContentResolver()); 990 // Register br for package removals and user removals. 991 final IntentFilter filter = new IntentFilter(); 992 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 993 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 994 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 995 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); 996 filter.addDataScheme("package"); 997 getContext().registerReceiverAsUser( 998 mBroadcastReceiver, UserHandle.ALL, filter, null, null); 999 final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED); 1000 getContext().registerReceiverAsUser( 1001 mBroadcastReceiver, UserHandle.ALL, userFilter, null, null); 1002 mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE); 1003 try { 1004 ActivityManager.getService().registerUidObserver(mUidObserver, 1005 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE 1006 | ActivityManager.UID_OBSERVER_IDLE, ActivityManager.PROCESS_STATE_UNKNOWN, 1007 null); 1008 } catch (RemoteException e) { 1009 // ignored; both services live in system_server 1010 } 1011 // Remove any jobs that are not associated with any of the current users. 1012 cancelJobsForNonExistentUsers(); 1013 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { 1014 synchronized (mLock) { 1015 // Let's go! 1016 mReadyToRock = true; 1017 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService( 1018 BatteryStats.SERVICE_NAME)); 1019 mLocalDeviceIdleController 1020 = LocalServices.getService(DeviceIdleController.LocalService.class); 1021 // Create the "runners". 1022 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) { 1023 mActiveServices.add( 1024 new JobServiceContext(this, mBatteryStats, mJobPackageTracker, 1025 getContext().getMainLooper())); 1026 } 1027 // Attach jobs to their controllers. 1028 mJobs.forEachJob(new JobStatusFunctor() { 1029 @Override 1030 public void process(JobStatus job) { 1031 for (int controller = 0; controller < mControllers.size(); controller++) { 1032 final StateController sc = mControllers.get(controller); 1033 sc.maybeStartTrackingJobLocked(job, null); 1034 } 1035 } 1036 }); 1037 // GO GO GO! 1038 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 1039 } 1040 } 1041 } 1042 1043 /** 1044 * Called when we have a job status object that we need to insert in our 1045 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know 1046 * about. 1047 */ 1048 private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { 1049 if (!jobStatus.isPreparedLocked()) { 1050 Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus); 1051 } 1052 jobStatus.enqueueTime = SystemClock.elapsedRealtime(); 1053 final boolean update = mJobs.add(jobStatus); 1054 if (mReadyToRock) { 1055 for (int i = 0; i < mControllers.size(); i++) { 1056 StateController controller = mControllers.get(i); 1057 if (update) { 1058 controller.maybeStopTrackingJobLocked(jobStatus, null, true); 1059 } 1060 controller.maybeStartTrackingJobLocked(jobStatus, lastJob); 1061 } 1062 } 1063 } 1064 1065 /** 1066 * Called when we want to remove a JobStatus object that we've finished executing. Returns the 1067 * object removed. 1068 */ 1069 private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, 1070 boolean writeBack) { 1071 // Deal with any remaining work items in the old job. 1072 jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob); 1073 1074 // Remove from store as well as controllers. 1075 final boolean removed = mJobs.remove(jobStatus, writeBack); 1076 if (removed && mReadyToRock) { 1077 for (int i=0; i<mControllers.size(); i++) { 1078 StateController controller = mControllers.get(i); 1079 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false); 1080 } 1081 } 1082 return removed; 1083 } 1084 1085 private boolean stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason) { 1086 for (int i=0; i<mActiveServices.size(); i++) { 1087 JobServiceContext jsc = mActiveServices.get(i); 1088 final JobStatus executing = jsc.getRunningJobLocked(); 1089 if (executing != null && executing.matches(job.getUid(), job.getJobId())) { 1090 jsc.cancelExecutingJobLocked(reason, debugReason); 1091 return true; 1092 } 1093 } 1094 return false; 1095 } 1096 1097 /** 1098 * @param job JobStatus we are querying against. 1099 * @return Whether or not the job represented by the status object is currently being run or 1100 * is pending. 1101 */ 1102 private boolean isCurrentlyActiveLocked(JobStatus job) { 1103 for (int i=0; i<mActiveServices.size(); i++) { 1104 JobServiceContext serviceContext = mActiveServices.get(i); 1105 final JobStatus running = serviceContext.getRunningJobLocked(); 1106 if (running != null && running.matches(job.getUid(), job.getJobId())) { 1107 return true; 1108 } 1109 } 1110 return false; 1111 } 1112 1113 void noteJobsPending(List<JobStatus> jobs) { 1114 for (int i = jobs.size() - 1; i >= 0; i--) { 1115 JobStatus job = jobs.get(i); 1116 mJobPackageTracker.notePending(job); 1117 } 1118 } 1119 1120 void noteJobsNonpending(List<JobStatus> jobs) { 1121 for (int i = jobs.size() - 1; i >= 0; i--) { 1122 JobStatus job = jobs.get(i); 1123 mJobPackageTracker.noteNonpending(job); 1124 } 1125 } 1126 1127 /** 1128 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to 1129 * specify an override deadline on a failed job (the failed job will run even though it's not 1130 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any 1131 * ready job with {@link JobStatus#numFailures} > 0 will be executed. 1132 * 1133 * @param failureToReschedule Provided job status that we will reschedule. 1134 * @return A newly instantiated JobStatus with the same constraints as the last job except 1135 * with adjusted timing constraints. 1136 * 1137 * @see #maybeQueueReadyJobsForExecutionLocked 1138 */ 1139 private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) { 1140 final long elapsedNowMillis = SystemClock.elapsedRealtime(); 1141 final JobInfo job = failureToReschedule.getJob(); 1142 1143 final long initialBackoffMillis = job.getInitialBackoffMillis(); 1144 final int backoffAttempts = failureToReschedule.getNumFailures() + 1; 1145 long delayMillis; 1146 1147 if (failureToReschedule.hasWorkLocked()) { 1148 if (backoffAttempts > mConstants.MAX_WORK_RESCHEDULE_COUNT) { 1149 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #" 1150 + backoffAttempts + " > work limit " 1151 + mConstants.MAX_STANDARD_RESCHEDULE_COUNT); 1152 return null; 1153 } 1154 } else if (backoffAttempts > mConstants.MAX_STANDARD_RESCHEDULE_COUNT) { 1155 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #" 1156 + backoffAttempts + " > std limit " + mConstants.MAX_STANDARD_RESCHEDULE_COUNT); 1157 return null; 1158 } 1159 1160 switch (job.getBackoffPolicy()) { 1161 case JobInfo.BACKOFF_POLICY_LINEAR: { 1162 long backoff = initialBackoffMillis; 1163 if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME) { 1164 backoff = mConstants.MIN_LINEAR_BACKOFF_TIME; 1165 } 1166 delayMillis = backoff * backoffAttempts; 1167 } break; 1168 default: 1169 if (DEBUG) { 1170 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential."); 1171 } 1172 case JobInfo.BACKOFF_POLICY_EXPONENTIAL: { 1173 long backoff = initialBackoffMillis; 1174 if (backoff < mConstants.MIN_EXP_BACKOFF_TIME) { 1175 backoff = mConstants.MIN_EXP_BACKOFF_TIME; 1176 } 1177 delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1); 1178 } break; 1179 } 1180 delayMillis = 1181 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS); 1182 JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis, 1183 JobStatus.NO_LATEST_RUNTIME, backoffAttempts, 1184 failureToReschedule.getLastSuccessfulRunTime(), System.currentTimeMillis()); 1185 for (int ic=0; ic<mControllers.size(); ic++) { 1186 StateController controller = mControllers.get(ic); 1187 controller.rescheduleForFailureLocked(newJob, failureToReschedule); 1188 } 1189 return newJob; 1190 } 1191 1192 /** 1193 * Called after a periodic has executed so we can reschedule it. We take the last execution 1194 * time of the job to be the time of completion (i.e. the time at which this function is 1195 * called). 1196 * This could be inaccurate b/c the job can run for as long as 1197 * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead 1198 * to underscheduling at least, rather than if we had taken the last execution time to be the 1199 * start of the execution. 1200 * @return A new job representing the execution criteria for this instantiation of the 1201 * recurring job. 1202 */ 1203 private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) { 1204 final long elapsedNow = SystemClock.elapsedRealtime(); 1205 // Compute how much of the period is remaining. 1206 long runEarly = 0L; 1207 1208 // If this periodic was rescheduled it won't have a deadline. 1209 if (periodicToReschedule.hasDeadlineConstraint()) { 1210 runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L); 1211 } 1212 long flex = periodicToReschedule.getJob().getFlexMillis(); 1213 long period = periodicToReschedule.getJob().getIntervalMillis(); 1214 long newLatestRuntimeElapsed = elapsedNow + runEarly + period; 1215 long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex; 1216 1217 if (DEBUG) { 1218 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" + 1219 newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s"); 1220 } 1221 return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed, 1222 newLatestRuntimeElapsed, 0 /* backoffAttempt */, 1223 System.currentTimeMillis() /* lastSuccessfulRunTime */, 1224 periodicToReschedule.getLastFailedRunTime()); 1225 } 1226 1227 // JobCompletedListener implementations. 1228 1229 /** 1230 * A job just finished executing. We fetch the 1231 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on 1232 * whether we want to reschedule we readd it to the controllers. 1233 * @param jobStatus Completed job. 1234 * @param needsReschedule Whether the implementing class should reschedule this job. 1235 */ 1236 @Override 1237 public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) { 1238 if (DEBUG) { 1239 Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule); 1240 } 1241 1242 // If the job wants to be rescheduled, we first need to make the next upcoming 1243 // job so we can transfer any appropriate state over from the previous job when 1244 // we stop it. 1245 final JobStatus rescheduledJob = needsReschedule 1246 ? getRescheduleJobForFailureLocked(jobStatus) : null; 1247 1248 // Do not write back immediately if this is a periodic job. The job may get lost if system 1249 // shuts down before it is added back. 1250 if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) { 1251 if (DEBUG) { 1252 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?"); 1253 } 1254 // We still want to check for jobs to execute, because this job may have 1255 // scheduled a new job under the same job id, and now we can run it. 1256 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget(); 1257 return; 1258 } 1259 1260 if (rescheduledJob != null) { 1261 try { 1262 rescheduledJob.prepareLocked(ActivityManager.getService()); 1263 } catch (SecurityException e) { 1264 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob); 1265 } 1266 startTrackingJobLocked(rescheduledJob, jobStatus); 1267 } else if (jobStatus.getJob().isPeriodic()) { 1268 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus); 1269 try { 1270 rescheduledPeriodic.prepareLocked(ActivityManager.getService()); 1271 } catch (SecurityException e) { 1272 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic); 1273 } 1274 startTrackingJobLocked(rescheduledPeriodic, jobStatus); 1275 } 1276 jobStatus.unprepareLocked(ActivityManager.getService()); 1277 reportActiveLocked(); 1278 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget(); 1279 } 1280 1281 // StateChangedListener implementations. 1282 1283 /** 1284 * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that 1285 * some controller's state has changed, so as to run through the list of jobs and start/stop 1286 * any that are eligible. 1287 */ 1288 @Override 1289 public void onControllerStateChanged() { 1290 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 1291 } 1292 1293 @Override 1294 public void onRunJobNow(JobStatus jobStatus) { 1295 mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget(); 1296 } 1297 1298 final private class JobHandler extends Handler { 1299 1300 public JobHandler(Looper looper) { 1301 super(looper); 1302 } 1303 1304 @Override 1305 public void handleMessage(Message message) { 1306 synchronized (mLock) { 1307 if (!mReadyToRock) { 1308 return; 1309 } 1310 switch (message.what) { 1311 case MSG_JOB_EXPIRED: { 1312 JobStatus runNow = (JobStatus) message.obj; 1313 // runNow can be null, which is a controller's way of indicating that its 1314 // state is such that all ready jobs should be run immediately. 1315 if (runNow != null && isReadyToBeExecutedLocked(runNow)) { 1316 mJobPackageTracker.notePending(runNow); 1317 addOrderedItem(mPendingJobs, runNow, mEnqueueTimeComparator); 1318 } else { 1319 queueReadyJobsForExecutionLocked(); 1320 } 1321 } break; 1322 case MSG_CHECK_JOB: 1323 if (mReportedActive) { 1324 // if jobs are currently being run, queue all ready jobs for execution. 1325 queueReadyJobsForExecutionLocked(); 1326 } else { 1327 // Check the list of jobs and run some of them if we feel inclined. 1328 maybeQueueReadyJobsForExecutionLocked(); 1329 } 1330 break; 1331 case MSG_CHECK_JOB_GREEDY: 1332 queueReadyJobsForExecutionLocked(); 1333 break; 1334 case MSG_STOP_JOB: 1335 cancelJobImplLocked((JobStatus) message.obj, null, 1336 "app no longer allowed to run"); 1337 break; 1338 } 1339 maybeRunPendingJobsLocked(); 1340 // Don't remove JOB_EXPIRED in case one came along while processing the queue. 1341 removeMessages(MSG_CHECK_JOB); 1342 } 1343 } 1344 } 1345 1346 private void stopNonReadyActiveJobsLocked() { 1347 for (int i=0; i<mActiveServices.size(); i++) { 1348 JobServiceContext serviceContext = mActiveServices.get(i); 1349 final JobStatus running = serviceContext.getRunningJobLocked(); 1350 if (running != null && !running.isReady()) { 1351 serviceContext.cancelExecutingJobLocked( 1352 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED, 1353 "cancelled due to unsatisfied constraints"); 1354 } 1355 } 1356 } 1357 1358 /** 1359 * Run through list of jobs and execute all possible - at least one is expired so we do 1360 * as many as we can. 1361 */ 1362 private void queueReadyJobsForExecutionLocked() { 1363 if (DEBUG) { 1364 Slog.d(TAG, "queuing all ready jobs for execution:"); 1365 } 1366 noteJobsNonpending(mPendingJobs); 1367 mPendingJobs.clear(); 1368 stopNonReadyActiveJobsLocked(); 1369 mJobs.forEachJob(mReadyQueueFunctor); 1370 mReadyQueueFunctor.postProcess(); 1371 1372 if (DEBUG) { 1373 final int queuedJobs = mPendingJobs.size(); 1374 if (queuedJobs == 0) { 1375 Slog.d(TAG, "No jobs pending."); 1376 } else { 1377 Slog.d(TAG, queuedJobs + " jobs queued."); 1378 } 1379 } 1380 } 1381 1382 final class ReadyJobQueueFunctor implements JobStatusFunctor { 1383 ArrayList<JobStatus> newReadyJobs; 1384 1385 @Override 1386 public void process(JobStatus job) { 1387 if (isReadyToBeExecutedLocked(job)) { 1388 if (DEBUG) { 1389 Slog.d(TAG, " queued " + job.toShortString()); 1390 } 1391 if (newReadyJobs == null) { 1392 newReadyJobs = new ArrayList<JobStatus>(); 1393 } 1394 newReadyJobs.add(job); 1395 } 1396 } 1397 1398 public void postProcess() { 1399 if (newReadyJobs != null) { 1400 noteJobsPending(newReadyJobs); 1401 mPendingJobs.addAll(newReadyJobs); 1402 if (mPendingJobs.size() > 1) { 1403 mPendingJobs.sort(mEnqueueTimeComparator); 1404 } 1405 } 1406 newReadyJobs = null; 1407 } 1408 } 1409 private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor(); 1410 1411 /** 1412 * The state of at least one job has changed. Here is where we could enforce various 1413 * policies on when we want to execute jobs. 1414 * Right now the policy is such: 1415 * If >1 of the ready jobs is idle mode we send all of them off 1416 * if more than 2 network connectivity jobs are ready we send them all off. 1417 * If more than 4 jobs total are ready we send them all off. 1418 * TODO: It would be nice to consolidate these sort of high-level policies somewhere. 1419 */ 1420 final class MaybeReadyJobQueueFunctor implements JobStatusFunctor { 1421 int chargingCount; 1422 int batteryNotLowCount; 1423 int storageNotLowCount; 1424 int idleCount; 1425 int backoffCount; 1426 int connectivityCount; 1427 int contentCount; 1428 List<JobStatus> runnableJobs; 1429 1430 public MaybeReadyJobQueueFunctor() { 1431 reset(); 1432 } 1433 1434 // Functor method invoked for each job via JobStore.forEachJob() 1435 @Override 1436 public void process(JobStatus job) { 1437 if (isReadyToBeExecutedLocked(job)) { 1438 try { 1439 if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(), 1440 job.getJob().getService().getPackageName())) { 1441 Slog.w(TAG, "Aborting job " + job.getUid() + ":" 1442 + job.getJob().toString() + " -- package not allowed to start"); 1443 mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget(); 1444 return; 1445 } 1446 } catch (RemoteException e) { 1447 } 1448 if (job.getNumFailures() > 0) { 1449 backoffCount++; 1450 } 1451 if (job.hasIdleConstraint()) { 1452 idleCount++; 1453 } 1454 if (job.hasConnectivityConstraint()) { 1455 connectivityCount++; 1456 } 1457 if (job.hasChargingConstraint()) { 1458 chargingCount++; 1459 } 1460 if (job.hasBatteryNotLowConstraint()) { 1461 batteryNotLowCount++; 1462 } 1463 if (job.hasStorageNotLowConstraint()) { 1464 storageNotLowCount++; 1465 } 1466 if (job.hasContentTriggerConstraint()) { 1467 contentCount++; 1468 } 1469 if (runnableJobs == null) { 1470 runnableJobs = new ArrayList<>(); 1471 } 1472 runnableJobs.add(job); 1473 } 1474 } 1475 1476 public void postProcess() { 1477 if (backoffCount > 0 || 1478 idleCount >= mConstants.MIN_IDLE_COUNT || 1479 connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT || 1480 chargingCount >= mConstants.MIN_CHARGING_COUNT || 1481 batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT || 1482 storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT || 1483 contentCount >= mConstants.MIN_CONTENT_COUNT || 1484 (runnableJobs != null 1485 && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) { 1486 if (DEBUG) { 1487 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs."); 1488 } 1489 noteJobsPending(runnableJobs); 1490 mPendingJobs.addAll(runnableJobs); 1491 if (mPendingJobs.size() > 1) { 1492 mPendingJobs.sort(mEnqueueTimeComparator); 1493 } 1494 } else { 1495 if (DEBUG) { 1496 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything."); 1497 } 1498 } 1499 1500 // Be ready for next time 1501 reset(); 1502 } 1503 1504 private void reset() { 1505 chargingCount = 0; 1506 idleCount = 0; 1507 backoffCount = 0; 1508 connectivityCount = 0; 1509 batteryNotLowCount = 0; 1510 storageNotLowCount = 0; 1511 contentCount = 0; 1512 runnableJobs = null; 1513 } 1514 } 1515 private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor(); 1516 1517 private void maybeQueueReadyJobsForExecutionLocked() { 1518 if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs..."); 1519 1520 noteJobsNonpending(mPendingJobs); 1521 mPendingJobs.clear(); 1522 stopNonReadyActiveJobsLocked(); 1523 mJobs.forEachJob(mMaybeQueueFunctor); 1524 mMaybeQueueFunctor.postProcess(); 1525 } 1526 1527 /** 1528 * Criteria for moving a job into the pending queue: 1529 * - It's ready. 1530 * - It's not pending. 1531 * - It's not already running on a JSC. 1532 * - The user that requested the job is running. 1533 * - The component is enabled and runnable. 1534 */ 1535 private boolean isReadyToBeExecutedLocked(JobStatus job) { 1536 final boolean jobReady = job.isReady(); 1537 1538 if (DEBUG) { 1539 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString() 1540 + " ready=" + jobReady); 1541 } 1542 1543 // This is a condition that is very likely to be false (most jobs that are 1544 // scheduled are sitting there, not ready yet) and very cheap to check (just 1545 // a few conditions on data in JobStatus). 1546 if (!jobReady) { 1547 return false; 1548 } 1549 1550 final boolean jobExists = mJobs.containsJob(job); 1551 1552 final int userId = job.getUserId(); 1553 final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId); 1554 1555 if (DEBUG) { 1556 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString() 1557 + " exists=" + jobExists + " userStarted=" + userStarted); 1558 } 1559 1560 // These are also fairly cheap to check, though they typically will not 1561 // be conditions we fail. 1562 if (!jobExists || !userStarted) { 1563 return false; 1564 } 1565 1566 final boolean jobPending = mPendingJobs.contains(job); 1567 final boolean jobActive = isCurrentlyActiveLocked(job); 1568 1569 if (DEBUG) { 1570 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString() 1571 + " pending=" + jobPending + " active=" + jobActive); 1572 } 1573 1574 // These can be a little more expensive (especially jobActive, since we need to 1575 // go through the array of all potentially active jobs), so we are doing them 1576 // later... but still before checking with the package manager! 1577 if (jobPending || jobActive) { 1578 return false; 1579 } 1580 1581 final boolean componentPresent; 1582 try { 1583 componentPresent = (AppGlobals.getPackageManager().getServiceInfo( 1584 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING, 1585 userId) != null); 1586 } catch (RemoteException e) { 1587 throw e.rethrowAsRuntimeException(); 1588 } 1589 1590 if (DEBUG) { 1591 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString() 1592 + " componentPresent=" + componentPresent); 1593 } 1594 1595 // Everything else checked out so far, so this is the final yes/no check 1596 return componentPresent; 1597 } 1598 1599 /** 1600 * Reconcile jobs in the pending queue against available execution contexts. 1601 * A controller can force a job into the pending queue even if it's already running, but 1602 * here is where we decide whether to actually execute it. 1603 */ 1604 private void maybeRunPendingJobsLocked() { 1605 if (DEBUG) { 1606 Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs."); 1607 } 1608 assignJobsToContextsLocked(); 1609 reportActiveLocked(); 1610 } 1611 1612 private int adjustJobPriority(int curPriority, JobStatus job) { 1613 if (curPriority < JobInfo.PRIORITY_TOP_APP) { 1614 float factor = mJobPackageTracker.getLoadFactor(job); 1615 if (factor >= mConstants.HEAVY_USE_FACTOR) { 1616 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING; 1617 } else if (factor >= mConstants.MODERATE_USE_FACTOR) { 1618 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING; 1619 } 1620 } 1621 return curPriority; 1622 } 1623 1624 private int evaluateJobPriorityLocked(JobStatus job) { 1625 int priority = job.getPriority(); 1626 if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) { 1627 return adjustJobPriority(priority, job); 1628 } 1629 int override = mUidPriorityOverride.get(job.getSourceUid(), 0); 1630 if (override != 0) { 1631 return adjustJobPriority(override, job); 1632 } 1633 return adjustJobPriority(priority, job); 1634 } 1635 1636 /** 1637 * Takes jobs from pending queue and runs them on available contexts. 1638 * If no contexts are available, preempts lower priority jobs to 1639 * run higher priority ones. 1640 * Lock on mJobs before calling this function. 1641 */ 1642 private void assignJobsToContextsLocked() { 1643 if (DEBUG) { 1644 Slog.d(TAG, printPendingQueue()); 1645 } 1646 1647 int memLevel; 1648 try { 1649 memLevel = ActivityManager.getService().getMemoryTrimLevel(); 1650 } catch (RemoteException e) { 1651 memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL; 1652 } 1653 switch (memLevel) { 1654 case ProcessStats.ADJ_MEM_FACTOR_MODERATE: 1655 mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT; 1656 break; 1657 case ProcessStats.ADJ_MEM_FACTOR_LOW: 1658 mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT; 1659 break; 1660 case ProcessStats.ADJ_MEM_FACTOR_CRITICAL: 1661 mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT; 1662 break; 1663 default: 1664 mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT; 1665 break; 1666 } 1667 1668 JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap; 1669 boolean[] act = mTmpAssignAct; 1670 int[] preferredUidForContext = mTmpAssignPreferredUidForContext; 1671 int numActive = 0; 1672 int numForeground = 0; 1673 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { 1674 final JobServiceContext js = mActiveServices.get(i); 1675 final JobStatus status = js.getRunningJobLocked(); 1676 if ((contextIdToJobMap[i] = status) != null) { 1677 numActive++; 1678 if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) { 1679 numForeground++; 1680 } 1681 } 1682 act[i] = false; 1683 preferredUidForContext[i] = js.getPreferredUid(); 1684 } 1685 if (DEBUG) { 1686 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial")); 1687 } 1688 for (int i=0; i<mPendingJobs.size(); i++) { 1689 JobStatus nextPending = mPendingJobs.get(i); 1690 1691 // If job is already running, go to next job. 1692 int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap); 1693 if (jobRunningContext != -1) { 1694 continue; 1695 } 1696 1697 final int priority = evaluateJobPriorityLocked(nextPending); 1698 nextPending.lastEvaluatedPriority = priority; 1699 1700 // Find a context for nextPending. The context should be available OR 1701 // it should have lowest priority among all running jobs 1702 // (sharing the same Uid as nextPending) 1703 int minPriority = Integer.MAX_VALUE; 1704 int minPriorityContextId = -1; 1705 for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) { 1706 JobStatus job = contextIdToJobMap[j]; 1707 int preferredUid = preferredUidForContext[j]; 1708 if (job == null) { 1709 if ((numActive < mMaxActiveJobs || 1710 (priority >= JobInfo.PRIORITY_TOP_APP && 1711 numForeground < mConstants.FG_JOB_COUNT)) && 1712 (preferredUid == nextPending.getUid() || 1713 preferredUid == JobServiceContext.NO_PREFERRED_UID)) { 1714 // This slot is free, and we haven't yet hit the limit on 1715 // concurrent jobs... we can just throw the job in to here. 1716 minPriorityContextId = j; 1717 break; 1718 } 1719 // No job on this context, but nextPending can't run here because 1720 // the context has a preferred Uid or we have reached the limit on 1721 // concurrent jobs. 1722 continue; 1723 } 1724 if (job.getUid() != nextPending.getUid()) { 1725 continue; 1726 } 1727 if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) { 1728 continue; 1729 } 1730 if (minPriority > nextPending.lastEvaluatedPriority) { 1731 minPriority = nextPending.lastEvaluatedPriority; 1732 minPriorityContextId = j; 1733 } 1734 } 1735 if (minPriorityContextId != -1) { 1736 contextIdToJobMap[minPriorityContextId] = nextPending; 1737 act[minPriorityContextId] = true; 1738 numActive++; 1739 if (priority >= JobInfo.PRIORITY_TOP_APP) { 1740 numForeground++; 1741 } 1742 } 1743 } 1744 if (DEBUG) { 1745 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final")); 1746 } 1747 mJobPackageTracker.noteConcurrency(numActive, numForeground); 1748 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { 1749 boolean preservePreferredUid = false; 1750 if (act[i]) { 1751 JobStatus js = mActiveServices.get(i).getRunningJobLocked(); 1752 if (js != null) { 1753 if (DEBUG) { 1754 Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJobLocked()); 1755 } 1756 // preferredUid will be set to uid of currently running job. 1757 mActiveServices.get(i).preemptExecutingJobLocked(); 1758 preservePreferredUid = true; 1759 } else { 1760 final JobStatus pendingJob = contextIdToJobMap[i]; 1761 if (DEBUG) { 1762 Slog.d(TAG, "About to run job on context " 1763 + String.valueOf(i) + ", job: " + pendingJob); 1764 } 1765 for (int ic=0; ic<mControllers.size(); ic++) { 1766 mControllers.get(ic).prepareForExecutionLocked(pendingJob); 1767 } 1768 if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) { 1769 Slog.d(TAG, "Error executing " + pendingJob); 1770 } 1771 if (mPendingJobs.remove(pendingJob)) { 1772 mJobPackageTracker.noteNonpending(pendingJob); 1773 } 1774 } 1775 } 1776 if (!preservePreferredUid) { 1777 mActiveServices.get(i).clearPreferredUid(); 1778 } 1779 } 1780 } 1781 1782 int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) { 1783 for (int i=0; i<map.length; i++) { 1784 if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) { 1785 return i; 1786 } 1787 } 1788 return -1; 1789 } 1790 1791 final class LocalService implements JobSchedulerInternal { 1792 1793 /** 1794 * Returns a list of all pending jobs. A running job is not considered pending. Periodic 1795 * jobs are always considered pending. 1796 */ 1797 @Override 1798 public List<JobInfo> getSystemScheduledPendingJobs() { 1799 synchronized (mLock) { 1800 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>(); 1801 mJobs.forEachJob(Process.SYSTEM_UID, new JobStatusFunctor() { 1802 @Override 1803 public void process(JobStatus job) { 1804 if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) { 1805 pendingJobs.add(job.getJob()); 1806 } 1807 } 1808 }); 1809 return pendingJobs; 1810 } 1811 } 1812 1813 @Override 1814 public void addBackingUpUid(int uid) { 1815 synchronized (mLock) { 1816 // No need to actually do anything here, since for a full backup the 1817 // activity manager will kill the process which will kill the job (and 1818 // cause it to restart, but now it can't run). 1819 mBackingUpUids.put(uid, uid); 1820 } 1821 } 1822 1823 @Override 1824 public void removeBackingUpUid(int uid) { 1825 synchronized (mLock) { 1826 mBackingUpUids.delete(uid); 1827 // If there are any jobs for this uid, we need to rebuild the pending list 1828 // in case they are now ready to run. 1829 if (mJobs.countJobsForUid(uid) > 0) { 1830 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 1831 } 1832 } 1833 } 1834 1835 @Override 1836 public void clearAllBackingUpUids() { 1837 synchronized (mLock) { 1838 if (mBackingUpUids.size() > 0) { 1839 mBackingUpUids.clear(); 1840 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 1841 } 1842 } 1843 } 1844 1845 @Override 1846 public JobStorePersistStats getPersistStats() { 1847 synchronized (mLock) { 1848 return new JobStorePersistStats(mJobs.getPersistStats()); 1849 } 1850 } 1851 } 1852 1853 /** 1854 * Binder stub trampoline implementation 1855 */ 1856 final class JobSchedulerStub extends IJobScheduler.Stub { 1857 /** Cache determination of whether a given app can persist jobs 1858 * key is uid of the calling app; value is undetermined/true/false 1859 */ 1860 private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>(); 1861 1862 // Enforce that only the app itself (or shared uid participant) can schedule a 1863 // job that runs one of the app's services, as well as verifying that the 1864 // named service properly requires the BIND_JOB_SERVICE permission 1865 private void enforceValidJobRequest(int uid, JobInfo job) { 1866 final IPackageManager pm = AppGlobals.getPackageManager(); 1867 final ComponentName service = job.getService(); 1868 try { 1869 ServiceInfo si = pm.getServiceInfo(service, 1870 PackageManager.MATCH_DIRECT_BOOT_AWARE 1871 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 1872 UserHandle.getUserId(uid)); 1873 if (si == null) { 1874 throw new IllegalArgumentException("No such service " + service); 1875 } 1876 if (si.applicationInfo.uid != uid) { 1877 throw new IllegalArgumentException("uid " + uid + 1878 " cannot schedule job in " + service.getPackageName()); 1879 } 1880 if (!JobService.PERMISSION_BIND.equals(si.permission)) { 1881 throw new IllegalArgumentException("Scheduled service " + service 1882 + " does not require android.permission.BIND_JOB_SERVICE permission"); 1883 } 1884 } catch (RemoteException e) { 1885 // Can't happen; the Package Manager is in this same process 1886 } 1887 } 1888 1889 private boolean canPersistJobs(int pid, int uid) { 1890 // If we get this far we're good to go; all we need to do now is check 1891 // whether the app is allowed to persist its scheduled work. 1892 final boolean canPersist; 1893 synchronized (mPersistCache) { 1894 Boolean cached = mPersistCache.get(uid); 1895 if (cached != null) { 1896 canPersist = cached.booleanValue(); 1897 } else { 1898 // Persisting jobs is tantamount to running at boot, so we permit 1899 // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED 1900 // permission 1901 int result = getContext().checkPermission( 1902 android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid); 1903 canPersist = (result == PackageManager.PERMISSION_GRANTED); 1904 mPersistCache.put(uid, canPersist); 1905 } 1906 } 1907 return canPersist; 1908 } 1909 1910 // IJobScheduler implementation 1911 @Override 1912 public int schedule(JobInfo job) throws RemoteException { 1913 if (DEBUG) { 1914 Slog.d(TAG, "Scheduling job: " + job.toString()); 1915 } 1916 final int pid = Binder.getCallingPid(); 1917 final int uid = Binder.getCallingUid(); 1918 1919 enforceValidJobRequest(uid, job); 1920 if (job.isPersisted()) { 1921 if (!canPersistJobs(pid, uid)) { 1922 throw new IllegalArgumentException("Error: requested job be persisted without" 1923 + " holding RECEIVE_BOOT_COMPLETED permission."); 1924 } 1925 } 1926 1927 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) { 1928 getContext().enforceCallingOrSelfPermission( 1929 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG); 1930 } 1931 1932 long ident = Binder.clearCallingIdentity(); 1933 try { 1934 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, -1, null); 1935 } finally { 1936 Binder.restoreCallingIdentity(ident); 1937 } 1938 } 1939 1940 // IJobScheduler implementation 1941 @Override 1942 public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException { 1943 if (DEBUG) { 1944 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work); 1945 } 1946 final int pid = Binder.getCallingPid(); 1947 final int uid = Binder.getCallingUid(); 1948 1949 enforceValidJobRequest(uid, job); 1950 if (job.isPersisted()) { 1951 throw new IllegalArgumentException("Can't enqueue work for persisted jobs"); 1952 } 1953 if (work == null) { 1954 throw new NullPointerException("work is null"); 1955 } 1956 1957 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) { 1958 getContext().enforceCallingOrSelfPermission( 1959 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG); 1960 } 1961 1962 long ident = Binder.clearCallingIdentity(); 1963 try { 1964 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, -1, null); 1965 } finally { 1966 Binder.restoreCallingIdentity(ident); 1967 } 1968 } 1969 1970 @Override 1971 public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag) 1972 throws RemoteException { 1973 final int callerUid = Binder.getCallingUid(); 1974 if (DEBUG) { 1975 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString() 1976 + " on behalf of " + packageName); 1977 } 1978 1979 if (packageName == null) { 1980 throw new NullPointerException("Must specify a package for scheduleAsPackage()"); 1981 } 1982 1983 int mayScheduleForOthers = getContext().checkCallingOrSelfPermission( 1984 android.Manifest.permission.UPDATE_DEVICE_STATS); 1985 if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) { 1986 throw new SecurityException("Caller uid " + callerUid 1987 + " not permitted to schedule jobs for other apps"); 1988 } 1989 1990 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) { 1991 getContext().enforceCallingOrSelfPermission( 1992 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG); 1993 } 1994 1995 long ident = Binder.clearCallingIdentity(); 1996 try { 1997 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid, 1998 packageName, userId, tag); 1999 } finally { 2000 Binder.restoreCallingIdentity(ident); 2001 } 2002 } 2003 2004 @Override 2005 public List<JobInfo> getAllPendingJobs() throws RemoteException { 2006 final int uid = Binder.getCallingUid(); 2007 2008 long ident = Binder.clearCallingIdentity(); 2009 try { 2010 return JobSchedulerService.this.getPendingJobs(uid); 2011 } finally { 2012 Binder.restoreCallingIdentity(ident); 2013 } 2014 } 2015 2016 @Override 2017 public JobInfo getPendingJob(int jobId) throws RemoteException { 2018 final int uid = Binder.getCallingUid(); 2019 2020 long ident = Binder.clearCallingIdentity(); 2021 try { 2022 return JobSchedulerService.this.getPendingJob(uid, jobId); 2023 } finally { 2024 Binder.restoreCallingIdentity(ident); 2025 } 2026 } 2027 2028 @Override 2029 public void cancelAll() throws RemoteException { 2030 final int uid = Binder.getCallingUid(); 2031 long ident = Binder.clearCallingIdentity(); 2032 try { 2033 JobSchedulerService.this.cancelJobsForUid(uid, "cancelAll() called by app"); 2034 } finally { 2035 Binder.restoreCallingIdentity(ident); 2036 } 2037 } 2038 2039 @Override 2040 public void cancel(int jobId) throws RemoteException { 2041 final int uid = Binder.getCallingUid(); 2042 2043 long ident = Binder.clearCallingIdentity(); 2044 try { 2045 JobSchedulerService.this.cancelJob(uid, jobId); 2046 } finally { 2047 Binder.restoreCallingIdentity(ident); 2048 } 2049 } 2050 2051 /** 2052 * "dumpsys" infrastructure 2053 */ 2054 @Override 2055 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2056 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return; 2057 2058 long identityToken = Binder.clearCallingIdentity(); 2059 try { 2060 JobSchedulerService.this.dumpInternal(pw, args); 2061 } finally { 2062 Binder.restoreCallingIdentity(identityToken); 2063 } 2064 } 2065 2066 @Override 2067 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 2068 String[] args, ShellCallback callback, ResultReceiver resultReceiver) { 2069 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec( 2070 this, in, out, err, args, callback, resultReceiver); 2071 } 2072 }; 2073 2074 // Shell command infrastructure: run the given job immediately 2075 int executeRunCommand(String pkgName, int userId, int jobId, boolean force) { 2076 if (DEBUG) { 2077 Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId 2078 + " " + jobId + " f=" + force); 2079 } 2080 2081 try { 2082 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, 2083 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM); 2084 if (uid < 0) { 2085 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE; 2086 } 2087 2088 synchronized (mLock) { 2089 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId); 2090 if (js == null) { 2091 return JobSchedulerShellCommand.CMD_ERR_NO_JOB; 2092 } 2093 2094 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT; 2095 if (!js.isConstraintsSatisfied()) { 2096 js.overrideState = 0; 2097 return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS; 2098 } 2099 2100 queueReadyJobsForExecutionLocked(); 2101 maybeRunPendingJobsLocked(); 2102 } 2103 } catch (RemoteException e) { 2104 // can't happen 2105 } 2106 return 0; 2107 } 2108 2109 // Shell command infrastructure: immediately timeout currently executing jobs 2110 int executeTimeoutCommand(PrintWriter pw, String pkgName, int userId, 2111 boolean hasJobId, int jobId) { 2112 if (DEBUG) { 2113 Slog.v(TAG, "executeTimeoutCommand(): " + pkgName + "/" + userId + " " + jobId); 2114 } 2115 2116 synchronized (mLock) { 2117 boolean foundSome = false; 2118 for (int i=0; i<mActiveServices.size(); i++) { 2119 final JobServiceContext jc = mActiveServices.get(i); 2120 final JobStatus js = jc.getRunningJobLocked(); 2121 if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId)) { 2122 foundSome = true; 2123 pw.print("Timing out: "); 2124 js.printUniqueId(pw); 2125 pw.print(" "); 2126 pw.println(js.getServiceComponent().flattenToShortString()); 2127 } 2128 } 2129 if (!foundSome) { 2130 pw.println("No matching executing jobs found."); 2131 } 2132 } 2133 return 0; 2134 } 2135 2136 void setMonitorBattery(boolean enabled) { 2137 synchronized (mLock) { 2138 if (mBatteryController != null) { 2139 mBatteryController.getTracker().setMonitorBatteryLocked(enabled); 2140 } 2141 } 2142 } 2143 2144 int getBatterySeq() { 2145 synchronized (mLock) { 2146 return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1; 2147 } 2148 } 2149 2150 boolean getBatteryCharging() { 2151 synchronized (mLock) { 2152 return mBatteryController != null 2153 ? mBatteryController.getTracker().isOnStablePower() : false; 2154 } 2155 } 2156 2157 boolean getBatteryNotLow() { 2158 synchronized (mLock) { 2159 return mBatteryController != null 2160 ? mBatteryController.getTracker().isBatteryNotLow() : false; 2161 } 2162 } 2163 2164 int getStorageSeq() { 2165 synchronized (mLock) { 2166 return mStorageController != null ? mStorageController.getTracker().getSeq() : -1; 2167 } 2168 } 2169 2170 boolean getStorageNotLow() { 2171 synchronized (mLock) { 2172 return mStorageController != null 2173 ? mStorageController.getTracker().isStorageNotLow() : false; 2174 } 2175 } 2176 2177 int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) { 2178 try { 2179 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, 2180 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM); 2181 if (uid < 0) { 2182 pw.print("unknown("); pw.print(pkgName); pw.println(")"); 2183 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE; 2184 } 2185 2186 synchronized (mLock) { 2187 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId); 2188 if (DEBUG) Slog.d(TAG, "get-job-state " + uid + "/" + jobId + ": " + js); 2189 if (js == null) { 2190 pw.print("unknown("); UserHandle.formatUid(pw, uid); 2191 pw.print("/jid"); pw.print(jobId); pw.println(")"); 2192 return JobSchedulerShellCommand.CMD_ERR_NO_JOB; 2193 } 2194 2195 boolean printed = false; 2196 if (mPendingJobs.contains(js)) { 2197 pw.print("pending"); 2198 printed = true; 2199 } 2200 if (isCurrentlyActiveLocked(js)) { 2201 if (printed) { 2202 pw.print(" "); 2203 } 2204 printed = true; 2205 pw.println("active"); 2206 } 2207 if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) { 2208 if (printed) { 2209 pw.print(" "); 2210 } 2211 printed = true; 2212 pw.println("user-stopped"); 2213 } 2214 if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) { 2215 if (printed) { 2216 pw.print(" "); 2217 } 2218 printed = true; 2219 pw.println("backing-up"); 2220 } 2221 boolean componentPresent = false; 2222 try { 2223 componentPresent = (AppGlobals.getPackageManager().getServiceInfo( 2224 js.getServiceComponent(), 2225 PackageManager.MATCH_DEBUG_TRIAGED_MISSING, 2226 js.getUserId()) != null); 2227 } catch (RemoteException e) { 2228 } 2229 if (!componentPresent) { 2230 if (printed) { 2231 pw.print(" "); 2232 } 2233 printed = true; 2234 pw.println("no-component"); 2235 } 2236 if (js.isReady()) { 2237 if (printed) { 2238 pw.print(" "); 2239 } 2240 printed = true; 2241 pw.println("ready"); 2242 } 2243 if (!printed) { 2244 pw.print("waiting"); 2245 } 2246 pw.println(); 2247 } 2248 } catch (RemoteException e) { 2249 // can't happen 2250 } 2251 return 0; 2252 } 2253 2254 private String printContextIdToJobMap(JobStatus[] map, String initial) { 2255 StringBuilder s = new StringBuilder(initial + ": "); 2256 for (int i=0; i<map.length; i++) { 2257 s.append("(") 2258 .append(map[i] == null? -1: map[i].getJobId()) 2259 .append(map[i] == null? -1: map[i].getUid()) 2260 .append(")" ); 2261 } 2262 return s.toString(); 2263 } 2264 2265 private String printPendingQueue() { 2266 StringBuilder s = new StringBuilder("Pending queue: "); 2267 Iterator<JobStatus> it = mPendingJobs.iterator(); 2268 while (it.hasNext()) { 2269 JobStatus js = it.next(); 2270 s.append("(") 2271 .append(js.getJob().getId()) 2272 .append(", ") 2273 .append(js.getUid()) 2274 .append(") "); 2275 } 2276 return s.toString(); 2277 } 2278 2279 static void dumpHelp(PrintWriter pw) { 2280 pw.println("Job Scheduler (jobscheduler) dump options:"); 2281 pw.println(" [-h] [package] ..."); 2282 pw.println(" -h: print this help"); 2283 pw.println(" [package] is an optional package name to limit the output to."); 2284 } 2285 2286 void dumpInternal(final PrintWriter pw, String[] args) { 2287 int filterUid = -1; 2288 if (!ArrayUtils.isEmpty(args)) { 2289 int opti = 0; 2290 while (opti < args.length) { 2291 String arg = args[opti]; 2292 if ("-h".equals(arg)) { 2293 dumpHelp(pw); 2294 return; 2295 } else if ("-a".equals(arg)) { 2296 // Ignore, we always dump all. 2297 } else if (arg.length() > 0 && arg.charAt(0) == '-') { 2298 pw.println("Unknown option: " + arg); 2299 return; 2300 } else { 2301 break; 2302 } 2303 opti++; 2304 } 2305 if (opti < args.length) { 2306 String pkg = args[opti]; 2307 try { 2308 filterUid = getContext().getPackageManager().getPackageUid(pkg, 2309 PackageManager.MATCH_ANY_USER); 2310 } catch (NameNotFoundException ignored) { 2311 pw.println("Invalid package: " + pkg); 2312 return; 2313 } 2314 } 2315 } 2316 2317 final int filterUidFinal = UserHandle.getAppId(filterUid); 2318 final long nowElapsed = SystemClock.elapsedRealtime(); 2319 final long nowUptime = SystemClock.uptimeMillis(); 2320 synchronized (mLock) { 2321 mConstants.dump(pw); 2322 pw.println(); 2323 pw.println("Started users: " + Arrays.toString(mStartedUsers)); 2324 pw.print("Registered "); 2325 pw.print(mJobs.size()); 2326 pw.println(" jobs:"); 2327 if (mJobs.size() > 0) { 2328 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs(); 2329 Collections.sort(jobs, new Comparator<JobStatus>() { 2330 @Override 2331 public int compare(JobStatus o1, JobStatus o2) { 2332 int uid1 = o1.getUid(); 2333 int uid2 = o2.getUid(); 2334 int id1 = o1.getJobId(); 2335 int id2 = o2.getJobId(); 2336 if (uid1 != uid2) { 2337 return uid1 < uid2 ? -1 : 1; 2338 } 2339 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0); 2340 } 2341 }); 2342 for (JobStatus job : jobs) { 2343 pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": "); 2344 pw.println(job.toShortStringExceptUniqueId()); 2345 2346 // Skip printing details if the caller requested a filter 2347 if (!job.shouldDump(filterUidFinal)) { 2348 continue; 2349 } 2350 2351 job.dump(pw, " ", true, nowElapsed); 2352 pw.print(" Ready: "); 2353 pw.print(isReadyToBeExecutedLocked(job)); 2354 pw.print(" (job="); 2355 pw.print(job.isReady()); 2356 pw.print(" user="); 2357 pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId())); 2358 pw.print(" !pending="); 2359 pw.print(!mPendingJobs.contains(job)); 2360 pw.print(" !active="); 2361 pw.print(!isCurrentlyActiveLocked(job)); 2362 pw.print(" !backingup="); 2363 pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0)); 2364 pw.print(" comp="); 2365 boolean componentPresent = false; 2366 try { 2367 componentPresent = (AppGlobals.getPackageManager().getServiceInfo( 2368 job.getServiceComponent(), 2369 PackageManager.MATCH_DEBUG_TRIAGED_MISSING, 2370 job.getUserId()) != null); 2371 } catch (RemoteException e) { 2372 } 2373 pw.print(componentPresent); 2374 pw.println(")"); 2375 } 2376 } else { 2377 pw.println(" None."); 2378 } 2379 for (int i=0; i<mControllers.size(); i++) { 2380 pw.println(); 2381 mControllers.get(i).dumpControllerStateLocked(pw, filterUidFinal); 2382 } 2383 pw.println(); 2384 pw.println("Uid priority overrides:"); 2385 for (int i=0; i< mUidPriorityOverride.size(); i++) { 2386 int uid = mUidPriorityOverride.keyAt(i); 2387 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) { 2388 pw.print(" "); pw.print(UserHandle.formatUid(uid)); 2389 pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i)); 2390 } 2391 } 2392 if (mBackingUpUids.size() > 0) { 2393 pw.println(); 2394 pw.println("Backing up uids:"); 2395 boolean first = true; 2396 for (int i = 0; i < mBackingUpUids.size(); i++) { 2397 int uid = mBackingUpUids.keyAt(i); 2398 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) { 2399 if (first) { 2400 pw.print(" "); 2401 first = false; 2402 } else { 2403 pw.print(", "); 2404 } 2405 pw.print(UserHandle.formatUid(uid)); 2406 } 2407 } 2408 pw.println(); 2409 } 2410 pw.println(); 2411 mJobPackageTracker.dump(pw, "", filterUidFinal); 2412 pw.println(); 2413 if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) { 2414 pw.println(); 2415 } 2416 pw.println("Pending queue:"); 2417 for (int i=0; i<mPendingJobs.size(); i++) { 2418 JobStatus job = mPendingJobs.get(i); 2419 pw.print(" Pending #"); pw.print(i); pw.print(": "); 2420 pw.println(job.toShortString()); 2421 job.dump(pw, " ", false, nowElapsed); 2422 int priority = evaluateJobPriorityLocked(job); 2423 if (priority != JobInfo.PRIORITY_DEFAULT) { 2424 pw.print(" Evaluated priority: "); pw.println(priority); 2425 } 2426 pw.print(" Tag: "); pw.println(job.getTag()); 2427 pw.print(" Enq: "); 2428 TimeUtils.formatDuration(job.madePending - nowUptime, pw); 2429 pw.println(); 2430 } 2431 pw.println(); 2432 pw.println("Active jobs:"); 2433 for (int i=0; i<mActiveServices.size(); i++) { 2434 JobServiceContext jsc = mActiveServices.get(i); 2435 pw.print(" Slot #"); pw.print(i); pw.print(": "); 2436 final JobStatus job = jsc.getRunningJobLocked(); 2437 if (job == null) { 2438 if (jsc.mStoppedReason != null) { 2439 pw.print("inactive since "); 2440 TimeUtils.formatDuration(jsc.mStoppedTime, nowElapsed, pw); 2441 pw.print(", stopped because: "); 2442 pw.println(jsc.mStoppedReason); 2443 } else { 2444 pw.println("inactive"); 2445 } 2446 continue; 2447 } else { 2448 pw.println(job.toShortString()); 2449 pw.print(" Running for: "); 2450 TimeUtils.formatDuration(nowElapsed - jsc.getExecutionStartTimeElapsed(), pw); 2451 pw.print(", timeout at: "); 2452 TimeUtils.formatDuration(jsc.getTimeoutElapsed() - nowElapsed, pw); 2453 pw.println(); 2454 job.dump(pw, " ", false, nowElapsed); 2455 int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked()); 2456 if (priority != JobInfo.PRIORITY_DEFAULT) { 2457 pw.print(" Evaluated priority: "); pw.println(priority); 2458 } 2459 pw.print(" Active at "); 2460 TimeUtils.formatDuration(job.madeActive - nowUptime, pw); 2461 pw.print(", pending for "); 2462 TimeUtils.formatDuration(job.madeActive - job.madePending, pw); 2463 pw.println(); 2464 } 2465 } 2466 if (filterUid == -1) { 2467 pw.println(); 2468 pw.print("mReadyToRock="); pw.println(mReadyToRock); 2469 pw.print("mReportedActive="); pw.println(mReportedActive); 2470 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs); 2471 } 2472 pw.println(); 2473 pw.print("PersistStats: "); 2474 pw.println(mJobs.getPersistStats()); 2475 } 2476 pw.println(); 2477 } 2478 } 2479