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