diff options
author | Christopher Tate <ctate@google.com> | 2014-06-09 19:50:00 -0700 |
---|---|---|
committer | Christopher Tate <ctate@google.com> | 2014-06-10 12:51:55 -0700 |
commit | 7060b04f6d92351b67222e636ab378a0273bf3e7 (patch) | |
tree | 82fce1e04dd58a5d79895d0869b3b0adeffbb417 | |
parent | 6d7a25f317be60ae8a4d8806e517052be2398753 (diff) | |
download | frameworks_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
38 files changed, 1737 insertions, 1758 deletions
@@ -77,9 +77,9 @@ LOCAL_SRC_FILES += \ 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/ITaskManager.aidl \ - core/java/android/app/task/ITaskService.aidl \ + core/java/android/app/job/IJobCallback.aidl \ + core/java/android/app/job/IJobScheduler.aidl \ + core/java/android/app/job/IJobService.aidl \ core/java/android/app/IThumbnailRetriever.aidl \ core/java/android/app/ITransientNotification.aidl \ core/java/android/app/IUiAutomationConnection.aidl \ diff --git a/CleanSpec.mk b/CleanSpec.mk index f3bb9b6..5b027b3 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -197,3 +197,6 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framew # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/task) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/app/task) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/TaskManager) diff --git a/api/current.txt b/api/current.txt index 7d8e796..60d5e067 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5356,23 +5356,9 @@ package android.app.backup { } -package android.app.maintenance { +package android.app.job { - public abstract class IdleService extends android.app.Service { - ctor public IdleService(); - method public final void finishIdle(); - method public final android.os.IBinder onBind(android.content.Intent); - method public abstract boolean onIdleStart(); - method public abstract void onIdleStop(); - field public static final java.lang.String PERMISSION_BIND = "android.permission.BIND_IDLE_SERVICE"; - field public static final java.lang.String SERVICE_INTERFACE = "android.service.idle.IdleService"; - } - -} - -package android.app.task { - - public class Task implements android.os.Parcelable { + public class JobInfo implements android.os.Parcelable { method public int describeContents(); method public int getBackoffPolicy(); method public android.os.PersistableBundle getExtras(); @@ -5390,55 +5376,69 @@ package android.app.task { field public static final android.os.Parcelable.Creator CREATOR; } - public static abstract interface Task.BackoffPolicy { + public static abstract interface JobInfo.BackoffPolicy { field public static final int EXPONENTIAL = 1; // 0x1 field public static final int LINEAR = 0; // 0x0 } - public static final class Task.Builder { - ctor public Task.Builder(int, android.content.ComponentName); - method public android.app.task.Task build(); - method public android.app.task.Task.Builder setBackoffCriteria(long, int); - method public android.app.task.Task.Builder setExtras(android.os.PersistableBundle); - method public android.app.task.Task.Builder setMinimumLatency(long); - method public android.app.task.Task.Builder setOverrideDeadline(long); - method public android.app.task.Task.Builder setPeriodic(long); - method public android.app.task.Task.Builder setRequiredNetworkCapabilities(int); - method public android.app.task.Task.Builder setRequiresCharging(boolean); - method public android.app.task.Task.Builder setRequiresDeviceIdle(boolean); + public static final class JobInfo.Builder { + ctor public JobInfo.Builder(int, android.content.ComponentName); + method public android.app.job.JobInfo build(); + method public android.app.job.JobInfo.Builder setBackoffCriteria(long, int); + method public android.app.job.JobInfo.Builder setExtras(android.os.PersistableBundle); + method public android.app.job.JobInfo.Builder setMinimumLatency(long); + method public android.app.job.JobInfo.Builder setOverrideDeadline(long); + method public android.app.job.JobInfo.Builder setPeriodic(long); + method public android.app.job.JobInfo.Builder setRequiredNetworkCapabilities(int); + method public android.app.job.JobInfo.Builder setRequiresCharging(boolean); + method public android.app.job.JobInfo.Builder setRequiresDeviceIdle(boolean); } - public static abstract interface Task.NetworkType { + public static abstract interface JobInfo.NetworkType { field public static final int ANY = 1; // 0x1 field public static final int NONE = 0; // 0x0 field public static final int UNMETERED = 2; // 0x2 } - public abstract class TaskManager { - ctor public TaskManager(); + public class JobParameters implements android.os.Parcelable { + method public int describeContents(); + method public android.os.PersistableBundle getExtras(); + method public int getJobId(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + + public abstract class JobScheduler { + ctor public JobScheduler(); method public abstract void cancel(int); method public abstract void cancelAll(); - method public abstract java.util.List<android.app.task.Task> getAllPendingTasks(); - method public abstract int schedule(android.app.task.Task); + method public abstract java.util.List<android.app.job.JobInfo> getAllPendingJobs(); + method public abstract int schedule(android.app.job.JobInfo); field public static final int RESULT_FAILURE = 0; // 0x0 field public static final int RESULT_SUCCESS = 1; // 0x1 } - public class TaskParams implements android.os.Parcelable { - method public int describeContents(); - method public android.os.PersistableBundle getExtras(); - method public int getTaskId(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator CREATOR; + public abstract class JobService extends android.app.Service { + ctor public JobService(); + method public final void jobFinished(android.app.job.JobParameters, boolean); + method public final android.os.IBinder onBind(android.content.Intent); + method public abstract boolean onStartJob(android.app.job.JobParameters); + method public abstract boolean onStopJob(android.app.job.JobParameters); + field public static final java.lang.String PERMISSION_BIND = "android.permission.BIND_JOB_SERVICE"; } - public abstract class TaskService extends android.app.Service { - ctor public TaskService(); +} + +package android.app.maintenance { + + public abstract class IdleService extends android.app.Service { + ctor public IdleService(); + method public final void finishIdle(); method public final android.os.IBinder onBind(android.content.Intent); - method public abstract boolean onStartTask(android.app.task.TaskParams); - 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"; + method public abstract boolean onIdleStart(); + method public abstract void onIdleStop(); + field public static final java.lang.String PERMISSION_BIND = "android.permission.BIND_IDLE_SERVICE"; + field public static final java.lang.String SERVICE_INTERFACE = "android.service.idle.IdleService"; } } @@ -7006,6 +7006,7 @@ package android.content { field public static final java.lang.String DROPBOX_SERVICE = "dropbox"; field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method"; field public static final java.lang.String INPUT_SERVICE = "input"; + field public static final java.lang.String JOB_SCHEDULER_SERVICE = "jobscheduler"; field public static final java.lang.String KEYGUARD_SERVICE = "keyguard"; field public static final java.lang.String LAUNCHER_APPS_SERVICE = "launcherapps"; field public static final java.lang.String LAYOUT_INFLATER_SERVICE = "layout_inflater"; @@ -7026,7 +7027,6 @@ package android.content { field public static final java.lang.String SEARCH_SERVICE = "search"; field public static final java.lang.String SENSOR_SERVICE = "sensor"; field public static final java.lang.String STORAGE_SERVICE = "storage"; - field public static final java.lang.String TASK_SERVICE = "task"; field public static final java.lang.String TELEPHONY_SERVICE = "phone"; field public static final java.lang.String TEXT_SERVICES_MANAGER_SERVICE = "textservices"; field public static final java.lang.String TV_INPUT_SERVICE = "tv_input"; diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 5bbc43c..c5190d3 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -133,7 +133,7 @@ import android.view.textservice.TextServicesManager; import android.accounts.AccountManager; import android.accounts.IAccountManager; import android.app.admin.DevicePolicyManager; -import android.app.task.ITaskManager; +import android.app.job.IJobScheduler; import android.app.trust.TrustManager; import com.android.internal.annotations.GuardedBy; @@ -697,10 +697,10 @@ class ContextImpl extends Context { return new UsageStatsManager(ctx.getOuterContext()); }}); - registerService(TASK_SERVICE, new ServiceFetcher() { + registerService(JOB_SCHEDULER_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { - IBinder b = ServiceManager.getService(TASK_SERVICE); - return new TaskManagerImpl(ITaskManager.Stub.asInterface(b)); + IBinder b = ServiceManager.getService(JOB_SCHEDULER_SERVICE); + return new JobSchedulerImpl(IJobScheduler.Stub.asInterface(b)); }}); } diff --git a/core/java/android/app/TaskManagerImpl.java b/core/java/android/app/JobSchedulerImpl.java index fe29fb7..09038d5 100644 --- a/core/java/android/app/TaskManagerImpl.java +++ b/core/java/android/app/JobSchedulerImpl.java @@ -17,38 +17,38 @@ // in android.app so ContextImpl has package access package android.app; -import android.app.task.ITaskManager; -import android.app.task.Task; -import android.app.task.TaskManager; +import android.app.job.JobInfo; +import android.app.job.JobScheduler; +import android.app.job.IJobScheduler; import android.os.RemoteException; import java.util.List; /** - * Concrete implementation of the TaskManager interface + * Concrete implementation of the JobScheduler interface * @hide */ -public class TaskManagerImpl extends TaskManager { - ITaskManager mBinder; +public class JobSchedulerImpl extends JobScheduler { + IJobScheduler mBinder; - /* package */ TaskManagerImpl(ITaskManager binder) { + /* package */ JobSchedulerImpl(IJobScheduler binder) { mBinder = binder; } @Override - public int schedule(Task task) { + public int schedule(JobInfo job) { try { - return mBinder.schedule(task); + return mBinder.schedule(job); } catch (RemoteException e) { - return TaskManager.RESULT_FAILURE; + return JobScheduler.RESULT_FAILURE; } } @Override - public void cancel(int taskId) { + public void cancel(int jobId) { try { - mBinder.cancel(taskId); + mBinder.cancel(jobId); } catch (RemoteException e) {} } @@ -62,9 +62,9 @@ public class TaskManagerImpl extends TaskManager { } @Override - public List<Task> getAllPendingTasks() { + public List<JobInfo> getAllPendingJobs() { try { - return mBinder.getAllPendingTasks(); + return mBinder.getAllPendingJobs(); } catch (RemoteException e) { return null; } diff --git a/core/java/android/app/task/ITaskCallback.aidl b/core/java/android/app/job/IJobCallback.aidl index d8a32fd..2d3948f 100644 --- a/core/java/android/app/task/ITaskCallback.aidl +++ b/core/java/android/app/job/IJobCallback.aidl @@ -14,43 +14,40 @@ * limitations under the License. */ -package android.app.task; - -import android.app.task.ITaskService; -import android.app.task.TaskParams; +package android.app.job; /** - * The server side of the TaskManager IPC protocols. The app-side implementation + * 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 Task Service instance is reporting. + * which Job Service instance is reporting. * * {@hide} */ -interface ITaskCallback { +interface IJobCallback { /** * Immediate callback to the system after sending a start signal, used to quickly detect ANR. * - * @param taskId Unique integer used to identify this task. - * @param ongoing True to indicate that the client is processing the task. False if the task is + * @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 taskId, boolean ongoing); + void acknowledgeStartMessage(int jobId, boolean ongoing); /** * Immediate callback to the system after sending a stop signal, used to quickly detect ANR. * - * @param taskId Unique integer used to identify this task. - * @param rescheulde Whether or not to reschedule this task. + * @param jobId Unique integer used to identify this job. + * @param reschedule Whether or not to reschedule this job. */ - void acknowledgeStopMessage(int taskId, boolean reschedule); + void acknowledgeStopMessage(int jobId, boolean reschedule); /* - * Tell the task manager that the client is done with its execution, so that it can go on to + * 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 taskId Unique integer used to identify this task. - * @param reschedule Whether or not to reschedule this task. + * @param jobId Unique integer used to identify this job. + * @param reschedule Whether or not to reschedule this job. */ - void taskFinished(int taskId, boolean reschedule); + void jobFinished(int jobId, boolean reschedule); } diff --git a/core/java/android/app/task/ITaskManager.aidl b/core/java/android/app/job/IJobScheduler.aidl index b56c78a..f1258ae 100644 --- a/core/java/android/app/task/ITaskManager.aidl +++ b/core/java/android/app/job/IJobScheduler.aidl @@ -14,17 +14,17 @@ * limitations under the License. */ -package android.app.task; +package android.app.job; -import android.app.task.Task; +import android.app.job.JobInfo; /** - * IPC interface that supports the app-facing {@link #TaskManager} api. + * IPC interface that supports the app-facing {@link #JobScheduler} api. * {@hide} */ -interface ITaskManager { - int schedule(in Task task); - void cancel(int taskId); +interface IJobScheduler { + int schedule(in JobInfo job); + void cancel(int jobId); void cancelAll(); - List<Task> getAllPendingTasks(); + List<JobInfo> getAllPendingJobs(); } diff --git a/core/java/android/app/task/ITaskService.aidl b/core/java/android/app/job/IJobService.aidl index 87b0191..63f8b81 100644 --- a/core/java/android/app/task/ITaskService.aidl +++ b/core/java/android/app/job/IJobService.aidl @@ -14,22 +14,19 @@ * limitations under the License. */ -package android.app.task; +package android.app.job; -import android.app.task.ITaskCallback; -import android.app.task.TaskParams; - -import android.os.Bundle; +import android.app.job.JobParameters; /** * 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 - * service implementation will extend android.app.task.TaskService. + * 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 ITaskService { - /** Begin execution of application's task. */ - void startTask(in TaskParams taskParams); +oneway interface IJobService { + /** Begin execution of application's job. */ + void startJob(in JobParameters jobParams); /** Stop execution of application's task. */ - void stopTask(in TaskParams taskParams); + void stopJob(in JobParameters jobParams); } diff --git a/core/java/android/app/task/Task.aidl b/core/java/android/app/job/JobInfo.aidl index 1f25439..7b198a8 100644 --- a/core/java/android/app/task/Task.aidl +++ b/core/java/android/app/job/JobInfo.aidl @@ -14,7 +14,6 @@ * limitations under the License. */ -package android.app.task; +package android.app.job; -parcelable Task; -
\ No newline at end of file +parcelable JobInfo; diff --git a/core/java/android/app/task/Task.java b/core/java/android/app/job/JobInfo.java index 0e660b3..a22e4cd 100644 --- a/core/java/android/app/task/Task.java +++ b/core/java/android/app/job/JobInfo.java @@ -14,7 +14,7 @@ * limitations under the License */ -package android.app.task; +package android.app.job; import android.content.ComponentName; import android.os.Bundle; @@ -23,22 +23,22 @@ import android.os.Parcelable; import android.os.PersistableBundle; /** - * Container of data passed to the {@link android.app.task.TaskManager} fully encapsulating the + * 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 Task.Builder}. + * using the {@link JobInfo.Builder}. */ -public class Task implements Parcelable { +public class JobInfo implements Parcelable { public interface NetworkType { /** Default. */ public final int NONE = 0; - /** This task requires network connectivity. */ + /** This job requires network connectivity. */ public final int ANY = 1; - /** This task requires network connectivity that is unmetered. */ + /** This job requires network connectivity that is unmetered. */ public final int UNMETERED = 2; } /** - * Amount of backoff a task has initially by default, in milliseconds. + * Amount of backoff a job has initially by default, in milliseconds. * @hide. */ public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 5000L; @@ -63,7 +63,7 @@ public class Task implements Parcelable { public final int EXPONENTIAL = 1; } - private final int taskId; + private final int jobId; // TODO: Change this to use PersistableBundle when that lands in master. private final PersistableBundle extras; private final ComponentName service; @@ -80,10 +80,10 @@ public class Task implements Parcelable { private final int backoffPolicy; /** - * Unique task id associated with this class. This is assigned to your task by the scheduler. + * Unique job id associated with this class. This is assigned to your job by the scheduler. */ public int getId() { - return taskId; + return jobId; } /** @@ -94,43 +94,43 @@ public class Task implements Parcelable { } /** - * Name of the service endpoint that will be called back into by the TaskManager. + * Name of the service endpoint that will be called back into by the JobScheduler. */ public ComponentName getService() { return service; } /** - * Whether this task needs the device to be plugged in. + * Whether this job 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. + * Whether this job needs the device to be in an Idle maintenance window. */ public boolean isRequireDeviceIdle() { return requireDeviceIdle; } /** - * See {@link android.app.task.Task.NetworkType} for a description of this value. + * See {@link android.app.job.JobInfo.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. + * 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 task recurs + * See {@link Builder#setOverrideDeadline(long)}. This value is not set if the job recurs * periodically. */ public long getMaxExecutionDelayMillis() { @@ -138,23 +138,23 @@ public class Task implements Parcelable { } /** - * Track whether this task will repeat with a given period. + * Track whether this job 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. + * 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 TaskManager will wait before rescheduling a failed task. This value - * will be increased depending on the backoff policy specified at task creation time. Defaults + * 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() { @@ -162,7 +162,7 @@ public class Task implements Parcelable { } /** - * See {@link android.app.task.Task.BackoffPolicy} for an explanation of the values this field + * See {@link android.app.job.JobInfo.BackoffPolicy} for an explanation of the values this field * can take. This defaults to exponential. */ public int getBackoffPolicy() { @@ -187,8 +187,8 @@ public class Task implements Parcelable { return hasLateConstraint; } - private Task(Parcel in) { - taskId = in.readInt(); + private JobInfo(Parcel in) { + jobId = in.readInt(); extras = in.readPersistableBundle(); service = in.readParcelable(null); requireCharging = in.readInt() == 1; @@ -204,10 +204,10 @@ public class Task implements Parcelable { hasLateConstraint = in.readInt() == 1; } - private Task(Task.Builder b) { - taskId = b.mTaskId; + private JobInfo(JobInfo.Builder b) { + jobId = b.mJobId; extras = b.mExtras; - service = b.mTaskService; + service = b.mJobService; requireCharging = b.mRequiresCharging; requireDeviceIdle = b.mRequiresDeviceIdle; networkCapabilities = b.mNetworkCapabilities; @@ -228,7 +228,7 @@ public class Task implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { - out.writeInt(taskId); + out.writeInt(jobId); out.writePersistableBundle(extras); out.writeParcelable(service, flags); out.writeInt(requireCharging ? 1 : 0); @@ -244,23 +244,23 @@ public class Task implements Parcelable { out.writeInt(hasLateConstraint ? 1 : 0); } - public static final Creator<Task> CREATOR = new Creator<Task>() { + public static final Creator<JobInfo> CREATOR = new Creator<JobInfo>() { @Override - public Task createFromParcel(Parcel in) { - return new Task(in); + public JobInfo createFromParcel(Parcel in) { + return new JobInfo(in); } @Override - public Task[] newArray(int size) { - return new Task[size]; + public JobInfo[] newArray(int size) { + return new JobInfo[size]; } }; - /** Builder class for constructing {@link Task} objects. */ + /** Builder class for constructing {@link JobInfo} objects. */ public static final class Builder { - private int mTaskId; + private int mJobId; private PersistableBundle mExtras = PersistableBundle.EMPTY; - private ComponentName mTaskService; + private ComponentName mJobService; // Requirements. private boolean mRequiresCharging; private boolean mRequiresDeviceIdle; @@ -280,15 +280,15 @@ public class Task implements Parcelable { 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 + * @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 taskService The endpoint that you implement that will receive the callback from the - * TaskManager. + * @param jobService The endpoint that you implement that will receive the callback from the + * JobScheduler. */ - public Builder(int taskId, ComponentName taskService) { - mTaskService = taskService; - mTaskId = taskId; + public Builder(int jobId, ComponentName jobService) { + mJobService = jobService; + mJobId = jobId; } /** @@ -302,10 +302,10 @@ public class Task implements Parcelable { /** * Set some description of the kind of network capabilities you would like to have. This - * will be a parameter defined in {@link android.app.task.Task.NetworkType}. + * 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 - * task if the network requested is not available your task will never run. See + * 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) { @@ -313,10 +313,10 @@ public class Task implements Parcelable { return this; } - /* - * Specify that to run this task, the device needs to be plugged in. This defaults to + /** + * Specify that to run this job, the device needs to be plugged in. This defaults to * false. - * @param requireCharging Whether or not the device is plugged in. + * @param requiresCharging Whether or not the device is plugged in. */ public Builder setRequiresCharging(boolean requiresCharging) { mRequiresCharging = requiresCharging; @@ -324,11 +324,11 @@ public class Task implements Parcelable { } /** - * Specify that to run, the task needs the device to be in idle mode. This defaults to + * 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 tasks. Bear in mind that battery usage will still be attributed + * 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. @@ -339,17 +339,17 @@ public class Task implements Parcelable { } /** - * 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, + * 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 task will be repeated until the phone is turned off, however it will only be + * 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 tasks without this permission, they simply will cease to exist after the phone + * 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 task will repeat. + * @param intervalMillis Millisecond interval for which this job will repeat. */ public Builder setPeriodic(long intervalMillis) { mIsPeriodic = true; @@ -359,11 +359,11 @@ public class Task implements Parcelable { } /** - * 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 + * 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.task.Task.Builder#build()} is called. - * @param minLatencyMillis Milliseconds before which this task will not be considered for + * {@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) { @@ -373,11 +373,11 @@ public class Task implements Parcelable { } /** - * Set deadline which is the maximum scheduling latency. The task will be run by 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 task, doing so will throw an + * this property on a periodic job, doing so will throw an * {@link java.lang.IllegalArgumentException} when - * {@link android.app.task.Task.Builder#build()} is called. + * {@link android.app.job.JobInfo.Builder#build()} is called. */ public Builder setOverrideDeadline(long maxExecutionDelayMillis) { mMaxExecutionDelayMillis = maxExecutionDelayMillis; @@ -389,13 +389,13 @@ public class Task implements Parcelable { * 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 + * 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 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 + * 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 task has + * @param initialBackoffMillis Millisecond time interval to wait initially when job has * failed. * @param backoffPolicy is one of {@link BackoffPolicy} */ @@ -407,25 +407,25 @@ public class Task implements Parcelable { } /** - * @return The task object to hand to the TaskManager. This object is immutable. + * @return The job object to hand to the JobScheduler. This object is immutable. */ - public Task build() { + public JobInfo build() { mExtras = new PersistableBundle(mExtras); // Make our own copy. - // Check that a deadline was not set on a periodic task. + // 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 task."); + "periodic job."); } if (mIsPeriodic && (mMinLatencyMillis != 0L)) { throw new IllegalArgumentException("Can't call setMinimumLatency() on a " + - "periodic task"); + "periodic job"); } if (mBackoffPolicySet && mRequiresDeviceIdle) { - throw new IllegalArgumentException("An idle mode task will not respect any" + + throw new IllegalArgumentException("An idle mode job will not respect any" + " back-off policy, so calling setBackoffCriteria with" + " setRequiresDeviceIdle is an error."); } - return new Task(this); + return new JobInfo(this); } } diff --git a/core/java/android/app/task/TaskParams.aidl b/core/java/android/app/job/JobParameters.aidl index 9b25855..e7551b9 100644 --- a/core/java/android/app/task/TaskParams.aidl +++ b/core/java/android/app/job/JobParameters.aidl @@ -14,6 +14,6 @@ * limitations under the License. */ -package android.app.task; +package android.app.job; -parcelable TaskParams;
\ No newline at end of file +parcelable JobParameters; diff --git a/core/java/android/app/task/TaskParams.java b/core/java/android/app/job/JobParameters.java index f4908c6..724856a 100644 --- a/core/java/android/app/task/TaskParams.java +++ b/core/java/android/app/job/JobParameters.java @@ -14,40 +14,42 @@ * limitations under the License */ -package android.app.task; +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 task. You do not create this object + * 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 TaskParams implements Parcelable { +public class JobParameters implements Parcelable { - private final int taskId; + private final int jobId; private final PersistableBundle extras; private final IBinder callback; /** @hide */ - public TaskParams(int taskId, PersistableBundle extras, IBinder callback) { - this.taskId = taskId; + public JobParameters(int jobId, PersistableBundle extras, IBinder callback) { + this.jobId = jobId; this.extras = extras; this.callback = callback; } /** - * @return The unique id of this task, specified at creation time. + * @return The unique id of this job, specified at creation time. */ - public int getTaskId() { - return taskId; + public int getJobId() { + return jobId; } /** - * @return The extras you passed in when constructing this task with - * {@link android.app.task.Task.Builder#setExtras(android.os.PersistableBundle)}. This will + * @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() { @@ -55,12 +57,12 @@ public class TaskParams implements Parcelable { } /** @hide */ - public ITaskCallback getCallback() { - return ITaskCallback.Stub.asInterface(callback); + public IJobCallback getCallback() { + return IJobCallback.Stub.asInterface(callback); } - private TaskParams(Parcel in) { - taskId = in.readInt(); + private JobParameters(Parcel in) { + jobId = in.readInt(); extras = in.readPersistableBundle(); callback = in.readStrongBinder(); } @@ -72,20 +74,20 @@ public class TaskParams implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(taskId); + dest.writeInt(jobId); dest.writePersistableBundle(extras); dest.writeStrongBinder(callback); } - public static final Creator<TaskParams> CREATOR = new Creator<TaskParams>() { + public static final Creator<JobParameters> CREATOR = new Creator<JobParameters>() { @Override - public TaskParams createFromParcel(Parcel in) { - return new TaskParams(in); + public JobParameters createFromParcel(Parcel in) { + return new JobParameters(in); } @Override - public TaskParams[] newArray(int size) { - return new TaskParams[size]; + 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/task/TaskService.java b/core/java/android/app/job/JobService.java index 8ce4484..eea0268 100644 --- a/core/java/android/app/task/TaskService.java +++ b/core/java/android/app/job/JobService.java @@ -14,9 +14,12 @@ * limitations under the License */ -package android.app.task; +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; @@ -28,72 +31,72 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; /** - * <p>Entry point for the callback from the {@link android.app.task.TaskManager}.</p> + * <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 TaskService#onStartTask(TaskParams)}, 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 + * 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 TaskManager - specifically - * {@link #onStopTask(android.app.task.TaskParams)}, which is meant to inform you that the + * 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 TaskService extends Service { - private static final String TAG = "TaskService"; +public abstract class JobService extends Service { + private static final String TAG = "JobService"; /** - * Task services must be protected with this permission: + * Job services must be protected with this permission: * * <pre class="prettyprint"> - * <service android:name="MyTaskService" - * android:permission="android.permission.BIND_TASK_SERVICE" > + * <service android:name="MyJobService" + * android:permission="android.permission.BIND_JOB_SERVICE" > * ... * </service> * </pre> * - * <p>If a task service is declared in the manifest but not protected with this + * <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_TASK_SERVICE"; + "android.permission.BIND_JOB_SERVICE"; /** * Identifier for a message that will result in a call to - * {@link #onStartTask(android.app.task.TaskParams)}. + * {@link #onStartJob(android.app.job.JobParameters)}. */ - private final int MSG_EXECUTE_TASK = 0; + private final int MSG_EXECUTE_JOB = 0; /** - * Message that will result in a call to {@link #onStopTask(android.app.task.TaskParams)}. + * Message that will result in a call to {@link #onStopJob(android.app.job.JobParameters)}. */ - private final int MSG_STOP_TASK = 1; + private final int MSG_STOP_JOB = 1; /** - * Message that the client has completed execution of this task. + * Message that the client has completed execution of this job. */ - private final int MSG_TASK_FINISHED = 2; + private final int MSG_JOB_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 + * Handler we post jobs to. Responsible for calling into the client logic, and handling the * callback to the system. */ @GuardedBy("mHandlerLock") - TaskHandler mHandler; + JobHandler mHandler; /** Binder for this service. */ - ITaskService mBinder = new ITaskService.Stub() { + IJobService mBinder = new IJobService.Stub() { @Override - public void startTask(TaskParams taskParams) { + public void startJob(JobParameters jobParams) { ensureHandler(); - Message m = Message.obtain(mHandler, MSG_EXECUTE_TASK, taskParams); + Message m = Message.obtain(mHandler, MSG_EXECUTE_JOB, jobParams); m.sendToTarget(); } @Override - public void stopTask(TaskParams taskParams) { + public void stopJob(JobParameters jobParams) { ensureHandler(); - Message m = Message.obtain(mHandler, MSG_STOP_TASK, taskParams); + Message m = Message.obtain(mHandler, MSG_STOP_JOB, jobParams); m.sendToTarget(); } }; @@ -102,7 +105,7 @@ public abstract class TaskService extends Service { void ensureHandler() { synchronized (mHandlerLock) { if (mHandler == null) { - mHandler = new TaskHandler(getMainLooper()); + mHandler = new JobHandler(getMainLooper()); } } } @@ -112,45 +115,45 @@ public abstract class TaskService extends Service { * (app-specified) mechanism. * @hide */ - class TaskHandler extends Handler { - TaskHandler(Looper looper) { + class JobHandler extends Handler { + JobHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { - final TaskParams params = (TaskParams) msg.obj; + final JobParameters params = (JobParameters) msg.obj; switch (msg.what) { - case MSG_EXECUTE_TASK: + case MSG_EXECUTE_JOB: try { - boolean workOngoing = TaskService.this.onStartTask(params); + boolean workOngoing = JobService.this.onStartJob(params); ackStartMessage(params, workOngoing); } catch (Exception e) { - Log.e(TAG, "Error while executing task: " + params.getTaskId()); + Log.e(TAG, "Error while executing job: " + params.getJobId()); throw new RuntimeException(e); } break; - case MSG_STOP_TASK: + case MSG_STOP_JOB: try { - boolean ret = TaskService.this.onStopTask(params); + boolean ret = JobService.this.onStopJob(params); ackStopMessage(params, ret); } catch (Exception e) { - Log.e(TAG, "Application unable to handle onStopTask.", e); + Log.e(TAG, "Application unable to handle onStopJob.", e); throw new RuntimeException(e); } break; - case MSG_TASK_FINISHED: + case MSG_JOB_FINISHED: final boolean needsReschedule = (msg.arg2 == 1); - ITaskCallback callback = params.getCallback(); + IJobCallback callback = params.getCallback(); if (callback != null) { try { - callback.taskFinished(params.getTaskId(), needsReschedule); + callback.jobFinished(params.getJobId(), needsReschedule); } catch (RemoteException e) { - Log.e(TAG, "Error reporting task finish to system: binder has gone" + + Log.e(TAG, "Error reporting job finish to system: binder has gone" + "away."); } } else { - Log.e(TAG, "finishTask() called for a nonexistent task id."); + Log.e(TAG, "finishJob() called for a nonexistent job id."); } break; default: @@ -159,34 +162,34 @@ public abstract class TaskService extends Service { } } - private void ackStartMessage(TaskParams params, boolean workOngoing) { - final ITaskCallback callback = params.getCallback(); - final int taskId = params.getTaskId(); + private void ackStartMessage(JobParameters params, boolean workOngoing) { + final IJobCallback callback = params.getCallback(); + final int jobId = params.getJobId(); if (callback != null) { try { - callback.acknowledgeStartMessage(taskId, workOngoing); + callback.acknowledgeStartMessage(jobId, workOngoing); } catch(RemoteException e) { - Log.e(TAG, "System unreachable for starting task."); + Log.e(TAG, "System unreachable for starting job."); } } else { if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Attempting to ack a task that has already been processed."); + Log.d(TAG, "Attempting to ack a job that has already been processed."); } } } - private void ackStopMessage(TaskParams params, boolean reschedule) { - final ITaskCallback callback = params.getCallback(); - final int taskId = params.getTaskId(); + private void ackStopMessage(JobParameters params, boolean reschedule) { + final IJobCallback callback = params.getCallback(); + final int jobId = params.getJobId(); if (callback != null) { try { - callback.acknowledgeStopMessage(taskId, reschedule); + callback.acknowledgeStopMessage(jobId, reschedule); } catch(RemoteException e) { - Log.e(TAG, "System unreachable for stopping task."); + Log.e(TAG, "System unreachable for stopping job."); } } else { if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Attempting to ack a task that has already been processed."); + Log.d(TAG, "Attempting to ack a job that has already been processed."); } } } @@ -198,59 +201,59 @@ public abstract class TaskService extends Service { } /** - * Override this method with the callback logic for your task. Any such logic needs to be + * 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 task, including the extras bundle you - * optionally provided at task-creation time. + * @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 task. + * there's no more work to be done for this job. */ - public abstract boolean onStartTask(TaskParams params); + public abstract boolean onStartJob(JobParameters params); /** - * This method is called if the system has determined that you must stop execution of your task - * even before you've had a chance to call {@link #taskFinished(TaskParams, boolean)}. + * 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.task.Task.Builder#setRequiredNetworkCapabilities(int)}, yet while your - * task was executing the user toggled WiFi. Another example is if you had specified - * {@link android.app.task.Task.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its + * {@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 task. - * @return True to indicate to the TaskManager whether you'd like to reschedule this task based - * on the retry criteria provided at task creation-time. False to drop the task. Regardless of - * the value returned, your task must stop executing. + * @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 onStopTask(TaskParams params); + public abstract boolean onStopJob(JobParameters params); /** - * Callback to inform the TaskManager you've finished executing. This can be called from any + * 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 task based on + * <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.task.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 + * {@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 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 + * @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 taskFinished(TaskParams params, boolean needsReschedule) { + public final void jobFinished(JobParameters params, boolean needsReschedule) { ensureHandler(); - Message m = Message.obtain(mHandler, MSG_TASK_FINISHED, params); + Message m = Message.obtain(mHandler, MSG_JOB_FINISHED, params); m.arg2 = needsReschedule ? 1 : 0; m.sendToTarget(); } diff --git a/core/java/android/app/task/TaskManager.java b/core/java/android/app/task/TaskManager.java deleted file mode 100644 index 00f57da..0000000 --- a/core/java/android/app/task/TaskManager.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 java.util.List; - -import android.content.Context; - -/** - * Class for scheduling various types of tasks 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.TASK_SERVICE)}. - */ -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. - */ - public static final int RESULT_FAILURE = 0; - /** - * 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. - public static final int RESULT_SUCCESS = 1; - - /** - * @param task The task you wish scheduled. See - * {@link android.app.task.Task.Builder Task.Builder} for more detail on the sorts of tasks - * you can schedule. - * @return If >0, this int returns 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(); - -} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 9fe9bce..cdcfd2e 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2059,7 +2059,7 @@ public abstract class Context { PRINT_SERVICE, MEDIA_SESSION_SERVICE, BATTERY_SERVICE, - TASK_SERVICE, + JOB_SCHEDULER_SERVICE, }) @Retention(RetentionPolicy.SOURCE) public @interface ServiceName {} @@ -2116,8 +2116,8 @@ public abstract class Context { * <dd> A {@link android.app.DownloadManager} for requesting HTTP downloads * <dt> {@link #BATTERY_SERVICE} ("batterymanager") * <dd> A {@link android.os.BatteryManager} for managing battery state - * <dt> {@link #TASK_SERVICE} ("taskmanager") - * <dd> A {@link android.app.task.TaskManager} for managing scheduled tasks + * <dt> {@link #JOB_SCHEDULER_SERVICE} ("taskmanager") + * <dd> A {@link android.app.job.JobScheduler} for managing scheduled tasks * </dl> * * <p>Note: System services obtained via this API may be closely associated with @@ -2171,8 +2171,8 @@ public abstract class Context { * @see android.app.DownloadManager * @see #BATTERY_SERVICE * @see android.os.BatteryManager - * @see #TASK_SERVICE - * @see android.app.task.TaskManager + * @see #JOB_SCHEDULER_SERVICE + * @see android.app.job.JobScheduler */ public abstract Object getSystemService(@ServiceName @NonNull String name); @@ -2761,12 +2761,12 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a {@link - * android.app.task.TaskManager} instance for managing occasional + * android.app.job.JobScheduler} instance for managing occasional * background tasks. * @see #getSystemService - * @see android.app.task.TaskManager + * @see android.app.job.JobScheduler */ - public static final String TASK_SERVICE = "task"; + public static final String JOB_SCHEDULER_SERVICE = "jobscheduler"; /** * Determine whether the given permission is allowed for a particular diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 3696806..8768779 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1758,11 +1758,11 @@ <!-- Allows the system to bind to an application's task services @hide --> - <permission android:name="android.permission.BIND_TASK_SERVICE" + <permission android:name="android.permission.BIND_JOB_SERVICE" android:protectionLevel="signature" - android:label="@string/permlab_bindTaskService" - android:description="@string/permdesc_bindTaskService" /> - <uses-permission android:name="android.permission.BIND_TASK_SERVICE"/> + android:label="@string/permlab_bindJobService" + android:description="@string/permdesc_bindJobService" /> + <uses-permission android:name="android.permission.BIND_JOB_SERVICE"/> <!-- ========================================= --> <!-- Permissions for special development tools --> @@ -2877,8 +2877,8 @@ </service> <service android:name="com.android.server.MountServiceIdler" - android:exported="false" - android:permission="android.permission.BIND_TASK_SERVICE" > + android:exported="true" + android:permission="android.permission.BIND_JOB_SERVICE" > </service> </application> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index d521746..a224cd5 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1261,10 +1261,10 @@ permission that an application must be granted by the user. Instead, it is part of a mechanism that applications use to indicate to the system that they want to do scheduled background work. --> - <string name="permlab_bindTaskService">run the application\'s scheduled background work</string> + <string name="permlab_bindJobService">run the application\'s scheduled background work</string> <!-- Description of an application permission, so that the user can understand what is being done if they are curious. --> - <string name="permdesc_bindTaskService">This permission allows the Android system to run the application in the background when requested.</string> + <string name="permdesc_bindJobService">This permission allows the Android system to run the application in the background when requested.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_diagnostic">read/write to resources owned by diag</string> diff --git a/services/core/java/com/android/server/MountServiceIdler.java b/services/core/java/com/android/server/MountServiceIdler.java index 6179082..bcb6e9e 100644 --- a/services/core/java/com/android/server/MountServiceIdler.java +++ b/services/core/java/com/android/server/MountServiceIdler.java @@ -18,32 +18,32 @@ package com.android.server; import java.util.Calendar; -import android.app.task.Task; -import android.app.task.TaskManager; -import android.app.task.TaskParams; -import android.app.task.TaskService; +import android.app.job.JobInfo; +import android.app.job.JobParameters; +import android.app.job.JobScheduler; +import android.app.job.JobService; import android.content.ComponentName; import android.content.Context; import android.util.Slog; -public class MountServiceIdler extends TaskService { +public class MountServiceIdler extends JobService { private static final String TAG = "MountServiceIdler"; private static ComponentName sIdleService = new ComponentName(MountServiceIdler.class.getPackage().getName(), MountServiceIdler.class.getName()); - private static int MOUNT_TASK_ID = 808; + private static int MOUNT_JOB_ID = 808; private boolean mStarted; - private TaskParams mTaskParams; + private JobParameters mJobParams; private Runnable mFinishCallback = new Runnable() { @Override public void run() { Slog.i(TAG, "Got mount service completion callback"); synchronized (mFinishCallback) { if (mStarted) { - taskFinished(mTaskParams, false); + jobFinished(mJobParams, false); mStarted = false; } } @@ -53,12 +53,12 @@ public class MountServiceIdler extends TaskService { }; @Override - public boolean onStartTask(TaskParams params) { + public boolean onStartJob(JobParameters params) { // The mount service will run an fstrim operation asynchronously // on a designated separate thread, so we provide it with a callback // that lets us cleanly end our idle timeslice. It's safe to call // finishIdle() from any thread. - mTaskParams = params; + mJobParams = params; MountService ms = MountService.sSelf; if (ms != null) { synchronized (mFinishCallback) { @@ -70,9 +70,9 @@ public class MountServiceIdler extends TaskService { } @Override - public boolean onStopTask(TaskParams params) { + public boolean onStopJob(JobParameters params) { // Once we kick off the fstrim we aren't actually interruptible; just note - // that we don't need to call taskFinished(), and let everything happen in + // that we don't need to call jobFinished(), and let everything happen in // the callback from the mount service. synchronized (mFinishCallback) { mStarted = false; @@ -84,12 +84,12 @@ public class MountServiceIdler extends TaskService { * Schedule the idle job that will ping the mount service */ public static void scheduleIdlePass(Context context) { - TaskManager tm = (TaskManager) context.getSystemService(Context.TASK_SERVICE); + JobScheduler tm = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); Calendar calendar = tomorrowMidnight(); final long timeToMidnight = calendar.getTimeInMillis() - System.currentTimeMillis(); - Task.Builder builder = new Task.Builder(MOUNT_TASK_ID, sIdleService); + JobInfo.Builder builder = new JobInfo.Builder(MOUNT_JOB_ID, sIdleService); builder.setRequiresDeviceIdle(true); builder.setRequiresCharging(true); builder.setMinimumLatency(timeToMidnight); diff --git a/services/core/java/com/android/server/task/TaskCompletedListener.java b/services/core/java/com/android/server/job/JobCompletedListener.java index c53f5ca..a7af9cd 100644 --- a/services/core/java/com/android/server/task/TaskCompletedListener.java +++ b/services/core/java/com/android/server/job/JobCompletedListener.java @@ -14,19 +14,19 @@ * limitations under the License */ -package com.android.server.task; +package com.android.server.job; -import com.android.server.task.controllers.TaskStatus; +import com.android.server.job.controllers.JobStatus; /** - * Used for communication between {@link com.android.server.task.TaskServiceContext} and the - * {@link com.android.server.task.TaskManagerService}. + * Used for communication between {@link com.android.server.job.JobServiceContext} and the + * {@link com.android.server.job.JobSchedulerService}. */ -public interface TaskCompletedListener { +public interface JobCompletedListener { /** - * Callback for when a task is completed. - * @param needsReschedule Whether the implementing class should reschedule this task. + * Callback for when a job is completed. + * @param needsReschedule Whether the implementing class should reschedule this job. */ - public void onTaskCompleted(TaskStatus taskStatus, boolean needsReschedule); + public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule); } diff --git a/services/core/java/com/android/server/task/TaskMapReadFinishedListener.java b/services/core/java/com/android/server/job/JobMapReadFinishedListener.java index c68d8db..f3e77e6 100644 --- a/services/core/java/com/android/server/task/TaskMapReadFinishedListener.java +++ b/services/core/java/com/android/server/job/JobMapReadFinishedListener.java @@ -14,21 +14,21 @@ * limitations under the License */ -package com.android.server.task; +package com.android.server.job; import java.util.List; -import com.android.server.task.controllers.TaskStatus; +import com.android.server.job.controllers.JobStatus; /** - * Callback definition for I/O thread to let the TaskManagerService know when + * Callback definition for I/O thread to let the JobManagerService know when * I/O read has completed. Done this way so we don't stall the main thread on * boot. */ -public interface TaskMapReadFinishedListener { +public interface JobMapReadFinishedListener { /** - * Called by the {@link TaskStore} at boot, when the disk read is finished. + * Called by the {@link JobStore} at boot, when the disk read is finished. */ - public void onTaskMapReadFinished(List<TaskStatus> tasks); + public void onJobMapReadFinished(List<JobStatus> jobs); } diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java new file mode 100644 index 0000000..0e9a9cc --- /dev/null +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -0,0 +1,764 @@ +/* + * 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 com.android.server.job; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import android.app.job.JobInfo; +import android.app.job.JobScheduler; +import android.app.job.JobService; +import android.app.job.IJobScheduler; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ServiceInfo; +import android.os.Binder; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.UserHandle; +import android.util.Slog; +import android.util.SparseArray; + +import com.android.server.job.controllers.BatteryController; +import com.android.server.job.controllers.ConnectivityController; +import com.android.server.job.controllers.IdleController; +import com.android.server.job.controllers.JobStatus; +import com.android.server.job.controllers.StateController; +import com.android.server.job.controllers.TimeController; + +import java.util.LinkedList; + +/** + * Responsible for taking jobs representing work to be performed by a client app, and determining + * based on the criteria specified when that job should be run against the client application's + * endpoint. + * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing + * about constraints, or the state of active jobs. It receives callbacks from the various + * controllers and completed jobs and operates accordingly. + * + * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object. + * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}. + * @hide + */ +public class JobSchedulerService extends com.android.server.SystemService + implements StateChangedListener, JobCompletedListener, JobMapReadFinishedListener { + // TODO: Switch this off for final version. + static final boolean DEBUG = true; + /** The number of concurrent jobs we run at one time. */ + private static final int MAX_JOB_CONTEXTS_COUNT = 3; + static final String TAG = "JobManagerService"; + /** Master list of jobs. */ + private final JobStore mJobs; + + static final int MSG_JOB_EXPIRED = 0; + static final int MSG_CHECK_JOB = 1; + + // Policy constants + /** + * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things + * early. + */ + private static final int MIN_IDLE_COUNT = 1; + /** + * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule + * things early. + */ + private static final int MIN_CONNECTIVITY_COUNT = 2; + /** + * Minimum # of jobs (with no particular constraints) for which the JMS will be happy running + * some work early. + */ + private static final int MIN_READY_JOBS_COUNT = 4; + + /** + * Track Services that have currently active or pending jobs. The index is provided by + * {@link JobStatus#getServiceToken()} + */ + private final List<JobServiceContext> mActiveServices = new LinkedList<JobServiceContext>(); + /** List of controllers that will notify this service of updates to jobs. */ + private List<StateController> mControllers; + /** + * Queue of pending jobs. The JobServiceContext class will receive jobs from this list + * when ready to execute them. + */ + private final LinkedList<JobStatus> mPendingJobs = new LinkedList<JobStatus>(); + + private final JobHandler mHandler; + private final JobSchedulerStub mJobSchedulerStub; + /** + * Cleans 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. + */ + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Slog.d(TAG, "Receieved: " + intent.getAction()); + if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) { + int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1); + if (DEBUG) { + Slog.d(TAG, "Removing jobs for uid: " + uidRemoved); + } + cancelJobsForUid(uidRemoved); + } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) { + final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); + if (DEBUG) { + Slog.d(TAG, "Removing jobs for user: " + userId); + } + cancelJobsForUser(userId); + } + } + }; + + /** + * 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. + * @param job JobInfo object containing execution parameters + * @param uId The package identifier of the application this job is for. + * @param canPersistJob Whether or not the client has the appropriate permissions for + * persisting this job. + * @return Result of this operation. See <code>JobScheduler#RESULT_*</code> return codes. + */ + public int schedule(JobInfo job, int uId, boolean canPersistJob) { + JobStatus jobStatus = new JobStatus(job, uId, canPersistJob); + cancelJob(uId, job.getId()); + startTrackingJob(jobStatus); + return JobScheduler.RESULT_SUCCESS; + } + + public List<JobInfo> getPendingJobs(int uid) { + ArrayList<JobInfo> outList = new ArrayList<JobInfo>(); + synchronized (mJobs) { + for (JobStatus job : mJobs.getJobs()) { + if (job.getUid() == uid) { + outList.add(job.getJob()); + } + } + } + return outList; + } + + private void cancelJobsForUser(int userHandle) { + synchronized (mJobs) { + List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle); + for (JobStatus toRemove : jobsForUser) { + if (DEBUG) { + Slog.d(TAG, "Cancelling: " + toRemove); + } + cancelJobLocked(toRemove); + } + } + } + + /** + * 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. + * @param uid To check against for removal of a job. + */ + public void cancelJobsForUid(int uid) { + // Remove from master list. + synchronized (mJobs) { + List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid); + for (JobStatus toRemove : jobsForUid) { + if (DEBUG) { + Slog.d(TAG, "Cancelling: " + toRemove); + } + cancelJobLocked(toRemove); + } + } + } + + /** + * 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. + * @param uid Uid of the calling client. + * @param jobId Id of the job, provided at schedule-time. + */ + public void cancelJob(int uid, int jobId) { + JobStatus toCancel; + synchronized (mJobs) { + toCancel = mJobs.getJobByUidAndJobId(uid, jobId); + if (toCancel != null) { + cancelJobLocked(toCancel); + } + } + } + + private void cancelJobLocked(JobStatus cancelled) { + // Remove from store. + stopTrackingJob(cancelled); + // Remove from pending queue. + mPendingJobs.remove(cancelled); + // Cancel if running. + stopJobOnServiceContextLocked(cancelled); + } + + /** + * Initializes the system service. + * <p> + * Subclasses must define a single argument constructor that accepts the context + * and passes it to super. + * </p> + * + * @param context The system server context. + */ + public JobSchedulerService(Context context) { + super(context); + // Create the controllers. + mControllers = new LinkedList<StateController>(); + mControllers.add(ConnectivityController.get(this)); + mControllers.add(TimeController.get(this)); + mControllers.add(IdleController.get(this)); + mControllers.add(BatteryController.get(this)); + + mHandler = new JobHandler(context.getMainLooper()); + mJobSchedulerStub = new JobSchedulerStub(); + // Create the "runners". + for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) { + mActiveServices.add( + new JobServiceContext(this, context.getMainLooper())); + } + mJobs = JobStore.initAndGet(this); + } + + @Override + public void onStart() { + publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub); + } + + @Override + 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); + } + } + + /** + * 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. + */ + private void startTrackingJob(JobStatus jobStatus) { + boolean update; + synchronized (mJobs) { + update = mJobs.add(jobStatus); + } + for (StateController controller : mControllers) { + if (update) { + controller.maybeStopTrackingJob(jobStatus); + } + controller.maybeStartTrackingJob(jobStatus); + } + } + + /** + * Called when we want to remove a JobStatus object that we've finished executing. Returns the + * object removed. + */ + private boolean stopTrackingJob(JobStatus jobStatus) { + boolean removed; + synchronized (mJobs) { + // Remove from store as well as controllers. + removed = mJobs.remove(jobStatus); + } + if (removed) { + for (StateController controller : mControllers) { + controller.maybeStopTrackingJob(jobStatus); + } + } + return removed; + } + + private boolean stopJobOnServiceContextLocked(JobStatus job) { + for (JobServiceContext jsc : mActiveServices) { + final JobStatus executing = jsc.getRunningJob(); + if (executing != null && executing.matches(job.getUid(), job.getJobId())) { + jsc.cancelExecutingJob(); + return true; + } + } + return false; + } + + /** + * @param job JobStatus we are querying against. + * @return Whether or not the job represented by the status object is currently being run or + * is pending. + */ + private boolean isCurrentlyActiveLocked(JobStatus job) { + for (JobServiceContext serviceContext : mActiveServices) { + final JobStatus running = serviceContext.getRunningJob(); + if (running != null && running.matches(job.getUid(), job.getJobId())) { + return true; + } + } + return false; + } + + /** + * 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. + * @param failureToReschedule Provided job status that we will reschedule. + * @return A newly instantiated JobStatus with the same constraints as the last job except + * with adjusted timing constraints. + */ + private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) { + final long elapsedNowMillis = SystemClock.elapsedRealtime(); + final JobInfo job = failureToReschedule.getJob(); + + final long initialBackoffMillis = job.getInitialBackoffMillis(); + final int backoffAttempt = failureToReschedule.getNumFailures() + 1; + long newEarliestRuntimeElapsed = elapsedNowMillis; + + switch (job.getBackoffPolicy()) { + case JobInfo.BackoffPolicy.LINEAR: + newEarliestRuntimeElapsed += initialBackoffMillis * backoffAttempt; + break; + default: + if (DEBUG) { + Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential."); + } + case JobInfo.BackoffPolicy.EXPONENTIAL: + newEarliestRuntimeElapsed += + Math.pow(initialBackoffMillis * 0.001, backoffAttempt) * 1000; + break; + } + newEarliestRuntimeElapsed = + Math.min(newEarliestRuntimeElapsed, JobInfo.MAX_BACKOFF_DELAY_MILLIS); + return new JobStatus(failureToReschedule, newEarliestRuntimeElapsed, + JobStatus.NO_LATEST_RUNTIME, backoffAttempt); + } + + /** + * 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. + * @return A new job representing the execution criteria for this instantiation of the + * recurring job. + */ + private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) { + 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 */); + } + + // JobCompletedListener implementations. + + /** + * 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. + * @param jobStatus Completed job. + * @param needsReschedule Whether the implementing class should reschedule this job. + */ + @Override + public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule) { + if (DEBUG) { + Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule); + } + if (!stopTrackingJob(jobStatus)) { + if (DEBUG) { + Slog.e(TAG, "Error removing job: 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(); + } + + // StateChangedListener implementations. + + /** + * Off-board work to our handler thread as quickly as possible, b/c this call is probably being + * made on the main thread. + * For now this takes the job and if it's ready to run it will run it. In future we might not + * provide the job, so that the StateChangedListener has to run through its list of jobs to + * see which are ready. This will further decouple the controllers from the execution logic. + */ + @Override + public void onControllerStateChanged() { + // Post a message to to run through the list of jobs and start/stop any that are eligible. + mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); + } + + @Override + public void onRunJobNow(JobStatus jobStatus) { + mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget(); + } + + /** + * Disk I/O is finished, take the list of jobs we read from disk and add them to our + * {@link JobStore}. + * This is run on the {@link com.android.server.IoThread} instance, which is a separate thread, + * and is called once at boot. + */ + @Override + public void onJobMapReadFinished(List<JobStatus> jobs) { + synchronized (mJobs) { + for (JobStatus js : jobs) { + if (mJobs.containsJobIdForUid(js.getJobId(), js.getUid())) { + // An app with BOOT_COMPLETED *might* have decided to reschedule their job, in + // the same amount of time it took us to read it from disk. If this is the case + // we leave it be. + continue; + } + startTrackingJob(js); + } + } + } + + private class JobHandler extends Handler { + + public JobHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message message) { + switch (message.what) { + case MSG_JOB_EXPIRED: + synchronized (mJobs) { + JobStatus runNow = (JobStatus) message.obj; + if (!mPendingJobs.contains(runNow)) { + mPendingJobs.add(runNow); + } + } + queueReadyJobsForExecutionH(); + break; + case MSG_CHECK_JOB: + // Check the list of jobs and run some of them if we feel inclined. + maybeQueueReadyJobsForExecutionH(); + break; + } + maybeRunPendingJobsH(); + // Don't remove JOB_EXPIRED in case one came along while processing the queue. + removeMessages(MSG_CHECK_JOB); + } + + /** + * Run through list of jobs and execute all possible - at least one is expired so we do + * as many as we can. + */ + private void queueReadyJobsForExecutionH() { + synchronized (mJobs) { + for (JobStatus job : mJobs.getJobs()) { + if (isReadyToBeExecutedLocked(job)) { + mPendingJobs.add(job); + } else if (isReadyToBeCancelledLocked(job)) { + stopJobOnServiceContextLocked(job); + } + } + } + } + + /** + * The state of at least one job has changed. Here is where we could enforce various + * policies on when we want to execute jobs. + * Right now the policy is such: + * If >1 of the ready jobs is idle mode we send all of them off + * if more than 2 network connectivity jobs are ready we send them all off. + * If more than 4 jobs total are ready we send them all off. + * TODO: It would be nice to consolidate these sort of high-level policies somewhere. + */ + private void maybeQueueReadyJobsForExecutionH() { + synchronized (mJobs) { + int idleCount = 0; + int backoffCount = 0; + int connectivityCount = 0; + List<JobStatus> runnableJobs = new ArrayList<JobStatus>(); + for (JobStatus job : mJobs.getJobs()) { + if (isReadyToBeExecutedLocked(job)) { + if (job.getNumFailures() > 0) { + backoffCount++; + } + if (job.hasIdleConstraint()) { + idleCount++; + } + if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint()) { + connectivityCount++; + } + runnableJobs.add(job); + } else if (isReadyToBeCancelledLocked(job)) { + stopJobOnServiceContextLocked(job); + } + } + if (backoffCount > 0 || idleCount >= MIN_IDLE_COUNT || + connectivityCount >= MIN_CONNECTIVITY_COUNT || + runnableJobs.size() >= MIN_READY_JOBS_COUNT) { + for (JobStatus job : runnableJobs) { + mPendingJobs.add(job); + } + } + } + } + + /** + * Criteria for moving a job into the pending queue: + * - It's ready. + * - It's not pending. + * - It's not already running on a JSC. + */ + private boolean isReadyToBeExecutedLocked(JobStatus job) { + return job.isReady() && !mPendingJobs.contains(job) && !isCurrentlyActiveLocked(job); + } + + /** + * Criteria for cancelling an active job: + * - It's not ready + * - It's running on a JSC. + */ + private boolean isReadyToBeCancelledLocked(JobStatus job) { + return !job.isReady() && isCurrentlyActiveLocked(job); + } + + /** + * Reconcile jobs in the pending queue against available execution contexts. + * A controller can force a job into the pending queue even if it's already running, but + * here is where we decide whether to actually execute it. + */ + private void maybeRunPendingJobsH() { + synchronized (mJobs) { + Iterator<JobStatus> it = mPendingJobs.iterator(); + while (it.hasNext()) { + JobStatus nextPending = it.next(); + JobServiceContext availableContext = null; + for (JobServiceContext jsc : mActiveServices) { + final JobStatus running = jsc.getRunningJob(); + if (running != null && running.matches(nextPending.getUid(), + nextPending.getJobId())) { + // Already running this tId for this uId, skip. + availableContext = null; + break; + } + if (jsc.isAvailable()) { + availableContext = jsc; + } + } + if (availableContext != null) { + if (!availableContext.executeRunnableJob(nextPending)) { + if (DEBUG) { + Slog.d(TAG, "Error executing " + nextPending); + } + mJobs.remove(nextPending); + } + it.remove(); + } + } + } + } + } + + /** + * Binder stub trampoline implementation + */ + final class JobSchedulerStub extends IJobScheduler.Stub { + /** Cache determination of whether a given app can persist jobs + * key is uid of the calling app; value is undetermined/true/false + */ + private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>(); + + // Enforce that only the app itself (or shared uid participant) can schedule a + // job that runs one of the app's services, as well as verifying that the + // named service properly requires the BIND_JOB_SERVICE permission + private void enforceValidJobRequest(int uid, JobInfo job) { + final PackageManager pm = getContext().getPackageManager(); + final ComponentName service = job.getService(); + try { + ServiceInfo si = pm.getServiceInfo(service, 0); + if (si.applicationInfo.uid != uid) { + throw new IllegalArgumentException("uid " + uid + + " cannot schedule job in " + service.getPackageName()); + } + if (!JobService.PERMISSION_BIND.equals(si.permission)) { + throw new IllegalArgumentException("Scheduled service " + service + + " does not require android.permission.BIND_JOB_SERVICE permission"); + } + } catch (NameNotFoundException e) { + throw new IllegalArgumentException("No such service: " + service); + } + } + + private boolean canPersistJobs(int pid, int uid) { + // If we get this far we're good to go; all we need to do now is check + // whether the app is allowed to persist its scheduled work. + final boolean canPersist; + synchronized (mPersistCache) { + Boolean cached = mPersistCache.get(uid); + if (cached != null) { + canPersist = cached.booleanValue(); + } else { + // Persisting jobs is tantamount to running at boot, so we permit + // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED + // permission + int result = getContext().checkPermission( + android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid); + canPersist = (result == PackageManager.PERMISSION_GRANTED); + mPersistCache.put(uid, canPersist); + } + } + return canPersist; + } + + // IJobScheduler implementation + @Override + public int schedule(JobInfo job) throws RemoteException { + if (DEBUG) { + Slog.d(TAG, "Scheduling job: " + job); + } + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + + enforceValidJobRequest(uid, job); + final boolean canPersist = canPersistJobs(pid, uid); + + long ident = Binder.clearCallingIdentity(); + try { + return JobSchedulerService.this.schedule(job, uid, canPersist); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public List<JobInfo> getAllPendingJobs() throws RemoteException { + final int uid = Binder.getCallingUid(); + + long ident = Binder.clearCallingIdentity(); + try { + return JobSchedulerService.this.getPendingJobs(uid); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public void cancelAll() throws RemoteException { + final int uid = Binder.getCallingUid(); + + long ident = Binder.clearCallingIdentity(); + try { + JobSchedulerService.this.cancelJobsForUid(uid); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public void cancel(int jobId) throws RemoteException { + final int uid = Binder.getCallingUid(); + + long ident = Binder.clearCallingIdentity(); + try { + JobSchedulerService.this.cancelJob(uid, jobId); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + /** + * "dumpsys" infrastructure + */ + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); + + long identityToken = Binder.clearCallingIdentity(); + try { + JobSchedulerService.this.dumpInternal(pw); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } + }; + + void dumpInternal(PrintWriter pw) { + synchronized (mJobs) { + pw.println("Registered jobs:"); + if (mJobs.size() > 0) { + for (JobStatus job : mJobs.getJobs()) { + job.dump(pw, " "); + } + } else { + pw.println(); + pw.println("No jobs scheduled."); + } + for (StateController controller : mControllers) { + pw.println(); + controller.dumpControllerState(pw); + } + pw.println(); + pw.println("Pending"); + for (JobStatus jobStatus : mPendingJobs) { + pw.println(jobStatus.hashCode()); + } + pw.println(); + pw.println("Active jobs:"); + for (JobServiceContext jsc : mActiveServices) { + if (jsc.isAvailable()) { + continue; + } else { + pw.println(jsc.getRunningJob().hashCode() + " for: " + + (SystemClock.elapsedRealtime() + - jsc.getExecutionStartTimeElapsed())/1000 + "s " + + "timeout: " + jsc.getTimeoutElapsed()); + } + } + } + pw.println(); + } +} diff --git a/services/core/java/com/android/server/task/TaskServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java index a21de88..92b643c 100644 --- a/services/core/java/com/android/server/task/TaskServiceContext.java +++ b/services/core/java/com/android/server/job/JobServiceContext.java @@ -14,12 +14,12 @@ * limitations under the License */ -package com.android.server.task; +package com.android.server.job; import android.app.ActivityManager; -import android.app.task.ITaskCallback; -import android.app.task.ITaskService; -import android.app.task.TaskParams; +import android.app.job.JobParameters; +import android.app.job.IJobCallback; +import android.app.job.IJobService; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -39,32 +39,32 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.task.controllers.TaskStatus; +import com.android.server.job.controllers.JobStatus; import java.util.concurrent.atomic.AtomicBoolean; /** - * Handles client binding and lifecycle of a task. A task will only execute one at a time on an + * Handles client binding and lifecycle of a job. A job will only execute one at a time on an * instance of this class. */ -public class TaskServiceContext extends ITaskCallback.Stub implements ServiceConnection { +public class JobServiceContext extends IJobCallback.Stub implements ServiceConnection { private static final boolean DEBUG = true; - private static final String TAG = "TaskServiceContext"; - /** Define the maximum # of tasks allowed to run on a service at once. */ - private static final int defaultMaxActiveTasksPerService = + private static final String TAG = "JobServiceContext"; + /** Define the maximum # of jobs allowed to run on a service at once. */ + private static final int defaultMaxActiveJobsPerService = ActivityManager.isLowRamDeviceStatic() ? 1 : 3; - /** Amount of time a task is allowed to execute for before being considered timed-out. */ + /** Amount of time a job is allowed to execute for before being considered timed-out. */ private static final long EXECUTING_TIMESLICE_MILLIS = 60 * 1000; - /** Amount of time the TaskManager will wait for a response from an app for a message. */ + /** Amount of time the JobScheduler will wait for a response from an app for a message. */ private static final long OP_TIMEOUT_MILLIS = 8 * 1000; /** String prefix for all wakelock names. */ - private static final String TM_WAKELOCK_PREFIX = "*task*/"; + private static final String JS_WAKELOCK_PREFIX = "*job*/"; private static final String[] VERB_STRINGS = { "VERB_STARTING", "VERB_EXECUTING", "VERB_STOPPING", "VERB_PENDING" }; - // States that a task occupies while interacting with the client. + // States that a job occupies while interacting with the client. static final int VERB_BINDING = 0; static final int VERB_STARTING = 1; static final int VERB_EXECUTING = 2; @@ -75,30 +75,30 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon private static final int MSG_TIMEOUT = 0; /** Received a callback from client. */ private static final int MSG_CALLBACK = 1; - /** Run through list and start any ready tasks.*/ + /** Run through list and start any ready jobs.*/ private static final int MSG_SERVICE_BOUND = 2; - /** Cancel a task. */ + /** Cancel a job. */ private static final int MSG_CANCEL = 3; - /** Shutdown the Task. Used when the client crashes and we can't die gracefully.*/ + /** Shutdown the job. Used when the client crashes and we can't die gracefully.*/ private static final int MSG_SHUTDOWN_EXECUTION = 4; private final Handler mCallbackHandler; - /** Make callbacks to {@link TaskManagerService} to inform on task completion status. */ - private final TaskCompletedListener mCompletedListener; + /** Make callbacks to {@link JobSchedulerService} to inform on job completion status. */ + private final JobCompletedListener mCompletedListener; /** Used for service binding, etc. */ private final Context mContext; private PowerManager.WakeLock mWakeLock; // Execution state. - private TaskParams mParams; + private JobParameters mParams; @VisibleForTesting int mVerb; private AtomicBoolean mCancelled = new AtomicBoolean(); - /** All the information maintained about the task currently being executed. */ - private TaskStatus mRunningTask; + /** All the information maintained about the job currently being executed. */ + private JobStatus mRunningJob; /** Binder to the client service. */ - ITaskService service; + IJobService service; private final Object mLock = new Object(); /** Whether this context is free. */ @@ -109,45 +109,45 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon /** Track when job will timeout. */ private long mTimeoutElapsed; - TaskServiceContext(TaskManagerService service, Looper looper) { + JobServiceContext(JobSchedulerService service, Looper looper) { this(service.getContext(), service, looper); } @VisibleForTesting - TaskServiceContext(Context context, TaskCompletedListener completedListener, Looper looper) { + JobServiceContext(Context context, JobCompletedListener completedListener, Looper looper) { mContext = context; - mCallbackHandler = new TaskServiceHandler(looper); + mCallbackHandler = new JobServiceHandler(looper); mCompletedListener = completedListener; mAvailable = true; } /** - * Give a task to this context for execution. Callers must first check {@link #isAvailable()} + * Give a job to this context for execution. Callers must first check {@link #isAvailable()} * to make sure this is a valid context. - * @param ts The status of the task that we are going to run. - * @return True if the task is valid and is running. False if the task cannot be executed. + * @param job The status of the job that we are going to run. + * @return True if the job is valid and is running. False if the job cannot be executed. */ - boolean executeRunnableTask(TaskStatus ts) { + boolean executeRunnableJob(JobStatus job) { synchronized (mLock) { if (!mAvailable) { Slog.e(TAG, "Starting new runnable but context is unavailable > Error."); return false; } - mRunningTask = ts; - mParams = new TaskParams(ts.getTaskId(), ts.getExtras(), this); + mRunningJob = job; + mParams = new JobParameters(job.getJobId(), job.getExtras(), this); mExecutionStartTimeElapsed = SystemClock.elapsedRealtime(); mVerb = VERB_BINDING; - final Intent intent = new Intent().setComponent(ts.getServiceComponent()); + final Intent intent = new Intent().setComponent(job.getServiceComponent()); boolean binding = mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND, - new UserHandle(ts.getUserId())); + new UserHandle(job.getUserId())); if (!binding) { if (DEBUG) { - Slog.d(TAG, ts.getServiceComponent().getShortClassName() + " unavailable."); + Slog.d(TAG, job.getServiceComponent().getShortClassName() + " unavailable."); } - mRunningTask = null; + mRunningJob = null; mParams = null; mExecutionStartTimeElapsed = 0L; return false; @@ -157,13 +157,13 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon } } - /** Used externally to query the running task. Will return null if there is no task running. */ - TaskStatus getRunningTask() { - return mRunningTask; + /** Used externally to query the running job. Will return null if there is no job running. */ + JobStatus getRunningJob() { + return mRunningJob; } - /** Called externally when a task that was scheduled for execution should be cancelled. */ - void cancelExecutingTask() { + /** Called externally when a job that was scheduled for execution should be cancelled. */ + void cancelExecutingJob() { mCallbackHandler.obtainMessage(MSG_CANCEL).sendToTarget(); } @@ -185,29 +185,29 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon } @Override - public void taskFinished(int taskId, boolean reschedule) { + public void jobFinished(int jobId, boolean reschedule) { if (!verifyCallingUid()) { return; } - mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, reschedule ? 1 : 0) + mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, reschedule ? 1 : 0) .sendToTarget(); } @Override - public void acknowledgeStopMessage(int taskId, boolean reschedule) { + public void acknowledgeStopMessage(int jobId, boolean reschedule) { if (!verifyCallingUid()) { return; } - mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, reschedule ? 1 : 0) + mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, reschedule ? 1 : 0) .sendToTarget(); } @Override - public void acknowledgeStartMessage(int taskId, boolean ongoing) { + public void acknowledgeStartMessage(int jobId, boolean ongoing) { if (!verifyCallingUid()) { return; } - mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, ongoing ? 1 : 0).sendToTarget(); + mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, ongoing ? 1 : 0).sendToTarget(); } /** @@ -219,25 +219,25 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon */ @Override public void onServiceConnected(ComponentName name, IBinder service) { - if (!name.equals(mRunningTask.getServiceComponent())) { + if (!name.equals(mRunningJob.getServiceComponent())) { mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget(); return; } - this.service = ITaskService.Stub.asInterface(service); + 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, - TM_WAKELOCK_PREFIX + mRunningTask.getServiceComponent().getPackageName()); - mWakeLock.setWorkSource(new WorkSource(mRunningTask.getUid())); + JS_WAKELOCK_PREFIX + mRunningJob.getServiceComponent().getPackageName()); + mWakeLock.setWorkSource(new WorkSource(mRunningJob.getUid())); mWakeLock.setReferenceCounted(false); mWakeLock.acquire(); mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget(); } /** - * If the client service crashes we reschedule this task and clean up. + * If the client service crashes we reschedule this job and clean up. * @param name The concrete component name of the service whose */ @Override @@ -251,7 +251,7 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon * @return True if the binder calling is coming from the client we expect. */ private boolean verifyCallingUid() { - if (mRunningTask == null || Binder.getCallingUid() != mRunningTask.getUid()) { + if (mRunningJob == null || Binder.getCallingUid() != mRunningJob.getUid()) { if (DEBUG) { Slog.d(TAG, "Stale callback received, ignoring."); } @@ -261,12 +261,12 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon } /** - * Handles the lifecycle of the TaskService binding/callbacks, etc. The convention within this + * Handles the lifecycle of the JobService binding/callbacks, etc. The convention within this * class is to append 'H' to each function name that can only be called on this handler. This * isn't strictly necessary because all of these functions are private, but helps clarity. */ - private class TaskServiceHandler extends Handler { - TaskServiceHandler(Looper looper) { + private class JobServiceHandler extends Handler { + JobServiceHandler(Looper looper) { super(looper); } @@ -278,7 +278,7 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon break; case MSG_CALLBACK: if (DEBUG) { - Slog.d(TAG, "MSG_CALLBACK of : " + mRunningTask + " v:" + + Slog.d(TAG, "MSG_CALLBACK of : " + mRunningJob + " v:" + VERB_STRINGS[mVerb]); } removeMessages(MSG_TIMEOUT); @@ -292,7 +292,7 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon handleFinishedH(reschedule); } else { if (DEBUG) { - Slog.d(TAG, "Unrecognised callback: " + mRunningTask); + Slog.d(TAG, "Unrecognised callback: " + mRunningJob); } } break; @@ -303,42 +303,42 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon handleOpTimeoutH(); break; case MSG_SHUTDOWN_EXECUTION: - closeAndCleanupTaskH(true /* needsReschedule */); + closeAndCleanupJobH(true /* needsReschedule */); break; default: Log.e(TAG, "Unrecognised message: " + message); } } - /** Start the task on the service. */ + /** Start the job on the service. */ private void handleServiceBoundH() { if (mVerb != VERB_BINDING) { - Slog.e(TAG, "Sending onStartTask for a task that isn't pending. " + Slog.e(TAG, "Sending onStartJob for a job that isn't pending. " + VERB_STRINGS[mVerb]); - closeAndCleanupTaskH(false /* reschedule */); + closeAndCleanupJobH(false /* reschedule */); return; } if (mCancelled.get()) { if (DEBUG) { - Slog.d(TAG, "Task cancelled while waiting for bind to complete. " - + mRunningTask); + Slog.d(TAG, "Job cancelled while waiting for bind to complete. " + + mRunningJob); } - closeAndCleanupTaskH(true /* reschedule */); + closeAndCleanupJobH(true /* reschedule */); return; } try { mVerb = VERB_STARTING; scheduleOpTimeOut(); - service.startTask(mParams); + service.startJob(mParams); } catch (RemoteException e) { Log.e(TAG, "Error sending onStart message to '" + - mRunningTask.getServiceComponent().getShortClassName() + "' ", e); + mRunningJob.getServiceComponent().getShortClassName() + "' ", e); } } /** * State behaviours. - * VERB_STARTING -> Successful start, change task to VERB_EXECUTING and post timeout. + * VERB_STARTING -> Successful start, change job to VERB_EXECUTING and post timeout. * _PENDING -> Error * _EXECUTING -> Error * _STOPPING -> Error @@ -348,7 +348,7 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon case VERB_STARTING: mVerb = VERB_EXECUTING; if (!workOngoing) { - // Task is finished already so fast-forward to handleFinished. + // Job is finished already so fast-forward to handleFinished. handleFinishedH(false); return; } @@ -360,14 +360,14 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon scheduleOpTimeOut(); break; default: - Log.e(TAG, "Handling started task but task wasn't starting! Was " + Log.e(TAG, "Handling started job but job wasn't starting! Was " + VERB_STRINGS[mVerb] + "."); return; } } /** - * VERB_EXECUTING -> Client called taskFinished(), clean up and notify done. + * VERB_EXECUTING -> Client called jobFinished(), clean up and notify done. * _STOPPING -> Successful finish, clean up and notify done. * _STARTING -> Error * _PENDING -> Error @@ -376,20 +376,20 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon switch (mVerb) { case VERB_EXECUTING: case VERB_STOPPING: - closeAndCleanupTaskH(reschedule); + closeAndCleanupJobH(reschedule); break; default: - Slog.e(TAG, "Got an execution complete message for a task that wasn't being" + + Slog.e(TAG, "Got an execution complete message for a job that wasn't being" + "executed. Was " + VERB_STRINGS[mVerb] + "."); } } /** - * A task can be in various states when a cancel request comes in: + * A job can be in various states when a cancel request comes in: * VERB_BINDING -> Cancelled before bind completed. Mark as cancelled and wait for * {@link #onServiceConnected(android.content.ComponentName, android.os.IBinder)} * _STARTING -> Mark as cancelled and wait for - * {@link TaskServiceContext#acknowledgeStartMessage(int, boolean)} + * {@link JobServiceContext#acknowledgeStartMessage(int, boolean)} * _EXECUTING -> call {@link #sendStopMessageH}}. * _ENDING -> No point in doing anything here, so we ignore. */ @@ -406,48 +406,48 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon // Nada. break; default: - Slog.e(TAG, "Cancelling a task without a valid verb: " + mVerb); + Slog.e(TAG, "Cancelling a job without a valid verb: " + mVerb); break; } } /** Process MSG_TIMEOUT here. */ private void handleOpTimeoutH() { - if (Log.isLoggable(TaskManagerService.TAG, Log.DEBUG)) { + if (Log.isLoggable(JobSchedulerService.TAG, Log.DEBUG)) { Log.d(TAG, "MSG_TIMEOUT of " + - mRunningTask.getServiceComponent().getShortClassName() + " : " - + mParams.getTaskId()); + mRunningJob.getServiceComponent().getShortClassName() + " : " + + mParams.getJobId()); } - final int taskId = mParams.getTaskId(); + final int jobId = mParams.getJobId(); switch (mVerb) { 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 TaskManager + // know what happened so let's log it and notify the JobScheduler // FINISHED/NO-RETRY. - Log.e(TAG, "No response from client for onStartTask '" + - mRunningTask.getServiceComponent().getShortClassName() + "' tId: " - + taskId); - closeAndCleanupTaskH(false /* needsReschedule */); + Log.e(TAG, "No response from client for onStartJob '" + + mRunningJob.getServiceComponent().getShortClassName() + "' tId: " + + jobId); + closeAndCleanupJobH(false /* needsReschedule */); break; case VERB_STOPPING: - // At least we got somewhere, so fail but ask the TaskManager to reschedule. - Log.e(TAG, "No response from client for onStopTask, '" + - mRunningTask.getServiceComponent().getShortClassName() + "' tId: " - + taskId); - closeAndCleanupTaskH(true /* needsReschedule */); + // At least we got somewhere, so fail but ask the JobScheduler to reschedule. + Log.e(TAG, "No response from client for onStopJob, '" + + mRunningJob.getServiceComponent().getShortClassName() + "' tId: " + + jobId); + closeAndCleanupJobH(true /* needsReschedule */); break; case VERB_EXECUTING: // Not an error - client ran out of time. - Log.i(TAG, "Client timed out while executing (no taskFinished received)." + + Log.i(TAG, "Client timed out while executing (no jobFinished received)." + " sending onStop. " + - mRunningTask.getServiceComponent().getShortClassName() + "' tId: " - + taskId); + mRunningJob.getServiceComponent().getShortClassName() + "' tId: " + + jobId); sendStopMessageH(); break; default: - Log.e(TAG, "Handling timeout for an unknown active task state: " - + mRunningTask); + Log.e(TAG, "Handling timeout for an unknown active job state: " + + mRunningJob); return; } } @@ -459,35 +459,35 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon private void sendStopMessageH() { mCallbackHandler.removeMessages(MSG_TIMEOUT); if (mVerb != VERB_EXECUTING) { - Log.e(TAG, "Sending onStopTask for a task that isn't started. " + mRunningTask); - closeAndCleanupTaskH(false /* reschedule */); + Log.e(TAG, "Sending onStopJob for a job that isn't started. " + mRunningJob); + closeAndCleanupJobH(false /* reschedule */); return; } try { mVerb = VERB_STOPPING; scheduleOpTimeOut(); - service.stopTask(mParams); + service.stopJob(mParams); } catch (RemoteException e) { - Log.e(TAG, "Error sending onStopTask to client.", e); - closeAndCleanupTaskH(false /* reschedule */); + Log.e(TAG, "Error sending onStopJob to client.", e); + closeAndCleanupJobH(false /* reschedule */); } } /** - * The provided task has finished, either by calling - * {@link android.app.task.TaskService#taskFinished(android.app.task.TaskParams, boolean)} + * The provided job has finished, either by calling + * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)} * or from acknowledging the stop message we sent. Either way, we're done tracking it and * we want to clean up internally. */ - private void closeAndCleanupTaskH(boolean reschedule) { + private void closeAndCleanupJobH(boolean reschedule) { removeMessages(MSG_TIMEOUT); synchronized (mLock) { mWakeLock.release(); - mContext.unbindService(TaskServiceContext.this); - mCompletedListener.onTaskCompleted(mRunningTask, reschedule); + mContext.unbindService(JobServiceContext.this); + mCompletedListener.onJobCompleted(mRunningJob, reschedule); mWakeLock = null; - mRunningTask = null; + mRunningJob = null; mParams = null; mVerb = -1; mCancelled.set(false); @@ -508,8 +508,8 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon EXECUTING_TIMESLICE_MILLIS : OP_TIMEOUT_MILLIS; if (DEBUG) { Slog.d(TAG, "Scheduling time out for '" + - mRunningTask.getServiceComponent().getShortClassName() + "' tId: " + - mParams.getTaskId() + ", in " + (timeoutMillis / 1000) + " s"); + mRunningJob.getServiceComponent().getShortClassName() + "' tId: " + + mParams.getJobId() + ", in " + (timeoutMillis / 1000) + " s"); } Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT); mCallbackHandler.sendMessageDelayed(m, timeoutMillis); diff --git a/services/core/java/com/android/server/task/TaskStore.java b/services/core/java/com/android/server/job/JobStore.java index 9e095e7..3ea7171 100644 --- a/services/core/java/com/android/server/task/TaskStore.java +++ b/services/core/java/com/android/server/job/JobStore.java @@ -14,10 +14,10 @@ * limitations under the License */ -package com.android.server.task; +package com.android.server.job; import android.content.ComponentName; -import android.app.task.Task; +import android.app.job.JobInfo; import android.content.Context; import android.os.Environment; import android.os.Handler; @@ -33,7 +33,7 @@ import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FastXmlSerializer; import com.android.server.IoThread; -import com.android.server.task.controllers.TaskStatus; +import com.android.server.job.controllers.JobStatus; import java.io.ByteArrayOutputStream; import java.io.File; @@ -50,94 +50,94 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; /** - * Maintain a list of classes, and accessor methods/logic for these tasks. + * Maintain a list of classes, and accessor methods/logic for these jobs. * This class offers the following functionality: - * - When a task is added, it will determine if the task requirements have changed (update) and + * - When a job is added, it will determine if the job requirements have changed (update) and * whether the controllers need to be updated. - * - Persists Tasks, figures out when to to rewrite the Task to disk. - * - Handles rescheduling of tasks. - * - When a periodic task is executed and must be re-added. - * - When a task fails and the client requests that it be retried with backoff. + * - Persists JobInfos, figures out when to to rewrite the JobInfo to disk. + * - Handles rescheduling of jobs. + * - When a periodic job is executed and must be re-added. + * - When a job fails and the client requests that it be retried with backoff. * - This class <strong>is not</strong> thread-safe. * * Note on locking: * All callers to this class must <strong>lock on the class object they are calling</strong>. - * This is important b/c {@link com.android.server.task.TaskStore.WriteTasksMapToDiskRunnable} - * and {@link com.android.server.task.TaskStore.ReadTaskMapFromDiskRunnable} lock on that + * This is important b/c {@link com.android.server.job.JobStore.WriteJobsMapToDiskRunnable} + * and {@link com.android.server.job.JobStore.ReadJobMapFromDiskRunnable} lock on that * object. */ -public class TaskStore { - private static final String TAG = "TaskManagerStore"; - private static final boolean DEBUG = TaskManagerService.DEBUG; +public class JobStore { + private static final String TAG = "JobStore"; + private static final boolean DEBUG = JobSchedulerService.DEBUG; /** Threshold to adjust how often we want to write to the db. */ private static final int MAX_OPS_BEFORE_WRITE = 1; - final ArraySet<TaskStatus> mTasksSet; + final ArraySet<JobStatus> mJobSet; final Context mContext; private int mDirtyOperations; private static final Object sSingletonLock = new Object(); - private final AtomicFile mTasksFile; + private final AtomicFile mJobsFile; /** Handler backed by IoThread for writing to disk. */ private final Handler mIoHandler = IoThread.getHandler(); - private static TaskStore sSingleton; + private static JobStore sSingleton; - /** Used by the {@Link TaskManagerService} to instantiate the TaskStore. */ - static TaskStore initAndGet(TaskManagerService taskManagerService) { + /** Used by the {@link JobSchedulerService} to instantiate the JobStore. */ + static JobStore initAndGet(JobSchedulerService jobManagerService) { synchronized (sSingletonLock) { if (sSingleton == null) { - sSingleton = new TaskStore(taskManagerService.getContext(), - Environment.getDataDirectory(), taskManagerService); + sSingleton = new JobStore(jobManagerService.getContext(), + Environment.getDataDirectory(), jobManagerService); } return sSingleton; } } @VisibleForTesting - public static TaskStore initAndGetForTesting(Context context, File dataDir, - TaskMapReadFinishedListener callback) { - return new TaskStore(context, dataDir, callback); + public static JobStore initAndGetForTesting(Context context, File dataDir, + JobMapReadFinishedListener callback) { + return new JobStore(context, dataDir, callback); } - private TaskStore(Context context, File dataDir, TaskMapReadFinishedListener callback) { + private JobStore(Context context, File dataDir, JobMapReadFinishedListener callback) { mContext = context; mDirtyOperations = 0; File systemDir = new File(dataDir, "system"); - File taskDir = new File(systemDir, "task"); - taskDir.mkdirs(); - mTasksFile = new AtomicFile(new File(taskDir, "tasks.xml")); + File jobDir = new File(systemDir, "job"); + jobDir.mkdirs(); + mJobsFile = new AtomicFile(new File(jobDir, "jobs.xml")); - mTasksSet = new ArraySet<TaskStatus>(); + mJobSet = new ArraySet<JobStatus>(); - readTaskMapFromDiskAsync(callback); + readJobMapFromDiskAsync(callback); } /** - * Add a task to the master list, persisting it if necessary. If the TaskStatus already exists, + * Add a job to the master list, persisting it if necessary. If the JobStatus already exists, * it will be replaced. - * @param taskStatus Task to add. - * @return Whether or not an equivalent TaskStatus was replaced by this operation. + * @param jobStatus Job to add. + * @return Whether or not an equivalent JobStatus was replaced by this operation. */ - public boolean add(TaskStatus taskStatus) { - boolean replaced = mTasksSet.remove(taskStatus); - mTasksSet.add(taskStatus); - if (taskStatus.isPersisted()) { + public boolean add(JobStatus jobStatus) { + boolean replaced = mJobSet.remove(jobStatus); + mJobSet.add(jobStatus); + if (jobStatus.isPersisted()) { maybeWriteStatusToDiskAsync(); } if (DEBUG) { - Slog.d(TAG, "Added task status to store: " + taskStatus); + Slog.d(TAG, "Added job status to store: " + jobStatus); } return replaced; } /** - * Whether this taskStatus object already exists in the TaskStore. + * Whether this jobStatus object already exists in the JobStore. */ - public boolean containsTaskIdForUid(int taskId, int uId) { - for (TaskStatus ts : mTasksSet) { - if (ts.getUid() == uId && ts.getTaskId() == taskId) { + public boolean containsJobIdForUid(int jobId, int uId) { + for (JobStatus ts : mJobSet) { + if (ts.getUid() == uId && ts.getJobId() == jobId) { return true; } } @@ -145,18 +145,18 @@ public class TaskStore { } public int size() { - return mTasksSet.size(); + return mJobSet.size(); } /** - * Remove the provided task. Will also delete the task if it was persisted. - * @return Whether or not the task existed to be removed. + * Remove the provided job. Will also delete the job if it was persisted. + * @return Whether or not the job existed to be removed. */ - public boolean remove(TaskStatus taskStatus) { - boolean removed = mTasksSet.remove(taskStatus); + public boolean remove(JobStatus jobStatus) { + boolean removed = mJobSet.remove(jobStatus); if (!removed) { if (DEBUG) { - Slog.d(TAG, "Couldn't remove task: didn't exist: " + taskStatus); + Slog.d(TAG, "Couldn't remove job: didn't exist: " + jobStatus); } return false; } @@ -166,48 +166,48 @@ public class TaskStore { @VisibleForTesting public void clear() { - mTasksSet.clear(); + mJobSet.clear(); maybeWriteStatusToDiskAsync(); } - public List<TaskStatus> getTasksByUser(int userHandle) { - List<TaskStatus> matchingTasks = new ArrayList<TaskStatus>(); - Iterator<TaskStatus> it = mTasksSet.iterator(); + public List<JobStatus> getJobsByUser(int userHandle) { + List<JobStatus> matchingJobs = new ArrayList<JobStatus>(); + Iterator<JobStatus> it = mJobSet.iterator(); while (it.hasNext()) { - TaskStatus ts = it.next(); + JobStatus ts = it.next(); if (UserHandle.getUserId(ts.getUid()) == userHandle) { - matchingTasks.add(ts); + matchingJobs.add(ts); } } - return matchingTasks; + return matchingJobs; } /** * @param uid Uid of the requesting app. - * @return All TaskStatus objects for a given uid from the master list. + * @return All JobStatus objects for a given uid from the master list. */ - public List<TaskStatus> getTasksByUid(int uid) { - List<TaskStatus> matchingTasks = new ArrayList<TaskStatus>(); - Iterator<TaskStatus> it = mTasksSet.iterator(); + public List<JobStatus> getJobsByUid(int uid) { + List<JobStatus> matchingJobs = new ArrayList<JobStatus>(); + Iterator<JobStatus> it = mJobSet.iterator(); while (it.hasNext()) { - TaskStatus ts = it.next(); + JobStatus ts = it.next(); if (ts.getUid() == uid) { - matchingTasks.add(ts); + matchingJobs.add(ts); } } - return matchingTasks; + return matchingJobs; } /** * @param uid Uid of the requesting app. - * @param taskId Task id, specified at schedule-time. - * @return the TaskStatus that matches the provided uId and taskId, or null if none found. + * @param jobId Job id, specified at schedule-time. + * @return the JobStatus that matches the provided uId and jobId, or null if none found. */ - public TaskStatus getTaskByUidAndTaskId(int uid, int taskId) { - Iterator<TaskStatus> it = mTasksSet.iterator(); + public JobStatus getJobByUidAndJobId(int uid, int jobId) { + Iterator<JobStatus> it = mJobSet.iterator(); while (it.hasNext()) { - TaskStatus ts = it.next(); - if (ts.getUid() == uid && ts.getTaskId() == taskId) { + JobStatus ts = it.next(); + if (ts.getUid() == uid && ts.getJobId() == jobid) { return ts; } } @@ -215,15 +215,15 @@ public class TaskStore { } /** - * @return The live array of TaskStatus objects. + * @return The live array of JobStatus objects. */ - public ArraySet<TaskStatus> getTasks() { - return mTasksSet; + public ArraySet<JobStatus> getJobs() { + return mJobSet; } /** Version of the db schema. */ - private static final int TASKS_FILE_VERSION = 0; - /** Tag corresponds to constraints this task needs. */ + private static final int JOBS_FILE_VERSION = 0; + /** Tag corresponds to constraints this job needs. */ private static final String XML_TAG_PARAMS_CONSTRAINTS = "constraints"; /** Tag corresponds to execution parameters. */ private static final String XML_TAG_PERIODIC = "periodic"; @@ -231,7 +231,7 @@ public class TaskStore { private static final String XML_TAG_EXTRAS = "extras"; /** - * Every time the state changes we write all the tasks in one swathe, instead of trying to + * Every time the state changes we write all the jobs in one swath, instead of trying to * track incremental changes. * @return Whether the operation was successful. This will only fail for e.g. if the system is * low on storage. If this happens, we continue as normal @@ -240,38 +240,38 @@ public class TaskStore { mDirtyOperations++; if (mDirtyOperations >= MAX_OPS_BEFORE_WRITE) { if (DEBUG) { - Slog.v(TAG, "Writing tasks to disk."); + Slog.v(TAG, "Writing jobs to disk."); } - mIoHandler.post(new WriteTasksMapToDiskRunnable()); + mIoHandler.post(new WriteJobsMapToDiskRunnable()); } } - private void readTaskMapFromDiskAsync(TaskMapReadFinishedListener callback) { - mIoHandler.post(new ReadTaskMapFromDiskRunnable(callback)); + private void readJobMapFromDiskAsync(JobMapReadFinishedListener callback) { + mIoHandler.post(new ReadJobMapFromDiskRunnable(callback)); } - public void readTaskMapFromDisk(TaskMapReadFinishedListener callback) { - new ReadTaskMapFromDiskRunnable(callback).run(); + public void readJobMapFromDisk(JobMapReadFinishedListener callback) { + new ReadJobMapFromDiskRunnable(callback).run(); } /** - * Runnable that writes {@link #mTasksSet} out to xml. - * NOTE: This Runnable locks on TaskStore.this + * Runnable that writes {@link #mJobSet} out to xml. + * NOTE: This Runnable locks on JobStore.this */ - private class WriteTasksMapToDiskRunnable implements Runnable { + private class WriteJobsMapToDiskRunnable implements Runnable { @Override public void run() { final long startElapsed = SystemClock.elapsedRealtime(); - synchronized (TaskStore.this) { - writeTasksMapImpl(); + synchronized (JobStore.this) { + writeJobsMapImpl(); } - if (TaskManagerService.DEBUG) { + if (JobSchedulerService.DEBUG) { Slog.v(TAG, "Finished writing, took " + (SystemClock.elapsedRealtime() - startElapsed) + "ms"); } } - private void writeTasksMapImpl() { + private void writeJobsMapImpl() { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); XmlSerializer out = new FastXmlSerializer(); @@ -279,31 +279,31 @@ public class TaskStore { out.startDocument(null, true); out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); - out.startTag(null, "task-info"); - out.attribute(null, "version", Integer.toString(TASKS_FILE_VERSION)); - for (int i = 0; i < mTasksSet.size(); i++) { - final TaskStatus taskStatus = mTasksSet.valueAt(i); + out.startTag(null, "job-info"); + out.attribute(null, "version", Integer.toString(JOBS_FILE_VERSION)); + for (int i = 0; i < mJobSet.size(); i++) { + final JobStatus jobStatus = mJobSet.valueAt(i); if (DEBUG) { - Slog.d(TAG, "Saving task " + taskStatus.getTaskId()); + Slog.d(TAG, "Saving job " + jobStatus.getJobId()); } - out.startTag(null, "task"); - addIdentifierAttributesToTaskTag(out, taskStatus); - writeConstraintsToXml(out, taskStatus); - writeExecutionCriteriaToXml(out, taskStatus); - writeBundleToXml(taskStatus.getExtras(), out); - out.endTag(null, "task"); + out.startTag(null, "job"); + addIdentifierAttributesToJobTag(out, jobStatus); + writeConstraintsToXml(out, jobStatus); + writeExecutionCriteriaToXml(out, jobStatus); + writeBundleToXml(jobStatus.getExtras(), out); + out.endTag(null, "job"); } - out.endTag(null, "task-info"); + out.endTag(null, "job-info"); out.endDocument(); // Write out to disk in one fell sweep. - FileOutputStream fos = mTasksFile.startWrite(); + FileOutputStream fos = mJobsFile.startWrite(); fos.write(baos.toByteArray()); - mTasksFile.finishWrite(fos); + mJobsFile.finishWrite(fos); mDirtyOperations = 0; } catch (IOException e) { if (DEBUG) { - Slog.v(TAG, "Error writing out task data.", e); + Slog.v(TAG, "Error writing out job data.", e); } } catch (XmlPullParserException e) { if (DEBUG) { @@ -312,13 +312,13 @@ public class TaskStore { } } - /** Write out a tag with data comprising the required fields of this task and its client. */ - private void addIdentifierAttributesToTaskTag(XmlSerializer out, TaskStatus taskStatus) + /** Write out a tag with data comprising the required fields of this job and its client. */ + private void addIdentifierAttributesToJobTag(XmlSerializer out, JobStatus jobStatus) throws IOException { - out.attribute(null, "taskid", Integer.toString(taskStatus.getTaskId())); - out.attribute(null, "package", taskStatus.getServiceComponent().getPackageName()); - out.attribute(null, "class", taskStatus.getServiceComponent().getClassName()); - out.attribute(null, "uid", Integer.toString(taskStatus.getUid())); + out.attribute(null, "jobid", Integer.toString(jobStatus.getJobId())); + out.attribute(null, "package", jobStatus.getServiceComponent().getPackageName()); + out.attribute(null, "class", jobStatus.getServiceComponent().getClassName()); + out.attribute(null, "uid", Integer.toString(jobStatus.getUid())); } private void writeBundleToXml(PersistableBundle extras, XmlSerializer out) @@ -328,57 +328,57 @@ public class TaskStore { out.endTag(null, XML_TAG_EXTRAS); } /** - * Write out a tag with data identifying this tasks constraints. If the constraint isn't here + * Write out a tag with data identifying this job's constraints. If the constraint isn't here * it doesn't apply. */ - private void writeConstraintsToXml(XmlSerializer out, TaskStatus taskStatus) throws IOException { + private void writeConstraintsToXml(XmlSerializer out, JobStatus jobStatus) throws IOException { out.startTag(null, XML_TAG_PARAMS_CONSTRAINTS); - if (taskStatus.hasUnmeteredConstraint()) { + if (jobStatus.hasUnmeteredConstraint()) { out.attribute(null, "unmetered", Boolean.toString(true)); } - if (taskStatus.hasConnectivityConstraint()) { + if (jobStatus.hasConnectivityConstraint()) { out.attribute(null, "connectivity", Boolean.toString(true)); } - if (taskStatus.hasIdleConstraint()) { + if (jobStatus.hasIdleConstraint()) { out.attribute(null, "idle", Boolean.toString(true)); } - if (taskStatus.hasChargingConstraint()) { + if (jobStatus.hasChargingConstraint()) { out.attribute(null, "charging", Boolean.toString(true)); } out.endTag(null, XML_TAG_PARAMS_CONSTRAINTS); } - private void writeExecutionCriteriaToXml(XmlSerializer out, TaskStatus taskStatus) + private void writeExecutionCriteriaToXml(XmlSerializer out, JobStatus jobStatus) throws IOException { - final Task task = taskStatus.getTask(); - if (taskStatus.getTask().isPeriodic()) { + final JobInfo job = jobStatus.getJob(); + if (jobStatus.getJob().isPeriodic()) { out.startTag(null, XML_TAG_PERIODIC); - out.attribute(null, "period", Long.toString(task.getIntervalMillis())); + out.attribute(null, "period", Long.toString(job.getIntervalMillis())); } else { out.startTag(null, XML_TAG_ONEOFF); } - if (taskStatus.hasDeadlineConstraint()) { + if (jobStatus.hasDeadlineConstraint()) { // Wall clock deadline. final long deadlineWallclock = System.currentTimeMillis() + - (taskStatus.getLatestRunTimeElapsed() - SystemClock.elapsedRealtime()); + (jobStatus.getLatestRunTimeElapsed() - SystemClock.elapsedRealtime()); out.attribute(null, "deadline", Long.toString(deadlineWallclock)); } - if (taskStatus.hasTimingDelayConstraint()) { + if (jobStatus.hasTimingDelayConstraint()) { final long delayWallclock = System.currentTimeMillis() + - (taskStatus.getEarliestRunTime() - SystemClock.elapsedRealtime()); + (jobStatus.getEarliestRunTime() - SystemClock.elapsedRealtime()); out.attribute(null, "delay", Long.toString(delayWallclock)); } // Only write out back-off policy if it differs from the default. - // This also helps the case where the task is idle -> these aren't allowed to specify + // This also helps the case where the job is idle -> these aren't allowed to specify // back-off. - if (taskStatus.getTask().getInitialBackoffMillis() != Task.DEFAULT_INITIAL_BACKOFF_MILLIS - || taskStatus.getTask().getBackoffPolicy() != Task.DEFAULT_BACKOFF_POLICY) { - out.attribute(null, "backoff-policy", Integer.toString(task.getBackoffPolicy())); - out.attribute(null, "initial-backoff", Long.toString(task.getInitialBackoffMillis())); + if (jobStatus.getJob().getInitialBackoffMillis() != JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS + || jobStatus.getJob().getBackoffPolicy() != JobInfo.DEFAULT_BACKOFF_POLICY) { + out.attribute(null, "backoff-policy", Integer.toString(job.getBackoffPolicy())); + out.attribute(null, "initial-backoff", Long.toString(job.getInitialBackoffMillis())); } - if (task.isPeriodic()) { + if (job.isPeriodic()) { out.endTag(null, XML_TAG_PERIODIC); } else { out.endTag(null, XML_TAG_ONEOFF); @@ -387,43 +387,43 @@ public class TaskStore { } /** - * Runnable that reads list of persisted task from xml. - * NOTE: This Runnable locks on TaskStore.this + * Runnable that reads list of persisted job from xml. + * NOTE: This Runnable locks on JobStore.this */ - private class ReadTaskMapFromDiskRunnable implements Runnable { - private TaskMapReadFinishedListener mCallback; - public ReadTaskMapFromDiskRunnable(TaskMapReadFinishedListener callback) { + private class ReadJobMapFromDiskRunnable implements Runnable { + private JobMapReadFinishedListener mCallback; + public ReadJobMapFromDiskRunnable(JobMapReadFinishedListener callback) { mCallback = callback; } @Override public void run() { try { - List<TaskStatus> tasks; - FileInputStream fis = mTasksFile.openRead(); - synchronized (TaskStore.this) { - tasks = readTaskMapImpl(fis); + List<JobStatus> jobs; + FileInputStream fis = mJobsFile.openRead(); + synchronized (JobStore.this) { + jobs = readJobMapImpl(fis); } fis.close(); - if (tasks != null) { - mCallback.onTaskMapReadFinished(tasks); + if (jobs != null) { + mCallback.onJobMapReadFinished(jobs); } } catch (FileNotFoundException e) { - if (TaskManagerService.DEBUG) { - Slog.d(TAG, "Could not find tasks file, probably there was nothing to load."); + if (JobSchedulerService.DEBUG) { + Slog.d(TAG, "Could not find jobs file, probably there was nothing to load."); } } catch (XmlPullParserException e) { - if (TaskManagerService.DEBUG) { + if (JobSchedulerService.DEBUG) { Slog.d(TAG, "Error parsing xml.", e); } } catch (IOException e) { - if (TaskManagerService.DEBUG) { + if (JobSchedulerService.DEBUG) { Slog.d(TAG, "Error parsing xml.", e); } } } - private List<TaskStatus> readTaskMapImpl(FileInputStream fis) throws XmlPullParserException, IOException { + private List<JobStatus> readJobMapImpl(FileInputStream fis) throws XmlPullParserException, IOException { XmlPullParser parser = Xml.newPullParser(); parser.setInput(fis, null); @@ -435,66 +435,66 @@ public class TaskStore { } if (eventType == XmlPullParser.END_DOCUMENT) { if (DEBUG) { - Slog.d(TAG, "No persisted tasks."); + Slog.d(TAG, "No persisted jobs."); } return null; } String tagName = parser.getName(); - if ("task-info".equals(tagName)) { - final List<TaskStatus> tasks = new ArrayList<TaskStatus>(); + if ("job-info".equals(tagName)) { + final List<JobStatus> jobs = new ArrayList<JobStatus>(); // Read in version info. try { int version = Integer.valueOf(parser.getAttributeValue(null, "version")); - if (version != TASKS_FILE_VERSION) { - Slog.d(TAG, "Invalid version number, aborting tasks file read."); + if (version != JOBS_FILE_VERSION) { + Slog.d(TAG, "Invalid version number, aborting jobs file read."); return null; } } catch (NumberFormatException e) { - Slog.e(TAG, "Invalid version number, aborting tasks file read."); + Slog.e(TAG, "Invalid version number, aborting jobs file read."); return null; } eventType = parser.next(); do { - // Read each <task/> + // Read each <job/> if (eventType == XmlPullParser.START_TAG) { tagName = parser.getName(); - // Start reading task. - if ("task".equals(tagName)) { - TaskStatus persistedTask = restoreTaskFromXml(parser); - if (persistedTask != null) { + // Start reading job. + if ("job".equals(tagName)) { + JobStatus persistedJob = restoreJobFromXml(parser); + if (persistedJob != null) { if (DEBUG) { - Slog.d(TAG, "Read out " + persistedTask); + Slog.d(TAG, "Read out " + persistedJob); } - tasks.add(persistedTask); + jobs.add(persistedJob); } else { - Slog.d(TAG, "Error reading task from file."); + Slog.d(TAG, "Error reading job from file."); } } } eventType = parser.next(); } while (eventType != XmlPullParser.END_DOCUMENT); - return tasks; + return jobs; } return null; } /** - * @param parser Xml parser at the beginning of a "<task/>" tag. The next "parser.next()" call - * will take the parser into the body of the task tag. - * @return Newly instantiated task holding all the information we just read out of the xml tag. + * @param parser Xml parser at the beginning of a "<job/>" tag. The next "parser.next()" call + * will take the parser into the body of the job tag. + * @return Newly instantiated job holding all the information we just read out of the xml tag. */ - private TaskStatus restoreTaskFromXml(XmlPullParser parser) throws XmlPullParserException, + private JobStatus restoreJobFromXml(XmlPullParser parser) throws XmlPullParserException, IOException { - Task.Builder taskBuilder; + JobInfo.Builder jobBuilder; int uid; - // Read out task identifier attributes. + // Read out job identifier attributes. try { - taskBuilder = buildBuilderFromXml(parser); + jobBuilder = buildBuilderFromXml(parser); uid = Integer.valueOf(parser.getAttributeValue(null, "uid")); } catch (NumberFormatException e) { - Slog.e(TAG, "Error parsing task's required fields, skipping"); + Slog.e(TAG, "Error parsing job's required fields, skipping"); return null; } @@ -510,7 +510,7 @@ public class TaskStore { return null; } try { - buildConstraintsFromXml(taskBuilder, parser); + buildConstraintsFromXml(jobBuilder, parser); } catch (NumberFormatException e) { Slog.d(TAG, "Error reading constraints, skipping."); return null; @@ -538,22 +538,22 @@ public class TaskStore { if (XML_TAG_PERIODIC.equals(parser.getName())) { try { String val = parser.getAttributeValue(null, "period"); - taskBuilder.setPeriodic(Long.valueOf(val)); + jobBuilder.setPeriodic(Long.valueOf(val)); } catch (NumberFormatException e) { Slog.d(TAG, "Error reading periodic execution criteria, skipping."); return null; } } else if (XML_TAG_ONEOFF.equals(parser.getName())) { try { - if (runtimes.first != TaskStatus.NO_EARLIEST_RUNTIME) { - taskBuilder.setMinimumLatency(runtimes.first - SystemClock.elapsedRealtime()); + if (runtimes.first != JobStatus.NO_EARLIEST_RUNTIME) { + jobBuilder.setMinimumLatency(runtimes.first - SystemClock.elapsedRealtime()); } - if (runtimes.second != TaskStatus.NO_LATEST_RUNTIME) { - taskBuilder.setOverrideDeadline( + if (runtimes.second != JobStatus.NO_LATEST_RUNTIME) { + jobBuilder.setOverrideDeadline( runtimes.second - SystemClock.elapsedRealtime()); } } catch (NumberFormatException e) { - Slog.d(TAG, "Error reading task execution criteria, skipping."); + Slog.d(TAG, "Error reading job execution criteria, skipping."); return null; } } else { @@ -563,7 +563,7 @@ public class TaskStore { // Expecting a parameters start tag. return null; } - maybeBuildBackoffPolicyFromXml(taskBuilder, parser); + maybeBuildBackoffPolicyFromXml(jobBuilder, parser); parser.nextTag(); // Consume parameters end tag. @@ -579,52 +579,52 @@ public class TaskStore { } PersistableBundle extras = PersistableBundle.restoreFromXml(parser); - taskBuilder.setExtras(extras); + jobBuilder.setExtras(extras); parser.nextTag(); // Consume </extras> - return new TaskStatus(taskBuilder.build(), uid, runtimes.first, runtimes.second); + return new JobStatus(jobBuilder.build(), uid, runtimes.first, runtimes.second); } - private Task.Builder buildBuilderFromXml(XmlPullParser parser) throws NumberFormatException { - // Pull out required fields from <task> attributes. - int taskId = Integer.valueOf(parser.getAttributeValue(null, "taskid")); + private JobInfo.Builder buildBuilderFromXml(XmlPullParser parser) throws NumberFormatException { + // Pull out required fields from <job> attributes. + int jobId = Integer.valueOf(parser.getAttributeValue(null, "jobid")); String packageName = parser.getAttributeValue(null, "package"); String className = parser.getAttributeValue(null, "class"); ComponentName cname = new ComponentName(packageName, className); - return new Task.Builder(taskId, cname); + return new JobInfo.Builder(jobId, cname); } - private void buildConstraintsFromXml(Task.Builder taskBuilder, XmlPullParser parser) { + private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) { String val = parser.getAttributeValue(null, "unmetered"); if (val != null) { - taskBuilder.setRequiredNetworkCapabilities(Task.NetworkType.UNMETERED); + jobBuilder.setRequiredNetworkCapabilities(JobInfo.NetworkType.UNMETERED); } val = parser.getAttributeValue(null, "connectivity"); if (val != null) { - taskBuilder.setRequiredNetworkCapabilities(Task.NetworkType.ANY); + jobBuilder.setRequiredNetworkCapabilities(JobInfo.NetworkType.ANY); } val = parser.getAttributeValue(null, "idle"); if (val != null) { - taskBuilder.setRequiresDeviceIdle(true); + jobBuilder.setRequiresDeviceIdle(true); } val = parser.getAttributeValue(null, "charging"); if (val != null) { - taskBuilder.setRequiresCharging(true); + jobBuilder.setRequiresCharging(true); } } /** * Builds the back-off policy out of the params tag. These attributes may not exist, depending - * on whether the back-off was set when the task was first scheduled. + * on whether the back-off was set when the job was first scheduled. */ - private void maybeBuildBackoffPolicyFromXml(Task.Builder taskBuilder, XmlPullParser parser) { + private void maybeBuildBackoffPolicyFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) { String val = parser.getAttributeValue(null, "initial-backoff"); if (val != null) { long initialBackoff = Long.valueOf(val); val = parser.getAttributeValue(null, "backoff-policy"); int backoffPolicy = Integer.valueOf(val); // Will throw NFE which we catch higher up. - taskBuilder.setBackoffCriteria(initialBackoff, backoffPolicy); + jobBuilder.setBackoffCriteria(initialBackoff, backoffPolicy); } } @@ -640,8 +640,8 @@ public class TaskStore { final long nowWallclock = System.currentTimeMillis(); final long nowElapsed = SystemClock.elapsedRealtime(); - long earliestRunTimeElapsed = TaskStatus.NO_EARLIEST_RUNTIME; - long latestRunTimeElapsed = TaskStatus.NO_LATEST_RUNTIME; + long earliestRunTimeElapsed = JobStatus.NO_EARLIEST_RUNTIME; + long latestRunTimeElapsed = JobStatus.NO_LATEST_RUNTIME; String val = parser.getAttributeValue(null, "deadline"); if (val != null) { long latestRuntimeWallclock = Long.valueOf(val); diff --git a/services/core/java/com/android/server/task/StateChangedListener.java b/services/core/java/com/android/server/job/StateChangedListener.java index ab5cc7c..90c203a 100644 --- a/services/core/java/com/android/server/task/StateChangedListener.java +++ b/services/core/java/com/android/server/job/StateChangedListener.java @@ -14,26 +14,26 @@ * limitations under the License */ -package com.android.server.task; +package com.android.server.job; -import com.android.server.task.controllers.TaskStatus; +import com.android.server.job.controllers.JobStatus; /** - * Interface through which a {@link com.android.server.task.controllers.StateController} informs - * the {@link com.android.server.task.TaskManagerService} that there are some tasks potentially + * Interface through which a {@link com.android.server.job.controllers.StateController} informs + * the {@link com.android.server.job.JobSchedulerService} that there are some tasks potentially * ready to be run. */ public interface StateChangedListener { /** - * Called by the controller to notify the TaskManager that it should check on the state of a + * Called by the controller to notify the JobManager that it should check on the state of a * task. */ public void onControllerStateChanged(); /** - * Called by the controller to notify the TaskManager that regardless of the state of the task, + * Called by the controller to notify the JobManager that regardless of the state of the task, * it must be run immediately. - * @param taskStatus The state of the task which is to be run immediately. + * @param jobStatus The state of the task which is to be run immediately. */ - public void onRunTaskNow(TaskStatus taskStatus); + public void onRunJobNow(JobStatus jobStatus); } diff --git a/services/core/java/com/android/server/task/controllers/BatteryController.java b/services/core/java/com/android/server/job/controllers/BatteryController.java index 443527f..010de5c 100644 --- a/services/core/java/com/android/server/task/controllers/BatteryController.java +++ b/services/core/java/com/android/server/job/controllers/BatteryController.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.server.task.controllers; +package com.android.server.job.controllers; import android.app.AlarmManager; import android.app.PendingIntent; @@ -32,8 +32,8 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.server.BatteryService; -import com.android.server.task.StateChangedListener; -import com.android.server.task.TaskManagerService; +import com.android.server.job.JobSchedulerService; +import com.android.server.job.StateChangedListener; import java.io.PrintWriter; import java.util.ArrayList; @@ -54,10 +54,10 @@ public class BatteryController extends StateController { /** Wait this long after phone is plugged in before doing any work. */ private static final long STABLE_CHARGING_THRESHOLD_MILLIS = 2 * 60 * 1000; // 2 minutes. - private List<TaskStatus> mTrackedTasks = new ArrayList<TaskStatus>(); + private List<JobStatus> mTrackedTasks = new ArrayList<JobStatus>(); private ChargingTracker mChargeTracker; - public static BatteryController get(TaskManagerService taskManagerService) { + public static BatteryController get(JobSchedulerService taskManagerService) { synchronized (sCreationLock) { if (sController == null) { sController = new BatteryController(taskManagerService, @@ -85,7 +85,7 @@ public class BatteryController extends StateController { } @Override - public void maybeStartTrackingTask(TaskStatus taskStatus) { + public void maybeStartTrackingJob(JobStatus taskStatus) { if (taskStatus.hasChargingConstraint()) { synchronized (mTrackedTasks) { mTrackedTasks.add(taskStatus); @@ -96,7 +96,7 @@ public class BatteryController extends StateController { } @Override - public void maybeStopTrackingTask(TaskStatus taskStatus) { + public void maybeStopTrackingJob(JobStatus taskStatus) { if (taskStatus.hasChargingConstraint()) { synchronized (mTrackedTasks) { mTrackedTasks.remove(taskStatus); @@ -108,7 +108,7 @@ public class BatteryController extends StateController { final boolean stablePower = mChargeTracker.isOnStablePower(); boolean reportChange = false; synchronized (mTrackedTasks) { - for (TaskStatus ts : mTrackedTasks) { + for (JobStatus ts : mTrackedTasks) { boolean previous = ts.chargingConstraintSatisfied.getAndSet(stablePower); if (previous != stablePower) { reportChange = true; diff --git a/services/core/java/com/android/server/task/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java index c1ab0f0..7e79ff7 100644 --- a/services/core/java/com/android/server/task/controllers/ConnectivityController.java +++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.server.task.controllers; +package com.android.server.job.controllers; import android.content.BroadcastReceiver; @@ -28,8 +28,8 @@ import android.os.UserHandle; import android.util.Slog; import com.android.server.ConnectivityService; -import com.android.server.task.StateChangedListener; -import com.android.server.task.TaskManagerService; +import com.android.server.job.JobSchedulerService; +import com.android.server.job.StateChangedListener; import java.io.PrintWriter; import java.util.LinkedList; @@ -42,9 +42,9 @@ import java.util.List; */ public class ConnectivityController extends StateController implements ConnectivityManager.OnNetworkActiveListener { - private static final String TAG = "TaskManager.Conn"; + private static final String TAG = "JobScheduler.Conn"; - private final List<TaskStatus> mTrackedTasks = new LinkedList<TaskStatus>(); + private final List<JobStatus> mTrackedJobs = new LinkedList<JobStatus>(); private final BroadcastReceiver mConnectivityChangedReceiver = new ConnectivityChangedReceiver(); /** Singleton. */ @@ -55,10 +55,10 @@ public class ConnectivityController extends StateController implements /** Track whether the latest active network is connected. */ private boolean mNetworkConnected; - public static ConnectivityController get(TaskManagerService taskManager) { + public static ConnectivityController get(JobSchedulerService jms) { synchronized (sCreationLock) { if (mSingleton == null) { - mSingleton = new ConnectivityController(taskManager, taskManager.getContext()); + mSingleton = new ConnectivityController(jms, jms.getContext()); } return mSingleton; } @@ -82,21 +82,21 @@ public class ConnectivityController extends StateController implements } @Override - public void maybeStartTrackingTask(TaskStatus taskStatus) { - if (taskStatus.hasConnectivityConstraint() || taskStatus.hasUnmeteredConstraint()) { - synchronized (mTrackedTasks) { - taskStatus.connectivityConstraintSatisfied.set(mNetworkConnected); - taskStatus.unmeteredConstraintSatisfied.set(mNetworkUnmetered); - mTrackedTasks.add(taskStatus); + public void maybeStartTrackingJob(JobStatus jobStatus) { + if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()) { + synchronized (mTrackedJobs) { + jobStatus.connectivityConstraintSatisfied.set(mNetworkConnected); + jobStatus.unmeteredConstraintSatisfied.set(mNetworkUnmetered); + mTrackedJobs.add(jobStatus); } } } @Override - public void maybeStopTrackingTask(TaskStatus taskStatus) { - if (taskStatus.hasConnectivityConstraint() || taskStatus.hasUnmeteredConstraint()) { - synchronized (mTrackedTasks) { - mTrackedTasks.remove(taskStatus); + public void maybeStopTrackingJob(JobStatus jobStatus) { + if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()) { + synchronized (mTrackedJobs) { + mTrackedJobs.remove(jobStatus); } } } @@ -104,16 +104,16 @@ public class ConnectivityController extends StateController implements /** * @param userId Id of the user for whom we are updating the connectivity state. */ - private void updateTrackedTasks(int userId) { - synchronized (mTrackedTasks) { + private void updateTrackedJobs(int userId) { + synchronized (mTrackedJobs) { boolean changed = false; - for (TaskStatus ts : mTrackedTasks) { - if (ts.getUserId() != userId) { + for (JobStatus js : mTrackedJobs) { + if (js.getUserId() != userId) { continue; } boolean prevIsConnected = - ts.connectivityConstraintSatisfied.getAndSet(mNetworkConnected); - boolean prevIsMetered = ts.unmeteredConstraintSatisfied.getAndSet(mNetworkUnmetered); + js.connectivityConstraintSatisfied.getAndSet(mNetworkConnected); + boolean prevIsMetered = js.unmeteredConstraintSatisfied.getAndSet(mNetworkUnmetered); if (prevIsConnected != mNetworkConnected || prevIsMetered != mNetworkUnmetered) { changed = true; } @@ -125,16 +125,16 @@ public class ConnectivityController extends StateController implements } /** - * We know the network has just come up. We want to run any tasks that are ready. + * We know the network has just come up. We want to run any jobs that are ready. */ public synchronized void onNetworkActive() { - synchronized (mTrackedTasks) { - for (TaskStatus ts : mTrackedTasks) { - if (ts.isReady()) { + synchronized (mTrackedJobs) { + for (JobStatus js : mTrackedJobs) { + if (js.isReady()) { if (DEBUG) { - Slog.d(TAG, "Running " + ts + " due to network activity."); + Slog.d(TAG, "Running " + js + " due to network activity."); } - mStateChangedListener.onRunTaskNow(ts); + mStateChangedListener.onRunJobNow(js); } } } @@ -169,7 +169,7 @@ public class ConnectivityController extends StateController implements if (activeNetwork == null) { mNetworkUnmetered = false; mNetworkConnected = false; - updateTrackedTasks(userid); + updateTrackedJobs(userid); } else if (activeNetwork.getType() == networkType) { mNetworkUnmetered = false; mNetworkConnected = !intent.getBooleanExtra( @@ -177,7 +177,7 @@ public class ConnectivityController extends StateController implements if (mNetworkConnected) { // No point making the call if we know there's no conn. mNetworkUnmetered = !connManager.isActiveNetworkMetered(); } - updateTrackedTasks(userid); + updateTrackedJobs(userid); } } else { if (DEBUG) { @@ -191,10 +191,10 @@ public class ConnectivityController extends StateController implements public void dumpControllerState(PrintWriter pw) { pw.println("Conn."); pw.println("connected: " + mNetworkConnected + " unmetered: " + mNetworkUnmetered); - for (TaskStatus ts: mTrackedTasks) { - pw.println(String.valueOf(ts.hashCode()).substring(0, 3) + ".." - + ": C=" + ts.hasConnectivityConstraint() - + ", UM=" + ts.hasUnmeteredConstraint()); + for (JobStatus js: mTrackedJobs) { + pw.println(String.valueOf(js.hashCode()).substring(0, 3) + ".." + + ": C=" + js.hasConnectivityConstraint() + + ", UM=" + js.hasUnmeteredConstraint()); } } }
\ No newline at end of file diff --git a/services/core/java/com/android/server/task/controllers/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java index e749b00..07ffe4d 100644 --- a/services/core/java/com/android/server/task/controllers/IdleController.java +++ b/services/core/java/com/android/server/job/controllers/IdleController.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.server.task.controllers; +package com.android.server.job.controllers; import java.io.PrintWriter; import java.util.ArrayList; @@ -29,8 +29,8 @@ import android.content.IntentFilter; import android.os.SystemClock; import android.util.Slog; -import com.android.server.task.StateChangedListener; -import com.android.server.task.TaskManagerService; +import com.android.server.job.JobSchedulerService; +import com.android.server.job.StateChangedListener; public class IdleController extends StateController { private static final String TAG = "IdleController"; @@ -43,14 +43,14 @@ public class IdleController extends StateController { private static final String ACTION_TRIGGER_IDLE = "com.android.server.task.controllers.IdleController.ACTION_TRIGGER_IDLE"; - final ArrayList<TaskStatus> mTrackedTasks = new ArrayList<TaskStatus>(); + final ArrayList<JobStatus> mTrackedTasks = new ArrayList<JobStatus>(); IdlenessTracker mIdleTracker; // Singleton factory private static Object sCreationLock = new Object(); private static volatile IdleController sController; - public static IdleController get(TaskManagerService service) { + public static IdleController get(JobSchedulerService service) { synchronized (sCreationLock) { if (sController == null) { sController = new IdleController(service, service.getContext()); @@ -68,7 +68,7 @@ public class IdleController extends StateController { * StateController interface */ @Override - public void maybeStartTrackingTask(TaskStatus taskStatus) { + public void maybeStartTrackingJob(JobStatus taskStatus) { if (taskStatus.hasIdleConstraint()) { synchronized (mTrackedTasks) { mTrackedTasks.add(taskStatus); @@ -78,7 +78,7 @@ public class IdleController extends StateController { } @Override - public void maybeStopTrackingTask(TaskStatus taskStatus) { + public void maybeStopTrackingJob(JobStatus taskStatus) { synchronized (mTrackedTasks) { mTrackedTasks.remove(taskStatus); } @@ -89,7 +89,7 @@ public class IdleController extends StateController { */ void reportNewIdleState(boolean isIdle) { synchronized (mTrackedTasks) { - for (TaskStatus task : mTrackedTasks) { + for (JobStatus task : mTrackedTasks) { task.idleConstraintSatisfied.set(isIdle); } } diff --git a/services/core/java/com/android/server/task/controllers/TaskStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index a286737..15a6b25 100644 --- a/services/core/java/com/android/server/task/controllers/TaskStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -14,9 +14,9 @@ * limitations under the License */ -package com.android.server.task.controllers; +package com.android.server.job.controllers; -import android.app.task.Task; +import android.app.job.JobInfo; import android.content.ComponentName; import android.os.PersistableBundle; import android.os.SystemClock; @@ -26,9 +26,9 @@ import java.io.PrintWriter; import java.util.concurrent.atomic.AtomicBoolean; /** - * Uniquely identifies a task internally. - * Created from the public {@link android.app.task.Task} object when it lands on the scheduler. - * Contains current state of the requirements of the task, as well as a function to evaluate + * Uniquely identifies a job internally. + * Created from the public {@link android.app.job.JobInfo} object when it lands on the scheduler. + * Contains current state of the requirements of the job, as well as a function to evaluate * whether it's ready to run. * This object is shared among the various controllers - hence why the different fields are atomic. * This isn't strictly necessary because each controller is only interested in a specific field, @@ -36,14 +36,14 @@ import java.util.concurrent.atomic.AtomicBoolean; * but we don't enforce that so this is safer. * @hide */ -public class TaskStatus { +public class JobStatus { public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE; public static final long NO_EARLIEST_RUNTIME = 0L; - final Task task; + final JobInfo job; final int uId; - /** At reschedule time we need to know whether to update task on disk. */ + /** At reschedule time we need to know whether to update job on disk. */ final boolean persisted; // Constraints. @@ -55,77 +55,77 @@ public class TaskStatus { final AtomicBoolean connectivityConstraintSatisfied = new AtomicBoolean(); /** - * Earliest point in the future at which this task will be eligible to run. A value of 0 + * Earliest point in the future at which this job will be eligible to run. A value of 0 * indicates there is no delay constraint. See {@link #hasTimingDelayConstraint()}. */ private long earliestRunTimeElapsedMillis; /** - * Latest point in the future at which this task must be run. A value of {@link Long#MAX_VALUE} + * Latest point in the future at which this job must be run. A value of {@link Long#MAX_VALUE} * indicates there is no deadline constraint. See {@link #hasDeadlineConstraint()}. */ private long latestRunTimeElapsedMillis; - /** How many times this task has failed, used to compute back-off. */ + /** How many times this job has failed, used to compute back-off. */ private final int numFailures; - /** Provide a handle to the service that this task will be run on. */ + /** Provide a handle to the service that this job will be run on. */ public int getServiceToken() { return uId; } - private TaskStatus(Task task, int uId, boolean persisted, int numFailures) { - this.task = task; + private JobStatus(JobInfo job, int uId, boolean persisted, int numFailures) { + this.job = job; this.uId = uId; this.numFailures = numFailures; this.persisted = persisted; } - /** Create a newly scheduled task. */ - public TaskStatus(Task task, int uId, boolean persisted) { - this(task, uId, persisted, 0); + /** Create a newly scheduled job. */ + public JobStatus(JobInfo job, int uId, boolean persisted) { + this(job, uId, persisted, 0); final long elapsedNow = SystemClock.elapsedRealtime(); - if (task.isPeriodic()) { + if (job.isPeriodic()) { earliestRunTimeElapsedMillis = elapsedNow; - latestRunTimeElapsedMillis = elapsedNow + task.getIntervalMillis(); + latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis(); } else { - earliestRunTimeElapsedMillis = task.hasEarlyConstraint() ? - elapsedNow + task.getMinLatencyMillis() : NO_EARLIEST_RUNTIME; - latestRunTimeElapsedMillis = task.hasLateConstraint() ? - elapsedNow + task.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME; + earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ? + elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME; + latestRunTimeElapsedMillis = job.hasLateConstraint() ? + elapsedNow + job.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME; } } /** - * Create a new TaskStatus that was loaded from disk. We ignore the provided - * {@link android.app.task.Task} time criteria because we can load a persisted periodic task - * from the {@link com.android.server.task.TaskStore} and still want to respect its + * Create a new JobStatus that was loaded from disk. We ignore the provided + * {@link android.app.job.JobInfo} time criteria because we can load a persisted periodic job + * from the {@link com.android.server.job.JobStore} and still want to respect its * wallclock runtime rather than resetting it on every boot. - * We consider a freshly loaded task to no longer be in back-off. + * We consider a freshly loaded job to no longer be in back-off. */ - public TaskStatus(Task task, int uId, long earliestRunTimeElapsedMillis, + public JobStatus(JobInfo job, int uId, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) { - this(task, uId, true, 0); + this(job, uId, true, 0); this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis; this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis; } - /** Create a new task to be rescheduled with the provided parameters. */ - public TaskStatus(TaskStatus rescheduling, long newEarliestRuntimeElapsedMillis, + /** Create a new job to be rescheduled with the provided parameters. */ + public JobStatus(JobStatus rescheduling, long newEarliestRuntimeElapsedMillis, long newLatestRuntimeElapsedMillis, int backoffAttempt) { - this(rescheduling.task, rescheduling.getUid(), rescheduling.isPersisted(), backoffAttempt); + this(rescheduling.job, rescheduling.getUid(), rescheduling.isPersisted(), backoffAttempt); earliestRunTimeElapsedMillis = newEarliestRuntimeElapsedMillis; latestRunTimeElapsedMillis = newLatestRuntimeElapsedMillis; } - public Task getTask() { - return task; + public JobInfo getJob() { + return job; } - public int getTaskId() { - return task.getId(); + public int getJobId() { + return job.getId(); } public int getNumFailures() { @@ -133,7 +133,7 @@ public class TaskStatus { } public ComponentName getServiceComponent() { - return task.getService(); + return job.getService(); } public int getUserId() { @@ -145,19 +145,19 @@ public class TaskStatus { } public PersistableBundle getExtras() { - return task.getExtras(); + return job.getExtras(); } public boolean hasConnectivityConstraint() { - return task.getNetworkCapabilities() == Task.NetworkType.ANY; + return job.getNetworkCapabilities() == JobInfo.NetworkType.ANY; } public boolean hasUnmeteredConstraint() { - return task.getNetworkCapabilities() == Task.NetworkType.UNMETERED; + return job.getNetworkCapabilities() == JobInfo.NetworkType.UNMETERED; } public boolean hasChargingConstraint() { - return task.isRequireCharging(); + return job.isRequireCharging(); } public boolean hasTimingDelayConstraint() { @@ -169,7 +169,7 @@ public class TaskStatus { } public boolean hasIdleConstraint() { - return task.isRequireDeviceIdle(); + return job.isRequireDeviceIdle(); } public long getEarliestRunTime() { @@ -184,7 +184,7 @@ public class TaskStatus { return persisted; } /** - * @return Whether or not this task is ready to run, based on its requirements. + * @return Whether or not this job is ready to run, based on its requirements. */ public synchronized boolean isReady() { return (!hasChargingConstraint() || chargingConstraintSatisfied.get()) @@ -196,36 +196,17 @@ public class TaskStatus { || (hasDeadlineConstraint() && deadlineConstraintSatisfied.get()); } - /*@Override - public int hashCode() { - int result = getServiceComponent().hashCode(); - result = 31 * result + task.getId(); - result = 31 * result + uId; - return result; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof TaskStatus)) return false; - - TaskStatus that = (TaskStatus) o; - return ((task.getId() == that.task.getId()) - && (uId == that.uId) - && (getServiceComponent().equals(that.getServiceComponent()))); - }*/ - - public boolean matches(int uid, int taskId) { - return this.task.getId() == taskId && this.uId == uid; + public boolean matches(int uid, int jobId) { + return this.job.getId() == jobId && this.uId == uid; } @Override public String toString() { return String.valueOf(hashCode()).substring(0, 3) + ".." - + ":[" + task.getService().getPackageName() + ",tId=" + task.getId() + + ":[" + job.getService().getPackageName() + ",jId=" + job.getId() + ",R=(" + earliestRunTimeElapsedMillis + "," + latestRunTimeElapsedMillis + ")" - + ",N=" + task.getNetworkCapabilities() + ",C=" + task.isRequireCharging() - + ",I=" + task.isRequireDeviceIdle() + ",F=" + numFailures + + ",N=" + job.getNetworkCapabilities() + ",C=" + job.isRequireCharging() + + ",I=" + job.isRequireDeviceIdle() + ",F=" + numFailures + (isReady() ? "(READY)" : "") + "]"; } diff --git a/services/core/java/com/android/server/task/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java index a7f52f5..81658bf 100644 --- a/services/core/java/com/android/server/task/controllers/StateController.java +++ b/services/core/java/com/android/server/job/controllers/StateController.java @@ -14,18 +14,18 @@ * limitations under the License */ -package com.android.server.task.controllers; +package com.android.server.job.controllers; import android.content.Context; -import com.android.server.task.StateChangedListener; -import com.android.server.task.TaskManagerService; +import com.android.server.job.JobSchedulerService; +import com.android.server.job.StateChangedListener; import java.io.PrintWriter; /** - * Incorporates shared controller logic between the various controllers of the TaskManager. - * These are solely responsible for tracking a list of tasks, and notifying the TM when these + * Incorporates shared controller logic between the various controllers of the JobManager. + * These are solely responsible for tracking a list of jobs, and notifying the JM when these * are ready to run, or whether they must be stopped. */ public abstract class StateController { @@ -39,16 +39,16 @@ public abstract class StateController { } /** - * Implement the logic here to decide whether a task should be tracked by this controller. - * This logic is put here so the TaskManger can be completely agnostic of Controller logic. + * Implement the logic here to decide whether a job should be tracked by this controller. + * This logic is put here so the JobManger can be completely agnostic of Controller logic. * Also called when updating a task, so implementing controllers have to be aware of * preexisting tasks. */ - public abstract void maybeStartTrackingTask(TaskStatus taskStatus); + public abstract void maybeStartTrackingJob(JobStatus jobStatus); /** * Remove task - this will happen if the task is cancelled, completed, etc. */ - public abstract void maybeStopTrackingTask(TaskStatus taskStatus); + public abstract void maybeStopTrackingJob(JobStatus jobStatus); public abstract void dumpControllerState(PrintWriter pw); diff --git a/services/core/java/com/android/server/task/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java index b75036c..e46226c 100644 --- a/services/core/java/com/android/server/task/controllers/TimeController.java +++ b/services/core/java/com/android/server/job/controllers/TimeController.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.server.task.controllers; +package com.android.server.job.controllers; import android.app.AlarmManager; import android.app.PendingIntent; @@ -25,8 +25,8 @@ import android.content.IntentFilter; import android.os.SystemClock; import android.util.Slog; -import com.android.server.task.StateChangedListener; -import com.android.server.task.TaskManagerService; +import com.android.server.job.JobSchedulerService; +import com.android.server.job.StateChangedListener; import java.io.PrintWriter; import java.util.Iterator; @@ -35,35 +35,35 @@ import java.util.List; import java.util.ListIterator; /** - * This class sets an alarm for the next expiring task, and determines whether a task's minimum + * This class sets an alarm for the next expiring job, and determines whether a job's minimum * delay has been satisfied. */ public class TimeController extends StateController { - private static final String TAG = "TaskManager.Time"; - private static final String ACTION_TASK_EXPIRED = - "android.content.taskmanager.TASK_DEADLINE_EXPIRED"; - private static final String ACTION_TASK_DELAY_EXPIRED = - "android.content.taskmanager.TASK_DELAY_EXPIRED"; + private static final String TAG = "JobScheduler.Time"; + private static final String ACTION_JOB_EXPIRED = + "android.content.jobscheduler.JOB_DEADLINE_EXPIRED"; + private static final String ACTION_JOB_DELAY_EXPIRED = + "android.content.jobscheduler.JOB_DELAY_EXPIRED"; - /** Set an alarm for the next task expiry. */ + /** Set an alarm for the next job expiry. */ private final PendingIntent mDeadlineExpiredAlarmIntent; - /** Set an alarm for the next task delay expiry. This*/ + /** Set an alarm for the next job delay expiry. This*/ private final PendingIntent mNextDelayExpiredAlarmIntent; /** Constant time determining how near in the future we'll set an alarm for. */ private static final long MIN_WAKEUP_INTERVAL_MILLIS = 15 * 1000; - private long mNextTaskExpiredElapsedMillis; + private long mNextJobExpiredElapsedMillis; private long mNextDelayExpiredElapsedMillis; private AlarmManager mAlarmService = null; - /** List of tracked tasks, sorted asc. by deadline */ - private final List<TaskStatus> mTrackedTasks = new LinkedList<TaskStatus>(); + /** List of tracked jobs, sorted asc. by deadline */ + private final List<JobStatus> mTrackedJobs = new LinkedList<JobStatus>(); /** Singleton. */ private static TimeController mSingleton; - public static synchronized TimeController get(TaskManagerService taskManager) { + public static synchronized TimeController get(JobSchedulerService jms) { if (mSingleton == null) { - mSingleton = new TimeController(taskManager, taskManager.getContext()); + mSingleton = new TimeController(jms, jms.getContext()); } return mSingleton; } @@ -72,66 +72,66 @@ public class TimeController extends StateController { super(stateChangedListener, context); mDeadlineExpiredAlarmIntent = PendingIntent.getBroadcast(mContext, 0 /* ignored */, - new Intent(ACTION_TASK_EXPIRED), 0); + new Intent(ACTION_JOB_EXPIRED), 0); mNextDelayExpiredAlarmIntent = PendingIntent.getBroadcast(mContext, 0 /* ignored */, - new Intent(ACTION_TASK_DELAY_EXPIRED), 0); - mNextTaskExpiredElapsedMillis = Long.MAX_VALUE; + new Intent(ACTION_JOB_DELAY_EXPIRED), 0); + mNextJobExpiredElapsedMillis = Long.MAX_VALUE; mNextDelayExpiredElapsedMillis = Long.MAX_VALUE; // Register BR for these intents. - IntentFilter intentFilter = new IntentFilter(ACTION_TASK_EXPIRED); - intentFilter.addAction(ACTION_TASK_DELAY_EXPIRED); + IntentFilter intentFilter = new IntentFilter(ACTION_JOB_EXPIRED); + intentFilter.addAction(ACTION_JOB_DELAY_EXPIRED); mContext.registerReceiver(mAlarmExpiredReceiver, intentFilter); } /** - * Check if the task has a timing constraint, and if so determine where to insert it in our + * Check if the job has a timing constraint, and if so determine where to insert it in our * list. */ @Override - public synchronized void maybeStartTrackingTask(TaskStatus task) { - if (task.hasTimingDelayConstraint() || task.hasDeadlineConstraint()) { - maybeStopTrackingTask(task); - ListIterator<TaskStatus> it = mTrackedTasks.listIterator(mTrackedTasks.size()); + public synchronized void maybeStartTrackingJob(JobStatus job) { + if (job.hasTimingDelayConstraint() || job.hasDeadlineConstraint()) { + maybeStopTrackingJob(job); + ListIterator<JobStatus> it = mTrackedJobs.listIterator(mTrackedJobs.size()); while (it.hasPrevious()) { - TaskStatus ts = it.previous(); - if (ts.getLatestRunTimeElapsed() < task.getLatestRunTimeElapsed()) { + JobStatus ts = it.previous(); + if (ts.getLatestRunTimeElapsed() < job.getLatestRunTimeElapsed()) { // Insert break; } } - it.add(task); + it.add(job); maybeUpdateAlarms( - task.hasTimingDelayConstraint() ? task.getEarliestRunTime() : Long.MAX_VALUE, - task.hasDeadlineConstraint() ? task.getLatestRunTimeElapsed() : Long.MAX_VALUE); + job.hasTimingDelayConstraint() ? job.getEarliestRunTime() : Long.MAX_VALUE, + job.hasDeadlineConstraint() ? job.getLatestRunTimeElapsed() : Long.MAX_VALUE); } } /** - * When we stop tracking a task, we only need to update our alarms if the task we're no longer + * When we stop tracking a job, we only need to update our alarms if the job we're no longer * tracking was the one our alarms were based off of. * Really an == comparison should be enough, but why play with fate? We'll do <=. */ @Override - public synchronized void maybeStopTrackingTask(TaskStatus taskStatus) { - if (mTrackedTasks.remove(taskStatus)) { + public synchronized void maybeStopTrackingJob(JobStatus job) { + if (mTrackedJobs.remove(job)) { checkExpiredDelaysAndResetAlarm(); checkExpiredDeadlinesAndResetAlarm(); } } /** - * Determines whether this controller can stop tracking the given task. - * The controller is no longer interested in a task once its time constraint is satisfied, and - * the task's deadline is fulfilled - unlike other controllers a time constraint can't toggle + * Determines whether this controller can stop tracking the given job. + * The controller is no longer interested in a job once its time constraint is satisfied, and + * the job's deadline is fulfilled - unlike other controllers a time constraint can't toggle * back and forth. */ - private boolean canStopTrackingTask(TaskStatus taskStatus) { - return (!taskStatus.hasTimingDelayConstraint() || - taskStatus.timeDelayConstraintSatisfied.get()) && - (!taskStatus.hasDeadlineConstraint() || - taskStatus.deadlineConstraintSatisfied.get()); + private boolean canStopTrackingJob(JobStatus job) { + return (!job.hasTimingDelayConstraint() || + job.timeDelayConstraintSatisfied.get()) && + (!job.hasDeadlineConstraint() || + job.deadlineConstraintSatisfied.get()); } private void ensureAlarmService() { @@ -141,27 +141,27 @@ public class TimeController extends StateController { } /** - * Checks list of tasks for ones that have an expired deadline, sending them to the TaskManager + * Checks list of jobs for ones that have an expired deadline, sending them to the JobScheduler * if so, removing them from this list, and updating the alarm for the next expiry time. */ private synchronized void checkExpiredDeadlinesAndResetAlarm() { long nextExpiryTime = Long.MAX_VALUE; final long nowElapsedMillis = SystemClock.elapsedRealtime(); - Iterator<TaskStatus> it = mTrackedTasks.iterator(); + Iterator<JobStatus> it = mTrackedJobs.iterator(); while (it.hasNext()) { - TaskStatus ts = it.next(); - if (!ts.hasDeadlineConstraint()) { + JobStatus job = it.next(); + if (!job.hasDeadlineConstraint()) { continue; } - final long taskDeadline = ts.getLatestRunTimeElapsed(); + final long jobDeadline = job.getLatestRunTimeElapsed(); - if (taskDeadline <= nowElapsedMillis) { - ts.deadlineConstraintSatisfied.set(true); - mStateChangedListener.onRunTaskNow(ts); + if (jobDeadline <= nowElapsedMillis) { + job.deadlineConstraintSatisfied.set(true); + mStateChangedListener.onRunJobNow(job); it.remove(); } else { // Sorted by expiry time, so take the next one and stop. - nextExpiryTime = taskDeadline; + nextExpiryTime = jobDeadline; break; } } @@ -169,31 +169,31 @@ public class TimeController extends StateController { } /** - * Handles alarm that notifies us that a task's delay has expired. Iterates through the list of - * tracked tasks and marks them as ready as appropriate. + * Handles alarm that notifies us that a job's delay has expired. Iterates through the list of + * tracked jobs and marks them as ready as appropriate. */ private synchronized void checkExpiredDelaysAndResetAlarm() { final long nowElapsedMillis = SystemClock.elapsedRealtime(); long nextDelayTime = Long.MAX_VALUE; boolean ready = false; - Iterator<TaskStatus> it = mTrackedTasks.iterator(); + Iterator<JobStatus> it = mTrackedJobs.iterator(); while (it.hasNext()) { - final TaskStatus ts = it.next(); - if (!ts.hasTimingDelayConstraint()) { + final JobStatus job = it.next(); + if (!job.hasTimingDelayConstraint()) { continue; } - final long taskDelayTime = ts.getEarliestRunTime(); - if (taskDelayTime <= nowElapsedMillis) { - ts.timeDelayConstraintSatisfied.set(true); - if (canStopTrackingTask(ts)) { + final long jobDelayTime = job.getEarliestRunTime(); + if (jobDelayTime <= nowElapsedMillis) { + job.timeDelayConstraintSatisfied.set(true); + if (canStopTrackingJob(job)) { it.remove(); } - if (ts.isReady()) { + if (job.isReady()) { ready = true; } } else { // Keep going through list to get next delay time. - if (nextDelayTime > taskDelayTime) { - nextDelayTime = taskDelayTime; + if (nextDelayTime > jobDelayTime) { + nextDelayTime = jobDelayTime; } } } @@ -207,13 +207,13 @@ public class TimeController extends StateController { if (delayExpiredElapsed < mNextDelayExpiredElapsedMillis) { setDelayExpiredAlarm(delayExpiredElapsed); } - if (deadlineExpiredElapsed < mNextTaskExpiredElapsedMillis) { + if (deadlineExpiredElapsed < mNextJobExpiredElapsedMillis) { setDeadlineExpiredAlarm(deadlineExpiredElapsed); } } /** - * Set an alarm with the {@link android.app.AlarmManager} for the next time at which a task's + * Set an alarm with the {@link android.app.AlarmManager} for the next time at which a job's * delay will expire. * This alarm <b>will not</b> wake up the phone. */ @@ -228,7 +228,7 @@ public class TimeController extends StateController { } /** - * Set an alarm with the {@link android.app.AlarmManager} for the next time at which a task's + * Set an alarm with the {@link android.app.AlarmManager} for the next time at which a job's * deadline will expire. * This alarm <b>will</b> wake up the phone. */ @@ -238,8 +238,8 @@ public class TimeController extends StateController { if (alarmTimeElapsedMillis < earliestWakeupTimeElapsed) { alarmTimeElapsedMillis = earliestWakeupTimeElapsed; } - mNextTaskExpiredElapsedMillis = alarmTimeElapsedMillis; - updateAlarmWithPendingIntent(mDeadlineExpiredAlarmIntent, mNextTaskExpiredElapsedMillis); + mNextJobExpiredElapsedMillis = alarmTimeElapsedMillis; + updateAlarmWithPendingIntent(mDeadlineExpiredAlarmIntent, mNextJobExpiredElapsedMillis); } private void updateAlarmWithPendingIntent(PendingIntent pi, long alarmTimeElapsed) { @@ -260,11 +260,11 @@ public class TimeController extends StateController { if (DEBUG) { Slog.d(TAG, "Just received alarm: " + intent.getAction()); } - // An task has just expired, so we run through the list of tasks that we have and + // A job has just expired, so we run through the list of jobs that we have and // notify our StateChangedListener. - if (ACTION_TASK_EXPIRED.equals(intent.getAction())) { + if (ACTION_JOB_EXPIRED.equals(intent.getAction())) { checkExpiredDeadlinesAndResetAlarm(); - } else if (ACTION_TASK_DELAY_EXPIRED.equals(intent.getAction())) { + } else if (ACTION_JOB_DELAY_EXPIRED.equals(intent.getAction())) { checkExpiredDelaysAndResetAlarm(); } } @@ -276,10 +276,10 @@ public class TimeController extends StateController { pw.println("Alarms (" + SystemClock.elapsedRealtime() + ")"); pw.println( "Next delay alarm in " + (mNextDelayExpiredElapsedMillis - nowElapsed)/1000 + "s"); - pw.println("Next deadline alarm in " + (mNextTaskExpiredElapsedMillis - nowElapsed)/1000 + pw.println("Next deadline alarm in " + (mNextJobExpiredElapsedMillis - nowElapsed)/1000 + "s"); pw.println("Tracking:"); - for (TaskStatus ts : mTrackedTasks) { + for (JobStatus ts : mTrackedJobs) { pw.println(String.valueOf(ts.hashCode()).substring(0, 3) + ".." + ": (" + (ts.hasTimingDelayConstraint() ? ts.getEarliestRunTime() : "N/A") + ", " + (ts.hasDeadlineConstraint() ?ts.getLatestRunTimeElapsed() : "N/A") diff --git a/services/core/java/com/android/server/task/TaskManagerService.java b/services/core/java/com/android/server/task/TaskManagerService.java deleted file mode 100644 index 0c55a1d..0000000 --- a/services/core/java/com/android/server/task/TaskManagerService.java +++ /dev/null @@ -1,764 +0,0 @@ -/* - * 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 com.android.server.task; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import android.app.task.ITaskManager; -import android.app.task.Task; -import android.app.task.TaskManager; -import android.content.BroadcastReceiver; -import android.app.task.TaskService; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ServiceInfo; -import android.os.Binder; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.UserHandle; -import android.util.Slog; -import android.util.SparseArray; - -import com.android.server.task.controllers.BatteryController; -import com.android.server.task.controllers.ConnectivityController; -import com.android.server.task.controllers.IdleController; -import com.android.server.task.controllers.StateController; -import com.android.server.task.controllers.TaskStatus; -import com.android.server.task.controllers.TimeController; - -import java.util.LinkedList; - -/** - * Responsible for taking tasks representing work to be performed by a client app, and determining - * based on the criteria specified when that task should be run against the client application's - * endpoint. - * Implements logic for scheduling, and rescheduling tasks. The TaskManagerService knows nothing - * about constraints, or the state of active tasks. It receives callbacks from the various - * controllers and completed tasks and operates accordingly. - * - * Note on locking: Any operations that manipulate {@link #mTasks} need to lock on that object. - * Any function with the suffix 'Locked' also needs to lock on {@link #mTasks}. - * @hide - */ -public class TaskManagerService extends com.android.server.SystemService - implements StateChangedListener, TaskCompletedListener, TaskMapReadFinishedListener { - // TODO: Switch this off for final version. - static final boolean DEBUG = true; - /** The number of concurrent tasks we run at one time. */ - private static final int MAX_TASK_CONTEXTS_COUNT = 3; - static final String TAG = "TaskManager"; - /** Master list of tasks. */ - private final TaskStore mTasks; - - static final int MSG_TASK_EXPIRED = 0; - static final int MSG_CHECK_TASKS = 1; - - // Policy constants - /** - * Minimum # of idle tasks that must be ready in order to force the TM to schedule things - * early. - */ - private static final int MIN_IDLE_COUNT = 1; - /** - * Minimum # of connectivity tasks that must be ready in order to force the TM to schedule - * things early. - */ - private static final int MIN_CONNECTIVITY_COUNT = 2; - /** - * Minimum # of tasks (with no particular constraints) for which the TM will be happy running - * some work early. - */ - private static final int MIN_READY_TASKS_COUNT = 4; - - /** - * Track Services that have currently active or pending tasks. The index is provided by - * {@link TaskStatus#getServiceToken()} - */ - private final List<TaskServiceContext> mActiveServices = new LinkedList<TaskServiceContext>(); - /** List of controllers that will notify this service of updates to tasks. */ - private List<StateController> mControllers; - /** - * Queue of pending tasks. The TaskServiceContext class will receive tasks from this list - * when ready to execute them. - */ - private final LinkedList<TaskStatus> mPendingTasks = new LinkedList<TaskStatus>(); - - private final TaskHandler mHandler; - private final TaskManagerStub mTaskManagerStub; - /** - * Cleans 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. - */ - private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - Slog.d(TAG, "Receieved: " + intent.getAction()); - if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) { - int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1); - if (DEBUG) { - Slog.d(TAG, "Removing jobs for uid: " + uidRemoved); - } - cancelTasksForUid(uidRemoved); - } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) { - final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); - if (DEBUG) { - Slog.d(TAG, "Removing jobs for user: " + userId); - } - cancelTasksForUser(userId); - } - } - }; - - /** - * Entry point from client to schedule the provided task. - * This cancels the task if it's already been scheduled, and replaces it with the one provided. - * @param task Task object containing execution parameters - * @param uId The package identifier of the application this task is for. - * @param canPersistTask Whether or not the client has the appropriate permissions for - * persisting this task. - * @return Result of this operation. See <code>TaskManager#RESULT_*</code> return codes. - */ - public int schedule(Task task, int uId, boolean canPersistTask) { - TaskStatus taskStatus = new TaskStatus(task, uId, canPersistTask); - cancelTask(uId, task.getId()); - startTrackingTask(taskStatus); - return TaskManager.RESULT_SUCCESS; - } - - public List<Task> getPendingTasks(int uid) { - ArrayList<Task> outList = new ArrayList<Task>(); - synchronized (mTasks) { - for (TaskStatus ts : mTasks.getTasks()) { - if (ts.getUid() == uid) { - outList.add(ts.getTask()); - } - } - } - return outList; - } - - private void cancelTasksForUser(int userHandle) { - synchronized (mTasks) { - List<TaskStatus> tasksForUser = mTasks.getTasksByUser(userHandle); - for (TaskStatus toRemove : tasksForUser) { - if (DEBUG) { - Slog.d(TAG, "Cancelling: " + toRemove); - } - cancelTaskLocked(toRemove); - } - } - } - - /** - * Entry point from client to cancel all tasks originating from their uid. - * This will remove the task from the master list, and cancel the task if it was staged for - * execution or being executed. - * @param uid To check against for removal of a task. - */ - public void cancelTasksForUid(int uid) { - // Remove from master list. - synchronized (mTasks) { - List<TaskStatus> tasksForUid = mTasks.getTasksByUid(uid); - for (TaskStatus toRemove : tasksForUid) { - if (DEBUG) { - Slog.d(TAG, "Cancelling: " + toRemove); - } - cancelTaskLocked(toRemove); - } - } - } - - /** - * Entry point from client to cancel the task corresponding to the taskId provided. - * This will remove the task from the master list, and cancel the task if it was staged for - * execution or being executed. - * @param uid Uid of the calling client. - * @param taskId Id of the task, provided at schedule-time. - */ - public void cancelTask(int uid, int taskId) { - TaskStatus toCancel; - synchronized (mTasks) { - toCancel = mTasks.getTaskByUidAndTaskId(uid, taskId); - if (toCancel != null) { - cancelTaskLocked(toCancel); - } - } - } - - private void cancelTaskLocked(TaskStatus cancelled) { - // Remove from store. - stopTrackingTask(cancelled); - // Remove from pending queue. - mPendingTasks.remove(cancelled); - // Cancel if running. - stopTaskOnServiceContextLocked(cancelled); - } - - /** - * Initializes the system service. - * <p> - * Subclasses must define a single argument constructor that accepts the context - * and passes it to super. - * </p> - * - * @param context The system server context. - */ - public TaskManagerService(Context context) { - super(context); - // Create the controllers. - mControllers = new LinkedList<StateController>(); - mControllers.add(ConnectivityController.get(this)); - mControllers.add(TimeController.get(this)); - mControllers.add(IdleController.get(this)); - mControllers.add(BatteryController.get(this)); - - mHandler = new TaskHandler(context.getMainLooper()); - mTaskManagerStub = new TaskManagerStub(); - // Create the "runners". - for (int i = 0; i < MAX_TASK_CONTEXTS_COUNT; i++) { - mActiveServices.add( - new TaskServiceContext(this, context.getMainLooper())); - } - mTasks = TaskStore.initAndGet(this); - } - - @Override - public void onStart() { - publishBinderService(Context.TASK_SERVICE, mTaskManagerStub); - } - - @Override - 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); - } - } - - /** - * Called when we have a task status object that we need to insert in our - * {@link com.android.server.task.TaskStore}, and make sure all the relevant controllers know - * about. - */ - private void startTrackingTask(TaskStatus taskStatus) { - boolean update; - synchronized (mTasks) { - update = mTasks.add(taskStatus); - } - for (StateController controller : mControllers) { - if (update) { - controller.maybeStopTrackingTask(taskStatus); - } - controller.maybeStartTrackingTask(taskStatus); - } - } - - /** - * Called when we want to remove a TaskStatus object that we've finished executing. Returns the - * object removed. - */ - private boolean stopTrackingTask(TaskStatus taskStatus) { - boolean removed; - synchronized (mTasks) { - // Remove from store as well as controllers. - removed = mTasks.remove(taskStatus); - } - if (removed) { - for (StateController controller : mControllers) { - controller.maybeStopTrackingTask(taskStatus); - } - } - return removed; - } - - private boolean stopTaskOnServiceContextLocked(TaskStatus ts) { - for (TaskServiceContext tsc : mActiveServices) { - final TaskStatus executing = tsc.getRunningTask(); - if (executing != null && executing.matches(ts.getUid(), ts.getTaskId())) { - tsc.cancelExecutingTask(); - return true; - } - } - return false; - } - - /** - * @param ts TaskStatus we are querying against. - * @return Whether or not the task represented by the status object is currently being run or - * is pending. - */ - private boolean isCurrentlyActiveLocked(TaskStatus ts) { - for (TaskServiceContext serviceContext : mActiveServices) { - final TaskStatus running = serviceContext.getRunningTask(); - if (running != null && running.matches(ts.getUid(), ts.getTaskId())) { - return true; - } - } - return false; - } - - /** - * A task is rescheduled with exponential back-off if the client requests this from their - * execution logic. - * A caveat is for idle-mode tasks, for which the idle-mode constraint will usurp the - * timeliness of the reschedule. For an idle-mode task, no deadline is given. - * @param failureToReschedule Provided task status that we will reschedule. - * @return A newly instantiated TaskStatus with the same constraints as the last task except - * with adjusted timing constraints. - */ - private TaskStatus getRescheduleTaskForFailure(TaskStatus failureToReschedule) { - final long elapsedNowMillis = SystemClock.elapsedRealtime(); - final Task task = failureToReschedule.getTask(); - - final long initialBackoffMillis = task.getInitialBackoffMillis(); - final int backoffAttempt = failureToReschedule.getNumFailures() + 1; - long newEarliestRuntimeElapsed = elapsedNowMillis; - - switch (task.getBackoffPolicy()) { - case Task.BackoffPolicy.LINEAR: - newEarliestRuntimeElapsed += initialBackoffMillis * backoffAttempt; - break; - default: - if (DEBUG) { - Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential."); - } - case Task.BackoffPolicy.EXPONENTIAL: - newEarliestRuntimeElapsed += - Math.pow(initialBackoffMillis * 0.001, backoffAttempt) * 1000; - break; - } - newEarliestRuntimeElapsed = - Math.min(newEarliestRuntimeElapsed, Task.MAX_BACKOFF_DELAY_MILLIS); - return new TaskStatus(failureToReschedule, newEarliestRuntimeElapsed, - TaskStatus.NO_LATEST_RUNTIME, backoffAttempt); - } - - /** - * Called after a periodic has executed so we can to re-add it. We take the last execution time - * of the task to be the time of completion (i.e. the time at which this function is called). - * This could be inaccurate b/c the task can run for as long as - * {@link com.android.server.task.TaskServiceContext#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. - * @return A new task representing the execution criteria for this instantiation of the - * recurring task. - */ - private TaskStatus getRescheduleTaskForPeriodic(TaskStatus periodicToReschedule) { - 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.getTask().getIntervalMillis(); - long newLatestRuntimeElapsed = newEarliestRunTimeElapsed + period; - - if (DEBUG) { - Slog.v(TAG, "Rescheduling executed periodic. New execution window [" + - newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s"); - } - return new TaskStatus(periodicToReschedule, newEarliestRunTimeElapsed, - newLatestRuntimeElapsed, 0 /* backoffAttempt */); - } - - // TaskCompletedListener implementations. - - /** - * A task just finished executing. We fetch the - * {@link com.android.server.task.controllers.TaskStatus} from the store and depending on - * whether we want to reschedule we readd it to the controllers. - * @param taskStatus Completed task. - * @param needsReschedule Whether the implementing class should reschedule this task. - */ - @Override - public void onTaskCompleted(TaskStatus taskStatus, boolean needsReschedule) { - if (DEBUG) { - Slog.d(TAG, "Completed " + taskStatus + ", reschedule=" + needsReschedule); - } - if (!stopTrackingTask(taskStatus)) { - if (DEBUG) { - Slog.e(TAG, "Error removing task: could not find task to remove. Was task " + - "removed while executing?"); - } - return; - } - if (needsReschedule) { - TaskStatus rescheduled = getRescheduleTaskForFailure(taskStatus); - startTrackingTask(rescheduled); - } else if (taskStatus.getTask().isPeriodic()) { - TaskStatus rescheduledPeriodic = getRescheduleTaskForPeriodic(taskStatus); - startTrackingTask(rescheduledPeriodic); - } - mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget(); - } - - // StateChangedListener implementations. - - /** - * Off-board work to our handler thread as quickly as possible, b/c this call is probably being - * made on the main thread. - * For now this takes the task and if it's ready to run it will run it. In future we might not - * provide the task, so that the StateChangedListener has to run through its list of tasks to - * see which are ready. This will further decouple the controllers from the execution logic. - */ - @Override - public void onControllerStateChanged() { - // Post a message to to run through the list of tasks and start/stop any that are eligible. - mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget(); - } - - @Override - public void onRunTaskNow(TaskStatus taskStatus) { - mHandler.obtainMessage(MSG_TASK_EXPIRED, taskStatus).sendToTarget(); - } - - /** - * Disk I/O is finished, take the list of tasks we read from disk and add them to our - * {@link TaskStore}. - * This is run on the {@link com.android.server.IoThread} instance, which is a separate thread, - * and is called once at boot. - */ - @Override - public void onTaskMapReadFinished(List<TaskStatus> tasks) { - synchronized (mTasks) { - for (TaskStatus ts : tasks) { - if (mTasks.containsTaskIdForUid(ts.getTaskId(), ts.getUid())) { - // An app with BOOT_COMPLETED *might* have decided to reschedule their task, in - // the same amount of time it took us to read it from disk. If this is the case - // we leave it be. - continue; - } - startTrackingTask(ts); - } - } - } - - private class TaskHandler extends Handler { - - public TaskHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message message) { - switch (message.what) { - case MSG_TASK_EXPIRED: - synchronized (mTasks) { - TaskStatus runNow = (TaskStatus) message.obj; - if (!mPendingTasks.contains(runNow)) { - mPendingTasks.add(runNow); - } - } - queueReadyTasksForExecutionH(); - break; - case MSG_CHECK_TASKS: - // Check the list of tasks and run some of them if we feel inclined. - maybeQueueReadyTasksForExecutionH(); - break; - } - maybeRunPendingTasksH(); - // Don't remove TASK_EXPIRED in case one came along while processing the queue. - removeMessages(MSG_CHECK_TASKS); - } - - /** - * Run through list of tasks and execute all possible - at least one is expired so we do - * as many as we can. - */ - private void queueReadyTasksForExecutionH() { - synchronized (mTasks) { - for (TaskStatus ts : mTasks.getTasks()) { - if (isReadyToBeExecutedLocked(ts)) { - mPendingTasks.add(ts); - } else if (isReadyToBeCancelledLocked(ts)) { - stopTaskOnServiceContextLocked(ts); - } - } - } - } - - /** - * The state of at least one task has changed. Here is where we could enforce various - * policies on when we want to execute tasks. - * Right now the policy is such: - * If >1 of the ready tasks is idle mode we send all of them off - * if more than 2 network connectivity tasks are ready we send them all off. - * If more than 4 tasks total are ready we send them all off. - * TODO: It would be nice to consolidate these sort of high-level policies somewhere. - */ - private void maybeQueueReadyTasksForExecutionH() { - synchronized (mTasks) { - int idleCount = 0; - int backoffCount = 0; - int connectivityCount = 0; - List<TaskStatus> runnableTasks = new ArrayList<TaskStatus>(); - for (TaskStatus ts : mTasks.getTasks()) { - if (isReadyToBeExecutedLocked(ts)) { - if (ts.getNumFailures() > 0) { - backoffCount++; - } - if (ts.hasIdleConstraint()) { - idleCount++; - } - if (ts.hasConnectivityConstraint() || ts.hasUnmeteredConstraint()) { - connectivityCount++; - } - runnableTasks.add(ts); - } else if (isReadyToBeCancelledLocked(ts)) { - stopTaskOnServiceContextLocked(ts); - } - } - if (backoffCount > 0 || idleCount >= MIN_IDLE_COUNT || - connectivityCount >= MIN_CONNECTIVITY_COUNT || - runnableTasks.size() >= MIN_READY_TASKS_COUNT) { - for (TaskStatus ts : runnableTasks) { - mPendingTasks.add(ts); - } - } - } - } - - /** - * Criteria for moving a job into the pending queue: - * - It's ready. - * - It's not pending. - * - It's not already running on a TSC. - */ - private boolean isReadyToBeExecutedLocked(TaskStatus ts) { - return ts.isReady() && !mPendingTasks.contains(ts) && !isCurrentlyActiveLocked(ts); - } - - /** - * Criteria for cancelling an active job: - * - It's not ready - * - It's running on a TSC. - */ - private boolean isReadyToBeCancelledLocked(TaskStatus ts) { - return !ts.isReady() && isCurrentlyActiveLocked(ts); - } - - /** - * Reconcile jobs in the pending queue against available execution contexts. - * A controller can force a task into the pending queue even if it's already running, but - * here is where we decide whether to actually execute it. - */ - private void maybeRunPendingTasksH() { - synchronized (mTasks) { - Iterator<TaskStatus> it = mPendingTasks.iterator(); - while (it.hasNext()) { - TaskStatus nextPending = it.next(); - TaskServiceContext availableContext = null; - for (TaskServiceContext tsc : mActiveServices) { - final TaskStatus running = tsc.getRunningTask(); - if (running != null && running.matches(nextPending.getUid(), - nextPending.getTaskId())) { - // Already running this tId for this uId, skip. - availableContext = null; - break; - } - if (tsc.isAvailable()) { - availableContext = tsc; - } - } - if (availableContext != null) { - if (!availableContext.executeRunnableTask(nextPending)) { - if (DEBUG) { - Slog.d(TAG, "Error executing " + nextPending); - } - mTasks.remove(nextPending); - } - it.remove(); - } - } - } - } - } - - /** - * Binder stub trampoline implementation - */ - final class TaskManagerStub extends ITaskManager.Stub { - /** Cache determination of whether a given app can persist tasks - * key is uid of the calling app; value is undetermined/true/false - */ - private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>(); - - // Enforce that only the app itself (or shared uid participant) can schedule a - // task that runs one of the app's services, as well as verifying that the - // named service properly requires the BIND_TASK_SERVICE permission - private void enforceValidJobRequest(int uid, Task job) { - final PackageManager pm = getContext().getPackageManager(); - final ComponentName service = job.getService(); - try { - ServiceInfo si = pm.getServiceInfo(service, 0); - if (si.applicationInfo.uid != uid) { - throw new IllegalArgumentException("uid " + uid + - " cannot schedule job in " + service.getPackageName()); - } - if (!TaskService.PERMISSION_BIND.equals(si.permission)) { - throw new IllegalArgumentException("Scheduled service " + service - + " does not require android.permission.BIND_TASK_SERVICE permission"); - } - } catch (NameNotFoundException e) { - throw new IllegalArgumentException("No such service: " + service); - } - } - - private boolean canPersistJobs(int pid, int uid) { - // If we get this far we're good to go; all we need to do now is check - // whether the app is allowed to persist its scheduled work. - final boolean canPersist; - synchronized (mPersistCache) { - Boolean cached = mPersistCache.get(uid); - if (cached != null) { - canPersist = cached.booleanValue(); - } else { - // Persisting tasks is tantamount to running at boot, so we permit - // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED - // permission - int result = getContext().checkPermission( - android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid); - canPersist = (result == PackageManager.PERMISSION_GRANTED); - mPersistCache.put(uid, canPersist); - } - } - return canPersist; - } - - // ITaskManager implementation - @Override - public int schedule(Task task) throws RemoteException { - if (DEBUG) { - Slog.d(TAG, "Scheduling task: " + task); - } - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - - enforceValidJobRequest(uid, task); - final boolean canPersist = canPersistJobs(pid, uid); - - long ident = Binder.clearCallingIdentity(); - try { - return TaskManagerService.this.schedule(task, uid, canPersist); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - @Override - public List<Task> getAllPendingTasks() throws RemoteException { - final int uid = Binder.getCallingUid(); - - long ident = Binder.clearCallingIdentity(); - try { - return TaskManagerService.this.getPendingTasks(uid); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - @Override - public void cancelAll() throws RemoteException { - final int uid = Binder.getCallingUid(); - - long ident = Binder.clearCallingIdentity(); - try { - TaskManagerService.this.cancelTasksForUid(uid); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - @Override - public void cancel(int taskId) throws RemoteException { - final int uid = Binder.getCallingUid(); - - long ident = Binder.clearCallingIdentity(); - try { - TaskManagerService.this.cancelTask(uid, taskId); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - /** - * "dumpsys" infrastructure - */ - @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); - - long identityToken = Binder.clearCallingIdentity(); - try { - TaskManagerService.this.dumpInternal(pw); - } finally { - Binder.restoreCallingIdentity(identityToken); - } - } - }; - - void dumpInternal(PrintWriter pw) { - synchronized (mTasks) { - pw.println("Registered tasks:"); - if (mTasks.size() > 0) { - for (TaskStatus ts : mTasks.getTasks()) { - ts.dump(pw, " "); - } - } else { - pw.println(); - pw.println("No tasks scheduled."); - } - for (StateController controller : mControllers) { - pw.println(); - controller.dumpControllerState(pw); - } - pw.println(); - pw.println("Pending"); - for (TaskStatus taskStatus : mPendingTasks) { - pw.println(taskStatus.hashCode()); - } - pw.println(); - pw.println("Active jobs:"); - for (TaskServiceContext tsc : mActiveServices) { - if (tsc.isAvailable()) { - continue; - } else { - pw.println(tsc.getRunningTask().hashCode() + " for: " + - (SystemClock.elapsedRealtime() - - tsc.getExecutionStartTimeElapsed())/1000 + "s " + - "timeout: " + tsc.getTimeoutElapsed()); - } - } - } - pw.println(); - } -} diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 2aa1220..db8c7a6 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -65,6 +65,7 @@ import com.android.server.display.DisplayManagerService; import com.android.server.dreams.DreamManagerService; import com.android.server.hdmi.HdmiControlService; import com.android.server.input.InputManagerService; +import com.android.server.job.JobSchedulerService; import com.android.server.lights.LightsManager; import com.android.server.lights.LightsService; import com.android.server.media.MediaRouterService; @@ -83,7 +84,6 @@ import com.android.server.power.ShutdownThread; import com.android.server.search.SearchManagerService; import com.android.server.statusbar.StatusBarManagerService; import com.android.server.storage.DeviceStorageMonitorService; -import com.android.server.task.TaskManagerService; import com.android.server.trust.TrustManagerService; import com.android.server.tv.TvInputManagerService; import com.android.server.twilight.TwilightService; @@ -131,8 +131,8 @@ public final class SystemServer { "com.android.server.wifi.p2p.WifiP2pService"; private static final String ETHERNET_SERVICE_CLASS = "com.android.server.ethernet.EthernetService"; - private static final String TASK_SERVICE_CLASS = - "com.android.server.task.TaskManagerService"; + private static final String JOB_SCHEDULER_SERVICE_CLASS = + "com.android.server.job.JobSchedulerService"; private final int mFactoryTestMode; private Timer mProfilerSnapshotTimer; @@ -832,7 +832,7 @@ public final class SystemServer { mSystemServiceManager.startService(UiModeManagerService.class); - mSystemServiceManager.startService(TaskManagerService.class); + mSystemServiceManager.startService(JobSchedulerService.class); if (!disableNonCoreServices) { try { diff --git a/services/tests/servicestests/src/com/android/server/task/TaskStoreTest.java b/services/tests/servicestests/src/com/android/server/task/TaskStoreTest.java index e7f9ca0..23ea174 100644 --- a/services/tests/servicestests/src/com/android/server/task/TaskStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/task/TaskStoreTest.java @@ -3,18 +3,20 @@ package com.android.server.task; import android.content.ComponentName; import android.content.Context; -import android.app.task.Task; -import android.app.task.Task.Builder; +import android.app.job.JobInfo; +import android.app.job.JobInfo.Builder; import android.os.PersistableBundle; import android.test.AndroidTestCase; import android.test.RenamingDelegatingContext; import android.util.Log; -import com.android.server.task.controllers.TaskStatus; +import com.android.server.job.JobMapReadFinishedListener; +import com.android.server.job.JobStore; +import com.android.server.job.controllers.JobStatus; import java.util.List; -import static com.android.server.task.TaskStore.initAndGet; +import static com.android.server.job.JobStore.initAndGet; /** * Test reading and writing correctly from file. */ @@ -26,12 +28,12 @@ public class TaskStoreTest extends AndroidTestCase { private ComponentName mComponent; private static final long IO_WAIT = 600L; - TaskStore mTaskStoreUnderTest; + JobStore mTaskStoreUnderTest; Context mTestContext; - TaskMapReadFinishedListener mTaskMapReadFinishedListenerStub = - new TaskMapReadFinishedListener() { + JobMapReadFinishedListener mTaskMapReadFinishedListenerStub = + new JobMapReadFinishedListener() { @Override - public void onTaskMapReadFinished(List<TaskStatus> tasks) { + public void onJobMapReadFinished(List<JobStatus> tasks) { // do nothing. } }; @@ -40,7 +42,7 @@ public class TaskStoreTest extends AndroidTestCase { public void setUp() throws Exception { mTestContext = new RenamingDelegatingContext(getContext(), TEST_PREFIX); Log.d(TAG, "Saving tasks to '" + mTestContext.getFilesDir() + "'"); - mTaskStoreUnderTest = TaskStore.initAndGetForTesting(mTestContext, + mTaskStoreUnderTest = JobStore.initAndGetForTesting(mTestContext, mTestContext.getFilesDir(), mTaskMapReadFinishedListenerStub); mComponent = new ComponentName(getContext().getPackageName(), StubClass.class.getName()); } @@ -56,23 +58,23 @@ public class TaskStoreTest extends AndroidTestCase { long runFromMillis = 2000L; // 2s long initialBackoff = 10000L; // 10s - final Task task = new Builder(taskId, mComponent) + final JobInfo task = new Builder(taskId, mComponent) .setRequiresCharging(true) - .setRequiredNetworkCapabilities(Task.NetworkType.ANY) - .setBackoffCriteria(initialBackoff, Task.BackoffPolicy.EXPONENTIAL) + .setRequiredNetworkCapabilities(JobInfo.NetworkType.ANY) + .setBackoffCriteria(initialBackoff, JobInfo.BackoffPolicy.EXPONENTIAL) .setOverrideDeadline(runByMillis) .setMinimumLatency(runFromMillis) .build(); - final TaskStatus ts = new TaskStatus(task, SOME_UID, true /* persisted */); + final JobStatus ts = new JobStatus(task, SOME_UID, true /* persisted */); mTaskStoreUnderTest.add(ts); Thread.sleep(IO_WAIT); // Manually load tasks from xml file. - mTaskStoreUnderTest.readTaskMapFromDisk(new TaskMapReadFinishedListener() { + mTaskStoreUnderTest.readJobMapFromDisk(new JobMapReadFinishedListener() { @Override - public void onTaskMapReadFinished(List<TaskStatus> tasks) { + public void onJobMapReadFinished(List<JobStatus> tasks) { assertEquals("Didn't get expected number of persisted tasks.", 1, tasks.size()); - TaskStatus loadedTaskStatus = tasks.get(0); - assertTasksEqual(task, loadedTaskStatus.getTask()); + JobStatus loadedTaskStatus = tasks.get(0); + assertTasksEqual(task, loadedTaskStatus.getJob()); assertEquals("Different uids.", SOME_UID, tasks.get(0).getUid()); compareTimestampsSubjectToIoLatency("Early run-times not the same after read.", ts.getEarliestRunTime(), loadedTaskStatus.getEarliestRunTime()); @@ -84,30 +86,30 @@ public class TaskStoreTest extends AndroidTestCase { } public void testWritingTwoFilesToDisk() throws Exception { - final Task task1 = new Builder(8, mComponent) + final JobInfo task1 = new Builder(8, mComponent) .setRequiresDeviceIdle(true) .setPeriodic(10000L) .setRequiresCharging(true) .build(); - final Task task2 = new Builder(12, mComponent) + final JobInfo task2 = new Builder(12, mComponent) .setMinimumLatency(5000L) - .setBackoffCriteria(15000L, Task.BackoffPolicy.LINEAR) + .setBackoffCriteria(15000L, JobInfo.BackoffPolicy.LINEAR) .setOverrideDeadline(30000L) - .setRequiredNetworkCapabilities(Task.NetworkType.UNMETERED) + .setRequiredNetworkCapabilities(JobInfo.NetworkType.UNMETERED) .build(); - final TaskStatus taskStatus1 = new TaskStatus(task1, SOME_UID, true /* persisted */); - final TaskStatus taskStatus2 = new TaskStatus(task2, SOME_UID, true /* persisted */); + final JobStatus taskStatus1 = new JobStatus(task1, SOME_UID, true /* persisted */); + final JobStatus taskStatus2 = new JobStatus(task2, SOME_UID, true /* persisted */); mTaskStoreUnderTest.add(taskStatus1); mTaskStoreUnderTest.add(taskStatus2); Thread.sleep(IO_WAIT); - mTaskStoreUnderTest.readTaskMapFromDisk(new TaskMapReadFinishedListener() { + mTaskStoreUnderTest.readJobMapFromDisk(new JobMapReadFinishedListener() { @Override - public void onTaskMapReadFinished(List<TaskStatus> tasks) { + public void onJobMapReadFinished(List<JobStatus> tasks) { assertEquals("Incorrect # of persisted tasks.", 2, tasks.size()); - TaskStatus loaded1 = tasks.get(0); - TaskStatus loaded2 = tasks.get(1); - assertTasksEqual(task1, loaded1.getTask()); - assertTasksEqual(task2, loaded2.getTask()); + JobStatus loaded1 = tasks.get(0); + JobStatus loaded2 = tasks.get(1); + assertTasksEqual(task1, loaded1.getJob()); + assertTasksEqual(task2, loaded2.getJob()); // Check that the loaded task has the correct runtimes. compareTimestampsSubjectToIoLatency("Early run-times not the same after read.", @@ -124,7 +126,7 @@ public class TaskStoreTest extends AndroidTestCase { } public void testWritingTaskWithExtras() throws Exception { - Task.Builder b = new Builder(8, mComponent) + JobInfo.Builder b = new Builder(8, mComponent) .setRequiresDeviceIdle(true) .setPeriodic(10000L) .setRequiresCharging(true); @@ -134,17 +136,17 @@ public class TaskStoreTest extends AndroidTestCase { extras.putString("hi", "there"); extras.putInt("into", 3); b.setExtras(extras); - final Task task = b.build(); - TaskStatus taskStatus = new TaskStatus(task, SOME_UID, true /* persisted */); + final JobInfo task = b.build(); + JobStatus taskStatus = new JobStatus(task, SOME_UID, true /* persisted */); mTaskStoreUnderTest.add(taskStatus); Thread.sleep(IO_WAIT); - mTaskStoreUnderTest.readTaskMapFromDisk(new TaskMapReadFinishedListener() { + mTaskStoreUnderTest.readJobMapFromDisk(new JobMapReadFinishedListener() { @Override - public void onTaskMapReadFinished(List<TaskStatus> tasks) { + public void onJobMapReadFinished(List<JobStatus> tasks) { assertEquals("Incorrect # of persisted tasks.", 1, tasks.size()); - TaskStatus loaded = tasks.get(0); - assertTasksEqual(task, loaded.getTask()); + JobStatus loaded = tasks.get(0); + assertTasksEqual(task, loaded.getJob()); } }); @@ -153,7 +155,7 @@ public class TaskStoreTest extends AndroidTestCase { /** * Helper function to throw an error if the provided task and TaskStatus objects are not equal. */ - private void assertTasksEqual(Task first, Task second) { + private void assertTasksEqual(JobInfo first, JobInfo second) { assertEquals("Different task ids.", first.getId(), second.getId()); assertEquals("Different components.", first.getService(), second.getService()); assertEquals("Different periodic status.", first.isPeriodic(), second.isPeriodic()); @@ -168,11 +170,11 @@ public class TaskStoreTest extends AndroidTestCase { assertEquals("Invalid idle constraint.", first.isRequireDeviceIdle(), second.isRequireDeviceIdle()); assertEquals("Invalid unmetered constraint.", - first.getNetworkCapabilities() == Task.NetworkType.UNMETERED, - second.getNetworkCapabilities() == Task.NetworkType.UNMETERED); + first.getNetworkCapabilities() == JobInfo.NetworkType.UNMETERED, + second.getNetworkCapabilities() == JobInfo.NetworkType.UNMETERED); assertEquals("Invalid connectivity constraint.", - first.getNetworkCapabilities() == Task.NetworkType.ANY, - second.getNetworkCapabilities() == Task.NetworkType.ANY); + first.getNetworkCapabilities() == JobInfo.NetworkType.ANY, + second.getNetworkCapabilities() == JobInfo.NetworkType.ANY); assertEquals("Invalid deadline constraint.", first.hasLateConstraint(), second.hasLateConstraint()); diff --git a/services/tests/servicestests/src/com/android/server/task/controllers/BatteryControllerTest.java b/services/tests/servicestests/src/com/android/server/task/controllers/BatteryControllerTest.java index 6617a05..9754e8c 100644 --- a/services/tests/servicestests/src/com/android/server/task/controllers/BatteryControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/task/controllers/BatteryControllerTest.java @@ -21,9 +21,11 @@ import android.content.ComponentName; import android.content.Intent; import android.test.AndroidTestCase; -import com.android.server.task.StateChangedListener; +import com.android.server.job.StateChangedListener; +import com.android.server.job.controllers.BatteryController; +import com.android.server.job.controllers.JobStatus; -import static com.android.server.task.controllers.BatteryController.getForTesting; +import static com.android.server.job.controllers.BatteryController.getForTesting; import static org.mockito.Mockito.*; @@ -40,7 +42,7 @@ public class BatteryControllerTest extends AndroidTestCase { } @Override - public void onRunTaskNow(TaskStatus taskStatus) { + public void onRunJobNow(JobStatus taskStatus) { } }; diff --git a/tests/JobSchedulerTestApp/AndroidManifest.xml b/tests/JobSchedulerTestApp/AndroidManifest.xml index 7431737..9654197 100644 --- a/tests/JobSchedulerTestApp/AndroidManifest.xml +++ b/tests/JobSchedulerTestApp/AndroidManifest.xml @@ -25,6 +25,7 @@ <service android:name=".service.TestJobService" + android:permission="android.permission.BIND_JOB_SERVICE" android:exported="true"/> </application> diff --git a/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java b/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java index 393c594..15050ef 100644 --- a/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java +++ b/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java @@ -17,8 +17,8 @@ package com.android.demo.jobSchedulerApp; import android.app.Activity; -import android.app.task.Task; -import android.app.task.TaskParams; +import android.app.job.JobInfo; +import android.app.job.JobParameters; import android.content.ComponentName; import android.content.Intent; import android.content.res.Resources; @@ -80,10 +80,10 @@ public class MainActivity extends Activity { RadioButton mWiFiConnectivityRadioButton; RadioButton mAnyConnectivityRadioButton; ComponentName mServiceComponent; - /** Service object to interact scheduled tasks. */ + /** Service object to interact scheduled jobs. */ TestJobService mTestService; - private static int kTaskId = 0; + private static int kJobId = 0; Handler mHandler = new Handler(/* default looper */) { @Override @@ -112,7 +112,7 @@ public class MainActivity extends Activity { } /** - * UI onclick listener to schedule a task. What this task is is defined in + * UI onclick listener to schedule a job. What this job is is defined in * TestJobService#scheduleJob() */ public void scheduleJob(View v) { @@ -120,7 +120,7 @@ public class MainActivity extends Activity { return; } - Task.Builder builder = new Task.Builder(kTaskId++, mServiceComponent); + JobInfo.Builder builder = new JobInfo.Builder(kJobId++, mServiceComponent); String delay = mDelayEditText.getText().toString(); if (delay != null && !TextUtils.isEmpty(delay)) { @@ -133,9 +133,9 @@ public class MainActivity extends Activity { boolean requiresUnmetered = mWiFiConnectivityRadioButton.isSelected(); boolean requiresAnyConnectivity = mAnyConnectivityRadioButton.isSelected(); if (requiresUnmetered) { - builder.setRequiredNetworkCapabilities(Task.NetworkType.UNMETERED); + builder.setRequiredNetworkCapabilities(JobInfo.NetworkType.UNMETERED); } else if (requiresAnyConnectivity) { - builder.setRequiredNetworkCapabilities(Task.NetworkType.ANY); + builder.setRequiredNetworkCapabilities(JobInfo.NetworkType.ANY); } mTestService.scheduleJob(builder.build()); @@ -143,24 +143,24 @@ public class MainActivity extends Activity { } /** - * UI onclick listener to call taskFinished() in our service. + * UI onclick listener to call jobFinished() in our service. */ public void finishJob(View v) { if (!ensureTestService()) { return; } - mTestService.callTaskFinished(); + mTestService.callJobFinished(); mParamsTextView.setText(""); } - public void onReceivedStartTask(TaskParams params) { + public void onReceivedStartJob(JobParameters params) { mShowStartView.setBackgroundColor(startJobColor); Message m = Message.obtain(mHandler, MSG_UNCOLOUR_START); mHandler.sendMessageDelayed(m, 1000L); // uncolour in 1 second. - mParamsTextView.setText("Executing: " + params.getTaskId() + " " + params.getExtras()); + mParamsTextView.setText("Executing: " + params.getJobId() + " " + params.getExtras()); } - public void onReceivedStopTask() { + public void onReceivedStopJob() { mShowStopView.setBackgroundColor(stopJobColor); Message m = Message.obtain(mHandler, MSG_UNCOLOUR_STOP); mHandler.sendMessageDelayed(m, 2000L); // uncolour in 1 second. diff --git a/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/service/TestJobService.java b/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/service/TestJobService.java index 7dd3cf1..bf8e887 100644 --- a/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/service/TestJobService.java +++ b/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/service/TestJobService.java @@ -16,28 +16,20 @@ package com.android.demo.jobSchedulerApp.service; -import android.app.Service; -import android.app.task.Task; -import android.app.task.TaskManager; -import android.app.task.TaskParams; -import android.app.task.TaskService; -import android.content.ComponentName; +import android.app.job.JobInfo; +import android.app.job.JobScheduler; +import android.app.job.JobParameters; +import android.app.job.JobService; import android.content.Context; import android.content.Intent; -import android.os.Binder; -import android.os.IBinder; import android.os.Message; import android.os.Messenger; -import android.os.PersistableBundle; import android.os.RemoteException; import android.util.Log; import com.android.demo.jobSchedulerApp.MainActivity; -import java.util.ArrayList; -import java.util.HashMap; import java.util.LinkedList; -import java.util.List; /** @@ -52,7 +44,7 @@ import java.util.List; * lifecycle of our and provide a handle to said SyncAdapter to the OS on * request. */ -public class TestJobService extends TaskService { +public class TestJobService extends JobService { private static final String TAG = "SyncService"; @Override @@ -82,44 +74,44 @@ public class TestJobService extends TaskService { } @Override - public boolean onStartTask(TaskParams params) { - taskParamsMap.add(params); + public boolean onStartJob(JobParameters params) { + jobParamsMap.add(params); if (mActivity != null) { - mActivity.onReceivedStartTask(params); + mActivity.onReceivedStartJob(params); } - Log.i(TAG, "on start task: " + params.getTaskId()); + Log.i(TAG, "on start job: " + params.getJobId()); return true; } @Override - public boolean onStopTask(TaskParams params) { - taskParamsMap.remove(params); - mActivity.onReceivedStopTask(); - Log.i(TAG, "on stop task: " + params.getTaskId()); + public boolean onStopJob(JobParameters params) { + jobParamsMap.remove(params); + mActivity.onReceivedStopJob(); + Log.i(TAG, "on stop job: " + params.getJobId()); return true; } MainActivity mActivity; - private final LinkedList<TaskParams> taskParamsMap = new LinkedList<TaskParams>(); + private final LinkedList<JobParameters> jobParamsMap = new LinkedList<JobParameters>(); public void setUiCallback(MainActivity activity) { mActivity = activity; } /** Send job to the JobScheduler. */ - public void scheduleJob(Task t) { + public void scheduleJob(JobInfo t) { Log.d(TAG, "Scheduling job"); - TaskManager tm = - (TaskManager) getSystemService(Context.TASK_SERVICE); + JobScheduler tm = + (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); tm.schedule(t); } - public boolean callTaskFinished() { - TaskParams params = taskParamsMap.poll(); + public boolean callJobFinished() { + JobParameters params = jobParamsMap.poll(); if (params == null) { return false; } else { - taskFinished(params, false); + jobFinished(params, false); return true; } } |