From fa380e982e41b0dcbbcf2201803abf26808016b5 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Mon, 19 May 2014 13:46:29 -0700 Subject: DO NOT MERGE - Run the task manager service at startup Also moves most of it into android.app.task rather than android.content. (Cherrypick from master) Change-Id: Ic07a664bf54bc3e40aa0b892946edba4bf37262a --- Android.mk | 1 + api/current.txt | 101 +++--- core/java/android/app/ContextImpl.java | 8 + core/java/android/app/TaskManagerImpl.java | 62 ++++ core/java/android/app/task/ITaskManager.aidl | 30 ++ core/java/android/app/task/Task.aidl | 20 ++ core/java/android/app/task/Task.java | 400 +++++++++++++++++++++ core/java/android/app/task/TaskManager.java | 73 ++++ core/java/android/app/task/TaskParams.java | 2 +- core/java/android/app/task/TaskService.java | 8 +- core/java/android/content/Context.java | 14 + core/java/android/content/Task.java | 400 --------------------- core/java/android/content/TaskManager.java | 66 ---- .../android/server/task/TaskManagerService.java | 171 +++++++-- .../android/server/task/TaskServiceContext.java | 2 +- .../java/com/android/server/task/TaskStore.java | 9 +- .../task/controllers/ConnectivityController.java | 10 +- .../server/task/controllers/TaskStatus.java | 39 +- services/java/com/android/server/SystemServer.java | 5 + 19 files changed, 853 insertions(+), 568 deletions(-) create mode 100644 core/java/android/app/TaskManagerImpl.java create mode 100644 core/java/android/app/task/ITaskManager.aidl create mode 100644 core/java/android/app/task/Task.aidl create mode 100644 core/java/android/app/task/Task.java create mode 100644 core/java/android/app/task/TaskManager.java delete mode 100644 core/java/android/content/Task.java delete mode 100644 core/java/android/content/TaskManager.java diff --git a/Android.mk b/Android.mk index 9c41f9f..607f5fa 100644 --- a/Android.mk +++ b/Android.mk @@ -78,6 +78,7 @@ LOCAL_SRC_FILES += \ 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/IThumbnailRetriever.aidl \ core/java/android/app/ITransientNotification.aidl \ diff --git a/api/current.txt b/api/current.txt index b270698..558ccaa 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5298,6 +5298,57 @@ package android.app.maintenance { package android.app.task { + public class Task implements android.os.Parcelable { + method public int describeContents(); + method public int getBackoffPolicy(); + method public android.os.Bundle getExtras(); + method public long getInitialBackoffMillis(); + method public long getIntervalMillis(); + method public long getMaxExecutionDelayMillis(); + method public long getMinLatencyMillis(); + method public int getNetworkCapabilities(); + method public android.content.ComponentName getService(); + method public int getTaskId(); + method public boolean isPeriodic(); + method public boolean isRequireCharging(); + method public boolean isRequireDeviceIdle(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + + public static abstract interface Task.BackoffPolicy { + field public static final int EXPONENTIAL = 1; // 0x1 + field public static final int LINEAR = 0; // 0x0 + } + + public final class Task.Builder { + ctor public Task.Builder(int, 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.Bundle); + 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 abstract interface Task.NetworkType { + field public static final int ANY = 0; // 0x0 + field public static final int UNMETERED = 1; // 0x1 + } + + public abstract class TaskManager { + ctor public TaskManager(); + method public abstract void cancel(int); + method public abstract void cancelAll(); + method public abstract java.util.List getAllPendingTasks(); + method public abstract int schedule(android.app.task.Task); + field public static final int RESULT_INVALID_PARAMETERS = -1; // 0xffffffff + field public static final int RESULT_OVER_QUOTA = -2; // 0xfffffffe + } + public class TaskParams implements android.os.Parcelable { method public int describeContents(); method public android.os.Bundle getExtras(); @@ -6995,6 +7046,7 @@ 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"; @@ -7933,55 +7985,6 @@ package android.content { method public abstract void onStatusChanged(int); } - public class Task implements android.os.Parcelable { - method public int describeContents(); - method public int getBackoffPolicy(); - method public android.os.Bundle getExtras(); - method public long getInitialBackoffMillis(); - method public long getIntervalMillis(); - method public long getMaxExecutionDelayMillis(); - method public long getMinLatencyMillis(); - method public int getNetworkCapabilities(); - method public android.content.ComponentName getService(); - method public int getTaskId(); - method public boolean isPeriodic(); - method public boolean isRequireCharging(); - method public boolean isRequireDeviceIdle(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator CREATOR; - } - - public static abstract interface Task.BackoffPolicy { - field public static final int EXPONENTIAL = 1; // 0x1 - field public static final int LINEAR = 0; // 0x0 - } - - public final class Task.Builder { - ctor public Task.Builder(int, android.content.ComponentName); - method public android.content.Task build(); - method public android.content.Task.Builder setBackoffCriteria(long, int); - method public android.content.Task.Builder setExtras(android.os.Bundle); - method public android.content.Task.Builder setMinimumLatency(long); - method public android.content.Task.Builder setOverrideDeadline(long); - method public android.content.Task.Builder setPeriodic(long); - method public android.content.Task.Builder setRequiredNetworkCapabilities(int); - method public android.content.Task.Builder setRequiresCharging(boolean); - method public android.content.Task.Builder setRequiresDeviceIdle(boolean); - } - - public static abstract interface Task.NetworkType { - field public static final int ANY = 0; // 0x0 - field public static final int UNMETERED = 1; // 0x1 - } - - public abstract class TaskManager { - ctor public TaskManager(); - method public abstract void cancel(int); - method public abstract void cancelAll(); - method public abstract java.util.List getAllPendingTasks(); - method public abstract int schedule(android.content.Task); - } - public class UriMatcher { ctor public UriMatcher(int); method public void addURI(java.lang.String, java.lang.String, int); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 1634d11..ff8688d 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -133,10 +133,12 @@ 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.trust.TrustManager; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IAppOpsService; +import com.android.internal.appwidget.IAppWidgetService.Stub; import com.android.internal.os.IDropBoxManagerService; import java.io.File; @@ -693,6 +695,12 @@ class ContextImpl extends Context { public Object createService(ContextImpl ctx) { return new UsageStatsManager(ctx.getOuterContext()); }}); + + registerService(TASK_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + IBinder b = ServiceManager.getService(TASK_SERVICE); + return new TaskManagerImpl(ITaskManager.Stub.asInterface(b)); + }}); } static ContextImpl getImpl(Context context) { diff --git a/core/java/android/app/TaskManagerImpl.java b/core/java/android/app/TaskManagerImpl.java new file mode 100644 index 0000000..f42839e --- /dev/null +++ b/core/java/android/app/TaskManagerImpl.java @@ -0,0 +1,62 @@ +/* + * 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. + */ + +// 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 java.util.List; + + +/** + * Concrete implementation of the TaskManager interface + * @hide + */ +public class TaskManagerImpl extends TaskManager { + ITaskManager mBinder; + + /* package */ TaskManagerImpl(ITaskManager binder) { + mBinder = binder; + } + + @Override + public int schedule(Task task) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void cancel(int taskId) { + // TODO Auto-generated method stub + + } + + @Override + public void cancelAll() { + // TODO Auto-generated method stub + + } + + @Override + public List getAllPendingTasks() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/core/java/android/app/task/ITaskManager.aidl b/core/java/android/app/task/ITaskManager.aidl new file mode 100644 index 0000000..b56c78a --- /dev/null +++ b/core/java/android/app/task/ITaskManager.aidl @@ -0,0 +1,30 @@ +/** + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.task; + +import android.app.task.Task; + + /** + * IPC interface that supports the app-facing {@link #TaskManager} api. + * {@hide} + */ +interface ITaskManager { + int schedule(in Task task); + void cancel(int taskId); + void cancelAll(); + List getAllPendingTasks(); +} diff --git a/core/java/android/app/task/Task.aidl b/core/java/android/app/task/Task.aidl new file mode 100644 index 0000000..1f25439 --- /dev/null +++ b/core/java/android/app/task/Task.aidl @@ -0,0 +1,20 @@ +/** + * 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; + +parcelable Task; + \ No newline at end of file diff --git a/core/java/android/app/task/Task.java b/core/java/android/app/task/Task.java new file mode 100644 index 0000000..dd184a5 --- /dev/null +++ b/core/java/android/app/task/Task.java @@ -0,0 +1,400 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.app.task; + +import android.content.ComponentName; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Container of data passed to the {@link android.app.task.TaskManager} fully encapsulating the + * parameters required to schedule work against the calling application. These are constructed + * using the {@link Task.Builder}. + */ +public class Task implements Parcelable { + + public interface NetworkType { + public final int ANY = 0; + public final int UNMETERED = 1; + } + + /** + * Linear: retry_time(failure_time, t) = failure_time + initial_retry_delay * t, t >= 1 + * Expon: retry_time(failure_time, t) = failure_time + initial_retry_delay ^ t, t >= 1 + */ + public interface BackoffPolicy { + public final int LINEAR = 0; + public final int EXPONENTIAL = 1; + } + + private final int taskId; + // TODO: Change this to use PersistableBundle when that lands in master. + private final Bundle extras; + private final ComponentName service; + private final boolean requireCharging; + private final boolean requireDeviceIdle; + private final int networkCapabilities; + private final long minLatencyMillis; + private final long maxExecutionDelayMillis; + private final boolean isPeriodic; + private final long intervalMillis; + private final long initialBackoffMillis; + private final int backoffPolicy; + + /** + * Unique task id associated with this class. This is assigned to your task by the scheduler. + */ + public int getTaskId() { + return taskId; + } + + /** + * Bundle of extras which are returned to your application at execution time. + */ + public Bundle getExtras() { + return extras; + } + + /** + * Name of the service endpoint that will be called back into by the TaskManager. + */ + public ComponentName getService() { + return service; + } + + /** + * Whether this task needs the device to be plugged in. + */ + public boolean isRequireCharging() { + return requireCharging; + } + + /** + * Whether this task needs the device to be in an Idle maintenance window. + */ + public boolean isRequireDeviceIdle() { + return requireDeviceIdle; + } + + /** + * See {@link android.app.task.Task.NetworkType} for a description of this value. + */ + public int getNetworkCapabilities() { + return networkCapabilities; + } + + /** + * Set for a task that does not recur periodically, to specify a delay after which the task + * will be eligible for execution. This value is not set if the task recurs periodically. + */ + public long getMinLatencyMillis() { + return minLatencyMillis; + } + + /** + * See {@link Builder#setOverrideDeadline(long)}. This value is not set if the task recurs + * periodically. + */ + public long getMaxExecutionDelayMillis() { + return maxExecutionDelayMillis; + } + + /** + * Track whether this task will repeat with a given period. + */ + public boolean isPeriodic() { + return isPeriodic; + } + + /** + * Set to the interval between occurrences of this task. This value is not set if the + * task does not recur periodically. + */ + public long getIntervalMillis() { + return intervalMillis; + } + + /** + * The amount of time the TaskManager will wait before rescheduling a failed task. This value + * will be increased depending on the backoff policy specified at task creation time. Defaults + * to 5 seconds. + */ + public long getInitialBackoffMillis() { + return initialBackoffMillis; + } + + /** + * See {@link android.app.task.Task.BackoffPolicy} for an explanation of the values this field + * can take. This defaults to exponential. + */ + public int getBackoffPolicy() { + return backoffPolicy; + } + + private Task(Parcel in) { + taskId = in.readInt(); + extras = in.readBundle(); + service = ComponentName.readFromParcel(in); + requireCharging = in.readInt() == 1; + requireDeviceIdle = in.readInt() == 1; + networkCapabilities = in.readInt(); + minLatencyMillis = in.readLong(); + maxExecutionDelayMillis = in.readLong(); + isPeriodic = in.readInt() == 1; + intervalMillis = in.readLong(); + initialBackoffMillis = in.readLong(); + backoffPolicy = in.readInt(); + } + + private Task(Task.Builder b) { + taskId = b.mTaskId; + extras = new Bundle(b.mExtras); + service = b.mTaskService; + requireCharging = b.mRequiresCharging; + requireDeviceIdle = b.mRequiresDeviceIdle; + networkCapabilities = b.mNetworkCapabilities; + minLatencyMillis = b.mMinLatencyMillis; + maxExecutionDelayMillis = b.mMaxExecutionDelayMillis; + isPeriodic = b.mIsPeriodic; + intervalMillis = b.mIntervalMillis; + initialBackoffMillis = b.mInitialBackoffMillis; + backoffPolicy = b.mBackoffPolicy; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(taskId); + out.writeBundle(extras); + ComponentName.writeToParcel(service, out); + out.writeInt(requireCharging ? 1 : 0); + out.writeInt(requireDeviceIdle ? 1 : 0); + out.writeInt(networkCapabilities); + out.writeLong(minLatencyMillis); + out.writeLong(maxExecutionDelayMillis); + out.writeInt(isPeriodic ? 1 : 0); + out.writeLong(intervalMillis); + out.writeLong(initialBackoffMillis); + out.writeInt(backoffPolicy); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Task createFromParcel(Parcel in) { + return new Task(in); + } + + @Override + public Task[] newArray(int size) { + return new Task[size]; + } + }; + + /** + * Builder class for constructing {@link Task} objects. + */ + public final class Builder { + private int mTaskId; + private Bundle mExtras; + private ComponentName mTaskService; + // Requirements. + private boolean mRequiresCharging; + private boolean mRequiresDeviceIdle; + private int mNetworkCapabilities; + // One-off parameters. + private long mMinLatencyMillis; + private long mMaxExecutionDelayMillis; + // Periodic parameters. + private boolean mIsPeriodic; + private long mIntervalMillis; + // Back-off parameters. + private long mInitialBackoffMillis = 5000L; + private int mBackoffPolicy = BackoffPolicy.EXPONENTIAL; + /** Easy way to track whether the client has tried to set a back-off policy. */ + private boolean mBackoffPolicySet = false; + + /** + * @param taskId Application-provided id for this task. Subsequent calls to cancel, or + * tasks created with the same taskId, will update the pre-existing task with + * the same id. + * @param taskService The endpoint that you implement that will receive the callback from the + * TaskManager. + */ + public Builder(int taskId, ComponentName taskService) { + mTaskService = taskService; + mTaskId = taskId; + } + + /** + * Set optional extras. This is persisted, so we only allow primitive types. + * @param extras Bundle containing extras you want the scheduler to hold on to for you. + */ + public Builder setExtras(Bundle extras) { + mExtras = extras; + return this; + } + + /** + * Set some description of the kind of network capabilities you would like to have. This + * will be a parameter defined in {@link android.app.task.Task.NetworkType}. + * Not calling this function means the network is not necessary. + * Bear in mind that calling this function defines network as a strict requirement for your + * task if the network requested is not available your task will never run. See + * {@link #setOverrideDeadline(long)} to change this behaviour. + */ + public Builder setRequiredNetworkCapabilities(int networkCapabilities) { + mNetworkCapabilities = networkCapabilities; + return this; + } + + /* + * Specify that to run this task, the device needs to be plugged in. This defaults to + * false. + * @param requireCharging Whether or not the device is plugged in. + */ + public Builder setRequiresCharging(boolean requiresCharging) { + mRequiresCharging = requiresCharging; + return this; + } + + /** + * Specify that to run, the task needs the device to be in idle mode. This defaults to + * false. + *

Idle mode is a loose definition provided by the system, which means that the device + * is not in use, and has not been in use for some time. As such, it is a good time to + * perform resource heavy tasks. Bear in mind that battery usage will still be attributed + * to your application, and surfaced to the user in battery stats.

+ * @param requiresDeviceIdle Whether or not the device need be within an idle maintenance + * window. + */ + public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) { + mRequiresDeviceIdle = requiresDeviceIdle; + return this; + } + + /** + * Specify that this task should recur with the provided interval, not more than once per + * period. You have no control over when within this interval this task will be executed, + * only the guarantee that it will be executed at most once within this interval. + * A periodic task will be repeated until the phone is turned off, however it will only be + * persisted 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 + * restarts. + * Setting this function on the builder with {@link #setMinimumLatency(long)} or + * {@link #setOverrideDeadline(long)} will result in an error. + * @param intervalMillis Millisecond interval for which this task will repeat. + */ + public Builder setPeriodic(long intervalMillis) { + mIsPeriodic = true; + mIntervalMillis = intervalMillis; + return this; + } + + /** + * Specify that this task should be delayed by the provided amount of time. + * Because it doesn't make sense setting this property on a periodic task, doing so will + * throw an {@link java.lang.IllegalArgumentException} when + * {@link android.app.task.Task.Builder#build()} is called. + * @param minLatencyMillis Milliseconds before which this task will not be considered for + * execution. + */ + public Builder setMinimumLatency(long minLatencyMillis) { + mMinLatencyMillis = minLatencyMillis; + return this; + } + + /** + * Set deadline which is the maximum scheduling latency. The task will be run by this + * deadline even if other requirements are not met. Because it doesn't make sense setting + * this property on a periodic task, doing so will throw an + * {@link java.lang.IllegalArgumentException} when + * {@link android.app.task.Task.Builder#build()} is called. + */ + public Builder setOverrideDeadline(long maxExecutionDelayMillis) { + mMaxExecutionDelayMillis = maxExecutionDelayMillis; + return this; + } + + /** + * Set up the back-off/retry policy. + * This defaults to some respectable values: {5 seconds, Exponential}. We cap back-off at + * 1hr. + * Note that trying to set a backoff criteria for a task with + * {@link #setRequiresDeviceIdle(boolean)} will throw an exception when you call build(). + * This is because back-off typically does not make sense for these types of tasks. See + * {@link android.app.task.TaskService#taskFinished(android.app.task.TaskParams, boolean)} + * for more description of the return value for the case of a task executing while in idle + * mode. + * @param initialBackoffMillis Millisecond time interval to wait initially when task has + * failed. + * @param backoffPolicy is one of {@link BackoffPolicy} + */ + public Builder setBackoffCriteria(long initialBackoffMillis, int backoffPolicy) { + mBackoffPolicySet = true; + mInitialBackoffMillis = initialBackoffMillis; + mBackoffPolicy = backoffPolicy; + return this; + } + + /** + * @return The task object to hand to the TaskManager. This object is immutable. + */ + public Task build() { + // Check that extras bundle only contains primitive types. + try { + for (String key : extras.keySet()) { + Object value = extras.get(key); + if (value == null) continue; + if (value instanceof Long) continue; + if (value instanceof Integer) continue; + if (value instanceof Boolean) continue; + if (value instanceof Float) continue; + if (value instanceof Double) continue; + if (value instanceof String) continue; + throw new IllegalArgumentException("Unexpected value type: " + + value.getClass().getName()); + } + } catch (IllegalArgumentException e) { + throw e; + } catch (RuntimeException exc) { + throw new IllegalArgumentException("error unparcelling Bundle", exc); + } + // Check that a deadline was not set on a periodic task. + if (mIsPeriodic && (mMaxExecutionDelayMillis != 0L)) { + throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " + + "periodic task."); + } + if (mIsPeriodic && (mMinLatencyMillis != 0L)) { + throw new IllegalArgumentException("Can't call setMinimumLatency() on a " + + "periodic task"); + } + if (mBackoffPolicySet && mRequiresDeviceIdle) { + throw new IllegalArgumentException("An idle mode task will not respect any" + + " back-off policy, so calling setBackoffCriteria with" + + " setRequiresDeviceIdle is an error."); + } + return new Task(this); + } + } + +} diff --git a/core/java/android/app/task/TaskManager.java b/core/java/android/app/task/TaskManager.java new file mode 100644 index 0000000..0fbe37d --- /dev/null +++ b/core/java/android/app/task/TaskManager.java @@ -0,0 +1,73 @@ +/* + * 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. + * + *

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_INVALID_PARAMETERS = -1; + + /** + * Returned from {@link #schedule(Task)} if this application has made too many requests for + * work over too short a time. + */ + // TODO: Determine if this is necessary. + public static final int RESULT_OVER_QUOTA = -2; + + /** + * @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 getAllPendingTasks(); + +} diff --git a/core/java/android/app/task/TaskParams.java b/core/java/android/app/task/TaskParams.java index 0351082..dacb348 100644 --- a/core/java/android/app/task/TaskParams.java +++ b/core/java/android/app/task/TaskParams.java @@ -47,7 +47,7 @@ public class TaskParams implements Parcelable { /** * @return The extras you passed in when constructing this task with - * {@link android.content.Task.Builder#setExtras(android.os.Bundle)}. This will + * {@link android.app.task.Task.Builder#setExtras(android.os.Bundle)}. This will * never be null. If you did not set any extras this will be an empty bundle. */ public Bundle getExtras() { diff --git a/core/java/android/app/task/TaskService.java b/core/java/android/app/task/TaskService.java index ab1a565..8ce4484 100644 --- a/core/java/android/app/task/TaskService.java +++ b/core/java/android/app/task/TaskService.java @@ -28,7 +28,7 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; /** - *

Entry point for the callback from the {@link android.content.TaskManager}.

+ *

Entry point for the callback from the {@link android.app.task.TaskManager}.

*

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.

@@ -215,9 +215,9 @@ public abstract class TaskService extends Service { * *

This will happen if the requirements specified at schedule time are no longer met. For * example you may have requested WiFi with - * {@link android.content.Task.Builder#setRequiredNetworkCapabilities(int)}, yet while your + * {@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.content.Task.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its + * {@link android.app.task.Task.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its * idle maintenance window. You are solely responsible for the behaviour of your application * upon receipt of this message; your app will likely start to misbehave if you ignore it. One * immediate repercussion is that the system will cease holding a wakelock for you.

@@ -237,7 +237,7 @@ public abstract class TaskService extends Service { * You can specify post-execution behaviour to the scheduler here with * needsReschedule . This will apply a back-off timer to your task based on * the default, or what was set with - * {@link android.content.Task.Builder#setBackoffCriteria(long, int)}. The original + * {@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 * to the queue and re-executed within a future idle maintenance window. diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 6ae006c..b0673b5 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2023,6 +2023,7 @@ public abstract class Context { PRINT_SERVICE, MEDIA_SESSION_SERVICE, BATTERY_SERVICE, + TASK_SERVICE, }) @Retention(RetentionPolicy.SOURCE) public @interface ServiceName {} @@ -2079,6 +2080,8 @@ public abstract class Context { *
A {@link android.app.DownloadManager} for requesting HTTP downloads *
{@link #BATTERY_SERVICE} ("batterymanager") *
A {@link android.os.BatteryManager} for managing battery state + *
{@link #TASK_SERVICE} ("taskmanager") + *
A {@link android.app.task.TaskManager} for managing scheduled tasks * * *

Note: System services obtained via this API may be closely associated with @@ -2134,6 +2137,8 @@ public abstract class Context { * @see android.app.DownloadManager * @see #BATTERY_SERVICE * @see android.os.BatteryManager + * @see #TASK_SERVICE + * @see android.app.task.TaskManager */ public abstract Object getSystemService(@ServiceName @NonNull String name); @@ -2728,6 +2733,15 @@ public abstract class Context { public static final String USAGE_STATS_SERVICE = "usagestats"; /** + * Use with {@link #getSystemService} to retrieve a {@link + * android.app.task.TaskManager} instance for managing occasional + * background tasks. + * @see #getSystemService + * @see android.app.task.TaskManager + */ + public static final String TASK_SERVICE = "task"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * diff --git a/core/java/android/content/Task.java b/core/java/android/content/Task.java deleted file mode 100644 index 407880f..0000000 --- a/core/java/android/content/Task.java +++ /dev/null @@ -1,400 +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.content; - -import android.app.task.TaskService; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Container of data passed to the {@link android.content.TaskManager} fully encapsulating the - * parameters required to schedule work against the calling application. These are constructed - * using the {@link Task.Builder}. - */ -public class Task implements Parcelable { - - public interface NetworkType { - public final int ANY = 0; - public final int UNMETERED = 1; - } - - /** - * Linear: retry_time(failure_time, t) = failure_time + initial_retry_delay * t, t >= 1 - * Expon: retry_time(failure_time, t) = failure_time + initial_retry_delay ^ t, t >= 1 - */ - public interface BackoffPolicy { - public final int LINEAR = 0; - public final int EXPONENTIAL = 1; - } - - private final int taskId; - // TODO: Change this to use PersistableBundle when that lands in master. - private final Bundle extras; - private final ComponentName service; - private final boolean requireCharging; - private final boolean requireDeviceIdle; - private final int networkCapabilities; - private final long minLatencyMillis; - private final long maxExecutionDelayMillis; - private final boolean isPeriodic; - private final long intervalMillis; - private final long initialBackoffMillis; - private final int backoffPolicy; - - /** - * Unique task id associated with this class. This is assigned to your task by the scheduler. - */ - public int getTaskId() { - return taskId; - } - - /** - * Bundle of extras which are returned to your application at execution time. - */ - public Bundle getExtras() { - return extras; - } - - /** - * Name of the service endpoint that will be called back into by the TaskManager. - */ - public ComponentName getService() { - return service; - } - - /** - * Whether this task needs the device to be plugged in. - */ - public boolean isRequireCharging() { - return requireCharging; - } - - /** - * Whether this task needs the device to be in an Idle maintenance window. - */ - public boolean isRequireDeviceIdle() { - return requireDeviceIdle; - } - - /** - * See {@link android.content.Task.NetworkType} for a description of this value. - */ - public int getNetworkCapabilities() { - return networkCapabilities; - } - - /** - * Set for a task that does not recur periodically, to specify a delay after which the task - * will be eligible for execution. This value is not set if the task recurs periodically. - */ - public long getMinLatencyMillis() { - return minLatencyMillis; - } - - /** - * See {@link Builder#setOverrideDeadline(long)}. This value is not set if the task recurs - * periodically. - */ - public long getMaxExecutionDelayMillis() { - return maxExecutionDelayMillis; - } - - /** - * Track whether this task will repeat with a given period. - */ - public boolean isPeriodic() { - return isPeriodic; - } - - /** - * Set to the interval between occurrences of this task. This value is not set if the - * task does not recur periodically. - */ - public long getIntervalMillis() { - return intervalMillis; - } - - /** - * The amount of time the TaskManager will wait before rescheduling a failed task. This value - * will be increased depending on the backoff policy specified at task creation time. Defaults - * to 5 seconds. - */ - public long getInitialBackoffMillis() { - return initialBackoffMillis; - } - - /** - * See {@link android.content.Task.BackoffPolicy} for an explanation of the values this field - * can take. This defaults to exponential. - */ - public int getBackoffPolicy() { - return backoffPolicy; - } - - private Task(Parcel in) { - taskId = in.readInt(); - extras = in.readBundle(); - service = ComponentName.readFromParcel(in); - requireCharging = in.readInt() == 1; - requireDeviceIdle = in.readInt() == 1; - networkCapabilities = in.readInt(); - minLatencyMillis = in.readLong(); - maxExecutionDelayMillis = in.readLong(); - isPeriodic = in.readInt() == 1; - intervalMillis = in.readLong(); - initialBackoffMillis = in.readLong(); - backoffPolicy = in.readInt(); - } - - private Task(Task.Builder b) { - taskId = b.mTaskId; - extras = new Bundle(b.mExtras); - service = b.mTaskService; - requireCharging = b.mRequiresCharging; - requireDeviceIdle = b.mRequiresDeviceIdle; - networkCapabilities = b.mNetworkCapabilities; - minLatencyMillis = b.mMinLatencyMillis; - maxExecutionDelayMillis = b.mMaxExecutionDelayMillis; - isPeriodic = b.mIsPeriodic; - intervalMillis = b.mIntervalMillis; - initialBackoffMillis = b.mInitialBackoffMillis; - backoffPolicy = b.mBackoffPolicy; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(taskId); - out.writeBundle(extras); - ComponentName.writeToParcel(service, out); - out.writeInt(requireCharging ? 1 : 0); - out.writeInt(requireDeviceIdle ? 1 : 0); - out.writeInt(networkCapabilities); - out.writeLong(minLatencyMillis); - out.writeLong(maxExecutionDelayMillis); - out.writeInt(isPeriodic ? 1 : 0); - out.writeLong(intervalMillis); - out.writeLong(initialBackoffMillis); - out.writeInt(backoffPolicy); - } - - public static final Creator CREATOR = new Creator() { - @Override - public Task createFromParcel(Parcel in) { - return new Task(in); - } - - @Override - public Task[] newArray(int size) { - return new Task[size]; - } - }; - - /** - * Builder class for constructing {@link Task} objects. - */ - public final class Builder { - private int mTaskId; - private Bundle mExtras; - private ComponentName mTaskService; - // Requirements. - private boolean mRequiresCharging; - private boolean mRequiresDeviceIdle; - private int mNetworkCapabilities; - // One-off parameters. - private long mMinLatencyMillis; - private long mMaxExecutionDelayMillis; - // Periodic parameters. - private boolean mIsPeriodic; - private long mIntervalMillis; - // Back-off parameters. - private long mInitialBackoffMillis = 5000L; - private int mBackoffPolicy = BackoffPolicy.EXPONENTIAL; - /** Easy way to track whether the client has tried to set a back-off policy. */ - private boolean mBackoffPolicySet = false; - - /** - * @param taskId Application-provided id for this task. Subsequent calls to cancel, or - * tasks created with the same taskId, will update the pre-existing task with - * the same id. - * @param taskService The endpoint that you implement that will receive the callback from the - * TaskManager. - */ - public Builder(int taskId, ComponentName taskService) { - mTaskService = taskService; - mTaskId = taskId; - } - - /** - * Set optional extras. This is persisted, so we only allow primitive types. - * @param extras Bundle containing extras you want the scheduler to hold on to for you. - */ - public Builder setExtras(Bundle extras) { - mExtras = extras; - return this; - } - - /** - * Set some description of the kind of network capabilities you would like to have. This - * will be a parameter defined in {@link android.content.Task.NetworkType}. - * Not calling this function means the network is not necessary. - * Bear in mind that calling this function defines network as a strict requirement for your - * task if the network requested is not available your task will never run. See - * {@link #setOverrideDeadline(long)} to change this behaviour. - */ - public Builder setRequiredNetworkCapabilities(int networkCapabilities) { - mNetworkCapabilities = networkCapabilities; - return this; - } - - /* - * Specify that to run this task, the device needs to be plugged in. This defaults to - * false. - * @param requireCharging Whether or not the device is plugged in. - */ - public Builder setRequiresCharging(boolean requiresCharging) { - mRequiresCharging = requiresCharging; - return this; - } - - /** - * Specify that to run, the task needs the device to be in idle mode. This defaults to - * false. - *

Idle mode is a loose definition provided by the system, which means that the device - * is not in use, and has not been in use for some time. As such, it is a good time to - * perform resource heavy tasks. Bear in mind that battery usage will still be attributed - * to your application, and surfaced to the user in battery stats.

- * @param requiresDeviceIdle Whether or not the device need be within an idle maintenance - * window. - */ - public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) { - mRequiresDeviceIdle = requiresDeviceIdle; - return this; - } - - /** - * Specify that this task should recur with the provided interval, not more than once per - * period. You have no control over when within this interval this task will be executed, - * only the guarantee that it will be executed at most once within this interval. - * A periodic task will be repeated until the phone is turned off, however it will only be - * persisted 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 - * restarts. - * Setting this function on the builder with {@link #setMinimumLatency(long)} or - * {@link #setOverrideDeadline(long)} will result in an error. - * @param intervalMillis Millisecond interval for which this task will repeat. - */ - public Builder setPeriodic(long intervalMillis) { - mIsPeriodic = true; - mIntervalMillis = intervalMillis; - return this; - } - - /** - * Specify that this task should be delayed by the provided amount of time. - * Because it doesn't make sense setting this property on a periodic task, doing so will - * throw an {@link java.lang.IllegalArgumentException} when - * {@link android.content.Task.Builder#build()} is called. - * @param minLatencyMillis Milliseconds before which this task will not be considered for - * execution. - */ - public Builder setMinimumLatency(long minLatencyMillis) { - mMinLatencyMillis = minLatencyMillis; - return this; - } - - /** - * Set deadline which is the maximum scheduling latency. The task will be run by this - * deadline even if other requirements are not met. Because it doesn't make sense setting - * this property on a periodic task, doing so will throw an - * {@link java.lang.IllegalArgumentException} when - * {@link android.content.Task.Builder#build()} is called. - */ - public Builder setOverrideDeadline(long maxExecutionDelayMillis) { - mMaxExecutionDelayMillis = maxExecutionDelayMillis; - return this; - } - - /** - * Set up the back-off/retry policy. - * This defaults to some respectable values: {5 seconds, Exponential}. We cap back-off at - * 1hr. - * Note that trying to set a backoff criteria for a task with - * {@link #setRequiresDeviceIdle(boolean)} will throw an exception when you call build(). - * This is because back-off typically does not make sense for these types of tasks. See - * {@link android.app.task.TaskService#taskFinished(android.app.task.TaskParams, boolean)} - * for more description of the return value for the case of a task executing while in idle - * mode. - * @param initialBackoffMillis Millisecond time interval to wait initially when task has - * failed. - * @param backoffPolicy is one of {@link BackoffPolicy} - */ - public Builder setBackoffCriteria(long initialBackoffMillis, int backoffPolicy) { - mBackoffPolicySet = true; - mInitialBackoffMillis = initialBackoffMillis; - mBackoffPolicy = backoffPolicy; - return this; - } - - /** - * @return The task object to hand to the TaskManager. This object is immutable. - */ - public Task build() { - // Check that extras bundle only contains primitive types. - try { - for (String key : extras.keySet()) { - Object value = extras.get(key); - if (value == null) continue; - if (value instanceof Long) continue; - if (value instanceof Integer) continue; - if (value instanceof Boolean) continue; - if (value instanceof Float) continue; - if (value instanceof Double) continue; - if (value instanceof String) continue; - throw new IllegalArgumentException("Unexpected value type: " - + value.getClass().getName()); - } - } catch (IllegalArgumentException e) { - throw e; - } catch (RuntimeException exc) { - throw new IllegalArgumentException("error unparcelling Bundle", exc); - } - // Check that a deadline was not set on a periodic task. - if (mIsPeriodic && (mMaxExecutionDelayMillis != 0L)) { - throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " + - "periodic task."); - } - if (mIsPeriodic && (mMinLatencyMillis != 0L)) { - throw new IllegalArgumentException("Can't call setMinimumLatency() on a " + - "periodic task"); - } - if (mBackoffPolicySet && mRequiresDeviceIdle) { - throw new IllegalArgumentException("An idle mode task will not respect any" + - " back-off policy, so calling setBackoffCriteria with" + - " setRequiresDeviceIdle is an error."); - } - return new Task(this); - } - } - -} diff --git a/core/java/android/content/TaskManager.java b/core/java/android/content/TaskManager.java deleted file mode 100644 index d28d78a..0000000 --- a/core/java/android/content/TaskManager.java +++ /dev/null @@ -1,66 +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.content; - -import java.util.List; - -/** - * Class for scheduling various types of tasks with the scheduling framework on the device. - * - * Get an instance of this class through {@link Context#getSystemService(String)}. - */ -public abstract class TaskManager { - /* - * Returned from {@link #schedule(Task)} when an invalid parameter was supplied. This can occur - * if the run-time for your task is too short, or perhaps the system can't resolve the - * requisite {@link TaskService} in your package. - */ - static final int RESULT_INVALID_PARAMETERS = -1; - /** - * Returned from {@link #schedule(Task)} if this application has made too many requests for - * work over too short a time. - */ - // TODO: Determine if this is necessary. - static final int RESULT_OVER_QUOTA = -2; - - /* - * @param task The task you wish scheduled. See {@link Task#TaskBuilder} for more detail on - * the sorts of tasks you can schedule. - * @return If >0, this int corresponds to the taskId of the successfully scheduled task. - * Otherwise you have to compare the return value to the error codes defined in this class. - */ - public abstract int schedule(Task task); - - /** - * Cancel a task that is pending in the TaskManager. - * @param taskId unique identifier for this task. Obtain this value from the tasks returned by - * {@link #getAllPendingTasks()}. - * @return - */ - public abstract void cancel(int taskId); - - /** - * Cancel all tasks that have been registered with the TaskManager by this package. - */ - public abstract void cancelAll(); - - /** - * @return a list of all the tasks registered by this package that have not yet been executed. - */ - public abstract List getAllPendingTasks(); - -} diff --git a/services/core/java/com/android/server/task/TaskManagerService.java b/services/core/java/com/android/server/task/TaskManagerService.java index 6d208ff..80030b4 100644 --- a/services/core/java/com/android/server/task/TaskManagerService.java +++ b/services/core/java/com/android/server/task/TaskManagerService.java @@ -16,12 +16,22 @@ package com.android.server.task; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + +import android.app.task.ITaskManager; +import android.app.task.Task; import android.content.Context; -import android.content.Task; +import android.content.pm.PackageManager; +import android.os.Binder; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.util.Log; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.Slog; import android.util.SparseArray; import com.android.server.task.controllers.TaskStatus; @@ -39,13 +49,6 @@ public class TaskManagerService extends com.android.server.SystemService /** Master list of tasks. */ private final TaskStore mTasks; - /** Check the pending queue and start any tasks. */ - static final int MSG_RUN_PENDING = 0; - /** Initiate the stop task flow. */ - static final int MSG_STOP_TASK = 1; - /** */ - static final int MSG_CHECK_TASKS = 2; - /** * Track Services that have currently active or pending tasks. The index is provided by * {@link TaskStatus#getServiceToken()} @@ -54,6 +57,14 @@ public class TaskManagerService extends com.android.server.SystemService new SparseArray(); private final TaskHandler mHandler; + private final TaskManagerStub mTaskManagerStub; + + /** Check the pending queue and start any tasks. */ + static final int MSG_RUN_PENDING = 0; + /** Initiate the stop task flow. */ + static final int MSG_STOP_TASK = 1; + /** */ + static final int MSG_CHECK_TASKS = 2; private class TaskHandler extends Handler { @@ -94,21 +105,6 @@ public class TaskManagerService extends com.android.server.SystemService } /** - * Entry point from client to schedule the provided task. - * This will add the task to the - * @param task Task object containing execution parameters - * @param userId The id of the user this task is for. - * @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 - * of this task. - * @return Result of this operation. See TaskManager#RESULT_* return codes. - */ - public int schedule(Task task, int userId, int uId, boolean canPersistTask) { - TaskStatus taskStatus = mTasks.addNewTaskForUser(task, userId, uId, canPersistTask); - return 0; - } - - /** * Initializes the system service. *

* Subclasses must define a single argument constructor that accepts the context @@ -121,11 +117,42 @@ public class TaskManagerService extends com.android.server.SystemService super(context); mTasks = new TaskStore(context); mHandler = new TaskHandler(context.getMainLooper()); + mTaskManagerStub = new TaskManagerStub(); } @Override public void onStart() { + publishBinderService(Context.TASK_SERVICE, mTaskManagerStub); + } + + /** + * Entry point from client to schedule the provided task. + * This will add the task to the + * @param task Task object containing execution parameters + * @param userId The id of the user this task is for. + * @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 of this task. + * @return Result of this operation. See TaskManager#RESULT_* return codes. + */ + public int schedule(Task task, int userId, int uId, boolean canPersistTask) { + TaskStatus taskStatus = mTasks.addNewTaskForUser(task, userId, uId, canPersistTask); + return 0; + } + public List getPendingTasks(int uid) { + ArrayList outList = new ArrayList(3); + synchronized (mTasks) { + final SparseArray tasks = mTasks.getTasks(); + final int N = tasks.size(); + for (int i = 0; i < N; i++) { + TaskStatus ts = tasks.get(i); + if (ts.getUid() == uid) { + outList.add(ts.getTask()); + } + } + } + return outList; } // StateChangedListener implementations. @@ -162,7 +189,7 @@ public class TaskManagerService extends com.android.server.SystemService public void onTaskCompleted(int serviceToken, int taskId, boolean needsReschedule) { final TaskServiceContext serviceContext = mActiveServices.get(serviceToken); if (serviceContext == null) { - Log.e(TAG, "Task completed for invalid service context; " + serviceToken); + Slog.e(TAG, "Task completed for invalid service context; " + serviceToken); return; } @@ -203,4 +230,98 @@ public class TaskManagerService extends com.android.server.SystemService private void postCheckTasksMessage() { mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget(); } + + /** + * 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 mPersistCache = new SparseArray(); + + // Determine whether the caller is allowed to persist tasks, with a small cache + // because the lookup is expensive enough that we'd like to avoid repeating it. + // This must be called from within the calling app's binder identity! + private boolean canCallerPersistTasks() { + final boolean canPersist; + final int callingUid = Binder.getCallingUid(); + synchronized (mPersistCache) { + Boolean cached = mPersistCache.get(callingUid); + if (cached) { + 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().checkCallingPermission( + android.Manifest.permission.RECEIVE_BOOT_COMPLETED); + canPersist = (result == PackageManager.PERMISSION_GRANTED); + mPersistCache.put(callingUid, canPersist); + } + } + return canPersist; + } + + // ITaskManager implementation + @Override + public int schedule(Task task) throws RemoteException { + final boolean canPersist = canCallerPersistTasks(); + final int uid = Binder.getCallingUid(); + final int userId = UserHandle.getCallingUserId(); + + long ident = Binder.clearCallingIdentity(); + try { + return TaskManagerService.this.schedule(task, userId, uid, canPersist); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public List getAllPendingTasks() throws RemoteException { + return null; + } + + @Override + public void cancelAll() throws RemoteException { + } + + @Override + public void cancel(int taskId) throws RemoteException { + } + + /** + * "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.print("Registered tasks:"); + if (mTasks.size() > 0) { + SparseArray tasks = mTasks.getTasks(); + for (int i = 0; i < tasks.size(); i++) { + TaskStatus ts = tasks.get(i); + pw.println(); + ts.dump(pw, " "); + } + } else { + pw.println(); + pw.println("No tasks scheduled."); + } + } + pw.println(); + } } diff --git a/services/core/java/com/android/server/task/TaskServiceContext.java b/services/core/java/com/android/server/task/TaskServiceContext.java index b51cbb3..165445a 100644 --- a/services/core/java/com/android/server/task/TaskServiceContext.java +++ b/services/core/java/com/android/server/task/TaskServiceContext.java @@ -45,7 +45,7 @@ import java.util.concurrent.atomic.AtomicBoolean; * is reused to start concurrent tasks on the TaskService. Information here is unique * to the service. * Functionality provided by this class: - * - Managages wakelock for the service. + * - Manages wakelock for the service. * - Sends onStartTask() and onStopTask() messages to client app, and handles callbacks. * - */ diff --git a/services/core/java/com/android/server/task/TaskStore.java b/services/core/java/com/android/server/task/TaskStore.java index 3bfc8a5..81187c8 100644 --- a/services/core/java/com/android/server/task/TaskStore.java +++ b/services/core/java/com/android/server/task/TaskStore.java @@ -16,8 +16,8 @@ package com.android.server.task; +import android.app.task.Task; import android.content.Context; -import android.content.Task; import android.util.SparseArray; import com.android.server.task.controllers.TaskStatus; @@ -95,6 +95,13 @@ public class TaskStore { } /** + * @return the number of tasks in the store + */ + public int size() { + return mTasks.size(); + } + + /** * @return The live array of TaskStatus objects. */ public SparseArray getTasks() { diff --git a/services/core/java/com/android/server/task/controllers/ConnectivityController.java b/services/core/java/com/android/server/task/controllers/ConnectivityController.java index 6a4e1f3..a0038c5 100644 --- a/services/core/java/com/android/server/task/controllers/ConnectivityController.java +++ b/services/core/java/com/android/server/task/controllers/ConnectivityController.java @@ -70,13 +70,10 @@ public class ConnectivityController extends StateController { } /** - * @param userId Id of the user for whom we are updating the connectivity state. + * */ - private void updateTrackedTasks(int userId) { + private void updateTrackedTasks() { for (TaskStatus ts : mTrackedTasks) { - if (ts.userId != userId) { - continue; - } boolean prevIsConnected = ts.connectivityConstraintSatisfied.getAndSet(mConnectivity); boolean prevIsMetered = ts.meteredConstraintSatisfied.getAndSet(mMetered); if (prevIsConnected != mConnectivity || prevIsMetered != mMetered) { @@ -107,14 +104,13 @@ public class ConnectivityController extends StateController { final NetworkInfo activeNetwork = connManager.getActiveNetworkInfo(); // This broadcast gets sent a lot, only update if the active network has changed. if (activeNetwork.getType() == networkType) { - final int userid = context.getUserId(); mMetered = false; mConnectivity = !intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); if (mConnectivity) { // No point making the call if we know there's no conn. mMetered = connManager.isActiveNetworkMetered(); } - updateTrackedTasks(userid); + updateTrackedTasks(); } } else { Log.w(TAG, "Unrecognised action in intent: " + action); diff --git a/services/core/java/com/android/server/task/controllers/TaskStatus.java b/services/core/java/com/android/server/task/controllers/TaskStatus.java index d96fedc..d270016 100644 --- a/services/core/java/com/android/server/task/controllers/TaskStatus.java +++ b/services/core/java/com/android/server/task/controllers/TaskStatus.java @@ -16,17 +16,19 @@ package com.android.server.task.controllers; +import android.app.task.Task; import android.content.ComponentName; -import android.content.Task; import android.content.pm.PackageParser; import android.os.Bundle; import android.os.SystemClock; +import android.os.UserHandle; +import java.io.PrintWriter; import java.util.concurrent.atomic.AtomicBoolean; /** * Uniquely identifies a task internally. - * Created from the public {@link android.content.Task} object when it lands on the scheduler. + * 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 * whether it's ready to run. * This object is shared among the various controllers - hence why the different fields are atomic. @@ -36,10 +38,9 @@ import java.util.concurrent.atomic.AtomicBoolean; * @hide */ public class TaskStatus { + final Task task; final int taskId; - final int userId; final int uId; - final ComponentName component; final Bundle extras; final AtomicBoolean chargingConstraintSatisfied = new AtomicBoolean(); @@ -57,9 +58,9 @@ public class TaskStatus { private long earliestRunTimeElapsedMillis; private long latestRunTimeElapsedMillis; - /** Provide a unique handle to the service that this task will be run on. */ + /** Provide a handle to the service that this task will be run on. */ public int getServiceToken() { - return component.hashCode() + userId; + return uId; } /** Generate a TaskStatus object for a given task and uid. */ @@ -70,9 +71,8 @@ public class TaskStatus { /** Set up the state of a newly scheduled task. */ TaskStatus(Task task, int userId, int uId) { + this.task = task; this.taskId = task.getTaskId(); - this.userId = userId; - this.component = task.getService(); this.extras = task.getExtras(); this.uId = uId; @@ -100,16 +100,20 @@ public class TaskStatus { hasConnectivityConstraint = task.getNetworkCapabilities() == Task.NetworkType.ANY; } + public Task getTask() { + return task; + } + public int getTaskId() { return taskId; } public ComponentName getServiceComponent() { - return component; + return task.getService(); } public int getUserId() { - return userId; + return UserHandle.getUserId(uId); } public int getUid() { @@ -161,9 +165,9 @@ public class TaskStatus { @Override public int hashCode() { - int result = component.hashCode(); + int result = getServiceComponent().hashCode(); result = 31 * result + taskId; - result = 31 * result + userId; + result = 31 * result + uId; return result; } @@ -174,7 +178,14 @@ public class TaskStatus { TaskStatus that = (TaskStatus) o; return ((taskId == that.taskId) - && (userId == that.userId) - && (component.equals(that.component))); + && (uId == that.uId) + && (getServiceComponent().equals(that.getServiceComponent()))); + } + + // Dumpsys infrastructure + public void dump(PrintWriter pw, String prefix) { + pw.print(prefix); pw.print("Task "); pw.println(taskId); + pw.print(prefix); pw.print("uid="); pw.println(uId); + pw.print(prefix); pw.print("component="); pw.println(task.getService()); } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index de46b16..bc34e0e 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -83,6 +83,7 @@ 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; @@ -132,6 +133,8 @@ public final class SystemServer { "com.android.server.hdmi.HdmiCecService"; 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 final int mFactoryTestMode; private Timer mProfilerSnapshotTimer; @@ -831,6 +834,8 @@ public final class SystemServer { mSystemServiceManager.startService(UiModeManagerService.class); + mSystemServiceManager.startService(TaskManagerService.class); + if (!disableNonCoreServices) { try { if (pm.hasSystemFeature(PackageManager.FEATURE_BACKUP)) { -- cgit v1.1