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.ActivityManager; 32 import android.app.ActivityManagerNative; 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.content.BroadcastReceiver; 41 import android.content.ComponentName; 42 import android.content.ContentResolver; 43 import android.content.Context; 44 import android.content.Intent; 45 import android.content.IntentFilter; 46 import android.content.pm.IPackageManager; 47 import android.content.pm.PackageManager; 48 import android.content.pm.ServiceInfo; 49 import android.content.pm.PackageManager.NameNotFoundException; 50 import android.database.ContentObserver; 51 import android.net.Uri; 52 import android.os.BatteryStats; 53 import android.os.Binder; 54 import android.os.Handler; 55 import android.os.Looper; 56 import android.os.Message; 57 import android.os.Process; 58 import android.os.PowerManager; 59 import android.os.RemoteException; 60 import android.os.ResultReceiver; 61 import android.os.ServiceManager; 62 import android.os.SystemClock; 63 import android.os.UserHandle; 64 import android.provider.Settings; 65 import android.util.KeyValueListParser; 66 import android.util.Slog; 67 import android.util.SparseArray; 68 import android.util.SparseIntArray; 69 import android.util.TimeUtils; 70 71 import com.android.internal.app.IBatteryStats; 72 import com.android.internal.app.procstats.ProcessStats; 73 import com.android.internal.util.ArrayUtils; 74 import com.android.server.DeviceIdleController; 75 import com.android.server.LocalServices; 76 import com.android.server.job.JobStore.JobStatusFunctor; 77 import com.android.server.job.controllers.AppIdleController; 78 import com.android.server.job.controllers.BatteryController; 79 import com.android.server.job.controllers.ConnectivityController; 80 import com.android.server.job.controllers.ContentObserverController; 81 import com.android.server.job.controllers.DeviceIdleJobsController; 82 import com.android.server.job.controllers.IdleController; 83 import com.android.server.job.controllers.JobStatus; 84 import com.android.server.job.controllers.StateController; 85 import com.android.server.job.controllers.TimeController; 86 87 import 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 */ 101 public 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 // except those using the idle exemption flag. 667 for (int i=0; i<mActiveServices.size(); i++) { 668 JobServiceContext jsc = mActiveServices.get(i); 669 final JobStatus executing = jsc.getRunningJob(); 670 if (executing != null 671 && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) { 672 jsc.cancelExecutingJob(JobParameters.REASON_DEVICE_IDLE); 673 } 674 } 675 } else { 676 // When coming out of idle, allow thing to start back up. 677 if (mReadyToRock) { 678 if (mLocalDeviceIdleController != null) { 679 if (!mReportedActive) { 680 mReportedActive = true; 681 mLocalDeviceIdleController.setJobsActive(true); 682 } 683 } 684 } 685 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 686 } 687 } 688 } 689 690 void reportActive() { 691 // active is true if pending queue contains jobs OR some job is running. 692 boolean active = mPendingJobs.size() > 0; 693 if (mPendingJobs.size() <= 0) { 694 for (int i=0; i<mActiveServices.size(); i++) { 695 final JobServiceContext jsc = mActiveServices.get(i); 696 final JobStatus job = jsc.getRunningJob(); 697 if (job != null 698 && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0 699 && !job.dozeWhitelisted) { 700 // We will report active if we have a job running and it is not an exception 701 // due to being in the foreground or whitelisted. 702 active = true; 703 break; 704 } 705 } 706 } 707 708 if (mReportedActive != active) { 709 mReportedActive = active; 710 if (mLocalDeviceIdleController != null) { 711 mLocalDeviceIdleController.setJobsActive(active); 712 } 713 } 714 } 715 716 /** 717 * Initializes the system service. 718 * <p> 719 * Subclasses must define a single argument constructor that accepts the context 720 * and passes it to super. 721 * </p> 722 * 723 * @param context The system server context. 724 */ 725 public JobSchedulerService(Context context) { 726 super(context); 727 mHandler = new JobHandler(context.getMainLooper()); 728 mConstants = new Constants(mHandler); 729 mJobSchedulerStub = new JobSchedulerStub(); 730 mJobs = JobStore.initAndGet(this); 731 732 // Create the controllers. 733 mControllers = new ArrayList<StateController>(); 734 mControllers.add(ConnectivityController.get(this)); 735 mControllers.add(TimeController.get(this)); 736 mControllers.add(IdleController.get(this)); 737 mControllers.add(BatteryController.get(this)); 738 mControllers.add(AppIdleController.get(this)); 739 mControllers.add(ContentObserverController.get(this)); 740 mControllers.add(DeviceIdleJobsController.get(this)); 741 } 742 743 @Override 744 public void onStart() { 745 publishLocalService(JobSchedulerInternal.class, new LocalService()); 746 publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub); 747 } 748 749 @Override 750 public void onBootPhase(int phase) { 751 if (PHASE_SYSTEM_SERVICES_READY == phase) { 752 mConstants.start(getContext().getContentResolver()); 753 // Register br for package removals and user removals. 754 final IntentFilter filter = new IntentFilter(); 755 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 756 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 757 filter.addDataScheme("package"); 758 getContext().registerReceiverAsUser( 759 mBroadcastReceiver, UserHandle.ALL, filter, null, null); 760 final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED); 761 getContext().registerReceiverAsUser( 762 mBroadcastReceiver, UserHandle.ALL, userFilter, null, null); 763 mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE); 764 try { 765 ActivityManagerNative.getDefault().registerUidObserver(mUidObserver, 766 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE 767 | ActivityManager.UID_OBSERVER_IDLE); 768 } catch (RemoteException e) { 769 // ignored; both services live in system_server 770 } 771 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { 772 synchronized (mLock) { 773 // Let's go! 774 mReadyToRock = true; 775 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService( 776 BatteryStats.SERVICE_NAME)); 777 mLocalDeviceIdleController 778 = LocalServices.getService(DeviceIdleController.LocalService.class); 779 // Create the "runners". 780 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) { 781 mActiveServices.add( 782 new JobServiceContext(this, mBatteryStats, mJobPackageTracker, 783 getContext().getMainLooper())); 784 } 785 // Attach jobs to their controllers. 786 mJobs.forEachJob(new JobStatusFunctor() { 787 @Override 788 public void process(JobStatus job) { 789 for (int controller = 0; controller < mControllers.size(); controller++) { 790 final StateController sc = mControllers.get(controller); 791 sc.maybeStartTrackingJobLocked(job, null); 792 } 793 } 794 }); 795 // GO GO GO! 796 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 797 } 798 } 799 } 800 801 /** 802 * Called when we have a job status object that we need to insert in our 803 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know 804 * about. 805 */ 806 private void startTrackingJob(JobStatus jobStatus, JobStatus lastJob) { 807 synchronized (mLock) { 808 final boolean update = mJobs.add(jobStatus); 809 if (mReadyToRock) { 810 for (int i = 0; i < mControllers.size(); i++) { 811 StateController controller = mControllers.get(i); 812 if (update) { 813 controller.maybeStopTrackingJobLocked(jobStatus, null, true); 814 } 815 controller.maybeStartTrackingJobLocked(jobStatus, lastJob); 816 } 817 } 818 } 819 } 820 821 /** 822 * Called when we want to remove a JobStatus object that we've finished executing. Returns the 823 * object removed. 824 */ 825 private boolean stopTrackingJob(JobStatus jobStatus, JobStatus incomingJob, 826 boolean writeBack) { 827 synchronized (mLock) { 828 // Remove from store as well as controllers. 829 final boolean removed = mJobs.remove(jobStatus, writeBack); 830 if (removed && mReadyToRock) { 831 for (int i=0; i<mControllers.size(); i++) { 832 StateController controller = mControllers.get(i); 833 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false); 834 } 835 } 836 return removed; 837 } 838 } 839 840 private boolean stopJobOnServiceContextLocked(JobStatus job, int reason) { 841 for (int i=0; i<mActiveServices.size(); i++) { 842 JobServiceContext jsc = mActiveServices.get(i); 843 final JobStatus executing = jsc.getRunningJob(); 844 if (executing != null && executing.matches(job.getUid(), job.getJobId())) { 845 jsc.cancelExecutingJob(reason); 846 return true; 847 } 848 } 849 return false; 850 } 851 852 /** 853 * @param job JobStatus we are querying against. 854 * @return Whether or not the job represented by the status object is currently being run or 855 * is pending. 856 */ 857 private boolean isCurrentlyActiveLocked(JobStatus job) { 858 for (int i=0; i<mActiveServices.size(); i++) { 859 JobServiceContext serviceContext = mActiveServices.get(i); 860 final JobStatus running = serviceContext.getRunningJob(); 861 if (running != null && running.matches(job.getUid(), job.getJobId())) { 862 return true; 863 } 864 } 865 return false; 866 } 867 868 void noteJobsPending(List<JobStatus> jobs) { 869 for (int i = jobs.size() - 1; i >= 0; i--) { 870 JobStatus job = jobs.get(i); 871 mJobPackageTracker.notePending(job); 872 } 873 } 874 875 void noteJobsNonpending(List<JobStatus> jobs) { 876 for (int i = jobs.size() - 1; i >= 0; i--) { 877 JobStatus job = jobs.get(i); 878 mJobPackageTracker.noteNonpending(job); 879 } 880 } 881 882 /** 883 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to 884 * specify an override deadline on a failed job (the failed job will run even though it's not 885 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any 886 * ready job with {@link JobStatus#numFailures} > 0 will be executed. 887 * 888 * @param failureToReschedule Provided job status that we will reschedule. 889 * @return A newly instantiated JobStatus with the same constraints as the last job except 890 * with adjusted timing constraints. 891 * 892 * @see JobHandler#maybeQueueReadyJobsForExecutionLockedH 893 */ 894 private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) { 895 final long elapsedNowMillis = SystemClock.elapsedRealtime(); 896 final JobInfo job = failureToReschedule.getJob(); 897 898 final long initialBackoffMillis = job.getInitialBackoffMillis(); 899 final int backoffAttempts = failureToReschedule.getNumFailures() + 1; 900 long delayMillis; 901 902 switch (job.getBackoffPolicy()) { 903 case JobInfo.BACKOFF_POLICY_LINEAR: 904 delayMillis = initialBackoffMillis * backoffAttempts; 905 break; 906 default: 907 if (DEBUG) { 908 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential."); 909 } 910 case JobInfo.BACKOFF_POLICY_EXPONENTIAL: 911 delayMillis = 912 (long) Math.scalb(initialBackoffMillis, backoffAttempts - 1); 913 break; 914 } 915 delayMillis = 916 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS); 917 JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis, 918 JobStatus.NO_LATEST_RUNTIME, backoffAttempts); 919 for (int ic=0; ic<mControllers.size(); ic++) { 920 StateController controller = mControllers.get(ic); 921 controller.rescheduleForFailure(newJob, failureToReschedule); 922 } 923 return newJob; 924 } 925 926 /** 927 * Called after a periodic has executed so we can reschedule it. We take the last execution 928 * time of the job to be the time of completion (i.e. the time at which this function is 929 * called). 930 * This could be inaccurate b/c the job can run for as long as 931 * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead 932 * to underscheduling at least, rather than if we had taken the last execution time to be the 933 * start of the execution. 934 * @return A new job representing the execution criteria for this instantiation of the 935 * recurring job. 936 */ 937 private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) { 938 final long elapsedNow = SystemClock.elapsedRealtime(); 939 // Compute how much of the period is remaining. 940 long runEarly = 0L; 941 942 // If this periodic was rescheduled it won't have a deadline. 943 if (periodicToReschedule.hasDeadlineConstraint()) { 944 runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L); 945 } 946 long flex = periodicToReschedule.getJob().getFlexMillis(); 947 long period = periodicToReschedule.getJob().getIntervalMillis(); 948 long newLatestRuntimeElapsed = elapsedNow + runEarly + period; 949 long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex; 950 951 if (DEBUG) { 952 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" + 953 newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s"); 954 } 955 return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed, 956 newLatestRuntimeElapsed, 0 /* backoffAttempt */); 957 } 958 959 // JobCompletedListener implementations. 960 961 /** 962 * A job just finished executing. We fetch the 963 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on 964 * whether we want to reschedule we readd it to the controllers. 965 * @param jobStatus Completed job. 966 * @param needsReschedule Whether the implementing class should reschedule this job. 967 */ 968 @Override 969 public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule) { 970 if (DEBUG) { 971 Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule); 972 } 973 // Do not write back immediately if this is a periodic job. The job may get lost if system 974 // shuts down before it is added back. 975 if (!stopTrackingJob(jobStatus, null, !jobStatus.getJob().isPeriodic())) { 976 if (DEBUG) { 977 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?"); 978 } 979 // We still want to check for jobs to execute, because this job may have 980 // scheduled a new job under the same job id, and now we can run it. 981 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget(); 982 return; 983 } 984 // Note: there is a small window of time in here where, when rescheduling a job, 985 // we will stop monitoring its content providers. This should be fixed by stopping 986 // the old job after scheduling the new one, but since we have no lock held here 987 // that may cause ordering problems if the app removes jobStatus while in here. 988 if (needsReschedule) { 989 JobStatus rescheduled = getRescheduleJobForFailure(jobStatus); 990 startTrackingJob(rescheduled, jobStatus); 991 } else if (jobStatus.getJob().isPeriodic()) { 992 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus); 993 startTrackingJob(rescheduledPeriodic, jobStatus); 994 } 995 reportActive(); 996 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget(); 997 } 998 999 // StateChangedListener implementations. 1000 1001 /** 1002 * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that 1003 * some controller's state has changed, so as to run through the list of jobs and start/stop 1004 * any that are eligible. 1005 */ 1006 @Override 1007 public void onControllerStateChanged() { 1008 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 1009 } 1010 1011 @Override 1012 public void onRunJobNow(JobStatus jobStatus) { 1013 mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget(); 1014 } 1015 1016 private class JobHandler extends Handler { 1017 1018 public JobHandler(Looper looper) { 1019 super(looper); 1020 } 1021 1022 @Override 1023 public void handleMessage(Message message) { 1024 synchronized (mLock) { 1025 if (!mReadyToRock) { 1026 return; 1027 } 1028 } 1029 switch (message.what) { 1030 case MSG_JOB_EXPIRED: 1031 synchronized (mLock) { 1032 JobStatus runNow = (JobStatus) message.obj; 1033 // runNow can be null, which is a controller's way of indicating that its 1034 // state is such that all ready jobs should be run immediately. 1035 if (runNow != null && !mPendingJobs.contains(runNow) 1036 && mJobs.containsJob(runNow)) { 1037 mJobPackageTracker.notePending(runNow); 1038 mPendingJobs.add(runNow); 1039 } 1040 queueReadyJobsForExecutionLockedH(); 1041 } 1042 break; 1043 case MSG_CHECK_JOB: 1044 synchronized (mLock) { 1045 if (mReportedActive) { 1046 // if jobs are currently being run, queue all ready jobs for execution. 1047 queueReadyJobsForExecutionLockedH(); 1048 } else { 1049 // Check the list of jobs and run some of them if we feel inclined. 1050 maybeQueueReadyJobsForExecutionLockedH(); 1051 } 1052 } 1053 break; 1054 case MSG_CHECK_JOB_GREEDY: 1055 synchronized (mLock) { 1056 queueReadyJobsForExecutionLockedH(); 1057 } 1058 break; 1059 case MSG_STOP_JOB: 1060 cancelJobImpl((JobStatus)message.obj, null); 1061 break; 1062 } 1063 maybeRunPendingJobsH(); 1064 // Don't remove JOB_EXPIRED in case one came along while processing the queue. 1065 removeMessages(MSG_CHECK_JOB); 1066 } 1067 1068 /** 1069 * Run through list of jobs and execute all possible - at least one is expired so we do 1070 * as many as we can. 1071 */ 1072 private void queueReadyJobsForExecutionLockedH() { 1073 if (DEBUG) { 1074 Slog.d(TAG, "queuing all ready jobs for execution:"); 1075 } 1076 noteJobsNonpending(mPendingJobs); 1077 mPendingJobs.clear(); 1078 mJobs.forEachJob(mReadyQueueFunctor); 1079 mReadyQueueFunctor.postProcess(); 1080 1081 if (DEBUG) { 1082 final int queuedJobs = mPendingJobs.size(); 1083 if (queuedJobs == 0) { 1084 Slog.d(TAG, "No jobs pending."); 1085 } else { 1086 Slog.d(TAG, queuedJobs + " jobs queued."); 1087 } 1088 } 1089 } 1090 1091 class ReadyJobQueueFunctor implements JobStatusFunctor { 1092 ArrayList<JobStatus> newReadyJobs; 1093 1094 @Override 1095 public void process(JobStatus job) { 1096 if (isReadyToBeExecutedLocked(job)) { 1097 if (DEBUG) { 1098 Slog.d(TAG, " queued " + job.toShortString()); 1099 } 1100 if (newReadyJobs == null) { 1101 newReadyJobs = new ArrayList<JobStatus>(); 1102 } 1103 newReadyJobs.add(job); 1104 } else if (areJobConstraintsNotSatisfiedLocked(job)) { 1105 stopJobOnServiceContextLocked(job, 1106 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED); 1107 } 1108 } 1109 1110 public void postProcess() { 1111 if (newReadyJobs != null) { 1112 noteJobsPending(newReadyJobs); 1113 mPendingJobs.addAll(newReadyJobs); 1114 } 1115 newReadyJobs = null; 1116 } 1117 } 1118 private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor(); 1119 1120 /** 1121 * The state of at least one job has changed. Here is where we could enforce various 1122 * policies on when we want to execute jobs. 1123 * Right now the policy is such: 1124 * If >1 of the ready jobs is idle mode we send all of them off 1125 * if more than 2 network connectivity jobs are ready we send them all off. 1126 * If more than 4 jobs total are ready we send them all off. 1127 * TODO: It would be nice to consolidate these sort of high-level policies somewhere. 1128 */ 1129 class MaybeReadyJobQueueFunctor implements JobStatusFunctor { 1130 int chargingCount; 1131 int idleCount; 1132 int backoffCount; 1133 int connectivityCount; 1134 int contentCount; 1135 List<JobStatus> runnableJobs; 1136 1137 public MaybeReadyJobQueueFunctor() { 1138 reset(); 1139 } 1140 1141 // Functor method invoked for each job via JobStore.forEachJob() 1142 @Override 1143 public void process(JobStatus job) { 1144 if (isReadyToBeExecutedLocked(job)) { 1145 try { 1146 if (ActivityManagerNative.getDefault().getAppStartMode(job.getUid(), 1147 job.getJob().getService().getPackageName()) 1148 == ActivityManager.APP_START_MODE_DISABLED) { 1149 Slog.w(TAG, "Aborting job " + job.getUid() + ":" 1150 + job.getJob().toString() + " -- package not allowed to start"); 1151 mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget(); 1152 return; 1153 } 1154 } catch (RemoteException e) { 1155 } 1156 if (job.getNumFailures() > 0) { 1157 backoffCount++; 1158 } 1159 if (job.hasIdleConstraint()) { 1160 idleCount++; 1161 } 1162 if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint() 1163 || job.hasNotRoamingConstraint()) { 1164 connectivityCount++; 1165 } 1166 if (job.hasChargingConstraint()) { 1167 chargingCount++; 1168 } 1169 if (job.hasContentTriggerConstraint()) { 1170 contentCount++; 1171 } 1172 if (runnableJobs == null) { 1173 runnableJobs = new ArrayList<>(); 1174 } 1175 runnableJobs.add(job); 1176 } else if (areJobConstraintsNotSatisfiedLocked(job)) { 1177 stopJobOnServiceContextLocked(job, 1178 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED); 1179 } 1180 } 1181 1182 public void postProcess() { 1183 if (backoffCount > 0 || 1184 idleCount >= mConstants.MIN_IDLE_COUNT || 1185 connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT || 1186 chargingCount >= mConstants.MIN_CHARGING_COUNT || 1187 contentCount >= mConstants.MIN_CONTENT_COUNT || 1188 (runnableJobs != null 1189 && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) { 1190 if (DEBUG) { 1191 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Running jobs."); 1192 } 1193 noteJobsPending(runnableJobs); 1194 mPendingJobs.addAll(runnableJobs); 1195 } else { 1196 if (DEBUG) { 1197 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Not running anything."); 1198 } 1199 } 1200 1201 // Be ready for next time 1202 reset(); 1203 } 1204 1205 private void reset() { 1206 chargingCount = 0; 1207 idleCount = 0; 1208 backoffCount = 0; 1209 connectivityCount = 0; 1210 contentCount = 0; 1211 runnableJobs = null; 1212 } 1213 } 1214 private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor(); 1215 1216 private void maybeQueueReadyJobsForExecutionLockedH() { 1217 if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs..."); 1218 1219 noteJobsNonpending(mPendingJobs); 1220 mPendingJobs.clear(); 1221 mJobs.forEachJob(mMaybeQueueFunctor); 1222 mMaybeQueueFunctor.postProcess(); 1223 } 1224 1225 /** 1226 * Criteria for moving a job into the pending queue: 1227 * - It's ready. 1228 * - It's not pending. 1229 * - It's not already running on a JSC. 1230 * - The user that requested the job is running. 1231 * - The component is enabled and runnable. 1232 */ 1233 private boolean isReadyToBeExecutedLocked(JobStatus job) { 1234 final boolean jobReady = job.isReady(); 1235 final boolean jobPending = mPendingJobs.contains(job); 1236 final boolean jobActive = isCurrentlyActiveLocked(job); 1237 1238 final int userId = job.getUserId(); 1239 final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId); 1240 final boolean componentPresent; 1241 try { 1242 componentPresent = (AppGlobals.getPackageManager().getServiceInfo( 1243 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING, 1244 userId) != null); 1245 } catch (RemoteException e) { 1246 throw e.rethrowAsRuntimeException(); 1247 } 1248 1249 if (DEBUG) { 1250 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString() 1251 + " ready=" + jobReady + " pending=" + jobPending 1252 + " active=" + jobActive + " userStarted=" + userStarted 1253 + " componentPresent=" + componentPresent); 1254 } 1255 return userStarted && componentPresent && jobReady && !jobPending && !jobActive; 1256 } 1257 1258 /** 1259 * Criteria for cancelling an active job: 1260 * - It's not ready 1261 * - It's running on a JSC. 1262 */ 1263 private boolean areJobConstraintsNotSatisfiedLocked(JobStatus job) { 1264 return !job.isReady() && isCurrentlyActiveLocked(job); 1265 } 1266 1267 /** 1268 * Reconcile jobs in the pending queue against available execution contexts. 1269 * A controller can force a job into the pending queue even if it's already running, but 1270 * here is where we decide whether to actually execute it. 1271 */ 1272 private void maybeRunPendingJobsH() { 1273 synchronized (mLock) { 1274 if (DEBUG) { 1275 Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs."); 1276 } 1277 assignJobsToContextsLocked(); 1278 reportActive(); 1279 } 1280 } 1281 } 1282 1283 private int adjustJobPriority(int curPriority, JobStatus job) { 1284 if (curPriority < JobInfo.PRIORITY_TOP_APP) { 1285 float factor = mJobPackageTracker.getLoadFactor(job); 1286 if (factor >= mConstants.HEAVY_USE_FACTOR) { 1287 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING; 1288 } else if (factor >= mConstants.MODERATE_USE_FACTOR) { 1289 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING; 1290 } 1291 } 1292 return curPriority; 1293 } 1294 1295 private int evaluateJobPriorityLocked(JobStatus job) { 1296 int priority = job.getPriority(); 1297 if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) { 1298 return adjustJobPriority(priority, job); 1299 } 1300 int override = mUidPriorityOverride.get(job.getSourceUid(), 0); 1301 if (override != 0) { 1302 return adjustJobPriority(override, job); 1303 } 1304 return adjustJobPriority(priority, job); 1305 } 1306 1307 /** 1308 * Takes jobs from pending queue and runs them on available contexts. 1309 * If no contexts are available, preempts lower priority jobs to 1310 * run higher priority ones. 1311 * Lock on mJobs before calling this function. 1312 */ 1313 private void assignJobsToContextsLocked() { 1314 if (DEBUG) { 1315 Slog.d(TAG, printPendingQueue()); 1316 } 1317 1318 int memLevel; 1319 try { 1320 memLevel = ActivityManagerNative.getDefault().getMemoryTrimLevel(); 1321 } catch (RemoteException e) { 1322 memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL; 1323 } 1324 switch (memLevel) { 1325 case ProcessStats.ADJ_MEM_FACTOR_MODERATE: 1326 mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT; 1327 break; 1328 case ProcessStats.ADJ_MEM_FACTOR_LOW: 1329 mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT; 1330 break; 1331 case ProcessStats.ADJ_MEM_FACTOR_CRITICAL: 1332 mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT; 1333 break; 1334 default: 1335 mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT; 1336 break; 1337 } 1338 1339 JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap; 1340 boolean[] act = mTmpAssignAct; 1341 int[] preferredUidForContext = mTmpAssignPreferredUidForContext; 1342 int numActive = 0; 1343 int numForeground = 0; 1344 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { 1345 final JobServiceContext js = mActiveServices.get(i); 1346 final JobStatus status = js.getRunningJob(); 1347 if ((contextIdToJobMap[i] = status) != null) { 1348 numActive++; 1349 if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) { 1350 numForeground++; 1351 } 1352 } 1353 act[i] = false; 1354 preferredUidForContext[i] = js.getPreferredUid(); 1355 } 1356 if (DEBUG) { 1357 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial")); 1358 } 1359 for (int i=0; i<mPendingJobs.size(); i++) { 1360 JobStatus nextPending = mPendingJobs.get(i); 1361 1362 // If job is already running, go to next job. 1363 int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap); 1364 if (jobRunningContext != -1) { 1365 continue; 1366 } 1367 1368 final int priority = evaluateJobPriorityLocked(nextPending); 1369 nextPending.lastEvaluatedPriority = priority; 1370 1371 // Find a context for nextPending. The context should be available OR 1372 // it should have lowest priority among all running jobs 1373 // (sharing the same Uid as nextPending) 1374 int minPriority = Integer.MAX_VALUE; 1375 int minPriorityContextId = -1; 1376 for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) { 1377 JobStatus job = contextIdToJobMap[j]; 1378 int preferredUid = preferredUidForContext[j]; 1379 if (job == null) { 1380 if ((numActive < mMaxActiveJobs || 1381 (priority >= JobInfo.PRIORITY_TOP_APP && 1382 numForeground < mConstants.FG_JOB_COUNT)) && 1383 (preferredUid == nextPending.getUid() || 1384 preferredUid == JobServiceContext.NO_PREFERRED_UID)) { 1385 // This slot is free, and we haven't yet hit the limit on 1386 // concurrent jobs... we can just throw the job in to here. 1387 minPriorityContextId = j; 1388 break; 1389 } 1390 // No job on this context, but nextPending can't run here because 1391 // the context has a preferred Uid or we have reached the limit on 1392 // concurrent jobs. 1393 continue; 1394 } 1395 if (job.getUid() != nextPending.getUid()) { 1396 continue; 1397 } 1398 if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) { 1399 continue; 1400 } 1401 if (minPriority > nextPending.lastEvaluatedPriority) { 1402 minPriority = nextPending.lastEvaluatedPriority; 1403 minPriorityContextId = j; 1404 } 1405 } 1406 if (minPriorityContextId != -1) { 1407 contextIdToJobMap[minPriorityContextId] = nextPending; 1408 act[minPriorityContextId] = true; 1409 numActive++; 1410 if (priority >= JobInfo.PRIORITY_TOP_APP) { 1411 numForeground++; 1412 } 1413 } 1414 } 1415 if (DEBUG) { 1416 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final")); 1417 } 1418 mJobPackageTracker.noteConcurrency(numActive, numForeground); 1419 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { 1420 boolean preservePreferredUid = false; 1421 if (act[i]) { 1422 JobStatus js = mActiveServices.get(i).getRunningJob(); 1423 if (js != null) { 1424 if (DEBUG) { 1425 Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJob()); 1426 } 1427 // preferredUid will be set to uid of currently running job. 1428 mActiveServices.get(i).preemptExecutingJob(); 1429 preservePreferredUid = true; 1430 } else { 1431 final JobStatus pendingJob = contextIdToJobMap[i]; 1432 if (DEBUG) { 1433 Slog.d(TAG, "About to run job on context " 1434 + String.valueOf(i) + ", job: " + pendingJob); 1435 } 1436 for (int ic=0; ic<mControllers.size(); ic++) { 1437 mControllers.get(ic).prepareForExecutionLocked(pendingJob); 1438 } 1439 if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) { 1440 Slog.d(TAG, "Error executing " + pendingJob); 1441 } 1442 if (mPendingJobs.remove(pendingJob)) { 1443 mJobPackageTracker.noteNonpending(pendingJob); 1444 } 1445 } 1446 } 1447 if (!preservePreferredUid) { 1448 mActiveServices.get(i).clearPreferredUid(); 1449 } 1450 } 1451 } 1452 1453 int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) { 1454 for (int i=0; i<map.length; i++) { 1455 if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) { 1456 return i; 1457 } 1458 } 1459 return -1; 1460 } 1461 1462 final class LocalService implements JobSchedulerInternal { 1463 1464 /** 1465 * Returns a list of all pending jobs. A running job is not considered pending. Periodic 1466 * jobs are always considered pending. 1467 */ 1468 @Override 1469 public List<JobInfo> getSystemScheduledPendingJobs() { 1470 synchronized (mLock) { 1471 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>(); 1472 mJobs.forEachJob(Process.SYSTEM_UID, new JobStatusFunctor() { 1473 @Override 1474 public void process(JobStatus job) { 1475 if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) { 1476 pendingJobs.add(job.getJob()); 1477 } 1478 } 1479 }); 1480 return pendingJobs; 1481 } 1482 } 1483 } 1484 1485 /** 1486 * Binder stub trampoline implementation 1487 */ 1488 final class JobSchedulerStub extends IJobScheduler.Stub { 1489 /** Cache determination of whether a given app can persist jobs 1490 * key is uid of the calling app; value is undetermined/true/false 1491 */ 1492 private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>(); 1493 1494 // Enforce that only the app itself (or shared uid participant) can schedule a 1495 // job that runs one of the app's services, as well as verifying that the 1496 // named service properly requires the BIND_JOB_SERVICE permission 1497 private void enforceValidJobRequest(int uid, JobInfo job) { 1498 final IPackageManager pm = AppGlobals.getPackageManager(); 1499 final ComponentName service = job.getService(); 1500 try { 1501 ServiceInfo si = pm.getServiceInfo(service, 1502 PackageManager.MATCH_DIRECT_BOOT_AWARE 1503 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 1504 UserHandle.getUserId(uid)); 1505 if (si == null) { 1506 throw new IllegalArgumentException("No such service " + service); 1507 } 1508 if (si.applicationInfo.uid != uid) { 1509 throw new IllegalArgumentException("uid " + uid + 1510 " cannot schedule job in " + service.getPackageName()); 1511 } 1512 if (!JobService.PERMISSION_BIND.equals(si.permission)) { 1513 throw new IllegalArgumentException("Scheduled service " + service 1514 + " does not require android.permission.BIND_JOB_SERVICE permission"); 1515 } 1516 } catch (RemoteException e) { 1517 // Can't happen; the Package Manager is in this same process 1518 } 1519 } 1520 1521 private boolean canPersistJobs(int pid, int uid) { 1522 // If we get this far we're good to go; all we need to do now is check 1523 // whether the app is allowed to persist its scheduled work. 1524 final boolean canPersist; 1525 synchronized (mPersistCache) { 1526 Boolean cached = mPersistCache.get(uid); 1527 if (cached != null) { 1528 canPersist = cached.booleanValue(); 1529 } else { 1530 // Persisting jobs is tantamount to running at boot, so we permit 1531 // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED 1532 // permission 1533 int result = getContext().checkPermission( 1534 android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid); 1535 canPersist = (result == PackageManager.PERMISSION_GRANTED); 1536 mPersistCache.put(uid, canPersist); 1537 } 1538 } 1539 return canPersist; 1540 } 1541 1542 // IJobScheduler implementation 1543 @Override 1544 public int schedule(JobInfo job) throws RemoteException { 1545 if (DEBUG) { 1546 Slog.d(TAG, "Scheduling job: " + job.toString()); 1547 } 1548 final int pid = Binder.getCallingPid(); 1549 final int uid = Binder.getCallingUid(); 1550 1551 enforceValidJobRequest(uid, job); 1552 if (job.isPersisted()) { 1553 if (!canPersistJobs(pid, uid)) { 1554 throw new IllegalArgumentException("Error: requested job be persisted without" 1555 + " holding RECEIVE_BOOT_COMPLETED permission."); 1556 } 1557 } 1558 1559 long ident = Binder.clearCallingIdentity(); 1560 try { 1561 return JobSchedulerService.this.schedule(job, uid); 1562 } finally { 1563 Binder.restoreCallingIdentity(ident); 1564 } 1565 } 1566 1567 @Override 1568 public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag) 1569 throws RemoteException { 1570 final int callerUid = Binder.getCallingUid(); 1571 if (DEBUG) { 1572 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString() 1573 + " on behalf of " + packageName); 1574 } 1575 1576 if (packageName == null) { 1577 throw new NullPointerException("Must specify a package for scheduleAsPackage()"); 1578 } 1579 1580 int mayScheduleForOthers = getContext().checkCallingOrSelfPermission( 1581 android.Manifest.permission.UPDATE_DEVICE_STATS); 1582 if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) { 1583 throw new SecurityException("Caller uid " + callerUid 1584 + " not permitted to schedule jobs for other apps"); 1585 } 1586 1587 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) { 1588 getContext().enforceCallingOrSelfPermission( 1589 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG); 1590 } 1591 1592 long ident = Binder.clearCallingIdentity(); 1593 try { 1594 return JobSchedulerService.this.scheduleAsPackage(job, callerUid, 1595 packageName, userId, tag); 1596 } finally { 1597 Binder.restoreCallingIdentity(ident); 1598 } 1599 } 1600 1601 @Override 1602 public List<JobInfo> getAllPendingJobs() throws RemoteException { 1603 final int uid = Binder.getCallingUid(); 1604 1605 long ident = Binder.clearCallingIdentity(); 1606 try { 1607 return JobSchedulerService.this.getPendingJobs(uid); 1608 } finally { 1609 Binder.restoreCallingIdentity(ident); 1610 } 1611 } 1612 1613 @Override 1614 public JobInfo getPendingJob(int jobId) throws RemoteException { 1615 final int uid = Binder.getCallingUid(); 1616 1617 long ident = Binder.clearCallingIdentity(); 1618 try { 1619 return JobSchedulerService.this.getPendingJob(uid, jobId); 1620 } finally { 1621 Binder.restoreCallingIdentity(ident); 1622 } 1623 } 1624 1625 @Override 1626 public void cancelAll() throws RemoteException { 1627 final int uid = Binder.getCallingUid(); 1628 1629 long ident = Binder.clearCallingIdentity(); 1630 try { 1631 JobSchedulerService.this.cancelJobsForUid(uid, true); 1632 } finally { 1633 Binder.restoreCallingIdentity(ident); 1634 } 1635 } 1636 1637 @Override 1638 public void cancel(int jobId) throws RemoteException { 1639 final int uid = Binder.getCallingUid(); 1640 1641 long ident = Binder.clearCallingIdentity(); 1642 try { 1643 JobSchedulerService.this.cancelJob(uid, jobId); 1644 } finally { 1645 Binder.restoreCallingIdentity(ident); 1646 } 1647 } 1648 1649 /** 1650 * "dumpsys" infrastructure 1651 */ 1652 @Override 1653 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1654 getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 1655 1656 long identityToken = Binder.clearCallingIdentity(); 1657 try { 1658 JobSchedulerService.this.dumpInternal(pw, args); 1659 } finally { 1660 Binder.restoreCallingIdentity(identityToken); 1661 } 1662 } 1663 1664 @Override 1665 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 1666 String[] args, ResultReceiver resultReceiver) throws RemoteException { 1667 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec( 1668 this, in, out, err, args, resultReceiver); 1669 } 1670 }; 1671 1672 // Shell command infrastructure: run the given job immediately 1673 int executeRunCommand(String pkgName, int userId, int jobId, boolean force) { 1674 if (DEBUG) { 1675 Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId 1676 + " " + jobId + " f=" + force); 1677 } 1678 1679 try { 1680 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, userId); 1681 if (uid < 0) { 1682 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE; 1683 } 1684 1685 synchronized (mLock) { 1686 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId); 1687 if (js == null) { 1688 return JobSchedulerShellCommand.CMD_ERR_NO_JOB; 1689 } 1690 1691 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT; 1692 if (!js.isConstraintsSatisfied()) { 1693 js.overrideState = 0; 1694 return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS; 1695 } 1696 1697 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget(); 1698 } 1699 } catch (RemoteException e) { 1700 // can't happen 1701 } 1702 return 0; 1703 } 1704 1705 private String printContextIdToJobMap(JobStatus[] map, String initial) { 1706 StringBuilder s = new StringBuilder(initial + ": "); 1707 for (int i=0; i<map.length; i++) { 1708 s.append("(") 1709 .append(map[i] == null? -1: map[i].getJobId()) 1710 .append(map[i] == null? -1: map[i].getUid()) 1711 .append(")" ); 1712 } 1713 return s.toString(); 1714 } 1715 1716 private String printPendingQueue() { 1717 StringBuilder s = new StringBuilder("Pending queue: "); 1718 Iterator<JobStatus> it = mPendingJobs.iterator(); 1719 while (it.hasNext()) { 1720 JobStatus js = it.next(); 1721 s.append("(") 1722 .append(js.getJob().getId()) 1723 .append(", ") 1724 .append(js.getUid()) 1725 .append(") "); 1726 } 1727 return s.toString(); 1728 } 1729 1730 static void dumpHelp(PrintWriter pw) { 1731 pw.println("Job Scheduler (jobscheduler) dump options:"); 1732 pw.println(" [-h] [package] ..."); 1733 pw.println(" -h: print this help"); 1734 pw.println(" [package] is an optional package name to limit the output to."); 1735 } 1736 1737 void dumpInternal(final PrintWriter pw, String[] args) { 1738 int filterUid = -1; 1739 if (!ArrayUtils.isEmpty(args)) { 1740 int opti = 0; 1741 while (opti < args.length) { 1742 String arg = args[opti]; 1743 if ("-h".equals(arg)) { 1744 dumpHelp(pw); 1745 return; 1746 } else if ("-a".equals(arg)) { 1747 // Ignore, we always dump all. 1748 } else if (arg.length() > 0 && arg.charAt(0) == '-') { 1749 pw.println("Unknown option: " + arg); 1750 return; 1751 } else { 1752 break; 1753 } 1754 opti++; 1755 } 1756 if (opti < args.length) { 1757 String pkg = args[opti]; 1758 try { 1759 filterUid = getContext().getPackageManager().getPackageUid(pkg, 1760 PackageManager.MATCH_UNINSTALLED_PACKAGES); 1761 } catch (NameNotFoundException ignored) { 1762 pw.println("Invalid package: " + pkg); 1763 return; 1764 } 1765 } 1766 } 1767 1768 final int filterUidFinal = UserHandle.getAppId(filterUid); 1769 final long now = SystemClock.elapsedRealtime(); 1770 synchronized (mLock) { 1771 mConstants.dump(pw); 1772 pw.println(); 1773 pw.println("Started users: " + Arrays.toString(mStartedUsers)); 1774 pw.print("Registered "); 1775 pw.print(mJobs.size()); 1776 pw.println(" jobs:"); 1777 if (mJobs.size() > 0) { 1778 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs(); 1779 Collections.sort(jobs, new Comparator<JobStatus>() { 1780 @Override 1781 public int compare(JobStatus o1, JobStatus o2) { 1782 int uid1 = o1.getUid(); 1783 int uid2 = o2.getUid(); 1784 int id1 = o1.getJobId(); 1785 int id2 = o2.getJobId(); 1786 if (uid1 != uid2) { 1787 return uid1 < uid2 ? -1 : 1; 1788 } 1789 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0); 1790 } 1791 }); 1792 for (JobStatus job : jobs) { 1793 pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": "); 1794 pw.println(job.toShortStringExceptUniqueId()); 1795 1796 // Skip printing details if the caller requested a filter 1797 if (!job.shouldDump(filterUidFinal)) { 1798 continue; 1799 } 1800 1801 job.dump(pw, " ", true); 1802 pw.print(" Ready: "); 1803 pw.print(mHandler.isReadyToBeExecutedLocked(job)); 1804 pw.print(" (job="); 1805 pw.print(job.isReady()); 1806 pw.print(" pending="); 1807 pw.print(mPendingJobs.contains(job)); 1808 pw.print(" active="); 1809 pw.print(isCurrentlyActiveLocked(job)); 1810 pw.print(" user="); 1811 pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId())); 1812 pw.println(")"); 1813 } 1814 } else { 1815 pw.println(" None."); 1816 } 1817 for (int i=0; i<mControllers.size(); i++) { 1818 pw.println(); 1819 mControllers.get(i).dumpControllerStateLocked(pw, filterUidFinal); 1820 } 1821 pw.println(); 1822 pw.println("Uid priority overrides:"); 1823 for (int i=0; i< mUidPriorityOverride.size(); i++) { 1824 int uid = mUidPriorityOverride.keyAt(i); 1825 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) { 1826 pw.print(" "); pw.print(UserHandle.formatUid(uid)); 1827 pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i)); 1828 } 1829 } 1830 pw.println(); 1831 mJobPackageTracker.dump(pw, "", filterUidFinal); 1832 pw.println(); 1833 if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) { 1834 pw.println(); 1835 } 1836 pw.println("Pending queue:"); 1837 for (int i=0; i<mPendingJobs.size(); i++) { 1838 JobStatus job = mPendingJobs.get(i); 1839 pw.print(" Pending #"); pw.print(i); pw.print(": "); 1840 pw.println(job.toShortString()); 1841 job.dump(pw, " ", false); 1842 int priority = evaluateJobPriorityLocked(job); 1843 if (priority != JobInfo.PRIORITY_DEFAULT) { 1844 pw.print(" Evaluated priority: "); pw.println(priority); 1845 } 1846 pw.print(" Tag: "); pw.println(job.getTag()); 1847 } 1848 pw.println(); 1849 pw.println("Active jobs:"); 1850 for (int i=0; i<mActiveServices.size(); i++) { 1851 JobServiceContext jsc = mActiveServices.get(i); 1852 pw.print(" Slot #"); pw.print(i); pw.print(": "); 1853 if (jsc.getRunningJob() == null) { 1854 pw.println("inactive"); 1855 continue; 1856 } else { 1857 pw.println(jsc.getRunningJob().toShortString()); 1858 pw.print(" Running for: "); 1859 TimeUtils.formatDuration(now - jsc.getExecutionStartTimeElapsed(), pw); 1860 pw.print(", timeout at: "); 1861 TimeUtils.formatDuration(jsc.getTimeoutElapsed() - now, pw); 1862 pw.println(); 1863 jsc.getRunningJob().dump(pw, " ", false); 1864 int priority = evaluateJobPriorityLocked(jsc.getRunningJob()); 1865 if (priority != JobInfo.PRIORITY_DEFAULT) { 1866 pw.print(" Evaluated priority: "); pw.println(priority); 1867 } 1868 } 1869 } 1870 if (filterUid == -1) { 1871 pw.println(); 1872 pw.print("mReadyToRock="); pw.println(mReadyToRock); 1873 pw.print("mReportedActive="); pw.println(mReportedActive); 1874 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs); 1875 } 1876 } 1877 pw.println(); 1878 } 1879 } 1880