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