Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4b291fbf authored by Matthew Williams's avatar Matthew Williams Committed by Android (Google) Code Review
Browse files

Merge "TM TaskServiceContext implementation"

parents 605a07be 691e93e8
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -34,14 +34,17 @@ interface ITaskCallback {
     * Immediate callback to the system after sending a start signal, used to quickly detect ANR.
     *
     * @param taskId Unique integer used to identify this task.
     * @param ongoing True to indicate that the client is processing the task. False if the task is
     * complete
     */
    void acknowledgeStartMessage(int taskId);
    void acknowledgeStartMessage(int taskId, 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.
     */
    void acknowledgeStopMessage(int taskId);
    void acknowledgeStopMessage(int taskId, boolean reschedule);
    /*
     * Tell the task manager that the client is done with its execution, so that it can go on to
     * the next one and stop attributing wakelock time to us etc.
+39 −42
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package android.app.task;

import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -124,22 +123,20 @@ public abstract class TaskService extends Service {
            switch (msg.what) {
                case MSG_EXECUTE_TASK:
                    try {
                        TaskService.this.onStartTask(params);
                        boolean workOngoing = TaskService.this.onStartTask(params);
                        ackStartMessage(params, workOngoing);
                    } catch (Exception e) {
                        Log.e(TAG, "Error while executing task: " + params.getTaskId());
                        throw new RuntimeException(e);
                    } finally {
                        maybeAckMessageReceived(params, MSG_EXECUTE_TASK);
                    }
                    break;
                case MSG_STOP_TASK:
                    try {
                        TaskService.this.onStopTask(params);
                        boolean ret = TaskService.this.onStopTask(params);
                        ackStopMessage(params, ret);
                    } catch (Exception e) {
                        Log.e(TAG, "Application unable to handle onStopTask.", e);
                        throw new RuntimeException(e);
                    } finally {
                        maybeAckMessageReceived(params, MSG_STOP_TASK);
                    }
                    break;
                case MSG_TASK_FINISHED:
@@ -162,30 +159,34 @@ public abstract class TaskService extends Service {
            }
        }

        /**
         * Messages come in on the application's main thread, so rather than run the risk of
         * waiting for an app that may be doing something foolhardy, we ack to the system after
         * processing a message. This allows us to throw up an ANR dialogue as quickly as possible.
         * @param params id of the task we're acking.
         * @param state Information about what message we're acking.
         */
        private void maybeAckMessageReceived(TaskParams params, int state) {
        private void ackStartMessage(TaskParams params, boolean workOngoing) {
            final ITaskCallback callback = params.getCallback();
            final int taskId = params.getTaskId();
            if (callback != null) {
                try {
                    if (state == MSG_EXECUTE_TASK) {
                        callback.acknowledgeStartMessage(taskId);
                    } else if (state == MSG_STOP_TASK) {
                        callback.acknowledgeStopMessage(taskId);
                    }
                     callback.acknowledgeStartMessage(taskId, workOngoing);
                } catch(RemoteException e) {
                    Log.e(TAG, "System unreachable for starting task.");
                }
            } else {
                if (Log.isLoggable(TAG, Log.DEBUG)) {
                    Log.d(TAG, state + ": Attempting to ack a task that has already been" +
                            "processed.");
                    Log.d(TAG, "Attempting to ack a task that has already been processed.");
                }
            }
        }

        private void ackStopMessage(TaskParams params, boolean reschedule) {
            final ITaskCallback callback = params.getCallback();
            final int taskId = params.getTaskId();
            if (callback != null) {
                try {
                    callback.acknowledgeStopMessage(taskId, reschedule);
                } catch(RemoteException e) {
                    Log.e(TAG, "System unreachable for stopping task.");
                }
            } else {
                if (Log.isLoggable(TAG, Log.DEBUG)) {
                    Log.d(TAG, "Attempting to ack a task that has already been processed.");
                }
            }
        }
@@ -203,12 +204,14 @@ public abstract class TaskService extends Service {
     *
     * @param params Parameters specifying info about this task, including the extras bundle you
     *               optionally provided at task-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.
     */
    public abstract void onStartTask(TaskParams params);
    public abstract boolean onStartTask(TaskParams params);

    /**
     * This method is called if your task should be stopped even before you've called
     * {@link #taskFinished(TaskParams, boolean)}.
     * 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)}.
     *
     * <p>This will happen if the requirements specified at schedule time are no longer met. For
     * example you may have requested WiFi with
@@ -217,33 +220,27 @@ public abstract class TaskService extends Service {
     * {@link android.content.Task.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its
     * idle maintenance window. You are solely responsible for the behaviour of your application
     * upon receipt of this message; your app will likely start to misbehave if you ignore it. One
     * repercussion is that the system will cease to hold a wakelock for you.</p>
     *
     * <p>After you've done your clean-up you are still expected to call
     * {@link #taskFinished(TaskParams, boolean)} this will inform the TaskManager that all is well, and
     * allow you to reschedule your task as it is probably uncompleted. Until you call
     * taskFinished() you will not receive any newly scheduled tasks with the given task id as the
     * TaskManager will consider the task to be in an error state.</p>
     * 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 criteria provided at task creation-time. False to drop the task. Regardless of the
     * value returned, your task must stop executing.
     * on the retry criteria provided at task creation-time. False to drop the task. Regardless of
     * the value returned, your task must stop executing.
     */
    public abstract boolean onStopTask(TaskParams params);

    /**
     * Callback to inform the TaskManager you have completed execution. This can be called from any
     * Callback to inform the TaskManager 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 the default, or what was
     *     set with {@link android.content.Task.Builder#setBackoffCriteria(long, int)}. The
     *     original requirements are always honoured even for a backed-off task.
     *     Note that a task running in idle mode will not be backed-off. Instead what will happen
     *     is the task will be re-added to the queue and re-executed within a future idle
     *     maintenance window.
     *     You can specify post-execution behaviour to the scheduler here with
     *     <code>needsReschedule </code>. This will apply a back-off timer to your task based on
     *     the default, or what was set with
     *     {@link android.content.Task.Builder#setBackoffCriteria(long, int)}. The original
     *     requirements are always honoured even for a backed-off task. Note that a task running in
     *     idle mode will not be backed-off. Instead what will happen is the task will be re-added
     *     to the queue and re-executed within a future idle maintenance window.
     * </p>
     *
     * @param params Parameters specifying system-provided info about this task, this was given to
+38 −0
Original line number Diff line number Diff line
/*
 * 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;

/**
 * Used for communication between {@link com.android.server.task.TaskServiceContext} and the
 * {@link com.android.server.task.TaskManagerService}.
 */
public interface TaskCompletedListener {

    /**
     * Callback for when a task is completed.
     * @param needsReschedule Whether the implementing class should reschedule this task.
     */
    public void onTaskCompleted(int serviceToken, int taskId, boolean needsReschedule);

    /**
     * Callback for when the implementing class needs to clean up the
     * {@link com.android.server.task.TaskServiceContext}. The scheduler can get this callback
     * several times if the TaskServiceContext got into a bad state (for e.g. the client crashed
     * and it needs to clean up).
     */
    public void onAllTasksCompleted(int serviceToken);
}
+105 −26
Original line number Diff line number Diff line
@@ -17,16 +17,15 @@
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;

import java.util.ArrayList;
import java.util.List;

/**
 * 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
@@ -34,25 +33,29 @@ import java.util.List;
 * @hide
 */
public class TaskManagerService extends com.android.server.SystemService
        implements StateChangedListener {
        implements StateChangedListener, TaskCompletedListener {
    static final String TAG = "TaskManager";

    /** Master list of tasks. */
    private final TaskList mTaskList;
    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> mPendingTaskServices =
    private final SparseArray<TaskServiceContext> mActiveServices =
            new SparseArray<TaskServiceContext>();

    private final TaskHandler mHandler;

    private class TaskHandler extends Handler {
        /** 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;

        public TaskHandler(Looper looper) {
            super(looper);
@@ -66,24 +69,45 @@ public class TaskManagerService extends com.android.server.SystemService
                    break;
                case MSG_STOP_TASK:

                    break;
                case MSG_CHECK_TASKS:
                    checkTasks();
                    break;
            }
        }

        /**
         * Helper to post a message to this handler that will run through the pending queue and
         * start any tasks it can.
         * Called when we need to run through the list of all tasks and start/stop executing one or
         * more of them.
         */
        void sendRunPendingTasksMessage() {
            Message m = Message.obtain(this, MSG_RUN_PENDING);
            m.sendToTarget();
        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);
                    }
                }
            }

        void sendOnStopMessage(TaskStatus taskStatus) {

        }
    }

    /**
     * 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>
@@ -95,7 +119,7 @@ public class TaskManagerService extends com.android.server.SystemService
     */
    public TaskManagerService(Context context) {
        super(context);
        mTaskList = new TaskList();
        mTasks = new TaskStore(context);
        mHandler = new TaskHandler(context.getMainLooper());
    }

@@ -104,25 +128,80 @@ public class TaskManagerService extends com.android.server.SystemService

    }

    // StateChangedListener implementations.

    /**
     * Offboard work to our handler thread as quickly as possible, b/c this call is probably being
     * 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.
     * @param taskStatus The state of the task which has changed.
     */
    @Override
    public void onTaskStateChanged(TaskStatus taskStatus) {
        if (taskStatus.isReady()) {
        postCheckTasksMessage();

        } else {
            if (mPendingTaskServices.get(taskStatus.getServiceToken()) != null) {
                // The task is either pending or being executed, which we have to cancel.
    }

    @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 onTaskDeadlineExpired(TaskStatus taskStatus) {
    public void onClientExecutionCompleted(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();
    }
}
+440 −19

File changed.

Preview size limit exceeded, changes collapsed.

Loading