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