OSDN Git Service

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