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