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