WindowContainer.java revision c5cc301689649695e03f502e7d1c1492ef5e5d1e
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 android.annotation.CallSuper;
20import android.content.res.Configuration;
21import android.util.Pools;
22
23import com.android.internal.util.ToBooleanFunction;
24
25import java.util.Comparator;
26import java.util.LinkedList;
27import java.util.function.Consumer;
28import java.util.function.Predicate;
29
30import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
31import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
32import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
33
34/**
35 * Defines common functionality for classes that can hold windows directly or through their
36 * children in a hierarchy form.
37 * The test class is {@link WindowContainerTests} which must be kept up-to-date and ran anytime
38 * changes are made to this class.
39 */
40class WindowContainer<E extends WindowContainer> implements Comparable<WindowContainer> {
41
42    static final int POSITION_TOP = Integer.MAX_VALUE;
43    static final int POSITION_BOTTOM = Integer.MIN_VALUE;
44
45    /**
46     * The parent of this window container.
47     * For removing or setting new parent {@link #setParent} should be used, because it also
48     * performs configuration updates based on new parent's settings.
49     */
50    private WindowContainer mParent = null;
51
52    // List of children for this window container. List is in z-order as the children appear on
53    // screen with the top-most window container at the tail of the list.
54    protected final LinkedList<E> mChildren = new LinkedList();
55
56    /** Contains override configuration settings applied to this window container. */
57    private Configuration mOverrideConfiguration = new Configuration();
58
59    /**
60     * Contains full configuration applied to this window container. Corresponds to full parent's
61     * config with applied {@link #mOverrideConfiguration}.
62     */
63    private Configuration mFullConfiguration = new Configuration();
64
65    /**
66     * Contains merged override configuration settings from the top of the hierarchy down to this
67     * particular instance. It is different from {@link #mFullConfiguration} because it starts from
68     * topmost container's override config instead of global config.
69     */
70    private Configuration mMergedOverrideConfiguration = new Configuration();
71
72    // The specified orientation for this window container.
73    protected int mOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
74
75    private final Pools.SynchronizedPool<ForAllWindowsConsumerWrapper> mConsumerWrapperPool =
76            new Pools.SynchronizedPool<>(3);
77
78    // The owner/creator for this container. No controller if null.
79    private WindowContainerController mController;
80
81    final protected WindowContainer getParent() {
82        return mParent;
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.mFullConfiguration);
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.addLast(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     * Returns full configuration applied to this window container.
285     * This method should be used for getting settings applied in each particular level of the
286     * hierarchy.
287     */
288    Configuration getConfiguration() {
289        return mFullConfiguration;
290    }
291
292    /**
293     * Notify that parent config changed and we need to update full configuration.
294     * @see #mFullConfiguration
295     */
296    void onConfigurationChanged(Configuration newParentConfig) {
297        mFullConfiguration.setTo(newParentConfig);
298        mFullConfiguration.updateFrom(mOverrideConfiguration);
299        for (int i = mChildren.size() - 1; i >= 0; --i) {
300            final WindowContainer child = mChildren.get(i);
301            child.onConfigurationChanged(mFullConfiguration);
302        }
303    }
304
305    /** Returns override configuration applied to this window container. */
306    Configuration getOverrideConfiguration() {
307        return mOverrideConfiguration;
308    }
309
310    /**
311     * Update override configuration and recalculate full config.
312     * @see #mOverrideConfiguration
313     * @see #mFullConfiguration
314     */
315    void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
316        mOverrideConfiguration.setTo(overrideConfiguration);
317        // Update full configuration of this container and all its children.
318        onConfigurationChanged(mParent != null ? mParent.getConfiguration() : Configuration.EMPTY);
319        // Update merged override config of this container and all its children.
320        onMergedOverrideConfigurationChanged();
321    }
322
323    /**
324     * Get merged override configuration from the top of the hierarchy down to this
325     * particular instance. This should be reported to client as override config.
326     */
327    Configuration getMergedOverrideConfiguration() {
328        return mMergedOverrideConfiguration;
329    }
330
331    /**
332     * Update merged override configuration based on corresponding parent's config and notify all
333     * its children. If there is no parent, merged override configuration will set equal to current
334     * override config.
335     * @see #mMergedOverrideConfiguration
336     */
337    private void onMergedOverrideConfigurationChanged() {
338        if (mParent != null) {
339            mMergedOverrideConfiguration.setTo(mParent.getMergedOverrideConfiguration());
340            mMergedOverrideConfiguration.updateFrom(mOverrideConfiguration);
341        } else {
342            mMergedOverrideConfiguration.setTo(mOverrideConfiguration);
343        }
344        for (int i = mChildren.size() - 1; i >= 0; --i) {
345            final WindowContainer child = mChildren.get(i);
346            child.onMergedOverrideConfigurationChanged();
347        }
348    }
349
350    /**
351     * Notify that the display this container is on has changed.
352     * @param dc The new display this container is on.
353     */
354    void onDisplayChanged(DisplayContent dc) {
355        for (int i = mChildren.size() - 1; i >= 0; --i) {
356            final WindowContainer child = mChildren.get(i);
357            child.onDisplayChanged(dc);
358        }
359    }
360
361    void setWaitingForDrawnIfResizingChanged() {
362        for (int i = mChildren.size() - 1; i >= 0; --i) {
363            final WindowContainer wc = mChildren.get(i);
364            wc.setWaitingForDrawnIfResizingChanged();
365        }
366    }
367
368    void onResize() {
369        for (int i = mChildren.size() - 1; i >= 0; --i) {
370            final WindowContainer wc = mChildren.get(i);
371            wc.onResize();
372        }
373    }
374
375    void onMovedByResize() {
376        for (int i = mChildren.size() - 1; i >= 0; --i) {
377            final WindowContainer wc = mChildren.get(i);
378            wc.onMovedByResize();
379        }
380    }
381
382    void resetDragResizingChangeReported() {
383        for (int i = mChildren.size() - 1; i >= 0; --i) {
384            final WindowContainer wc = mChildren.get(i);
385            wc.resetDragResizingChangeReported();
386        }
387    }
388
389    void forceWindowsScaleableInTransaction(boolean force) {
390        for (int i = mChildren.size() - 1; i >= 0; --i) {
391            final WindowContainer wc = mChildren.get(i);
392            wc.forceWindowsScaleableInTransaction(force);
393        }
394    }
395
396    boolean isAnimating() {
397        for (int j = mChildren.size() - 1; j >= 0; j--) {
398            final WindowContainer wc = mChildren.get(j);
399            if (wc.isAnimating()) {
400                return true;
401            }
402        }
403        return false;
404    }
405
406    void sendAppVisibilityToClients() {
407        for (int i = mChildren.size() - 1; i >= 0; --i) {
408            final WindowContainer wc = mChildren.get(i);
409            wc.sendAppVisibilityToClients();
410        }
411    }
412
413    void setVisibleBeforeClientHidden() {
414        for (int i = mChildren.size() - 1; i >= 0; --i) {
415            final WindowContainer wc = mChildren.get(i);
416            wc.setVisibleBeforeClientHidden();
417        }
418    }
419
420    /**
421     * Returns true if the container or one of its children as some content it can display or wants
422     * to display (e.g. app views or saved surface).
423     *
424     * NOTE: While this method will return true if the there is some content to display, it doesn't
425     * mean the container is visible. Use {@link #isVisible()} to determine if the container is
426     * visible.
427     */
428    boolean hasContentToDisplay() {
429        for (int i = mChildren.size() - 1; i >= 0; --i) {
430            final WindowContainer wc = mChildren.get(i);
431            if (wc.hasContentToDisplay()) {
432                return true;
433            }
434        }
435        return false;
436    }
437
438    /**
439     * Returns true if the container or one of its children is considered visible from the
440     * WindowManager perspective which usually means valid surface and some other internal state
441     * are true.
442     *
443     * NOTE: While this method will return true if the surface is visible, it doesn't mean the
444     * client has actually displayed any content. Use {@link #hasContentToDisplay()} to determine if
445     * the container has any content to display.
446     */
447    boolean isVisible() {
448        // TODO: Will this be more correct if it checks the visibility of its parents?
449        // It depends...For example, Tasks and Stacks are only visible if there children are visible
450        // but, WindowState are not visible if there parent are not visible. Maybe have the
451        // container specify which direction to traverse for visibility?
452        for (int i = mChildren.size() - 1; i >= 0; --i) {
453            final WindowContainer wc = mChildren.get(i);
454            if (wc.isVisible()) {
455                return true;
456            }
457        }
458        return false;
459    }
460
461    /** Returns the top child container or this container if there are no children. */
462    WindowContainer getTop() {
463        return mChildren.isEmpty() ? this : mChildren.peekLast();
464    }
465
466    /** Returns true if there is still a removal being deferred */
467    boolean checkCompleteDeferredRemoval() {
468        boolean stillDeferringRemoval = false;
469
470        for (int i = mChildren.size() - 1; i >= 0; --i) {
471            final WindowContainer wc = mChildren.get(i);
472            stillDeferringRemoval |= wc.checkCompleteDeferredRemoval();
473        }
474
475        return stillDeferringRemoval;
476    }
477
478    /** Checks if all windows in an app are all drawn and shows them if needed. */
479    void checkAppWindowsReadyToShow() {
480        for (int i = mChildren.size() - 1; i >= 0; --i) {
481            final WindowContainer wc = mChildren.get(i);
482            wc.checkAppWindowsReadyToShow();
483        }
484    }
485
486    /** Step currently ongoing animation for App window containers. */
487    void stepAppWindowsAnimation(long currentTime) {
488        for (int i = mChildren.size() - 1; i >= 0; --i) {
489            final WindowContainer wc = mChildren.get(i);
490            wc.stepAppWindowsAnimation(currentTime);
491        }
492    }
493
494    void onAppTransitionDone() {
495        for (int i = mChildren.size() - 1; i >= 0; --i) {
496            final WindowContainer wc = mChildren.get(i);
497            wc.onAppTransitionDone();
498        }
499    }
500
501    void setOrientation(int orientation) {
502        mOrientation = orientation;
503    }
504
505    /**
506     * Returns the specified orientation for this window container or one of its children is there
507     * is one set, or {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSET} if no
508     * specification is set.
509     * NOTE: {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} is a
510     * specification...
511     */
512    int getOrientation() {
513
514        if (!fillsParent() || !isVisible()) {
515            // Ignore invisible containers or containers that don't completely fills their parents.
516            return SCREEN_ORIENTATION_UNSET;
517        }
518
519        // The container fills its parent so we can use it orientation if it has one specified,
520        // otherwise we prefer to use the orientation of its topmost child that has one
521        // specified and fall back on this container's unset or unspecified value as a candidate
522        // if none of the children have a better candidate for the orientation.
523        if (mOrientation != SCREEN_ORIENTATION_UNSET
524                && mOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
525            return mOrientation;
526        }
527        int candidate = mOrientation;
528
529        for (int i = mChildren.size() - 1; i >= 0; --i) {
530            final WindowContainer wc = mChildren.get(i);
531
532            final int orientation = wc.getOrientation();
533            if (orientation == SCREEN_ORIENTATION_BEHIND) {
534                // container wants us to use the orientation of the container behind it. See if we
535                // can find one. Else return SCREEN_ORIENTATION_BEHIND so the caller can choose to
536                // look behind this container.
537                candidate = orientation;
538                continue;
539            }
540
541            if (orientation == SCREEN_ORIENTATION_UNSET) {
542                continue;
543            }
544
545            if (wc.fillsParent() || orientation != SCREEN_ORIENTATION_UNSPECIFIED) {
546                // Use the orientation if the container fills its parent or requested an explicit
547                // orientation that isn't SCREEN_ORIENTATION_UNSPECIFIED.
548                return orientation;
549            }
550        }
551
552        return candidate;
553    }
554
555    /**
556     * Returns true if this container is opaque and fills all the space made available by its parent
557     * container.
558     *
559     * NOTE: It is possible for this container to occupy more space than the parent has (or less),
560     * this is just a signal from the client to window manager stating its intent, but not what it
561     * actually does.
562     */
563    boolean fillsParent() {
564        return false;
565    }
566
567    // TODO: Users would have their own window containers under the display container?
568    void switchUser() {
569        for (int i = mChildren.size() - 1; i >= 0; --i) {
570            mChildren.get(i).switchUser();
571        }
572    }
573
574    /**
575     * For all windows at or below this container call the callback.
576     * @param   callback Calls the {@link ToBooleanFunction#apply} method for each window found and
577     *                   stops the search if {@link ToBooleanFunction#apply} returns true.
578     * @param   traverseTopToBottom If true traverses the hierarchy from top-to-bottom in terms of
579     *                              z-order, else from bottom-to-top.
580     * @return  True if the search ended before we reached the end of the hierarchy due to
581     *          {@link ToBooleanFunction#apply} returning true.
582     */
583    boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
584        if (traverseTopToBottom) {
585            for (int i = mChildren.size() - 1; i >= 0; --i) {
586                if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) {
587                    return true;
588                }
589            }
590        } else {
591            final int count = mChildren.size();
592            for (int i = 0; i < count; i++) {
593                if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) {
594                    return true;
595                }
596            }
597        }
598        return false;
599    }
600
601    void forAllWindows(Consumer<WindowState> callback, boolean traverseTopToBottom) {
602        ForAllWindowsConsumerWrapper wrapper = obtainConsumerWrapper(callback);
603        forAllWindows(wrapper, traverseTopToBottom);
604        wrapper.release();
605    }
606
607    WindowState getWindow(Predicate<WindowState> callback) {
608        for (int i = mChildren.size() - 1; i >= 0; --i) {
609            final WindowState w = mChildren.get(i).getWindow(callback);
610            if (w != null) {
611                return w;
612            }
613        }
614
615        return null;
616    }
617
618    /**
619     * Returns 1, 0, or -1 depending on if this container is greater than, equal to, or lesser than
620     * the input container in terms of z-order.
621     */
622    @Override
623    public int compareTo(WindowContainer other) {
624        if (this == other) {
625            return 0;
626        }
627
628        if (mParent != null && mParent == other.mParent) {
629            final LinkedList<WindowContainer> list = mParent.mChildren;
630            return list.indexOf(this) > list.indexOf(other) ? 1 : -1;
631        }
632
633        final LinkedList<WindowContainer> thisParentChain = mTmpChain1;
634        final LinkedList<WindowContainer> otherParentChain = mTmpChain2;
635        getParents(thisParentChain);
636        other.getParents(otherParentChain);
637
638        // Find the common ancestor of both containers.
639        WindowContainer commonAncestor = null;
640        WindowContainer thisTop = thisParentChain.peekLast();
641        WindowContainer otherTop = otherParentChain.peekLast();
642        while (thisTop != null && otherTop != null && thisTop == otherTop) {
643            commonAncestor = thisParentChain.removeLast();
644            otherParentChain.removeLast();
645            thisTop = thisParentChain.peekLast();
646            otherTop = otherParentChain.peekLast();
647        }
648
649        // Containers don't belong to the same hierarchy???
650        if (commonAncestor == null) {
651            throw new IllegalArgumentException("No in the same hierarchy this="
652                    + thisParentChain + " other=" + otherParentChain);
653        }
654
655        // Children are always considered greater than their parents, so if one of the containers
656        // we are comparing it the parent of the other then whichever is the child is greater.
657        if (commonAncestor == this) {
658            return -1;
659        } else if (commonAncestor == other) {
660            return 1;
661        }
662
663        // The position of the first non-common ancestor in the common ancestor list determines
664        // which is greater the which.
665        final LinkedList<WindowContainer> list = commonAncestor.mChildren;
666        return list.indexOf(thisParentChain.peekLast()) > list.indexOf(otherParentChain.peekLast())
667                ? 1 : -1;
668    }
669
670    private void getParents(LinkedList<WindowContainer> parents) {
671        parents.clear();
672        WindowContainer current = this;
673        do {
674            parents.addLast(current);
675            current = current.mParent;
676        } while (current != null);
677    }
678
679    WindowContainerController getController() {
680        return mController;
681    }
682
683    void setController(WindowContainerController controller) {
684        if (mController != null && controller != null) {
685            throw new IllegalArgumentException("Can't set controller=" + mController
686                    + " for container=" + this + " Already set to=" + mController);
687        }
688        if (controller != null) {
689            controller.setContainer(this);
690        } else if (mController != null) {
691            mController.setContainer(null);
692        }
693        mController = controller;
694    }
695
696    /**
697     * Dumps the names of this container children in the input print writer indenting each
698     * level with the input prefix.
699     */
700    void dumpChildrenNames(StringBuilder out, String prefix) {
701        final String childPrefix = prefix + " ";
702        out.append(getName() + "\n");
703        for (int i = mChildren.size() - 1; i >= 0; --i) {
704            final WindowContainer wc = mChildren.get(i);
705            out.append(childPrefix + "#" + i + " ");
706            wc.dumpChildrenNames(out, childPrefix);
707        }
708    }
709
710    String getName() {
711        return toString();
712    }
713
714    private ForAllWindowsConsumerWrapper obtainConsumerWrapper(Consumer<WindowState> consumer) {
715        ForAllWindowsConsumerWrapper wrapper = mConsumerWrapperPool.acquire();
716        if (wrapper == null) {
717            wrapper = new ForAllWindowsConsumerWrapper();
718        }
719        wrapper.setConsumer(consumer);
720        return wrapper;
721    }
722
723    private final class ForAllWindowsConsumerWrapper implements ToBooleanFunction<WindowState> {
724
725        private Consumer<WindowState> mConsumer;
726
727        void setConsumer(Consumer<WindowState> consumer) {
728            mConsumer = consumer;
729        }
730
731        @Override
732        public boolean apply(WindowState w) {
733            mConsumer.accept(w);
734            return false;
735        }
736
737        void release() {
738            mConsumer = null;
739            mConsumerWrapperPool.release(this);
740        }
741    }
742}
743