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 
17 package com.android.server.wm;
18 
19 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
20 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
21 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
22 import static android.content.res.Configuration.EMPTY;
23 
24 import android.annotation.CallSuper;
25 import android.content.res.Configuration;
26 import android.util.Pools;
27 
28 import com.android.internal.util.ToBooleanFunction;
29 
30 import java.util.Comparator;
31 import java.util.LinkedList;
32 import java.util.function.Consumer;
33 import 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  */
41 class 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 
getParent()82     final protected WindowContainer getParent() {
83         return mParent;
84     }
85 
setParent(WindowContainer parent)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      */
onParentSet()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
addChild(E child, Comparator<E> comparator)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
addChild(E child, int index)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
removeChild(E child)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
removeImmediately()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...
removeIfPossible()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. */
hasChild(WindowContainer 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
positionChildAt(int position, E child, boolean includingParents)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      */
getConfiguration()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      */
onConfigurationChanged(Configuration newParentConfig)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. */
getOverrideConfiguration()307     Configuration getOverrideConfiguration() {
308         return mOverrideConfiguration;
309     }
310 
311     /**
312      * Update override configuration and recalculate full config.
313      * @see #mOverrideConfiguration
314      * @see #mFullConfiguration
315      */
onOverrideConfigurationChanged(Configuration overrideConfiguration)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      */
onDescendantOverrideConfigurationChanged()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      */
getMergedOverrideConfiguration()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      */
onMergedOverrideConfigurationChanged()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      */
onDisplayChanged(DisplayContent dc)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 
setWaitingForDrawnIfResizingChanged()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 
onResize()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 
onMovedByResize()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 
resetDragResizingChangeReported()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 
forceWindowsScaleableInTransaction(boolean force)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 
isAnimating()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 
sendAppVisibilityToClients()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 
setVisibleBeforeClientHidden()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      */
hasContentToDisplay()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      */
isVisible()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. */
getTopChild()476     E getTopChild() {
477         return mChildren.peekLast();
478     }
479 
480     /** Returns true if there is still a removal being deferred */
checkCompleteDeferredRemoval()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. */
checkAppWindowsReadyToShow()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. */
stepAppWindowsAnimation(long currentTime)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 
onAppTransitionDone()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 
setOrientation(int orientation)515     void setOrientation(int orientation) {
516         mOrientation = orientation;
517     }
518 
getOrientation()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      */
getOrientation(int candidate)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      */
fillsParent()586     boolean fillsParent() {
587         return false;
588     }
589 
590     // TODO: Users would have their own window containers under the display container?
switchUser()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      */
forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom)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 
forAllWindows(Consumer<WindowState> callback, boolean traverseTopToBottom)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      */
forAllTasks(Consumer<Task> callback)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 
getWindow(Predicate<WindowState> callback)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
compareTo(WindowContainer other)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 
getParents(LinkedList<WindowContainer> parents)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 
getController()718     WindowContainerController getController() {
719         return mController;
720     }
721 
setController(WindowContainerController controller)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      */
dumpChildrenNames(StringBuilder out, String prefix)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 
getName()749     String getName() {
750         return toString();
751     }
752 
obtainConsumerWrapper(Consumer<WindowState> consumer)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 
setConsumer(Consumer<WindowState> consumer)766         void setConsumer(Consumer<WindowState> consumer) {
767             mConsumer = consumer;
768         }
769 
770         @Override
apply(WindowState w)771         public boolean apply(WindowState w) {
772             mConsumer.accept(w);
773             return false;
774         }
775 
release()776         void release() {
777             mConsumer = null;
778             mConsumerWrapperPool.release(this);
779         }
780     }
781 }
782