• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     /**
476 a     * Returns whether this child is on top of the window hierarchy.
477      */
isOnTop()478     boolean isOnTop() {
479         return getParent().getTopChild() == this && getParent().isOnTop();
480     }
481 
482     /** Returns the top child container. */
getTopChild()483     E getTopChild() {
484         return mChildren.peekLast();
485     }
486 
487     /** Returns true if there is still a removal being deferred */
checkCompleteDeferredRemoval()488     boolean checkCompleteDeferredRemoval() {
489         boolean stillDeferringRemoval = false;
490 
491         for (int i = mChildren.size() - 1; i >= 0; --i) {
492             final WindowContainer wc = mChildren.get(i);
493             stillDeferringRemoval |= wc.checkCompleteDeferredRemoval();
494         }
495 
496         return stillDeferringRemoval;
497     }
498 
499     /** Checks if all windows in an app are all drawn and shows them if needed. */
checkAppWindowsReadyToShow()500     void checkAppWindowsReadyToShow() {
501         for (int i = mChildren.size() - 1; i >= 0; --i) {
502             final WindowContainer wc = mChildren.get(i);
503             wc.checkAppWindowsReadyToShow();
504         }
505     }
506 
507     /** Step currently ongoing animation for App window containers. */
stepAppWindowsAnimation(long currentTime)508     void stepAppWindowsAnimation(long currentTime) {
509         for (int i = mChildren.size() - 1; i >= 0; --i) {
510             final WindowContainer wc = mChildren.get(i);
511             wc.stepAppWindowsAnimation(currentTime);
512         }
513     }
514 
onAppTransitionDone()515     void onAppTransitionDone() {
516         for (int i = mChildren.size() - 1; i >= 0; --i) {
517             final WindowContainer wc = mChildren.get(i);
518             wc.onAppTransitionDone();
519         }
520     }
521 
setOrientation(int orientation)522     void setOrientation(int orientation) {
523         mOrientation = orientation;
524     }
525 
getOrientation()526     int getOrientation() {
527         return getOrientation(mOrientation);
528     }
529 
530     /**
531      * Returns the specified orientation for this window container or one of its children is there
532      * is one set, or {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSET} if no
533      * specification is set.
534      * NOTE: {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} is a
535      * specification...
536      *
537      * @param candidate The current orientation candidate that will be returned if we don't find a
538      *                  better match.
539      * @return The orientation as specified by this branch or the window hierarchy.
540      */
getOrientation(int candidate)541     int getOrientation(int candidate) {
542         if (!fillsParent()) {
543             // Ignore containers that don't completely fill their parents.
544             return SCREEN_ORIENTATION_UNSET;
545         }
546 
547         // The container fills its parent so we can use it orientation if it has one
548         // specified; otherwise we prefer to use the orientation of its topmost child that has one
549         // specified and fall back on this container's unset or unspecified value as a candidate
550         // if none of the children have a better candidate for the orientation.
551         if (mOrientation != SCREEN_ORIENTATION_UNSET
552                 && mOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
553             return mOrientation;
554         }
555 
556         for (int i = mChildren.size() - 1; i >= 0; --i) {
557             final WindowContainer wc = mChildren.get(i);
558 
559             // TODO: Maybe mOrientation should default to SCREEN_ORIENTATION_UNSET vs.
560             // SCREEN_ORIENTATION_UNSPECIFIED?
561             final int orientation = wc.getOrientation(candidate == SCREEN_ORIENTATION_BEHIND
562                     ? SCREEN_ORIENTATION_BEHIND : SCREEN_ORIENTATION_UNSET);
563             if (orientation == SCREEN_ORIENTATION_BEHIND) {
564                 // container wants us to use the orientation of the container behind it. See if we
565                 // can find one. Else return SCREEN_ORIENTATION_BEHIND so the caller can choose to
566                 // look behind this container.
567                 candidate = orientation;
568                 continue;
569             }
570 
571             if (orientation == SCREEN_ORIENTATION_UNSET) {
572                 continue;
573             }
574 
575             if (wc.fillsParent() || orientation != SCREEN_ORIENTATION_UNSPECIFIED) {
576                 // Use the orientation if the container fills its parent or requested an explicit
577                 // orientation that isn't SCREEN_ORIENTATION_UNSPECIFIED.
578                 return orientation;
579             }
580         }
581 
582         return candidate;
583     }
584 
585     /**
586      * Returns true if this container is opaque and fills all the space made available by its parent
587      * container.
588      *
589      * NOTE: It is possible for this container to occupy more space than the parent has (or less),
590      * this is just a signal from the client to window manager stating its intent, but not what it
591      * actually does.
592      */
fillsParent()593     boolean fillsParent() {
594         return false;
595     }
596 
597     // TODO: Users would have their own window containers under the display container?
switchUser()598     void switchUser() {
599         for (int i = mChildren.size() - 1; i >= 0; --i) {
600             mChildren.get(i).switchUser();
601         }
602     }
603 
604     /**
605      * For all windows at or below this container call the callback.
606      * @param   callback Calls the {@link ToBooleanFunction#apply} method for each window found and
607      *                   stops the search if {@link ToBooleanFunction#apply} returns true.
608      * @param   traverseTopToBottom If true traverses the hierarchy from top-to-bottom in terms of
609      *                              z-order, else from bottom-to-top.
610      * @return  True if the search ended before we reached the end of the hierarchy due to
611      *          {@link ToBooleanFunction#apply} returning true.
612      */
forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom)613     boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
614         if (traverseTopToBottom) {
615             for (int i = mChildren.size() - 1; i >= 0; --i) {
616                 if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) {
617                     return true;
618                 }
619             }
620         } else {
621             final int count = mChildren.size();
622             for (int i = 0; i < count; i++) {
623                 if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) {
624                     return true;
625                 }
626             }
627         }
628         return false;
629     }
630 
forAllWindows(Consumer<WindowState> callback, boolean traverseTopToBottom)631     void forAllWindows(Consumer<WindowState> callback, boolean traverseTopToBottom) {
632         ForAllWindowsConsumerWrapper wrapper = obtainConsumerWrapper(callback);
633         forAllWindows(wrapper, traverseTopToBottom);
634         wrapper.release();
635     }
636 
637     /**
638      * For all tasks at or below this container call the callback.
639      *
640      * @param callback Callback to be called for every task.
641      */
forAllTasks(Consumer<Task> callback)642     void forAllTasks(Consumer<Task> callback) {
643         for (int i = mChildren.size() - 1; i >= 0; --i) {
644             mChildren.get(i).forAllTasks(callback);
645         }
646     }
647 
getWindow(Predicate<WindowState> callback)648     WindowState getWindow(Predicate<WindowState> callback) {
649         for (int i = mChildren.size() - 1; i >= 0; --i) {
650             final WindowState w = mChildren.get(i).getWindow(callback);
651             if (w != null) {
652                 return w;
653             }
654         }
655 
656         return null;
657     }
658 
659     /**
660      * Returns 1, 0, or -1 depending on if this container is greater than, equal to, or lesser than
661      * the input container in terms of z-order.
662      */
663     @Override
compareTo(WindowContainer other)664     public int compareTo(WindowContainer other) {
665         if (this == other) {
666             return 0;
667         }
668 
669         if (mParent != null && mParent == other.mParent) {
670             final WindowList<WindowContainer> list = mParent.mChildren;
671             return list.indexOf(this) > list.indexOf(other) ? 1 : -1;
672         }
673 
674         final LinkedList<WindowContainer> thisParentChain = mTmpChain1;
675         final LinkedList<WindowContainer> otherParentChain = mTmpChain2;
676         try {
677             getParents(thisParentChain);
678             other.getParents(otherParentChain);
679 
680             // Find the common ancestor of both containers.
681             WindowContainer commonAncestor = null;
682             WindowContainer thisTop = thisParentChain.peekLast();
683             WindowContainer otherTop = otherParentChain.peekLast();
684             while (thisTop != null && otherTop != null && thisTop == otherTop) {
685                 commonAncestor = thisParentChain.removeLast();
686                 otherParentChain.removeLast();
687                 thisTop = thisParentChain.peekLast();
688                 otherTop = otherParentChain.peekLast();
689             }
690 
691             // Containers don't belong to the same hierarchy???
692             if (commonAncestor == null) {
693                 throw new IllegalArgumentException("No in the same hierarchy this="
694                         + thisParentChain + " other=" + otherParentChain);
695             }
696 
697             // Children are always considered greater than their parents, so if one of the containers
698             // we are comparing it the parent of the other then whichever is the child is greater.
699             if (commonAncestor == this) {
700                 return -1;
701             } else if (commonAncestor == other) {
702                 return 1;
703             }
704 
705             // The position of the first non-common ancestor in the common ancestor list determines
706             // which is greater the which.
707             final WindowList<WindowContainer> list = commonAncestor.mChildren;
708             return list.indexOf(thisParentChain.peekLast()) > list.indexOf(otherParentChain.peekLast())
709                     ? 1 : -1;
710         } finally {
711             mTmpChain1.clear();
712             mTmpChain2.clear();
713         }
714     }
715 
getParents(LinkedList<WindowContainer> parents)716     private void getParents(LinkedList<WindowContainer> parents) {
717         parents.clear();
718         WindowContainer current = this;
719         do {
720             parents.addLast(current);
721             current = current.mParent;
722         } while (current != null);
723     }
724 
getController()725     WindowContainerController getController() {
726         return mController;
727     }
728 
setController(WindowContainerController controller)729     void setController(WindowContainerController controller) {
730         if (mController != null && controller != null) {
731             throw new IllegalArgumentException("Can't set controller=" + mController
732                     + " for container=" + this + " Already set to=" + mController);
733         }
734         if (controller != null) {
735             controller.setContainer(this);
736         } else if (mController != null) {
737             mController.setContainer(null);
738         }
739         mController = controller;
740     }
741 
742     /**
743      * Dumps the names of this container children in the input print writer indenting each
744      * level with the input prefix.
745      */
dumpChildrenNames(StringBuilder out, String prefix)746     void dumpChildrenNames(StringBuilder out, String prefix) {
747         final String childPrefix = prefix + " ";
748         out.append(getName() + "\n");
749         for (int i = mChildren.size() - 1; i >= 0; --i) {
750             final WindowContainer wc = mChildren.get(i);
751             out.append(childPrefix + "#" + i + " ");
752             wc.dumpChildrenNames(out, childPrefix);
753         }
754     }
755 
getName()756     String getName() {
757         return toString();
758     }
759 
obtainConsumerWrapper(Consumer<WindowState> consumer)760     private ForAllWindowsConsumerWrapper obtainConsumerWrapper(Consumer<WindowState> consumer) {
761         ForAllWindowsConsumerWrapper wrapper = mConsumerWrapperPool.acquire();
762         if (wrapper == null) {
763             wrapper = new ForAllWindowsConsumerWrapper();
764         }
765         wrapper.setConsumer(consumer);
766         return wrapper;
767     }
768 
769     private final class ForAllWindowsConsumerWrapper implements ToBooleanFunction<WindowState> {
770 
771         private Consumer<WindowState> mConsumer;
772 
setConsumer(Consumer<WindowState> consumer)773         void setConsumer(Consumer<WindowState> consumer) {
774             mConsumer = consumer;
775         }
776 
777         @Override
apply(WindowState w)778         public boolean apply(WindowState w) {
779             mConsumer.accept(w);
780             return false;
781         }
782 
release()783         void release() {
784             mConsumer = null;
785             mConsumerWrapperPool.release(this);
786         }
787     }
788 }
789