Fields Summary |
---|
static final boolean | DEBUG |
private static final int | MAX_JOB_CONTEXTS_COUNTThe number of concurrent jobs we run at one time. |
static final String | TAG |
final JobStore | mJobsMaster list of jobs. |
static final int | MSG_JOB_EXPIRED |
static final int | MSG_CHECK_JOB |
static final int | MIN_IDLE_COUNTMinimum # of idle jobs that must be ready in order to force the JMS to schedule things
early. |
static final int | MIN_CHARGING_COUNTMinimum # of charging jobs that must be ready in order to force the JMS to schedule things
early. |
static final int | MIN_CONNECTIVITY_COUNTMinimum # of connectivity jobs that must be ready in order to force the JMS to schedule
things early. |
static final int | MIN_READY_JOBS_COUNTMinimum # of jobs (with no particular constraints) for which the JMS will be happy running
some work early.
This is correlated with the amount of batching we'll be able to do. |
final List | mActiveServicesTrack Services that have currently active or pending jobs. The index is provided by
{@link JobStatus#getServiceToken()} |
List | mControllersList of controllers that will notify this service of updates to jobs. |
final ArrayList | mPendingJobsQueue of pending jobs. The JobServiceContext class will receive jobs from this list
when ready to execute them. |
final ArrayList | mStartedUsers |
final JobHandler | mHandler |
final JobSchedulerStub | mJobSchedulerStub |
com.android.internal.app.IBatteryStats | mBatteryStats |
boolean | mReadyToRockSet to true once we are allowed to run third party apps. |
private final android.content.BroadcastReceiver | mBroadcastReceiverCleans up outstanding jobs when a package is removed. Even if it's being replaced later we
still clean up. On reinstall the package will have a new uid. |
Methods Summary |
---|
public void | cancelJob(int uid, int jobId)Entry point from client to cancel the job corresponding to the jobId provided.
This will remove the job from the master list, and cancel the job if it was staged for
execution or being executed.
JobStatus toCancel;
synchronized (mJobs) {
toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
}
if (toCancel != null) {
cancelJobImpl(toCancel);
}
|
private void | cancelJobImpl(com.android.server.job.controllers.JobStatus cancelled)
if (DEBUG) {
Slog.d(TAG, "Cancelling: " + cancelled);
}
stopTrackingJob(cancelled);
synchronized (mJobs) {
// Remove from pending queue.
mPendingJobs.remove(cancelled);
// Cancel if running.
stopJobOnServiceContextLocked(cancelled);
}
|
public void | cancelJobsForUid(int uid)Entry point from client to cancel all jobs originating from their uid.
This will remove the job from the master list, and cancel the job if it was staged for
execution or being executed.
List<JobStatus> jobsForUid;
synchronized (mJobs) {
jobsForUid = mJobs.getJobsByUid(uid);
}
for (int i=0; i<jobsForUid.size(); i++) {
JobStatus toRemove = jobsForUid.get(i);
cancelJobImpl(toRemove);
}
|
private void | cancelJobsForUser(int userHandle)
List<JobStatus> jobsForUser;
synchronized (mJobs) {
jobsForUser = mJobs.getJobsByUser(userHandle);
}
for (int i=0; i<jobsForUser.size(); i++) {
JobStatus toRemove = jobsForUser.get(i);
cancelJobImpl(toRemove);
}
|
void | dumpInternal(java.io.PrintWriter pw)
final long now = SystemClock.elapsedRealtime();
synchronized (mJobs) {
pw.print("Started users: ");
for (int i=0; i<mStartedUsers.size(); i++) {
pw.print("u" + mStartedUsers.get(i) + " ");
}
pw.println();
pw.println("Registered jobs:");
if (mJobs.size() > 0) {
ArraySet<JobStatus> jobs = mJobs.getJobs();
for (int i=0; i<jobs.size(); i++) {
JobStatus job = jobs.valueAt(i);
job.dump(pw, " ");
}
} else {
pw.println(" None.");
}
for (int i=0; i<mControllers.size(); i++) {
pw.println();
mControllers.get(i).dumpControllerState(pw);
}
pw.println();
pw.println("Pending:");
for (int i=0; i<mPendingJobs.size(); i++) {
pw.println(mPendingJobs.get(i).hashCode());
}
pw.println();
pw.println("Active jobs:");
for (int i=0; i<mActiveServices.size(); i++) {
JobServiceContext jsc = mActiveServices.get(i);
if (jsc.isAvailable()) {
continue;
} else {
final long timeout = jsc.getTimeoutElapsed();
pw.print("Running for: ");
pw.print((now - jsc.getExecutionStartTimeElapsed())/1000);
pw.print("s timeout=");
pw.print(timeout);
pw.print(" fromnow=");
pw.println(timeout-now);
jsc.getRunningJob().dump(pw, " ");
}
}
pw.println();
pw.print("mReadyToRock="); pw.println(mReadyToRock);
}
pw.println();
|
public java.util.List | getPendingJobs(int uid)
ArrayList<JobInfo> outList = new ArrayList<JobInfo>();
synchronized (mJobs) {
ArraySet<JobStatus> jobs = mJobs.getJobs();
for (int i=0; i<jobs.size(); i++) {
JobStatus job = jobs.valueAt(i);
if (job.getUid() == uid) {
outList.add(job.getJob());
}
}
}
return outList;
|
private com.android.server.job.controllers.JobStatus | getRescheduleJobForFailure(com.android.server.job.controllers.JobStatus failureToReschedule)A job is rescheduled with exponential back-off if the client requests this from their
execution logic.
A caveat is for idle-mode jobs, for which the idle-mode constraint will usurp the
timeliness of the reschedule. For an idle-mode job, no deadline is given.
final long elapsedNowMillis = SystemClock.elapsedRealtime();
final JobInfo job = failureToReschedule.getJob();
final long initialBackoffMillis = job.getInitialBackoffMillis();
final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
long delayMillis;
switch (job.getBackoffPolicy()) {
case JobInfo.BACKOFF_POLICY_LINEAR:
delayMillis = initialBackoffMillis * backoffAttempts;
break;
default:
if (DEBUG) {
Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
}
case JobInfo.BACKOFF_POLICY_EXPONENTIAL:
delayMillis =
(long) Math.scalb(initialBackoffMillis, backoffAttempts - 1);
break;
}
delayMillis =
Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
return new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis,
JobStatus.NO_LATEST_RUNTIME, backoffAttempts);
|
private com.android.server.job.controllers.JobStatus | getRescheduleJobForPeriodic(com.android.server.job.controllers.JobStatus periodicToReschedule)Called after a periodic has executed so we can to re-add it. We take the last execution time
of the job to be the time of completion (i.e. the time at which this function is called).
This could be inaccurate b/c the job can run for as long as
{@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
to underscheduling at least, rather than if we had taken the last execution time to be the
start of the execution.
final long elapsedNow = SystemClock.elapsedRealtime();
// Compute how much of the period is remaining.
long runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0);
long newEarliestRunTimeElapsed = elapsedNow + runEarly;
long period = periodicToReschedule.getJob().getIntervalMillis();
long newLatestRuntimeElapsed = newEarliestRunTimeElapsed + period;
if (DEBUG) {
Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
}
return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed,
newLatestRuntimeElapsed, 0 /* backoffAttempt */);
|
private boolean | isCurrentlyActiveLocked(com.android.server.job.controllers.JobStatus job)
for (int i=0; i<mActiveServices.size(); i++) {
JobServiceContext serviceContext = mActiveServices.get(i);
final JobStatus running = serviceContext.getRunningJob();
if (running != null && running.matches(job.getUid(), job.getJobId())) {
return true;
}
}
return false;
|
public void | onBootPhase(int phase)
if (PHASE_SYSTEM_SERVICES_READY == phase) {
// Register br for package removals and user removals.
final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
getContext().registerReceiverAsUser(
mBroadcastReceiver, UserHandle.ALL, filter, null, null);
final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
getContext().registerReceiverAsUser(
mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
} else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
synchronized (mJobs) {
// Let's go!
mReadyToRock = true;
mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
BatteryStats.SERVICE_NAME));
// Create the "runners".
for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
mActiveServices.add(
new JobServiceContext(this, mBatteryStats,
getContext().getMainLooper()));
}
// Attach jobs to their controllers.
ArraySet<JobStatus> jobs = mJobs.getJobs();
for (int i=0; i<jobs.size(); i++) {
JobStatus job = jobs.valueAt(i);
for (int controller=0; controller<mControllers.size(); controller++) {
mControllers.get(controller).maybeStartTrackingJob(job);
}
}
// GO GO GO!
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
}
}
|
public void | onControllerStateChanged()Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
some controller's state has changed, so as to run through the list of jobs and start/stop
any that are eligible.
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
|
public void | onJobCompleted(com.android.server.job.controllers.JobStatus jobStatus, boolean needsReschedule)A job just finished executing. We fetch the
{@link com.android.server.job.controllers.JobStatus} from the store and depending on
whether we want to reschedule we readd it to the controllers.
if (DEBUG) {
Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
}
if (!stopTrackingJob(jobStatus)) {
if (DEBUG) {
Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
}
return;
}
if (needsReschedule) {
JobStatus rescheduled = getRescheduleJobForFailure(jobStatus);
startTrackingJob(rescheduled);
} else if (jobStatus.getJob().isPeriodic()) {
JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
startTrackingJob(rescheduledPeriodic);
}
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
|
public void | onRunJobNow(com.android.server.job.controllers.JobStatus jobStatus)
mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
|
public void | onStart()
publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
|
public void | onStartUser(int userHandle)
mStartedUsers.add(userHandle);
// Let's kick any outstanding jobs for this user.
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
|
public void | onStopUser(int userHandle)
mStartedUsers.remove(Integer.valueOf(userHandle));
|
public int | schedule(android.app.job.JobInfo job, int uId)Entry point from client to schedule the provided job.
This cancels the job if it's already been scheduled, and replaces it with the one provided.
JobStatus jobStatus = new JobStatus(job, uId);
cancelJob(uId, job.getId());
startTrackingJob(jobStatus);
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
return JobScheduler.RESULT_SUCCESS;
|
private void | startTrackingJob(com.android.server.job.controllers.JobStatus jobStatus)Called when we have a job status object that we need to insert in our
{@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
about.
boolean update;
boolean rocking;
synchronized (mJobs) {
update = mJobs.add(jobStatus);
rocking = mReadyToRock;
}
if (rocking) {
for (int i=0; i<mControllers.size(); i++) {
StateController controller = mControllers.get(i);
if (update) {
controller.maybeStopTrackingJob(jobStatus);
}
controller.maybeStartTrackingJob(jobStatus);
}
}
|
private boolean | stopJobOnServiceContextLocked(com.android.server.job.controllers.JobStatus job)
for (int i=0; i<mActiveServices.size(); i++) {
JobServiceContext jsc = mActiveServices.get(i);
final JobStatus executing = jsc.getRunningJob();
if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
jsc.cancelExecutingJob();
return true;
}
}
return false;
|
private boolean | stopTrackingJob(com.android.server.job.controllers.JobStatus jobStatus)Called when we want to remove a JobStatus object that we've finished executing. Returns the
object removed.
boolean removed;
boolean rocking;
synchronized (mJobs) {
// Remove from store as well as controllers.
removed = mJobs.remove(jobStatus);
rocking = mReadyToRock;
}
if (removed && rocking) {
for (int i=0; i<mControllers.size(); i++) {
StateController controller = mControllers.get(i);
controller.maybeStopTrackingJob(jobStatus);
}
}
return removed;
|