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