Home | History | Annotate | Download | only in wm
      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 
     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     /**
    476 a     * Returns whether this child is on top of the window hierarchy.
    477      */
    478     boolean isOnTop() {
    479         return getParent().getTopChild() == this && getParent().isOnTop();
    480     }
    481 
    482     /** Returns the top child container. */
    483     E getTopChild() {
    484         return mChildren.peekLast();
    485     }
    486 
    487     /** Returns true if there is still a removal being deferred */
    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. */
    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. */
    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 
    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 
    522     void setOrientation(int orientation) {
    523         mOrientation = orientation;
    524     }
    525 
    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      */
    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      */
    593     boolean fillsParent() {
    594         return false;
    595     }
    596 
    597     // TODO: Users would have their own window containers under the display container?
    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      */
    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 
    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      */
    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 
    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
    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 
    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 
    725     WindowContainerController getController() {
    726         return mController;
    727     }
    728 
    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      */
    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 
    756     String getName() {
    757         return toString();
    758     }
    759 
    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 
    773         void setConsumer(Consumer<WindowState> consumer) {
    774             mConsumer = consumer;
    775         }
    776 
    777         @Override
    778         public boolean apply(WindowState w) {
    779             mConsumer.accept(w);
    780             return false;
    781         }
    782 
    783         void release() {
    784             mConsumer = null;
    785             mConsumerWrapperPool.release(this);
    786         }
    787     }
    788 }
    789