Import Android SDK Platform PI [4335822]

/google/data/ro/projects/android/fetch_artifact \
    --bid 4335822 \
    --target sdk_phone_armv7-win_sdk \
    sdk-repo-linux-sources-4335822.zip

AndroidVersion.ApiLevel has been modified to appear as 28

Change-Id: Ic8f04be005a71c2b9abeaac754d8da8d6f9a2c32
diff --git a/com/android/server/wm/WindowContainer.java b/com/android/server/wm/WindowContainer.java
new file mode 100644
index 0000000..926719d
--- /dev/null
+++ b/com/android/server/wm/WindowContainer.java
@@ -0,0 +1,720 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.res.Configuration.EMPTY;
+
+import android.annotation.CallSuper;
+import android.content.res.Configuration;
+import android.util.Pools;
+
+import com.android.internal.util.ToBooleanFunction;
+
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+/**
+ * Defines common functionality for classes that can hold windows directly or through their
+ * children in a hierarchy form.
+ * The test class is {@link WindowContainerTests} which must be kept up-to-date and ran anytime
+ * changes are made to this class.
+ */
+class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
+        implements Comparable<WindowContainer> {
+
+    static final int POSITION_TOP = Integer.MAX_VALUE;
+    static final int POSITION_BOTTOM = Integer.MIN_VALUE;
+
+    /**
+     * The parent of this window container.
+     * For removing or setting new parent {@link #setParent} should be used, because it also
+     * performs configuration updates based on new parent's settings.
+     */
+    private WindowContainer mParent = null;
+
+    // List of children for this window container. List is in z-order as the children appear on
+    // screen with the top-most window container at the tail of the list.
+    protected final WindowList<E> mChildren = new WindowList<E>();
+
+    // The specified orientation for this window container.
+    protected int mOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+
+    private final Pools.SynchronizedPool<ForAllWindowsConsumerWrapper> mConsumerWrapperPool =
+            new Pools.SynchronizedPool<>(3);
+
+    // The owner/creator for this container. No controller if null.
+    private WindowContainerController mController;
+
+    @Override
+    final protected WindowContainer getParent() {
+        return mParent;
+    }
+
+
+    @Override
+    final protected int getChildCount() {
+        return mChildren.size();
+    }
+
+    @Override
+    final protected E getChildAt(int index) {
+        return mChildren.get(index);
+    }
+
+    final protected void setParent(WindowContainer parent) {
+        mParent = parent;
+        // Removing parent usually means that we've detached this entity to destroy it or to attach
+        // to another parent. In both cases we don't need to update the configuration now.
+        if (mParent != null) {
+            // Update full configuration of this container and all its children.
+            onConfigurationChanged(mParent.getConfiguration());
+            // Update merged override configuration of this container and all its children.
+            onMergedOverrideConfigurationChanged();
+        }
+
+        onParentSet();
+    }
+
+    /**
+     * Callback that is triggered when @link WindowContainer#setParent(WindowContainer)} was called.
+     * Supposed to be overridden and contain actions that should be executed after parent was set.
+     */
+    void onParentSet() {
+        // Do nothing by default.
+    }
+
+    // Temp. holders for a chain of containers we are currently processing.
+    private final LinkedList<WindowContainer> mTmpChain1 = new LinkedList();
+    private final LinkedList<WindowContainer> mTmpChain2 = new LinkedList();
+
+    /**
+     * Adds the input window container has a child of this container in order based on the input
+     * comparator.
+     * @param child The window container to add as a child of this window container.
+     * @param comparator Comparator to use in determining the position the child should be added to.
+     *                   If null, the child will be added to the top.
+     */
+    @CallSuper
+    protected void addChild(E child, Comparator<E> comparator) {
+        if (child.getParent() != null) {
+            throw new IllegalArgumentException("addChild: container=" + child.getName()
+                    + " is already a child of container=" + child.getParent().getName()
+                    + " can't add to container=" + getName());
+        }
+
+        int positionToAdd = -1;
+        if (comparator != null) {
+            final int count = mChildren.size();
+            for (int i = 0; i < count; i++) {
+                if (comparator.compare(child, mChildren.get(i)) < 0) {
+                    positionToAdd = i;
+                    break;
+                }
+            }
+        }
+
+        if (positionToAdd == -1) {
+            mChildren.add(child);
+        } else {
+            mChildren.add(positionToAdd, child);
+        }
+        // Set the parent after we've actually added a child in case a subclass depends on this.
+        child.setParent(this);
+    }
+
+    /** Adds the input window container has a child of this container at the input index. */
+    @CallSuper
+    void addChild(E child, int index) {
+        if (child.getParent() != null) {
+            throw new IllegalArgumentException("addChild: container=" + child.getName()
+                    + " is already a child of container=" + child.getParent().getName()
+                    + " can't add to container=" + getName());
+        }
+        mChildren.add(index, child);
+        // Set the parent after we've actually added a child in case a subclass depends on this.
+        child.setParent(this);
+    }
+
+    /**
+     * Removes the input child container from this container which is its parent.
+     *
+     * @return True if the container did contain the input child and it was detached.
+     */
+    @CallSuper
+    void removeChild(E child) {
+        if (mChildren.remove(child)) {
+            child.setParent(null);
+        } else {
+            throw new IllegalArgumentException("removeChild: container=" + child.getName()
+                    + " is not a child of container=" + getName());
+        }
+    }
+
+    /**
+     * Removes this window container and its children with no regard for what else might be going on
+     * in the system. For example, the container will be removed during animation if this method is
+     * called which isn't desirable. For most cases you want to call {@link #removeIfPossible()}
+     * which allows the system to defer removal until a suitable time.
+     */
+    @CallSuper
+    void removeImmediately() {
+        while (!mChildren.isEmpty()) {
+            final WindowContainer child = mChildren.peekLast();
+            child.removeImmediately();
+            // Need to do this after calling remove on the child because the child might try to
+            // remove/detach itself from its parent which will cause an exception if we remove
+            // it before calling remove on the child.
+            mChildren.remove(child);
+        }
+
+        if (mParent != null) {
+            mParent.removeChild(this);
+        }
+
+        if (mController != null) {
+            setController(null);
+        }
+    }
+
+    /**
+     * Removes this window container and its children taking care not to remove them during a
+     * critical stage in the system. For example, some containers will not be removed during
+     * animation if this method is called.
+     */
+    // TODO: figure-out implementation that works best for this.
+    // E.g. when do we remove from parent list? maybe not...
+    void removeIfPossible() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.removeIfPossible();
+        }
+    }
+
+    /** Returns true if this window container has the input child. */
+    boolean hasChild(WindowContainer child) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer current = mChildren.get(i);
+            if (current == child || current.hasChild(child)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Move a child from it's current place in siblings list to the specified position,
+     * with an option to move all its parents to top.
+     * @param position Target position to move the child to.
+     * @param child Child to move to selected position.
+     * @param includingParents Flag indicating whether we need to move the entire branch of the
+     *                         hierarchy when we're moving a child to {@link #POSITION_TOP} or
+     *                         {@link #POSITION_BOTTOM}. When moving to other intermediate positions
+     *                         this flag will do nothing.
+     */
+    @CallSuper
+    void positionChildAt(int position, E child, boolean includingParents) {
+
+        if (child.getParent() != this) {
+            throw new IllegalArgumentException("removeChild: container=" + child.getName()
+                    + " is not a child of container=" + getName()
+                    + " current parent=" + child.getParent());
+        }
+
+        if ((position < 0 && position != POSITION_BOTTOM)
+                || (position > mChildren.size() && position != POSITION_TOP)) {
+            throw new IllegalArgumentException("positionAt: invalid position=" + position
+                    + ", children number=" + mChildren.size());
+        }
+
+        if (position >= mChildren.size() - 1) {
+            position = POSITION_TOP;
+        } else if (position == 0) {
+            position = POSITION_BOTTOM;
+        }
+
+        switch (position) {
+            case POSITION_TOP:
+                if (mChildren.peekLast() != child) {
+                    mChildren.remove(child);
+                    mChildren.add(child);
+                }
+                if (includingParents && getParent() != null) {
+                    getParent().positionChildAt(POSITION_TOP, this /* child */,
+                            true /* includingParents */);
+                }
+                break;
+            case POSITION_BOTTOM:
+                if (mChildren.peekFirst() != child) {
+                    mChildren.remove(child);
+                    mChildren.addFirst(child);
+                }
+                if (includingParents && getParent() != null) {
+                    getParent().positionChildAt(POSITION_BOTTOM, this /* child */,
+                            true /* includingParents */);
+                }
+                break;
+            default:
+                mChildren.remove(child);
+                mChildren.add(position, child);
+        }
+    }
+
+    /**
+     * Update override configuration and recalculate full config.
+     * @see #mOverrideConfiguration
+     * @see #mFullConfiguration
+     */
+    @Override
+    final public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
+        super.onOverrideConfigurationChanged(overrideConfiguration);
+        if (mParent != null) {
+            mParent.onDescendantOverrideConfigurationChanged();
+        }
+    }
+
+    /**
+     * Notify that a descendant's overrideConfiguration has changed.
+     */
+    void onDescendantOverrideConfigurationChanged() {
+        if (mParent != null) {
+            mParent.onDescendantOverrideConfigurationChanged();
+        }
+    }
+
+    /**
+     * Notify that the display this container is on has changed.
+     * @param dc The new display this container is on.
+     */
+    void onDisplayChanged(DisplayContent dc) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer child = mChildren.get(i);
+            child.onDisplayChanged(dc);
+        }
+    }
+
+    void setWaitingForDrawnIfResizingChanged() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.setWaitingForDrawnIfResizingChanged();
+        }
+    }
+
+    void onResize() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.onResize();
+        }
+    }
+
+    void onMovedByResize() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.onMovedByResize();
+        }
+    }
+
+    void resetDragResizingChangeReported() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.resetDragResizingChangeReported();
+        }
+    }
+
+    void forceWindowsScaleableInTransaction(boolean force) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.forceWindowsScaleableInTransaction(force);
+        }
+    }
+
+    boolean isAnimating() {
+        for (int j = mChildren.size() - 1; j >= 0; j--) {
+            final WindowContainer wc = mChildren.get(j);
+            if (wc.isAnimating()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void sendAppVisibilityToClients() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.sendAppVisibilityToClients();
+        }
+    }
+
+    /**
+     * Returns true if the container or one of its children as some content it can display or wants
+     * to display (e.g. app views or saved surface).
+     *
+     * NOTE: While this method will return true if the there is some content to display, it doesn't
+     * mean the container is visible. Use {@link #isVisible()} to determine if the container is
+     * visible.
+     */
+    boolean hasContentToDisplay() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            if (wc.hasContentToDisplay()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if the container or one of its children is considered visible from the
+     * WindowManager perspective which usually means valid surface and some other internal state
+     * are true.
+     *
+     * NOTE: While this method will return true if the surface is visible, it doesn't mean the
+     * client has actually displayed any content. Use {@link #hasContentToDisplay()} to determine if
+     * the container has any content to display.
+     */
+    boolean isVisible() {
+        // TODO: Will this be more correct if it checks the visibility of its parents?
+        // It depends...For example, Tasks and Stacks are only visible if there children are visible
+        // but, WindowState are not visible if there parent are not visible. Maybe have the
+        // container specify which direction to traverse for visibility?
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            if (wc.isVisible()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+a     * Returns whether this child is on top of the window hierarchy.
+     */
+    boolean isOnTop() {
+        return getParent().getTopChild() == this && getParent().isOnTop();
+    }
+
+    /** Returns the top child container. */
+    E getTopChild() {
+        return mChildren.peekLast();
+    }
+
+    /** Returns true if there is still a removal being deferred */
+    boolean checkCompleteDeferredRemoval() {
+        boolean stillDeferringRemoval = false;
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            stillDeferringRemoval |= wc.checkCompleteDeferredRemoval();
+        }
+
+        return stillDeferringRemoval;
+    }
+
+    /** Checks if all windows in an app are all drawn and shows them if needed. */
+    void checkAppWindowsReadyToShow() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.checkAppWindowsReadyToShow();
+        }
+    }
+
+    /** Step currently ongoing animation for App window containers. */
+    void stepAppWindowsAnimation(long currentTime) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.stepAppWindowsAnimation(currentTime);
+        }
+    }
+
+    void onAppTransitionDone() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.onAppTransitionDone();
+        }
+    }
+
+    void setOrientation(int orientation) {
+        mOrientation = orientation;
+    }
+
+    int getOrientation() {
+        return getOrientation(mOrientation);
+    }
+
+    /**
+     * Returns the specified orientation for this window container or one of its children is there
+     * is one set, or {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSET} if no
+     * specification is set.
+     * NOTE: {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} is a
+     * specification...
+     *
+     * @param candidate The current orientation candidate that will be returned if we don't find a
+     *                  better match.
+     * @return The orientation as specified by this branch or the window hierarchy.
+     */
+    int getOrientation(int candidate) {
+        if (!fillsParent()) {
+            // Ignore containers that don't completely fill their parents.
+            return SCREEN_ORIENTATION_UNSET;
+        }
+
+        // The container fills its parent so we can use it orientation if it has one
+        // specified; otherwise we prefer to use the orientation of its topmost child that has one
+        // specified and fall back on this container's unset or unspecified value as a candidate
+        // if none of the children have a better candidate for the orientation.
+        if (mOrientation != SCREEN_ORIENTATION_UNSET
+                && mOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
+            return mOrientation;
+        }
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+
+            // TODO: Maybe mOrientation should default to SCREEN_ORIENTATION_UNSET vs.
+            // SCREEN_ORIENTATION_UNSPECIFIED?
+            final int orientation = wc.getOrientation(candidate == SCREEN_ORIENTATION_BEHIND
+                    ? SCREEN_ORIENTATION_BEHIND : SCREEN_ORIENTATION_UNSET);
+            if (orientation == SCREEN_ORIENTATION_BEHIND) {
+                // container wants us to use the orientation of the container behind it. See if we
+                // can find one. Else return SCREEN_ORIENTATION_BEHIND so the caller can choose to
+                // look behind this container.
+                candidate = orientation;
+                continue;
+            }
+
+            if (orientation == SCREEN_ORIENTATION_UNSET) {
+                continue;
+            }
+
+            if (wc.fillsParent() || orientation != SCREEN_ORIENTATION_UNSPECIFIED) {
+                // Use the orientation if the container fills its parent or requested an explicit
+                // orientation that isn't SCREEN_ORIENTATION_UNSPECIFIED.
+                return orientation;
+            }
+        }
+
+        return candidate;
+    }
+
+    /**
+     * Returns true if this container is opaque and fills all the space made available by its parent
+     * container.
+     *
+     * NOTE: It is possible for this container to occupy more space than the parent has (or less),
+     * this is just a signal from the client to window manager stating its intent, but not what it
+     * actually does.
+     */
+    boolean fillsParent() {
+        return false;
+    }
+
+    // TODO: Users would have their own window containers under the display container?
+    void switchUser() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            mChildren.get(i).switchUser();
+        }
+    }
+
+    /**
+     * For all windows at or below this container call the callback.
+     * @param   callback Calls the {@link ToBooleanFunction#apply} method for each window found and
+     *                   stops the search if {@link ToBooleanFunction#apply} returns true.
+     * @param   traverseTopToBottom If true traverses the hierarchy from top-to-bottom in terms of
+     *                              z-order, else from bottom-to-top.
+     * @return  True if the search ended before we reached the end of the hierarchy due to
+     *          {@link ToBooleanFunction#apply} returning true.
+     */
+    boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
+        if (traverseTopToBottom) {
+            for (int i = mChildren.size() - 1; i >= 0; --i) {
+                if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) {
+                    return true;
+                }
+            }
+        } else {
+            final int count = mChildren.size();
+            for (int i = 0; i < count; i++) {
+                if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    void forAllWindows(Consumer<WindowState> callback, boolean traverseTopToBottom) {
+        ForAllWindowsConsumerWrapper wrapper = obtainConsumerWrapper(callback);
+        forAllWindows(wrapper, traverseTopToBottom);
+        wrapper.release();
+    }
+
+    /**
+     * For all tasks at or below this container call the callback.
+     *
+     * @param callback Callback to be called for every task.
+     */
+    void forAllTasks(Consumer<Task> callback) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            mChildren.get(i).forAllTasks(callback);
+        }
+    }
+
+    WindowState getWindow(Predicate<WindowState> callback) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState w = mChildren.get(i).getWindow(callback);
+            if (w != null) {
+                return w;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns 1, 0, or -1 depending on if this container is greater than, equal to, or lesser than
+     * the input container in terms of z-order.
+     */
+    @Override
+    public int compareTo(WindowContainer other) {
+        if (this == other) {
+            return 0;
+        }
+
+        if (mParent != null && mParent == other.mParent) {
+            final WindowList<WindowContainer> list = mParent.mChildren;
+            return list.indexOf(this) > list.indexOf(other) ? 1 : -1;
+        }
+
+        final LinkedList<WindowContainer> thisParentChain = mTmpChain1;
+        final LinkedList<WindowContainer> otherParentChain = mTmpChain2;
+        try {
+            getParents(thisParentChain);
+            other.getParents(otherParentChain);
+
+            // Find the common ancestor of both containers.
+            WindowContainer commonAncestor = null;
+            WindowContainer thisTop = thisParentChain.peekLast();
+            WindowContainer otherTop = otherParentChain.peekLast();
+            while (thisTop != null && otherTop != null && thisTop == otherTop) {
+                commonAncestor = thisParentChain.removeLast();
+                otherParentChain.removeLast();
+                thisTop = thisParentChain.peekLast();
+                otherTop = otherParentChain.peekLast();
+            }
+
+            // Containers don't belong to the same hierarchy???
+            if (commonAncestor == null) {
+                throw new IllegalArgumentException("No in the same hierarchy this="
+                        + thisParentChain + " other=" + otherParentChain);
+            }
+
+            // Children are always considered greater than their parents, so if one of the containers
+            // we are comparing it the parent of the other then whichever is the child is greater.
+            if (commonAncestor == this) {
+                return -1;
+            } else if (commonAncestor == other) {
+                return 1;
+            }
+
+            // The position of the first non-common ancestor in the common ancestor list determines
+            // which is greater the which.
+            final WindowList<WindowContainer> list = commonAncestor.mChildren;
+            return list.indexOf(thisParentChain.peekLast()) > list.indexOf(otherParentChain.peekLast())
+                    ? 1 : -1;
+        } finally {
+            mTmpChain1.clear();
+            mTmpChain2.clear();
+        }
+    }
+
+    private void getParents(LinkedList<WindowContainer> parents) {
+        parents.clear();
+        WindowContainer current = this;
+        do {
+            parents.addLast(current);
+            current = current.mParent;
+        } while (current != null);
+    }
+
+    WindowContainerController getController() {
+        return mController;
+    }
+
+    void setController(WindowContainerController controller) {
+        if (mController != null && controller != null) {
+            throw new IllegalArgumentException("Can't set controller=" + mController
+                    + " for container=" + this + " Already set to=" + mController);
+        }
+        if (controller != null) {
+            controller.setContainer(this);
+        } else if (mController != null) {
+            mController.setContainer(null);
+        }
+        mController = controller;
+    }
+
+    /**
+     * Dumps the names of this container children in the input print writer indenting each
+     * level with the input prefix.
+     */
+    void dumpChildrenNames(StringBuilder out, String prefix) {
+        final String childPrefix = prefix + " ";
+        out.append(getName() + "\n");
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            out.append(childPrefix + "#" + i + " ");
+            wc.dumpChildrenNames(out, childPrefix);
+        }
+    }
+
+    String getName() {
+        return toString();
+    }
+
+    private ForAllWindowsConsumerWrapper obtainConsumerWrapper(Consumer<WindowState> consumer) {
+        ForAllWindowsConsumerWrapper wrapper = mConsumerWrapperPool.acquire();
+        if (wrapper == null) {
+            wrapper = new ForAllWindowsConsumerWrapper();
+        }
+        wrapper.setConsumer(consumer);
+        return wrapper;
+    }
+
+    private final class ForAllWindowsConsumerWrapper implements ToBooleanFunction<WindowState> {
+
+        private Consumer<WindowState> mConsumer;
+
+        void setConsumer(Consumer<WindowState> consumer) {
+            mConsumer = consumer;
+        }
+
+        @Override
+        public boolean apply(WindowState w) {
+            mConsumer.accept(w);
+            return false;
+        }
+
+        void release() {
+            mConsumer = null;
+            mConsumerWrapperPool.release(this);
+        }
+    }
+}