JobSchedulerService.java revision e9a988caca733d2f292991a52a0047685a69812f
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 17package com.android.server.job; 18 19import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; 20import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; 21 22import java.io.FileDescriptor; 23import java.io.PrintWriter; 24import java.util.ArrayList; 25import java.util.Arrays; 26import java.util.Collections; 27import java.util.Comparator; 28import java.util.Iterator; 29import java.util.List; 30 31import android.app.ActivityManager; 32import android.app.ActivityManagerNative; 33import android.app.AppGlobals; 34import android.app.IUidObserver; 35import android.app.job.JobInfo; 36import android.app.job.JobParameters; 37import android.app.job.JobScheduler; 38import android.app.job.JobService; 39import android.app.job.IJobScheduler; 40import android.content.BroadcastReceiver; 41import android.content.ComponentName; 42import android.content.ContentResolver; 43import android.content.Context; 44import android.content.Intent; 45import android.content.IntentFilter; 46import android.content.pm.IPackageManager; 47import android.content.pm.PackageManager; 48import android.content.pm.ServiceInfo; 49import android.content.pm.PackageManager.NameNotFoundException; 50import android.database.ContentObserver; 51import android.net.Uri; 52import android.os.BatteryStats; 53import android.os.Binder; 54import android.os.Handler; 55import android.os.Looper; 56import android.os.Message; 57import android.os.Process; 58import android.os.PowerManager; 59import android.os.RemoteException; 60import android.os.ResultReceiver; 61import android.os.ServiceManager; 62import android.os.SystemClock; 63import android.os.UserHandle; 64import android.provider.Settings; 65import android.util.KeyValueListParser; 66import android.util.Slog; 67import android.util.SparseArray; 68import android.util.SparseIntArray; 69import android.util.TimeUtils; 70 71import com.android.internal.app.IBatteryStats; 72import com.android.internal.app.procstats.ProcessStats; 73import com.android.internal.util.ArrayUtils; 74import com.android.server.DeviceIdleController; 75import com.android.server.LocalServices; 76import com.android.server.job.JobStore.JobStatusFunctor; 77import com.android.server.job.controllers.AppIdleController; 78import com.android.server.job.controllers.BatteryController; 79import com.android.server.job.controllers.ConnectivityController; 80import com.android.server.job.controllers.ContentObserverController; 81import com.android.server.job.controllers.DeviceIdleJobsController; 82import com.android.server.job.controllers.IdleController; 83import com.android.server.job.controllers.JobStatus; 84import com.android.server.job.controllers.StateController; 85import com.android.server.job.controllers.TimeController; 86 87import libcore.util.EmptyArray; 88 89/** 90 * Responsible for taking jobs representing work to be performed by a client app, and determining 91 * based on the criteria specified when that job should be run against the client application's 92 * endpoint. 93 * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing 94 * about constraints, or the state of active jobs. It receives callbacks from the various 95 * controllers and completed jobs and operates accordingly. 96 * 97 * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object. 98 * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}. 99 * @hide 100 */ 101public final class JobSchedulerService extends com.android.server.SystemService 102 implements StateChangedListener, JobCompletedListener { 103 static final String TAG = "JobSchedulerService"; 104 public static final boolean DEBUG = false; 105 106 /** The maximum number of concurrent jobs we run at one time. */ 107 private static final int MAX_JOB_CONTEXTS_COUNT = 16; 108 /** Enforce a per-app limit on scheduled jobs? */ 109 private static final boolean ENFORCE_MAX_JOBS = true; 110 /** The maximum number of jobs that we allow an unprivileged app to schedule */ 111 private static final int MAX_JOBS_PER_APP = 100; 112 113 114 /** Global local for all job scheduler state. */ 115 final Object mLock = new Object(); 116 /** Master list of jobs. */ 117 final JobStore mJobs; 118 /** Tracking amount of time each package runs for. */ 119 final JobPackageTracker mJobPackageTracker = new JobPackageTracker(); 120 121 static final int MSG_JOB_EXPIRED = 0; 122 static final int MSG_CHECK_JOB = 1; 123 static final int MSG_STOP_JOB = 2; 124 static final int MSG_CHECK_JOB_GREEDY = 3; 125 126 /** 127 * Track Services that have currently active or pending jobs. The index is provided by 128 * {@link JobStatus#getServiceToken()} 129 */ 130 final List<JobServiceContext> mActiveServices = new ArrayList<>(); 131 /** List of controllers that will notify this service of updates to jobs. */ 132 List<StateController> mControllers; 133 /** 134 * Queue of pending jobs. The JobServiceContext class will receive jobs from this list 135 * when ready to execute them. 136 */ 137 final ArrayList<JobStatus> mPendingJobs = new ArrayList<>(); 138 139 int[] mStartedUsers = EmptyArray.INT; 140 141 final JobHandler mHandler; 142 final JobSchedulerStub mJobSchedulerStub; 143 144 IBatteryStats mBatteryStats; 145 PowerManager mPowerManager; 146 DeviceIdleController.LocalService mLocalDeviceIdleController; 147 148 /** 149 * Set to true once we are allowed to run third party apps. 150 */ 151 boolean mReadyToRock; 152 153 /** 154 * What we last reported to DeviceIdleController about whether we are active. 155 */ 156 boolean mReportedActive; 157 158 /** 159 * Current limit on the number of concurrent JobServiceContext entries we want to 160 * keep actively running a job. 161 */ 162 int mMaxActiveJobs = 1; 163 164 /** 165 * Which uids are currently in the foreground. 166 */ 167 final SparseIntArray mUidPriorityOverride = new SparseIntArray(); 168 169 // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked -- 170 171 /** 172 * This array essentially stores the state of mActiveServices array. 173 * The ith index stores the job present on the ith JobServiceContext. 174 * We manipulate this array until we arrive at what jobs should be running on 175 * what JobServiceContext. 176 */ 177 JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT]; 178 /** 179 * Indicates whether we need to act on this jobContext id 180 */ 181 boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT]; 182 /** 183 * The uid whose jobs we would like to assign to a context. 184 */ 185 int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT]; 186 187 /** 188 * All times are in milliseconds. These constants are kept synchronized with the system 189 * global Settings. Any access to this class or its fields should be done while 190 * holding the JobSchedulerService.mLock lock. 191 */ 192 private final class Constants extends ContentObserver { 193 // Key names stored in the settings value. 194 private static final String KEY_MIN_IDLE_COUNT = "min_idle_count"; 195 private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count"; 196 private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count"; 197 private static final String KEY_MIN_CONTENT_COUNT = "min_content_count"; 198 private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count"; 199 private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor"; 200 private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor"; 201 private static final String KEY_FG_JOB_COUNT = "fg_job_count"; 202 private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count"; 203 private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count"; 204 private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count"; 205 private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count"; 206 207 private static final int DEFAULT_MIN_IDLE_COUNT = 1; 208 private static final int DEFAULT_MIN_CHARGING_COUNT = 1; 209 private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1; 210 private static final int DEFAULT_MIN_CONTENT_COUNT = 1; 211 private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1; 212 private static final float DEFAULT_HEAVY_USE_FACTOR = .9f; 213 private static final float DEFAULT_MODERATE_USE_FACTOR = .5f; 214 private static final int DEFAULT_FG_JOB_COUNT = 4; 215 private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6; 216 private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4; 217 private static final int DEFAULT_BG_LOW_JOB_COUNT = 2; 218 private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1; 219 220 /** 221 * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things 222 * early. 223 */ 224 int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT; 225 /** 226 * Minimum # of charging jobs that must be ready in order to force the JMS to schedule 227 * things early. 228 */ 229 int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT; 230 /** 231 * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule 232 * things early. 1 == Run connectivity jobs as soon as ready. 233 */ 234 int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT; 235 /** 236 * Minimum # of content trigger jobs that must be ready in order to force the JMS to 237 * schedule things early. 238 */ 239 int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT; 240 /** 241 * Minimum # of jobs (with no particular constraints) for which the JMS will be happy 242 * running some work early. This (and thus the other min counts) is now set to 1, to 243 * prevent any batching at this level. Since we now do batching through doze, that is 244 * a much better mechanism. 245 */ 246 int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT; 247 /** 248 * This is the job execution factor that is considered to be heavy use of the system. 249 */ 250 float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR; 251 /** 252 * This is the job execution factor that is considered to be moderate use of the system. 253 */ 254 float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR; 255 /** 256 * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app. 257 */ 258 int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT; 259 /** 260 * The maximum number of background jobs we allow when the system is in a normal 261 * memory state. 262 */ 263 int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT; 264 /** 265 * The maximum number of background jobs we allow when the system is in a moderate 266 * memory state. 267 */ 268 int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT; 269 /** 270 * The maximum number of background jobs we allow when the system is in a low 271 * memory state. 272 */ 273 int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT; 274 /** 275 * The maximum number of background jobs we allow when the system is in a critical 276 * memory state. 277 */ 278 int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT; 279 280 private ContentResolver mResolver; 281 private final KeyValueListParser mParser = new KeyValueListParser(','); 282 283 public Constants(Handler handler) { 284 super(handler); 285 } 286 287 public void start(ContentResolver resolver) { 288 mResolver = resolver; 289 mResolver.registerContentObserver(Settings.Global.getUriFor( 290 Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this); 291 updateConstants(); 292 } 293 294 @Override 295 public void onChange(boolean selfChange, Uri uri) { 296 updateConstants(); 297 } 298 299 private void updateConstants() { 300 synchronized (mLock) { 301 try { 302 mParser.setString(Settings.Global.getString(mResolver, 303 Settings.Global.ALARM_MANAGER_CONSTANTS)); 304 } catch (IllegalArgumentException e) { 305 // Failed to parse the settings string, log this and move on 306 // with defaults. 307 Slog.e(TAG, "Bad device idle settings", e); 308 } 309 310 MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT, 311 DEFAULT_MIN_IDLE_COUNT); 312 MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT, 313 DEFAULT_MIN_CHARGING_COUNT); 314 MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT, 315 DEFAULT_MIN_CONNECTIVITY_COUNT); 316 MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT, 317 DEFAULT_MIN_CONTENT_COUNT); 318 MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT, 319 DEFAULT_MIN_READY_JOBS_COUNT); 320 HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR, 321 DEFAULT_HEAVY_USE_FACTOR); 322 MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR, 323 DEFAULT_MODERATE_USE_FACTOR); 324 FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT, 325 DEFAULT_FG_JOB_COUNT); 326 BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT, 327 DEFAULT_BG_NORMAL_JOB_COUNT); 328 if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) { 329 BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT; 330 } 331 BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT, 332 DEFAULT_BG_MODERATE_JOB_COUNT); 333 if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) { 334 BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT; 335 } 336 BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT, 337 DEFAULT_BG_LOW_JOB_COUNT); 338 if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) { 339 BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT; 340 } 341 BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT, 342 DEFAULT_BG_CRITICAL_JOB_COUNT); 343 if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) { 344 BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT; 345 } 346 } 347 } 348 349 void dump(PrintWriter pw) { 350 pw.println(" Settings:"); 351 352 pw.print(" "); pw.print(KEY_MIN_IDLE_COUNT); pw.print("="); 353 pw.print(MIN_IDLE_COUNT); pw.println(); 354 355 pw.print(" "); pw.print(KEY_MIN_CHARGING_COUNT); pw.print("="); 356 pw.print(MIN_CHARGING_COUNT); pw.println(); 357 358 pw.print(" "); pw.print(KEY_MIN_CONNECTIVITY_COUNT); pw.print("="); 359 pw.print(MIN_CONNECTIVITY_COUNT); pw.println(); 360 361 pw.print(" "); pw.print(KEY_MIN_CONTENT_COUNT); pw.print("="); 362 pw.print(MIN_CONTENT_COUNT); pw.println(); 363 364 pw.print(" "); pw.print(KEY_MIN_READY_JOBS_COUNT); pw.print("="); 365 pw.print(MIN_READY_JOBS_COUNT); pw.println(); 366 367 pw.print(" "); pw.print(KEY_HEAVY_USE_FACTOR); pw.print("="); 368 pw.print(HEAVY_USE_FACTOR); pw.println(); 369 370 pw.print(" "); pw.print(KEY_MODERATE_USE_FACTOR); pw.print("="); 371 pw.print(MODERATE_USE_FACTOR); pw.println(); 372 373 pw.print(" "); pw.print(KEY_FG_JOB_COUNT); pw.print("="); 374 pw.print(FG_JOB_COUNT); pw.println(); 375 376 pw.print(" "); pw.print(KEY_BG_NORMAL_JOB_COUNT); pw.print("="); 377 pw.print(BG_NORMAL_JOB_COUNT); pw.println(); 378 379 pw.print(" "); pw.print(KEY_BG_MODERATE_JOB_COUNT); pw.print("="); 380 pw.print(BG_MODERATE_JOB_COUNT); pw.println(); 381 382 pw.print(" "); pw.print(KEY_BG_LOW_JOB_COUNT); pw.print("="); 383 pw.print(BG_LOW_JOB_COUNT); pw.println(); 384 385 pw.print(" "); pw.print(KEY_BG_CRITICAL_JOB_COUNT); pw.print("="); 386 pw.print(BG_CRITICAL_JOB_COUNT); pw.println(); 387 } 388 } 389 390 final Constants mConstants; 391 392 /** 393 * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we 394 * still clean up. On reinstall the package will have a new uid. 395 */ 396 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 397 @Override 398 public void onReceive(Context context, Intent intent) { 399 if (DEBUG) { 400 Slog.d(TAG, "Receieved: " + intent.getAction()); 401 } 402 if (Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())) { 403 // Purge the app's jobs if the whole package was just disabled. When this is 404 // the case the component name will be a bare package name. 405 final String pkgName = getPackageName(intent); 406 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1); 407 if (pkgName != null && pkgUid != -1) { 408 final String[] changedComponents = intent.getStringArrayExtra( 409 Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST); 410 if (changedComponents != null) { 411 for (String component : changedComponents) { 412 if (component.equals(pkgName)) { 413 if (DEBUG) { 414 Slog.d(TAG, "Package state change: " + pkgName); 415 } 416 try { 417 final int userId = UserHandle.getUserId(pkgUid); 418 IPackageManager pm = AppGlobals.getPackageManager(); 419 final int state = pm.getApplicationEnabledSetting(pkgName, userId); 420 if (state == COMPONENT_ENABLED_STATE_DISABLED 421 || state == COMPONENT_ENABLED_STATE_DISABLED_USER) { 422 if (DEBUG) { 423 Slog.d(TAG, "Removing jobs for package " + pkgName 424 + " in user " + userId); 425 } 426 cancelJobsForUid(pkgUid, true); 427 } 428 } catch (RemoteException e) { /* cannot happen */ } 429 break; 430 } 431 } 432 } 433 } else { 434 Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid); 435 } 436 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) { 437 // If this is an outright uninstall rather than the first half of an 438 // app update sequence, cancel the jobs associated with the app. 439 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { 440 int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1); 441 if (DEBUG) { 442 Slog.d(TAG, "Removing jobs for uid: " + uidRemoved); 443 } 444 cancelJobsForUid(uidRemoved, true); 445 } 446 } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) { 447 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); 448 if (DEBUG) { 449 Slog.d(TAG, "Removing jobs for user: " + userId); 450 } 451 cancelJobsForUser(userId); 452 } 453 } 454 }; 455 456 private String getPackageName(Intent intent) { 457 Uri uri = intent.getData(); 458 String pkg = uri != null ? uri.getSchemeSpecificPart() : null; 459 return pkg; 460 } 461 462 final private IUidObserver mUidObserver = new IUidObserver.Stub() { 463 @Override public void onUidStateChanged(int uid, int procState) throws RemoteException { 464 updateUidState(uid, procState); 465 } 466 467 @Override public void onUidGone(int uid) throws RemoteException { 468 updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); 469 } 470 471 @Override public void onUidActive(int uid) throws RemoteException { 472 } 473 474 @Override public void onUidIdle(int uid) throws RemoteException { 475 cancelJobsForUid(uid, false); 476 } 477 }; 478 479 public Object getLock() { 480 return mLock; 481 } 482 483 public JobStore getJobStore() { 484 return mJobs; 485 } 486 487 @Override 488 public void onStartUser(int userHandle) { 489 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle); 490 // Let's kick any outstanding jobs for this user. 491 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 492 } 493 494 @Override 495 public void onUnlockUser(int userHandle) { 496 // Let's kick any outstanding jobs for this user. 497 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 498 } 499 500 @Override 501 public void onStopUser(int userHandle) { 502 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle); 503 } 504 505 /** 506 * Entry point from client to schedule the provided job. 507 * This cancels the job if it's already been scheduled, and replaces it with the one provided. 508 * @param job JobInfo object containing execution parameters 509 * @param uId The package identifier of the application this job is for. 510 * @return Result of this operation. See <code>JobScheduler#RESULT_*</code> return codes. 511 */ 512 public int schedule(JobInfo job, int uId) { 513 return scheduleAsPackage(job, uId, null, -1, null); 514 } 515 516 public int scheduleAsPackage(JobInfo job, int uId, String packageName, int userId, 517 String tag) { 518 JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag); 519 try { 520 if (ActivityManagerNative.getDefault().getAppStartMode(uId, 521 job.getService().getPackageName()) == ActivityManager.APP_START_MODE_DISABLED) { 522 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString() 523 + " -- package not allowed to start"); 524 return JobScheduler.RESULT_FAILURE; 525 } 526 } catch (RemoteException e) { 527 } 528 if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString()); 529 JobStatus toCancel; 530 synchronized (mLock) { 531 // Jobs on behalf of others don't apply to the per-app job cap 532 if (ENFORCE_MAX_JOBS && packageName == null) { 533 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) { 534 Slog.w(TAG, "Too many jobs for uid " + uId); 535 throw new IllegalStateException("Apps may not schedule more than " 536 + MAX_JOBS_PER_APP + " distinct jobs"); 537 } 538 } 539 540 toCancel = mJobs.getJobByUidAndJobId(uId, job.getId()); 541 if (toCancel != null) { 542 cancelJobImpl(toCancel, jobStatus); 543 } 544 startTrackingJob(jobStatus, toCancel); 545 } 546 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 547 return JobScheduler.RESULT_SUCCESS; 548 } 549 550 public List<JobInfo> getPendingJobs(int uid) { 551 synchronized (mLock) { 552 List<JobStatus> jobs = mJobs.getJobsByUid(uid); 553 ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size()); 554 for (int i = jobs.size() - 1; i >= 0; i--) { 555 JobStatus job = jobs.get(i); 556 outList.add(job.getJob()); 557 } 558 return outList; 559 } 560 } 561 562 public JobInfo getPendingJob(int uid, int jobId) { 563 synchronized (mLock) { 564 List<JobStatus> jobs = mJobs.getJobsByUid(uid); 565 for (int i = jobs.size() - 1; i >= 0; i--) { 566 JobStatus job = jobs.get(i); 567 if (job.getJobId() == jobId) { 568 return job.getJob(); 569 } 570 } 571 return null; 572 } 573 } 574 575 void cancelJobsForUser(int userHandle) { 576 List<JobStatus> jobsForUser; 577 synchronized (mLock) { 578 jobsForUser = mJobs.getJobsByUser(userHandle); 579 } 580 for (int i=0; i<jobsForUser.size(); i++) { 581 JobStatus toRemove = jobsForUser.get(i); 582 cancelJobImpl(toRemove, null); 583 } 584 } 585 586 /** 587 * Entry point from client to cancel all jobs originating from their uid. 588 * This will remove the job from the master list, and cancel the job if it was staged for 589 * execution or being executed. 590 * @param uid Uid to check against for removal of a job. 591 * @param forceAll If true, all jobs for the uid will be canceled; if false, only those 592 * whose apps are stopped. 593 */ 594 public void cancelJobsForUid(int uid, boolean forceAll) { 595 List<JobStatus> jobsForUid; 596 synchronized (mLock) { 597 jobsForUid = mJobs.getJobsByUid(uid); 598 } 599 for (int i=0; i<jobsForUid.size(); i++) { 600 JobStatus toRemove = jobsForUid.get(i); 601 if (!forceAll) { 602 String packageName = toRemove.getServiceComponent().getPackageName(); 603 try { 604 if (ActivityManagerNative.getDefault().getAppStartMode(uid, packageName) 605 != ActivityManager.APP_START_MODE_DISABLED) { 606 continue; 607 } 608 } catch (RemoteException e) { 609 } 610 } 611 cancelJobImpl(toRemove, null); 612 } 613 } 614 615 /** 616 * Entry point from client to cancel the job corresponding to the jobId provided. 617 * This will remove the job from the master list, and cancel the job if it was staged for 618 * execution or being executed. 619 * @param uid Uid of the calling client. 620 * @param jobId Id of the job, provided at schedule-time. 621 */ 622 public void cancelJob(int uid, int jobId) { 623 JobStatus toCancel; 624 synchronized (mLock) { 625 toCancel = mJobs.getJobByUidAndJobId(uid, jobId); 626 } 627 if (toCancel != null) { 628 cancelJobImpl(toCancel, null); 629 } 630 } 631 632 private void cancelJobImpl(JobStatus cancelled, JobStatus incomingJob) { 633 if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString()); 634 stopTrackingJob(cancelled, incomingJob, true /* writeBack */); 635 synchronized (mLock) { 636 // Remove from pending queue. 637 if (mPendingJobs.remove(cancelled)) { 638 mJobPackageTracker.noteNonpending(cancelled); 639 } 640 // Cancel if running. 641 stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED); 642 reportActive(); 643 } 644 } 645 646 void updateUidState(int uid, int procState) { 647 synchronized (mLock) { 648 if (procState == ActivityManager.PROCESS_STATE_TOP) { 649 // Only use this if we are exactly the top app. All others can live 650 // with just the foreground priority. This means that persistent processes 651 // can never be the top app priority... that is fine. 652 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP); 653 } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { 654 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP); 655 } else { 656 mUidPriorityOverride.delete(uid); 657 } 658 } 659 } 660 661 @Override 662 public void onDeviceIdleStateChanged(boolean deviceIdle) { 663 synchronized (mLock) { 664 if (deviceIdle) { 665 // When becoming idle, make sure no jobs are actively running. 666 for (int i=0; i<mActiveServices.size(); i++) { 667 JobServiceContext jsc = mActiveServices.get(i); 668 final JobStatus executing = jsc.getRunningJob(); 669 if (executing != null) { 670 jsc.cancelExecutingJob(JobParameters.REASON_DEVICE_IDLE); 671 } 672 } 673 } else { 674 // When coming out of idle, allow thing to start back up. 675 if (mReadyToRock) { 676 if (mLocalDeviceIdleController != null) { 677 if (!mReportedActive) { 678 mReportedActive = true; 679 mLocalDeviceIdleController.setJobsActive(true); 680 } 681 } 682 } 683 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 684 } 685 } 686 } 687 688 void reportActive() { 689 // active is true if pending queue contains jobs OR some job is running. 690 boolean active = mPendingJobs.size() > 0; 691 if (mPendingJobs.size() <= 0) { 692 for (int i=0; i<mActiveServices.size(); i++) { 693 JobServiceContext jsc = mActiveServices.get(i); 694 if (jsc.getRunningJob() != null) { 695 active = true; 696 break; 697 } 698 } 699 } 700 701 if (mReportedActive != active) { 702 mReportedActive = active; 703 if (mLocalDeviceIdleController != null) { 704 mLocalDeviceIdleController.setJobsActive(active); 705 } 706 } 707 } 708 709 /** 710 * Initializes the system service. 711 * <p> 712 * Subclasses must define a single argument constructor that accepts the context 713 * and passes it to super. 714 * </p> 715 * 716 * @param context The system server context. 717 */ 718 public JobSchedulerService(Context context) { 719 super(context); 720 // Create the controllers. 721 mControllers = new ArrayList<StateController>(); 722 mControllers.add(ConnectivityController.get(this)); 723 mControllers.add(TimeController.get(this)); 724 mControllers.add(IdleController.get(this)); 725 mControllers.add(BatteryController.get(this)); 726 mControllers.add(AppIdleController.get(this)); 727 mControllers.add(ContentObserverController.get(this)); 728 mControllers.add(DeviceIdleJobsController.get(this)); 729 730 mHandler = new JobHandler(context.getMainLooper()); 731 mConstants = new Constants(mHandler); 732 mJobSchedulerStub = new JobSchedulerStub(); 733 mJobs = JobStore.initAndGet(this); 734 } 735 736 @Override 737 public void onStart() { 738 publishLocalService(JobSchedulerInternal.class, new LocalService()); 739 publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub); 740 } 741 742 @Override 743 public void onBootPhase(int phase) { 744 if (PHASE_SYSTEM_SERVICES_READY == phase) { 745 mConstants.start(getContext().getContentResolver()); 746 // Register br for package removals and user removals. 747 final IntentFilter filter = new IntentFilter(); 748 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 749 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 750 filter.addDataScheme("package"); 751 getContext().registerReceiverAsUser( 752 mBroadcastReceiver, UserHandle.ALL, filter, null, null); 753 final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED); 754 getContext().registerReceiverAsUser( 755 mBroadcastReceiver, UserHandle.ALL, userFilter, null, null); 756 mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE); 757 try { 758 ActivityManagerNative.getDefault().registerUidObserver(mUidObserver, 759 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE 760 | ActivityManager.UID_OBSERVER_IDLE); 761 } catch (RemoteException e) { 762 // ignored; both services live in system_server 763 } 764 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { 765 synchronized (mLock) { 766 // Let's go! 767 mReadyToRock = true; 768 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService( 769 BatteryStats.SERVICE_NAME)); 770 mLocalDeviceIdleController 771 = LocalServices.getService(DeviceIdleController.LocalService.class); 772 // Create the "runners". 773 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) { 774 mActiveServices.add( 775 new JobServiceContext(this, mBatteryStats, mJobPackageTracker, 776 getContext().getMainLooper())); 777 } 778 // Attach jobs to their controllers. 779 mJobs.forEachJob(new JobStatusFunctor() { 780 @Override 781 public void process(JobStatus job) { 782 for (int controller = 0; controller < mControllers.size(); controller++) { 783 final StateController sc = mControllers.get(controller); 784 sc.maybeStartTrackingJobLocked(job, null); 785 } 786 } 787 }); 788 // GO GO GO! 789 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 790 } 791 } 792 } 793 794 /** 795 * Called when we have a job status object that we need to insert in our 796 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know 797 * about. 798 */ 799 private void startTrackingJob(JobStatus jobStatus, JobStatus lastJob) { 800 synchronized (mLock) { 801 final boolean update = mJobs.add(jobStatus); 802 if (mReadyToRock) { 803 for (int i = 0; i < mControllers.size(); i++) { 804 StateController controller = mControllers.get(i); 805 if (update) { 806 controller.maybeStopTrackingJobLocked(jobStatus, null, true); 807 } 808 controller.maybeStartTrackingJobLocked(jobStatus, lastJob); 809 } 810 } 811 } 812 } 813 814 /** 815 * Called when we want to remove a JobStatus object that we've finished executing. Returns the 816 * object removed. 817 */ 818 private boolean stopTrackingJob(JobStatus jobStatus, JobStatus incomingJob, 819 boolean writeBack) { 820 synchronized (mLock) { 821 // Remove from store as well as controllers. 822 final boolean removed = mJobs.remove(jobStatus, writeBack); 823 if (removed && mReadyToRock) { 824 for (int i=0; i<mControllers.size(); i++) { 825 StateController controller = mControllers.get(i); 826 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false); 827 } 828 } 829 return removed; 830 } 831 } 832 833 private boolean stopJobOnServiceContextLocked(JobStatus job, int reason) { 834 for (int i=0; i<mActiveServices.size(); i++) { 835 JobServiceContext jsc = mActiveServices.get(i); 836 final JobStatus executing = jsc.getRunningJob(); 837 if (executing != null && executing.matches(job.getUid(), job.getJobId())) { 838 jsc.cancelExecutingJob(reason); 839 return true; 840 } 841 } 842 return false; 843 } 844 845 /** 846 * @param job JobStatus we are querying against. 847 * @return Whether or not the job represented by the status object is currently being run or 848 * is pending. 849 */ 850 private boolean isCurrentlyActiveLocked(JobStatus job) { 851 for (int i=0; i<mActiveServices.size(); i++) { 852 JobServiceContext serviceContext = mActiveServices.get(i); 853 final JobStatus running = serviceContext.getRunningJob(); 854 if (running != null && running.matches(job.getUid(), job.getJobId())) { 855 return true; 856 } 857 } 858 return false; 859 } 860 861 void noteJobsPending(List<JobStatus> jobs) { 862 for (int i = jobs.size() - 1; i >= 0; i--) { 863 JobStatus job = jobs.get(i); 864 mJobPackageTracker.notePending(job); 865 } 866 } 867 868 void noteJobsNonpending(List<JobStatus> jobs) { 869 for (int i = jobs.size() - 1; i >= 0; i--) { 870 JobStatus job = jobs.get(i); 871 mJobPackageTracker.noteNonpending(job); 872 } 873 } 874 875 /** 876 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to 877 * specify an override deadline on a failed job (the failed job will run even though it's not 878 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any 879 * ready job with {@link JobStatus#numFailures} > 0 will be executed. 880 * 881 * @param failureToReschedule Provided job status that we will reschedule. 882 * @return A newly instantiated JobStatus with the same constraints as the last job except 883 * with adjusted timing constraints. 884 * 885 * @see JobHandler#maybeQueueReadyJobsForExecutionLockedH 886 */ 887 private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) { 888 final long elapsedNowMillis = SystemClock.elapsedRealtime(); 889 final JobInfo job = failureToReschedule.getJob(); 890 891 final long initialBackoffMillis = job.getInitialBackoffMillis(); 892 final int backoffAttempts = failureToReschedule.getNumFailures() + 1; 893 long delayMillis; 894 895 switch (job.getBackoffPolicy()) { 896 case JobInfo.BACKOFF_POLICY_LINEAR: 897 delayMillis = initialBackoffMillis * backoffAttempts; 898 break; 899 default: 900 if (DEBUG) { 901 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential."); 902 } 903 case JobInfo.BACKOFF_POLICY_EXPONENTIAL: 904 delayMillis = 905 (long) Math.scalb(initialBackoffMillis, backoffAttempts - 1); 906 break; 907 } 908 delayMillis = 909 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS); 910 JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis, 911 JobStatus.NO_LATEST_RUNTIME, backoffAttempts); 912 for (int ic=0; ic<mControllers.size(); ic++) { 913 StateController controller = mControllers.get(ic); 914 controller.rescheduleForFailure(newJob, failureToReschedule); 915 } 916 return newJob; 917 } 918 919 /** 920 * Called after a periodic has executed so we can reschedule it. We take the last execution 921 * time of the job to be the time of completion (i.e. the time at which this function is 922 * called). 923 * This could be inaccurate b/c the job can run for as long as 924 * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead 925 * to underscheduling at least, rather than if we had taken the last execution time to be the 926 * start of the execution. 927 * @return A new job representing the execution criteria for this instantiation of the 928 * recurring job. 929 */ 930 private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) { 931 final long elapsedNow = SystemClock.elapsedRealtime(); 932 // Compute how much of the period is remaining. 933 long runEarly = 0L; 934 935 // If this periodic was rescheduled it won't have a deadline. 936 if (periodicToReschedule.hasDeadlineConstraint()) { 937 runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L); 938 } 939 long flex = periodicToReschedule.getJob().getFlexMillis(); 940 long period = periodicToReschedule.getJob().getIntervalMillis(); 941 long newLatestRuntimeElapsed = elapsedNow + runEarly + period; 942 long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex; 943 944 if (DEBUG) { 945 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" + 946 newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s"); 947 } 948 return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed, 949 newLatestRuntimeElapsed, 0 /* backoffAttempt */); 950 } 951 952 // JobCompletedListener implementations. 953 954 /** 955 * A job just finished executing. We fetch the 956 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on 957 * whether we want to reschedule we readd it to the controllers. 958 * @param jobStatus Completed job. 959 * @param needsReschedule Whether the implementing class should reschedule this job. 960 */ 961 @Override 962 public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule) { 963 if (DEBUG) { 964 Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule); 965 } 966 // Do not write back immediately if this is a periodic job. The job may get lost if system 967 // shuts down before it is added back. 968 if (!stopTrackingJob(jobStatus, null, !jobStatus.getJob().isPeriodic())) { 969 if (DEBUG) { 970 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?"); 971 } 972 // We still want to check for jobs to execute, because this job may have 973 // scheduled a new job under the same job id, and now we can run it. 974 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget(); 975 return; 976 } 977 // Note: there is a small window of time in here where, when rescheduling a job, 978 // we will stop monitoring its content providers. This should be fixed by stopping 979 // the old job after scheduling the new one, but since we have no lock held here 980 // that may cause ordering problems if the app removes jobStatus while in here. 981 if (needsReschedule) { 982 JobStatus rescheduled = getRescheduleJobForFailure(jobStatus); 983 startTrackingJob(rescheduled, jobStatus); 984 } else if (jobStatus.getJob().isPeriodic()) { 985 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus); 986 startTrackingJob(rescheduledPeriodic, jobStatus); 987 } 988 reportActive(); 989 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget(); 990 } 991 992 // StateChangedListener implementations. 993 994 /** 995 * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that 996 * some controller's state has changed, so as to run through the list of jobs and start/stop 997 * any that are eligible. 998 */ 999 @Override 1000 public void onControllerStateChanged() { 1001 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 1002 } 1003 1004 @Override 1005 public void onRunJobNow(JobStatus jobStatus) { 1006 mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget(); 1007 } 1008 1009 private class JobHandler extends Handler { 1010 1011 public JobHandler(Looper looper) { 1012 super(looper); 1013 } 1014 1015 @Override 1016 public void handleMessage(Message message) { 1017 synchronized (mLock) { 1018 if (!mReadyToRock) { 1019 return; 1020 } 1021 } 1022 switch (message.what) { 1023 case MSG_JOB_EXPIRED: 1024 synchronized (mLock) { 1025 JobStatus runNow = (JobStatus) message.obj; 1026 // runNow can be null, which is a controller's way of indicating that its 1027 // state is such that all ready jobs should be run immediately. 1028 if (runNow != null && !mPendingJobs.contains(runNow) 1029 && mJobs.containsJob(runNow)) { 1030 mJobPackageTracker.notePending(runNow); 1031 mPendingJobs.add(runNow); 1032 } 1033 queueReadyJobsForExecutionLockedH(); 1034 } 1035 break; 1036 case MSG_CHECK_JOB: 1037 synchronized (mLock) { 1038 if (mReportedActive) { 1039 // if jobs are currently being run, queue all ready jobs for execution. 1040 queueReadyJobsForExecutionLockedH(); 1041 } else { 1042 // Check the list of jobs and run some of them if we feel inclined. 1043 maybeQueueReadyJobsForExecutionLockedH(); 1044 } 1045 } 1046 break; 1047 case MSG_CHECK_JOB_GREEDY: 1048 synchronized (mLock) { 1049 queueReadyJobsForExecutionLockedH(); 1050 } 1051 break; 1052 case MSG_STOP_JOB: 1053 cancelJobImpl((JobStatus)message.obj, null); 1054 break; 1055 } 1056 maybeRunPendingJobsH(); 1057 // Don't remove JOB_EXPIRED in case one came along while processing the queue. 1058 removeMessages(MSG_CHECK_JOB); 1059 } 1060 1061 /** 1062 * Run through list of jobs and execute all possible - at least one is expired so we do 1063 * as many as we can. 1064 */ 1065 private void queueReadyJobsForExecutionLockedH() { 1066 if (DEBUG) { 1067 Slog.d(TAG, "queuing all ready jobs for execution:"); 1068 } 1069 noteJobsNonpending(mPendingJobs); 1070 mPendingJobs.clear(); 1071 mJobs.forEachJob(mReadyQueueFunctor); 1072 mReadyQueueFunctor.postProcess(); 1073 1074 if (DEBUG) { 1075 final int queuedJobs = mPendingJobs.size(); 1076 if (queuedJobs == 0) { 1077 Slog.d(TAG, "No jobs pending."); 1078 } else { 1079 Slog.d(TAG, queuedJobs + " jobs queued."); 1080 } 1081 } 1082 } 1083 1084 class ReadyJobQueueFunctor implements JobStatusFunctor { 1085 ArrayList<JobStatus> newReadyJobs; 1086 1087 @Override 1088 public void process(JobStatus job) { 1089 if (isReadyToBeExecutedLocked(job)) { 1090 if (DEBUG) { 1091 Slog.d(TAG, " queued " + job.toShortString()); 1092 } 1093 if (newReadyJobs == null) { 1094 newReadyJobs = new ArrayList<JobStatus>(); 1095 } 1096 newReadyJobs.add(job); 1097 } else if (areJobConstraintsNotSatisfiedLocked(job)) { 1098 stopJobOnServiceContextLocked(job, 1099 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED); 1100 } 1101 } 1102 1103 public void postProcess() { 1104 if (newReadyJobs != null) { 1105 noteJobsPending(newReadyJobs); 1106 mPendingJobs.addAll(newReadyJobs); 1107 } 1108 newReadyJobs = null; 1109 } 1110 } 1111 private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor(); 1112 1113 /** 1114 * The state of at least one job has changed. Here is where we could enforce various 1115 * policies on when we want to execute jobs. 1116 * Right now the policy is such: 1117 * If >1 of the ready jobs is idle mode we send all of them off 1118 * if more than 2 network connectivity jobs are ready we send them all off. 1119 * If more than 4 jobs total are ready we send them all off. 1120 * TODO: It would be nice to consolidate these sort of high-level policies somewhere. 1121 */ 1122 class MaybeReadyJobQueueFunctor implements JobStatusFunctor { 1123 int chargingCount; 1124 int idleCount; 1125 int backoffCount; 1126 int connectivityCount; 1127 int contentCount; 1128 List<JobStatus> runnableJobs; 1129 1130 public MaybeReadyJobQueueFunctor() { 1131 reset(); 1132 } 1133 1134 // Functor method invoked for each job via JobStore.forEachJob() 1135 @Override 1136 public void process(JobStatus job) { 1137 if (isReadyToBeExecutedLocked(job)) { 1138 try { 1139 if (ActivityManagerNative.getDefault().getAppStartMode(job.getUid(), 1140 job.getJob().getService().getPackageName()) 1141 == ActivityManager.APP_START_MODE_DISABLED) { 1142 Slog.w(TAG, "Aborting job " + job.getUid() + ":" 1143 + job.getJob().toString() + " -- package not allowed to start"); 1144 mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget(); 1145 return; 1146 } 1147 } catch (RemoteException e) { 1148 } 1149 if (job.getNumFailures() > 0) { 1150 backoffCount++; 1151 } 1152 if (job.hasIdleConstraint()) { 1153 idleCount++; 1154 } 1155 if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint() 1156 || job.hasNotRoamingConstraint()) { 1157 connectivityCount++; 1158 } 1159 if (job.hasChargingConstraint()) { 1160 chargingCount++; 1161 } 1162 if (job.hasContentTriggerConstraint()) { 1163 contentCount++; 1164 } 1165 if (runnableJobs == null) { 1166 runnableJobs = new ArrayList<>(); 1167 } 1168 runnableJobs.add(job); 1169 } else if (areJobConstraintsNotSatisfiedLocked(job)) { 1170 stopJobOnServiceContextLocked(job, 1171 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED); 1172 } 1173 } 1174 1175 public void postProcess() { 1176 if (backoffCount > 0 || 1177 idleCount >= mConstants.MIN_IDLE_COUNT || 1178 connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT || 1179 chargingCount >= mConstants.MIN_CHARGING_COUNT || 1180 contentCount >= mConstants.MIN_CONTENT_COUNT || 1181 (runnableJobs != null 1182 && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) { 1183 if (DEBUG) { 1184 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Running jobs."); 1185 } 1186 noteJobsPending(runnableJobs); 1187 mPendingJobs.addAll(runnableJobs); 1188 } else { 1189 if (DEBUG) { 1190 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Not running anything."); 1191 } 1192 } 1193 1194 // Be ready for next time 1195 reset(); 1196 } 1197 1198 private void reset() { 1199 chargingCount = 0; 1200 idleCount = 0; 1201 backoffCount = 0; 1202 connectivityCount = 0; 1203 contentCount = 0; 1204 runnableJobs = null; 1205 } 1206 } 1207 private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor(); 1208 1209 private void maybeQueueReadyJobsForExecutionLockedH() { 1210 if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs..."); 1211 1212 noteJobsNonpending(mPendingJobs); 1213 mPendingJobs.clear(); 1214 mJobs.forEachJob(mMaybeQueueFunctor); 1215 mMaybeQueueFunctor.postProcess(); 1216 } 1217 1218 /** 1219 * Criteria for moving a job into the pending queue: 1220 * - It's ready. 1221 * - It's not pending. 1222 * - It's not already running on a JSC. 1223 * - The user that requested the job is running. 1224 * - The component is enabled and runnable. 1225 */ 1226 private boolean isReadyToBeExecutedLocked(JobStatus job) { 1227 final boolean jobReady = job.isReady(); 1228 final boolean jobPending = mPendingJobs.contains(job); 1229 final boolean jobActive = isCurrentlyActiveLocked(job); 1230 1231 final int userId = job.getUserId(); 1232 final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId); 1233 final boolean componentPresent; 1234 try { 1235 componentPresent = (AppGlobals.getPackageManager().getServiceInfo( 1236 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING, 1237 userId) != null); 1238 } catch (RemoteException e) { 1239 throw e.rethrowAsRuntimeException(); 1240 } 1241 1242 if (DEBUG) { 1243 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString() 1244 + " ready=" + jobReady + " pending=" + jobPending 1245 + " active=" + jobActive + " userStarted=" + userStarted 1246 + " componentPresent=" + componentPresent); 1247 } 1248 return userStarted && componentPresent && jobReady && !jobPending && !jobActive; 1249 } 1250 1251 /** 1252 * Criteria for cancelling an active job: 1253 * - It's not ready 1254 * - It's running on a JSC. 1255 */ 1256 private boolean areJobConstraintsNotSatisfiedLocked(JobStatus job) { 1257 return !job.isReady() && isCurrentlyActiveLocked(job); 1258 } 1259 1260 /** 1261 * Reconcile jobs in the pending queue against available execution contexts. 1262 * A controller can force a job into the pending queue even if it's already running, but 1263 * here is where we decide whether to actually execute it. 1264 */ 1265 private void maybeRunPendingJobsH() { 1266 synchronized (mLock) { 1267 if (DEBUG) { 1268 Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs."); 1269 } 1270 assignJobsToContextsLocked(); 1271 reportActive(); 1272 } 1273 } 1274 } 1275 1276 private int adjustJobPriority(int curPriority, JobStatus job) { 1277 if (curPriority < JobInfo.PRIORITY_TOP_APP) { 1278 float factor = mJobPackageTracker.getLoadFactor(job); 1279 if (factor >= mConstants.HEAVY_USE_FACTOR) { 1280 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING; 1281 } else if (factor >= mConstants.MODERATE_USE_FACTOR) { 1282 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING; 1283 } 1284 } 1285 return curPriority; 1286 } 1287 1288 private int evaluateJobPriorityLocked(JobStatus job) { 1289 int priority = job.getPriority(); 1290 if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) { 1291 return adjustJobPriority(priority, job); 1292 } 1293 int override = mUidPriorityOverride.get(job.getSourceUid(), 0); 1294 if (override != 0) { 1295 return adjustJobPriority(override, job); 1296 } 1297 return adjustJobPriority(priority, job); 1298 } 1299 1300 /** 1301 * Takes jobs from pending queue and runs them on available contexts. 1302 * If no contexts are available, preempts lower priority jobs to 1303 * run higher priority ones. 1304 * Lock on mJobs before calling this function. 1305 */ 1306 private void assignJobsToContextsLocked() { 1307 if (DEBUG) { 1308 Slog.d(TAG, printPendingQueue()); 1309 } 1310 1311 int memLevel; 1312 try { 1313 memLevel = ActivityManagerNative.getDefault().getMemoryTrimLevel(); 1314 } catch (RemoteException e) { 1315 memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL; 1316 } 1317 switch (memLevel) { 1318 case ProcessStats.ADJ_MEM_FACTOR_MODERATE: 1319 mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT; 1320 break; 1321 case ProcessStats.ADJ_MEM_FACTOR_LOW: 1322 mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT; 1323 break; 1324 case ProcessStats.ADJ_MEM_FACTOR_CRITICAL: 1325 mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT; 1326 break; 1327 default: 1328 mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT; 1329 break; 1330 } 1331 1332 JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap; 1333 boolean[] act = mTmpAssignAct; 1334 int[] preferredUidForContext = mTmpAssignPreferredUidForContext; 1335 int numActive = 0; 1336 int numForeground = 0; 1337 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { 1338 final JobServiceContext js = mActiveServices.get(i); 1339 final JobStatus status = js.getRunningJob(); 1340 if ((contextIdToJobMap[i] = status) != null) { 1341 numActive++; 1342 if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) { 1343 numForeground++; 1344 } 1345 } 1346 act[i] = false; 1347 preferredUidForContext[i] = js.getPreferredUid(); 1348 } 1349 if (DEBUG) { 1350 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial")); 1351 } 1352 for (int i=0; i<mPendingJobs.size(); i++) { 1353 JobStatus nextPending = mPendingJobs.get(i); 1354 1355 // If job is already running, go to next job. 1356 int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap); 1357 if (jobRunningContext != -1) { 1358 continue; 1359 } 1360 1361 final int priority = evaluateJobPriorityLocked(nextPending); 1362 nextPending.lastEvaluatedPriority = priority; 1363 1364 // Find a context for nextPending. The context should be available OR 1365 // it should have lowest priority among all running jobs 1366 // (sharing the same Uid as nextPending) 1367 int minPriority = Integer.MAX_VALUE; 1368 int minPriorityContextId = -1; 1369 for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) { 1370 JobStatus job = contextIdToJobMap[j]; 1371 int preferredUid = preferredUidForContext[j]; 1372 if (job == null) { 1373 if ((numActive < mMaxActiveJobs || 1374 (priority >= JobInfo.PRIORITY_TOP_APP && 1375 numForeground < mConstants.FG_JOB_COUNT)) && 1376 (preferredUid == nextPending.getUid() || 1377 preferredUid == JobServiceContext.NO_PREFERRED_UID)) { 1378 // This slot is free, and we haven't yet hit the limit on 1379 // concurrent jobs... we can just throw the job in to here. 1380 minPriorityContextId = j; 1381 break; 1382 } 1383 // No job on this context, but nextPending can't run here because 1384 // the context has a preferred Uid or we have reached the limit on 1385 // concurrent jobs. 1386 continue; 1387 } 1388 if (job.getUid() != nextPending.getUid()) { 1389 continue; 1390 } 1391 if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) { 1392 continue; 1393 } 1394 if (minPriority > nextPending.lastEvaluatedPriority) { 1395 minPriority = nextPending.lastEvaluatedPriority; 1396 minPriorityContextId = j; 1397 } 1398 } 1399 if (minPriorityContextId != -1) { 1400 contextIdToJobMap[minPriorityContextId] = nextPending; 1401 act[minPriorityContextId] = true; 1402 numActive++; 1403 if (priority >= JobInfo.PRIORITY_TOP_APP) { 1404 numForeground++; 1405 } 1406 } 1407 } 1408 if (DEBUG) { 1409 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final")); 1410 } 1411 mJobPackageTracker.noteConcurrency(numActive, numForeground); 1412 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { 1413 boolean preservePreferredUid = false; 1414 if (act[i]) { 1415 JobStatus js = mActiveServices.get(i).getRunningJob(); 1416 if (js != null) { 1417 if (DEBUG) { 1418 Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJob()); 1419 } 1420 // preferredUid will be set to uid of currently running job. 1421 mActiveServices.get(i).preemptExecutingJob(); 1422 preservePreferredUid = true; 1423 } else { 1424 final JobStatus pendingJob = contextIdToJobMap[i]; 1425 if (DEBUG) { 1426 Slog.d(TAG, "About to run job on context " 1427 + String.valueOf(i) + ", job: " + pendingJob); 1428 } 1429 for (int ic=0; ic<mControllers.size(); ic++) { 1430 mControllers.get(ic).prepareForExecutionLocked(pendingJob); 1431 } 1432 if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) { 1433 Slog.d(TAG, "Error executing " + pendingJob); 1434 } 1435 if (mPendingJobs.remove(pendingJob)) { 1436 mJobPackageTracker.noteNonpending(pendingJob); 1437 } 1438 } 1439 } 1440 if (!preservePreferredUid) { 1441 mActiveServices.get(i).clearPreferredUid(); 1442 } 1443 } 1444 } 1445 1446 int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) { 1447 for (int i=0; i<map.length; i++) { 1448 if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) { 1449 return i; 1450 } 1451 } 1452 return -1; 1453 } 1454 1455 final class LocalService implements JobSchedulerInternal { 1456 1457 /** 1458 * Returns a list of all pending jobs. A running job is not considered pending. Periodic 1459 * jobs are always considered pending. 1460 */ 1461 @Override 1462 public List<JobInfo> getSystemScheduledPendingJobs() { 1463 synchronized (mLock) { 1464 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>(); 1465 mJobs.forEachJob(Process.SYSTEM_UID, new JobStatusFunctor() { 1466 @Override 1467 public void process(JobStatus job) { 1468 if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) { 1469 pendingJobs.add(job.getJob()); 1470 } 1471 } 1472 }); 1473 return pendingJobs; 1474 } 1475 } 1476 } 1477 1478 /** 1479 * Binder stub trampoline implementation 1480 */ 1481 final class JobSchedulerStub extends IJobScheduler.Stub { 1482 /** Cache determination of whether a given app can persist jobs 1483 * key is uid of the calling app; value is undetermined/true/false 1484 */ 1485 private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>(); 1486 1487 // Enforce that only the app itself (or shared uid participant) can schedule a 1488 // job that runs one of the app's services, as well as verifying that the 1489 // named service properly requires the BIND_JOB_SERVICE permission 1490 private void enforceValidJobRequest(int uid, JobInfo job) { 1491 final IPackageManager pm = AppGlobals.getPackageManager(); 1492 final ComponentName service = job.getService(); 1493 try { 1494 ServiceInfo si = pm.getServiceInfo(service, 1495 PackageManager.MATCH_DIRECT_BOOT_AWARE 1496 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 1497 UserHandle.getUserId(uid)); 1498 if (si == null) { 1499 throw new IllegalArgumentException("No such service " + service); 1500 } 1501 if (si.applicationInfo.uid != uid) { 1502 throw new IllegalArgumentException("uid " + uid + 1503 " cannot schedule job in " + service.getPackageName()); 1504 } 1505 if (!JobService.PERMISSION_BIND.equals(si.permission)) { 1506 throw new IllegalArgumentException("Scheduled service " + service 1507 + " does not require android.permission.BIND_JOB_SERVICE permission"); 1508 } 1509 } catch (RemoteException e) { 1510 // Can't happen; the Package Manager is in this same process 1511 } 1512 } 1513 1514 private boolean canPersistJobs(int pid, int uid) { 1515 // If we get this far we're good to go; all we need to do now is check 1516 // whether the app is allowed to persist its scheduled work. 1517 final boolean canPersist; 1518 synchronized (mPersistCache) { 1519 Boolean cached = mPersistCache.get(uid); 1520 if (cached != null) { 1521 canPersist = cached.booleanValue(); 1522 } else { 1523 // Persisting jobs is tantamount to running at boot, so we permit 1524 // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED 1525 // permission 1526 int result = getContext().checkPermission( 1527 android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid); 1528 canPersist = (result == PackageManager.PERMISSION_GRANTED); 1529 mPersistCache.put(uid, canPersist); 1530 } 1531 } 1532 return canPersist; 1533 } 1534 1535 // IJobScheduler implementation 1536 @Override 1537 public int schedule(JobInfo job) throws RemoteException { 1538 if (DEBUG) { 1539 Slog.d(TAG, "Scheduling job: " + job.toString()); 1540 } 1541 final int pid = Binder.getCallingPid(); 1542 final int uid = Binder.getCallingUid(); 1543 1544 enforceValidJobRequest(uid, job); 1545 if (job.isPersisted()) { 1546 if (!canPersistJobs(pid, uid)) { 1547 throw new IllegalArgumentException("Error: requested job be persisted without" 1548 + " holding RECEIVE_BOOT_COMPLETED permission."); 1549 } 1550 } 1551 1552 long ident = Binder.clearCallingIdentity(); 1553 try { 1554 return JobSchedulerService.this.schedule(job, uid); 1555 } finally { 1556 Binder.restoreCallingIdentity(ident); 1557 } 1558 } 1559 1560 @Override 1561 public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag) 1562 throws RemoteException { 1563 final int callerUid = Binder.getCallingUid(); 1564 if (DEBUG) { 1565 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString() 1566 + " on behalf of " + packageName); 1567 } 1568 1569 if (packageName == null) { 1570 throw new NullPointerException("Must specify a package for scheduleAsPackage()"); 1571 } 1572 1573 int mayScheduleForOthers = getContext().checkCallingOrSelfPermission( 1574 android.Manifest.permission.UPDATE_DEVICE_STATS); 1575 if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) { 1576 throw new SecurityException("Caller uid " + callerUid 1577 + " not permitted to schedule jobs for other apps"); 1578 } 1579 1580 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) { 1581 getContext().enforceCallingOrSelfPermission( 1582 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG); 1583 } 1584 1585 long ident = Binder.clearCallingIdentity(); 1586 try { 1587 return JobSchedulerService.this.scheduleAsPackage(job, callerUid, 1588 packageName, userId, tag); 1589 } finally { 1590 Binder.restoreCallingIdentity(ident); 1591 } 1592 } 1593 1594 @Override 1595 public List<JobInfo> getAllPendingJobs() throws RemoteException { 1596 final int uid = Binder.getCallingUid(); 1597 1598 long ident = Binder.clearCallingIdentity(); 1599 try { 1600 return JobSchedulerService.this.getPendingJobs(uid); 1601 } finally { 1602 Binder.restoreCallingIdentity(ident); 1603 } 1604 } 1605 1606 @Override 1607 public JobInfo getPendingJob(int jobId) throws RemoteException { 1608 final int uid = Binder.getCallingUid(); 1609 1610 long ident = Binder.clearCallingIdentity(); 1611 try { 1612 return JobSchedulerService.this.getPendingJob(uid, jobId); 1613 } finally { 1614 Binder.restoreCallingIdentity(ident); 1615 } 1616 } 1617 1618 @Override 1619 public void cancelAll() throws RemoteException { 1620 final int uid = Binder.getCallingUid(); 1621 1622 long ident = Binder.clearCallingIdentity(); 1623 try { 1624 JobSchedulerService.this.cancelJobsForUid(uid, true); 1625 } finally { 1626 Binder.restoreCallingIdentity(ident); 1627 } 1628 } 1629 1630 @Override 1631 public void cancel(int jobId) throws RemoteException { 1632 final int uid = Binder.getCallingUid(); 1633 1634 long ident = Binder.clearCallingIdentity(); 1635 try { 1636 JobSchedulerService.this.cancelJob(uid, jobId); 1637 } finally { 1638 Binder.restoreCallingIdentity(ident); 1639 } 1640 } 1641 1642 /** 1643 * "dumpsys" infrastructure 1644 */ 1645 @Override 1646 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1647 getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 1648 1649 long identityToken = Binder.clearCallingIdentity(); 1650 try { 1651 JobSchedulerService.this.dumpInternal(pw, args); 1652 } finally { 1653 Binder.restoreCallingIdentity(identityToken); 1654 } 1655 } 1656 1657 @Override 1658 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 1659 String[] args, ResultReceiver resultReceiver) throws RemoteException { 1660 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec( 1661 this, in, out, err, args, resultReceiver); 1662 } 1663 }; 1664 1665 // Shell command infrastructure: run the given job immediately 1666 int executeRunCommand(String pkgName, int userId, int jobId, boolean force) { 1667 if (DEBUG) { 1668 Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId 1669 + " " + jobId + " f=" + force); 1670 } 1671 1672 try { 1673 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, userId); 1674 if (uid < 0) { 1675 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE; 1676 } 1677 1678 synchronized (mLock) { 1679 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId); 1680 if (js == null) { 1681 return JobSchedulerShellCommand.CMD_ERR_NO_JOB; 1682 } 1683 1684 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT; 1685 if (!js.isConstraintsSatisfied()) { 1686 js.overrideState = 0; 1687 return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS; 1688 } 1689 1690 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget(); 1691 } 1692 } catch (RemoteException e) { 1693 // can't happen 1694 } 1695 return 0; 1696 } 1697 1698 private String printContextIdToJobMap(JobStatus[] map, String initial) { 1699 StringBuilder s = new StringBuilder(initial + ": "); 1700 for (int i=0; i<map.length; i++) { 1701 s.append("(") 1702 .append(map[i] == null? -1: map[i].getJobId()) 1703 .append(map[i] == null? -1: map[i].getUid()) 1704 .append(")" ); 1705 } 1706 return s.toString(); 1707 } 1708 1709 private String printPendingQueue() { 1710 StringBuilder s = new StringBuilder("Pending queue: "); 1711 Iterator<JobStatus> it = mPendingJobs.iterator(); 1712 while (it.hasNext()) { 1713 JobStatus js = it.next(); 1714 s.append("(") 1715 .append(js.getJob().getId()) 1716 .append(", ") 1717 .append(js.getUid()) 1718 .append(") "); 1719 } 1720 return s.toString(); 1721 } 1722 1723 static void dumpHelp(PrintWriter pw) { 1724 pw.println("Job Scheduler (jobscheduler) dump options:"); 1725 pw.println(" [-h] [package] ..."); 1726 pw.println(" -h: print this help"); 1727 pw.println(" [package] is an optional package name to limit the output to."); 1728 } 1729 1730 void dumpInternal(final PrintWriter pw, String[] args) { 1731 int filterUid = -1; 1732 if (!ArrayUtils.isEmpty(args)) { 1733 int opti = 0; 1734 while (opti < args.length) { 1735 String arg = args[opti]; 1736 if ("-h".equals(arg)) { 1737 dumpHelp(pw); 1738 return; 1739 } else if ("-a".equals(arg)) { 1740 // Ignore, we always dump all. 1741 } else if (arg.length() > 0 && arg.charAt(0) == '-') { 1742 pw.println("Unknown option: " + arg); 1743 return; 1744 } else { 1745 break; 1746 } 1747 opti++; 1748 } 1749 if (opti < args.length) { 1750 String pkg = args[opti]; 1751 try { 1752 filterUid = getContext().getPackageManager().getPackageUid(pkg, 1753 PackageManager.MATCH_UNINSTALLED_PACKAGES); 1754 } catch (NameNotFoundException ignored) { 1755 pw.println("Invalid package: " + pkg); 1756 return; 1757 } 1758 } 1759 } 1760 1761 final int filterUidFinal = UserHandle.getAppId(filterUid); 1762 final long now = SystemClock.elapsedRealtime(); 1763 synchronized (mLock) { 1764 mConstants.dump(pw); 1765 pw.println(); 1766 pw.println("Started users: " + Arrays.toString(mStartedUsers)); 1767 pw.print("Registered "); 1768 pw.print(mJobs.size()); 1769 pw.println(" jobs:"); 1770 if (mJobs.size() > 0) { 1771 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs(); 1772 Collections.sort(jobs, new Comparator<JobStatus>() { 1773 @Override 1774 public int compare(JobStatus o1, JobStatus o2) { 1775 int uid1 = o1.getUid(); 1776 int uid2 = o2.getUid(); 1777 int id1 = o1.getJobId(); 1778 int id2 = o2.getJobId(); 1779 if (uid1 != uid2) { 1780 return uid1 < uid2 ? -1 : 1; 1781 } 1782 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0); 1783 } 1784 }); 1785 for (JobStatus job : jobs) { 1786 pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": "); 1787 pw.println(job.toShortStringExceptUniqueId()); 1788 1789 // Skip printing details if the caller requested a filter 1790 if (!job.shouldDump(filterUidFinal)) { 1791 continue; 1792 } 1793 1794 job.dump(pw, " ", true); 1795 pw.print(" Ready: "); 1796 pw.print(mHandler.isReadyToBeExecutedLocked(job)); 1797 pw.print(" (job="); 1798 pw.print(job.isReady()); 1799 pw.print(" pending="); 1800 pw.print(mPendingJobs.contains(job)); 1801 pw.print(" active="); 1802 pw.print(isCurrentlyActiveLocked(job)); 1803 pw.print(" user="); 1804 pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId())); 1805 pw.println(")"); 1806 } 1807 } else { 1808 pw.println(" None."); 1809 } 1810 for (int i=0; i<mControllers.size(); i++) { 1811 pw.println(); 1812 mControllers.get(i).dumpControllerStateLocked(pw, filterUidFinal); 1813 } 1814 pw.println(); 1815 pw.println("Uid priority overrides:"); 1816 for (int i=0; i< mUidPriorityOverride.size(); i++) { 1817 int uid = mUidPriorityOverride.keyAt(i); 1818 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) { 1819 pw.print(" "); pw.print(UserHandle.formatUid(uid)); 1820 pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i)); 1821 } 1822 } 1823 pw.println(); 1824 mJobPackageTracker.dump(pw, "", filterUidFinal); 1825 pw.println(); 1826 if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) { 1827 pw.println(); 1828 } 1829 pw.println("Pending queue:"); 1830 for (int i=0; i<mPendingJobs.size(); i++) { 1831 JobStatus job = mPendingJobs.get(i); 1832 pw.print(" Pending #"); pw.print(i); pw.print(": "); 1833 pw.println(job.toShortString()); 1834 job.dump(pw, " ", false); 1835 int priority = evaluateJobPriorityLocked(job); 1836 if (priority != JobInfo.PRIORITY_DEFAULT) { 1837 pw.print(" Evaluated priority: "); pw.println(priority); 1838 } 1839 pw.print(" Tag: "); pw.println(job.getTag()); 1840 } 1841 pw.println(); 1842 pw.println("Active jobs:"); 1843 for (int i=0; i<mActiveServices.size(); i++) { 1844 JobServiceContext jsc = mActiveServices.get(i); 1845 pw.print(" Slot #"); pw.print(i); pw.print(": "); 1846 if (jsc.getRunningJob() == null) { 1847 pw.println("inactive"); 1848 continue; 1849 } else { 1850 pw.println(jsc.getRunningJob().toShortString()); 1851 pw.print(" Running for: "); 1852 TimeUtils.formatDuration(now - jsc.getExecutionStartTimeElapsed(), pw); 1853 pw.print(", timeout at: "); 1854 TimeUtils.formatDuration(jsc.getTimeoutElapsed() - now, pw); 1855 pw.println(); 1856 jsc.getRunningJob().dump(pw, " ", false); 1857 int priority = evaluateJobPriorityLocked(jsc.getRunningJob()); 1858 if (priority != JobInfo.PRIORITY_DEFAULT) { 1859 pw.print(" Evaluated priority: "); pw.println(priority); 1860 } 1861 } 1862 } 1863 if (filterUid == -1) { 1864 pw.println(); 1865 pw.print("mReadyToRock="); pw.println(mReadyToRock); 1866 pw.print("mReportedActive="); pw.println(mReportedActive); 1867 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs); 1868 } 1869 } 1870 pw.println(); 1871 } 1872} 1873