WindowContainer.java revision 6191149c9c8d41a08c0167d94d21ac463c144b71
1/*
2 * Copyright (C) 2016 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
17package com.android.server.wm;
18
19import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
20import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
21import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
22import static com.android.server.wm.proto.WindowContainerProto.CONFIGURATION_CONTAINER;
23import static com.android.server.wm.proto.WindowContainerProto.ORIENTATION;
24
25import android.annotation.CallSuper;
26import android.content.res.Configuration;
27import android.util.Pools;
28
29import android.util.proto.ProtoOutputStream;
30import com.android.internal.util.ToBooleanFunction;
31
32import java.util.Comparator;
33import java.util.LinkedList;
34import java.util.function.Consumer;
35import java.util.function.Predicate;
36
37/**
38 * Defines common functionality for classes that can hold windows directly or through their
39 * children in a hierarchy form.
40 * The test class is {@link WindowContainerTests} which must be kept up-to-date and ran anytime
41 * changes are made to this class.
42 */
43class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
44        implements Comparable<WindowContainer> {
45
46    static final int POSITION_TOP = Integer.MAX_VALUE;
47    static final int POSITION_BOTTOM = Integer.MIN_VALUE;
48
49    /**
50     * The parent of this window container.
51     * For removing or setting new parent {@link #setParent} should be used, because it also
52     * performs configuration updates based on new parent's settings.
53     */
54    private WindowContainer mParent = null;
55
56    // List of children for this window container. List is in z-order as the children appear on
57    // screen with the top-most window container at the tail of the list.
58    protected final WindowList<E> mChildren = new WindowList<E>();
59
60    // The specified orientation for this window container.
61    protected int mOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
62
63    private final Pools.SynchronizedPool<ForAllWindowsConsumerWrapper> mConsumerWrapperPool =
64            new Pools.SynchronizedPool<>(3);
65
66    // The owner/creator for this container. No controller if null.
67    private WindowContainerController mController;
68
69    @Override
70    final protected WindowContainer getParent() {
71        return mParent;
72    }
73
74
75    @Override
76    protected int getChildCount() {
77        return mChildren.size();
78    }
79
80    @Override
81    protected E getChildAt(int index) {
82        return mChildren.get(index);
83    }
84
85    final protected void setParent(WindowContainer parent) {
86        mParent = parent;
87        // Removing parent usually means that we've detached this entity to destroy it or to attach
88        // to another parent. In both cases we don't need to update the configuration now.
89        if (mParent != null) {
90            // Update full configuration of this container and all its children.
91            onConfigurationChanged(mParent.getConfiguration());
92            // Update merged override configuration of this container and all its children.
93            onMergedOverrideConfigurationChanged();
94        }
95
96        onParentSet();
97    }
98
99    /**
100     * Callback that is triggered when @link WindowContainer#setParent(WindowContainer)} was called.
101     * Supposed to be overridden and contain actions that should be executed after parent was set.
102     */
103    void onParentSet() {
104        // Do nothing by default.
105    }
106
107    // Temp. holders for a chain of containers we are currently processing.
108    private final LinkedList<WindowContainer> mTmpChain1 = new LinkedList();
109    private final LinkedList<WindowContainer> mTmpChain2 = new LinkedList();
110
111    /**
112     * Adds the input window container has a child of this container in order based on the input
113     * comparator.
114     * @param child The window container to add as a child of this window container.
115     * @param comparator Comparator to use in determining the position the child should be added to.
116     *                   If null, the child will be added to the top.
117     */
118    @CallSuper
119    protected void addChild(E child, Comparator<E> comparator) {
120        if (child.getParent() != null) {
121            throw new IllegalArgumentException("addChild: container=" + child.getName()
122                    + " is already a child of container=" + child.getParent().getName()
123                    + " can't add to container=" + getName());
124        }
125
126        int positionToAdd = -1;
127        if (comparator != null) {
128            final int count = mChildren.size();
129            for (int i = 0; i < count; i++) {
130                if (comparator.compare(child, mChildren.get(i)) < 0) {
131                    positionToAdd = i;
132                    break;
133                }
134            }
135        }
136
137        if (positionToAdd == -1) {
138            mChildren.add(child);
139        } else {
140            mChildren.add(positionToAdd, child);
141        }
142        // Set the parent after we've actually added a child in case a subclass depends on this.
143        child.setParent(this);
144    }
145
146    /** Adds the input window container has a child of this container at the input index. */
147    @CallSuper
148    void addChild(E child, int index) {
149        if (child.getParent() != null) {
150            throw new IllegalArgumentException("addChild: container=" + child.getName()
151                    + " is already a child of container=" + child.getParent().getName()
152                    + " can't add to container=" + getName());
153        }
154        mChildren.add(index, child);
155        // Set the parent after we've actually added a child in case a subclass depends on this.
156        child.setParent(this);
157    }
158
159    /**
160     * Removes the input child container from this container which is its parent.
161     *
162     * @return True if the container did contain the input child and it was detached.
163     */
164    @CallSuper
165    void removeChild(E child) {
166        if (mChildren.remove(child)) {
167            child.setParent(null);
168        } else {
169            throw new IllegalArgumentException("removeChild: container=" + child.getName()
170                    + " is not a child of container=" + getName());
171        }
172    }
173
174    /**
175     * Removes this window container and its children with no regard for what else might be going on
176     * in the system. For example, the container will be removed during animation if this method is
177     * called which isn't desirable. For most cases you want to call {@link #removeIfPossible()}
178     * which allows the system to defer removal until a suitable time.
179     */
180    @CallSuper
181    void removeImmediately() {
182        while (!mChildren.isEmpty()) {
183            final WindowContainer child = mChildren.peekLast();
184            child.removeImmediately();
185            // Need to do this after calling remove on the child because the child might try to
186            // remove/detach itself from its parent which will cause an exception if we remove
187            // it before calling remove on the child.
188            mChildren.remove(child);
189        }
190
191        if (mParent != null) {
192            mParent.removeChild(this);
193        }
194
195        if (mController != null) {
196            setController(null);
197        }
198    }
199
200    /**
201     * Removes this window container and its children taking care not to remove them during a
202     * critical stage in the system. For example, some containers will not be removed during
203     * animation if this method is called.
204     */
205    // TODO: figure-out implementation that works best for this.
206    // E.g. when do we remove from parent list? maybe not...
207    void removeIfPossible() {
208        for (int i = mChildren.size() - 1; i >= 0; --i) {
209            final WindowContainer wc = mChildren.get(i);
210            wc.removeIfPossible();
211        }
212    }
213
214    /** Returns true if this window container has the input child. */
215    boolean hasChild(WindowContainer child) {
216        for (int i = mChildren.size() - 1; i >= 0; --i) {
217            final WindowContainer current = mChildren.get(i);
218            if (current == child || current.hasChild(child)) {
219                return true;
220            }
221        }
222        return false;
223    }
224
225    /**
226     * Move a child from it's current place in siblings list to the specified position,
227     * with an option to move all its parents to top.
228     * @param position Target position to move the child to.
229     * @param child Child to move to selected position.
230     * @param includingParents Flag indicating whether we need to move the entire branch of the
231     *                         hierarchy when we're moving a child to {@link #POSITION_TOP} or
232     *                         {@link #POSITION_BOTTOM}. When moving to other intermediate positions
233     *                         this flag will do nothing.
234     */
235    @CallSuper
236    void positionChildAt(int position, E child, boolean includingParents) {
237
238        if (child.getParent() != this) {
239            throw new IllegalArgumentException("removeChild: container=" + child.getName()
240                    + " is not a child of container=" + getName()
241                    + " current parent=" + child.getParent());
242        }
243
244        if ((position < 0 && position != POSITION_BOTTOM)
245                || (position > mChildren.size() && position != POSITION_TOP)) {
246            throw new IllegalArgumentException("positionAt: invalid position=" + position
247                    + ", children number=" + mChildren.size());
248        }
249
250        if (position >= mChildren.size() - 1) {
251            position = POSITION_TOP;
252        } else if (position == 0) {
253            position = POSITION_BOTTOM;
254        }
255
256        switch (position) {
257            case POSITION_TOP:
258                if (mChildren.peekLast() != child) {
259                    mChildren.remove(child);
260                    mChildren.add(child);
261                }
262                if (includingParents && getParent() != null) {
263                    getParent().positionChildAt(POSITION_TOP, this /* child */,
264                            true /* includingParents */);
265                }
266                break;
267            case POSITION_BOTTOM:
268                if (mChildren.peekFirst() != child) {
269                    mChildren.remove(child);
270                    mChildren.addFirst(child);
271                }
272                if (includingParents && getParent() != null) {
273                    getParent().positionChildAt(POSITION_BOTTOM, this /* child */,
274                            true /* includingParents */);
275                }
276                break;
277            default:
278                mChildren.remove(child);
279                mChildren.add(position, child);
280        }
281    }
282
283    /**
284     * Update override configuration and recalculate full config.
285     * @see #mOverrideConfiguration
286     * @see #mFullConfiguration
287     */
288    @Override
289    final public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
290        super.onOverrideConfigurationChanged(overrideConfiguration);
291        if (mParent != null) {
292            mParent.onDescendantOverrideConfigurationChanged();
293        }
294    }
295
296    /**
297     * Notify that a descendant's overrideConfiguration has changed.
298     */
299    void onDescendantOverrideConfigurationChanged() {
300        if (mParent != null) {
301            mParent.onDescendantOverrideConfigurationChanged();
302        }
303    }
304
305    /**
306     * Notify that the display this container is on has changed.
307     * @param dc The new display this container is on.
308     */
309    void onDisplayChanged(DisplayContent dc) {
310        for (int i = mChildren.size() - 1; i >= 0; --i) {
311            final WindowContainer child = mChildren.get(i);
312            child.onDisplayChanged(dc);
313        }
314    }
315
316    void setWaitingForDrawnIfResizingChanged() {
317        for (int i = mChildren.size() - 1; i >= 0; --i) {
318            final WindowContainer wc = mChildren.get(i);
319            wc.setWaitingForDrawnIfResizingChanged();
320        }
321    }
322
323    void onResize() {
324        for (int i = mChildren.size() - 1; i >= 0; --i) {
325            final WindowContainer wc = mChildren.get(i);
326            wc.onResize();
327        }
328    }
329
330    void onMovedByResize() {
331        for (int i = mChildren.size() - 1; i >= 0; --i) {
332            final WindowContainer wc = mChildren.get(i);
333            wc.onMovedByResize();
334        }
335    }
336
337    void resetDragResizingChangeReported() {
338        for (int i = mChildren.size() - 1; i >= 0; --i) {
339            final WindowContainer wc = mChildren.get(i);
340            wc.resetDragResizingChangeReported();
341        }
342    }
343
344    void forceWindowsScaleableInTransaction(boolean force) {
345        for (int i = mChildren.size() - 1; i >= 0; --i) {
346            final WindowContainer wc = mChildren.get(i);
347            wc.forceWindowsScaleableInTransaction(force);
348        }
349    }
350
351    boolean isAnimating() {
352        for (int j = mChildren.size() - 1; j >= 0; j--) {
353            final WindowContainer wc = mChildren.get(j);
354            if (wc.isAnimating()) {
355                return true;
356            }
357        }
358        return false;
359    }
360
361    void sendAppVisibilityToClients() {
362        for (int i = mChildren.size() - 1; i >= 0; --i) {
363            final WindowContainer wc = mChildren.get(i);
364            wc.sendAppVisibilityToClients();
365        }
366    }
367
368    /**
369     * Returns true if the container or one of its children as some content it can display or wants
370     * to display (e.g. app views or saved surface).
371     *
372     * NOTE: While this method will return true if the there is some content to display, it doesn't
373     * mean the container is visible. Use {@link #isVisible()} to determine if the container is
374     * visible.
375     */
376    boolean hasContentToDisplay() {
377        for (int i = mChildren.size() - 1; i >= 0; --i) {
378            final WindowContainer wc = mChildren.get(i);
379            if (wc.hasContentToDisplay()) {
380                return true;
381            }
382        }
383        return false;
384    }
385
386    /**
387     * Returns true if the container or one of its children is considered visible from the
388     * WindowManager perspective which usually means valid surface and some other internal state
389     * are true.
390     *
391     * NOTE: While this method will return true if the surface is visible, it doesn't mean the
392     * client has actually displayed any content. Use {@link #hasContentToDisplay()} to determine if
393     * the container has any content to display.
394     */
395    boolean isVisible() {
396        // TODO: Will this be more correct if it checks the visibility of its parents?
397        // It depends...For example, Tasks and Stacks are only visible if there children are visible
398        // but, WindowState are not visible if there parent are not visible. Maybe have the
399        // container specify which direction to traverse for visibility?
400        for (int i = mChildren.size() - 1; i >= 0; --i) {
401            final WindowContainer wc = mChildren.get(i);
402            if (wc.isVisible()) {
403                return true;
404            }
405        }
406        return false;
407    }
408
409    /**
410a     * Returns whether this child is on top of the window hierarchy.
411     */
412    boolean isOnTop() {
413        return getParent().getTopChild() == this && getParent().isOnTop();
414    }
415
416    /** Returns the top child container. */
417    E getTopChild() {
418        return mChildren.peekLast();
419    }
420
421    /** Returns true if there is still a removal being deferred */
422    boolean checkCompleteDeferredRemoval() {
423        boolean stillDeferringRemoval = false;
424
425        for (int i = mChildren.size() - 1; i >= 0; --i) {
426            final WindowContainer wc = mChildren.get(i);
427            stillDeferringRemoval |= wc.checkCompleteDeferredRemoval();
428        }
429
430        return stillDeferringRemoval;
431    }
432
433    /** Checks if all windows in an app are all drawn and shows them if needed. */
434    void checkAppWindowsReadyToShow() {
435        for (int i = mChildren.size() - 1; i >= 0; --i) {
436            final WindowContainer wc = mChildren.get(i);
437            wc.checkAppWindowsReadyToShow();
438        }
439    }
440
441    /** Step currently ongoing animation for App window containers. */
442    void stepAppWindowsAnimation(long currentTime) {
443        for (int i = mChildren.size() - 1; i >= 0; --i) {
444            final WindowContainer wc = mChildren.get(i);
445            wc.stepAppWindowsAnimation(currentTime);
446        }
447    }
448
449    void onAppTransitionDone() {
450        for (int i = mChildren.size() - 1; i >= 0; --i) {
451            final WindowContainer wc = mChildren.get(i);
452            wc.onAppTransitionDone();
453        }
454    }
455
456    void setOrientation(int orientation) {
457        mOrientation = orientation;
458    }
459
460    int getOrientation() {
461        return getOrientation(mOrientation);
462    }
463
464    /**
465     * Returns the specified orientation for this window container or one of its children is there
466     * is one set, or {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSET} if no
467     * specification is set.
468     * NOTE: {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} is a
469     * specification...
470     *
471     * @param candidate The current orientation candidate that will be returned if we don't find a
472     *                  better match.
473     * @return The orientation as specified by this branch or the window hierarchy.
474     */
475    int getOrientation(int candidate) {
476        if (!fillsParent()) {
477            // Ignore containers that don't completely fill their parents.
478            return SCREEN_ORIENTATION_UNSET;
479        }
480
481        // The container fills its parent so we can use it orientation if it has one
482        // specified; otherwise we prefer to use the orientation of its topmost child that has one
483        // specified and fall back on this container's unset or unspecified value as a candidate
484        // if none of the children have a better candidate for the orientation.
485        if (mOrientation != SCREEN_ORIENTATION_UNSET
486                && mOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
487            return mOrientation;
488        }
489
490        for (int i = mChildren.size() - 1; i >= 0; --i) {
491            final WindowContainer wc = mChildren.get(i);
492
493            // TODO: Maybe mOrientation should default to SCREEN_ORIENTATION_UNSET vs.
494            // SCREEN_ORIENTATION_UNSPECIFIED?
495            final int orientation = wc.getOrientation(candidate == SCREEN_ORIENTATION_BEHIND
496                    ? SCREEN_ORIENTATION_BEHIND : SCREEN_ORIENTATION_UNSET);
497            if (orientation == SCREEN_ORIENTATION_BEHIND) {
498                // container wants us to use the orientation of the container behind it. See if we
499                // can find one. Else return SCREEN_ORIENTATION_BEHIND so the caller can choose to
500                // look behind this container.
501                candidate = orientation;
502                continue;
503            }
504
505            if (orientation == SCREEN_ORIENTATION_UNSET) {
506                continue;
507            }
508
509            if (wc.fillsParent() || orientation != SCREEN_ORIENTATION_UNSPECIFIED) {
510                // Use the orientation if the container fills its parent or requested an explicit
511                // orientation that isn't SCREEN_ORIENTATION_UNSPECIFIED.
512                return orientation;
513            }
514        }
515
516        return candidate;
517    }
518
519    /**
520     * Returns true if this container is opaque and fills all the space made available by its parent
521     * container.
522     *
523     * NOTE: It is possible for this container to occupy more space than the parent has (or less),
524     * this is just a signal from the client to window manager stating its intent, but not what it
525     * actually does.
526     */
527    boolean fillsParent() {
528        return false;
529    }
530
531    // TODO: Users would have their own window containers under the display container?
532    void switchUser() {
533        for (int i = mChildren.size() - 1; i >= 0; --i) {
534            mChildren.get(i).switchUser();
535        }
536    }
537
538    /**
539     * For all windows at or below this container call the callback.
540     * @param   callback Calls the {@link ToBooleanFunction#apply} method for each window found and
541     *                   stops the search if {@link ToBooleanFunction#apply} returns true.
542     * @param   traverseTopToBottom If true traverses the hierarchy from top-to-bottom in terms of
543     *                              z-order, else from bottom-to-top.
544     * @return  True if the search ended before we reached the end of the hierarchy due to
545     *          {@link ToBooleanFunction#apply} returning true.
546     */
547    boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
548        if (traverseTopToBottom) {
549            for (int i = mChildren.size() - 1; i >= 0; --i) {
550                if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) {
551                    return true;
552                }
553            }
554        } else {
555            final int count = mChildren.size();
556            for (int i = 0; i < count; i++) {
557                if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) {
558                    return true;
559                }
560            }
561        }
562        return false;
563    }
564
565    void forAllWindows(Consumer<WindowState> callback, boolean traverseTopToBottom) {
566        ForAllWindowsConsumerWrapper wrapper = obtainConsumerWrapper(callback);
567        forAllWindows(wrapper, traverseTopToBottom);
568        wrapper.release();
569    }
570
571    /**
572     * For all tasks at or below this container call the callback.
573     *
574     * @param callback Callback to be called for every task.
575     */
576    void forAllTasks(Consumer<Task> callback) {
577        for (int i = mChildren.size() - 1; i >= 0; --i) {
578            mChildren.get(i).forAllTasks(callback);
579        }
580    }
581
582    WindowState getWindow(Predicate<WindowState> callback) {
583        for (int i = mChildren.size() - 1; i >= 0; --i) {
584            final WindowState w = mChildren.get(i).getWindow(callback);
585            if (w != null) {
586                return w;
587            }
588        }
589
590        return null;
591    }
592
593    /**
594     * Returns 1, 0, or -1 depending on if this container is greater than, equal to, or lesser than
595     * the input container in terms of z-order.
596     */
597    @Override
598    public int compareTo(WindowContainer other) {
599        if (this == other) {
600            return 0;
601        }
602
603        if (mParent != null && mParent == other.mParent) {
604            final WindowList<WindowContainer> list = mParent.mChildren;
605            return list.indexOf(this) > list.indexOf(other) ? 1 : -1;
606        }
607
608        final LinkedList<WindowContainer> thisParentChain = mTmpChain1;
609        final LinkedList<WindowContainer> otherParentChain = mTmpChain2;
610        try {
611            getParents(thisParentChain);
612            other.getParents(otherParentChain);
613
614            // Find the common ancestor of both containers.
615            WindowContainer commonAncestor = null;
616            WindowContainer thisTop = thisParentChain.peekLast();
617            WindowContainer otherTop = otherParentChain.peekLast();
618            while (thisTop != null && otherTop != null && thisTop == otherTop) {
619                commonAncestor = thisParentChain.removeLast();
620                otherParentChain.removeLast();
621                thisTop = thisParentChain.peekLast();
622                otherTop = otherParentChain.peekLast();
623            }
624
625            // Containers don't belong to the same hierarchy???
626            if (commonAncestor == null) {
627                throw new IllegalArgumentException("No in the same hierarchy this="
628                        + thisParentChain + " other=" + otherParentChain);
629            }
630
631            // Children are always considered greater than their parents, so if one of the containers
632            // we are comparing it the parent of the other then whichever is the child is greater.
633            if (commonAncestor == this) {
634                return -1;
635            } else if (commonAncestor == other) {
636                return 1;
637            }
638
639            // The position of the first non-common ancestor in the common ancestor list determines
640            // which is greater the which.
641            final WindowList<WindowContainer> list = commonAncestor.mChildren;
642            return list.indexOf(thisParentChain.peekLast()) > list.indexOf(otherParentChain.peekLast())
643                    ? 1 : -1;
644        } finally {
645            mTmpChain1.clear();
646            mTmpChain2.clear();
647        }
648    }
649
650    private void getParents(LinkedList<WindowContainer> parents) {
651        parents.clear();
652        WindowContainer current = this;
653        do {
654            parents.addLast(current);
655            current = current.mParent;
656        } while (current != null);
657    }
658
659    WindowContainerController getController() {
660        return mController;
661    }
662
663    void setController(WindowContainerController controller) {
664        if (mController != null && controller != null) {
665            throw new IllegalArgumentException("Can't set controller=" + mController
666                    + " for container=" + this + " Already set to=" + mController);
667        }
668        if (controller != null) {
669            controller.setContainer(this);
670        } else if (mController != null) {
671            mController.setContainer(null);
672        }
673        mController = controller;
674    }
675
676    /**
677     * Dumps the names of this container children in the input print writer indenting each
678     * level with the input prefix.
679     */
680    void dumpChildrenNames(StringBuilder out, String prefix) {
681        final String childPrefix = prefix + " ";
682        out.append(getName() + "\n");
683        for (int i = mChildren.size() - 1; i >= 0; --i) {
684            final WindowContainer wc = mChildren.get(i);
685            out.append(childPrefix + "#" + i + " ");
686            wc.dumpChildrenNames(out, childPrefix);
687        }
688    }
689
690    /**
691     * Write to a protocol buffer output stream. Protocol buffer message definition is at
692     * {@link com.android.server.wm.proto.WindowContainerProto}.
693     *
694     * @param protoOutputStream Stream to write the WindowContainer object to.
695     * @param fieldId           Field Id of the WindowContainer as defined in the parent message.
696     * @hide
697     */
698    @CallSuper
699    @Override
700    public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
701        final long token = protoOutputStream.start(fieldId);
702        super.writeToProto(protoOutputStream, CONFIGURATION_CONTAINER);
703        protoOutputStream.write(ORIENTATION, mOrientation);
704        protoOutputStream.end(token);
705    }
706
707    String getName() {
708        return toString();
709    }
710
711    private ForAllWindowsConsumerWrapper obtainConsumerWrapper(Consumer<WindowState> consumer) {
712        ForAllWindowsConsumerWrapper wrapper = mConsumerWrapperPool.acquire();
713        if (wrapper == null) {
714            wrapper = new ForAllWindowsConsumerWrapper();
715        }
716        wrapper.setConsumer(consumer);
717        return wrapper;
718    }
719
720    private final class ForAllWindowsConsumerWrapper implements ToBooleanFunction<WindowState> {
721
722        private Consumer<WindowState> mConsumer;
723
724        void setConsumer(Consumer<WindowState> consumer) {
725            mConsumer = consumer;
726        }
727
728        @Override
729        public boolean apply(WindowState w) {
730            mConsumer.accept(w);
731            return false;
732        }
733
734        void release() {
735            mConsumer = null;
736            mConsumerWrapperPool.release(this);
737        }
738    }
739}
740