import android.content.Context;
/**
- * Class for scheduling various types of jobs with the scheduling framework on the device.
+ * This is an API for scheduling various types of jobs against the framework that will be executed
+ * in your application's own process.
+ * <p>
* See {@link android.app.job.JobInfo} for more description of the types of jobs that can be run
- * and how to construct them.
+ * and how to construct them. You will construct these JobInfo objects and pass them to the
+ * JobScheduler with {@link #schedule(JobInfo)}. When the criteria declared are met, the
+ * system will execute this job on your application's {@link android.app.job.JobService}.
+ * You identify which JobService is meant to execute the logic for your job when you create the
+ * JobInfo with {@link android.app.job.JobInfo.Builder#Builder(int, android.content.ComponentName)}.
+ * </p>
+ * <p>
* The framework will be intelligent about when you receive your callbacks, and attempt to batch
* and defer them as much as possible. Typically if you don't specify a deadline on your job, it
* can be run at any moment depending on the current state of the JobScheduler's internal queue,
* however it might be deferred as long as until the next time the device is connected to a power
* source.
+ * </p>
* <p>You do not
* instantiate this class directly; instead, retrieve it through
* {@link android.content.Context#getSystemService
List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
for (int i=0; i<jobsForUser.size(); i++) {
JobStatus toRemove = jobsForUser.get(i);
- if (DEBUG) {
- Slog.d(TAG, "Cancelling: " + toRemove);
- }
cancelJobLocked(toRemove);
}
}
List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
for (int i=0; i<jobsForUid.size(); i++) {
JobStatus toRemove = jobsForUid.get(i);
- if (DEBUG) {
- Slog.d(TAG, "Cancelling: " + toRemove);
- }
cancelJobLocked(toRemove);
}
}
private void queueReadyJobsForExecutionH() {
synchronized (mJobs) {
ArraySet<JobStatus> jobs = mJobs.getJobs();
+ if (DEBUG) {
+ Slog.d(TAG, "queuing all ready jobs for execution:");
+ }
for (int i=0; i<jobs.size(); i++) {
JobStatus job = jobs.valueAt(i);
if (isReadyToBeExecutedLocked(job)) {
+ if (DEBUG) {
+ Slog.d(TAG, " queued " + job.toShortString());
+ }
mPendingJobs.add(job);
} else if (isReadyToBeCancelledLocked(job)) {
stopJobOnServiceContextLocked(job);
}
}
+ if (DEBUG) {
+ final int queuedJobs = mPendingJobs.size();
+ if (queuedJobs == 0) {
+ Slog.d(TAG, "No jobs pending.");
+ } else {
+ Slog.d(TAG, queuedJobs + " jobs queued.");
+ }
+ }
}
}
private void maybeRunPendingJobsH() {
synchronized (mJobs) {
Iterator<JobStatus> it = mPendingJobs.iterator();
+ if (DEBUG) {
+ Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
+ }
while (it.hasNext()) {
JobStatus nextPending = it.next();
JobServiceContext availableContext = null;
mExecutionStartTimeElapsed = SystemClock.elapsedRealtime();
mVerb = VERB_BINDING;
+ scheduleOpTimeOut();
final Intent intent = new Intent().setComponent(job.getServiceComponent());
boolean binding = mContext.bindServiceAsUser(intent, this,
Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,
return;
}
this.service = IJobService.Stub.asInterface(service);
- // Remove all timeouts.
- mCallbackHandler.removeMessages(MSG_TIMEOUT);
final PowerManager pm =
(PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mRunningJob.getTag());
public void handleMessage(Message message) {
switch (message.what) {
case MSG_SERVICE_BOUND:
+ removeMessages(MSG_TIMEOUT);
handleServiceBoundH();
break;
case MSG_CALLBACK:
/** Start the job on the service. */
private void handleServiceBoundH() {
+ if (DEBUG) {
+ Slog.d(TAG, "MSG_SERVICE_BOUND for " + mRunningJob.toShortString());
+ }
if (mVerb != VERB_BINDING) {
Slog.e(TAG, "Sending onStartJob for a job that isn't pending. "
+ VERB_STRINGS[mVerb]);
/** Process MSG_TIMEOUT here. */
private void handleOpTimeoutH() {
- if (JobSchedulerService.DEBUG) {
- Slog.d(TAG, "MSG_TIMEOUT of " +
- mRunningJob.getServiceComponent().getShortClassName() + " : "
- + mParams.getJobId());
- }
-
- final int jobId = mParams.getJobId();
switch (mVerb) {
+ case VERB_BINDING:
+ Slog.e(TAG, "Time-out while trying to bind " + mRunningJob.toShortString() +
+ ", dropping.");
+ closeAndCleanupJobH(false /* needsReschedule */);
+ break;
case VERB_STARTING:
// Client unresponsive - wedged or failed to respond in time. We don't really
// know what happened so let's log it and notify the JobScheduler
// FINISHED/NO-RETRY.
Slog.e(TAG, "No response from client for onStartJob '" +
- mRunningJob.getServiceComponent().getShortClassName() + "' jId: "
- + jobId);
+ mRunningJob.toShortString());
closeAndCleanupJobH(false /* needsReschedule */);
break;
case VERB_STOPPING:
// At least we got somewhere, so fail but ask the JobScheduler to reschedule.
Slog.e(TAG, "No response from client for onStopJob, '" +
- mRunningJob.getServiceComponent().getShortClassName() + "' jId: "
- + jobId);
+ mRunningJob.toShortString());
closeAndCleanupJobH(true /* needsReschedule */);
break;
case VERB_EXECUTING:
// Not an error - client ran out of time.
Slog.i(TAG, "Client timed out while executing (no jobFinished received)." +
- " sending onStop. " +
- mRunningJob.getServiceComponent().getShortClassName() + "' jId: "
- + jobId);
+ " sending onStop. " + mRunningJob.toShortString());
sendStopMessageH();
break;
default:
- Slog.e(TAG, "Handling timeout for an unknown active job state: "
- + mRunningJob);
- return;
+ Slog.e(TAG, "Handling timeout for an invalid job state: " +
+ mRunningJob.toShortString() + ", dropping.");
+ closeAndCleanupJobH(false /* needsReschedule */);
}
}
} catch (RemoteException e) {
// Whatever.
}
- mWakeLock.release();
+ if (mWakeLock != null) {
+ mWakeLock.release();
+ }
mContext.unbindService(JobServiceContext.this);
mWakeLock = null;
mRunningJob = null;
removeMessages(MSG_SHUTDOWN_EXECUTION);
mCompletedListener.onJobCompleted(completedJob, reschedule);
}
+ }
- /**
- * Called when sending a message to the client, over whose execution we have no control. If
- * we haven't received a response in a certain amount of time, we want to give up and carry
- * on with life.
- */
- private void scheduleOpTimeOut() {
- mCallbackHandler.removeMessages(MSG_TIMEOUT);
+ /**
+ * Called when sending a message to the client, over whose execution we have no control. If
+ * we haven't received a response in a certain amount of time, we want to give up and carry
+ * on with life.
+ */
+ private void scheduleOpTimeOut() {
+ mCallbackHandler.removeMessages(MSG_TIMEOUT);
- final long timeoutMillis = (mVerb == VERB_EXECUTING) ?
- EXECUTING_TIMESLICE_MILLIS : OP_TIMEOUT_MILLIS;
- if (DEBUG) {
- Slog.d(TAG, "Scheduling time out for '" +
- mRunningJob.getServiceComponent().getShortClassName() + "' jId: " +
- mParams.getJobId() + ", in " + (timeoutMillis / 1000) + " s");
- }
- Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT);
- mCallbackHandler.sendMessageDelayed(m, timeoutMillis);
- mTimeoutElapsed = SystemClock.elapsedRealtime() + timeoutMillis;
+ final long timeoutMillis = (mVerb == VERB_EXECUTING) ?
+ EXECUTING_TIMESLICE_MILLIS : OP_TIMEOUT_MILLIS;
+ if (DEBUG) {
+ Slog.d(TAG, "Scheduling time out for '" +
+ mRunningJob.getServiceComponent().getShortClassName() + "' jId: " +
+ mParams.getJobId() + ", in " + (timeoutMillis / 1000) + " s");
}
+ Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT);
+ mCallbackHandler.sendMessageDelayed(m, timeoutMillis);
+ mTimeoutElapsed = SystemClock.elapsedRealtime() + timeoutMillis;
}
}