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.compat.annotation.UnsupportedAppUsage;
21 import android.filterfw.format.ObjectFormat;
22 import android.filterfw.io.GraphIOException;
23 import android.filterfw.io.TextGraphReader;
24 import android.util.Log;
25 
26 import java.io.Serializable;
27 import java.lang.annotation.Annotation;
28 import java.lang.reflect.Field;
29 import java.util.Collection;
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.Map.Entry;
33 import java.util.Set;
34 
35 /**
36  * @hide
37  */
38 public abstract class Filter {
39 
40     static final int STATUS_PREINIT               = 0;
41     static final int STATUS_UNPREPARED            = 1;
42     static final int STATUS_PREPARED              = 2;
43     static final int STATUS_PROCESSING            = 3;
44     static final int STATUS_SLEEPING              = 4;
45     static final int STATUS_FINISHED              = 5;
46     static final int STATUS_ERROR                 = 6;
47     static final int STATUS_RELEASED              = 7;
48 
49     private String mName;
50 
51     private int mInputCount = -1;
52     private int mOutputCount = -1;
53 
54     private HashMap<String, InputPort> mInputPorts;
55     private HashMap<String, OutputPort> mOutputPorts;
56 
57     private HashSet<Frame> mFramesToRelease;
58     private HashMap<String, Frame> mFramesToSet;
59 
60     private int mStatus = 0;
61     private boolean mIsOpen = false;
62     private int mSleepDelay;
63 
64     private long mCurrentTimestamp;
65 
66     private boolean mLogVerbose;
67     private static final String TAG = "Filter";
68 
69     @UnsupportedAppUsage
Filter(String name)70     public Filter(String name) {
71         mName = name;
72         mFramesToRelease = new HashSet<Frame>();
73         mFramesToSet = new HashMap<String, Frame>();
74         mStatus = STATUS_PREINIT;
75 
76         mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
77     }
78 
79     /** Tests to see if a given filter is installed on the system. Requires
80      * full filter package name, including filterpack.
81      */
82     @UnsupportedAppUsage
isAvailable(String filterName)83     public static final boolean isAvailable(String filterName) {
84         ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
85         Class filterClass;
86         // First see if a class of that name exists
87         try {
88             filterClass = contextClassLoader.loadClass(filterName);
89         } catch (ClassNotFoundException e) {
90             return false;
91         }
92         // Then make sure it's a subclass of Filter.
93         if (!Filter.class.isAssignableFrom(filterClass)) {
94             return false;
95         }
96         return true;
97     }
98 
initWithValueMap(KeyValueMap valueMap)99     public final void initWithValueMap(KeyValueMap valueMap) {
100         // Initialization
101         initFinalPorts(valueMap);
102 
103         // Setup remaining ports
104         initRemainingPorts(valueMap);
105 
106         // This indicates that final ports can no longer be set
107         mStatus = STATUS_UNPREPARED;
108     }
109 
initWithAssignmentString(String assignments)110     public final void initWithAssignmentString(String assignments) {
111         try {
112             KeyValueMap valueMap = new TextGraphReader().readKeyValueAssignments(assignments);
113             initWithValueMap(valueMap);
114         } catch (GraphIOException e) {
115             throw new IllegalArgumentException(e.getMessage());
116         }
117     }
118 
initWithAssignmentList(Object... keyValues)119     public final void initWithAssignmentList(Object... keyValues) {
120         KeyValueMap valueMap = new KeyValueMap();
121         valueMap.setKeyValues(keyValues);
122         initWithValueMap(valueMap);
123     }
124 
init()125     public final void init() throws ProtocolException {
126         KeyValueMap valueMap = new KeyValueMap();
127         initWithValueMap(valueMap);
128     }
129 
getFilterClassName()130     public String getFilterClassName() {
131         return getClass().getSimpleName();
132     }
133 
getName()134     public final String getName() {
135         return mName;
136     }
137 
isOpen()138     public boolean isOpen() {
139         return mIsOpen;
140     }
141 
setInputFrame(String inputName, Frame frame)142     public void setInputFrame(String inputName, Frame frame) {
143         FilterPort port = getInputPort(inputName);
144         if (!port.isOpen()) {
145             port.open();
146         }
147         port.setFrame(frame);
148     }
149 
150     @UnsupportedAppUsage
setInputValue(String inputName, Object value)151     public final void setInputValue(String inputName, Object value) {
152         setInputFrame(inputName, wrapInputValue(inputName, value));
153     }
154 
prepare(FilterContext context)155     protected void prepare(FilterContext context) {
156     }
157 
parametersUpdated(Set<String> updated)158     protected void parametersUpdated(Set<String> updated) {
159     }
160 
delayNextProcess(int millisecs)161     protected void delayNextProcess(int millisecs) {
162         mSleepDelay = millisecs;
163         mStatus = STATUS_SLEEPING;
164     }
165 
setupPorts()166     public abstract void setupPorts();
167 
getOutputFormat(String portName, FrameFormat inputFormat)168     public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
169         return null;
170     }
171 
getInputFormat(String portName)172     public final FrameFormat getInputFormat(String portName) {
173         InputPort inputPort = getInputPort(portName);
174         return inputPort.getSourceFormat();
175     }
176 
open(FilterContext context)177     public void open(FilterContext context) {
178     }
179 
process(FilterContext context)180     public abstract void process(FilterContext context);
181 
getSleepDelay()182     public final int getSleepDelay() {
183         return 250;
184     }
185 
close(FilterContext context)186     public void close(FilterContext context) {
187     }
188 
tearDown(FilterContext context)189     public void tearDown(FilterContext context) {
190     }
191 
getNumberOfConnectedInputs()192     public final int getNumberOfConnectedInputs() {
193         int c = 0;
194         for (InputPort inputPort : mInputPorts.values()) {
195             if (inputPort.isConnected()) {
196                 ++c;
197             }
198         }
199         return c;
200     }
201 
getNumberOfConnectedOutputs()202     public final int getNumberOfConnectedOutputs() {
203         int c = 0;
204         for (OutputPort outputPort : mOutputPorts.values()) {
205             if (outputPort.isConnected()) {
206                 ++c;
207             }
208         }
209         return c;
210     }
211 
getNumberOfInputs()212     public final int getNumberOfInputs() {
213         return mOutputPorts == null ? 0 : mInputPorts.size();
214     }
215 
getNumberOfOutputs()216     public final int getNumberOfOutputs() {
217         return mInputPorts == null ? 0 : mOutputPorts.size();
218     }
219 
getInputPort(String portName)220     public final InputPort getInputPort(String portName) {
221         if (mInputPorts == null) {
222             throw new NullPointerException("Attempting to access input port '" + portName
223                 + "' of " + this + " before Filter has been initialized!");
224         }
225         InputPort result = mInputPorts.get(portName);
226         if (result == null) {
227             throw new IllegalArgumentException("Unknown input port '" + portName + "' on filter "
228                 + this + "!");
229         }
230         return result;
231     }
232 
getOutputPort(String portName)233     public final OutputPort getOutputPort(String portName) {
234         if (mInputPorts == null) {
235             throw new NullPointerException("Attempting to access output port '" + portName
236                 + "' of " + this + " before Filter has been initialized!");
237         }
238         OutputPort result = mOutputPorts.get(portName);
239         if (result == null) {
240             throw new IllegalArgumentException("Unknown output port '" + portName + "' on filter "
241                 + this + "!");
242         }
243         return result;
244     }
245 
pushOutput(String name, Frame frame)246     protected final void pushOutput(String name, Frame frame) {
247         if (frame.getTimestamp() == Frame.TIMESTAMP_NOT_SET) {
248             if (mLogVerbose) Log.v(TAG, "Default-setting output Frame timestamp on port " + name + " to " + mCurrentTimestamp);
249             frame.setTimestamp(mCurrentTimestamp);
250         }
251         getOutputPort(name).pushFrame(frame);
252     }
253 
pullInput(String name)254     protected final Frame pullInput(String name) {
255         Frame result = getInputPort(name).pullFrame();
256         if (mCurrentTimestamp == Frame.TIMESTAMP_UNKNOWN) {
257             mCurrentTimestamp = result.getTimestamp();
258             if (mLogVerbose) Log.v(TAG, "Default-setting current timestamp from input port " + name + " to " + mCurrentTimestamp);
259         }
260         // As result is retained, we add it to the release pool here
261         mFramesToRelease.add(result);
262 
263         return result;
264     }
265 
fieldPortValueUpdated(String name, FilterContext context)266     public void fieldPortValueUpdated(String name, FilterContext context) {
267     }
268 
269     /**
270      * Transfers any frame from an input port to its destination. This is useful to force a
271      * transfer from a FieldPort or ProgramPort to its connected target (field or program variable).
272      */
transferInputPortFrame(String name, FilterContext context)273     protected void transferInputPortFrame(String name, FilterContext context) {
274         getInputPort(name).transfer(context);
275     }
276 
277     /**
278      * Assigns all program variables to the ports they are connected to. Call this after
279      * constructing a Program instance with attached ProgramPorts.
280      */
initProgramInputs(Program program, FilterContext context)281     protected void initProgramInputs(Program program, FilterContext context) {
282         if (program != null) {
283             for (InputPort inputPort : mInputPorts.values()) {
284                 if (inputPort.getTarget() == program) {
285                     inputPort.transfer(context);
286                 }
287             }
288         }
289     }
290 
291     /**
292      * Adds an input port to the filter. You should call this from within setupPorts, if your
293      * filter has input ports. No type-checking is performed on the input. If you would like to
294      * check against a type mask, use
295      * {@link #addMaskedInputPort(String, FrameFormat) addMaskedInputPort} instead.
296      *
297      * @param name the name of the input port
298      */
addInputPort(String name)299     protected void addInputPort(String name) {
300         addMaskedInputPort(name, null);
301     }
302 
303     /**
304      * Adds an input port to the filter. You should call this from within setupPorts, if your
305      * filter has input ports. When type-checking is performed, the input format is
306      * checked against the provided format mask. An exception is thrown in case of a conflict.
307      *
308      * @param name the name of the input port
309      * @param formatMask a format mask, which filters the allowable input types
310      */
addMaskedInputPort(String name, FrameFormat formatMask)311     protected void addMaskedInputPort(String name, FrameFormat formatMask) {
312         InputPort port = new StreamPort(this, name);
313         if (mLogVerbose) Log.v(TAG, "Filter " + this + " adding " + port);
314         mInputPorts.put(name, port);
315         port.setPortFormat(formatMask);
316     }
317 
318     /**
319      * Adds an output port to the filter with a fixed output format. You should call this from
320      * within setupPorts, if your filter has output ports. You cannot use this method, if your
321      * output format depends on the input format (e.g. in a pass-through filter). In this case, use
322      * {@link #addOutputBasedOnInput(String, String) addOutputBasedOnInput} instead.
323      *
324      * @param name the name of the output port
325      * @param format the fixed output format of this port
326      */
addOutputPort(String name, FrameFormat format)327     protected void addOutputPort(String name, FrameFormat format) {
328         OutputPort port = new OutputPort(this, name);
329         if (mLogVerbose) Log.v(TAG, "Filter " + this + " adding " + port);
330         port.setPortFormat(format);
331         mOutputPorts.put(name, port);
332     }
333 
334     /**
335      * Adds an output port to the filter. You should call this from within setupPorts, if your
336      * filter has output ports. Using this method indicates that the output format for this
337      * particular port, depends on the format of an input port. You MUST also override
338      * {@link #getOutputFormat(String, FrameFormat) getOutputFormat} to specify what format your
339      * filter will output for a given input. If the output format of your filter port does not
340      * depend on the input, use {@link #addOutputPort(String, FrameFormat) addOutputPort} instead.
341      *
342      * @param outputName the name of the output port
343      * @param inputName the name of the input port, that this output depends on
344      */
addOutputBasedOnInput(String outputName, String inputName)345     protected void addOutputBasedOnInput(String outputName, String inputName) {
346         OutputPort port = new OutputPort(this, outputName);
347         if (mLogVerbose) Log.v(TAG, "Filter " + this + " adding " + port);
348         port.setBasePort(getInputPort(inputName));
349         mOutputPorts.put(outputName, port);
350     }
351 
addFieldPort(String name, Field field, boolean hasDefault, boolean isFinal)352     protected void addFieldPort(String name,
353                                 Field field,
354                                 boolean hasDefault,
355                                 boolean isFinal) {
356         // Make sure field is accessible
357         field.setAccessible(true);
358 
359         // Create port for this input
360         InputPort fieldPort = isFinal
361             ? new FinalPort(this, name, field, hasDefault)
362             : new FieldPort(this, name, field, hasDefault);
363 
364         // Create format for this input
365         if (mLogVerbose) Log.v(TAG, "Filter " + this + " adding " + fieldPort);
366         MutableFrameFormat format = ObjectFormat.fromClass(field.getType(),
367                                                            FrameFormat.TARGET_SIMPLE);
368         fieldPort.setPortFormat(format);
369 
370         // Add port
371         mInputPorts.put(name, fieldPort);
372     }
373 
addProgramPort(String name, String varName, Field field, Class varType, boolean hasDefault)374     protected void addProgramPort(String name,
375                                   String varName,
376                                   Field field,
377                                   Class varType,
378                                   boolean hasDefault) {
379         // Make sure field is accessible
380         field.setAccessible(true);
381 
382         // Create port for this input
383         InputPort programPort = new ProgramPort(this, name, varName, field, hasDefault);
384 
385         // Create format for this input
386         if (mLogVerbose) Log.v(TAG, "Filter " + this + " adding " + programPort);
387         MutableFrameFormat format = ObjectFormat.fromClass(varType,
388                                                            FrameFormat.TARGET_SIMPLE);
389         programPort.setPortFormat(format);
390 
391         // Add port
392         mInputPorts.put(name, programPort);
393     }
394 
closeOutputPort(String name)395     protected void closeOutputPort(String name) {
396         getOutputPort(name).close();
397     }
398 
399     /**
400      * Specifies whether the filter should not be scheduled until a frame is available on that
401      * input port. Note, that setting this to false, does not block a new frame from coming in
402      * (though there is no necessity to pull that frame for processing).
403      * @param portName the name of the input port.
404      * @param waits true, if the filter should wait for a frame on this port.
405      */
setWaitsOnInputPort(String portName, boolean waits)406     protected void setWaitsOnInputPort(String portName, boolean waits) {
407         getInputPort(portName).setBlocking(waits);
408     }
409 
410     /**
411      * Specifies whether the filter should not be scheduled until the output port is free, i.e.
412      * there is no frame waiting on that output.
413      * @param portName the name of the output port.
414      * @param waits true, if the filter should wait for the port to become free.
415      */
setWaitsOnOutputPort(String portName, boolean waits)416     protected void setWaitsOnOutputPort(String portName, boolean waits) {
417         getOutputPort(portName).setBlocking(waits);
418     }
419 
toString()420     public String toString() {
421         return "'" + getName() + "' (" + getFilterClassName() + ")";
422     }
423 
424     // Core internal methods ///////////////////////////////////////////////////////////////////////
getInputPorts()425     final Collection<InputPort> getInputPorts() {
426         return mInputPorts.values();
427     }
428 
getOutputPorts()429     final Collection<OutputPort> getOutputPorts() {
430         return mOutputPorts.values();
431     }
432 
getStatus()433     final synchronized int getStatus() {
434         return mStatus;
435     }
436 
unsetStatus(int flag)437     final synchronized void unsetStatus(int flag) {
438         mStatus &= ~flag;
439     }
440 
performOpen(FilterContext context)441     final synchronized void performOpen(FilterContext context) {
442         if (!mIsOpen) {
443             if (mStatus == STATUS_UNPREPARED) {
444                 if (mLogVerbose) Log.v(TAG, "Preparing " + this);
445                 prepare(context);
446                 mStatus = STATUS_PREPARED;
447             }
448             if (mStatus == STATUS_PREPARED) {
449                 if (mLogVerbose) Log.v(TAG, "Opening " + this);
450                 open(context);
451                 mStatus = STATUS_PROCESSING;
452             }
453             if (mStatus != STATUS_PROCESSING) {
454                 throw new RuntimeException("Filter " + this + " was brought into invalid state during "
455                     + "opening (state: " + mStatus + ")!");
456             }
457             mIsOpen = true;
458         }
459     }
460 
performProcess(FilterContext context)461     final synchronized void performProcess(FilterContext context) {
462         if (mStatus == STATUS_RELEASED) {
463             throw new RuntimeException("Filter " + this + " is already torn down!");
464         }
465         transferInputFrames(context);
466         if (mStatus < STATUS_PROCESSING) {
467             performOpen(context);
468         }
469         if (mLogVerbose) Log.v(TAG, "Processing " + this);
470         mCurrentTimestamp = Frame.TIMESTAMP_UNKNOWN;
471         process(context);
472         releasePulledFrames(context);
473         if (filterMustClose()) {
474             performClose(context);
475         }
476     }
477 
performClose(FilterContext context)478     final synchronized void performClose(FilterContext context) {
479         if (mIsOpen) {
480             if (mLogVerbose) Log.v(TAG, "Closing " + this);
481             mIsOpen = false;
482             mStatus = STATUS_PREPARED;
483             close(context);
484             closePorts();
485         }
486     }
487 
performTearDown(FilterContext context)488     final synchronized void performTearDown(FilterContext context) {
489         performClose(context);
490         if (mStatus != STATUS_RELEASED) {
491             tearDown(context);
492             mStatus = STATUS_RELEASED;
493         }
494     }
495 
canProcess()496     synchronized final boolean canProcess() {
497         if (mLogVerbose) Log.v(TAG, "Checking if can process: " + this + " (" + mStatus + ").");
498         if (mStatus <= STATUS_PROCESSING) {
499             return inputConditionsMet() && outputConditionsMet();
500         } else {
501             return false;
502         }
503     }
504 
openOutputs()505     final void openOutputs() {
506         if (mLogVerbose) Log.v(TAG, "Opening all output ports on " + this + "!");
507         for (OutputPort outputPort : mOutputPorts.values()) {
508             if (!outputPort.isOpen()) {
509                 outputPort.open();
510             }
511         }
512     }
513 
clearInputs()514     final void clearInputs() {
515         for (InputPort inputPort : mInputPorts.values()) {
516             inputPort.clear();
517         }
518     }
519 
clearOutputs()520     final void clearOutputs() {
521         for (OutputPort outputPort : mOutputPorts.values()) {
522             outputPort.clear();
523         }
524     }
525 
notifyFieldPortValueUpdated(String name, FilterContext context)526     final void notifyFieldPortValueUpdated(String name, FilterContext context) {
527         if (mStatus == STATUS_PROCESSING || mStatus == STATUS_PREPARED) {
528             fieldPortValueUpdated(name, context);
529         }
530     }
531 
pushInputFrame(String inputName, Frame frame)532     final synchronized void pushInputFrame(String inputName, Frame frame) {
533         FilterPort port = getInputPort(inputName);
534         if (!port.isOpen()) {
535             port.open();
536         }
537         port.pushFrame(frame);
538     }
539 
pushInputValue(String inputName, Object value)540     final synchronized void pushInputValue(String inputName, Object value) {
541         pushInputFrame(inputName, wrapInputValue(inputName, value));
542     }
543 
544     // Filter internal methods /////////////////////////////////////////////////////////////////////
initFinalPorts(KeyValueMap values)545     private final void initFinalPorts(KeyValueMap values) {
546         mInputPorts = new HashMap<String, InputPort>();
547         mOutputPorts = new HashMap<String, OutputPort>();
548         addAndSetFinalPorts(values);
549     }
550 
initRemainingPorts(KeyValueMap values)551     private final void initRemainingPorts(KeyValueMap values) {
552         addAnnotatedPorts();
553         setupPorts();   // TODO: rename to addFilterPorts() ?
554         setInitialInputValues(values);
555     }
556 
addAndSetFinalPorts(KeyValueMap values)557     private final void addAndSetFinalPorts(KeyValueMap values) {
558         Class filterClass = getClass();
559         Annotation annotation;
560         for (Field field : filterClass.getDeclaredFields()) {
561             if ((annotation = field.getAnnotation(GenerateFinalPort.class)) != null) {
562                 GenerateFinalPort generator = (GenerateFinalPort)annotation;
563                 String name = generator.name().isEmpty() ? field.getName() : generator.name();
564                 boolean hasDefault = generator.hasDefault();
565                 addFieldPort(name, field, hasDefault, true);
566                 if (values.containsKey(name)) {
567                     setImmediateInputValue(name, values.get(name));
568                     values.remove(name);
569                 } else if (!generator.hasDefault()) {
570                     throw new RuntimeException("No value specified for final input port '"
571                         + name + "' of filter " + this + "!");
572                 }
573             }
574         }
575     }
576 
addAnnotatedPorts()577     private final void addAnnotatedPorts() {
578         Class filterClass = getClass();
579         Annotation annotation;
580         for (Field field : filterClass.getDeclaredFields()) {
581             if ((annotation = field.getAnnotation(GenerateFieldPort.class)) != null) {
582                 GenerateFieldPort generator = (GenerateFieldPort)annotation;
583                 addFieldGenerator(generator, field);
584             } else if ((annotation = field.getAnnotation(GenerateProgramPort.class)) != null) {
585                 GenerateProgramPort generator = (GenerateProgramPort)annotation;
586                 addProgramGenerator(generator, field);
587             } else if ((annotation = field.getAnnotation(GenerateProgramPorts.class)) != null) {
588                 GenerateProgramPorts generators = (GenerateProgramPorts)annotation;
589                 for (GenerateProgramPort generator : generators.value()) {
590                     addProgramGenerator(generator, field);
591                 }
592             }
593         }
594     }
595 
addFieldGenerator(GenerateFieldPort generator, Field field)596     private final void addFieldGenerator(GenerateFieldPort generator, Field field) {
597         String name = generator.name().isEmpty() ? field.getName() : generator.name();
598         boolean hasDefault = generator.hasDefault();
599         addFieldPort(name, field, hasDefault, false);
600     }
601 
addProgramGenerator(GenerateProgramPort generator, Field field)602     private final void addProgramGenerator(GenerateProgramPort generator, Field field) {
603         String name = generator.name();
604         String varName = generator.variableName().isEmpty() ? name
605                                                             : generator.variableName();
606         Class varType = generator.type();
607         boolean hasDefault = generator.hasDefault();
608         addProgramPort(name, varName, field, varType, hasDefault);
609     }
610 
setInitialInputValues(KeyValueMap values)611     private final void setInitialInputValues(KeyValueMap values) {
612         for (Entry<String, Object> entry : values.entrySet()) {
613             setInputValue(entry.getKey(), entry.getValue());
614         }
615     }
616 
setImmediateInputValue(String name, Object value)617     private final void setImmediateInputValue(String name, Object value) {
618         if (mLogVerbose) Log.v(TAG, "Setting immediate value " + value + " for port " + name + "!");
619         FilterPort port = getInputPort(name);
620         port.open();
621         port.setFrame(SimpleFrame.wrapObject(value, null));
622     }
623 
transferInputFrames(FilterContext context)624     private final void transferInputFrames(FilterContext context) {
625         for (InputPort inputPort : mInputPorts.values()) {
626             inputPort.transfer(context);
627         }
628     }
629 
wrapInputValue(String inputName, Object value)630     private final Frame wrapInputValue(String inputName, Object value) {
631         MutableFrameFormat inputFormat = ObjectFormat.fromObject(value, FrameFormat.TARGET_SIMPLE);
632         if (value == null) {
633             // If the value is null, the format cannot guess the class, so we adjust it to the
634             // class of the input port here
635             FrameFormat portFormat = getInputPort(inputName).getPortFormat();
636             Class portClass = (portFormat == null) ? null : portFormat.getObjectClass();
637             inputFormat.setObjectClass(portClass);
638         }
639 
640         // Serialize if serializable, and type is not an immutable primitive.
641         boolean shouldSerialize = !(value instanceof Number)
642             && !(value instanceof Boolean)
643             && !(value instanceof String)
644             && value instanceof Serializable;
645 
646         // Create frame wrapper
647         Frame frame = shouldSerialize
648             ? new SerializedFrame(inputFormat, null)
649             : new SimpleFrame(inputFormat, null);
650         frame.setObjectValue(value);
651         return frame;
652     }
653 
releasePulledFrames(FilterContext context)654     private final void releasePulledFrames(FilterContext context) {
655         for (Frame frame : mFramesToRelease) {
656             context.getFrameManager().releaseFrame(frame);
657         }
658         mFramesToRelease.clear();
659     }
660 
inputConditionsMet()661     private final boolean inputConditionsMet() {
662         for (FilterPort port : mInputPorts.values()) {
663             if (!port.isReady()) {
664                 if (mLogVerbose) Log.v(TAG, "Input condition not met: " + port + "!");
665                 return false;
666             }
667         }
668         return true;
669     }
670 
outputConditionsMet()671     private final boolean outputConditionsMet() {
672         for (FilterPort port : mOutputPorts.values()) {
673             if (!port.isReady()) {
674                 if (mLogVerbose) Log.v(TAG, "Output condition not met: " + port + "!");
675                 return false;
676             }
677         }
678         return true;
679     }
680 
closePorts()681     private final void closePorts() {
682         if (mLogVerbose) Log.v(TAG, "Closing all ports on " + this + "!");
683         for (InputPort inputPort : mInputPorts.values()) {
684             inputPort.close();
685         }
686         for (OutputPort outputPort : mOutputPorts.values()) {
687             outputPort.close();
688         }
689     }
690 
filterMustClose()691     private final boolean filterMustClose() {
692         for (InputPort inputPort : mInputPorts.values()) {
693             if (inputPort.filterMustClose()) {
694                 if (mLogVerbose) Log.v(TAG, "Filter " + this + " must close due to port " + inputPort);
695                 return true;
696             }
697         }
698         for (OutputPort outputPort : mOutputPorts.values()) {
699             if (outputPort.filterMustClose()) {
700                 if (mLogVerbose) Log.v(TAG, "Filter " + this + " must close due to port " + outputPort);
701                 return true;
702             }
703         }
704         return false;
705     }
706 }
707