blob: 819774adf3881643d0d4a7e5546dbac395f73c84 [file] [log] [blame]
/*
* Copyright (C) 2011 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.filterfw.core;
import android.os.AsyncTask;
import android.util.Log;
/**
* @hide
*/
public class AsyncRunner extends GraphRunner{
private Class mSchedulerClass;
private SyncRunner mRunner;
private AsyncRunnerTask mRunTask;
private OnRunnerDoneListener mDoneListener;
private boolean isProcessing;
private Exception mException;
private class RunnerResult {
public int status = RESULT_UNKNOWN;
public Exception exception;
}
private class AsyncRunnerTask extends AsyncTask<SyncRunner, Void, RunnerResult> {
private static final String TAG = "AsyncRunnerTask";
@Override
protected RunnerResult doInBackground(SyncRunner... runner) {
RunnerResult result = new RunnerResult();
try {
if (runner.length > 1) {
throw new RuntimeException("More than one runner received!");
}
runner[0].assertReadyToStep();
// Preparation
if (mLogVerbose) Log.v(TAG, "Starting background graph processing.");
activateGlContext();
if (mLogVerbose) Log.v(TAG, "Preparing filter graph for processing.");
runner[0].beginProcessing();
if (mLogVerbose) Log.v(TAG, "Running graph.");
// Run loop
result.status = RESULT_RUNNING;
while (!isCancelled() && result.status == RESULT_RUNNING) {
if (!runner[0].performStep()) {
result.status = runner[0].determinePostRunState();
if (result.status == GraphRunner.RESULT_SLEEPING) {
runner[0].waitUntilWake();
result.status = RESULT_RUNNING;
}
}
}
// Cleanup
if (isCancelled()) {
result.status = RESULT_STOPPED;
}
} catch (Exception exception) {
result.exception = exception;
result.status = RESULT_ERROR;
}
// Deactivate context.
try {
deactivateGlContext();
} catch (Exception exception) {
result.exception = exception;
result.status = RESULT_ERROR;
}
if (mLogVerbose) Log.v(TAG, "Done with background graph processing.");
return result;
}
@Override
protected void onCancelled(RunnerResult result) {
onPostExecute(result);
}
@Override
protected void onPostExecute(RunnerResult result) {
if (mLogVerbose) Log.v(TAG, "Starting post-execute.");
setRunning(false);
if (result == null) {
// Cancelled before got to doInBackground
result = new RunnerResult();
result.status = RESULT_STOPPED;
}
setException(result.exception);
if (result.status == RESULT_STOPPED || result.status == RESULT_ERROR) {
if (mLogVerbose) Log.v(TAG, "Closing filters.");
try {
mRunner.close();
} catch (Exception exception) {
result.status = RESULT_ERROR;
setException(exception);
}
}
if (mDoneListener != null) {
if (mLogVerbose) Log.v(TAG, "Calling graph done callback.");
mDoneListener.onRunnerDone(result.status);
}
if (mLogVerbose) Log.v(TAG, "Completed post-execute.");
}
}
private boolean mLogVerbose;
private static final String TAG = "AsyncRunner";
/** Create a new asynchronous graph runner with the given filter
* context, and the given scheduler class.
*
* Must be created on the UI thread.
*/
public AsyncRunner(FilterContext context, Class schedulerClass) {
super(context);
mSchedulerClass = schedulerClass;
mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
}
/** Create a new asynchronous graph runner with the given filter
* context. Uses a default scheduler.
*
* Must be created on the UI thread.
*/
public AsyncRunner(FilterContext context) {
super(context);
mSchedulerClass = SimpleScheduler.class;
mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
}
/** Set a callback to be called in the UI thread once the AsyncRunner
* completes running a graph, whether the completion is due to a stop() call
* or the filters running out of data to process.
*/
@Override
public void setDoneCallback(OnRunnerDoneListener listener) {
mDoneListener = listener;
}
/** Sets the graph to be run. Will call prepare() on graph. Cannot be called
* when a graph is already running.
*/
synchronized public void setGraph(FilterGraph graph) {
if (isRunning()) {
throw new RuntimeException("Graph is already running!");
}
mRunner = new SyncRunner(mFilterContext, graph, mSchedulerClass);
}
@Override
public FilterGraph getGraph() {
return mRunner != null ? mRunner.getGraph() : null;
}
/** Execute the graph in a background thread. */
@Override
synchronized public void run() {
if (mLogVerbose) Log.v(TAG, "Running graph.");
setException(null);
if (isRunning()) {
throw new RuntimeException("Graph is already running!");
}
if (mRunner == null) {
throw new RuntimeException("Cannot run before a graph is set!");
}
mRunTask = this.new AsyncRunnerTask();
setRunning(true);
mRunTask.execute(mRunner);
}
/** Stop graph execution. This is an asynchronous call; register a callback
* with setDoneCallback to be notified of when the background processing has
* been completed. Calling stop will close the filter graph. */
@Override
synchronized public void stop() {
if (mRunTask != null && !mRunTask.isCancelled() ) {
if (mLogVerbose) Log.v(TAG, "Stopping graph.");
mRunTask.cancel(false);
}
}
@Override
synchronized public void close() {
if (isRunning()) {
throw new RuntimeException("Cannot close graph while it is running!");
}
if (mLogVerbose) Log.v(TAG, "Closing filters.");
mRunner.close();
}
/** Check if background processing is happening */
@Override
synchronized public boolean isRunning() {
return isProcessing;
}
@Override
synchronized public Exception getError() {
return mException;
}
synchronized private void setRunning(boolean running) {
isProcessing = running;
}
synchronized private void setException(Exception exception) {
mException = exception;
}
}