JobSchedulerService.java revision 019f6d9caa7de3c3ebd42ca7fd1a229d74c86e7e
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 java.io.FileDescriptor; 20import java.io.PrintWriter; 21import java.util.ArrayList; 22import java.util.Arrays; 23import java.util.Iterator; 24import java.util.List; 25 26import android.app.ActivityManager; 27import android.app.ActivityManagerNative; 28import android.app.AppGlobals; 29import android.app.IUidObserver; 30import android.app.job.JobInfo; 31import android.app.job.JobParameters; 32import android.app.job.JobScheduler; 33import android.app.job.JobService; 34import android.app.job.IJobScheduler; 35import android.content.BroadcastReceiver; 36import android.content.ComponentName; 37import android.content.Context; 38import android.content.Intent; 39import android.content.IntentFilter; 40import android.content.pm.IPackageManager; 41import android.content.pm.PackageManager; 42import android.content.pm.ServiceInfo; 43import android.os.BatteryStats; 44import android.os.Binder; 45import android.os.Handler; 46import android.os.Looper; 47import android.os.Message; 48import android.os.PowerManager; 49import android.os.RemoteException; 50import android.os.ServiceManager; 51import android.os.SystemClock; 52import android.os.UserHandle; 53import android.util.Slog; 54import android.util.SparseArray; 55 56import android.util.SparseBooleanArray; 57import com.android.internal.app.IBatteryStats; 58import com.android.internal.util.ArrayUtils; 59import com.android.server.DeviceIdleController; 60import com.android.server.LocalServices; 61import com.android.server.job.JobStore.JobStatusFunctor; 62import com.android.server.job.controllers.AppIdleController; 63import com.android.server.job.controllers.BatteryController; 64import com.android.server.job.controllers.ConnectivityController; 65import com.android.server.job.controllers.ContentObserverController; 66import com.android.server.job.controllers.IdleController; 67import com.android.server.job.controllers.JobStatus; 68import com.android.server.job.controllers.StateController; 69import com.android.server.job.controllers.TimeController; 70 71import libcore.util.EmptyArray; 72 73/** 74 * Responsible for taking jobs representing work to be performed by a client app, and determining 75 * based on the criteria specified when that job should be run against the client application's 76 * endpoint. 77 * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing 78 * about constraints, or the state of active jobs. It receives callbacks from the various 79 * controllers and completed jobs and operates accordingly. 80 * 81 * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object. 82 * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}. 83 * @hide 84 */ 85public final class JobSchedulerService extends com.android.server.SystemService 86 implements StateChangedListener, JobCompletedListener { 87 static final String TAG = "JobSchedulerService"; 88 public static final boolean DEBUG = false; 89 90 /** The number of concurrent jobs we run at one time. */ 91 private static final int MAX_JOB_CONTEXTS_COUNT 92 = ActivityManager.isLowRamDeviceStatic() ? 3 : 6; 93 /** Enforce a per-app limit on scheduled jobs? */ 94 private static final boolean ENFORCE_MAX_JOBS = false; 95 /** The maximum number of jobs that we allow an unprivileged app to schedule */ 96 private static final int MAX_JOBS_PER_APP = 100; 97 98 /** Global local for all job scheduler state. */ 99 final Object mLock = new Object(); 100 /** Master list of jobs. */ 101 final JobStore mJobs; 102 103 static final int MSG_JOB_EXPIRED = 0; 104 static final int MSG_CHECK_JOB = 1; 105 static final int MSG_STOP_JOB = 2; 106 static final int MSG_CHECK_JOB_GREEDY = 3; 107 108 // Policy constants 109 /** 110 * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things 111 * early. 112 */ 113 static final int MIN_IDLE_COUNT = 1; 114 /** 115 * Minimum # of charging jobs that must be ready in order to force the JMS to schedule things 116 * early. 117 */ 118 static final int MIN_CHARGING_COUNT = 1; 119 /** 120 * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule 121 * things early. 122 */ 123 static final int MIN_CONNECTIVITY_COUNT = 1; // Run connectivity jobs as soon as ready. 124 /** 125 * Minimum # of content trigger jobs that must be ready in order to force the JMS to schedule 126 * things early. 127 */ 128 static final int MIN_CONTENT_COUNT = 1; 129 /** 130 * Minimum # of jobs (with no particular constraints) for which the JMS will be happy running 131 * some work early. 132 * This is correlated with the amount of batching we'll be able to do. 133 */ 134 static final int MIN_READY_JOBS_COUNT = 2; 135 136 /** 137 * Track Services that have currently active or pending jobs. The index is provided by 138 * {@link JobStatus#getServiceToken()} 139 */ 140 final List<JobServiceContext> mActiveServices = new ArrayList<>(); 141 /** List of controllers that will notify this service of updates to jobs. */ 142 List<StateController> mControllers; 143 /** 144 * Queue of pending jobs. The JobServiceContext class will receive jobs from this list 145 * when ready to execute them. 146 */ 147 final ArrayList<JobStatus> mPendingJobs = new ArrayList<>(); 148 149 int[] mStartedUsers = EmptyArray.INT; 150 151 final JobHandler mHandler; 152 final JobSchedulerStub mJobSchedulerStub; 153 154 IBatteryStats mBatteryStats; 155 PowerManager mPowerManager; 156 DeviceIdleController.LocalService mLocalDeviceIdleController; 157 158 /** 159 * Set to true once we are allowed to run third party apps. 160 */ 161 boolean mReadyToRock; 162 163 /** 164 * True when in device idle mode, so we don't want to schedule any jobs. 165 */ 166 boolean mDeviceIdleMode; 167 168 /** 169 * What we last reported to DeviceIdleController about whether we are active. 170 */ 171 boolean mReportedActive; 172 173 /** 174 * Which uids are currently in the foreground. 175 */ 176 final SparseBooleanArray mForegroundUids = new SparseBooleanArray(); 177 178 /** 179 * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we 180 * still clean up. On reinstall the package will have a new uid. 181 */ 182 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 183 @Override 184 public void onReceive(Context context, Intent intent) { 185 Slog.d(TAG, "Receieved: " + intent.getAction()); 186 if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) { 187 // If this is an outright uninstall rather than the first half of an 188 // app update sequence, cancel the jobs associated with the app. 189 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { 190 int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1); 191 if (DEBUG) { 192 Slog.d(TAG, "Removing jobs for uid: " + uidRemoved); 193 } 194 cancelJobsForUid(uidRemoved, true); 195 } 196 } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) { 197 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); 198 if (DEBUG) { 199 Slog.d(TAG, "Removing jobs for user: " + userId); 200 } 201 cancelJobsForUser(userId); 202 } else if (PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction()) 203 || PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction())) { 204 updateIdleMode(mPowerManager != null 205 ? (mPowerManager.isDeviceIdleMode() 206 || mPowerManager.isLightDeviceIdleMode()) 207 : false); 208 } 209 } 210 }; 211 212 final private IUidObserver mUidObserver = new IUidObserver.Stub() { 213 @Override public void onUidStateChanged(int uid, int procState) throws RemoteException { 214 updateUidState(uid, procState); 215 } 216 217 @Override public void onUidGone(int uid) throws RemoteException { 218 updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); 219 } 220 221 @Override public void onUidActive(int uid) throws RemoteException { 222 } 223 224 @Override public void onUidIdle(int uid) throws RemoteException { 225 cancelJobsForUid(uid, false); 226 } 227 }; 228 229 public Object getLock() { 230 return mLock; 231 } 232 233 @Override 234 public void onStartUser(int userHandle) { 235 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle); 236 // Let's kick any outstanding jobs for this user. 237 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 238 } 239 240 @Override 241 public void onUnlockUser(int userHandle) { 242 // Let's kick any outstanding jobs for this user. 243 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 244 } 245 246 @Override 247 public void onStopUser(int userHandle) { 248 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle); 249 } 250 251 /** 252 * Entry point from client to schedule the provided job. 253 * This cancels the job if it's already been scheduled, and replaces it with the one provided. 254 * @param job JobInfo object containing execution parameters 255 * @param uId The package identifier of the application this job is for. 256 * @return Result of this operation. See <code>JobScheduler#RESULT_*</code> return codes. 257 */ 258 public int schedule(JobInfo job, int uId) { 259 return scheduleAsPackage(job, uId, null, -1, null); 260 } 261 262 public int scheduleAsPackage(JobInfo job, int uId, String packageName, int userId, 263 String tag) { 264 JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag); 265 try { 266 if (ActivityManagerNative.getDefault().getAppStartMode(uId, 267 job.getService().getPackageName()) == ActivityManager.APP_START_MODE_DISABLED) { 268 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString() 269 + " -- package not allowed to start"); 270 return JobScheduler.RESULT_FAILURE; 271 } 272 } catch (RemoteException e) { 273 } 274 if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString()); 275 JobStatus toCancel; 276 synchronized (mLock) { 277 // Jobs on behalf of others don't apply to the per-app job cap 278 if (ENFORCE_MAX_JOBS && packageName == null) { 279 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) { 280 Slog.w(TAG, "Too many jobs for uid " + uId); 281 throw new IllegalStateException("Apps may not schedule more than " 282 + MAX_JOBS_PER_APP + " distinct jobs"); 283 } 284 } 285 286 toCancel = mJobs.getJobByUidAndJobId(uId, job.getId()); 287 } 288 startTrackingJob(jobStatus, toCancel); 289 if (toCancel != null) { 290 cancelJobImpl(toCancel); 291 } 292 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 293 return JobScheduler.RESULT_SUCCESS; 294 } 295 296 public List<JobInfo> getPendingJobs(int uid) { 297 synchronized (mLock) { 298 List<JobStatus> jobs = mJobs.getJobsByUid(uid); 299 ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size()); 300 for (int i = jobs.size() - 1; i >= 0; i--) { 301 JobStatus job = jobs.get(i); 302 outList.add(job.getJob()); 303 } 304 return outList; 305 } 306 } 307 308 void cancelJobsForUser(int userHandle) { 309 List<JobStatus> jobsForUser; 310 synchronized (mLock) { 311 jobsForUser = mJobs.getJobsByUser(userHandle); 312 } 313 for (int i=0; i<jobsForUser.size(); i++) { 314 JobStatus toRemove = jobsForUser.get(i); 315 cancelJobImpl(toRemove); 316 } 317 } 318 319 /** 320 * Entry point from client to cancel all jobs originating from their uid. 321 * This will remove the job from the master list, and cancel the job if it was staged for 322 * execution or being executed. 323 * @param uid Uid to check against for removal of a job. 324 * @param forceAll If true, all jobs for the uid will be canceled; if false, only those 325 * whose apps are stopped. 326 */ 327 public void cancelJobsForUid(int uid, boolean forceAll) { 328 List<JobStatus> jobsForUid; 329 synchronized (mLock) { 330 jobsForUid = mJobs.getJobsByUid(uid); 331 } 332 for (int i=0; i<jobsForUid.size(); i++) { 333 JobStatus toRemove = jobsForUid.get(i); 334 if (!forceAll) { 335 String packageName = toRemove.getServiceComponent().getPackageName(); 336 try { 337 if (ActivityManagerNative.getDefault().getAppStartMode(uid, packageName) 338 != ActivityManager.APP_START_MODE_DISABLED) { 339 continue; 340 } 341 } catch (RemoteException e) { 342 } 343 } 344 cancelJobImpl(toRemove); 345 } 346 } 347 348 /** 349 * Entry point from client to cancel the job corresponding to the jobId provided. 350 * This will remove the job from the master list, and cancel the job if it was staged for 351 * execution or being executed. 352 * @param uid Uid of the calling client. 353 * @param jobId Id of the job, provided at schedule-time. 354 */ 355 public void cancelJob(int uid, int jobId) { 356 JobStatus toCancel; 357 synchronized (mLock) { 358 toCancel = mJobs.getJobByUidAndJobId(uid, jobId); 359 } 360 if (toCancel != null) { 361 cancelJobImpl(toCancel); 362 } 363 } 364 365 private void cancelJobImpl(JobStatus cancelled) { 366 if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString()); 367 stopTrackingJob(cancelled, true /* writeBack */); 368 synchronized (mLock) { 369 // Remove from pending queue. 370 mPendingJobs.remove(cancelled); 371 // Cancel if running. 372 stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED); 373 reportActive(); 374 } 375 } 376 377 void updateUidState(int uid, int procState) { 378 synchronized (mLock) { 379 boolean foreground = procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; 380 boolean changed = false; 381 if (foreground) { 382 if (!mForegroundUids.get(uid)) { 383 changed = true; 384 mForegroundUids.put(uid, true); 385 } 386 } else { 387 int index = mForegroundUids.indexOfKey(uid); 388 if (index >= 0) { 389 mForegroundUids.removeAt(index); 390 changed = true; 391 } 392 } 393 } 394 } 395 396 void updateIdleMode(boolean enabled) { 397 boolean changed = false; 398 boolean rocking; 399 synchronized (mLock) { 400 if (mDeviceIdleMode != enabled) { 401 changed = true; 402 } 403 rocking = mReadyToRock; 404 } 405 if (changed) { 406 if (rocking) { 407 for (int i=0; i<mControllers.size(); i++) { 408 mControllers.get(i).deviceIdleModeChanged(enabled); 409 } 410 } 411 synchronized (mLock) { 412 mDeviceIdleMode = enabled; 413 if (enabled) { 414 // When becoming idle, make sure no jobs are actively running. 415 for (int i=0; i<mActiveServices.size(); i++) { 416 JobServiceContext jsc = mActiveServices.get(i); 417 final JobStatus executing = jsc.getRunningJob(); 418 if (executing != null) { 419 jsc.cancelExecutingJob(JobParameters.REASON_DEVICE_IDLE); 420 } 421 } 422 } else { 423 // When coming out of idle, allow thing to start back up. 424 if (rocking) { 425 if (mLocalDeviceIdleController != null) { 426 if (!mReportedActive) { 427 mReportedActive = true; 428 mLocalDeviceIdleController.setJobsActive(true); 429 } 430 } 431 } 432 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 433 } 434 } 435 } 436 } 437 438 void reportActive() { 439 // active is true if pending queue contains jobs OR some job is running. 440 boolean active = mPendingJobs.size() > 0; 441 if (mPendingJobs.size() <= 0) { 442 for (int i=0; i<mActiveServices.size(); i++) { 443 JobServiceContext jsc = mActiveServices.get(i); 444 if (jsc.getRunningJob() != null) { 445 active = true; 446 break; 447 } 448 } 449 } 450 451 if (mReportedActive != active) { 452 mReportedActive = active; 453 if (mLocalDeviceIdleController != null) { 454 mLocalDeviceIdleController.setJobsActive(active); 455 } 456 } 457 } 458 459 /** 460 * Initializes the system service. 461 * <p> 462 * Subclasses must define a single argument constructor that accepts the context 463 * and passes it to super. 464 * </p> 465 * 466 * @param context The system server context. 467 */ 468 public JobSchedulerService(Context context) { 469 super(context); 470 // Create the controllers. 471 mControllers = new ArrayList<StateController>(); 472 mControllers.add(ConnectivityController.get(this)); 473 mControllers.add(TimeController.get(this)); 474 mControllers.add(IdleController.get(this)); 475 mControllers.add(BatteryController.get(this)); 476 mControllers.add(AppIdleController.get(this)); 477 mControllers.add(ContentObserverController.get(this)); 478 479 mHandler = new JobHandler(context.getMainLooper()); 480 mJobSchedulerStub = new JobSchedulerStub(); 481 mJobs = JobStore.initAndGet(this); 482 } 483 484 @Override 485 public void onStart() { 486 publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub); 487 } 488 489 @Override 490 public void onBootPhase(int phase) { 491 if (PHASE_SYSTEM_SERVICES_READY == phase) { 492 // Register br for package removals and user removals. 493 final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); 494 filter.addDataScheme("package"); 495 getContext().registerReceiverAsUser( 496 mBroadcastReceiver, UserHandle.ALL, filter, null, null); 497 final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED); 498 userFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); 499 userFilter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED); 500 getContext().registerReceiverAsUser( 501 mBroadcastReceiver, UserHandle.ALL, userFilter, null, null); 502 mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE); 503 try { 504 ActivityManagerNative.getDefault().registerUidObserver(mUidObserver, 505 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE 506 | ActivityManager.UID_OBSERVER_IDLE); 507 } catch (RemoteException e) { 508 // ignored; both services live in system_server 509 } 510 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { 511 synchronized (mLock) { 512 // Let's go! 513 mReadyToRock = true; 514 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService( 515 BatteryStats.SERVICE_NAME)); 516 mLocalDeviceIdleController 517 = LocalServices.getService(DeviceIdleController.LocalService.class); 518 // Create the "runners". 519 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) { 520 mActiveServices.add( 521 new JobServiceContext(this, mBatteryStats, 522 getContext().getMainLooper())); 523 } 524 // Attach jobs to their controllers. 525 mJobs.forEachJob(new JobStatusFunctor() { 526 @Override 527 public void process(JobStatus job) { 528 for (int controller = 0; controller < mControllers.size(); controller++) { 529 final StateController sc = mControllers.get(controller); 530 sc.deviceIdleModeChanged(mDeviceIdleMode); 531 sc.maybeStartTrackingJobLocked(job, null); 532 } 533 } 534 }); 535 // GO GO GO! 536 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 537 } 538 } 539 } 540 541 /** 542 * Called when we have a job status object that we need to insert in our 543 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know 544 * about. 545 */ 546 private void startTrackingJob(JobStatus jobStatus, JobStatus lastJob) { 547 synchronized (mLock) { 548 final boolean update = mJobs.add(jobStatus); 549 if (mReadyToRock) { 550 for (int i = 0; i < mControllers.size(); i++) { 551 StateController controller = mControllers.get(i); 552 if (update) { 553 controller.maybeStopTrackingJobLocked(jobStatus, true); 554 } 555 controller.maybeStartTrackingJobLocked(jobStatus, lastJob); 556 } 557 } 558 } 559 } 560 561 /** 562 * Called when we want to remove a JobStatus object that we've finished executing. Returns the 563 * object removed. 564 */ 565 private boolean stopTrackingJob(JobStatus jobStatus, boolean writeBack) { 566 synchronized (mLock) { 567 // Remove from store as well as controllers. 568 final boolean removed = mJobs.remove(jobStatus, writeBack); 569 if (removed && mReadyToRock) { 570 for (int i=0; i<mControllers.size(); i++) { 571 StateController controller = mControllers.get(i); 572 controller.maybeStopTrackingJobLocked(jobStatus, false); 573 } 574 } 575 return removed; 576 } 577 } 578 579 private boolean stopJobOnServiceContextLocked(JobStatus job, int reason) { 580 for (int i=0; i<mActiveServices.size(); i++) { 581 JobServiceContext jsc = mActiveServices.get(i); 582 final JobStatus executing = jsc.getRunningJob(); 583 if (executing != null && executing.matches(job.getUid(), job.getJobId())) { 584 jsc.cancelExecutingJob(reason); 585 return true; 586 } 587 } 588 return false; 589 } 590 591 /** 592 * @param job JobStatus we are querying against. 593 * @return Whether or not the job represented by the status object is currently being run or 594 * is pending. 595 */ 596 private boolean isCurrentlyActiveLocked(JobStatus job) { 597 for (int i=0; i<mActiveServices.size(); i++) { 598 JobServiceContext serviceContext = mActiveServices.get(i); 599 final JobStatus running = serviceContext.getRunningJob(); 600 if (running != null && running.matches(job.getUid(), job.getJobId())) { 601 return true; 602 } 603 } 604 return false; 605 } 606 607 /** 608 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to 609 * specify an override deadline on a failed job (the failed job will run even though it's not 610 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any 611 * ready job with {@link JobStatus#numFailures} > 0 will be executed. 612 * 613 * @param failureToReschedule Provided job status that we will reschedule. 614 * @return A newly instantiated JobStatus with the same constraints as the last job except 615 * with adjusted timing constraints. 616 * 617 * @see JobHandler#maybeQueueReadyJobsForExecutionLockedH 618 */ 619 private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) { 620 final long elapsedNowMillis = SystemClock.elapsedRealtime(); 621 final JobInfo job = failureToReschedule.getJob(); 622 623 final long initialBackoffMillis = job.getInitialBackoffMillis(); 624 final int backoffAttempts = failureToReschedule.getNumFailures() + 1; 625 long delayMillis; 626 627 switch (job.getBackoffPolicy()) { 628 case JobInfo.BACKOFF_POLICY_LINEAR: 629 delayMillis = initialBackoffMillis * backoffAttempts; 630 break; 631 default: 632 if (DEBUG) { 633 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential."); 634 } 635 case JobInfo.BACKOFF_POLICY_EXPONENTIAL: 636 delayMillis = 637 (long) Math.scalb(initialBackoffMillis, backoffAttempts - 1); 638 break; 639 } 640 delayMillis = 641 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS); 642 JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis, 643 JobStatus.NO_LATEST_RUNTIME, backoffAttempts); 644 for (int ic=0; ic<mControllers.size(); ic++) { 645 StateController controller = mControllers.get(ic); 646 controller.rescheduleForFailure(newJob, failureToReschedule); 647 } 648 return newJob; 649 } 650 651 /** 652 * Called after a periodic has executed so we can reschedule it. We take the last execution 653 * time of the job to be the time of completion (i.e. the time at which this function is 654 * called). 655 * This could be inaccurate b/c the job can run for as long as 656 * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead 657 * to underscheduling at least, rather than if we had taken the last execution time to be the 658 * start of the execution. 659 * @return A new job representing the execution criteria for this instantiation of the 660 * recurring job. 661 */ 662 private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) { 663 final long elapsedNow = SystemClock.elapsedRealtime(); 664 // Compute how much of the period is remaining. 665 long runEarly = 0L; 666 667 // If this periodic was rescheduled it won't have a deadline. 668 if (periodicToReschedule.hasDeadlineConstraint()) { 669 runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L); 670 } 671 long flex = periodicToReschedule.getJob().getFlexMillis(); 672 long period = periodicToReschedule.getJob().getIntervalMillis(); 673 long newLatestRuntimeElapsed = elapsedNow + runEarly + period; 674 long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex; 675 676 if (DEBUG) { 677 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" + 678 newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s"); 679 } 680 return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed, 681 newLatestRuntimeElapsed, 0 /* backoffAttempt */); 682 } 683 684 // JobCompletedListener implementations. 685 686 /** 687 * A job just finished executing. We fetch the 688 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on 689 * whether we want to reschedule we readd it to the controllers. 690 * @param jobStatus Completed job. 691 * @param needsReschedule Whether the implementing class should reschedule this job. 692 */ 693 @Override 694 public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule) { 695 if (DEBUG) { 696 Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule); 697 } 698 // Do not write back immediately if this is a periodic job. The job may get lost if system 699 // shuts down before it is added back. 700 if (!stopTrackingJob(jobStatus, !jobStatus.getJob().isPeriodic())) { 701 if (DEBUG) { 702 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?"); 703 } 704 // We still want to check for jobs to execute, because this job may have 705 // scheduled a new job under the same job id, and now we can run it. 706 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget(); 707 return; 708 } 709 // Note: there is a small window of time in here where, when rescheduling a job, 710 // we will stop monitoring its content providers. This should be fixed by stopping 711 // the old job after scheduling the new one, but since we have no lock held here 712 // that may cause ordering problems if the app removes jobStatus while in here. 713 if (needsReschedule) { 714 JobStatus rescheduled = getRescheduleJobForFailure(jobStatus); 715 startTrackingJob(rescheduled, jobStatus); 716 } else if (jobStatus.getJob().isPeriodic()) { 717 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus); 718 startTrackingJob(rescheduledPeriodic, jobStatus); 719 } 720 reportActive(); 721 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget(); 722 } 723 724 // StateChangedListener implementations. 725 726 /** 727 * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that 728 * some controller's state has changed, so as to run through the list of jobs and start/stop 729 * any that are eligible. 730 */ 731 @Override 732 public void onControllerStateChanged() { 733 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); 734 } 735 736 @Override 737 public void onRunJobNow(JobStatus jobStatus) { 738 mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget(); 739 } 740 741 private class JobHandler extends Handler { 742 743 public JobHandler(Looper looper) { 744 super(looper); 745 } 746 747 @Override 748 public void handleMessage(Message message) { 749 synchronized (mLock) { 750 if (!mReadyToRock) { 751 return; 752 } 753 } 754 switch (message.what) { 755 case MSG_JOB_EXPIRED: 756 synchronized (mLock) { 757 JobStatus runNow = (JobStatus) message.obj; 758 // runNow can be null, which is a controller's way of indicating that its 759 // state is such that all ready jobs should be run immediately. 760 if (runNow != null && !mPendingJobs.contains(runNow) 761 && mJobs.containsJob(runNow)) { 762 mPendingJobs.add(runNow); 763 } 764 queueReadyJobsForExecutionLockedH(); 765 } 766 break; 767 case MSG_CHECK_JOB: 768 synchronized (mLock) { 769 if (mReportedActive) { 770 // if jobs are currently being run, queue all ready jobs for execution. 771 queueReadyJobsForExecutionLockedH(); 772 } else { 773 // Check the list of jobs and run some of them if we feel inclined. 774 maybeQueueReadyJobsForExecutionLockedH(); 775 } 776 } 777 break; 778 case MSG_CHECK_JOB_GREEDY: 779 synchronized (mLock) { 780 queueReadyJobsForExecutionLockedH(); 781 } 782 break; 783 case MSG_STOP_JOB: 784 cancelJobImpl((JobStatus)message.obj); 785 break; 786 } 787 maybeRunPendingJobsH(); 788 // Don't remove JOB_EXPIRED in case one came along while processing the queue. 789 removeMessages(MSG_CHECK_JOB); 790 } 791 792 /** 793 * Run through list of jobs and execute all possible - at least one is expired so we do 794 * as many as we can. 795 */ 796 private void queueReadyJobsForExecutionLockedH() { 797 if (DEBUG) { 798 Slog.d(TAG, "queuing all ready jobs for execution:"); 799 } 800 mPendingJobs.clear(); 801 mJobs.forEachJob(mReadyQueueFunctor); 802 mReadyQueueFunctor.postProcess(); 803 804 if (DEBUG) { 805 final int queuedJobs = mPendingJobs.size(); 806 if (queuedJobs == 0) { 807 Slog.d(TAG, "No jobs pending."); 808 } else { 809 Slog.d(TAG, queuedJobs + " jobs queued."); 810 } 811 } 812 } 813 814 class ReadyJobQueueFunctor implements JobStatusFunctor { 815 ArrayList<JobStatus> newReadyJobs; 816 817 @Override 818 public void process(JobStatus job) { 819 if (isReadyToBeExecutedLocked(job)) { 820 if (DEBUG) { 821 Slog.d(TAG, " queued " + job.toShortString()); 822 } 823 if (newReadyJobs == null) { 824 newReadyJobs = new ArrayList<JobStatus>(); 825 } 826 newReadyJobs.add(job); 827 } else if (areJobConstraintsNotSatisfiedLocked(job)) { 828 stopJobOnServiceContextLocked(job, 829 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED); 830 } 831 } 832 833 public void postProcess() { 834 if (newReadyJobs != null) { 835 mPendingJobs.addAll(newReadyJobs); 836 } 837 newReadyJobs = null; 838 } 839 } 840 private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor(); 841 842 /** 843 * The state of at least one job has changed. Here is where we could enforce various 844 * policies on when we want to execute jobs. 845 * Right now the policy is such: 846 * If >1 of the ready jobs is idle mode we send all of them off 847 * if more than 2 network connectivity jobs are ready we send them all off. 848 * If more than 4 jobs total are ready we send them all off. 849 * TODO: It would be nice to consolidate these sort of high-level policies somewhere. 850 */ 851 class MaybeReadyJobQueueFunctor implements JobStatusFunctor { 852 int chargingCount; 853 int idleCount; 854 int backoffCount; 855 int connectivityCount; 856 int contentCount; 857 List<JobStatus> runnableJobs; 858 859 public MaybeReadyJobQueueFunctor() { 860 reset(); 861 } 862 863 // Functor method invoked for each job via JobStore.forEachJob() 864 @Override 865 public void process(JobStatus job) { 866 if (isReadyToBeExecutedLocked(job)) { 867 try { 868 if (ActivityManagerNative.getDefault().getAppStartMode(job.getUid(), 869 job.getJob().getService().getPackageName()) 870 == ActivityManager.APP_START_MODE_DISABLED) { 871 Slog.w(TAG, "Aborting job " + job.getUid() + ":" 872 + job.getJob().toString() + " -- package not allowed to start"); 873 mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget(); 874 return; 875 } 876 } catch (RemoteException e) { 877 } 878 if (job.getNumFailures() > 0) { 879 backoffCount++; 880 } 881 if (job.hasIdleConstraint()) { 882 idleCount++; 883 } 884 if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint()) { 885 connectivityCount++; 886 } 887 if (job.hasChargingConstraint()) { 888 chargingCount++; 889 } 890 if (job.hasContentTriggerConstraint()) { 891 contentCount++; 892 } 893 if (runnableJobs == null) { 894 runnableJobs = new ArrayList<>(); 895 } 896 runnableJobs.add(job); 897 } else if (areJobConstraintsNotSatisfiedLocked(job)) { 898 stopJobOnServiceContextLocked(job, 899 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED); 900 } 901 } 902 903 public void postProcess() { 904 if (backoffCount > 0 || 905 idleCount >= MIN_IDLE_COUNT || 906 connectivityCount >= MIN_CONNECTIVITY_COUNT || 907 chargingCount >= MIN_CHARGING_COUNT || 908 contentCount >= MIN_CONTENT_COUNT || 909 (runnableJobs != null && runnableJobs.size() >= MIN_READY_JOBS_COUNT)) { 910 if (DEBUG) { 911 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Running jobs."); 912 } 913 mPendingJobs.addAll(runnableJobs); 914 } else { 915 if (DEBUG) { 916 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Not running anything."); 917 } 918 } 919 920 // Be ready for next time 921 reset(); 922 } 923 924 private void reset() { 925 chargingCount = 0; 926 idleCount = 0; 927 backoffCount = 0; 928 connectivityCount = 0; 929 contentCount = 0; 930 runnableJobs = null; 931 } 932 } 933 private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor(); 934 935 private void maybeQueueReadyJobsForExecutionLockedH() { 936 if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs..."); 937 938 mPendingJobs.clear(); 939 mJobs.forEachJob(mMaybeQueueFunctor); 940 mMaybeQueueFunctor.postProcess(); 941 } 942 943 /** 944 * Criteria for moving a job into the pending queue: 945 * - It's ready. 946 * - It's not pending. 947 * - It's not already running on a JSC. 948 * - The user that requested the job is running. 949 * - The component is enabled and runnable. 950 */ 951 private boolean isReadyToBeExecutedLocked(JobStatus job) { 952 final boolean jobReady = job.isReady(); 953 final boolean jobPending = mPendingJobs.contains(job); 954 final boolean jobActive = isCurrentlyActiveLocked(job); 955 956 final int userId = job.getUserId(); 957 final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId); 958 final boolean componentPresent; 959 try { 960 componentPresent = (AppGlobals.getPackageManager().getServiceInfo( 961 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING, 962 userId) != null); 963 } catch (RemoteException e) { 964 throw e.rethrowAsRuntimeException(); 965 } 966 967 if (DEBUG) { 968 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString() 969 + " ready=" + jobReady + " pending=" + jobPending 970 + " active=" + jobActive + " userStarted=" + userStarted 971 + " componentPresent=" + componentPresent); 972 } 973 return userStarted && componentPresent && jobReady && !jobPending && !jobActive; 974 } 975 976 /** 977 * Criteria for cancelling an active job: 978 * - It's not ready 979 * - It's running on a JSC. 980 */ 981 private boolean areJobConstraintsNotSatisfiedLocked(JobStatus job) { 982 return !job.isReady() && isCurrentlyActiveLocked(job); 983 } 984 985 /** 986 * Reconcile jobs in the pending queue against available execution contexts. 987 * A controller can force a job into the pending queue even if it's already running, but 988 * here is where we decide whether to actually execute it. 989 */ 990 private void maybeRunPendingJobsH() { 991 synchronized (mLock) { 992 if (mDeviceIdleMode) { 993 // If device is idle, we will not schedule jobs to run. 994 return; 995 } 996 if (DEBUG) { 997 Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs."); 998 } 999 assignJobsToContextsLocked(); 1000 reportActive(); 1001 } 1002 } 1003 } 1004 1005 private int evaluateJobPriorityLocked(JobStatus job) { 1006 int priority = job.getPriority(); 1007 if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) { 1008 return priority; 1009 } 1010 if (mForegroundUids.get(job.getSourceUid())) { 1011 return JobInfo.PRIORITY_FOREGROUND_APP; 1012 } 1013 return priority; 1014 } 1015 1016 /** 1017 * Takes jobs from pending queue and runs them on available contexts. 1018 * If no contexts are available, preempts lower priority jobs to 1019 * run higher priority ones. 1020 * Lock on mJobs before calling this function. 1021 */ 1022 private void assignJobsToContextsLocked() { 1023 if (DEBUG) { 1024 Slog.d(TAG, printPendingQueue()); 1025 } 1026 1027 // This array essentially stores the state of mActiveServices array. 1028 // ith index stores the job present on the ith JobServiceContext. 1029 // We manipulate this array until we arrive at what jobs should be running on 1030 // what JobServiceContext. 1031 JobStatus[] contextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT]; 1032 // Indicates whether we need to act on this jobContext id 1033 boolean[] act = new boolean[MAX_JOB_CONTEXTS_COUNT]; 1034 int[] preferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT]; 1035 for (int i=0; i<mActiveServices.size(); i++) { 1036 contextIdToJobMap[i] = mActiveServices.get(i).getRunningJob(); 1037 preferredUidForContext[i] = mActiveServices.get(i).getPreferredUid(); 1038 } 1039 if (DEBUG) { 1040 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial")); 1041 } 1042 Iterator<JobStatus> it = mPendingJobs.iterator(); 1043 while (it.hasNext()) { 1044 JobStatus nextPending = it.next(); 1045 1046 // If job is already running, go to next job. 1047 int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap); 1048 if (jobRunningContext != -1) { 1049 continue; 1050 } 1051 1052 nextPending.lastEvaluatedPriority = evaluateJobPriorityLocked(nextPending); 1053 1054 // Find a context for nextPending. The context should be available OR 1055 // it should have lowest priority among all running jobs 1056 // (sharing the same Uid as nextPending) 1057 int minPriority = Integer.MAX_VALUE; 1058 int minPriorityContextId = -1; 1059 for (int i=0; i<mActiveServices.size(); i++) { 1060 JobStatus job = contextIdToJobMap[i]; 1061 int preferredUid = preferredUidForContext[i]; 1062 if (job == null && (preferredUid == nextPending.getUid() || 1063 preferredUid == JobServiceContext.NO_PREFERRED_UID) ) { 1064 minPriorityContextId = i; 1065 break; 1066 } 1067 if (job == null) { 1068 // No job on this context, but nextPending can't run here because 1069 // the context has a preferred Uid. 1070 continue; 1071 } 1072 if (job.getUid() != nextPending.getUid()) { 1073 continue; 1074 } 1075 if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) { 1076 continue; 1077 } 1078 if (minPriority > nextPending.lastEvaluatedPriority) { 1079 minPriority = nextPending.lastEvaluatedPriority; 1080 minPriorityContextId = i; 1081 } 1082 } 1083 if (minPriorityContextId != -1) { 1084 contextIdToJobMap[minPriorityContextId] = nextPending; 1085 act[minPriorityContextId] = true; 1086 } 1087 } 1088 if (DEBUG) { 1089 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final")); 1090 } 1091 for (int i=0; i<mActiveServices.size(); i++) { 1092 boolean preservePreferredUid = false; 1093 if (act[i]) { 1094 JobStatus js = mActiveServices.get(i).getRunningJob(); 1095 if (js != null) { 1096 if (DEBUG) { 1097 Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJob()); 1098 } 1099 // preferredUid will be set to uid of currently running job. 1100 mActiveServices.get(i).preemptExecutingJob(); 1101 preservePreferredUid = true; 1102 } else { 1103 final JobStatus pendingJob = contextIdToJobMap[i]; 1104 if (DEBUG) { 1105 Slog.d(TAG, "About to run job on context " 1106 + String.valueOf(i) + ", job: " + pendingJob); 1107 } 1108 for (int ic=0; ic<mControllers.size(); ic++) { 1109 mControllers.get(ic).prepareForExecutionLocked(pendingJob); 1110 } 1111 if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) { 1112 Slog.d(TAG, "Error executing " + pendingJob); 1113 } 1114 mPendingJobs.remove(pendingJob); 1115 } 1116 } 1117 if (!preservePreferredUid) { 1118 mActiveServices.get(i).clearPreferredUid(); 1119 } 1120 } 1121 } 1122 1123 int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) { 1124 for (int i=0; i<map.length; i++) { 1125 if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) { 1126 return i; 1127 } 1128 } 1129 return -1; 1130 } 1131 1132 /** 1133 * Binder stub trampoline implementation 1134 */ 1135 final class JobSchedulerStub extends IJobScheduler.Stub { 1136 /** Cache determination of whether a given app can persist jobs 1137 * key is uid of the calling app; value is undetermined/true/false 1138 */ 1139 private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>(); 1140 1141 // Enforce that only the app itself (or shared uid participant) can schedule a 1142 // job that runs one of the app's services, as well as verifying that the 1143 // named service properly requires the BIND_JOB_SERVICE permission 1144 private void enforceValidJobRequest(int uid, JobInfo job) { 1145 final IPackageManager pm = AppGlobals.getPackageManager(); 1146 final ComponentName service = job.getService(); 1147 try { 1148 ServiceInfo si = pm.getServiceInfo(service, 1149 PackageManager.MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(uid)); 1150 if (si == null) { 1151 throw new IllegalArgumentException("No such service " + service); 1152 } 1153 if (si.applicationInfo.uid != uid) { 1154 throw new IllegalArgumentException("uid " + uid + 1155 " cannot schedule job in " + service.getPackageName()); 1156 } 1157 if (!JobService.PERMISSION_BIND.equals(si.permission)) { 1158 throw new IllegalArgumentException("Scheduled service " + service 1159 + " does not require android.permission.BIND_JOB_SERVICE permission"); 1160 } 1161 } catch (RemoteException e) { 1162 // Can't happen; the Package Manager is in this same process 1163 } 1164 } 1165 1166 private boolean canPersistJobs(int pid, int uid) { 1167 // If we get this far we're good to go; all we need to do now is check 1168 // whether the app is allowed to persist its scheduled work. 1169 final boolean canPersist; 1170 synchronized (mPersistCache) { 1171 Boolean cached = mPersistCache.get(uid); 1172 if (cached != null) { 1173 canPersist = cached.booleanValue(); 1174 } else { 1175 // Persisting jobs is tantamount to running at boot, so we permit 1176 // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED 1177 // permission 1178 int result = getContext().checkPermission( 1179 android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid); 1180 canPersist = (result == PackageManager.PERMISSION_GRANTED); 1181 mPersistCache.put(uid, canPersist); 1182 } 1183 } 1184 return canPersist; 1185 } 1186 1187 // IJobScheduler implementation 1188 @Override 1189 public int schedule(JobInfo job) throws RemoteException { 1190 if (DEBUG) { 1191 Slog.d(TAG, "Scheduling job: " + job.toString()); 1192 } 1193 final int pid = Binder.getCallingPid(); 1194 final int uid = Binder.getCallingUid(); 1195 1196 enforceValidJobRequest(uid, job); 1197 if (job.isPersisted()) { 1198 if (!canPersistJobs(pid, uid)) { 1199 throw new IllegalArgumentException("Error: requested job be persisted without" 1200 + " holding RECEIVE_BOOT_COMPLETED permission."); 1201 } 1202 } 1203 1204 long ident = Binder.clearCallingIdentity(); 1205 try { 1206 return JobSchedulerService.this.schedule(job, uid); 1207 } finally { 1208 Binder.restoreCallingIdentity(ident); 1209 } 1210 } 1211 1212 @Override 1213 public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag) 1214 throws RemoteException { 1215 final int callerUid = Binder.getCallingUid(); 1216 if (DEBUG) { 1217 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString() 1218 + " on behalf of " + packageName); 1219 } 1220 1221 if (packageName == null) { 1222 throw new NullPointerException("Must specify a package for scheduleAsPackage()"); 1223 } 1224 1225 int mayScheduleForOthers = getContext().checkCallingOrSelfPermission( 1226 android.Manifest.permission.UPDATE_DEVICE_STATS); 1227 if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) { 1228 throw new SecurityException("Caller uid " + callerUid 1229 + " not permitted to schedule jobs for other apps"); 1230 } 1231 1232 long ident = Binder.clearCallingIdentity(); 1233 try { 1234 return JobSchedulerService.this.scheduleAsPackage(job, callerUid, 1235 packageName, userId, tag); 1236 } finally { 1237 Binder.restoreCallingIdentity(ident); 1238 } 1239 } 1240 1241 @Override 1242 public List<JobInfo> getAllPendingJobs() throws RemoteException { 1243 final int uid = Binder.getCallingUid(); 1244 1245 long ident = Binder.clearCallingIdentity(); 1246 try { 1247 return JobSchedulerService.this.getPendingJobs(uid); 1248 } finally { 1249 Binder.restoreCallingIdentity(ident); 1250 } 1251 } 1252 1253 @Override 1254 public void cancelAll() throws RemoteException { 1255 final int uid = Binder.getCallingUid(); 1256 1257 long ident = Binder.clearCallingIdentity(); 1258 try { 1259 JobSchedulerService.this.cancelJobsForUid(uid, true); 1260 } finally { 1261 Binder.restoreCallingIdentity(ident); 1262 } 1263 } 1264 1265 @Override 1266 public void cancel(int jobId) throws RemoteException { 1267 final int uid = Binder.getCallingUid(); 1268 1269 long ident = Binder.clearCallingIdentity(); 1270 try { 1271 JobSchedulerService.this.cancelJob(uid, jobId); 1272 } finally { 1273 Binder.restoreCallingIdentity(ident); 1274 } 1275 } 1276 1277 /** 1278 * "dumpsys" infrastructure 1279 */ 1280 @Override 1281 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1282 getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 1283 1284 long identityToken = Binder.clearCallingIdentity(); 1285 try { 1286 JobSchedulerService.this.dumpInternal(pw); 1287 } finally { 1288 Binder.restoreCallingIdentity(identityToken); 1289 } 1290 } 1291 }; 1292 1293 private String printContextIdToJobMap(JobStatus[] map, String initial) { 1294 StringBuilder s = new StringBuilder(initial + ": "); 1295 for (int i=0; i<map.length; i++) { 1296 s.append("(") 1297 .append(map[i] == null? -1: map[i].getJobId()) 1298 .append(map[i] == null? -1: map[i].getUid()) 1299 .append(")" ); 1300 } 1301 return s.toString(); 1302 } 1303 1304 private String printPendingQueue() { 1305 StringBuilder s = new StringBuilder("Pending queue: "); 1306 Iterator<JobStatus> it = mPendingJobs.iterator(); 1307 while (it.hasNext()) { 1308 JobStatus js = it.next(); 1309 s.append("(") 1310 .append(js.getJob().getId()) 1311 .append(", ") 1312 .append(js.getUid()) 1313 .append(") "); 1314 } 1315 return s.toString(); 1316 } 1317 1318 void dumpInternal(final PrintWriter pw) { 1319 final long now = SystemClock.elapsedRealtime(); 1320 synchronized (mLock) { 1321 pw.println("Started users: " + Arrays.toString(mStartedUsers)); 1322 pw.println("Registered jobs:"); 1323 if (mJobs.size() > 0) { 1324 mJobs.forEachJob(new JobStatusFunctor() { 1325 private int index = 0; 1326 1327 @Override 1328 public void process(JobStatus job) { 1329 pw.print(" Job #"); pw.print(index++); pw.print(": "); 1330 pw.println(job.toShortString()); 1331 job.dump(pw, " "); 1332 pw.print(" Ready: "); 1333 pw.print(mHandler.isReadyToBeExecutedLocked(job)); 1334 pw.print(" (job="); 1335 pw.print(job.isReady()); 1336 pw.print(" pending="); 1337 pw.print(mPendingJobs.contains(job)); 1338 pw.print(" active="); 1339 pw.print(isCurrentlyActiveLocked(job)); 1340 pw.print(" user="); 1341 pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId())); 1342 pw.println(")"); 1343 } 1344 }); 1345 } else { 1346 pw.println(" None."); 1347 } 1348 for (int i=0; i<mControllers.size(); i++) { 1349 pw.println(); 1350 mControllers.get(i).dumpControllerStateLocked(pw); 1351 } 1352 pw.println(); 1353 pw.println("Foreground uids:"); 1354 for (int i=0; i<mForegroundUids.size(); i++) { 1355 pw.print(" "); pw.println(UserHandle.formatUid(mForegroundUids.keyAt(i))); 1356 } 1357 pw.println(); 1358 pw.println("Pending queue:"); 1359 for (int i=0; i<mPendingJobs.size(); i++) { 1360 JobStatus job = mPendingJobs.get(i); 1361 pw.print(" Pending #"); pw.print(i); pw.print(": "); 1362 pw.println(job.toShortString()); 1363 int priority = evaluateJobPriorityLocked(job); 1364 if (priority != JobInfo.PRIORITY_DEFAULT) { 1365 pw.print(" Evaluated priority: "); pw.println(priority); 1366 } 1367 pw.print(" Tag: "); pw.println(job.getTag()); 1368 } 1369 pw.println(); 1370 pw.println("Active jobs:"); 1371 for (int i=0; i<mActiveServices.size(); i++) { 1372 JobServiceContext jsc = mActiveServices.get(i); 1373 if (jsc.getRunningJob() == null) { 1374 continue; 1375 } else { 1376 final long timeout = jsc.getTimeoutElapsed(); 1377 pw.print("Running for: "); 1378 pw.print((now - jsc.getExecutionStartTimeElapsed())/1000); 1379 pw.print("s timeout="); 1380 pw.print(timeout); 1381 pw.print(" fromnow="); 1382 pw.println(timeout-now); 1383 jsc.getRunningJob().dump(pw, " "); 1384 int priority = evaluateJobPriorityLocked(jsc.getRunningJob()); 1385 if (priority != JobInfo.PRIORITY_DEFAULT) { 1386 pw.print(" Evaluated priority: "); pw.println(priority); 1387 } 1388 } 1389 } 1390 pw.println(); 1391 pw.print("mReadyToRock="); pw.println(mReadyToRock); 1392 pw.print("mDeviceIdleMode="); pw.println(mDeviceIdleMode); 1393 pw.print("mReportedActive="); pw.println(mReportedActive); 1394 } 1395 pw.println(); 1396 } 1397} 1398