summaryrefslogtreecommitdiffstats
path: root/core/java/android/app/job
diff options
context:
space:
mode:
authorChristopher Tate <ctate@google.com>2014-06-09 19:50:00 -0700
committerChristopher Tate <ctate@google.com>2014-06-10 12:51:55 -0700
commit7060b04f6d92351b67222e636ab378a0273bf3e7 (patch)
tree82fce1e04dd58a5d79895d0869b3b0adeffbb417 /core/java/android/app/job
parent6d7a25f317be60ae8a4d8806e517052be2398753 (diff)
downloadframeworks_base-7060b04f6d92351b67222e636ab378a0273bf3e7.zip
frameworks_base-7060b04f6d92351b67222e636ab378a0273bf3e7.tar.gz
frameworks_base-7060b04f6d92351b67222e636ab378a0273bf3e7.tar.bz2
Out with the old; in with the new
Switch to the official "JobScheduler" etc naming. Bug 14997851 Change-Id: I73a61aaa9af0740c114d08188bd97c52f3ac86b7
Diffstat (limited to 'core/java/android/app/job')
-rw-r--r--core/java/android/app/job/IJobCallback.aidl53
-rw-r--r--core/java/android/app/job/IJobScheduler.aidl30
-rw-r--r--core/java/android/app/job/IJobService.aidl32
-rw-r--r--core/java/android/app/job/JobInfo.aidl19
-rw-r--r--core/java/android/app/job/JobInfo.java432
-rw-r--r--core/java/android/app/job/JobParameters.aidl19
-rw-r--r--core/java/android/app/job/JobParameters.java93
-rw-r--r--core/java/android/app/job/JobScheduler.java72
-rw-r--r--core/java/android/app/job/JobService.java260
9 files changed, 1010 insertions, 0 deletions
diff --git a/core/java/android/app/job/IJobCallback.aidl b/core/java/android/app/job/IJobCallback.aidl
new file mode 100644
index 0000000..2d3948f
--- /dev/null
+++ b/core/java/android/app/job/IJobCallback.aidl
@@ -0,0 +1,53 @@
+/**
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.job;
+
+/**
+ * The server side of the JobScheduler IPC protocols. The app-side implementation
+ * invokes on this interface to indicate completion of the (asynchronous) instructions
+ * issued by the server.
+ *
+ * In all cases, the 'who' parameter is the caller's service binder, used to track
+ * which Job Service instance is reporting.
+ *
+ * {@hide}
+ */
+interface IJobCallback {
+ /**
+ * Immediate callback to the system after sending a start signal, used to quickly detect ANR.
+ *
+ * @param jobId Unique integer used to identify this job.
+ * @param ongoing True to indicate that the client is processing the job. False if the job is
+ * complete
+ */
+ void acknowledgeStartMessage(int jobId, boolean ongoing);
+ /**
+ * Immediate callback to the system after sending a stop signal, used to quickly detect ANR.
+ *
+ * @param jobId Unique integer used to identify this job.
+ * @param reschedule Whether or not to reschedule this job.
+ */
+ void acknowledgeStopMessage(int jobId, boolean reschedule);
+ /*
+ * Tell the job manager that the client is done with its execution, so that it can go on to
+ * the next one and stop attributing wakelock time to us etc.
+ *
+ * @param jobId Unique integer used to identify this job.
+ * @param reschedule Whether or not to reschedule this job.
+ */
+ void jobFinished(int jobId, boolean reschedule);
+}
diff --git a/core/java/android/app/job/IJobScheduler.aidl b/core/java/android/app/job/IJobScheduler.aidl
new file mode 100644
index 0000000..f1258ae
--- /dev/null
+++ b/core/java/android/app/job/IJobScheduler.aidl
@@ -0,0 +1,30 @@
+/**
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.job;
+
+import android.app.job.JobInfo;
+
+ /**
+ * IPC interface that supports the app-facing {@link #JobScheduler} api.
+ * {@hide}
+ */
+interface IJobScheduler {
+ int schedule(in JobInfo job);
+ void cancel(int jobId);
+ void cancelAll();
+ List<JobInfo> getAllPendingJobs();
+}
diff --git a/core/java/android/app/job/IJobService.aidl b/core/java/android/app/job/IJobService.aidl
new file mode 100644
index 0000000..63f8b81
--- /dev/null
+++ b/core/java/android/app/job/IJobService.aidl
@@ -0,0 +1,32 @@
+/**
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.job;
+
+import android.app.job.JobParameters;
+
+/**
+ * Interface that the framework uses to communicate with application code that implements a
+ * JobService. End user code does not implement this interface directly; instead, the app's
+ * service implementation will extend android.app.job.JobService.
+ * {@hide}
+ */
+oneway interface IJobService {
+ /** Begin execution of application's job. */
+ void startJob(in JobParameters jobParams);
+ /** Stop execution of application's task. */
+ void stopJob(in JobParameters jobParams);
+}
diff --git a/core/java/android/app/job/JobInfo.aidl b/core/java/android/app/job/JobInfo.aidl
new file mode 100644
index 0000000..7b198a8
--- /dev/null
+++ b/core/java/android/app/job/JobInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.job;
+
+parcelable JobInfo;
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
new file mode 100644
index 0000000..a22e4cd
--- /dev/null
+++ b/core/java/android/app/job/JobInfo.java
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.app.job;
+
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+/**
+ * Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the
+ * parameters required to schedule work against the calling application. These are constructed
+ * using the {@link JobInfo.Builder}.
+ */
+public class JobInfo implements Parcelable {
+ public interface NetworkType {
+ /** Default. */
+ public final int NONE = 0;
+ /** This job requires network connectivity. */
+ public final int ANY = 1;
+ /** This job requires network connectivity that is unmetered. */
+ public final int UNMETERED = 2;
+ }
+
+ /**
+ * Amount of backoff a job has initially by default, in milliseconds.
+ * @hide.
+ */
+ public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 5000L;
+
+ /**
+ * Default type of backoff.
+ * @hide
+ */
+ public static final int DEFAULT_BACKOFF_POLICY = BackoffPolicy.EXPONENTIAL;
+ /**
+ * Maximum backoff we allow for a job, in milliseconds.
+ * @hide
+ */
+ public static final long MAX_BACKOFF_DELAY_MILLIS = 24 * 60 * 60 * 1000; // 24 hours.
+
+ /**
+ * Linear: retry_time(failure_time, t) = failure_time + initial_retry_delay * t, t >= 1
+ * Expon: retry_time(failure_time, t) = failure_time + initial_retry_delay ^ t, t >= 1
+ */
+ public interface BackoffPolicy {
+ public final int LINEAR = 0;
+ public final int EXPONENTIAL = 1;
+ }
+
+ private final int jobId;
+ // TODO: Change this to use PersistableBundle when that lands in master.
+ private final PersistableBundle extras;
+ private final ComponentName service;
+ private final boolean requireCharging;
+ private final boolean requireDeviceIdle;
+ private final boolean hasEarlyConstraint;
+ private final boolean hasLateConstraint;
+ private final int networkCapabilities;
+ private final long minLatencyMillis;
+ private final long maxExecutionDelayMillis;
+ private final boolean isPeriodic;
+ private final long intervalMillis;
+ private final long initialBackoffMillis;
+ private final int backoffPolicy;
+
+ /**
+ * Unique job id associated with this class. This is assigned to your job by the scheduler.
+ */
+ public int getId() {
+ return jobId;
+ }
+
+ /**
+ * Bundle of extras which are returned to your application at execution time.
+ */
+ public PersistableBundle getExtras() {
+ return extras;
+ }
+
+ /**
+ * Name of the service endpoint that will be called back into by the JobScheduler.
+ */
+ public ComponentName getService() {
+ return service;
+ }
+
+ /**
+ * Whether this job needs the device to be plugged in.
+ */
+ public boolean isRequireCharging() {
+ return requireCharging;
+ }
+
+ /**
+ * Whether this job needs the device to be in an Idle maintenance window.
+ */
+ public boolean isRequireDeviceIdle() {
+ return requireDeviceIdle;
+ }
+
+ /**
+ * See {@link android.app.job.JobInfo.NetworkType} for a description of this value.
+ */
+ public int getNetworkCapabilities() {
+ return networkCapabilities;
+ }
+
+ /**
+ * Set for a job that does not recur periodically, to specify a delay after which the job
+ * will be eligible for execution. This value is not set if the job recurs periodically.
+ */
+ public long getMinLatencyMillis() {
+ return minLatencyMillis;
+ }
+
+ /**
+ * See {@link Builder#setOverrideDeadline(long)}. This value is not set if the job recurs
+ * periodically.
+ */
+ public long getMaxExecutionDelayMillis() {
+ return maxExecutionDelayMillis;
+ }
+
+ /**
+ * Track whether this job will repeat with a given period.
+ */
+ public boolean isPeriodic() {
+ return isPeriodic;
+ }
+
+ /**
+ * Set to the interval between occurrences of this job. This value is <b>not</b> set if the
+ * job does not recur periodically.
+ */
+ public long getIntervalMillis() {
+ return intervalMillis;
+ }
+
+ /**
+ * The amount of time the JobScheduler will wait before rescheduling a failed job. This value
+ * will be increased depending on the backoff policy specified at job creation time. Defaults
+ * to 5 seconds.
+ */
+ public long getInitialBackoffMillis() {
+ return initialBackoffMillis;
+ }
+
+ /**
+ * See {@link android.app.job.JobInfo.BackoffPolicy} for an explanation of the values this field
+ * can take. This defaults to exponential.
+ */
+ public int getBackoffPolicy() {
+ return backoffPolicy;
+ }
+
+ /**
+ * User can specify an early constraint of 0L, which is valid, so we keep track of whether the
+ * function was called at all.
+ * @hide
+ */
+ public boolean hasEarlyConstraint() {
+ return hasEarlyConstraint;
+ }
+
+ /**
+ * User can specify a late constraint of 0L, which is valid, so we keep track of whether the
+ * function was called at all.
+ * @hide
+ */
+ public boolean hasLateConstraint() {
+ return hasLateConstraint;
+ }
+
+ private JobInfo(Parcel in) {
+ jobId = in.readInt();
+ extras = in.readPersistableBundle();
+ service = in.readParcelable(null);
+ requireCharging = in.readInt() == 1;
+ requireDeviceIdle = in.readInt() == 1;
+ networkCapabilities = in.readInt();
+ minLatencyMillis = in.readLong();
+ maxExecutionDelayMillis = in.readLong();
+ isPeriodic = in.readInt() == 1;
+ intervalMillis = in.readLong();
+ initialBackoffMillis = in.readLong();
+ backoffPolicy = in.readInt();
+ hasEarlyConstraint = in.readInt() == 1;
+ hasLateConstraint = in.readInt() == 1;
+ }
+
+ private JobInfo(JobInfo.Builder b) {
+ jobId = b.mJobId;
+ extras = b.mExtras;
+ service = b.mJobService;
+ requireCharging = b.mRequiresCharging;
+ requireDeviceIdle = b.mRequiresDeviceIdle;
+ networkCapabilities = b.mNetworkCapabilities;
+ minLatencyMillis = b.mMinLatencyMillis;
+ maxExecutionDelayMillis = b.mMaxExecutionDelayMillis;
+ isPeriodic = b.mIsPeriodic;
+ intervalMillis = b.mIntervalMillis;
+ initialBackoffMillis = b.mInitialBackoffMillis;
+ backoffPolicy = b.mBackoffPolicy;
+ hasEarlyConstraint = b.mHasEarlyConstraint;
+ hasLateConstraint = b.mHasLateConstraint;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(jobId);
+ out.writePersistableBundle(extras);
+ out.writeParcelable(service, flags);
+ out.writeInt(requireCharging ? 1 : 0);
+ out.writeInt(requireDeviceIdle ? 1 : 0);
+ out.writeInt(networkCapabilities);
+ out.writeLong(minLatencyMillis);
+ out.writeLong(maxExecutionDelayMillis);
+ out.writeInt(isPeriodic ? 1 : 0);
+ out.writeLong(intervalMillis);
+ out.writeLong(initialBackoffMillis);
+ out.writeInt(backoffPolicy);
+ out.writeInt(hasEarlyConstraint ? 1 : 0);
+ out.writeInt(hasLateConstraint ? 1 : 0);
+ }
+
+ public static final Creator<JobInfo> CREATOR = new Creator<JobInfo>() {
+ @Override
+ public JobInfo createFromParcel(Parcel in) {
+ return new JobInfo(in);
+ }
+
+ @Override
+ public JobInfo[] newArray(int size) {
+ return new JobInfo[size];
+ }
+ };
+
+ /** Builder class for constructing {@link JobInfo} objects. */
+ public static final class Builder {
+ private int mJobId;
+ private PersistableBundle mExtras = PersistableBundle.EMPTY;
+ private ComponentName mJobService;
+ // Requirements.
+ private boolean mRequiresCharging;
+ private boolean mRequiresDeviceIdle;
+ private int mNetworkCapabilities;
+ // One-off parameters.
+ private long mMinLatencyMillis;
+ private long mMaxExecutionDelayMillis;
+ // Periodic parameters.
+ private boolean mIsPeriodic;
+ private boolean mHasEarlyConstraint;
+ private boolean mHasLateConstraint;
+ private long mIntervalMillis;
+ // Back-off parameters.
+ private long mInitialBackoffMillis = DEFAULT_INITIAL_BACKOFF_MILLIS;
+ private int mBackoffPolicy = DEFAULT_BACKOFF_POLICY;
+ /** Easy way to track whether the client has tried to set a back-off policy. */
+ private boolean mBackoffPolicySet = false;
+
+ /**
+ * @param jobId Application-provided id for this job. Subsequent calls to cancel, or
+ * jobs created with the same jobId, will update the pre-existing job with
+ * the same id.
+ * @param jobService The endpoint that you implement that will receive the callback from the
+ * JobScheduler.
+ */
+ public Builder(int jobId, ComponentName jobService) {
+ mJobService = jobService;
+ mJobId = jobId;
+ }
+
+ /**
+ * Set optional extras. This is persisted, so we only allow primitive types.
+ * @param extras Bundle containing extras you want the scheduler to hold on to for you.
+ */
+ public Builder setExtras(PersistableBundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
+ * Set some description of the kind of network capabilities you would like to have. This
+ * will be a parameter defined in {@link android.app.job.JobInfo.NetworkType}.
+ * Not calling this function means the network is not necessary.
+ * Bear in mind that calling this function defines network as a strict requirement for your
+ * job if the network requested is not available your job will never run. See
+ * {@link #setOverrideDeadline(long)} to change this behaviour.
+ */
+ public Builder setRequiredNetworkCapabilities(int networkCapabilities) {
+ mNetworkCapabilities = networkCapabilities;
+ return this;
+ }
+
+ /**
+ * Specify that to run this job, the device needs to be plugged in. This defaults to
+ * false.
+ * @param requiresCharging Whether or not the device is plugged in.
+ */
+ public Builder setRequiresCharging(boolean requiresCharging) {
+ mRequiresCharging = requiresCharging;
+ return this;
+ }
+
+ /**
+ * Specify that to run, the job needs the device to be in idle mode. This defaults to
+ * false.
+ * <p>Idle mode is a loose definition provided by the system, which means that the device
+ * is not in use, and has not been in use for some time. As such, it is a good time to
+ * perform resource heavy jobs. Bear in mind that battery usage will still be attributed
+ * to your application, and surfaced to the user in battery stats.</p>
+ * @param requiresDeviceIdle Whether or not the device need be within an idle maintenance
+ * window.
+ */
+ public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) {
+ mRequiresDeviceIdle = requiresDeviceIdle;
+ return this;
+ }
+
+ /**
+ * Specify that this job should recur with the provided interval, not more than once per
+ * period. You have no control over when within this interval this job will be executed,
+ * only the guarantee that it will be executed at most once within this interval.
+ * A periodic job will be repeated until the phone is turned off, however it will only be
+ * persisted beyond boot if the client app has declared the
+ * {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission. You can schedule
+ * periodic jobs without this permission, they simply will cease to exist after the phone
+ * restarts.
+ * Setting this function on the builder with {@link #setMinimumLatency(long)} or
+ * {@link #setOverrideDeadline(long)} will result in an error.
+ * @param intervalMillis Millisecond interval for which this job will repeat.
+ */
+ public Builder setPeriodic(long intervalMillis) {
+ mIsPeriodic = true;
+ mIntervalMillis = intervalMillis;
+ mHasEarlyConstraint = mHasLateConstraint = true;
+ return this;
+ }
+
+ /**
+ * Specify that this job should be delayed by the provided amount of time.
+ * Because it doesn't make sense setting this property on a periodic job, doing so will
+ * throw an {@link java.lang.IllegalArgumentException} when
+ * {@link android.app.job.JobInfo.Builder#build()} is called.
+ * @param minLatencyMillis Milliseconds before which this job will not be considered for
+ * execution.
+ */
+ public Builder setMinimumLatency(long minLatencyMillis) {
+ mMinLatencyMillis = minLatencyMillis;
+ mHasEarlyConstraint = true;
+ return this;
+ }
+
+ /**
+ * Set deadline which is the maximum scheduling latency. The job will be run by this
+ * deadline even if other requirements are not met. Because it doesn't make sense setting
+ * this property on a periodic job, doing so will throw an
+ * {@link java.lang.IllegalArgumentException} when
+ * {@link android.app.job.JobInfo.Builder#build()} is called.
+ */
+ public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
+ mMaxExecutionDelayMillis = maxExecutionDelayMillis;
+ mHasLateConstraint = true;
+ return this;
+ }
+
+ /**
+ * Set up the back-off/retry policy.
+ * This defaults to some respectable values: {5 seconds, Exponential}. We cap back-off at
+ * 1hr.
+ * Note that trying to set a backoff criteria for a job with
+ * {@link #setRequiresDeviceIdle(boolean)} will throw an exception when you call build().
+ * This is because back-off typically does not make sense for these types of jobs. See
+ * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)}
+ * for more description of the return value for the case of a job executing while in idle
+ * mode.
+ * @param initialBackoffMillis Millisecond time interval to wait initially when job has
+ * failed.
+ * @param backoffPolicy is one of {@link BackoffPolicy}
+ */
+ public Builder setBackoffCriteria(long initialBackoffMillis, int backoffPolicy) {
+ mBackoffPolicySet = true;
+ mInitialBackoffMillis = initialBackoffMillis;
+ mBackoffPolicy = backoffPolicy;
+ return this;
+ }
+
+ /**
+ * @return The job object to hand to the JobScheduler. This object is immutable.
+ */
+ public JobInfo build() {
+ mExtras = new PersistableBundle(mExtras); // Make our own copy.
+ // Check that a deadline was not set on a periodic job.
+ if (mIsPeriodic && (mMaxExecutionDelayMillis != 0L)) {
+ throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " +
+ "periodic job.");
+ }
+ if (mIsPeriodic && (mMinLatencyMillis != 0L)) {
+ throw new IllegalArgumentException("Can't call setMinimumLatency() on a " +
+ "periodic job");
+ }
+ if (mBackoffPolicySet && mRequiresDeviceIdle) {
+ throw new IllegalArgumentException("An idle mode job will not respect any" +
+ " back-off policy, so calling setBackoffCriteria with" +
+ " setRequiresDeviceIdle is an error.");
+ }
+ return new JobInfo(this);
+ }
+ }
+
+}
diff --git a/core/java/android/app/job/JobParameters.aidl b/core/java/android/app/job/JobParameters.aidl
new file mode 100644
index 0000000..e7551b9
--- /dev/null
+++ b/core/java/android/app/job/JobParameters.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.job;
+
+parcelable JobParameters;
diff --git a/core/java/android/app/job/JobParameters.java b/core/java/android/app/job/JobParameters.java
new file mode 100644
index 0000000..724856a
--- /dev/null
+++ b/core/java/android/app/job/JobParameters.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.app.job;
+
+import android.app.job.IJobCallback;
+import android.app.job.IJobCallback.Stub;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+/**
+ * Contains the parameters used to configure/identify your job. You do not create this object
+ * yourself, instead it is handed in to your application by the System.
+ */
+public class JobParameters implements Parcelable {
+
+ private final int jobId;
+ private final PersistableBundle extras;
+ private final IBinder callback;
+
+ /** @hide */
+ public JobParameters(int jobId, PersistableBundle extras, IBinder callback) {
+ this.jobId = jobId;
+ this.extras = extras;
+ this.callback = callback;
+ }
+
+ /**
+ * @return The unique id of this job, specified at creation time.
+ */
+ public int getJobId() {
+ return jobId;
+ }
+
+ /**
+ * @return The extras you passed in when constructing this job with
+ * {@link android.app.job.JobInfo.Builder#setExtras(android.os.PersistableBundle)}. This will
+ * never be null. If you did not set any extras this will be an empty bundle.
+ */
+ public PersistableBundle getExtras() {
+ return extras;
+ }
+
+ /** @hide */
+ public IJobCallback getCallback() {
+ return IJobCallback.Stub.asInterface(callback);
+ }
+
+ private JobParameters(Parcel in) {
+ jobId = in.readInt();
+ extras = in.readPersistableBundle();
+ callback = in.readStrongBinder();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(jobId);
+ dest.writePersistableBundle(extras);
+ dest.writeStrongBinder(callback);
+ }
+
+ public static final Creator<JobParameters> CREATOR = new Creator<JobParameters>() {
+ @Override
+ public JobParameters createFromParcel(Parcel in) {
+ return new JobParameters(in);
+ }
+
+ @Override
+ public JobParameters[] newArray(int size) {
+ return new JobParameters[size];
+ }
+ };
+}
diff --git a/core/java/android/app/job/JobScheduler.java b/core/java/android/app/job/JobScheduler.java
new file mode 100644
index 0000000..7fe192c
--- /dev/null
+++ b/core/java/android/app/job/JobScheduler.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.app.job;
+
+import java.util.List;
+
+import android.content.Context;
+
+/**
+ * Class for scheduling various types of jobs with the scheduling framework on the device.
+ *
+ * <p>You do not
+ * instantiate this class directly; instead, retrieve it through
+ * {@link android.content.Context#getSystemService
+ * Context.getSystemService(Context.JOB_SCHEDULER_SERVICE)}.
+ */
+public abstract class JobScheduler {
+ /**
+ * Returned from {@link #schedule(JobInfo)} when an invalid parameter was supplied. This can occur
+ * if the run-time for your job is too short, or perhaps the system can't resolve the
+ * requisite {@link JobService} in your package.
+ */
+ public static final int RESULT_FAILURE = 0;
+ /**
+ * Returned from {@link #schedule(JobInfo)} if this application has made too many requests for
+ * work over too short a time.
+ */
+ // TODO: Determine if this is necessary.
+ public static final int RESULT_SUCCESS = 1;
+
+ /**
+ * @param job The job you wish scheduled. See
+ * {@link android.app.job.JobInfo.Builder JobInfo.Builder} for more detail on the sorts of jobs
+ * you can schedule.
+ * @return If >0, this int returns the jobId of the successfully scheduled job.
+ * Otherwise you have to compare the return value to the error codes defined in this class.
+ */
+ public abstract int schedule(JobInfo job);
+
+ /**
+ * Cancel a job that is pending in the JobScheduler.
+ * @param jobId unique identifier for this job. Obtain this value from the jobs returned by
+ * {@link #getAllPendingJobs()}.
+ * @return
+ */
+ public abstract void cancel(int jobId);
+
+ /**
+ * Cancel all jobs that have been registered with the JobScheduler by this package.
+ */
+ public abstract void cancelAll();
+
+ /**
+ * @return a list of all the jobs registered by this package that have not yet been executed.
+ */
+ public abstract List<JobInfo> getAllPendingJobs();
+
+}
diff --git a/core/java/android/app/job/JobService.java b/core/java/android/app/job/JobService.java
new file mode 100644
index 0000000..eea0268
--- /dev/null
+++ b/core/java/android/app/job/JobService.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.app.job;
+
+import android.app.Service;
+import android.app.job.IJobCallback;
+import android.app.job.IJobService;
+import android.app.job.IJobService.Stub;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * <p>Entry point for the callback from the {@link android.app.job.JobScheduler}.</p>
+ * <p>This is the base class that handles asynchronous requests that were previously scheduled. You
+ * are responsible for overriding {@link JobService#onStartJob(JobParameters)}, which is where
+ * you will implement your job logic.</p>
+ * <p>This service executes each incoming job on a {@link android.os.Handler} running on your
+ * application's main thread. This means that you <b>must</b> offload your execution logic to
+ * another thread/handler/{@link android.os.AsyncTask} of your choosing. Not doing so will result
+ * in blocking any future callbacks from the JobManager - specifically
+ * {@link #onStopJob(android.app.job.JobParameters)}, which is meant to inform you that the
+ * scheduling requirements are no longer being met.</p>
+ */
+public abstract class JobService extends Service {
+ private static final String TAG = "JobService";
+
+ /**
+ * Job services must be protected with this permission:
+ *
+ * <pre class="prettyprint">
+ * <service android:name="MyJobService"
+ * android:permission="android.permission.BIND_JOB_SERVICE" >
+ * ...
+ * </service>
+ * </pre>
+ *
+ * <p>If a job service is declared in the manifest but not protected with this
+ * permission, that service will be ignored by the OS.
+ */
+ public static final String PERMISSION_BIND =
+ "android.permission.BIND_JOB_SERVICE";
+
+ /**
+ * Identifier for a message that will result in a call to
+ * {@link #onStartJob(android.app.job.JobParameters)}.
+ */
+ private final int MSG_EXECUTE_JOB = 0;
+ /**
+ * Message that will result in a call to {@link #onStopJob(android.app.job.JobParameters)}.
+ */
+ private final int MSG_STOP_JOB = 1;
+ /**
+ * Message that the client has completed execution of this job.
+ */
+ private final int MSG_JOB_FINISHED = 2;
+
+ /** Lock object for {@link #mHandler}. */
+ private final Object mHandlerLock = new Object();
+
+ /**
+ * Handler we post jobs to. Responsible for calling into the client logic, and handling the
+ * callback to the system.
+ */
+ @GuardedBy("mHandlerLock")
+ JobHandler mHandler;
+
+ /** Binder for this service. */
+ IJobService mBinder = new IJobService.Stub() {
+ @Override
+ public void startJob(JobParameters jobParams) {
+ ensureHandler();
+ Message m = Message.obtain(mHandler, MSG_EXECUTE_JOB, jobParams);
+ m.sendToTarget();
+ }
+ @Override
+ public void stopJob(JobParameters jobParams) {
+ ensureHandler();
+ Message m = Message.obtain(mHandler, MSG_STOP_JOB, jobParams);
+ m.sendToTarget();
+ }
+ };
+
+ /** @hide */
+ void ensureHandler() {
+ synchronized (mHandlerLock) {
+ if (mHandler == null) {
+ mHandler = new JobHandler(getMainLooper());
+ }
+ }
+ }
+
+ /**
+ * Runs on application's main thread - callbacks are meant to offboard work to some other
+ * (app-specified) mechanism.
+ * @hide
+ */
+ class JobHandler extends Handler {
+ JobHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ final JobParameters params = (JobParameters) msg.obj;
+ switch (msg.what) {
+ case MSG_EXECUTE_JOB:
+ try {
+ boolean workOngoing = JobService.this.onStartJob(params);
+ ackStartMessage(params, workOngoing);
+ } catch (Exception e) {
+ Log.e(TAG, "Error while executing job: " + params.getJobId());
+ throw new RuntimeException(e);
+ }
+ break;
+ case MSG_STOP_JOB:
+ try {
+ boolean ret = JobService.this.onStopJob(params);
+ ackStopMessage(params, ret);
+ } catch (Exception e) {
+ Log.e(TAG, "Application unable to handle onStopJob.", e);
+ throw new RuntimeException(e);
+ }
+ break;
+ case MSG_JOB_FINISHED:
+ final boolean needsReschedule = (msg.arg2 == 1);
+ IJobCallback callback = params.getCallback();
+ if (callback != null) {
+ try {
+ callback.jobFinished(params.getJobId(), needsReschedule);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error reporting job finish to system: binder has gone" +
+ "away.");
+ }
+ } else {
+ Log.e(TAG, "finishJob() called for a nonexistent job id.");
+ }
+ break;
+ default:
+ Log.e(TAG, "Unrecognised message received.");
+ break;
+ }
+ }
+
+ private void ackStartMessage(JobParameters params, boolean workOngoing) {
+ final IJobCallback callback = params.getCallback();
+ final int jobId = params.getJobId();
+ if (callback != null) {
+ try {
+ callback.acknowledgeStartMessage(jobId, workOngoing);
+ } catch(RemoteException e) {
+ Log.e(TAG, "System unreachable for starting job.");
+ }
+ } else {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Attempting to ack a job that has already been processed.");
+ }
+ }
+ }
+
+ private void ackStopMessage(JobParameters params, boolean reschedule) {
+ final IJobCallback callback = params.getCallback();
+ final int jobId = params.getJobId();
+ if (callback != null) {
+ try {
+ callback.acknowledgeStopMessage(jobId, reschedule);
+ } catch(RemoteException e) {
+ Log.e(TAG, "System unreachable for stopping job.");
+ }
+ } else {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Attempting to ack a job that has already been processed.");
+ }
+ }
+ }
+ }
+
+ /** @hide */
+ public final IBinder onBind(Intent intent) {
+ return mBinder.asBinder();
+ }
+
+ /**
+ * Override this method with the callback logic for your job. Any such logic needs to be
+ * performed on a separate thread, as this function is executed on your application's main
+ * thread.
+ *
+ * @param params Parameters specifying info about this job, including the extras bundle you
+ * optionally provided at job-creation time.
+ * @return True if your service needs to process the work (on a separate thread). False if
+ * there's no more work to be done for this job.
+ */
+ public abstract boolean onStartJob(JobParameters params);
+
+ /**
+ * This method is called if the system has determined that you must stop execution of your job
+ * even before you've had a chance to call {@link #jobFinished(JobParameters, boolean)}.
+ *
+ * <p>This will happen if the requirements specified at schedule time are no longer met. For
+ * example you may have requested WiFi with
+ * {@link android.app.job.JobInfo.Builder#setRequiredNetworkCapabilities(int)}, yet while your
+ * job was executing the user toggled WiFi. Another example is if you had specified
+ * {@link android.app.job.JobInfo.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its
+ * idle maintenance window. You are solely responsible for the behaviour of your application
+ * upon receipt of this message; your app will likely start to misbehave if you ignore it. One
+ * immediate repercussion is that the system will cease holding a wakelock for you.</p>
+ *
+ * @param params Parameters specifying info about this job.
+ * @return True to indicate to the JobManager whether you'd like to reschedule this job based
+ * on the retry criteria provided at job creation-time. False to drop the job. Regardless of
+ * the value returned, your job must stop executing.
+ */
+ public abstract boolean onStopJob(JobParameters params);
+
+ /**
+ * Callback to inform the JobManager you've finished executing. This can be called from any
+ * thread, as it will ultimately be run on your application's main thread. When the system
+ * receives this message it will release the wakelock being held.
+ * <p>
+ * You can specify post-execution behaviour to the scheduler here with
+ * <code>needsReschedule </code>. This will apply a back-off timer to your job based on
+ * the default, or what was set with
+ * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)}. The original
+ * requirements are always honoured even for a backed-off job. Note that a job running in
+ * idle mode will not be backed-off. Instead what will happen is the job will be re-added
+ * to the queue and re-executed within a future idle maintenance window.
+ * </p>
+ *
+ * @param params Parameters specifying system-provided info about this job, this was given to
+ * your application in {@link #onStartJob(JobParameters)}.
+ * @param needsReschedule True if this job is complete, false if you want the JobManager to
+ * reschedule you.
+ */
+ public final void jobFinished(JobParameters params, boolean needsReschedule) {
+ ensureHandler();
+ Message m = Message.obtain(mHandler, MSG_JOB_FINISHED, params);
+ m.arg2 = needsReschedule ? 1 : 0;
+ m.sendToTarget();
+ }
+} \ No newline at end of file