Merge "TaskManager API first pass."
diff --git a/Android.mk b/Android.mk
index 232f5bf..966c64d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -76,6 +76,8 @@
 	core/java/android/app/ISearchManagerCallback.aidl \
 	core/java/android/app/IServiceConnection.aidl \
 	core/java/android/app/IStopUserCallback.aidl \
+        core/java/android/app/task/ITaskCallback.aidl \
+        core/java/android/app/task/ITaskService.aidl \
 	core/java/android/app/IThumbnailReceiver.aidl \
 	core/java/android/app/IThumbnailRetriever.aidl \
 	core/java/android/app/ITransientNotification.aidl \
diff --git a/api/current.txt b/api/current.txt
index 0794685..e206528 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5166,6 +5166,26 @@
 
 }
 
+package android.app.task {
+
+  public class TaskParams implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getTaskId();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+  }
+
+  public abstract class TaskService extends android.app.Service {
+    ctor public TaskService();
+    method public final android.os.IBinder onBind(android.content.Intent);
+    method public abstract void onStartTask(android.app.task.TaskParams, android.os.Bundle);
+    method public abstract boolean onStopTask(android.app.task.TaskParams);
+    method public final void taskFinished(android.app.task.TaskParams, boolean);
+    field public static final java.lang.String PERMISSION_BIND = "android.permission.BIND_TASK_SERVICE";
+  }
+
+}
+
 package android.appwidget {
 
   public class AppWidgetHost {
@@ -7528,6 +7548,55 @@
     method public abstract void onStatusChanged(int);
   }
 
+  public class Task implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getBackoffPolicy();
+    method public android.os.Bundle getExtras();
+    method public long getInitialBackoffMillis();
+    method public long getIntervalMillis();
+    method public long getMaxExecutionDelayMillis();
+    method public long getMinLatencyMillis();
+    method public int getNetworkCapabilities();
+    method public java.lang.String getServiceClassName();
+    method public int getTaskId();
+    method public boolean isPeriodic();
+    method public boolean isRequireCharging();
+    method public boolean isRequireDeviceIdle();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+  }
+
+  public static abstract interface Task.BackoffPolicy {
+    field public static final int EXPONENTIAL = 1; // 0x1
+    field public static final int LINEAR = 0; // 0x0
+  }
+
+  public final class Task.Builder {
+    ctor public Task.Builder(int, java.lang.Class<android.app.task.TaskService>);
+    method public android.content.Task build();
+    method public android.content.Task.Builder setBackoffCriteria(long, int);
+    method public android.content.Task.Builder setExtras(android.os.Bundle);
+    method public android.content.Task.Builder setMinimumLatency(long);
+    method public android.content.Task.Builder setOverrideDeadline(long);
+    method public android.content.Task.Builder setPeriodic(long);
+    method public android.content.Task.Builder setRequiredNetworkCapabilities(int);
+    method public android.content.Task.Builder setRequiresCharging(boolean);
+    method public android.content.Task.Builder setRequiresDeviceIdle(boolean);
+  }
+
+  public static abstract interface Task.NetworkType {
+    field public static final int ANY = 0; // 0x0
+    field public static final int UNMETERED = 1; // 0x1
+  }
+
+  public abstract class TaskManager {
+    ctor public TaskManager();
+    method public abstract void cancel(int);
+    method public abstract void cancelAll();
+    method public abstract java.util.List<android.content.Task> getAllPendingTasks();
+    method public abstract int schedule(android.content.Task);
+  }
+
   public class UriMatcher {
     ctor public UriMatcher(int);
     method public void addURI(java.lang.String, java.lang.String, int);
diff --git a/core/java/android/app/task/ITaskCallback.aidl b/core/java/android/app/task/ITaskCallback.aidl
new file mode 100644
index 0000000..63fcdd8
--- /dev/null
+++ b/core/java/android/app/task/ITaskCallback.aidl
@@ -0,0 +1,52 @@
+/**
+ * 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.task;
+
+import android.app.task.ITaskService;
+import android.app.task.TaskParams;
+
+/**
+ * The server side of the TaskManager 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 Task Service instance is reporting.
+ *
+ * {@hide}
+ */
+interface ITaskCallback {
+    /**
+     * Immediate callback to the system after sending a start signal, used to quickly detect ANR.
+     *
+     * @param taskId Unique integer used to identify this task.
+     */
+    void acknowledgeStartMessage(int taskId);
+    /**
+     * Immediate callback to the system after sending a stop signal, used to quickly detect ANR.
+     *
+     * @param taskId Unique integer used to identify this task.
+     */
+    void acknowledgeStopMessage(int taskId);
+    /*
+     * Tell the task 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 taskId Unique integer used to identify this task.
+     */
+    void taskFinished(int taskId, boolean taskFailed);
+}
diff --git a/core/java/android/app/task/ITaskService.aidl b/core/java/android/app/task/ITaskService.aidl
new file mode 100644
index 0000000..c25abc5
--- /dev/null
+++ b/core/java/android/app/task/ITaskService.aidl
@@ -0,0 +1,35 @@
+/**
+ * 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.task;
+
+import android.app.task.ITaskCallback;
+import android.app.task.TaskParams;
+
+import android.os.Bundle;
+
+/**
+ * Interface that the framework uses to communicate with application code
+ * that implements a TaskService.  End user code does not implement this interface directly;
+ * instead, the app's idle service implementation will extend android.app.maintenance.IdleService.
+ * {@hide}
+ */
+oneway interface ITaskService {
+    /** Begin execution of application's task. */
+    void startTask(in TaskParams taskParams);
+    /** Stop execution of application's task. */
+    void stopTask(in TaskParams taskParams);
+}
diff --git a/core/java/android/app/task/TaskParams.aidl b/core/java/android/app/task/TaskParams.aidl
new file mode 100644
index 0000000..9b25855
--- /dev/null
+++ b/core/java/android/app/task/TaskParams.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.task;
+
+parcelable TaskParams;
\ No newline at end of file
diff --git a/core/java/android/app/task/TaskParams.java b/core/java/android/app/task/TaskParams.java
new file mode 100644
index 0000000..429281c
--- /dev/null
+++ b/core/java/android/app/task/TaskParams.java
@@ -0,0 +1,87 @@
+/*
+ * 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.task;
+
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Contains the parameters used to configure/identify your task. You do not create this object
+ * yourself, instead it is handed in to your application by the System.
+ */
+public class TaskParams implements Parcelable {
+
+    private final int taskId;
+    private final Bundle extras;
+    private final IBinder mCallback;
+
+    /**
+     * @return The unique id of this task, specified at creation time using
+     * {@link android.content.Task.Builder#Builder(int, Class)}.
+     */
+    public int getTaskId() {
+        return taskId;
+    }
+
+    /**
+     * @return The extras you passed in when constructing this task with
+     * {@link android.content.Task.Builder#setExtras(android.os.Bundle)}. This will
+     * never be null. If you did not set any extras this will be an empty bundle.
+     */
+    public Bundle getExtras() {
+        return extras;
+    }
+
+    /**
+     * @hide
+     */
+    public ITaskCallback getCallback() {
+        return ITaskCallback.Stub.asInterface(mCallback);
+    }
+
+    private TaskParams(Parcel in) {
+        taskId = in.readInt();
+        extras = in.readBundle();
+        mCallback = in.readStrongBinder();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(taskId);
+        dest.writeBundle(extras);
+        dest.writeStrongBinder(mCallback);
+    }
+
+    public static final Creator<TaskParams> CREATOR = new Creator<TaskParams>() {
+        @Override
+        public TaskParams createFromParcel(Parcel in) {
+            return new TaskParams(in);
+        }
+
+        @Override
+        public TaskParams[] newArray(int size) {
+            return new TaskParams[size];
+        }
+    };
+}
diff --git a/core/java/android/app/task/TaskService.java b/core/java/android/app/task/TaskService.java
new file mode 100644
index 0000000..d22f7f1
--- /dev/null
+++ b/core/java/android/app/task/TaskService.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.task;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+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.content.TaskManager}.</p>
+ * <p>This is the base class that handles asynchronous requests that were previously scheduled. You
+ * are responsible for overriding {@Link AbstracTaskService#onPerformTask(Bundle)}, which is where
+ * you will implement your task logic.</p>
+ * <p>This service executes each incoming task 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 TaskManager - specifically
+ * {@link #onStopTask(android.app.task.TaskParams)}, which is meant to inform you that the
+ * scheduling requirements are no longer being met.</p>
+ */
+public abstract class TaskService extends Service {
+    private static final String TAG = "TaskService";
+
+    /**
+     * Task services must be protected with this permission:
+     *
+     * <pre class="prettyprint">
+     *     <service android:name="MyTaskService"
+     *              android:permission="android.permission.BIND_TASK_SERVICE" >
+     *         ...
+     *     </service>
+     * </pre>
+     *
+     * <p>If a task 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_TASK_SERVICE";
+
+    /**
+     * Identifier for a message that will result in a call to
+     * {@link #onStartTask(android.app.task.TaskParams, android.os.Bundle)}.
+     */
+    private final int MSG_EXECUTE_TASK = 0;
+    /**
+     * Message that will result in a call to {@link #onStopTask(android.app.task.TaskParams)}.
+     */
+    private final int MSG_STOP_TASK = 1;
+    /**
+     * Message that the client has completed execution of this task.
+     */
+    private final int MSG_TASK_FINISHED = 2;
+
+    /** Lock object for {@link #mHandler}. */
+    private final Object mHandlerLock = new Object();
+
+    /**
+     * Handler we post tasks to. Responsible for calling into the client logic, and handling the
+     * callback to the system.
+     */
+    @GuardedBy("mHandlerLock")
+    TaskHandler mHandler;
+
+    /** Binder for this service. */
+    ITaskService mBinder = new ITaskService.Stub() {
+        @Override
+        public void startTask(TaskParams taskParams) {
+            ensureHandler();
+            Message m = Message.obtain(mHandler, MSG_EXECUTE_TASK, taskParams);
+            m.sendToTarget();
+        }
+        @Override
+        public void stopTask(TaskParams taskParams) {
+            ensureHandler();
+            Message m = Message.obtain(mHandler, MSG_STOP_TASK, taskParams);
+            m.sendToTarget();
+        }
+    };
+
+    /** @hide */
+    void ensureHandler() {
+        synchronized (mHandlerLock) {
+            if (mHandler == null) {
+                mHandler = new TaskHandler(getMainLooper());
+            }
+        }
+    }
+
+    /**
+     * Runs on application's main thread - callbacks are meant to offboard work to some other
+     * (app-specified) mechanism.
+     * @hide
+     */
+    class TaskHandler extends Handler {
+        TaskHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            final TaskParams params = (TaskParams) msg.obj;
+            switch (msg.what) {
+                case MSG_EXECUTE_TASK:
+                    try {
+                        TaskService.this.onStartTask(params);
+                    } catch (Exception e) {
+                        Log.e(TAG, "Error while executing task: " + params.getTaskId());
+                        throw new RuntimeException(e);
+                    } finally {
+                        maybeAckMessageReceived(params, MSG_EXECUTE_TASK);
+                    }
+                    break;
+                case MSG_STOP_TASK:
+                    try {
+                        TaskService.this.onStopTask(params);
+                    } catch (Exception e) {
+                        Log.e(TAG, "Application unable to handle onStopTask.", e);
+                        throw new RuntimeException(e);
+                    } finally {
+                        maybeAckMessageReceived(params, MSG_STOP_TASK);
+                    }
+                    break;
+                case MSG_TASK_FINISHED:
+                    final boolean needsReschedule = (msg.arg2 == 1);
+                    ITaskCallback callback = params.getCallback();
+                    if (callback != null) {
+                        try {
+                            callback.taskFinished(params.getTaskId(), needsReschedule);
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "Error reporting task finish to system: binder has gone" +
+                                    "away.");
+                        }
+                    } else {
+                        Log.e(TAG, "finishTask() called for a nonexistent task id.");
+                    }
+                    break;
+                default:
+                    Log.e(TAG, "Unrecognised message received.");
+                    break;
+            }
+        }
+
+        /**
+         * Messages come in on the application's main thread, so rather than run the risk of
+         * waiting for an app that may be doing something foolhardy, we ack to the system after
+         * processing a message. This allows us to throw up an ANR dialogue as quickly as possible.
+         * @param params id of the task we're acking.
+         * @param state Information about what message we're acking.
+         */
+        private void maybeAckMessageReceived(TaskParams params, int state) {
+            final ITaskCallback callback = params.getCallback();
+            final int taskId = params.getTaskId();
+            if (callback != null) {
+                try {
+                    if (state == MSG_EXECUTE_TASK) {
+                        callback.acknowledgeStartMessage(taskId);
+                    } else if (state == MSG_STOP_TASK) {
+                        callback.acknowledgeStopMessage(taskId);
+                    }
+                } catch(RemoteException e) {
+                    Log.e(TAG, "System unreachable for starting task.");
+                }
+            } else {
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG, state + ": Attempting to ack a task that has already been" +
+                            "processed.");
+                }
+            }
+        }
+    }
+
+    /** @hide */
+    public final IBinder onBind(Intent intent) {
+        return mBinder.asBinder();
+    }
+
+    /**
+     * Override this method with the callback logic for your task. 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 task, including the extras bundle you
+     *               optionally provided at task-creation time.
+     */
+    public abstract void onStartTask(TaskParams params);
+
+    /**
+     * This method is called if your task should be stopped even before you've called
+     * {@link #taskFinished(TaskParams, 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.content.Task.Builder#setRequiredNetworkCapabilities(int)}, yet while your
+     * task was executing the user toggled WiFi. Another example is if you had specified
+     * {@link android.content.Task.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
+     * repercussion is that the system will cease to hold a wakelock for you.</p>
+     *
+     * <p>After you've done your clean-up you are still expected to call
+     * {@link #taskFinished(TaskParams, boolean)} this will inform the TaskManager that all is well, and
+     * allow you to reschedule your task as it is probably uncompleted. Until you call
+     * taskFinished() you will not receive any newly scheduled tasks with the given task id as the
+     * TaskManager will consider the task to be in an error state.</p>
+     *
+     * @param params Parameters specifying info about this task.
+     * @return True to indicate to the TaskManager whether you'd like to reschedule this task based
+     * on the criteria provided at task creation-time. False to drop the task. Regardless of the
+     * value returned, your task must stop executing.
+     */
+    public abstract boolean onStopTask(TaskParams params);
+
+    /**
+     * Callback to inform the TaskManager you have completed execution. 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 task based on the default, or what was
+     *     set with {@link android.content.Task.Builder#setBackoffCriteria(long, int)}. The
+     *     original requirements are always honoured even for a backed-off task.
+     *     Note that a task running in idle mode will not be backed-off. Instead what will happen
+     *     is the task 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 task, this was given to
+     *               your application in {@link #onStartTask(TaskParams)}.
+     * @param needsReschedule True if this task is complete, false if you want the TaskManager to
+     *                        reschedule you.
+     */
+    public final void taskFinished(TaskParams params, boolean needsReschedule) {
+        ensureHandler();
+        Message m = Message.obtain(mHandler, MSG_TASK_FINISHED, params);
+        m.arg2 = needsReschedule ? 1 : 0;
+        m.sendToTarget();
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/content/Task.java b/core/java/android/content/Task.java
new file mode 100644
index 0000000..ed5ed884
--- /dev/null
+++ b/core/java/android/content/Task.java
@@ -0,0 +1,400 @@
+/*
+ * 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.content;
+
+import android.app.task.TaskService;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Container of data passed to the {@link android.content.TaskManager} fully encapsulating the
+ * parameters required to schedule work against the calling application. These are constructed
+ * using the {@link Task.Builder}.
+ */
+public class Task implements Parcelable {
+
+    public interface NetworkType {
+        public final int ANY = 0;
+        public final int UNMETERED = 1;
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * Unique task id associated with this class. This is assigned to your task by the scheduler.
+     */
+    public int getTaskId() {
+        return taskId;
+    }
+
+    /**
+     * Bundle of extras which are returned to your application at execution time.
+     */
+    public Bundle getExtras() {
+        return extras;
+    }
+
+    /**
+     * Name of the service endpoint that will be called back into by the TaskManager.
+     */
+    public String getServiceClassName() {
+        return serviceClassName;
+    }
+
+    /**
+     * Whether this task needs the device to be plugged in.
+     */
+    public boolean isRequireCharging() {
+        return requireCharging;
+    }
+
+    /**
+     * Whether this task needs the device to be in an Idle maintenance window.
+     */
+    public boolean isRequireDeviceIdle() {
+        return requireDeviceIdle;
+    }
+
+    /**
+     * See {@link android.content.Task.NetworkType} for a description of this value.
+     */
+    public int getNetworkCapabilities() {
+        return networkCapabilities;
+    }
+
+    /**
+     * Set for a task that does not recur periodically, to specify a delay after which the task
+     * will be eligible for execution. This value is not set if the task recurs periodically.
+     */
+    public long getMinLatencyMillis() {
+        return minLatencyMillis;
+    }
+
+    /**
+     * See {@link Builder#setOverrideDeadline(long)}. This value is not set if the task recurs
+     * periodically.
+     */
+    public long getMaxExecutionDelayMillis() {
+        return maxExecutionDelayMillis;
+    }
+
+    /**
+     * Track whether this task will repeat with a given period.
+     */
+    public boolean isPeriodic() {
+        return isPeriodic;
+    }
+
+    /**
+     * Set to the interval between occurrences of this task. This value is <b>not</b> set if the
+     * task does not recur periodically.
+     */
+    public long getIntervalMillis() {
+        return intervalMillis;
+    }
+
+    /**
+     * The amount of time the TaskManager will wait before rescheduling a failed task. This value
+     * will be increased depending on the backoff policy specified at task creation time. Defaults
+     * to 5 seconds.
+     */
+    public long getInitialBackoffMillis() {
+        return initialBackoffMillis;
+    }
+
+    /**
+     * See {@link android.content.Task.BackoffPolicy} for an explanation of the values this field
+     * can take. This defaults to exponential.
+     */
+    public int getBackoffPolicy() {
+        return backoffPolicy;
+    }
+
+    private final int taskId;
+    // TODO: Change this to use PersistableBundle when that lands in master.
+    private final Bundle extras;
+    private final String serviceClassName;
+    private final boolean requireCharging;
+    private final boolean requireDeviceIdle;
+    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;
+
+    private Task(Parcel in) {
+        taskId = in.readInt();
+        extras = in.readBundle();
+        serviceClassName = in.readString();
+        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();
+    }
+
+    private Task(Task.Builder b) {
+        taskId = b.mTaskId;
+        extras = new Bundle(b.mExtras);
+        serviceClassName = b.mTaskServiceClassName;
+        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;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(taskId);
+        out.writeBundle(extras);
+        out.writeString(serviceClassName);
+        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);
+    }
+
+    public static final Creator<Task> CREATOR = new Creator<Task>() {
+        @Override
+        public Task createFromParcel(Parcel in) {
+            return new Task(in);
+        }
+
+        @Override
+        public Task[] newArray(int size) {
+            return new Task[size];
+        }
+    };
+
+    /**
+     * Builder class for constructing {@link Task} objects.
+     */
+    public final class Builder {
+        private int mTaskId;
+        private Bundle mExtras;
+        private String mTaskServiceClassName;
+        // 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 long mIntervalMillis;
+        // Back-off parameters.
+        private long mInitialBackoffMillis = 5000L;
+        private int mBackoffPolicy = BackoffPolicy.EXPONENTIAL;
+        /** Easy way to track whether the client has tried to set a back-off policy. */
+        private boolean mBackoffPolicySet = false;
+
+        /**
+         * @param taskId Application-provided id for this task. Subsequent calls to cancel, or
+         *               tasks created with the same taskId, will update the pre-existing task with
+         *               the same id.
+         * @param cls The endpoint that you implement that will receive the callback from the
+         *            TaskManager.
+         */
+        public Builder(int taskId, Class<TaskService> cls) {
+            mTaskServiceClassName = cls.getClass().getName();
+            mTaskId = taskId;
+        }
+
+        /**
+         * 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(Bundle 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.content.Task.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
+         * task if the network requested is not available your task 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 task, the device needs to be plugged in. This defaults to
+         * false.
+         * @param requireCharging Whether or not the device is plugged in.
+         */
+        public Builder setRequiresCharging(boolean requiresCharging) {
+            mRequiresCharging = requiresCharging;
+            return this;
+        }
+
+        /**
+         * Specify that to run, the task 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 tasks. 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 task should recur with the provided interval, not more than once per
+         * period. You have no control over when within this interval this task will be executed,
+         * only the guarantee that it will be executed at most once within this interval.
+         * A periodic task will be repeated until the phone is turned off, however it will only be
+         * persisted if the client app has declared the
+         * {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission. You can schedule
+         * periodic tasks 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 task will repeat.
+         */
+        public Builder setPeriodic(long intervalMillis) {
+            mIsPeriodic = true;
+            mIntervalMillis = intervalMillis;
+            return this;
+        }
+
+        /**
+         * Specify that this task should be delayed by the provided amount of time.
+         * Because it doesn't make sense setting this property on a periodic task, doing so will
+         * throw an {@link java.lang.IllegalArgumentException} when
+         * {@link android.content.Task.Builder#build()} is called.
+         * @param minLatencyMillis Milliseconds before which this task will not be considered for
+         *                         execution.
+         */
+        public Builder setMinimumLatency(long minLatencyMillis) {
+            mMinLatencyMillis = minLatencyMillis;
+            return this;
+        }
+
+        /**
+         * Set deadline which is the maximum scheduling latency. The task 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 task, doing so will throw an
+         * {@link java.lang.IllegalArgumentException} when
+         * {@link android.content.Task.Builder#build()} is called.
+         */
+        public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
+            mMaxExecutionDelayMillis = maxExecutionDelayMillis;
+            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 task 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 tasks. See
+         * {@link android.app.task.TaskService#taskFinished(android.app.task.TaskParams, boolean)}
+         * for more description of the return value for the case of a task executing while in idle
+         * mode.
+         * @param initialBackoffMillis Millisecond time interval to wait initially when task 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 task object to hand to the TaskManager. This object is immutable.
+         */
+        public Task build() {
+            // Check that extras bundle only contains primitive types.
+            try {
+                for (String key : extras.keySet()) {
+                    Object value = extras.get(key);
+                    if (value == null) continue;
+                    if (value instanceof Long) continue;
+                    if (value instanceof Integer) continue;
+                    if (value instanceof Boolean) continue;
+                    if (value instanceof Float) continue;
+                    if (value instanceof Double) continue;
+                    if (value instanceof String) continue;
+                    throw new IllegalArgumentException("Unexpected value type: "
+                            + value.getClass().getName());
+                }
+            } catch (IllegalArgumentException e) {
+                throw e;
+            } catch (RuntimeException exc) {
+                throw new IllegalArgumentException("error unparcelling Bundle", exc);
+            }
+            // Check that a deadline was not set on a periodic task.
+            if (mIsPeriodic && (mMaxExecutionDelayMillis != 0L)) {
+                throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " +
+                        "periodic task.");
+            }
+            if (mIsPeriodic && (mMinLatencyMillis != 0L)) {
+                throw new IllegalArgumentException("Can't call setMinimumLatency() on a " +
+                        "periodic task");
+            }
+            if (mBackoffPolicySet && mRequiresDeviceIdle) {
+                throw new IllegalArgumentException("An idle mode task will not respect any" +
+                        " back-off policy, so calling setBackoffCriteria with" +
+                        " setRequiresDeviceIdle is an error.");
+            }
+            return new Task(this);
+        }
+    }
+
+}
diff --git a/core/java/android/content/TaskManager.java b/core/java/android/content/TaskManager.java
new file mode 100644
index 0000000..d28d78a
--- /dev/null
+++ b/core/java/android/content/TaskManager.java
@@ -0,0 +1,66 @@
+/*
+ * 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.content;
+
+import java.util.List;
+
+/**
+ * Class for scheduling various types of tasks with the scheduling framework on the device.
+ *
+ * Get an instance of this class through {@link Context#getSystemService(String)}.
+ */
+public abstract class TaskManager {
+    /*
+     * Returned from {@link #schedule(Task)} when an invalid parameter was supplied. This can occur
+     * if the run-time for your task is too short, or perhaps the system can't resolve the
+     * requisite {@link TaskService} in your package.
+     */
+    static final int RESULT_INVALID_PARAMETERS = -1;
+    /**
+     * Returned from {@link #schedule(Task)} if this application has made too many requests for
+     * work over too short a time.
+     */
+    // TODO: Determine if this is necessary.
+    static final int RESULT_OVER_QUOTA = -2;
+
+    /*
+     * @param task The task you wish scheduled. See {@link Task#TaskBuilder} for more detail on
+     * the sorts of tasks you can schedule.
+     * @return If >0, this int corresponds to the taskId of the successfully scheduled task.
+     * Otherwise you have to compare the return value to the error codes defined in this class.
+     */
+    public abstract int schedule(Task task);
+
+    /**
+     * Cancel a task that is pending in the TaskManager.
+     * @param taskId unique identifier for this task. Obtain this value from the tasks returned by
+     * {@link #getAllPendingTasks()}.
+     * @return
+     */
+    public abstract void cancel(int taskId);
+
+    /**
+     * Cancel all tasks that have been registered with the TaskManager by this package.
+     */
+    public abstract void cancelAll();
+
+    /**
+     * @return a list of all the tasks registered by this package that have not yet been executed.
+     */
+    public abstract List<Task> getAllPendingTasks();
+
+}