1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 18package android.filterfw.core; 19 20import android.os.AsyncTask; 21import android.os.Handler; 22 23import android.util.Log; 24 25import java.lang.InterruptedException; 26import java.lang.Runnable; 27import java.util.concurrent.CancellationException; 28import java.util.concurrent.ExecutionException; 29import java.util.concurrent.TimeoutException; 30import java.util.concurrent.TimeUnit; 31 32/** 33 * @hide 34 */ 35public class AsyncRunner extends GraphRunner{ 36 37 private Class mSchedulerClass; 38 private SyncRunner mRunner; 39 private AsyncRunnerTask mRunTask; 40 41 private OnRunnerDoneListener mDoneListener; 42 private boolean isProcessing; 43 44 private Exception mException; 45 46 private class RunnerResult { 47 public int status = RESULT_UNKNOWN; 48 public Exception exception; 49 } 50 51 private class AsyncRunnerTask extends AsyncTask<SyncRunner, Void, RunnerResult> { 52 53 private static final String TAG = "AsyncRunnerTask"; 54 55 @Override 56 protected RunnerResult doInBackground(SyncRunner... runner) { 57 RunnerResult result = new RunnerResult(); 58 try { 59 if (runner.length > 1) { 60 throw new RuntimeException("More than one runner received!"); 61 } 62 63 runner[0].assertReadyToStep(); 64 65 // Preparation 66 if (mLogVerbose) Log.v(TAG, "Starting background graph processing."); 67 activateGlContext(); 68 69 if (mLogVerbose) Log.v(TAG, "Preparing filter graph for processing."); 70 runner[0].beginProcessing(); 71 72 if (mLogVerbose) Log.v(TAG, "Running graph."); 73 74 // Run loop 75 result.status = RESULT_RUNNING; 76 while (!isCancelled() && result.status == RESULT_RUNNING) { 77 if (!runner[0].performStep()) { 78 result.status = runner[0].determinePostRunState(); 79 if (result.status == GraphRunner.RESULT_SLEEPING) { 80 runner[0].waitUntilWake(); 81 result.status = RESULT_RUNNING; 82 } 83 } 84 } 85 86 // Cleanup 87 if (isCancelled()) { 88 result.status = RESULT_STOPPED; 89 } 90 } catch (Exception exception) { 91 result.exception = exception; 92 result.status = RESULT_ERROR; 93 } 94 95 // Deactivate context. 96 try { 97 deactivateGlContext(); 98 } catch (Exception exception) { 99 result.exception = exception; 100 result.status = RESULT_ERROR; 101 } 102 103 if (mLogVerbose) Log.v(TAG, "Done with background graph processing."); 104 return result; 105 } 106 107 @Override 108 protected void onCancelled(RunnerResult result) { 109 onPostExecute(result); 110 } 111 112 @Override 113 protected void onPostExecute(RunnerResult result) { 114 if (mLogVerbose) Log.v(TAG, "Starting post-execute."); 115 setRunning(false); 116 if (result == null) { 117 // Cancelled before got to doInBackground 118 result = new RunnerResult(); 119 result.status = RESULT_STOPPED; 120 } 121 setException(result.exception); 122 if (result.status == RESULT_STOPPED || result.status == RESULT_ERROR) { 123 if (mLogVerbose) Log.v(TAG, "Closing filters."); 124 try { 125 mRunner.close(); 126 } catch (Exception exception) { 127 result.status = RESULT_ERROR; 128 setException(exception); 129 } 130 } 131 if (mDoneListener != null) { 132 if (mLogVerbose) Log.v(TAG, "Calling graph done callback."); 133 mDoneListener.onRunnerDone(result.status); 134 } 135 if (mLogVerbose) Log.v(TAG, "Completed post-execute."); 136 } 137 } 138 139 private boolean mLogVerbose; 140 private static final String TAG = "AsyncRunner"; 141 142 /** Create a new asynchronous graph runner with the given filter 143 * context, and the given scheduler class. 144 * 145 * Must be created on the UI thread. 146 */ 147 public AsyncRunner(FilterContext context, Class schedulerClass) { 148 super(context); 149 150 mSchedulerClass = schedulerClass; 151 mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE); 152 } 153 154 /** Create a new asynchronous graph runner with the given filter 155 * context. Uses a default scheduler. 156 * 157 * Must be created on the UI thread. 158 */ 159 public AsyncRunner(FilterContext context) { 160 super(context); 161 162 mSchedulerClass = SimpleScheduler.class; 163 mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE); 164 } 165 166 /** Set a callback to be called in the UI thread once the AsyncRunner 167 * completes running a graph, whether the completion is due to a stop() call 168 * or the filters running out of data to process. 169 */ 170 @Override 171 public void setDoneCallback(OnRunnerDoneListener listener) { 172 mDoneListener = listener; 173 } 174 175 /** Sets the graph to be run. Will call prepare() on graph. Cannot be called 176 * when a graph is already running. 177 */ 178 synchronized public void setGraph(FilterGraph graph) { 179 if (isRunning()) { 180 throw new RuntimeException("Graph is already running!"); 181 } 182 mRunner = new SyncRunner(mFilterContext, graph, mSchedulerClass); 183 } 184 185 @Override 186 public FilterGraph getGraph() { 187 return mRunner != null ? mRunner.getGraph() : null; 188 } 189 190 /** Execute the graph in a background thread. */ 191 @Override 192 synchronized public void run() { 193 if (mLogVerbose) Log.v(TAG, "Running graph."); 194 setException(null); 195 196 if (isRunning()) { 197 throw new RuntimeException("Graph is already running!"); 198 } 199 if (mRunner == null) { 200 throw new RuntimeException("Cannot run before a graph is set!"); 201 } 202 mRunTask = this.new AsyncRunnerTask(); 203 204 setRunning(true); 205 mRunTask.execute(mRunner); 206 } 207 208 /** Stop graph execution. This is an asynchronous call; register a callback 209 * with setDoneCallback to be notified of when the background processing has 210 * been completed. Calling stop will close the filter graph. */ 211 @Override 212 synchronized public void stop() { 213 if (mRunTask != null && !mRunTask.isCancelled() ) { 214 if (mLogVerbose) Log.v(TAG, "Stopping graph."); 215 mRunTask.cancel(false); 216 } 217 } 218 219 @Override 220 synchronized public void close() { 221 if (isRunning()) { 222 throw new RuntimeException("Cannot close graph while it is running!"); 223 } 224 if (mLogVerbose) Log.v(TAG, "Closing filters."); 225 mRunner.close(); 226 } 227 228 /** Check if background processing is happening */ 229 @Override 230 synchronized public boolean isRunning() { 231 return isProcessing; 232 } 233 234 @Override 235 synchronized public Exception getError() { 236 return mException; 237 } 238 239 synchronized private void setRunning(boolean running) { 240 isProcessing = running; 241 } 242 243 synchronized private void setException(Exception exception) { 244 mException = exception; 245 } 246 247} 248