blob: 6d208ff4e49ee4697281d957e28ed5d9761c191d [file] [log] [blame]
/*
* 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 android.content.Context;
import android.content.Task;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.util.SparseArray;
import com.android.server.task.controllers.TaskStatus;
/**
* 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.
* @hide
*/
public class TaskManagerService extends com.android.server.SystemService
implements StateChangedListener, TaskCompletedListener {
static final String TAG = "TaskManager";
/** 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()}
*/
private final SparseArray<TaskServiceContext> mActiveServices =
new SparseArray<TaskServiceContext>();
private final TaskHandler mHandler;
private class TaskHandler extends Handler {
public TaskHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message message) {
switch (message.what) {
case MSG_RUN_PENDING:
break;
case MSG_STOP_TASK:
break;
case MSG_CHECK_TASKS:
checkTasks();
break;
}
}
/**
* Called when we need to run through the list of all tasks and start/stop executing one or
* more of them.
*/
private void checkTasks() {
synchronized (mTasks) {
final SparseArray<TaskStatus> tasks = mTasks.getTasks();
for (int i = 0; i < tasks.size(); i++) {
TaskStatus ts = tasks.valueAt(i);
if (ts.isReady() && ! isCurrentlyActive(ts)) {
assignTaskToServiceContext(ts);
}
}
}
}
}
/**
* 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 <code>TaskManager#RESULT_*</code> 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.
* <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);
mTasks = new TaskStore(context);
mHandler = new TaskHandler(context.getMainLooper());
}
@Override
public void onStart() {
}
// 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 onTaskStateChanged(TaskStatus taskStatus) {
postCheckTasksMessage();
}
@Override
public void onTaskDeadlineExpired(TaskStatus taskStatus) {
}
// 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 serviceToken key for the service context in {@link #mActiveServices}.
* @param taskId Id of the task that is complete.
* @param needsReschedule Whether the implementing class should reschedule this task.
*/
@Override
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);
return;
}
}
@Override
public void onAllTasksCompleted(int serviceToken) {
}
private void assignTaskToServiceContext(TaskStatus ts) {
TaskServiceContext serviceContext =
mActiveServices.get(ts.getServiceToken());
if (serviceContext == null) {
serviceContext = new TaskServiceContext(this, mHandler.getLooper(), ts);
mActiveServices.put(ts.getServiceToken(), serviceContext);
}
serviceContext.addPendingTask(ts);
}
/**
* @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 isCurrentlyActive(TaskStatus ts) {
TaskServiceContext serviceContext = mActiveServices.get(ts.getServiceToken());
if (serviceContext == null) {
return false;
}
return serviceContext.hasTaskPending(ts);
}
/**
* Post a message to {@link #mHandler} to run through the list of tasks and start/stop any that
* are eligible.
*/
private void postCheckTasksMessage() {
mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget();
}
}